Bug 225680 - Ability to detach tabs. r=mconnor, enn. a=beltzner.

This commit is contained in:
Asaf Romano 2008-11-15 21:47:53 +02:00
Родитель d4b644f7d7
Коммит 02251f4f79
4 изменённых файлов: 121 добавлений и 31 удалений

Просмотреть файл

@ -863,7 +863,10 @@ function BrowserStartup() {
var uriToLoad = null; var uriToLoad = null;
// window.arguments[0]: URI to load (string), or an nsISupportsArray of // window.arguments[0]: URI to load (string), or an nsISupportsArray of
// nsISupportsStrings to load // nsISupportsStrings to load, or a xul:tab of
// a tabbrowser, which will be replaced by this
// window (for this case, all other arguments are
// ignored).
// [1]: character set (string) // [1]: character set (string)
// [2]: referrer (nsIURI) // [2]: referrer (nsIURI)
// [3]: postData (nsIInputStream) // [3]: postData (nsIInputStream)
@ -880,7 +883,7 @@ function BrowserStartup() {
appCore.startPageCycler(); appCore.startPageCycler();
#else #else
# only load url passed in when we're not page cycling # only load url passed in when we're not page cycling
if (uriToLoad && !isLoadingBlank) { if (uriToLoad && !isLoadingBlank) {
if (uriToLoad instanceof Ci.nsISupportsArray) { if (uriToLoad instanceof Ci.nsISupportsArray) {
let count = uriToLoad.Count(); let count = uriToLoad.Count();
let specs = []; let specs = [];
@ -895,6 +898,17 @@ function BrowserStartup() {
gBrowser.loadTabs(specs, false, true); gBrowser.loadTabs(specs, false, true);
} catch (e) {} } catch (e) {}
} }
else if (uriToLoad instanceof XULElement) {
// swap the given tab with the default about:blank tab and then close
// the original tab in the other window.
// Stop the about:blank load
gBrowser.selectedBrowser.stop();
// make sure it has a docshell
gBrowser.selectedBrowser.docShell;
gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
}
else if (window.arguments.length >= 3) { else if (window.arguments.length >= 3) {
loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null, loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
window.arguments[4] || false); window.arguments[4] || false);

Просмотреть файл

@ -271,6 +271,9 @@
</tooltip> </tooltip>
<toolbox id="navigator-toolbox" class="toolbox-top" mode="icons" <toolbox id="navigator-toolbox" class="toolbox-top" mode="icons"
ondragover="gBrowser._onDragOver(event); event.stopPropagation();"
ondrop="gBrowser._onDrop(event); event.stopPropagation();"
ondragleave="gBrowser._onDragLeave(event); event.stopPropagation();"
defaultmode="icons"> defaultmode="icons">
<!-- Menu --> <!-- Menu -->
<toolbar type="menubar" id="toolbar-menubar" class="chromeclass-menubar" customizable="true" <toolbar type="menubar" id="toolbar-menubar" class="chromeclass-menubar" customizable="true"

Просмотреть файл

@ -50,6 +50,8 @@
%tabBrowserDTD; %tabBrowserDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD; %globalDTD;
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
%placesDTD;
]> ]>
<bindings id="tabBrowserBindings" <bindings id="tabBrowserBindings"
@ -66,7 +68,10 @@
<xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/> <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
<xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown" <xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();"> onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();">
<xul:hbox class="tab-drop-indicator-bar" collapsed="true" chromedir="&locale.dir;"> <xul:hbox class="tab-drop-indicator-bar" collapsed="true" chromedir="&locale.dir;"
ondragover="this.parentNode.parentNode._onDragOver(event); event.stopPropagation();"
ondragleave="this.parentNode.parentNode._onDragLeave(event); event.stopPropagation();"
ondrop="this.parentNode.parentNode._onDrop(event); event.stopPropagation();">
<xul:hbox class="tab-drop-indicator" mousethrough="always"/> <xul:hbox class="tab-drop-indicator" mousethrough="always"/>
</xul:hbox> </xul:hbox>
<xul:hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child" <xul:hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child"
@ -74,6 +79,7 @@
ondragstart="this.parentNode.parentNode._onDragStart(event); event.stopPropagation();" ondragstart="this.parentNode.parentNode._onDragStart(event); event.stopPropagation();"
ondragover="this.parentNode.parentNode._onDragOver(event); event.stopPropagation();" ondragover="this.parentNode.parentNode._onDragOver(event); event.stopPropagation();"
ondrop="this.parentNode.parentNode._onDrop(event); event.stopPropagation();" ondrop="this.parentNode.parentNode._onDrop(event); event.stopPropagation();"
ondragend="this.parentNode.parentNode._onDragEnd(event); event.stopPropagation();"
ondragleave="this.parentNode.parentNode._onDragLeave(event); event.stopPropagation();"> ondragleave="this.parentNode.parentNode._onDragLeave(event); event.stopPropagation();">
<xul:tooltip onpopupshowing="return this.parentNode.parentNode.parentNode.createTooltip(event);"/> <xul:tooltip onpopupshowing="return this.parentNode.parentNode.parentNode.createTooltip(event);"/>
<xul:menupopup anonid="tabContextMenu" onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);"> <xul:menupopup anonid="tabContextMenu" onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
@ -92,6 +98,11 @@
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode; oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/> tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
<xul:menuseparator/> <xul:menuseparator/>
<xul:menuitem id="context_openTabInWindow" label="&cmd.open_window.label;"
tbattr="tabbrowser-multiple"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser._replaceTabWithWindow(tabbrowser.mContextTab);"/>
<xul:menuseparator/>
<xul:menuitem id="context_bookmarkTab" <xul:menuitem id="context_bookmarkTab"
label="&bookmarkThisTab.label;" label="&bookmarkThisTab.label;"
accesskey="&bookmarkThisTab.accesskey;" accesskey="&bookmarkThisTab.accesskey;"
@ -693,10 +704,11 @@
</method> </method>
<method name="updateCurrentBrowser"> <method name="updateCurrentBrowser">
<parameter name="aForceUpdate"/>
<body> <body>
<![CDATA[ <![CDATA[
var newBrowser = this.getBrowserAtIndex(this.mTabContainer.selectedIndex); var newBrowser = this.getBrowserAtIndex(this.mTabContainer.selectedIndex);
if (this.mCurrentBrowser == newBrowser) if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
return; return;
if (this.mCurrentBrowser) { if (this.mCurrentBrowser) {
@ -1609,6 +1621,13 @@
ourBrowser.webProgress.addProgressListener(filter, ourBrowser.webProgress.addProgressListener(filter,
Components.interfaces.nsIWebProgress.NOTIFY_ALL); Components.interfaces.nsIWebProgress.NOTIFY_ALL);
this.setTabTitle(aOurTab);
// If the tab was already selected (this happpens in the scenraio of
// _replaceTabWithWindow), notify onLoactionChange, etc.
if (aOurTab == this.selectedTab)
this.updateCurrentBrowser(true);
]]> ]]>
</body> </body>
</method> </method>
@ -1783,17 +1802,12 @@
if (target.localName == "tab" && if (target.localName == "tab" &&
aEvent.originalTarget.localName != "toolbarbutton") { aEvent.originalTarget.localName != "toolbarbutton") {
var dt = aEvent.dataTransfer; var dt = aEvent.dataTransfer;
dt.mozSetDataAt("application/x-moz-node", target, 0); // We're internetionally not setting any other data-type, otherwise
var URI = this.getBrowserForTab(aEvent.target).currentURI; // applications may override our drop-as-window behavior
if (URI) { dt.mozSetDataAt("application/x-moz-tabbrowser-tab", target, 0);
var spec = URI.spec;
dt.mozSetDataAt("text/x-moz-url", spec + "\n" + aEvent.target.label, 0); var canvas = tabPreviews.capture(target, false);
dt.mozSetDataAt("text/uri-list", spec + "\n" + aEvent.target.label, 0); dt.setDragImage(canvas, 0, 0);
dt.mozSetDataAt("text/plain", spec, 0);
dt.mozSetDataAt("text/html", '<a href="' + spec + '">' + aEvent.target.label + '</a>', 0);
} else {
dt.mozSetDataAt("text/plain", "about:blank", 0);
}
} }
]]> ]]>
</body> </body>
@ -1806,10 +1820,23 @@
["text/x-moz-url", "text/uri-list", "text/plain", "application/x-moz-file"] ["text/x-moz-url", "text/uri-list", "text/plain", "application/x-moz-file"]
]]></field> ]]></field>
<field name="_cachedTargetInToolbox">null</field>
<method name="_setEffectAllowedForDataTransfer"> <method name="_setEffectAllowedForDataTransfer">
<parameter name="aEvent"/> <parameter name="aEvent"/>
<body> <body>
<![CDATA[ <![CDATA[
// Find out if the we're dragged over the toolbox
var target = aEvent.target;
var isInToolbox = target == this._cachedTargetInToolbox;
while (target && !isInToolbox) {
if (target == gNavToolbox) {
isInToolbox = true;
this._cachedTargetInToolbox = target;
}
else
target = target.parentNode;
}
var dt = aEvent.dataTransfer; var dt = aEvent.dataTransfer;
// Disallow dropping multiple items // Disallow dropping multiple items
if (dt.mozItemCount > 1) if (dt.mozItemCount > 1)
@ -1818,8 +1845,8 @@
var types = dt.mozTypesAt(0); var types = dt.mozTypesAt(0);
var sourceNode = null; var sourceNode = null;
// tabs are always added as the first type // tabs are always added as the first type
if (types[0] == "application/x-moz-node") { if (types[0] == "application/x-moz-tabbrowser-tab") {
var sourceNode = dt.mozGetDataAt("application/x-moz-node", 0); var sourceNode = dt.mozGetDataAt("application/x-moz-tabbrowser-tab", 0);
if (sourceNode instanceof XULElement && if (sourceNode instanceof XULElement &&
sourceNode.localName == "tab" && sourceNode.localName == "tab" &&
(sourceNode.parentNode == this.mTabContainer || (sourceNode.parentNode == this.mTabContainer ||
@ -1829,13 +1856,23 @@
(aEvent.screenX >= sourceNode.boxObject.screenX && (aEvent.screenX >= sourceNode.boxObject.screenX &&
aEvent.screenX <= (sourceNode.boxObject.screenX + aEvent.screenX <= (sourceNode.boxObject.screenX +
sourceNode.boxObject.width))) { sourceNode.boxObject.width))) {
return dt.effectAllowed = "none"; return dt.effectAllowed = "none";
} }
// Within the toolbox, allow dropping by the height of a tab off
// the tabbar
if (isInToolbox &&
aEvent.screenY < sourceNode.boxObject.screenY -
sourceNode.boxObject.height)
return dt.effectAllowed = "none";
return dt.effectAllowed = "copyMove"; return dt.effectAllowed = "copyMove";
} }
} }
// only tab-drags are taken care off within the toolbox
if (!isInToolbox)
return dt.effectAllowed = "none";
for (var i=0; i < this._supportedLinkDropTypes.length; i++) { for (var i=0; i < this._supportedLinkDropTypes.length; i++) {
if (types.contains(this._supportedLinkDropTypes[i])) { if (types.contains(this._supportedLinkDropTypes[i])) {
// Here we need to to do this manually // Here we need to to do this manually
@ -1956,12 +1993,15 @@
var dropEffect = dt.dropEffect; var dropEffect = dt.dropEffect;
var draggedTab; var draggedTab;
if (dropEffect != "link") { // copy or move if (dropEffect != "link") { // copy or move
draggedTab = dt.mozGetDataAt("application/x-moz-node", 0); draggedTab = dt.mozGetDataAt("application/x-moz-tabbrowser-tab", 0);
NS_ASSERT(draggedTab && draggedTab.localName == "tab", NS_ASSERT(draggedTab && draggedTab.localName == "tab",
"copy or move action without a tab"); "copy or move action without a tab");
} }
if (draggedTab && (dropEffect == "copy" || draggedTab.parentNode == this.mTabContainer)) { this.mTabDropIndicatorBar.collapsed = true;
if (draggedTab && (dropEffect == "copy" ||
draggedTab.parentNode == this.mTabContainer)) {
var newIndex = this.getNewIndex(aEvent); var newIndex = this.getNewIndex(aEvent);
if (dropEffect == "copy") { if (dropEffect == "copy") {
// copy the dropped tab (wherever it's from) // copy the dropped tab (wherever it's from)
@ -1998,7 +2038,6 @@
// swapBrowsersAndCloseOther, so that the updateCurrentBrowser // swapBrowsersAndCloseOther, so that the updateCurrentBrowser
// it triggers will correctly update our URL bar. // it triggers will correctly update our URL bar.
this.selectedTab = newTab; this.selectedTab = newTab;
this.setTabTitle(newTab);
} }
else { else {
var url; var url;
@ -2060,20 +2099,51 @@
</body> </body>
</method> </method>
<method name="_onDragEnd">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (this.mTabs.length == 1)
return;
var dt = aEvent.dataTransfer;
if (dt.dropEffect == "none") {
var draggedTab = dt.mozGetDataAt("application/x-moz-tabbrowser-tab", 0);
this._replaceTabWithWindow(draggedTab);
}
]]>
</body>
</method>
<method name="_replaceTabWithWindow">
<parameter name="aTab"/>
<body>
<![CDATA[
// tell a new window to take the "dropped" tab
var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
ww.openWindow(window,
getBrowserURL(),
null,
"chrome,dialog=no,all",
aTab);
]]>
</body>
</method>
<method name="_onDragLeave"> <method name="_onDragLeave">
<parameter name="aEvent"/> <parameter name="aEvent"/>
<body> <body>
<![CDATA[ <![CDATA[
this.mDragTime = 0; this.mDragTime = 0;
var dropEffect = aEvent.dataTransfer.dropEffect; // This does not work at all (see bug 458613)
if (dropEffect == "move" || dropEffect == "copy") { var target = aEvent.relatedTarget;
var target = aEvent.relatedTarget; while (target && (target != this && target != gNavToolbox))
while (target && target != this.mStrip) target = target.parentNode;
target = target.parentNode; if (target)
if (target) return;
return;
}
this.mTabDropIndicatorBar.collapsed = true; this.mTabDropIndicatorBar.collapsed = true;
]]> ]]>
</body> </body>

Просмотреть файл

@ -1032,6 +1032,7 @@
var ip = this.insertionPoint; var ip = this.insertionPoint;
if (!ip || !PlacesControllerDragHelper.canDrop(ip)) { if (!ip || !PlacesControllerDragHelper.canDrop(ip)) {
ib.removeAttribute("dragging"); ib.removeAttribute("dragging");
PlacesControllerDragHelper.currentDataTransfer = null;
return; return;
} }
@ -1103,8 +1104,10 @@
]]></handler> ]]></handler>
<handler event="drop"><![CDATA[ <handler event="drop"><![CDATA[
// Cache the dataTransfer // If the data-transfer isn't cached, it's not ours
PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer; // This happens when dropping tabs
if (event.dataTransfer != PlacesControllerDragHelper.currentDataTransfer)
return;
var dropPoint = this._getDropPoint(event); var dropPoint = this._getDropPoint(event);
if (!dropPoint) if (!dropPoint)