зеркало из https://github.com/mozilla/gecko-dev.git
418 строки
15 KiB
XML
418 строки
15 KiB
XML
<?xml version="1.0"?>
|
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
|
|
|
|
<bindings
|
|
xmlns="http://www.mozilla.org/xbl"
|
|
xmlns:xbl="http://www.mozilla.org/xbl"
|
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
|
|
<binding id="documenttab">
|
|
<content observes="bcast_urlbarState">
|
|
<xul:vbox anonid="container" class="documenttab-container" left="0" top="0" observes="bcast_urlbarState">
|
|
<xul:stack anonid="page" flex="1">
|
|
<html:canvas anonid="thumbnail" class="documenttab-thumbnail" left="0" moz-opaque="true" empty="true"
|
|
observes="bcast_urlbarState"/>
|
|
<xul:hbox anonid="reload" class="documenttab-reload" left="0" top="0" onclick="document.getBindingParent(this)._onUndo();" observes="bcast_urlbarState"/>
|
|
<xul:hbox anonid="close-container" class="documenttab-close-container" top="0" align="center" onclick="event.stopPropagation(); document.getBindingParent(this)._onClose()" observes="bcast_urlbarState">
|
|
<xul:image anonid="close" class="documenttab-close" mousethrough="always" observes="bcast_urlbarState"/>
|
|
</xul:hbox>
|
|
</xul:stack>
|
|
<xul:label anonid="title" crop="end" class="documenttab-title" observes="bcast_urlbarState"/>
|
|
</xul:vbox>
|
|
</content>
|
|
|
|
<handlers>
|
|
<handler event="click" clickcount="1" action="this._onClick()"/>
|
|
</handlers>
|
|
|
|
<implementation>
|
|
<field name="ignoreUndo">false</field>
|
|
<field name="thumbnail" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "thumbnail");</field>
|
|
<field name="_reload" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "reload");</field>
|
|
<field name="_closeContainer" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "close-container");</field>
|
|
<field name="_title" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "title");</field>
|
|
<field name="_container" readonly="true">this.parentNode.parentNode;</field>
|
|
|
|
<constructor>
|
|
<![CDATA[
|
|
this.updateTabletLayout(this.thumbnail);
|
|
]]>
|
|
</constructor>
|
|
|
|
<method name="updateTabletLayout">
|
|
<parameter name="thumbnail"/>
|
|
<body>
|
|
<![CDATA[
|
|
let tabWidth, tabHeight;
|
|
|
|
if (Util.isTablet()) {
|
|
tabWidth = 176;
|
|
tabHeight = 108;
|
|
} else {
|
|
tabWidth = 104;
|
|
tabHeight = 65;
|
|
}
|
|
|
|
if (tabWidth != thumbnail.width) {
|
|
let reload = this._reload;
|
|
let closeContainer = this._closeContainer;
|
|
let title = this._title;
|
|
|
|
thumbnail.width = reload.width = title.width = tabWidth;
|
|
thumbnail.height = reload.height = closeContainer.height = tabHeight;
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_onClick">
|
|
<body>
|
|
<![CDATA[
|
|
this._container.selectedTab = this;
|
|
|
|
let selectFn = new Function("event", this._container.parentNode.getAttribute("onselect"));
|
|
selectFn.call(this);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_onClose">
|
|
<body>
|
|
<![CDATA[
|
|
let callbackFunc = this._container.parentNode.getAttribute(this.hasAttribute("reload") ? "onclosereloadtab" : "onclosetab");
|
|
let closeFn = new Function("event", callbackFunc);
|
|
closeFn.call(this);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_onUndo">
|
|
<body>
|
|
<![CDATA[
|
|
let container = this._container;
|
|
let closeFn = new Function("event", container.getAttribute("onreloadtab"));
|
|
closeFn.call(this);
|
|
container.removeClosedTab(this);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="updateTitle">
|
|
<parameter name="title"/>
|
|
<body>
|
|
<![CDATA[
|
|
this._title.value = title;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="updateThumbnail">
|
|
<parameter name="browser"/>
|
|
<parameter name="width"/>
|
|
<parameter name="height"/>
|
|
<body>
|
|
<![CDATA[
|
|
let thumbnail = this.thumbnail;
|
|
// Ensure the thumbnail will have the correct
|
|
// dimensions for tablet and phone modes
|
|
this.updateTabletLayout(thumbnail);
|
|
|
|
if (browser.currentURI.spec == "about:blank") {
|
|
thumbnail.setAttribute("empty", "true");
|
|
return;
|
|
}
|
|
thumbnail.removeAttribute("empty");
|
|
|
|
const tabWidth = thumbnail.width;
|
|
const tabHeight = thumbnail.height;
|
|
|
|
let ratio = tabHeight / tabWidth;
|
|
if (browser.contentDocumentWidth > 0)
|
|
width = Math.min(width, browser.contentDocumentWidth);
|
|
|
|
if (browser.contentDocumentHeight > 0)
|
|
height = Math.min(height, browser.contentDocumentHeight);
|
|
|
|
let newHeight = width * ratio;
|
|
if (height >= newHeight) {
|
|
height = newHeight;
|
|
} else {
|
|
// the browser aspect ratio does not match the tabs aspect ratio
|
|
width = height / ratio;
|
|
}
|
|
|
|
// Recreate the canvas as it may be tainted and not useable for remote pages
|
|
if (thumbnail.hasAttribute("restored")) {
|
|
thumbnail.removeAttribute("restored");
|
|
thumbnail = this.thumbnail.cloneNode(false);
|
|
this.thumbnail.parentNode.replaceChild(thumbnail, this.thumbnail);
|
|
this.thumbnail = thumbnail;
|
|
}
|
|
|
|
let self = this;
|
|
let renderer = rendererFactory(browser, thumbnail);
|
|
renderer.drawContent(function(ctx, callback) {
|
|
ctx.save();
|
|
ctx.clearRect(0, 0, tabWidth, tabHeight);
|
|
ctx.scale(tabWidth / width, tabHeight / height);
|
|
callback(browser, 0, 0, width, height, "white");
|
|
ctx.restore();
|
|
|
|
// We don't have an event for the async drawContent anymore, so hack it
|
|
setTimeout(function() {
|
|
// Save the thumbnail to the session in case we need to use it in a restore
|
|
let data = thumbnail.toDataURL("image/png");
|
|
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
|
ss.setTabValue(self, "thumbnail", data);
|
|
}, 800);
|
|
});
|
|
]]>
|
|
</body>
|
|
</method>
|
|
</implementation>
|
|
</binding>
|
|
|
|
<binding id="tablist">
|
|
<content>
|
|
<xul:scrollbox anonid="tabs-scrollbox" class="tabs-scrollbox" flex="1">
|
|
<xul:vbox class="tabs-list" anonid="tabs-children" />
|
|
</xul:scrollbox>
|
|
<xul:box class="tabs-list" anonid="tabs-undo"/>
|
|
</content>
|
|
|
|
<handlers>
|
|
<handler event="TapDouble" action="this._onDoubleTap();"/>
|
|
</handlers>
|
|
|
|
<implementation>
|
|
<field name="children">document.getAnonymousElementByAttribute(this, "anonid", "tabs-children");</field>
|
|
<field name="_scrollbox">document.getAnonymousElementByAttribute(this, "anonid", "tabs-scrollbox");</field>
|
|
<field name="_tabsUndo">document.getAnonymousElementByAttribute(this, "anonid", "tabs-undo");</field>
|
|
<field name="_selectedTab">null</field>
|
|
|
|
<constructor>
|
|
<![CDATA[
|
|
// This customDragger wraps the default dragger, and looks for
|
|
// horizontal movement to hide or show the tab bar in tablet mode.
|
|
let scrollbox = this._scrollbox;
|
|
let [,scroller,dragger] = ScrollUtils.getScrollboxFromElement(scrollbox);
|
|
scrollbox.customDragger = {
|
|
_grabSidebar: false,
|
|
|
|
isDraggable: function isDraggable(target, content) {
|
|
return { x: true, y: true };
|
|
},
|
|
|
|
dragStart: function dragStart(cx, cy, target) {
|
|
this._canGrabSidebar = true;
|
|
this._grabSidebar = false;
|
|
dragger.dragStart(cx, cy, target, scroller);
|
|
},
|
|
|
|
dragStop: function dragStop(dx, dy) {
|
|
if (this._grabSidebar)
|
|
TabletSidebar.ungrab();
|
|
dragger.dragStop(dx, dy, scroller);
|
|
},
|
|
|
|
dragMove: function dragMove(dx, dy) {
|
|
if (this._canGrabSidebar) {
|
|
this._grabSidebar = TabletSidebar.tryGrab(dx);
|
|
// After trying once, don't keep checking every move.
|
|
this._canGrabSidebar = false;
|
|
}
|
|
if (this._grabSidebar)
|
|
TabletSidebar.slideBy(dx);
|
|
return dragger.dragMove(dx, dy, scroller);
|
|
}
|
|
};
|
|
]]>
|
|
</constructor>
|
|
|
|
<!-- Used by the chrome input handler -->
|
|
<property name="boxObject"
|
|
readonly="true"
|
|
onget="return this._scrollbox.boxObject;"/>
|
|
|
|
<field name="scrollBoxObject">
|
|
this.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
|
|
</field>
|
|
|
|
<field name="_closedTab">null</field>
|
|
<property name="hasClosedTab" readonly="true" onget="return !!this._closedTab;"/>
|
|
|
|
<property name="selectedTab" onget="return this._selectedTab;">
|
|
<setter>
|
|
<![CDATA[
|
|
if (this._selectedTab)
|
|
this._selectedTab.removeAttribute("selected");
|
|
|
|
if (val)
|
|
val.setAttribute("selected", "true");
|
|
|
|
this._selectedTab = val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<method name="addTab">
|
|
<body>
|
|
<![CDATA[
|
|
let tab = document.createElement("documenttab");
|
|
this.children.appendChild(tab);
|
|
this._updateWidth();
|
|
return tab;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="removeTab">
|
|
<parameter name="aTab"/>
|
|
<body>
|
|
<![CDATA[
|
|
let closedTab = this._closedTab;
|
|
if (closedTab) {
|
|
this._tabsUndo.removeChild(closedTab);
|
|
this._closedTab = null;
|
|
}
|
|
|
|
let isEmpty = (aTab.thumbnail && aTab.thumbnail.hasAttribute("empty"));
|
|
if (aTab.ignoreUndo || isEmpty) {
|
|
this.children.removeChild(aTab);
|
|
} else if (!closedTab || closedTab != aTab) {
|
|
// duplicate the old thumbnail to the new one because moving the
|
|
// tab in the dom clear the canvas
|
|
let oldThumbnail = aTab.thumbnail.toDataURL("image/png");
|
|
this._tabsUndo.appendChild(aTab);
|
|
let thumbnailImg = new Image();
|
|
thumbnailImg.onload = function() {
|
|
if (aTab.thumbnail)
|
|
aTab.thumbnail.getContext("2d").drawImage(thumbnailImg, 0, 0);
|
|
};
|
|
thumbnailImg.src = oldThumbnail;
|
|
|
|
aTab.setAttribute("reload", "true");
|
|
this._closedTab = aTab;
|
|
}
|
|
|
|
this.resize();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="removeClosedTab">
|
|
<body><![CDATA[
|
|
if (!this._closedTab)
|
|
return;
|
|
|
|
this.removeTab(this._closedTab);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="resize">
|
|
<body>
|
|
<![CDATA[
|
|
let container = Rect.fromRect(this.parentNode.getBoundingClientRect());
|
|
if (container.height == 0)
|
|
return; // Don't try to resize while collapsed.
|
|
|
|
let element = Rect.fromRect(this._scrollbox.getBoundingClientRect());
|
|
let undo = Rect.fromRect(this._tabsUndo.getBoundingClientRect());
|
|
let lastChild = Rect.fromRect(this.parentNode.lastChild.getBoundingClientRect());
|
|
|
|
let height = window.innerHeight - element.top - (lastChild.top - element.bottom) - lastChild.height;
|
|
this.children.style.height = height + "px";
|
|
this._scrollbox.style.height = height + "px";
|
|
|
|
this._updateWidth();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<field name="_columnsCount">0</field>
|
|
<method name="_updateWidth">
|
|
<body>
|
|
<![CDATA[
|
|
// XXX we can do better than using a constant
|
|
const COLUMN_MARGIN = 20;
|
|
let firstBox = this.children.firstChild.getBoundingClientRect();
|
|
let lastBox = this.children.lastChild.getBoundingClientRect();
|
|
|
|
// We can't rely on getBoundingClientRect() for this.children height
|
|
// it is not synced (sometimes, especially during resize) with the
|
|
// style.height rule
|
|
let columnsCount = Util.isTablet() ? 1 : Math.ceil(this.children.childNodes.length / Math.floor(parseInt(this.children.style.height) / (firstBox.height + 4)));
|
|
if (this._columnsCount != columnsCount && window.innerWidth > 1) { // > 1 to ignore column resizing while the main window is loading
|
|
let width = columnsCount * (COLUMN_MARGIN + firstBox.width);
|
|
this.children.style.width = width + "px";
|
|
|
|
// Clamp the sidebar width so it won't overflow the window. Only clamp
|
|
// the scrollbox. The children need to be the full width.
|
|
if (width > window.innerWidth - firstBox.width)
|
|
width = window.innerWidth - firstBox.width;
|
|
this._scrollbox.style.width = width + "px";
|
|
this._columnsCount = columnsCount;
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_onDoubleTap">
|
|
<body>
|
|
<![CDATA[
|
|
new Function("event", this.getAttribute("ondoubletap")).call();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
</implementation>
|
|
</binding>
|
|
|
|
<binding id="documenttab-popup" extends="chrome://browser/content/bindings.xml#richlistitem">
|
|
<content>
|
|
<xul:box class="tab-popup-item-box">
|
|
<xul:image class="documenttab-popup-checkmark"/>
|
|
<xul:image class="documenttab-popup-favicon" xbl:inherits="src=img"/>
|
|
<xul:label class="documenttab-popup-label" xbl:inherits="value=label" crop="center"/>
|
|
<xul:button class="documenttab-popup-closebutton" onclick="event.stopPropagation(); document.getBindingParent(this)._onClose()" />
|
|
</xul:box>
|
|
</content>
|
|
|
|
<implementation>
|
|
<field name="_container" readonly="true">this.parentNode;</field>
|
|
<field name="tab">null</field>
|
|
|
|
<method name="_onClick">
|
|
<body>
|
|
<![CDATA[
|
|
this._container.selectedTab = this;
|
|
let selectFn = new Function("event", this._container.getAttribute("onselect"));
|
|
selectFn.call(this.tab);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_onClose">
|
|
<body>
|
|
<![CDATA[
|
|
let callbackFunc = this._container.getAttribute(this.hasAttribute("reload") ? "onclosereloadtab" : "onclosetab");
|
|
let closeFn = new Function("event", callbackFunc);
|
|
closeFn.call(this.tab);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="click" action="this._onClick();"/>
|
|
</handlers>
|
|
|
|
</binding>
|
|
|
|
</bindings>
|