зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to central, a=merge
--HG-- extra : commitid : GoUn7UzWJCi
This commit is contained in:
Коммит
541c7a5b69
|
@ -94,7 +94,7 @@ browser/extensions/loop/content/preferences/prefs.js
|
|||
# Libs we don't need to check
|
||||
browser/extensions/loop/content/panels/vendor
|
||||
browser/extensions/loop/content/shared/vendor
|
||||
browser/extensions/loop/standalone/content/libs
|
||||
browser/extensions/loop/standalone/content/vendor
|
||||
# Libs we don't need to check
|
||||
browser/extensions/loop/test/shared/vendor
|
||||
# Coverage files
|
||||
|
|
|
@ -210,7 +210,6 @@ DEFAULT_TEST_PREFS = {
|
|||
'security.default_personal_cert': 'Select Automatically',
|
||||
'network.http.prompt-temp-redirect': False,
|
||||
'security.warn_viewing_mixed': False,
|
||||
'browser.panorama.experienced_first_run': True,
|
||||
# Set a future policy version to avoid the telemetry prompt.
|
||||
'toolkit.telemetry.prompted': 999,
|
||||
'toolkit.telemetry.notifiedOptOut': 999,
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
"security.default_personal_cert": "Select Automatically",
|
||||
"network.http.prompt-temp-redirect": false,
|
||||
"security.warn_viewing_mixed": false,
|
||||
"browser.panorama.experienced_first_run": true,
|
||||
"toolkit.telemetry.prompted": 999,
|
||||
"toolkit.telemetry.notifiedOptOut": 999,
|
||||
"extensions.defaultProviders.enabled": true,
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
Cu.import("resource:///modules/TabGroupsMigrator.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
const SUPPORT_URL = "https://support.mozilla.org/kb/tab-groups-removal";
|
||||
|
||||
function fillInDescription() {
|
||||
let descElement = document.getElementById("mainDescription");
|
||||
let text = descElement.getAttribute("_description_label");
|
||||
let learnMoreText = descElement.getAttribute("_learnmore_label");
|
||||
let link = document.createElement("a");
|
||||
link.href = SUPPORT_URL;
|
||||
link.target = "_blank";
|
||||
link.textContent = learnMoreText;
|
||||
// We don't want to assign the label (plaintext, because we took it out of
|
||||
// getAttribute, so html entities have been resolved) directly to innerHTML.
|
||||
// Using a separate element and then using innerHTML on that would mean we
|
||||
// depend on the HTML encoding (or lack thereof) of %S, which l10n tools
|
||||
// might change.
|
||||
// So instead, split the plaintext we have and create textNodes:
|
||||
let nodes = text.split(/%S/).map(document.createTextNode.bind(document));
|
||||
// then insert the link:
|
||||
nodes.splice(1, 0, link);
|
||||
// then append all of these in turn:
|
||||
for (let node of nodes) {
|
||||
descElement.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
let loadHandler = e => {
|
||||
window.removeEventListener("DOMContentLoaded", loadHandler);
|
||||
fillInDescription();
|
||||
resolve();
|
||||
};
|
||||
window.addEventListener("DOMContentLoaded", loadHandler, false);
|
||||
});
|
||||
|
||||
let tabGroupsBookmarkItemId;
|
||||
// If the session wasn't restored this run/session, this might be null.
|
||||
// Then we shouldn't show the button:
|
||||
if (TabGroupsMigrator.bookmarkedGroupsPromise) {
|
||||
let bookmarkPromise = TabGroupsMigrator.bookmarkedGroupsPromise.then(bm => {
|
||||
return PlacesUtils.promiseItemId(bm.guid);
|
||||
}).then(itemId => { tabGroupsBookmarkItemId = itemId });
|
||||
|
||||
Promise.all([bookmarkPromise, loadPromise]).then(function() {
|
||||
document.getElementById("show-migrated-bookmarks-button").style.removeProperty("display");
|
||||
});
|
||||
}
|
||||
|
||||
function showMigratedGroups() {
|
||||
let browserWin = getBrowserWindow();
|
||||
browserWin.PlacesCommandHook.showPlacesOrganizer(["BookmarksMenu", tabGroupsBookmarkItemId]);
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
# 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/.
|
||||
-->
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
|
||||
%htmlDTD;
|
||||
<!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
|
||||
%netErrorDTD;
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
%globalDTD;
|
||||
<!ENTITY % restorepageDTD SYSTEM "chrome://browser/locale/aboutSessionRestore.dtd">
|
||||
%restorepageDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<head>
|
||||
<title>&tabgroupsmigration.tabtitle;</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css" media="all"/>
|
||||
<link rel="stylesheet" href="chrome://browser/skin/aboutSessionRestore.css" type="text/css" media="all"/>
|
||||
<link rel="icon" type="image/png" href="chrome://global/skin/icons/warning-16.png"/>
|
||||
|
||||
<script type="application/javascript;version=1.8" src="chrome://browser/content/aboutSessionRestore.js"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://browser/content/aboutTabGroupsMigration.js"/>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
|
||||
<div class="container restore-chosen">
|
||||
|
||||
<div class="title">
|
||||
<h1 class="title-text">&tabgroupsmigration.pagetitle;</h1>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p id="mainDescription"
|
||||
_learnmore_label="&tabgroupsmigration.learnMoreLink;"
|
||||
_description_label="&tabgroupsmigration.description;"></p>
|
||||
<button id="show-migrated-bookmarks-button" style="display:none" onclick="showMigratedGroups()">&tabgroupsmigration.bookmarkbutton;</button>
|
||||
|
||||
<p>&tabgroupsmigration.restoredescription;</p>
|
||||
</div>
|
||||
<div class="tree-container" available="true">
|
||||
<xul:tree id="tabList" seltype="single" hidecolumnpicker="true"
|
||||
onclick="onListClick(event);" onkeydown="onListKeyDown(event);"
|
||||
_window_label="&restorepage.windowLabel;">
|
||||
<xul:treecols>
|
||||
<xul:treecol cycler="true" id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
|
||||
<xul:splitter class="tree-splitter"/>
|
||||
<xul:treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
|
||||
</xul:treecols>
|
||||
<xul:treechildren flex="1"/>
|
||||
</xul:tree>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
#ifdef XP_UNIX
|
||||
<xul:button id="errorCancel"
|
||||
label="&restorepage.closeButton;"
|
||||
accesskey="&restorepage.close.access;"
|
||||
oncommand="startNewSession();"/>
|
||||
<xul:button class="primary"
|
||||
id="errorTryAgain"
|
||||
label="&restorepage.tryagainButton;"
|
||||
accesskey="&restorepage.restore.access;"
|
||||
oncommand="restoreSession();"/>
|
||||
#else
|
||||
<xul:button class="primary"
|
||||
id="errorTryAgain"
|
||||
label="&restorepage.tryagainButton;"
|
||||
accesskey="&restorepage.restore.access;"
|
||||
oncommand="restoreSession();"/>
|
||||
<xul:button id="errorCancel"
|
||||
label="&restorepage.closeButton;"
|
||||
accesskey="&restorepage.close.access;"
|
||||
oncommand="startNewSession();"/>
|
||||
#endif
|
||||
</div>
|
||||
<!-- holds the session data for when the tab is closed -->
|
||||
<input type="text" id="sessionData" style="display: none;"/>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -82,7 +82,6 @@
|
|||
<command id="Browser:NextTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(1, true);" reserved="true"/>
|
||||
<command id="Browser:PrevTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(-1, true);" reserved="true"/>
|
||||
<command id="Browser:ShowAllTabs" oncommand="allTabs.open();"/>
|
||||
<command id="Browser:ToggleTabView" oncommand="TabView.toggle();"/>
|
||||
<command id="cmd_fullZoomReduce" oncommand="FullZoom.reduce()"/>
|
||||
<command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
|
||||
<command id="cmd_fullZoomReset" oncommand="FullZoom.reset()"/>
|
||||
|
@ -178,7 +177,6 @@
|
|||
<broadcaster id="isFrameImage"/>
|
||||
<broadcaster id="singleFeedMenuitemState" disabled="true"/>
|
||||
<broadcaster id="multipleFeedsMenuState" hidden="true"/>
|
||||
<broadcaster id="tabviewGroupsNumber" groups="1"/>
|
||||
<broadcaster id="sync-setup-state"/>
|
||||
<broadcaster id="sync-syncnow-state" hidden="true"/>
|
||||
<broadcaster id="sync-reauth-state" hidden="true"/>
|
||||
|
@ -428,8 +426,6 @@
|
|||
|
||||
<key id="key_switchTextDirection" key="&bidiSwitchTextDirectionItem.commandkey;" command="cmd_switchTextDirection" modifiers="accel,shift" />
|
||||
|
||||
<key id="key_tabview" key="&tabView.commandkey;" command="Browser:ToggleTabView" modifiers="accel,shift"/>
|
||||
|
||||
<key id="key_privatebrowsing" command="Tools:PrivateBrowsing" key="&privateBrowsingCmd.commandkey;" modifiers="accel,shift"/>
|
||||
<key id="key_sanitize" command="Tools:Sanitize" keycode="VK_DELETE" modifiers="accel,shift"/>
|
||||
#ifdef XP_MACOSX
|
||||
|
|
|
@ -1,490 +0,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/. */
|
||||
|
||||
var TabView = {
|
||||
_deck: null,
|
||||
_iframe: null,
|
||||
_window: null,
|
||||
_initialized: false,
|
||||
_browserKeyHandlerInitialized: false,
|
||||
_closedLastVisibleTabBeforeFrameInitialized: false,
|
||||
_isFrameLoading: false,
|
||||
_initFrameCallbacks: [],
|
||||
PREF_BRANCH: "browser.panorama.",
|
||||
PREF_FIRST_RUN: "browser.panorama.experienced_first_run",
|
||||
PREF_STARTUP_PAGE: "browser.startup.page",
|
||||
PREF_RESTORE_ENABLED_ONCE: "browser.panorama.session_restore_enabled_once",
|
||||
GROUPS_IDENTIFIER: "tabview-groups",
|
||||
VISIBILITY_IDENTIFIER: "tabview-visibility",
|
||||
|
||||
// ----------
|
||||
get windowTitle() {
|
||||
delete this.windowTitle;
|
||||
let brandBundle = document.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
let title = gNavigatorBundle.getFormattedString("tabview.title", [brandShortName]);
|
||||
return this.windowTitle = title;
|
||||
},
|
||||
|
||||
// ----------
|
||||
get firstUseExperienced() {
|
||||
let pref = this.PREF_FIRST_RUN;
|
||||
if (Services.prefs.prefHasUserValue(pref))
|
||||
return Services.prefs.getBoolPref(pref);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
set firstUseExperienced(val) {
|
||||
Services.prefs.setBoolPref(this.PREF_FIRST_RUN, val);
|
||||
},
|
||||
|
||||
// ----------
|
||||
get sessionRestoreEnabledOnce() {
|
||||
let pref = this.PREF_RESTORE_ENABLED_ONCE;
|
||||
if (Services.prefs.prefHasUserValue(pref))
|
||||
return Services.prefs.getBoolPref(pref);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
set sessionRestoreEnabledOnce(val) {
|
||||
Services.prefs.setBoolPref(this.PREF_RESTORE_ENABLED_ONCE, val);
|
||||
},
|
||||
|
||||
// ----------
|
||||
init: function TabView_init() {
|
||||
// disable the ToggleTabView command for popup windows
|
||||
goSetCommandEnabled("Browser:ToggleTabView", window.toolbar.visible);
|
||||
if (!window.toolbar.visible)
|
||||
return;
|
||||
|
||||
if (this._initialized)
|
||||
return;
|
||||
|
||||
if (this.firstUseExperienced) {
|
||||
// ___ visibility
|
||||
|
||||
let data = SessionStore.getWindowValue(window, this.VISIBILITY_IDENTIFIER);
|
||||
if (data && data == "true") {
|
||||
this.show();
|
||||
} else {
|
||||
try {
|
||||
data = SessionStore.getWindowValue(window, this.GROUPS_IDENTIFIER);
|
||||
if (data) {
|
||||
let parsedData = JSON.parse(data);
|
||||
this.updateGroupNumberBroadcaster(parsedData.totalNumber || 1);
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
let self = this;
|
||||
// if a tab is changed from hidden to unhidden and the iframe is not
|
||||
// initialized, load the iframe and setup the tab.
|
||||
this._tabShowEventListener = function(event) {
|
||||
if (!self._window)
|
||||
self._initFrame(function() {
|
||||
self._window.UI.onTabSelect(gBrowser.selectedTab);
|
||||
if (self._closedLastVisibleTabBeforeFrameInitialized) {
|
||||
self._closedLastVisibleTabBeforeFrameInitialized = false;
|
||||
self._window.UI.showTabView(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
this._tabCloseEventListener = function(event) {
|
||||
if (!self._window && gBrowser.visibleTabs.length == 0)
|
||||
self._closedLastVisibleTabBeforeFrameInitialized = true;
|
||||
};
|
||||
gBrowser.tabContainer.addEventListener(
|
||||
"TabShow", this._tabShowEventListener, false);
|
||||
gBrowser.tabContainer.addEventListener(
|
||||
"TabClose", this._tabCloseEventListener, false);
|
||||
|
||||
if (this._tabBrowserHasHiddenTabs()) {
|
||||
this._setBrowserKeyHandlers();
|
||||
} else {
|
||||
// for restoring last session and undoing recently closed window
|
||||
this._SSWindowStateReadyListener = function (event) {
|
||||
if (this._tabBrowserHasHiddenTabs())
|
||||
this._setBrowserKeyHandlers();
|
||||
}.bind(this);
|
||||
window.addEventListener(
|
||||
"SSWindowStateReady", this._SSWindowStateReadyListener, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Services.prefs.addObserver(this.PREF_BRANCH, this, false);
|
||||
|
||||
this._initialized = true;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Observes topic changes.
|
||||
observe: function TabView_observe(subject, topic, data) {
|
||||
if (data == this.PREF_FIRST_RUN && this.firstUseExperienced) {
|
||||
this._addToolbarButton();
|
||||
this.enableSessionRestore();
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Uninitializes TabView.
|
||||
uninit: function TabView_uninit() {
|
||||
if (!this._initialized)
|
||||
return;
|
||||
|
||||
Services.prefs.removeObserver(this.PREF_BRANCH, this);
|
||||
|
||||
if (this._tabShowEventListener)
|
||||
gBrowser.tabContainer.removeEventListener(
|
||||
"TabShow", this._tabShowEventListener, false);
|
||||
|
||||
if (this._tabCloseEventListener)
|
||||
gBrowser.tabContainer.removeEventListener(
|
||||
"TabClose", this._tabCloseEventListener, false);
|
||||
|
||||
if (this._SSWindowStateReadyListener)
|
||||
window.removeEventListener(
|
||||
"SSWindowStateReady", this._SSWindowStateReadyListener, false);
|
||||
|
||||
this._initialized = false;
|
||||
|
||||
if (this._window) {
|
||||
this._window = null;
|
||||
}
|
||||
|
||||
if (this._iframe) {
|
||||
this._iframe.remove();
|
||||
this._iframe = null;
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Creates the frame and calls the callback once it's loaded.
|
||||
// If the frame already exists, calls the callback immediately.
|
||||
_initFrame: function TabView__initFrame(callback) {
|
||||
let hasCallback = typeof callback == "function";
|
||||
|
||||
// prevent frame to be initialized for popup windows
|
||||
if (!window.toolbar.visible)
|
||||
return;
|
||||
|
||||
if (this._window) {
|
||||
if (hasCallback)
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasCallback)
|
||||
this._initFrameCallbacks.push(callback);
|
||||
|
||||
if (this._isFrameLoading)
|
||||
return;
|
||||
|
||||
this._isFrameLoading = true;
|
||||
|
||||
TelemetryStopwatch.start("PANORAMA_INITIALIZATION_TIME_MS");
|
||||
|
||||
// ___ find the deck
|
||||
this._deck = document.getElementById("tab-view-deck");
|
||||
|
||||
// ___ create the frame
|
||||
this._iframe = document.createElement("iframe");
|
||||
this._iframe.id = "tab-view";
|
||||
this._iframe.setAttribute("transparent", "true");
|
||||
this._iframe.setAttribute("tooltip", "tab-view-tooltip");
|
||||
this._iframe.flex = 1;
|
||||
|
||||
let self = this;
|
||||
|
||||
window.addEventListener("tabviewframeinitialized", function onInit() {
|
||||
window.removeEventListener("tabviewframeinitialized", onInit, false);
|
||||
|
||||
TelemetryStopwatch.finish("PANORAMA_INITIALIZATION_TIME_MS");
|
||||
|
||||
self._isFrameLoading = false;
|
||||
self._window = self._iframe.contentWindow;
|
||||
self._setBrowserKeyHandlers();
|
||||
|
||||
if (self._tabShowEventListener) {
|
||||
gBrowser.tabContainer.removeEventListener(
|
||||
"TabShow", self._tabShowEventListener, false);
|
||||
self._tabShowEventListener = null;
|
||||
}
|
||||
if (self._tabCloseEventListener) {
|
||||
gBrowser.tabContainer.removeEventListener(
|
||||
"TabClose", self._tabCloseEventListener, false);
|
||||
self._tabCloseEventListener = null;
|
||||
}
|
||||
if (self._SSWindowStateReadyListener) {
|
||||
window.removeEventListener(
|
||||
"SSWindowStateReady", self._SSWindowStateReadyListener, false);
|
||||
self._SSWindowStateReadyListener = null;
|
||||
}
|
||||
|
||||
self._initFrameCallbacks.forEach(cb => cb());
|
||||
self._initFrameCallbacks = [];
|
||||
}, false);
|
||||
|
||||
this._iframe.setAttribute("src", "chrome://browser/content/tabview.html");
|
||||
this._deck.appendChild(this._iframe);
|
||||
|
||||
// ___ create tooltip
|
||||
let tooltip = document.createElement("tooltip");
|
||||
tooltip.id = "tab-view-tooltip";
|
||||
tooltip.setAttribute("onpopupshowing", "return TabView.fillInTooltip(document.tooltipNode);");
|
||||
document.getElementById("mainPopupSet").appendChild(tooltip);
|
||||
},
|
||||
|
||||
// ----------
|
||||
getContentWindow: function TabView_getContentWindow() {
|
||||
return this._window;
|
||||
},
|
||||
|
||||
// ----------
|
||||
isVisible: function TabView_isVisible() {
|
||||
return (this._deck ? this._deck.selectedPanel == this._iframe : false);
|
||||
},
|
||||
|
||||
// ----------
|
||||
show: function TabView_show() {
|
||||
if (this.isVisible())
|
||||
return;
|
||||
|
||||
let self = this;
|
||||
this._initFrame(function() {
|
||||
self._window.UI.showTabView(true);
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
hide: function TabView_hide() {
|
||||
if (this.isVisible() && this._window) {
|
||||
this._window.UI.exit();
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
toggle: function TabView_toggle() {
|
||||
if (this.isVisible())
|
||||
this.hide();
|
||||
else
|
||||
this.show();
|
||||
},
|
||||
|
||||
// ----------
|
||||
_tabBrowserHasHiddenTabs: function TabView_tabBrowserHasHiddenTabs() {
|
||||
return (gBrowser.tabs.length - gBrowser.visibleTabs.length) > 0;
|
||||
},
|
||||
|
||||
// ----------
|
||||
updateContextMenu: function TabView_updateContextMenu(tab, popup) {
|
||||
let separator = document.getElementById("context_tabViewNamedGroups");
|
||||
let isEmpty = true;
|
||||
|
||||
while (popup.firstChild && popup.firstChild != separator)
|
||||
popup.removeChild(popup.firstChild);
|
||||
|
||||
let self = this;
|
||||
this._initFrame(function() {
|
||||
let activeGroup = tab._tabViewTabItem.parent;
|
||||
let groupItems = self._window.GroupItems.groupItems;
|
||||
|
||||
groupItems.forEach(function(groupItem) {
|
||||
// if group has title, it's not hidden and there is no active group or
|
||||
// the active group id doesn't match the group id, a group menu item
|
||||
// would be added.
|
||||
if (!groupItem.hidden &&
|
||||
(groupItem.getTitle().trim() || groupItem.getChildren().length) &&
|
||||
(!activeGroup || activeGroup.id != groupItem.id)) {
|
||||
let menuItem = self._createGroupMenuItem(groupItem);
|
||||
popup.insertBefore(menuItem, separator);
|
||||
isEmpty = false;
|
||||
}
|
||||
});
|
||||
separator.hidden = isEmpty;
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
_createGroupMenuItem: function TabView__createGroupMenuItem(groupItem) {
|
||||
let menuItem = document.createElement("menuitem");
|
||||
let title = groupItem.getTitle();
|
||||
|
||||
if (!title.trim()) {
|
||||
let topChildLabel = groupItem.getTopChild().tab.label;
|
||||
let childNum = groupItem.getChildren().length;
|
||||
|
||||
if (childNum > 1) {
|
||||
let num = childNum - 1;
|
||||
title =
|
||||
gNavigatorBundle.getString("tabview.moveToUnnamedGroup.label");
|
||||
title = PluralForm.get(num, title).replace("#1", topChildLabel).replace("#2", num);
|
||||
} else {
|
||||
title = topChildLabel;
|
||||
}
|
||||
}
|
||||
|
||||
menuItem.setAttribute("label", title);
|
||||
menuItem.setAttribute("tooltiptext", title);
|
||||
menuItem.setAttribute("crop", "center");
|
||||
menuItem.setAttribute("class", "tabview-menuitem");
|
||||
menuItem.setAttribute(
|
||||
"oncommand",
|
||||
"TabView.moveTabTo(TabContextMenu.contextTab,'" + groupItem.id + "')");
|
||||
|
||||
return menuItem;
|
||||
},
|
||||
|
||||
// ----------
|
||||
moveTabTo: function TabView_moveTabTo(tab, groupItemId) {
|
||||
if (this._window) {
|
||||
this._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
|
||||
} else {
|
||||
let self = this;
|
||||
this._initFrame(function() {
|
||||
self._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Adds new key commands to the browser, for invoking the Tab Candy UI
|
||||
// and for switching between groups of tabs when outside of the Tab Candy UI.
|
||||
_setBrowserKeyHandlers: function TabView__setBrowserKeyHandlers() {
|
||||
if (this._browserKeyHandlerInitialized)
|
||||
return;
|
||||
|
||||
this._browserKeyHandlerInitialized = true;
|
||||
|
||||
let self = this;
|
||||
window.addEventListener("keypress", function(event) {
|
||||
if (self.isVisible() || !self._tabBrowserHasHiddenTabs())
|
||||
return;
|
||||
|
||||
let charCode = event.charCode;
|
||||
// Control (+ Shift) + `
|
||||
if (event.ctrlKey && !event.metaKey && !event.altKey &&
|
||||
(charCode == 96 || charCode == 126)) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
self._initFrame(function() {
|
||||
let groupItems = self._window.GroupItems;
|
||||
let tabItem = groupItems.getNextGroupItemTab(event.shiftKey);
|
||||
if (!tabItem)
|
||||
return;
|
||||
|
||||
if (gBrowser.selectedTab.pinned)
|
||||
groupItems.updateActiveGroupItemAndTabBar(tabItem, {dontSetActiveTabInGroup: true});
|
||||
else
|
||||
gBrowser.selectedTab = tabItem.tab;
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Prepares the tab view for undo close tab.
|
||||
prepareUndoCloseTab: function TabView_prepareUndoCloseTab(blankTabToRemove) {
|
||||
if (this._window) {
|
||||
this._window.UI.restoredClosedTab = true;
|
||||
|
||||
if (blankTabToRemove && blankTabToRemove._tabViewTabItem)
|
||||
blankTabToRemove._tabViewTabItem.isRemovedAfterRestore = true;
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Cleans up the tab view after undo close tab.
|
||||
afterUndoCloseTab: function TabView_afterUndoCloseTab() {
|
||||
if (this._window)
|
||||
this._window.UI.restoredClosedTab = false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// On move to group pop showing.
|
||||
moveToGroupPopupShowing: function TabView_moveToGroupPopupShowing(event) {
|
||||
// Update the context menu only if Panorama was already initialized or if
|
||||
// there are hidden tabs.
|
||||
let numHiddenTabs = gBrowser.tabs.length - gBrowser.visibleTabs.length;
|
||||
if (this._window || numHiddenTabs > 0)
|
||||
this.updateContextMenu(TabContextMenu.contextTab, event.target);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _addToolbarButton
|
||||
// Adds the TabView button to the TabsToolbar.
|
||||
_addToolbarButton: function TabView__addToolbarButton() {
|
||||
let buttonId = "tabview-button";
|
||||
|
||||
if (CustomizableUI.getPlacementOfWidget(buttonId))
|
||||
return;
|
||||
|
||||
let allTabsBtnPlacement = CustomizableUI.getPlacementOfWidget("alltabs-button");
|
||||
// allTabsBtnPlacement can never be null because the button isn't removable
|
||||
let desiredPosition = allTabsBtnPlacement.position + 1;
|
||||
CustomizableUI.addWidgetToArea(buttonId, "TabsToolbar", desiredPosition);
|
||||
// NB: this is for backwards compatibility, and should be removed by
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=976041
|
||||
document.persist("TabsToolbar", "currentset");
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: updateGroupNumberBroadcaster
|
||||
// Updates the group number broadcaster.
|
||||
updateGroupNumberBroadcaster: function TabView_updateGroupNumberBroadcaster(number) {
|
||||
let groupsNumber = document.getElementById("tabviewGroupsNumber");
|
||||
groupsNumber.setAttribute("groups", number);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: enableSessionRestore
|
||||
// Enables automatic session restore when the browser is started. Does
|
||||
// nothing if we already did that once in the past.
|
||||
enableSessionRestore: function TabView_enableSessionRestore() {
|
||||
if (!this._window || !this.firstUseExperienced)
|
||||
return;
|
||||
|
||||
// do nothing if we already enabled session restore once
|
||||
if (this.sessionRestoreEnabledOnce)
|
||||
return;
|
||||
|
||||
this.sessionRestoreEnabledOnce = true;
|
||||
|
||||
// enable session restore if necessary
|
||||
if (Services.prefs.getIntPref(this.PREF_STARTUP_PAGE) != 3) {
|
||||
Services.prefs.setIntPref(this.PREF_STARTUP_PAGE, 3);
|
||||
|
||||
// show banner
|
||||
this._window.UI.notifySessionRestoreEnabled();
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: fillInTooltip
|
||||
// Fills in the tooltip text.
|
||||
fillInTooltip: function fillInTooltip(tipElement) {
|
||||
let retVal = false;
|
||||
let titleText = null;
|
||||
let direction = tipElement.ownerDocument.dir;
|
||||
|
||||
while (!titleText && tipElement) {
|
||||
if (tipElement.nodeType == Node.ELEMENT_NODE)
|
||||
titleText = tipElement.getAttribute("title");
|
||||
tipElement = tipElement.parentNode;
|
||||
}
|
||||
let tipNode = document.getElementById("tab-view-tooltip");
|
||||
tipNode.style.direction = direction;
|
||||
|
||||
if (titleText) {
|
||||
tipNode.setAttribute("label", titleText);
|
||||
retVal = true;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
};
|
|
@ -836,11 +836,6 @@ statuspanel[inactive][previoustype=overLink] {
|
|||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#promobox");
|
||||
}
|
||||
|
||||
/* tabview menus */
|
||||
.tabview-menuitem {
|
||||
max-width: 32em;
|
||||
}
|
||||
|
||||
/* gcli */
|
||||
|
||||
html|*#gcli-tooltip-frame,
|
||||
|
|
|
@ -1388,7 +1388,6 @@ var gBrowserInit = {
|
|||
RestoreLastSessionObserver.init();
|
||||
|
||||
SocialUI.init();
|
||||
TabView.init();
|
||||
|
||||
// Telemetry for master-password - we do this after 5 seconds as it
|
||||
// can cause IO if NSS/PSM has not already initialized.
|
||||
|
@ -1509,7 +1508,6 @@ var gBrowserInit = {
|
|||
|
||||
gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
|
||||
ctrlTab.uninit();
|
||||
TabView.uninit();
|
||||
SocialUI.uninit();
|
||||
gBrowserThumbnails.uninit();
|
||||
FullZoom.destroy();
|
||||
|
@ -1564,7 +1562,7 @@ var gBrowserInit = {
|
|||
'viewToolbarsMenu', 'viewSidebarMenuMenu', 'Browser:Reload',
|
||||
'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu', 'View:PageSource', 'View:FullScreen',
|
||||
'viewHistorySidebar', 'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs',
|
||||
'View:PageInfo', 'Browser:ToggleTabView'];
|
||||
'View:PageInfo'];
|
||||
var element;
|
||||
|
||||
for (let disabledItem of disabledItems) {
|
||||
|
@ -5521,7 +5519,6 @@ const nodeToTooltipMap = {
|
|||
"new-tab-button": "newTabButton.tooltip",
|
||||
"tabs-newtab-button": "newTabButton.tooltip",
|
||||
"fullscreen-button": "fullscreenButton.tooltip",
|
||||
"tabview-button": "tabviewButton.tooltip",
|
||||
"downloads-button": "downloads.tooltip",
|
||||
};
|
||||
const nodeToShortcutMap = {
|
||||
|
@ -5533,7 +5530,6 @@ const nodeToShortcutMap = {
|
|||
"new-tab-button": "key_newNavigatorTab",
|
||||
"tabs-newtab-button": "key_newNavigatorTab",
|
||||
"fullscreen-button": "key_fullScreen",
|
||||
"tabview-button": "key_tabview",
|
||||
"downloads-button": "key_openDownloads"
|
||||
};
|
||||
const gDynamicTooltipCache = new Map();
|
||||
|
@ -6534,11 +6530,6 @@ function CanCloseWindow()
|
|||
|
||||
function WindowIsClosing()
|
||||
{
|
||||
if (TabView.isVisible()) {
|
||||
TabView.hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!closeWindow(false, warnAboutClosingWindow))
|
||||
return false;
|
||||
|
||||
|
@ -6758,9 +6749,7 @@ function undoCloseTab(aIndex) {
|
|||
|
||||
var tab = null;
|
||||
if (SessionStore.getClosedTabCount(window) > (aIndex || 0)) {
|
||||
TabView.prepareUndoCloseTab(blankTabToRemove);
|
||||
tab = SessionStore.undoCloseTab(window, aIndex || 0);
|
||||
TabView.afterUndoCloseTab();
|
||||
|
||||
if (blankTabToRemove)
|
||||
gBrowser.removeTab(blankTabToRemove);
|
||||
|
@ -7854,10 +7843,6 @@ var TabContextMenu = {
|
|||
if (!bookmarkAllTabs.hidden)
|
||||
PlacesCommandHook.updateBookmarkAllTabsCommand();
|
||||
|
||||
// Hide "Move to Group" if it's a pinned tab.
|
||||
document.getElementById("context_tabViewMenu").hidden =
|
||||
(this.contextTab.pinned || !TabView.firstUseExperienced);
|
||||
|
||||
// Adjust the state of the toggle mute menu item.
|
||||
let toggleMute = document.getElementById("context_toggleMuteTab");
|
||||
if (this.contextTab.hasAttribute("muted")) {
|
||||
|
|
|
@ -91,15 +91,6 @@
|
|||
<menuitem id="context_unpinTab" label="&unpinTab.label;" hidden="true"
|
||||
accesskey="&unpinTab.accesskey;"
|
||||
oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
|
||||
<menu id="context_tabViewMenu" label="&moveToGroup.label;"
|
||||
accesskey="&moveToGroup.accesskey;">
|
||||
<menupopup id="context_tabViewMenuPopup"
|
||||
onpopupshowing="if (event.target == this) TabView.moveToGroupPopupShowing(event);">
|
||||
<menuseparator id="context_tabViewNamedGroups" hidden="true"/>
|
||||
<menuitem id="context_tabViewNewGroup" label="&moveToNewGroup.label;"
|
||||
oncommand="TabView.moveTabTo(TabContextMenu.contextTab, null);"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuitem id="context_openTabInWindow" label="&moveToNewWindow.label;"
|
||||
accesskey="&moveToNewWindow.accesskey;"
|
||||
tbattr="tabbrowser-multiple"
|
||||
|
@ -590,12 +581,6 @@
|
|||
removable="false">
|
||||
<menupopup id="alltabs-popup"
|
||||
position="after_end">
|
||||
<menuitem id="menu_tabview"
|
||||
class="menuitem-iconic"
|
||||
key="key_tabview"
|
||||
label="&viewTabGroups.label;"
|
||||
command="Browser:ToggleTabView"
|
||||
observes="tabviewGroupsNumber"/>
|
||||
<menuitem id="alltabs_undoCloseTab"
|
||||
class="menuitem-iconic"
|
||||
key="key_undoCloseTab"
|
||||
|
@ -1058,12 +1043,6 @@
|
|||
class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
label="&syncToolbarButton.label;"
|
||||
oncommand="gSyncUI.handleToolbarButton()"/>
|
||||
|
||||
<toolbarbutton id="tabview-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
label="&tabGroupsButton.label;"
|
||||
command="Browser:ToggleTabView"
|
||||
tooltip="dynamic-shortcut-tooltip"
|
||||
observes="tabviewGroupsNumber"/>
|
||||
</toolbarpalette>
|
||||
</toolbox>
|
||||
|
||||
|
|
|
@ -56,8 +56,7 @@ var gChatWindow = {
|
|||
'key_selectTab4', 'key_selectTab5', 'key_selectTab6',
|
||||
'key_selectTab7', 'key_selectTab8', 'key_selectLastTab',
|
||||
'viewHistorySidebar', 'viewBookmarksSidebar',
|
||||
'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs',
|
||||
'Browser:ToggleTabView'];
|
||||
'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs'];
|
||||
|
||||
for (let disabledItem of disabledItems) {
|
||||
document.getElementById(disabledItem).setAttribute("disabled", "true");
|
||||
|
|
|
@ -1007,14 +1007,7 @@
|
|||
<method name="updateTitlebar">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if ("TabView" in window && TabView.isVisible()) {
|
||||
// ToDo: this will be removed when we gain ability to draw to the menu bar.
|
||||
// Bug 586175
|
||||
this.ownerDocument.title = TabView.windowTitle;
|
||||
}
|
||||
else {
|
||||
this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
|
||||
}
|
||||
this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -4374,11 +4367,9 @@
|
|||
if (tabForEvent.selected)
|
||||
return;
|
||||
|
||||
// If this is a tabprompt, and we're not in tabview/panorama with
|
||||
// the prompt being a beforeunload one, we won't switch tabs
|
||||
// (unless this behaviour has been disabled entirely using the pref).
|
||||
// If this is a tabprompt, we won't switch tabs
|
||||
// (unless this behaviour has been disabled entirely using the pref)
|
||||
if (event.detail && event.detail.tabPrompt &&
|
||||
!(event.detail.inPermitUnload && ("TabView" in window) && TabView.isVisible()) &&
|
||||
Services.prefs.getBoolPref("browser.tabs.dontfocusfordialogs")) {
|
||||
let docPrincipal = targetIsWindow ? event.target.document.nodePrincipal : null;
|
||||
// At least one of these should/will be non-null:
|
||||
|
@ -6224,7 +6215,7 @@
|
|||
var tabstripBO = tabContainer.mTabstrip.scrollBoxObject;
|
||||
for (var i = 0; i < this.childNodes.length; i++) {
|
||||
let curTab = this.childNodes[i].tab;
|
||||
if (!curTab) // "Tab Groups" menuitem and its menuseparator
|
||||
if (!curTab) // "Undo close tab", menuseparator, or entries put here by addons.
|
||||
continue;
|
||||
let curTabBO = curTab.boxObject;
|
||||
if (curTabBO.screenX >= tabstripBO.screenX &&
|
||||
|
|
|
@ -15,12 +15,6 @@ function testAttrib(elem, attrib, attribValue, msg) {
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
TabView._initFrame(runTest);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
is(gBrowser.tabs.length, 1, "one tab is open initially");
|
||||
|
||||
// Add several new tabs in sequence, hiding some, to ensure that the
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
TabView._initFrame(runTest);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// establish initial state
|
||||
is(gBrowser.tabs.length, 1, "we start with one tab");
|
||||
|
||||
|
|
|
@ -7,12 +7,6 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
TabView._initFrame(runTest);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// Add a tab that will get removed and hidden
|
||||
let testTab = gBrowser.addTab("about:blank", {skipAnimation: true});
|
||||
is(gBrowser.visibleTabs.length, 2, "just added a tab, so 2 tabs");
|
||||
|
|
|
@ -29,18 +29,6 @@ const EXPECTED_REFLOWS = [
|
|||
"_handleNewTab@chrome://browser/content/tabbrowser.xml|" +
|
||||
"onxbltransitionend@chrome://browser/content/tabbrowser.xml|",
|
||||
|
||||
// The TabView iframe causes reflows in the parent document.
|
||||
"iQClass_height@chrome://browser/content/tabview.js|" +
|
||||
"GroupItem_getContentBounds@chrome://browser/content/tabview.js|" +
|
||||
"GroupItem_shouldStack@chrome://browser/content/tabview.js|" +
|
||||
"GroupItem_arrange@chrome://browser/content/tabview.js|" +
|
||||
"GroupItem_add@chrome://browser/content/tabview.js|" +
|
||||
"GroupItems_newTab@chrome://browser/content/tabview.js|" +
|
||||
"TabItem__reconnect@chrome://browser/content/tabview.js|" +
|
||||
"TabItem@chrome://browser/content/tabview.js|" +
|
||||
"TabItems_link@chrome://browser/content/tabview.js|" +
|
||||
"TabItems_init/this._eventListeners.open@chrome://browser/content/tabview.js|",
|
||||
|
||||
// SessionStore.getWindowDimensions()
|
||||
"ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm|" +
|
||||
"ssi_updateWindowFeatures/<@resource:///modules/sessionstore/SessionStore.jsm|" +
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
add_task(function* () {
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
yield new Promise(resolve => TabView._initFrame(resolve));
|
||||
|
||||
// There should be one tab when we start the test
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
|
||||
|
@ -31,13 +27,6 @@ add_task(function* () {
|
|||
gBrowser.selectedTab = testTab;
|
||||
gBrowser.showOnlyTheseTabs([testTab]);
|
||||
|
||||
// if the tabview frame is initialized, we need to move the orignal tab to
|
||||
// another group; otherwise, selecting a tab would make all three tabs in
|
||||
// the same group to display.
|
||||
let tabViewWindow = TabView.getContentWindow();
|
||||
if (tabViewWindow)
|
||||
tabViewWindow.GroupItems.moveTabToGroupItem(origTab, null);
|
||||
|
||||
visible = gBrowser.visibleTabs;
|
||||
is(visible.length, 2, "2 tabs should be visible including the pinned");
|
||||
is(visible[0], pinned, "first is pinned");
|
||||
|
@ -101,8 +90,5 @@ add_task(function* () {
|
|||
is(gBrowser.tabs.length, 1, "sanity check that it matches");
|
||||
is(gBrowser.selectedTab, origTab, "got the orig tab");
|
||||
is(origTab.hidden, false, "and it's not hidden -- visible!");
|
||||
|
||||
if (tabViewWindow)
|
||||
tabViewWindow.GroupItems.groupItems[0].close();
|
||||
});
|
||||
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
TabView._initFrame(runTest);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
let tabOne = gBrowser.addTab("about:blank");
|
||||
let tabTwo = gBrowser.addTab("http://mochi.test:8888/");
|
||||
gBrowser.selectedTab = tabTwo;
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
TabView._initFrame(runTest);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// There should be one tab when we start the test
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
is(gBrowser.visibleTabs.length, 1, "1 tab should be open");
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
add_task(function* test() {
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
yield new Promise(resolve => TabView._initFrame(resolve));
|
||||
|
||||
// There should be one tab when we start the test
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
is(gBrowser.visibleTabs.length, 1, "there is one visible tab");
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
add_task(function* test() {
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
yield new Promise(resolve => TabView._initFrame(resolve));
|
||||
|
||||
gPrefService.setBoolPref("browser.ctrlTab.previews", true);
|
||||
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
|
|
|
@ -72,6 +72,8 @@ browser.jar:
|
|||
content/browser/aboutTabCrashed.css (content/aboutTabCrashed.css)
|
||||
content/browser/aboutTabCrashed.js (content/aboutTabCrashed.js)
|
||||
content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml)
|
||||
* content/browser/aboutTabGroupsMigration.xhtml (content/aboutTabGroupsMigration.xhtml)
|
||||
content/browser/aboutTabGroupsMigration.js (content/aboutTabGroupsMigration.js)
|
||||
* content/browser/browser.css (content/browser.css)
|
||||
* content/browser/browser.js (content/browser.js)
|
||||
* content/browser/browser.xul (content/browser.xul)
|
||||
|
@ -93,7 +95,6 @@ browser.jar:
|
|||
content/browser/browser-social.js (content/browser-social.js)
|
||||
* content/browser/browser-syncui.js (content/browser-syncui.js)
|
||||
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
|
||||
content/browser/browser-tabview.js (content/browser-tabview.js)
|
||||
content/browser/browser-thumbnails.js (content/browser-thumbnails.js)
|
||||
content/browser/browser-trackingprotection.js (content/browser-trackingprotection.js)
|
||||
* content/browser/chatWindow.xul (content/chatWindow.xul)
|
||||
|
|
|
@ -104,7 +104,6 @@ skip-if = os == "linux" # Intermittent failures
|
|||
skip-if = e10s # Bug 1088710
|
||||
[browser_967000_button_feeds.js]
|
||||
[browser_967000_button_sync.js]
|
||||
[browser_967000_button_tabView.js]
|
||||
[browser_968447_bookmarks_toolbar_items_in_panel.js]
|
||||
skip-if = os == "linux" # Intemittent failures - bug 979207
|
||||
[browser_968565_insert_before_hidden_items.js]
|
||||
|
|
|
@ -1,55 +0,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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(function() {
|
||||
info("Check Tab Groups button functionality");
|
||||
let deferred = Promise.defer();
|
||||
let timeout = null;
|
||||
|
||||
// add the Tab View button to the panel
|
||||
CustomizableUI.addWidgetToArea("tabview-button", CustomizableUI.AREA_PANEL);
|
||||
|
||||
window.addEventListener("tabviewhidden", function tabViewHidden() {
|
||||
clearTimeout(timeout);
|
||||
window.removeEventListener("tabviewhidden", tabViewHidden, false);
|
||||
ok(true, "Tab View is closed");
|
||||
deferred.resolve();
|
||||
}, false);
|
||||
|
||||
window.addEventListener("tabviewshown", function tabViewShown() {
|
||||
window.removeEventListener("tabviewshown", tabViewShown, false);
|
||||
ok(true, "Tab Groups are loaded");
|
||||
// close tabs view
|
||||
window.TabView.hide();
|
||||
timeout = setTimeout(() => {
|
||||
window.removeEventListener("tabviewhidden", tabViewHidden, false);
|
||||
deferred.reject("Tabs view wasn't hidden within 20000ms");
|
||||
}, 20000);
|
||||
}, false);
|
||||
|
||||
// check the button's functionality
|
||||
yield PanelUI.show();
|
||||
|
||||
let tabViewButton = document.getElementById("tabview-button");
|
||||
ok(tabViewButton, "Tab Groups button was added to the Panel Menu");
|
||||
tabViewButton.click();
|
||||
|
||||
yield deferred.promise;
|
||||
|
||||
ok(!isPanelUIOpen(), "The panel is closed");
|
||||
|
||||
if(isPanelUIOpen()) {
|
||||
let panelHidePromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield panelHidePromise;
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
// reset the panel to the default state
|
||||
yield resetCustomization();
|
||||
ok(CustomizableUI.inDefaultState, "UI is in default state again.");
|
||||
});
|
|
@ -21,7 +21,6 @@ DIRS += [
|
|||
'sessionstore',
|
||||
'shell',
|
||||
'selfsupport',
|
||||
'tabview',
|
||||
'uitour',
|
||||
'translation',
|
||||
]
|
||||
|
|
|
@ -93,6 +93,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabGroupsMigrator",
|
||||
"resource:///modules/TabGroupsMigrator.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
|
@ -1350,21 +1353,6 @@ BrowserGlue.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
if (this._mayNeedToWarnAboutTabGroups) {
|
||||
let haveTabGroups = false;
|
||||
let wins = Services.wm.getEnumerator("navigator:browser");
|
||||
while (wins.hasMoreElements()) {
|
||||
let win = wins.getNext();
|
||||
if (win.TabView._tabBrowserHasHiddenTabs() && win.TabView.firstUseExperienced) {
|
||||
haveTabGroups = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (haveTabGroups) {
|
||||
this._showTabGroupsDeprecationNotification();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef E10S_TESTING_ONLY
|
||||
E10SUINotification.checkStatus();
|
||||
#else
|
||||
|
@ -1405,25 +1393,12 @@ BrowserGlue.prototype = {
|
|||
},
|
||||
#endif
|
||||
|
||||
_showTabGroupsDeprecationNotification() {
|
||||
let brandShortName = gBrandBundle.GetStringFromName("brandShortName");
|
||||
let text = gBrowserBundle.formatStringFromName("tabgroups.deprecationwarning.description",
|
||||
[brandShortName], 1);
|
||||
let learnMore = gBrowserBundle.GetStringFromName("tabgroups.deprecationwarning.learnMore.label");
|
||||
let learnMoreKey = gBrowserBundle.GetStringFromName("tabgroups.deprecationwarning.learnMore.accesskey");
|
||||
|
||||
let win = RecentWindow.getMostRecentBrowserWindow();
|
||||
let notifyBox = win.document.getElementById("high-priority-global-notificationbox");
|
||||
let button = {
|
||||
label: learnMore,
|
||||
accessKey: learnMoreKey,
|
||||
callback: function(aNotificationBar, aButton) {
|
||||
win.openUILinkIn("https://support.mozilla.org/kb/tab-groups-removal", "tab");
|
||||
},
|
||||
_maybeMigrateTabGroups() {
|
||||
let migrationObserver = (stateAsSupportsString, topic) => {
|
||||
Services.obs.removeObserver(migrationObserver, "sessionstore-state-read");
|
||||
TabGroupsMigrator.migrate(stateAsSupportsString);
|
||||
};
|
||||
|
||||
notifyBox.appendNotification(text, "tabgroups-removal-notification", null,
|
||||
notifyBox.PRIORITY_WARNING_MEDIUM, [button]);
|
||||
Services.obs.addObserver(migrationObserver, "sessionstore-state-read", false);
|
||||
},
|
||||
|
||||
_onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
|
||||
|
@ -1933,7 +1908,7 @@ BrowserGlue.prototype = {
|
|||
},
|
||||
|
||||
_migrateUI: function BG__migrateUI() {
|
||||
const UI_VERSION = 34;
|
||||
const UI_VERSION = 35;
|
||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
|
||||
let currentUIVersion = 0;
|
||||
try {
|
||||
|
@ -2278,9 +2253,8 @@ BrowserGlue.prototype = {
|
|||
this._notifyNotificationsUpgrade().catch(Cu.reportError);
|
||||
}
|
||||
|
||||
if (currentUIVersion < 34) {
|
||||
// We'll do something once windows are open:
|
||||
this._mayNeedToWarnAboutTabGroups = true;
|
||||
if (currentUIVersion < 35) {
|
||||
this._maybeMigrateTabGroups();
|
||||
}
|
||||
|
||||
// Update the migration version.
|
||||
|
|
|
@ -32,7 +32,6 @@ var SessionMigrationInternal = {
|
|||
* - with tab group info (hidden + group id)
|
||||
* - with selected tab info
|
||||
* - with selected window info
|
||||
* - with tabgroups info
|
||||
*
|
||||
* The complete state is then wrapped into the "about:welcomeback" page as
|
||||
* form field info to be restored when restoring the state.
|
||||
|
@ -53,21 +52,8 @@ var SessionMigrationInternal = {
|
|||
tab.index = oldTab.index;
|
||||
tab.hidden = oldTab.hidden;
|
||||
tab.pinned = oldTab.pinned;
|
||||
// The tabgroup info is in the extData, so we need to get it out.
|
||||
if (oldTab.extData && "tabview-tab" in oldTab.extData) {
|
||||
tab.extData = {"tabview-tab": oldTab.extData["tabview-tab"]};
|
||||
}
|
||||
return tab;
|
||||
});
|
||||
// There are various tabgroup-related attributes that we need to get out
|
||||
// of the session restore data for the window, too.
|
||||
if (oldWin.extData) {
|
||||
for (let k of Object.keys(oldWin.extData)) {
|
||||
if (k.startsWith("tabview-")) {
|
||||
win.extData[k] = oldWin.extData[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
win.selected = oldWin.selected;
|
||||
win._closedTabs = [];
|
||||
return win;
|
||||
|
|
|
@ -1770,7 +1770,8 @@ var SessionStoreInternal = {
|
|||
}
|
||||
|
||||
// Default delay of 2 seconds gives enough time to catch multiple TabShow
|
||||
// events due to changing groups in Panorama.
|
||||
// events. This used to be due to changing groups in 'tab groups'. We
|
||||
// might be able to get rid of this now?
|
||||
this.saveStateDelayed(aWindow);
|
||||
},
|
||||
|
||||
|
@ -1782,7 +1783,8 @@ var SessionStoreInternal = {
|
|||
}
|
||||
|
||||
// Default delay of 2 seconds gives enough time to catch multiple TabHide
|
||||
// events due to changing groups in Panorama.
|
||||
// events. This used to be due to changing groups in 'tab groups'. We
|
||||
// might be able to get rid of this now?
|
||||
this.saveStateDelayed(aWindow);
|
||||
},
|
||||
|
||||
|
@ -2290,10 +2292,7 @@ var SessionStoreInternal = {
|
|||
// Restore into that window - pretend it's a followup since we'll already
|
||||
// have a focused window.
|
||||
//XXXzpao This is going to merge extData together (taking what was in
|
||||
// winState over what is in the window already. The hack we have
|
||||
// in _preWindowToRestoreInto will prevent most (all?) Panorama
|
||||
// weirdness but we will still merge other extData.
|
||||
// Bug 588217 should make this go away by merging the group data.
|
||||
// winState over what is in the window already.
|
||||
let options = {overwriteTabs: canOverwriteTabs, isFollowUp: true};
|
||||
this.restoreWindow(windowToUse, winState, options);
|
||||
}
|
||||
|
@ -2501,25 +2500,10 @@ var SessionStoreInternal = {
|
|||
// the previous session's tabs to the end. This will be set if possible.
|
||||
let canOverwriteTabs = false;
|
||||
|
||||
// Step 1 of processing:
|
||||
// Inspect extData for Panorama identifiers. If found, then we want to
|
||||
// inspect further. If there is a single group, then we can use this
|
||||
// window. If there are multiple groups then we won't use this window.
|
||||
let groupsData = this.getWindowValue(aWindow, "tabview-groups");
|
||||
if (groupsData) {
|
||||
groupsData = JSON.parse(groupsData);
|
||||
|
||||
// If there are multiple groups, we don't want to use this window.
|
||||
if (groupsData.totalNumber > 1)
|
||||
return [false, false];
|
||||
}
|
||||
|
||||
// Step 2 of processing:
|
||||
// If we're still here, then the window is usable. Look at the open tabs in
|
||||
// comparison to home pages. If all the tabs are home pages then we'll end
|
||||
// up overwriting all of them. Otherwise we'll just close the tabs that
|
||||
// match home pages. Tabs with the about:blank URI will always be
|
||||
// overwritten.
|
||||
// Look at the open tabs in comparison to home pages. If all the tabs are
|
||||
// home pages then we'll end up overwriting all of them. Otherwise we'll
|
||||
// just close the tabs that match home pages. Tabs with the about:blank
|
||||
// URI will always be overwritten.
|
||||
let homePages = ["about:blank"];
|
||||
let removableTabs = [];
|
||||
let tabbrowser = aWindow.gBrowser;
|
||||
|
|
|
@ -70,7 +70,7 @@ function initTreeView() {
|
|||
gTreeData = [];
|
||||
gStateObject.windows.forEach(function(aWinData, aIx) {
|
||||
var winState = {
|
||||
label: winLabel.replace("%S", (aIx + 1)),
|
||||
label: aWinData.tabGroupsMigrationTitle || winLabel.replace("%S", (aIx + 1)),
|
||||
open: true,
|
||||
checked: true,
|
||||
ix: aIx
|
||||
|
|
|
@ -13,13 +13,6 @@ function observeOneRestore(callback) {
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Disable Panorama, since it conflicts with this test.
|
||||
let tabview = document.getElementById("tab-view");
|
||||
if (tabview) {
|
||||
document.getElementById("tab-view").contentWindow.UI.uninit();
|
||||
TabView.uninit();
|
||||
}
|
||||
|
||||
// There should be one tab when we start the test
|
||||
let [origTab] = gBrowser.visibleTabs;
|
||||
let hiddenTab = gBrowser.addTab();
|
||||
|
@ -53,11 +46,6 @@ function test() {
|
|||
gBrowser.removeTab(hiddenTab);
|
||||
gBrowser.removeTab(extraTab);
|
||||
|
||||
// Re-enable Panorama.
|
||||
if (tabview) {
|
||||
TabView.init();
|
||||
}
|
||||
|
||||
finish();
|
||||
});
|
||||
ss.setBrowserState(JSON.stringify(stateObj));
|
||||
|
|
|
@ -6,10 +6,6 @@ add_task(function* () {
|
|||
/** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
yield new Promise(resolve => TabView._initFrame(resolve));
|
||||
|
||||
// Set the pref to true so we know exactly how many tabs should be restoring at
|
||||
// any given time. This guarantees that a finishing load won't start another.
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
|
||||
|
|
|
@ -7,12 +7,7 @@
|
|||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
// Ensure TabView has been initialized already. Otherwise it could
|
||||
// activate at an unexpected time and show/hide tabs.
|
||||
TabView._initFrame(runTest);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// We speed up the interval between session saves to ensure that the test
|
||||
// runs quickly.
|
||||
Services.prefs.setIntPref("browser.sessionstore.interval", 2000);
|
||||
|
|
|
@ -1,92 +0,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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var Cu = Components.utils;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource:///modules/tabview/utils.jsm");
|
||||
|
||||
// Bug 671101 - directly using webProgress in this context
|
||||
// causes docShells to leak
|
||||
this.__defineGetter__("webProgress", function () {
|
||||
let ifaceReq = docShell.QueryInterface(Ci.nsIInterfaceRequestor);
|
||||
return ifaceReq.getInterface(Ci.nsIWebProgress);
|
||||
});
|
||||
|
||||
// ----------
|
||||
// WindowEventHandler
|
||||
//
|
||||
// Handles events dispatched by the content window.
|
||||
var WindowEventHandler = {
|
||||
// ----------
|
||||
// Function: onDOMWillOpenModalDialog
|
||||
// Sends a synchronous message when the "onDOMWillOpenModalDialog" event
|
||||
// is fired right before a modal dialog will be opened by the current page.
|
||||
onDOMWillOpenModalDialog: function WEH_onDOMWillOpenModalDialog(event) {
|
||||
// (event.isTrusted == true) when the event is generated by a user action
|
||||
// and does not originate from a script.
|
||||
if (!event.isTrusted)
|
||||
return;
|
||||
|
||||
// we're intentionally sending a synchronous message to handle this event
|
||||
// as quick as possible, switch the selected tab and hide the tabview
|
||||
// before the modal dialog is shown
|
||||
sendSyncMessage("Panorama:DOMWillOpenModalDialog");
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: onMozAfterPaint
|
||||
// Sends an asynchronous message when the "onMozAfterPaint" event
|
||||
// is fired.
|
||||
onMozAfterPaint: function WEH_onMozAfterPaint(event) {
|
||||
if (event.clientRects.length > 0) {
|
||||
sendAsyncMessage("Panorama:MozAfterPaint");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// add event listeners
|
||||
addEventListener("DOMWillOpenModalDialog", WindowEventHandler.onDOMWillOpenModalDialog, false);
|
||||
addEventListener("MozAfterPaint", WindowEventHandler.onMozAfterPaint, false);
|
||||
|
||||
// ----------
|
||||
// WindowMessageHandler
|
||||
//
|
||||
// Handles messages sent by the chrome process.
|
||||
var WindowMessageHandler = {
|
||||
// ----------
|
||||
// Function: isDocumentLoaded
|
||||
// Checks if the currently active document is loaded.
|
||||
isDocumentLoaded: function WMH_isDocumentLoaded(cx) {
|
||||
let isLoaded = (content &&
|
||||
content.document.readyState != "uninitialized" &&
|
||||
!webProgress.isLoadingDocument);
|
||||
|
||||
sendAsyncMessage(cx.name, {isLoaded: isLoaded});
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isImageDocument
|
||||
// Checks if the currently active document is an image document or not.
|
||||
isImageDocument: function WMH_isImageDocument(cx) {
|
||||
let isImageDocument = (content &&
|
||||
content.document instanceof Ci.nsIImageDocument);
|
||||
|
||||
sendAsyncMessage(cx.name, {isImageDocument: isImageDocument});
|
||||
},
|
||||
|
||||
waitForDocumentLoad: function WMH_waitForDocumentLoad() {
|
||||
addEventListener("load", function listener() {
|
||||
removeEventListener("load", listener, true);
|
||||
sendAsyncMessage("Panorama:documentLoaded");
|
||||
}, true);
|
||||
},
|
||||
};
|
||||
|
||||
// add message listeners
|
||||
addMessageListener("Panorama:isDocumentLoaded", WindowMessageHandler.isDocumentLoaded);
|
||||
addMessageListener("Panorama:isImageDocument", WindowMessageHandler.isImageDocument);
|
||||
addMessageListener("Panorama:waitForDocumentLoad", WindowMessageHandler.waitForDocumentLoad);
|
|
@ -1,269 +0,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/. */
|
||||
|
||||
// **********
|
||||
// Title: drag.js
|
||||
|
||||
// ----------
|
||||
// Variable: drag
|
||||
// The Drag that's currently in process.
|
||||
var drag = {
|
||||
info: null,
|
||||
zIndex: 100,
|
||||
lastMoveTime: 0
|
||||
};
|
||||
|
||||
//----------
|
||||
//Variable: resize
|
||||
//The resize (actually a Drag) that is currently in process
|
||||
var resize = {
|
||||
info: null,
|
||||
lastMoveTime: 0
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Drag (formerly DragInfo)
|
||||
// Helper class for dragging <Item>s
|
||||
//
|
||||
// ----------
|
||||
// Constructor: Drag
|
||||
// Called to create a Drag in response to an <Item> draggable "start" event.
|
||||
// Note that it is also used partially during <Item>'s resizable method as well.
|
||||
//
|
||||
// Parameters:
|
||||
// item - The <Item> being dragged
|
||||
// event - The DOM event that kicks off the drag
|
||||
function Drag(item, event) {
|
||||
Utils.assert(item && (item.isAnItem || item.isAFauxItem),
|
||||
'must be an item, or at least a faux item');
|
||||
|
||||
this.item = item;
|
||||
this.el = item.container;
|
||||
this.$el = iQ(this.el);
|
||||
this.parent = this.item.parent;
|
||||
this.startPosition = new Point(event.clientX, event.clientY);
|
||||
this.startTime = Date.now();
|
||||
|
||||
this.item.isDragging = true;
|
||||
this.item.setZ(999999);
|
||||
|
||||
this.safeWindowBounds = Items.getSafeWindowBounds();
|
||||
|
||||
Trenches.activateOthersTrenches(this.el);
|
||||
};
|
||||
|
||||
Drag.prototype = {
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [Drag (item)] for debug use
|
||||
toString: function Drag_toString() {
|
||||
return "[Drag (" + this.item + ")]";
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: snapBounds
|
||||
// Adjusts the given bounds according to the currently active trenches. Used by <Drag.snap>
|
||||
//
|
||||
// Parameters:
|
||||
// bounds - (<Rect>) bounds
|
||||
// stationaryCorner - which corner is stationary? by default, the top left in LTR mode,
|
||||
// and top right in RTL mode.
|
||||
// "topleft", "bottomleft", "topright", "bottomright"
|
||||
// assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
|
||||
// keepProportional - (boolean) if assumeConstantSize is false, whether we should resize
|
||||
// proportionally or not
|
||||
// checkItemStatus - (boolean) make sure this is a valid item which should be snapped
|
||||
snapBounds: function Drag_snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, checkItemStatus) {
|
||||
if (!stationaryCorner)
|
||||
stationaryCorner = UI.rtl ? 'topright' : 'topleft';
|
||||
var update = false; // need to update
|
||||
var updateX = false;
|
||||
var updateY = false;
|
||||
var newRect;
|
||||
var snappedTrenches = {};
|
||||
|
||||
// OH SNAP!
|
||||
|
||||
// if we aren't holding down the meta key or have trenches disabled...
|
||||
if (!Keys.meta && !Trenches.disabled) {
|
||||
// snappable = true if we aren't a tab on top of something else, and
|
||||
// there's no active drop site...
|
||||
let snappable = !(this.item.isATabItem &&
|
||||
this.item.overlapsWithOtherItems()) &&
|
||||
!iQ(".acceptsDrop").length;
|
||||
if (!checkItemStatus || snappable) {
|
||||
newRect = Trenches.snap(bounds, stationaryCorner, assumeConstantSize,
|
||||
keepProportional);
|
||||
if (newRect) { // might be false if no changes were made
|
||||
update = true;
|
||||
snappedTrenches = newRect.snappedTrenches || {};
|
||||
bounds = newRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the bounds are in the window.
|
||||
newRect = this.snapToEdge(bounds, stationaryCorner, assumeConstantSize,
|
||||
keepProportional);
|
||||
if (newRect) {
|
||||
update = true;
|
||||
bounds = newRect;
|
||||
Utils.extend(snappedTrenches, newRect.snappedTrenches);
|
||||
}
|
||||
|
||||
Trenches.hideGuides();
|
||||
for (var edge in snappedTrenches) {
|
||||
var trench = snappedTrenches[edge];
|
||||
if (typeof trench == 'object') {
|
||||
trench.showGuide = true;
|
||||
trench.show();
|
||||
}
|
||||
}
|
||||
|
||||
return update ? bounds : false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: snap
|
||||
// Called when a drag or mousemove occurs. Set the bounds based on the mouse move first, then
|
||||
// call snap and it will adjust the item's bounds if appropriate. Also triggers the display of
|
||||
// trenches that it snapped to.
|
||||
//
|
||||
// Parameters:
|
||||
// stationaryCorner - which corner is stationary? by default, the top left in LTR mode,
|
||||
// and top right in RTL mode.
|
||||
// "topleft", "bottomleft", "topright", "bottomright"
|
||||
// assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
|
||||
// keepProportional - (boolean) if assumeConstantSize is false, whether we should resize
|
||||
// proportionally or not
|
||||
snap: function Drag_snap(stationaryCorner, assumeConstantSize, keepProportional) {
|
||||
var bounds = this.item.getBounds();
|
||||
bounds = this.snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, true);
|
||||
if (bounds) {
|
||||
this.item.setBounds(bounds, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// --------
|
||||
// Function: snapToEdge
|
||||
// Returns a version of the bounds snapped to the edge if it is close enough. If not,
|
||||
// returns false. If <Keys.meta> is true, this function will simply enforce the
|
||||
// window edges.
|
||||
//
|
||||
// Parameters:
|
||||
// rect - (<Rect>) current bounds of the object
|
||||
// stationaryCorner - which corner is stationary? by default, the top left in LTR mode,
|
||||
// and top right in RTL mode.
|
||||
// "topleft", "bottomleft", "topright", "bottomright"
|
||||
// assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
|
||||
// keepProportional - (boolean) if we are allowed to change the rect's size, whether the
|
||||
// dimensions should scaled proportionally or not.
|
||||
snapToEdge: function Drag_snapToEdge(rect, stationaryCorner, assumeConstantSize, keepProportional) {
|
||||
|
||||
var swb = this.safeWindowBounds;
|
||||
var update = false;
|
||||
var updateX = false;
|
||||
var updateY = false;
|
||||
var snappedTrenches = {};
|
||||
|
||||
var snapRadius = (Keys.meta ? 0 : Trenches.defaultRadius);
|
||||
if (rect.left < swb.left + snapRadius ) {
|
||||
if (stationaryCorner.indexOf('right') > -1 && !assumeConstantSize)
|
||||
rect.width = rect.right - swb.left;
|
||||
rect.left = swb.left;
|
||||
update = true;
|
||||
updateX = true;
|
||||
snappedTrenches.left = 'edge';
|
||||
}
|
||||
|
||||
if (rect.right > swb.right - snapRadius) {
|
||||
if (updateX || !assumeConstantSize) {
|
||||
var newWidth = swb.right - rect.left;
|
||||
if (keepProportional)
|
||||
rect.height = rect.height * newWidth / rect.width;
|
||||
rect.width = newWidth;
|
||||
update = true;
|
||||
} else if (!updateX || !Trenches.preferLeft) {
|
||||
rect.left = swb.right - rect.width;
|
||||
update = true;
|
||||
}
|
||||
snappedTrenches.right = 'edge';
|
||||
delete snappedTrenches.left;
|
||||
}
|
||||
if (rect.top < swb.top + snapRadius) {
|
||||
if (stationaryCorner.indexOf('bottom') > -1 && !assumeConstantSize)
|
||||
rect.height = rect.bottom - swb.top;
|
||||
rect.top = swb.top;
|
||||
update = true;
|
||||
updateY = true;
|
||||
snappedTrenches.top = 'edge';
|
||||
}
|
||||
if (rect.bottom > swb.bottom - snapRadius) {
|
||||
if (updateY || !assumeConstantSize) {
|
||||
var newHeight = swb.bottom - rect.top;
|
||||
if (keepProportional)
|
||||
rect.width = rect.width * newHeight / rect.height;
|
||||
rect.height = newHeight;
|
||||
update = true;
|
||||
} else if (!updateY || !Trenches.preferTop) {
|
||||
rect.top = swb.bottom - rect.height;
|
||||
update = true;
|
||||
}
|
||||
snappedTrenches.top = 'edge';
|
||||
delete snappedTrenches.bottom;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
rect.snappedTrenches = snappedTrenches;
|
||||
return rect;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: drag
|
||||
// Called in response to an <Item> draggable "drag" event.
|
||||
drag: function Drag_drag(event) {
|
||||
this.snap(UI.rtl ? 'topright' : 'topleft', true);
|
||||
|
||||
if (this.parent && this.parent.expanded) {
|
||||
var distance = this.startPosition.distance(new Point(event.clientX, event.clientY));
|
||||
if (distance > 100) {
|
||||
this.parent.remove(this.item);
|
||||
this.parent.collapse();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: stop
|
||||
// Called in response to an <Item> draggable "stop" event.
|
||||
//
|
||||
// Parameters:
|
||||
// immediately - bool for doing the pushAway immediately, without animation
|
||||
stop: function Drag_stop(immediately) {
|
||||
Trenches.hideGuides();
|
||||
this.item.isDragging = false;
|
||||
|
||||
if (this.parent && this.parent != this.item.parent)
|
||||
this.parent.closeIfEmpty();
|
||||
|
||||
if (this.parent && this.parent.expanded)
|
||||
this.parent.arrange();
|
||||
|
||||
if (this.item.parent)
|
||||
this.item.parent.arrange();
|
||||
|
||||
if (this.item.isAGroupItem) {
|
||||
this.item.setZ(drag.zIndex);
|
||||
drag.zIndex++;
|
||||
|
||||
this.item.pushAway(immediately);
|
||||
}
|
||||
|
||||
Trenches.disactivate();
|
||||
}
|
||||
};
|
|
@ -1,146 +0,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/. */
|
||||
|
||||
Components.utils.import('resource://gre/modules/PlacesUtils.jsm');
|
||||
|
||||
var FavIcons = {
|
||||
// Pref that controls whether to display site icons.
|
||||
PREF_CHROME_SITE_ICONS: "browser.chrome.site_icons",
|
||||
|
||||
// Pref that controls whether to display fav icons.
|
||||
PREF_CHROME_FAVICONS: "browser.chrome.favicons",
|
||||
|
||||
// Lazy getter for pref browser.chrome.site_icons.
|
||||
get _prefSiteIcons() {
|
||||
delete this._prefSiteIcons;
|
||||
this._prefSiteIcons = Services.prefs.getBoolPref(this.PREF_CHROME_SITE_ICONS);
|
||||
},
|
||||
|
||||
// Lazy getter for pref browser.chrome.favicons.
|
||||
get _prefFavicons() {
|
||||
delete this._prefFavicons;
|
||||
this._prefFavicons = Services.prefs.getBoolPref(this.PREF_CHROME_FAVICONS);
|
||||
},
|
||||
|
||||
get defaultFavicon() {
|
||||
return this._favIconService.defaultFavicon.spec;
|
||||
},
|
||||
|
||||
init: function FavIcons_init() {
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "_favIconService",
|
||||
"@mozilla.org/browser/favicon-service;1", "nsIFaviconService");
|
||||
|
||||
Services.prefs.addObserver(this.PREF_CHROME_SITE_ICONS, this, false);
|
||||
Services.prefs.addObserver(this.PREF_CHROME_FAVICONS, this, false);
|
||||
},
|
||||
|
||||
uninit: function FavIcons_uninit() {
|
||||
Services.prefs.removeObserver(this.PREF_CHROME_SITE_ICONS, this);
|
||||
Services.prefs.removeObserver(this.PREF_CHROME_FAVICONS, this);
|
||||
},
|
||||
|
||||
observe: function FavIcons_observe(subject, topic, data) {
|
||||
let value = Services.prefs.getBoolPref(data);
|
||||
|
||||
if (data == this.PREF_CHROME_SITE_ICONS)
|
||||
this._prefSiteIcons = value;
|
||||
else if (data == this.PREF_CHROME_FAVICONS)
|
||||
this._prefFavicons = value;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: getFavIconUrlForTab
|
||||
// Gets the "favicon link URI" for the given xul:tab, or null if unavailable.
|
||||
getFavIconUrlForTab: function FavIcons_getFavIconUrlForTab(tab, callback) {
|
||||
this._isImageDocument(tab, function (isImageDoc) {
|
||||
if (isImageDoc) {
|
||||
callback(tab.pinned ? tab.image : null);
|
||||
} else {
|
||||
this._getFavIconForNonImageDocument(tab, callback);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _getFavIconForNonImageDocument
|
||||
// Retrieves the favicon for a tab containing a non-image document.
|
||||
_getFavIconForNonImageDocument:
|
||||
function FavIcons_getFavIconForNonImageDocument(tab, callback) {
|
||||
|
||||
if (tab.image)
|
||||
this._getFavIconFromTabImage(tab, callback);
|
||||
else if (this._shouldLoadFavIcon(tab))
|
||||
this._getFavIconForHttpDocument(tab, callback);
|
||||
else
|
||||
callback(null);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _getFavIconFromTabImage
|
||||
// Retrieves the favicon for tab with a tab image.
|
||||
_getFavIconFromTabImage:
|
||||
function FavIcons_getFavIconFromTabImage(tab, callback) {
|
||||
|
||||
let tabImage = gBrowser.getIcon(tab);
|
||||
|
||||
// If the tab image's url starts with http(s), fetch icon from favicon
|
||||
// service via the moz-anno protocol.
|
||||
if (/^https?:/.test(tabImage)) {
|
||||
let tabImageURI = gWindow.makeURI(tabImage);
|
||||
tabImage = this._favIconService.getFaviconLinkForIcon(tabImageURI).spec;
|
||||
}
|
||||
|
||||
callback(tabImage);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _getFavIconForHttpDocument
|
||||
// Retrieves the favicon for tab containg a http(s) document.
|
||||
_getFavIconForHttpDocument:
|
||||
function FavIcons_getFavIconForHttpDocument(tab, callback) {
|
||||
|
||||
let {currentURI} = tab.linkedBrowser;
|
||||
this._favIconService.getFaviconURLForPage(currentURI, function (uri) {
|
||||
if (uri) {
|
||||
let icon = this._favIconService.getFaviconLinkForIcon(uri).spec;
|
||||
callback(icon);
|
||||
} else {
|
||||
callback(this.defaultFavicon);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _isImageDocument
|
||||
// Checks whether an image is loaded into the given tab.
|
||||
_isImageDocument: function UI__isImageDocument(tab, callback) {
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
let message = "Panorama:isImageDocument";
|
||||
|
||||
mm.addMessageListener(message, function onMessage(cx) {
|
||||
mm.removeMessageListener(cx.name, onMessage);
|
||||
callback(cx.json.isImageDocument);
|
||||
});
|
||||
|
||||
mm.sendAsyncMessage(message);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _shouldLoadFavIcon
|
||||
// Checks whether fav icon should be loaded for a given tab.
|
||||
_shouldLoadFavIcon: function FavIcons_shouldLoadFavIcon(tab) {
|
||||
// No need to load a favicon if the user doesn't want site or favicons.
|
||||
if (!this._prefSiteIcons || !this._prefFavicons)
|
||||
return false;
|
||||
|
||||
let uri = tab.linkedBrowser.currentURI;
|
||||
|
||||
// Stop here if we don't have a valid nsIURI.
|
||||
if (!uri || !(uri instanceof Ci.nsIURI))
|
||||
return false;
|
||||
|
||||
// Load favicons for http(s) pages only.
|
||||
return uri.schemeIs("http") || uri.schemeIs("https");
|
||||
}
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,765 +0,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/. */
|
||||
|
||||
// **********
|
||||
// Title: iq.js
|
||||
// Various helper functions, in the vein of jQuery.
|
||||
|
||||
// ----------
|
||||
// Function: iQ
|
||||
// Returns an iQClass object which represents an individual element or a group
|
||||
// of elements. It works pretty much like jQuery(), with a few exceptions,
|
||||
// most notably that you can't use strings with complex html,
|
||||
// just simple tags like '<div>'.
|
||||
function iQ(selector, context) {
|
||||
// The iQ object is actually just the init constructor 'enhanced'
|
||||
return new iQClass(selector, context);
|
||||
};
|
||||
|
||||
// A simple way to check for HTML strings or ID strings
|
||||
// (both of which we optimize for)
|
||||
var quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/;
|
||||
|
||||
// Match a standalone tag
|
||||
var rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
|
||||
|
||||
// ##########
|
||||
// Class: iQClass
|
||||
// The actual class of iQ result objects, representing an individual element
|
||||
// or a group of elements.
|
||||
//
|
||||
// ----------
|
||||
// Function: iQClass
|
||||
// You don't call this directly; this is what's called by iQ().
|
||||
function iQClass(selector, context) {
|
||||
|
||||
// Handle $(""), $(null), or $(undefined)
|
||||
if (!selector) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Handle $(DOMElement)
|
||||
if (selector.nodeType) {
|
||||
this.context = selector;
|
||||
this[0] = selector;
|
||||
this.length = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
// The body element only exists once, optimize finding it
|
||||
if (selector === "body" && !context) {
|
||||
this.context = document;
|
||||
this[0] = document.body;
|
||||
this.selector = "body";
|
||||
this.length = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Handle HTML strings
|
||||
if (typeof selector === "string") {
|
||||
// Are we dealing with HTML string or an ID?
|
||||
|
||||
let match = quickExpr.exec(selector);
|
||||
|
||||
// Verify a match, and that no context was specified for #id
|
||||
if (match && (match[1] || !context)) {
|
||||
|
||||
// HANDLE $(html) -> $(array)
|
||||
if (match[1]) {
|
||||
let doc = (context ? context.ownerDocument || context : document);
|
||||
|
||||
// If a single string is passed in and it's a single tag
|
||||
// just do a createElement and skip the rest
|
||||
let ret = rsingleTag.exec(selector);
|
||||
|
||||
if (ret) {
|
||||
if (Utils.isPlainObject(context)) {
|
||||
Utils.assert(false, 'does not support HTML creation with context');
|
||||
} else {
|
||||
selector = [doc.createElement(ret[1])];
|
||||
}
|
||||
|
||||
} else {
|
||||
Utils.assert(false, 'does not support complex HTML creation');
|
||||
}
|
||||
|
||||
return Utils.merge(this, selector);
|
||||
|
||||
// HANDLE $("#id")
|
||||
} else {
|
||||
let elem = document.getElementById(match[2]);
|
||||
|
||||
if (elem) {
|
||||
this.length = 1;
|
||||
this[0] = elem;
|
||||
}
|
||||
|
||||
this.context = document;
|
||||
this.selector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
// HANDLE $("TAG")
|
||||
} else if (!context && /^\w+$/.test(selector)) {
|
||||
this.selector = selector;
|
||||
this.context = document;
|
||||
selector = document.getElementsByTagName(selector);
|
||||
return Utils.merge(this, selector);
|
||||
|
||||
// HANDLE $(expr, $(...))
|
||||
} else if (!context || context.iq) {
|
||||
return (context || iQ(document)).find(selector);
|
||||
|
||||
// HANDLE $(expr, context)
|
||||
// (which is just equivalent to: $(context).find(expr)
|
||||
} else {
|
||||
return iQ(context).find(selector);
|
||||
}
|
||||
|
||||
// HANDLE $(function)
|
||||
// Shortcut for document ready
|
||||
} else if (typeof selector == "function") {
|
||||
Utils.log('iQ does not support ready functions');
|
||||
return null;
|
||||
}
|
||||
|
||||
if ("selector" in selector) {
|
||||
this.selector = selector.selector;
|
||||
this.context = selector.context;
|
||||
}
|
||||
|
||||
let ret = this || [];
|
||||
if (selector != null) {
|
||||
// The window, strings (and functions) also have 'length'
|
||||
if (selector.length == null || typeof selector == "string" || selector.setInterval) {
|
||||
Array.push(ret, selector);
|
||||
} else {
|
||||
Utils.merge(ret, selector);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
iQClass.prototype = {
|
||||
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [iQ...] for debug use
|
||||
toString: function iQClass_toString() {
|
||||
if (this.length > 1) {
|
||||
if (this.selector)
|
||||
return "[iQ (" + this.selector + ")]";
|
||||
else
|
||||
return "[iQ multi-object]";
|
||||
}
|
||||
|
||||
if (this.length == 1)
|
||||
return "[iQ (" + this[0].toString() + ")]";
|
||||
|
||||
return "[iQ non-object]";
|
||||
},
|
||||
|
||||
// Start with an empty selector
|
||||
selector: "",
|
||||
|
||||
// The default length of a iQ object is 0
|
||||
length: 0,
|
||||
|
||||
// ----------
|
||||
// Function: each
|
||||
// Execute a callback for every element in the matched set.
|
||||
each: function iQClass_each(callback) {
|
||||
if (typeof callback != "function") {
|
||||
Utils.assert(false, "each's argument must be a function");
|
||||
return null;
|
||||
}
|
||||
for (let i = 0; this[i] != null && callback(this[i]) !== false; i++) {}
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: addClass
|
||||
// Adds the given class(es) to the receiver.
|
||||
addClass: function iQClass_addClass(value) {
|
||||
Utils.assertThrow(typeof value == "string" && value,
|
||||
'requires a valid string argument');
|
||||
|
||||
let length = this.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let elem = this[i];
|
||||
if (elem.nodeType === 1) {
|
||||
value.split(/\s+/).forEach(function(className) {
|
||||
elem.classList.add(className);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: removeClass
|
||||
// Removes the given class(es) from the receiver.
|
||||
removeClass: function iQClass_removeClass(value) {
|
||||
if (typeof value != "string" || !value) {
|
||||
Utils.assert(false, 'does not support function argument');
|
||||
return null;
|
||||
}
|
||||
|
||||
let length = this.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let elem = this[i];
|
||||
if (elem.nodeType === 1 && elem.className) {
|
||||
value.split(/\s+/).forEach(function(className) {
|
||||
elem.classList.remove(className);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: hasClass
|
||||
// Returns true is the receiver has the given css class.
|
||||
hasClass: function iQClass_hasClass(singleClassName) {
|
||||
let length = this.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (this[i].classList.contains(singleClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: find
|
||||
// Searches the receiver and its children, returning a new iQ object with
|
||||
// elements that match the given selector.
|
||||
find: function iQClass_find(selector) {
|
||||
let ret = [];
|
||||
let length = 0;
|
||||
|
||||
let l = this.length;
|
||||
for (let i = 0; i < l; i++) {
|
||||
length = ret.length;
|
||||
try {
|
||||
Utils.merge(ret, this[i].querySelectorAll(selector));
|
||||
} catch(e) {
|
||||
Utils.log('iQ.find error (bad selector)', e);
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
// Make sure that the results are unique
|
||||
for (let n = length; n < ret.length; n++) {
|
||||
for (let r = 0; r < length; r++) {
|
||||
if (ret[r] === ret[n]) {
|
||||
ret.splice(n--, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iQ(ret);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: contains
|
||||
// Check to see if a given DOM node descends from the receiver.
|
||||
contains: function iQClass_contains(selector) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
|
||||
// fast path when querySelector() can be used
|
||||
if ('string' == typeof selector)
|
||||
return null != this[0].querySelector(selector);
|
||||
|
||||
let object = iQ(selector);
|
||||
Utils.assert(object.length <= 1, 'does not yet support multi-objects');
|
||||
|
||||
let elem = object[0];
|
||||
if (!elem || !elem.parentNode)
|
||||
return false;
|
||||
|
||||
do {
|
||||
elem = elem.parentNode;
|
||||
} while (elem && this[0] != elem);
|
||||
|
||||
return this[0] == elem;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: remove
|
||||
// Removes the receiver from the DOM.
|
||||
remove: function iQClass_remove(options) {
|
||||
if (!options || !options.preserveEventHandlers)
|
||||
this.unbindAll();
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
if (elem.parentNode) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: empty
|
||||
// Removes all of the reciever's children and HTML content from the DOM.
|
||||
empty: function iQClass_empty() {
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
while (elem.firstChild) {
|
||||
iQ(elem.firstChild).unbindAll();
|
||||
elem.removeChild(elem.firstChild);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: width
|
||||
// Returns the width of the receiver, including padding and border.
|
||||
width: function iQClass_width() {
|
||||
return Math.floor(this[0].offsetWidth);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: height
|
||||
// Returns the height of the receiver, including padding and border.
|
||||
height: function iQClass_height() {
|
||||
return Math.floor(this[0].offsetHeight);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: position
|
||||
// Returns an object with the receiver's position in left and top
|
||||
// properties.
|
||||
position: function iQClass_position() {
|
||||
let bounds = this.bounds();
|
||||
return new Point(bounds.left, bounds.top);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: bounds
|
||||
// Returns a <Rect> with the receiver's bounds.
|
||||
bounds: function iQClass_bounds() {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
let rect = this[0].getBoundingClientRect();
|
||||
return new Rect(Math.floor(rect.left), Math.floor(rect.top),
|
||||
Math.floor(rect.width), Math.floor(rect.height));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: data
|
||||
// Pass in both key and value to attach some data to the receiver;
|
||||
// pass in just key to retrieve it.
|
||||
data: function iQClass_data(key, value) {
|
||||
let data = null;
|
||||
if (value === undefined) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
data = this[0].iQData;
|
||||
if (data)
|
||||
return data[key];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
data = elem.iQData;
|
||||
|
||||
if (!data)
|
||||
data = elem.iQData = {};
|
||||
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: html
|
||||
// Given a value, sets the receiver's innerHTML to it; otherwise returns
|
||||
// what's already there.
|
||||
html: function iQClass_html(value) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
if (value === undefined)
|
||||
return this[0].innerHTML;
|
||||
|
||||
this[0].innerHTML = value;
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: text
|
||||
// Given a value, sets the receiver's textContent to it; otherwise returns
|
||||
// what's already there.
|
||||
text: function iQClass_text(value) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
if (value === undefined) {
|
||||
return this[0].textContent;
|
||||
}
|
||||
|
||||
return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: val
|
||||
// Given a value, sets the receiver's value to it; otherwise returns what's already there.
|
||||
val: function iQClass_val(value) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
if (value === undefined) {
|
||||
return this[0].value;
|
||||
}
|
||||
|
||||
this[0].value = value;
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: appendTo
|
||||
// Appends the receiver to the result of iQ(selector).
|
||||
appendTo: function iQClass_appendTo(selector) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
iQ(selector).append(this);
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: append
|
||||
// Appends the result of iQ(selector) to the receiver.
|
||||
append: function iQClass_append(selector) {
|
||||
let object = iQ(selector);
|
||||
Utils.assert(object.length == 1 && this.length == 1,
|
||||
'does not yet support multi-objects (or null objects)');
|
||||
this[0].appendChild(object[0]);
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: attr
|
||||
// Sets or gets an attribute on the element(s).
|
||||
attr: function iQClass_attr(key, value) {
|
||||
Utils.assert(typeof key === 'string', 'string key');
|
||||
if (value === undefined) {
|
||||
Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
|
||||
return this[0].getAttribute(key);
|
||||
}
|
||||
|
||||
for (let i = 0; this[i] != null; i++)
|
||||
this[i].setAttribute(key, value);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: css
|
||||
// Sets or gets CSS properties on the receiver. When setting certain numerical properties,
|
||||
// will automatically add "px". A property can be removed by setting it to null.
|
||||
//
|
||||
// Possible call patterns:
|
||||
// a: object, b: undefined - sets with properties from a
|
||||
// a: string, b: undefined - gets property specified by a
|
||||
// a: string, b: string/number - sets property specified by a to b
|
||||
css: function iQClass_css(a, b) {
|
||||
let properties = null;
|
||||
|
||||
if (typeof a === 'string') {
|
||||
let key = a;
|
||||
if (b === undefined) {
|
||||
Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
|
||||
|
||||
return window.getComputedStyle(this[0], null).getPropertyValue(key);
|
||||
}
|
||||
properties = {};
|
||||
properties[key] = b;
|
||||
} else if (a instanceof Rect) {
|
||||
properties = {
|
||||
left: a.left,
|
||||
top: a.top,
|
||||
width: a.width,
|
||||
height: a.height
|
||||
};
|
||||
} else {
|
||||
properties = a;
|
||||
}
|
||||
|
||||
let pixels = {
|
||||
'left': true,
|
||||
'top': true,
|
||||
'right': true,
|
||||
'bottom': true,
|
||||
'width': true,
|
||||
'height': true
|
||||
};
|
||||
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
for (let key in properties) {
|
||||
let value = properties[key];
|
||||
|
||||
if (pixels[key] && typeof value != 'string')
|
||||
value += 'px';
|
||||
|
||||
if (value == null) {
|
||||
elem.style.removeProperty(key);
|
||||
} else if (key.indexOf('-') != -1)
|
||||
elem.style.setProperty(key, value, '');
|
||||
else
|
||||
elem.style[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: animate
|
||||
// Uses CSS transitions to animate the element.
|
||||
//
|
||||
// Parameters:
|
||||
// css - an object map of the CSS properties to change
|
||||
// options - an object with various properites (see below)
|
||||
//
|
||||
// Possible "options" properties:
|
||||
// duration - how long to animate, in milliseconds
|
||||
// easing - easing function to use. Possibilities include
|
||||
// "tabviewBounce", "easeInQuad". Default is "ease".
|
||||
// complete - function to call once the animation is done, takes nothing
|
||||
// in, but "this" is set to the element that was animated.
|
||||
animate: function iQClass_animate(css, options) {
|
||||
Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
let easings = {
|
||||
tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.29)",
|
||||
easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care
|
||||
fast: 'cubic-bezier(0.7,0,1,1)'
|
||||
};
|
||||
|
||||
let duration = (options.duration || 400);
|
||||
let easing = (easings[options.easing] || 'ease');
|
||||
|
||||
if (css instanceof Rect) {
|
||||
css = {
|
||||
left: css.left,
|
||||
top: css.top,
|
||||
width: css.width,
|
||||
height: css.height
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// The latest versions of Firefox do not animate from a non-explicitly
|
||||
// set css properties. So for each element to be animated, go through
|
||||
// and explicitly define 'em.
|
||||
let rupper = /([A-Z])/g;
|
||||
this.each(function(elem) {
|
||||
let cStyle = window.getComputedStyle(elem, null);
|
||||
for (let prop in css) {
|
||||
prop = prop.replace(rupper, "-$1").toLowerCase();
|
||||
iQ(elem).css(prop, cStyle.getPropertyValue(prop));
|
||||
}
|
||||
});
|
||||
|
||||
this.css({
|
||||
'transition-property': Object.keys(css).join(", "),
|
||||
'transition-duration': (duration / 1000) + 's',
|
||||
'transition-timing-function': easing
|
||||
});
|
||||
|
||||
this.css(css);
|
||||
|
||||
let self = this;
|
||||
setTimeout(function() {
|
||||
self.css({
|
||||
'transition-property': 'none',
|
||||
'transition-duration': '',
|
||||
'transition-timing-function': ''
|
||||
});
|
||||
|
||||
if (typeof options.complete == "function")
|
||||
options.complete.apply(self);
|
||||
}, duration);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: fadeOut
|
||||
// Animates the receiver to full transparency. Calls callback on completion.
|
||||
fadeOut: function iQClass_fadeOut(callback) {
|
||||
Utils.assert(typeof callback == "function" || callback === undefined,
|
||||
'does not yet support duration');
|
||||
|
||||
this.animate({
|
||||
opacity: 0
|
||||
}, {
|
||||
duration: 400,
|
||||
complete: function() {
|
||||
iQ(this).css({display: 'none'});
|
||||
if (typeof callback == "function")
|
||||
callback.apply(this);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: fadeIn
|
||||
// Animates the receiver to full opacity.
|
||||
fadeIn: function iQClass_fadeIn() {
|
||||
this.css({display: ''});
|
||||
this.animate({
|
||||
opacity: 1
|
||||
}, {
|
||||
duration: 400
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: hide
|
||||
// Hides the receiver.
|
||||
hide: function iQClass_hide() {
|
||||
this.css({display: 'none', opacity: 0});
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: show
|
||||
// Shows the receiver.
|
||||
show: function iQClass_show() {
|
||||
this.css({display: '', opacity: 1});
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: bind
|
||||
// Binds the given function to the given event type. Also wraps the function
|
||||
// in a try/catch block that does a Utils.log on any errors.
|
||||
bind: function iQClass_bind(type, func) {
|
||||
let handler = function(event) {
|
||||
return func.apply(this, [event]);
|
||||
};
|
||||
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
if (!elem.iQEventData)
|
||||
elem.iQEventData = {};
|
||||
|
||||
if (!elem.iQEventData[type])
|
||||
elem.iQEventData[type] = [];
|
||||
|
||||
elem.iQEventData[type].push({
|
||||
original: func,
|
||||
modified: handler
|
||||
});
|
||||
|
||||
elem.addEventListener(type, handler, false);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: one
|
||||
// Binds the given function to the given event type, but only for one call;
|
||||
// automatically unbinds after the event fires once.
|
||||
one: function iQClass_one(type, func) {
|
||||
Utils.assert(typeof func == "function", 'does not support eventData argument');
|
||||
|
||||
let handler = function(e) {
|
||||
iQ(this).unbind(type, handler);
|
||||
return func.apply(this, [e]);
|
||||
};
|
||||
|
||||
return this.bind(type, handler);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: unbind
|
||||
// Unbinds the given function from the given event type.
|
||||
unbind: function iQClass_unbind(type, func) {
|
||||
Utils.assert(typeof func == "function", 'Must provide a function');
|
||||
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
let handler = func;
|
||||
if (elem.iQEventData && elem.iQEventData[type]) {
|
||||
let count = elem.iQEventData[type].length;
|
||||
for (let a = 0; a < count; a++) {
|
||||
let pair = elem.iQEventData[type][a];
|
||||
if (pair.original == func) {
|
||||
handler = pair.modified;
|
||||
elem.iQEventData[type].splice(a, 1);
|
||||
if (!elem.iQEventData[type].length) {
|
||||
delete elem.iQEventData[type];
|
||||
if (!Object.keys(elem.iQEventData).length)
|
||||
delete elem.iQEventData;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elem.removeEventListener(type, handler, false);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: unbindAll
|
||||
// Unbinds all event handlers.
|
||||
unbindAll: function iQClass_unbindAll() {
|
||||
for (let i = 0; this[i] != null; i++) {
|
||||
let elem = this[i];
|
||||
|
||||
for (let j = 0; j < elem.childElementCount; j++)
|
||||
iQ(elem.children[j]).unbindAll();
|
||||
|
||||
if (!elem.iQEventData)
|
||||
continue;
|
||||
|
||||
Object.keys(elem.iQEventData).forEach(function (type) {
|
||||
while (elem.iQEventData && elem.iQEventData[type])
|
||||
this.unbind(type, elem.iQEventData[type][0].original);
|
||||
}, this);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
// ----------
|
||||
// Create various event aliases
|
||||
var events = [
|
||||
'keyup',
|
||||
'keydown',
|
||||
'keypress',
|
||||
'mouseup',
|
||||
'mousedown',
|
||||
'mouseover',
|
||||
'mouseout',
|
||||
'mousemove',
|
||||
'click',
|
||||
'dblclick',
|
||||
'resize',
|
||||
'change',
|
||||
'blur',
|
||||
'focus'
|
||||
];
|
||||
|
||||
events.forEach(function(event) {
|
||||
iQClass.prototype[event] = function(func) {
|
||||
return this.bind(event, func);
|
||||
};
|
||||
});
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,9 +0,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/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/tabview.css
|
||||
* content/browser/tabview.js
|
||||
content/browser/tabview.html
|
||||
content/browser/tabview-content.js (content.js)
|
|
@ -1,831 +0,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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// **********
|
||||
// Title: utils.js
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Point", "Rect", "Range", "Subscribable", "Utils", "MRUList"];
|
||||
|
||||
// #########
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// ##########
|
||||
// Class: Point
|
||||
// A simple point.
|
||||
//
|
||||
// Constructor: Point
|
||||
// If a is a Point, creates a copy of it. Otherwise, expects a to be x,
|
||||
// and creates a Point with it along with y. If either a or y are omitted,
|
||||
// 0 is used in their place.
|
||||
this.Point = function Point(a, y) {
|
||||
if (Utils.isPoint(a)) {
|
||||
this.x = a.x;
|
||||
this.y = a.y;
|
||||
} else {
|
||||
this.x = (Utils.isNumber(a) ? a : 0);
|
||||
this.y = (Utils.isNumber(y) ? y : 0);
|
||||
}
|
||||
};
|
||||
|
||||
Point.prototype = {
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [Point (x,y)] for debug use
|
||||
toString: function Point_toString() {
|
||||
return "[Point (" + this.x + "," + this.y + ")]";
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: distance
|
||||
// Returns the distance from this point to the given <Point>.
|
||||
distance: function Point_distance(point) {
|
||||
var ax = this.x - point.x;
|
||||
var ay = this.y - point.y;
|
||||
return Math.sqrt((ax * ax) + (ay * ay));
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Rect
|
||||
// A simple rectangle. Note that in addition to the left and width, it also has
|
||||
// a right property; changing one affects the others appropriately. Same for the
|
||||
// vertical properties.
|
||||
//
|
||||
// Constructor: Rect
|
||||
// If a is a Rect, creates a copy of it. Otherwise, expects a to be left,
|
||||
// and creates a Rect with it along with top, width, and height.
|
||||
this.Rect = function Rect(a, top, width, height) {
|
||||
// Note: perhaps 'a' should really be called 'rectOrLeft'
|
||||
if (Utils.isRect(a)) {
|
||||
this.left = a.left;
|
||||
this.top = a.top;
|
||||
this.width = a.width;
|
||||
this.height = a.height;
|
||||
} else {
|
||||
this.left = a;
|
||||
this.top = top;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
};
|
||||
|
||||
Rect.prototype = {
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [Rect (left,top,width,height)] for debug use
|
||||
toString: function Rect_toString() {
|
||||
return "[Rect (" + this.left + "," + this.top + "," +
|
||||
this.width + "," + this.height + ")]";
|
||||
},
|
||||
|
||||
get right() {
|
||||
return this.left + this.width;
|
||||
},
|
||||
set right(value) {
|
||||
this.width = value - this.left;
|
||||
},
|
||||
|
||||
get bottom() {
|
||||
return this.top + this.height;
|
||||
},
|
||||
set bottom(value) {
|
||||
this.height = value - this.top;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Variable: xRange
|
||||
// Gives you a new <Range> for the horizontal dimension.
|
||||
get xRange() {
|
||||
return new Range(this.left, this.right);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Variable: yRange
|
||||
// Gives you a new <Range> for the vertical dimension.
|
||||
get yRange() {
|
||||
return new Range(this.top, this.bottom);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: intersects
|
||||
// Returns true if this rectangle intersects the given <Rect>.
|
||||
intersects: function Rect_intersects(rect) {
|
||||
return (rect.right > this.left &&
|
||||
rect.left < this.right &&
|
||||
rect.bottom > this.top &&
|
||||
rect.top < this.bottom);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: intersection
|
||||
// Returns a new <Rect> with the intersection of this rectangle and the give <Rect>,
|
||||
// or null if they don't intersect.
|
||||
intersection: function Rect_intersection(rect) {
|
||||
var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0);
|
||||
box.right = Math.min(rect.right, this.right);
|
||||
box.bottom = Math.min(rect.bottom, this.bottom);
|
||||
if (box.width > 0 && box.height > 0)
|
||||
return box;
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: contains
|
||||
// Returns a boolean denoting if the <Rect> or <Point> is contained inside
|
||||
// this rectangle.
|
||||
//
|
||||
// Parameters
|
||||
// - A <Rect> or a <Point>
|
||||
contains: function Rect_contains(a) {
|
||||
if (Utils.isPoint(a))
|
||||
return (a.x > this.left &&
|
||||
a.x < this.right &&
|
||||
a.y > this.top &&
|
||||
a.y < this.bottom);
|
||||
|
||||
return (a.left >= this.left &&
|
||||
a.right <= this.right &&
|
||||
a.top >= this.top &&
|
||||
a.bottom <= this.bottom);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: center
|
||||
// Returns a new <Point> with the center location of this rectangle.
|
||||
center: function Rect_center() {
|
||||
return new Point(this.left + (this.width / 2), this.top + (this.height / 2));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: size
|
||||
// Returns a new <Point> with the dimensions of this rectangle.
|
||||
size: function Rect_size() {
|
||||
return new Point(this.width, this.height);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: position
|
||||
// Returns a new <Point> with the top left of this rectangle.
|
||||
position: function Rect_position() {
|
||||
return new Point(this.left, this.top);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: area
|
||||
// Returns the area of this rectangle.
|
||||
area: function Rect_area() {
|
||||
return this.width * this.height;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: inset
|
||||
// Makes the rect smaller (if the arguments are positive) as if a margin is added all around
|
||||
// the initial rect, with the margin widths (symmetric) being specified by the arguments.
|
||||
//
|
||||
// Paramaters
|
||||
// - A <Point> or two arguments: x and y
|
||||
inset: function Rect_inset(a, b) {
|
||||
if (Utils.isPoint(a)) {
|
||||
b = a.y;
|
||||
a = a.x;
|
||||
}
|
||||
|
||||
this.left += a;
|
||||
this.width -= a * 2;
|
||||
this.top += b;
|
||||
this.height -= b * 2;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: offset
|
||||
// Moves (translates) the rect by the given vector.
|
||||
//
|
||||
// Paramaters
|
||||
// - A <Point> or two arguments: x and y
|
||||
offset: function Rect_offset(a, b) {
|
||||
if (Utils.isPoint(a)) {
|
||||
this.left += a.x;
|
||||
this.top += a.y;
|
||||
} else {
|
||||
this.left += a;
|
||||
this.top += b;
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: equals
|
||||
// Returns true if this rectangle is identical to the given <Rect>.
|
||||
equals: function Rect_equals(rect) {
|
||||
return (rect.left == this.left &&
|
||||
rect.top == this.top &&
|
||||
rect.width == this.width &&
|
||||
rect.height == this.height);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: union
|
||||
// Returns a new <Rect> with the union of this rectangle and the given <Rect>.
|
||||
union: function Rect_union(a) {
|
||||
var newLeft = Math.min(a.left, this.left);
|
||||
var newTop = Math.min(a.top, this.top);
|
||||
var newWidth = Math.max(a.right, this.right) - newLeft;
|
||||
var newHeight = Math.max(a.bottom, this.bottom) - newTop;
|
||||
var newRect = new Rect(newLeft, newTop, newWidth, newHeight);
|
||||
|
||||
return newRect;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: copy
|
||||
// Copies the values of the given <Rect> into this rectangle.
|
||||
copy: function Rect_copy(a) {
|
||||
this.left = a.left;
|
||||
this.top = a.top;
|
||||
this.width = a.width;
|
||||
this.height = a.height;
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Range
|
||||
// A physical interval, with a min and max.
|
||||
//
|
||||
// Constructor: Range
|
||||
// Creates a Range with the given min and max
|
||||
this.Range = function Range(min, max) {
|
||||
if (Utils.isRange(min) && !max) { // if the one variable given is a range, copy it.
|
||||
this.min = min.min;
|
||||
this.max = min.max;
|
||||
} else {
|
||||
this.min = min || 0;
|
||||
this.max = max || 0;
|
||||
}
|
||||
};
|
||||
|
||||
Range.prototype = {
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [Range (min,max)] for debug use
|
||||
toString: function Range_toString() {
|
||||
return "[Range (" + this.min + "," + this.max + ")]";
|
||||
},
|
||||
|
||||
// Variable: extent
|
||||
// Equivalent to max-min
|
||||
get extent() {
|
||||
return (this.max - this.min);
|
||||
},
|
||||
|
||||
set extent(extent) {
|
||||
this.max = extent - this.min;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: contains
|
||||
// Whether the <Range> contains the given <Range> or value or not.
|
||||
//
|
||||
// Parameters
|
||||
// - a number or <Range>
|
||||
contains: function Range_contains(value) {
|
||||
if (Utils.isNumber(value))
|
||||
return value >= this.min && value <= this.max;
|
||||
if (Utils.isRange(value))
|
||||
return value.min >= this.min && value.max <= this.max;
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: overlaps
|
||||
// Whether the <Range> overlaps with the given <Range> value or not.
|
||||
//
|
||||
// Parameters
|
||||
// - a number or <Range>
|
||||
overlaps: function Range_overlaps(value) {
|
||||
if (Utils.isNumber(value))
|
||||
return this.contains(value);
|
||||
if (Utils.isRange(value))
|
||||
return !(value.max < this.min || this.max < value.min);
|
||||
return false;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: proportion
|
||||
// Maps the given value to the range [0,1], so that it returns 0 if the value is <= the min,
|
||||
// returns 1 if the value >= the max, and returns an interpolated "proportion" in (min, max).
|
||||
//
|
||||
// Parameters
|
||||
// - a number
|
||||
// - (bool) smooth? If true, a smooth tanh-based function will be used instead of the linear.
|
||||
proportion: function Range_proportion(value, smooth) {
|
||||
if (value <= this.min)
|
||||
return 0;
|
||||
if (this.max <= value)
|
||||
return 1;
|
||||
|
||||
var proportion = (value - this.min) / this.extent;
|
||||
|
||||
if (smooth) {
|
||||
// The ease function ".5+.5*Math.tanh(4*x-2)" is a pretty
|
||||
// little graph. It goes from near 0 at x=0 to near 1 at x=1
|
||||
// smoothly and beautifully.
|
||||
// http://www.wolframalpha.com/input/?i=.5+%2B+.5+*+tanh%28%284+*+x%29+-+2%29
|
||||
let tanh = function tanh(x) {
|
||||
var e = Math.exp(x);
|
||||
return (e - 1/e) / (e + 1/e);
|
||||
};
|
||||
|
||||
return .5 - .5 * tanh(2 - 4 * proportion);
|
||||
}
|
||||
|
||||
return proportion;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: scale
|
||||
// Takes the given value in [0,1] and maps it to the associated value on the Range.
|
||||
//
|
||||
// Parameters
|
||||
// - a number in [0,1]
|
||||
scale: function Range_scale(value) {
|
||||
if (value > 1)
|
||||
value = 1;
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
return this.min + this.extent * value;
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Subscribable
|
||||
// A mix-in for allowing objects to collect subscribers for custom events.
|
||||
this.Subscribable = function Subscribable() {
|
||||
this.subscribers = null;
|
||||
};
|
||||
|
||||
Subscribable.prototype = {
|
||||
// ----------
|
||||
// Function: addSubscriber
|
||||
// The given callback will be called when the Subscribable fires the given event.
|
||||
addSubscriber: function Subscribable_addSubscriber(eventName, callback) {
|
||||
try {
|
||||
Utils.assertThrow(typeof callback == "function", "callback must be a function");
|
||||
Utils.assertThrow(eventName && typeof eventName == "string",
|
||||
"eventName must be a non-empty string");
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.subscribers)
|
||||
this.subscribers = {};
|
||||
|
||||
if (!this.subscribers[eventName])
|
||||
this.subscribers[eventName] = [];
|
||||
|
||||
let subscribers = this.subscribers[eventName];
|
||||
if (subscribers.indexOf(callback) == -1)
|
||||
subscribers.push(callback);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: removeSubscriber
|
||||
// Removes the subscriber associated with the event for the given callback.
|
||||
removeSubscriber: function Subscribable_removeSubscriber(eventName, callback) {
|
||||
try {
|
||||
Utils.assertThrow(typeof callback == "function", "callback must be a function");
|
||||
Utils.assertThrow(eventName && typeof eventName == "string",
|
||||
"eventName must be a non-empty string");
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.subscribers || !this.subscribers[eventName])
|
||||
return;
|
||||
|
||||
let subscribers = this.subscribers[eventName];
|
||||
let index = subscribers.indexOf(callback);
|
||||
|
||||
if (index > -1)
|
||||
subscribers.splice(index, 1);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _sendToSubscribers
|
||||
// Internal routine. Used by the Subscribable to fire events.
|
||||
_sendToSubscribers: function Subscribable__sendToSubscribers(eventName, eventInfo) {
|
||||
try {
|
||||
Utils.assertThrow(eventName && typeof eventName == "string",
|
||||
"eventName must be a non-empty string");
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.subscribers || !this.subscribers[eventName])
|
||||
return;
|
||||
|
||||
let subsCopy = this.subscribers[eventName].concat();
|
||||
subsCopy.forEach(function (callback) {
|
||||
try {
|
||||
callback(this, eventInfo);
|
||||
} catch(e) {
|
||||
Utils.log(e);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Utils
|
||||
// Singelton with common utility functions.
|
||||
this.Utils = {
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [Utils] for debug use
|
||||
toString: function Utils_toString() {
|
||||
return "[Utils]";
|
||||
},
|
||||
|
||||
// ___ Logging
|
||||
useConsole: true, // as opposed to dump
|
||||
showTime: false,
|
||||
|
||||
// ----------
|
||||
// Function: log
|
||||
// Prints the given arguments to the JavaScript error console as a message.
|
||||
// Pass as many arguments as you want, it'll print them all.
|
||||
log: function Utils_log() {
|
||||
var text = this.expandArgumentsForLog(arguments);
|
||||
var prefix = this.showTime ? Date.now() + ': ' : '';
|
||||
if (this.useConsole)
|
||||
Services.console.logStringMessage(prefix + text);
|
||||
else
|
||||
dump(prefix + text + '\n');
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: error
|
||||
// Prints the given arguments to the JavaScript error console as an error.
|
||||
// Pass as many arguments as you want, it'll print them all.
|
||||
error: function Utils_error() {
|
||||
var text = this.expandArgumentsForLog(arguments);
|
||||
var prefix = this.showTime ? Date.now() + ': ' : '';
|
||||
if (this.useConsole)
|
||||
Cu.reportError(prefix + "tabview error: " + text);
|
||||
else
|
||||
dump(prefix + "TABVIEW ERROR: " + text + '\n');
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: trace
|
||||
// Prints the given arguments to the JavaScript error console as a message,
|
||||
// along with a full stack trace.
|
||||
// Pass as many arguments as you want, it'll print them all.
|
||||
trace: function Utils_trace() {
|
||||
var text = this.expandArgumentsForLog(arguments);
|
||||
|
||||
// cut off the first line of the stack trace, because that's just this function.
|
||||
let stack = Error().stack.split("\n").slice(1);
|
||||
|
||||
// if the caller was assert, cut out the line for the assert function as well.
|
||||
if (stack[0].indexOf("Utils_assert(") == 0)
|
||||
stack.splice(0, 1);
|
||||
|
||||
this.log('trace: ' + text + '\n' + stack.join("\n"));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: assert
|
||||
// Prints a stack trace along with label (as a console message) if condition is false.
|
||||
assert: function Utils_assert(condition, label) {
|
||||
if (!condition) {
|
||||
let text;
|
||||
if (typeof label != 'string')
|
||||
text = 'badly formed assert';
|
||||
else
|
||||
text = "tabview assert: " + label;
|
||||
|
||||
this.trace(text);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: assertThrow
|
||||
// Throws label as an exception if condition is false.
|
||||
assertThrow: function Utils_assertThrow(condition, label) {
|
||||
if (!condition) {
|
||||
let text;
|
||||
if (typeof label != 'string')
|
||||
text = 'badly formed assert';
|
||||
else
|
||||
text = "tabview assert: " + label;
|
||||
|
||||
// cut off the first line of the stack trace, because that's just this function.
|
||||
let stack = Error().stack.split("\n").slice(1);
|
||||
|
||||
throw text + "\n" + stack.join("\n");
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: expandObject
|
||||
// Prints the given object to a string, including all of its properties.
|
||||
expandObject: function Utils_expandObject(obj) {
|
||||
var s = obj + ' = {';
|
||||
for (let prop in obj) {
|
||||
let value;
|
||||
try {
|
||||
value = obj[prop];
|
||||
} catch(e) {
|
||||
value = '[!!error retrieving property]';
|
||||
}
|
||||
|
||||
s += prop + ': ';
|
||||
if (typeof value == 'string')
|
||||
s += '\'' + value + '\'';
|
||||
else if (typeof value == 'function')
|
||||
s += 'function';
|
||||
else
|
||||
s += value;
|
||||
|
||||
s += ', ';
|
||||
}
|
||||
return s + '}';
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: expandArgumentsForLog
|
||||
// Expands all of the given args (an array) into a single string.
|
||||
expandArgumentsForLog: function Utils_expandArgumentsForLog(args) {
|
||||
var that = this;
|
||||
return Array.map(args, function(arg) {
|
||||
return typeof arg == 'object' ? that.expandObject(arg) : arg;
|
||||
}).join('; ');
|
||||
},
|
||||
|
||||
// ___ Misc
|
||||
|
||||
// ----------
|
||||
// Function: isLeftClick
|
||||
// Given a DOM mouse event, returns true if it was for the left mouse button.
|
||||
isLeftClick: function Utils_isLeftClick(event) {
|
||||
return event.button == 0;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isMiddleClick
|
||||
// Given a DOM mouse event, returns true if it was for the middle mouse button.
|
||||
isMiddleClick: function Utils_isMiddleClick(event) {
|
||||
return event.button == 1;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isRightClick
|
||||
// Given a DOM mouse event, returns true if it was for the right mouse button.
|
||||
isRightClick: function Utils_isRightClick(event) {
|
||||
return event.button == 2;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isDOMElement
|
||||
// Returns true if the given object is a DOM element.
|
||||
isDOMElement: function Utils_isDOMElement(object) {
|
||||
return object instanceof Ci.nsIDOMElement;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isValidXULTab
|
||||
// A xulTab is valid if it has not been closed,
|
||||
// and it has not been removed from the DOM
|
||||
// Returns true if the tab is valid.
|
||||
isValidXULTab: function Utils_isValidXULTab(xulTab) {
|
||||
return !xulTab.closing && xulTab.parentNode;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isNumber
|
||||
// Returns true if the argument is a valid number.
|
||||
isNumber: function Utils_isNumber(n) {
|
||||
return typeof n == 'number' && !isNaN(n);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isRect
|
||||
// Returns true if the given object (r) looks like a <Rect>.
|
||||
isRect: function Utils_isRect(r) {
|
||||
return (r &&
|
||||
this.isNumber(r.left) &&
|
||||
this.isNumber(r.top) &&
|
||||
this.isNumber(r.width) &&
|
||||
this.isNumber(r.height));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isRange
|
||||
// Returns true if the given object (r) looks like a <Range>.
|
||||
isRange: function Utils_isRange(r) {
|
||||
return (r &&
|
||||
this.isNumber(r.min) &&
|
||||
this.isNumber(r.max));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isPoint
|
||||
// Returns true if the given object (p) looks like a <Point>.
|
||||
isPoint: function Utils_isPoint(p) {
|
||||
return (p && this.isNumber(p.x) && this.isNumber(p.y));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isPlainObject
|
||||
// Check to see if an object is a plain object (created using "{}" or "new Object").
|
||||
isPlainObject: function Utils_isPlainObject(obj) {
|
||||
// Must be an Object.
|
||||
// Make sure that DOM nodes and window objects don't pass through, as well
|
||||
if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" ||
|
||||
obj.nodeType || obj.setInterval) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not own constructor property must be Object
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
if (obj.constructor &&
|
||||
!hasOwnProperty.call(obj, "constructor") &&
|
||||
!hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Own properties are enumerated firstly, so to speed up,
|
||||
// if last one is own, then all properties are own.
|
||||
|
||||
var key;
|
||||
for (key in obj) {}
|
||||
|
||||
return key === undefined || hasOwnProperty.call(obj, key);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isEmptyObject
|
||||
// Returns true if the given object has no members.
|
||||
isEmptyObject: function Utils_isEmptyObject(obj) {
|
||||
for (let name in obj)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: copy
|
||||
// Returns a copy of the argument. Note that this is a shallow copy; if the argument
|
||||
// has properties that are themselves objects, those properties will be copied by reference.
|
||||
copy: function Utils_copy(value) {
|
||||
if (value && typeof value == 'object') {
|
||||
if (Array.isArray(value))
|
||||
return this.extend([], value);
|
||||
return this.extend({}, value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: merge
|
||||
// Merge two array-like objects into the first and return it.
|
||||
merge: function Utils_merge(first, second) {
|
||||
Array.forEach(second, el => Array.push(first, el));
|
||||
return first;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: extend
|
||||
// Pass several objects in and it will combine them all into the first object and return it.
|
||||
extend: function Utils_extend() {
|
||||
|
||||
// copy reference to target object
|
||||
let target = arguments[0] || {};
|
||||
// Deep copy is not supported
|
||||
if (typeof target === "boolean") {
|
||||
this.assert(false, "The first argument of extend cannot be a boolean." +
|
||||
"Deep copy is not supported.");
|
||||
return target;
|
||||
}
|
||||
|
||||
// Back when this was in iQ + iQ.fn, so you could extend iQ objects with it.
|
||||
// This is no longer supported.
|
||||
let length = arguments.length;
|
||||
if (length === 1) {
|
||||
this.assert(false, "Extending the iQ prototype using extend is not supported.");
|
||||
return target;
|
||||
}
|
||||
|
||||
// Handle case when target is a string or something
|
||||
if (typeof target != "object" && typeof target != "function") {
|
||||
target = {};
|
||||
}
|
||||
|
||||
for (let i = 1; i < length; i++) {
|
||||
// Only deal with non-null/undefined values
|
||||
let options = arguments[i];
|
||||
if (options != null) {
|
||||
// Extend the base object
|
||||
for (let name in options) {
|
||||
let copy = options[name];
|
||||
|
||||
// Prevent never-ending loop
|
||||
if (target === copy)
|
||||
continue;
|
||||
|
||||
if (copy !== undefined)
|
||||
target[name] = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the modified object
|
||||
return target;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: attempt
|
||||
// Tries to execute a number of functions. Returns immediately the return
|
||||
// value of the first non-failed function without executing successive
|
||||
// functions, or null.
|
||||
attempt: function Utils_attempt() {
|
||||
let args = arguments;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
try {
|
||||
return args[i]();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: MRUList
|
||||
// A most recently used list.
|
||||
//
|
||||
// Constructor: MRUList
|
||||
// If a is an array of entries, creates a copy of it.
|
||||
this.MRUList = function MRUList(a) {
|
||||
if (Array.isArray(a))
|
||||
this._list = a.concat();
|
||||
else
|
||||
this._list = [];
|
||||
};
|
||||
|
||||
MRUList.prototype = {
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [List (entry1, entry2, ...)] for debug use
|
||||
toString: function MRUList_toString() {
|
||||
return "[List (" + this._list.join(", ") + ")]";
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: update
|
||||
// Updates/inserts the given entry as the most recently used one in the list.
|
||||
update: function MRUList_update(entry) {
|
||||
this.remove(entry);
|
||||
this._list.unshift(entry);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: remove
|
||||
// Removes the given entry from the list.
|
||||
remove: function MRUList_remove(entry) {
|
||||
let index = this._list.indexOf(entry);
|
||||
if (index > -1)
|
||||
this._list.splice(index, 1);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: peek
|
||||
// Returns the most recently used entry. If a filter exists, gets the most
|
||||
// recently used entry which matches the filter.
|
||||
peek: function MRUList_peek(filter) {
|
||||
let match = null;
|
||||
if (filter && typeof filter == "function")
|
||||
this._list.some(function MRUList_peek_getEntry(entry) {
|
||||
if (filter(entry)) {
|
||||
match = entry
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
else
|
||||
match = this._list.length > 0 ? this._list[0] : null;
|
||||
|
||||
return match;
|
||||
},
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXTRA_JS_MODULES.tabview = ['modules/utils.jsm']
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'test/browser.ini',
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Firefox', 'Panorama')
|
|
@ -1,614 +0,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/. */
|
||||
|
||||
/* ******************************
|
||||
*
|
||||
* This file incorporates work from:
|
||||
* Quicksilver Score (qs_score):
|
||||
* http://rails-oceania.googlecode.com/svn/lachiecox/qs_score/trunk/qs_score.js
|
||||
* This incorporated work is covered by the following copyright and
|
||||
* permission notice:
|
||||
* Copyright 2008 Lachie Cox
|
||||
* Licensed under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* ***************************** */
|
||||
|
||||
// **********
|
||||
// Title: search.js
|
||||
// Implementation for the search functionality of Firefox Panorama.
|
||||
|
||||
// ##########
|
||||
// Class: TabUtils
|
||||
//
|
||||
// A collection of helper functions for dealing with both <TabItem>s and
|
||||
// <xul:tab>s without having to worry which one is which.
|
||||
var TabUtils = {
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [TabUtils] for debug use.
|
||||
toString: function TabUtils_toString() {
|
||||
return "[TabUtils]";
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: nameOfTab
|
||||
// Given a <TabItem> or a <xul:tab> returns the tab's name.
|
||||
nameOf: function TabUtils_nameOf(tab) {
|
||||
// We can have two types of tabs: A <TabItem> or a <xul:tab>
|
||||
// because we have to deal with both tabs represented inside
|
||||
// of active Panoramas as well as for windows in which
|
||||
// Panorama has yet to be activated. We uses object sniffing to
|
||||
// determine the type of tab and then returns its name.
|
||||
return tab.label != undefined ? tab.label : tab.$tabTitle[0].textContent;
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: URLOf
|
||||
// Given a <TabItem> or a <xul:tab> returns the URL of tab.
|
||||
URLOf: function TabUtils_URLOf(tab) {
|
||||
// Convert a <TabItem> to <xul:tab>
|
||||
if ("tab" in tab)
|
||||
tab = tab.tab;
|
||||
return tab.linkedBrowser.currentURI.spec;
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: faviconURLOf
|
||||
// Given a <TabItem> or a <xul:tab> returns the URL of tab's favicon.
|
||||
faviconURLOf: function TabUtils_faviconURLOf(tab) {
|
||||
return tab.image != undefined ? tab.image : tab.$favImage[0].src;
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: focus
|
||||
// Given a <TabItem> or a <xul:tab>, focuses it and it's window.
|
||||
focus: function TabUtils_focus(tab) {
|
||||
// Convert a <TabItem> to a <xul:tab>
|
||||
if ("tab" in tab)
|
||||
tab = tab.tab;
|
||||
tab.ownerDocument.defaultView.gBrowser.selectedTab = tab;
|
||||
tab.ownerDocument.defaultView.focus();
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: TabMatcher
|
||||
//
|
||||
// A class that allows you to iterate over matching and not-matching tabs,
|
||||
// given a case-insensitive search term.
|
||||
function TabMatcher(term) {
|
||||
this.term = term;
|
||||
}
|
||||
|
||||
TabMatcher.prototype = {
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [TabMatcher (term)] for debug use.
|
||||
toString: function TabMatcher_toString() {
|
||||
return "[TabMatcher (" + this.term + ")]";
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: _filterAndSortForMatches
|
||||
// Given an array of <TabItem>s and <xul:tab>s returns a new array
|
||||
// of tabs whose name matched the search term, sorted by lexical
|
||||
// closeness.
|
||||
_filterAndSortForMatches: function TabMatcher__filterAndSortForMatches(tabs) {
|
||||
let self = this;
|
||||
tabs = tabs.filter(function TabMatcher__filterAndSortForMatches_filter(tab) {
|
||||
let name = TabUtils.nameOf(tab);
|
||||
let url = TabUtils.URLOf(tab);
|
||||
return name.match(new RegExp(self.term, "i")) || url.match(new RegExp(self.term, "i"));
|
||||
});
|
||||
|
||||
tabs.sort(function TabMatcher__filterAndSortForMatches_sort(x, y) {
|
||||
let yScore = self._scorePatternMatch(self.term, TabUtils.nameOf(y));
|
||||
let xScore = self._scorePatternMatch(self.term, TabUtils.nameOf(x));
|
||||
return yScore - xScore;
|
||||
});
|
||||
|
||||
return tabs;
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: _filterForUnmatches
|
||||
// Given an array of <TabItem>s returns an unsorted array of tabs whose name
|
||||
// does not match the the search term.
|
||||
_filterForUnmatches: function TabMatcher__filterForUnmatches(tabs) {
|
||||
let self = this;
|
||||
return tabs.filter(function TabMatcher__filterForUnmatches_filter(tab) {
|
||||
let name = tab.$tabTitle[0].textContent;
|
||||
let url = TabUtils.URLOf(tab);
|
||||
return !name.match(new RegExp(self.term, "i")) && !url.match(new RegExp(self.term, "i"));
|
||||
});
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: _getTabsForOtherWindows
|
||||
// Returns an array of <TabItem>s and <xul:tabs>s representing tabs
|
||||
// from all windows but the current window. <TabItem>s will be returned
|
||||
// for windows in which Panorama has been activated at least once, while
|
||||
// <xul:tab>s will be returned for windows in which Panorama has never
|
||||
// been activated.
|
||||
_getTabsForOtherWindows: function TabMatcher__getTabsForOtherWindows() {
|
||||
let enumerator = Services.wm.getEnumerator("navigator:browser");
|
||||
let allTabs = [];
|
||||
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let win = enumerator.getNext();
|
||||
// This function gets tabs from other windows, not from the current window
|
||||
if (win != gWindow)
|
||||
allTabs.push.apply(allTabs, win.gBrowser.tabs);
|
||||
}
|
||||
return allTabs;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: matchedTabsFromOtherWindows
|
||||
// Returns an array of <TabItem>s and <xul:tab>s that match the search term
|
||||
// from all windows but the current window. <TabItem>s will be returned for
|
||||
// windows in which Panorama has been activated at least once, while
|
||||
// <xul:tab>s will be returned for windows in which Panorama has never
|
||||
// been activated.
|
||||
// (new TabMatcher("app")).matchedTabsFromOtherWindows();
|
||||
matchedTabsFromOtherWindows: function TabMatcher_matchedTabsFromOtherWindows() {
|
||||
if (this.term.length < 2)
|
||||
return [];
|
||||
|
||||
let tabs = this._getTabsForOtherWindows();
|
||||
return this._filterAndSortForMatches(tabs);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: matched
|
||||
// Returns an array of <TabItem>s which match the current search term.
|
||||
// If the term is less than 2 characters in length, it returns nothing.
|
||||
matched: function TabMatcher_matched() {
|
||||
if (this.term.length < 2)
|
||||
return [];
|
||||
|
||||
let tabs = TabItems.getItems();
|
||||
return this._filterAndSortForMatches(tabs);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: unmatched
|
||||
// Returns all of <TabItem>s that .matched() doesn't return.
|
||||
unmatched: function TabMatcher_unmatched() {
|
||||
let tabs = TabItems.getItems();
|
||||
if (this.term.length < 2)
|
||||
return tabs;
|
||||
|
||||
return this._filterForUnmatches(tabs);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: doSearch
|
||||
// Performs the search. Lets you provide three functions.
|
||||
// The first is on all matched tabs in the window, the second on all unmatched
|
||||
// tabs in the window, and the third on all matched tabs in other windows.
|
||||
// The first two functions take two parameters: A <TabItem> and its integer index
|
||||
// indicating the absolute rank of the <TabItem> in terms of match to
|
||||
// the search term. The last function also takes two paramaters, but can be
|
||||
// passed both <TabItem>s and <xul:tab>s and the index is offset by the
|
||||
// number of matched tabs inside the window.
|
||||
doSearch: function TabMatcher_doSearch(matchFunc, unmatchFunc, otherFunc) {
|
||||
let matches = this.matched();
|
||||
let unmatched = this.unmatched();
|
||||
let otherMatches = this.matchedTabsFromOtherWindows();
|
||||
|
||||
matches.forEach(function(tab, i) {
|
||||
matchFunc(tab, i);
|
||||
});
|
||||
|
||||
otherMatches.forEach(function(tab,i) {
|
||||
otherFunc(tab, i+matches.length);
|
||||
});
|
||||
|
||||
unmatched.forEach(function(tab, i) {
|
||||
unmatchFunc(tab, i);
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _scorePatternMatch
|
||||
// Given a pattern string, returns a score between 0 and 1 of how well
|
||||
// that pattern matches the original string. It mimics the heuristics
|
||||
// of the Mac application launcher Quicksilver.
|
||||
_scorePatternMatch: function TabMatcher__scorePatternMatch(pattern, matched, offset) {
|
||||
offset = offset || 0;
|
||||
pattern = pattern.toLowerCase();
|
||||
matched = matched.toLowerCase();
|
||||
|
||||
if (pattern.length == 0)
|
||||
return 0.9;
|
||||
if (pattern.length > matched.length)
|
||||
return 0.0;
|
||||
|
||||
for (let i = pattern.length; i > 0; i--) {
|
||||
let sub_pattern = pattern.substring(0,i);
|
||||
let index = matched.indexOf(sub_pattern);
|
||||
|
||||
if (index < 0)
|
||||
continue;
|
||||
if (index + pattern.length > matched.length + offset)
|
||||
continue;
|
||||
|
||||
let next_string = matched.substring(index+sub_pattern.length);
|
||||
let next_pattern = null;
|
||||
|
||||
if (i >= pattern.length)
|
||||
next_pattern = '';
|
||||
else
|
||||
next_pattern = pattern.substring(i);
|
||||
|
||||
let remaining_score = this._scorePatternMatch(next_pattern, next_string, offset + index);
|
||||
|
||||
if (remaining_score > 0) {
|
||||
let score = matched.length-next_string.length;
|
||||
|
||||
if (index != 0) {
|
||||
let c = matched.charCodeAt(index-1);
|
||||
if (c == 32 || c == 9) {
|
||||
for (let j = (index - 2); j >= 0; j--) {
|
||||
c = matched.charCodeAt(j);
|
||||
score -= ((c == 32 || c == 9) ? 1 : 0.15);
|
||||
}
|
||||
} else {
|
||||
score -= index;
|
||||
}
|
||||
}
|
||||
|
||||
score += remaining_score * next_string.length;
|
||||
score /= matched.length;
|
||||
return score;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: TabHandlers
|
||||
//
|
||||
// A object that handles all of the event handlers.
|
||||
var TabHandlers = {
|
||||
_mouseDownLocation: null,
|
||||
|
||||
// ---------
|
||||
// Function: onMatch
|
||||
// Adds styles and event listeners to the matched tab items.
|
||||
onMatch: function TabHandlers_onMatch(tab, index) {
|
||||
tab.addClass("onTop");
|
||||
index != 0 ? tab.addClass("notMainMatch") : tab.removeClass("notMainMatch");
|
||||
|
||||
// Remove any existing handlers before adding the new ones.
|
||||
// If we don't do this, then we may add more handlers than
|
||||
// we remove.
|
||||
tab.$canvas
|
||||
.unbind("mousedown", TabHandlers._hideHandler)
|
||||
.unbind("mouseup", TabHandlers._showHandler);
|
||||
|
||||
tab.$canvas
|
||||
.mousedown(TabHandlers._hideHandler)
|
||||
.mouseup(TabHandlers._showHandler);
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: onUnmatch
|
||||
// Removes styles and event listeners from the unmatched tab items.
|
||||
onUnmatch: function TabHandlers_onUnmatch(tab, index) {
|
||||
tab.$container.removeClass("onTop");
|
||||
tab.removeClass("notMainMatch");
|
||||
|
||||
tab.$canvas
|
||||
.unbind("mousedown", TabHandlers._hideHandler)
|
||||
.unbind("mouseup", TabHandlers._showHandler);
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: onOther
|
||||
// Removes styles and event listeners from the unmatched tabs.
|
||||
onOther: function TabHandlers_onOther(tab, index) {
|
||||
// Unlike the other on* functions, in this function tab can
|
||||
// either be a <TabItem> or a <xul:tab>. In other functions
|
||||
// it is always a <TabItem>. Also note that index is offset
|
||||
// by the number of matches within the window.
|
||||
let item = iQ("<div/>")
|
||||
.addClass("inlineMatch")
|
||||
.click(function TabHandlers_onOther_click(event) {
|
||||
Search.hide(event);
|
||||
TabUtils.focus(tab);
|
||||
});
|
||||
|
||||
iQ("<img/>")
|
||||
.attr("src", TabUtils.faviconURLOf(tab))
|
||||
.appendTo(item);
|
||||
|
||||
iQ("<span/>")
|
||||
.text(TabUtils.nameOf(tab))
|
||||
.appendTo(item);
|
||||
|
||||
index != 0 ? item.addClass("notMainMatch") : item.removeClass("notMainMatch");
|
||||
item.appendTo("#results");
|
||||
iQ("#otherresults").show();
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: _hideHandler
|
||||
// Performs when mouse down on a canvas of tab item.
|
||||
_hideHandler: function TabHandlers_hideHandler(event) {
|
||||
iQ("#search").fadeOut();
|
||||
iQ("#searchshade").fadeOut();
|
||||
TabHandlers._mouseDownLocation = {x:event.clientX, y:event.clientY};
|
||||
},
|
||||
|
||||
// ---------
|
||||
// Function: _showHandler
|
||||
// Performs when mouse up on a canvas of tab item.
|
||||
_showHandler: function TabHandlers_showHandler(event) {
|
||||
// If the user clicks on a tab without moving the mouse then
|
||||
// they are zooming into the tab and we need to exit search
|
||||
// mode.
|
||||
if (TabHandlers._mouseDownLocation.x == event.clientX &&
|
||||
TabHandlers._mouseDownLocation.y == event.clientY) {
|
||||
Search.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
iQ("#searchshade").show();
|
||||
iQ("#search").show();
|
||||
iQ("#searchbox")[0].focus();
|
||||
// Marshal the search.
|
||||
setTimeout(Search.perform, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: Search
|
||||
//
|
||||
// A object that handles the search feature.
|
||||
var Search = {
|
||||
_initiatedBy: "",
|
||||
_blockClick: false,
|
||||
_currentHandler: null,
|
||||
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [Search] for debug use.
|
||||
toString: function Search_toString() {
|
||||
return "[Search]";
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: init
|
||||
// Initializes the searchbox to be focused, and everything else to be hidden,
|
||||
// and to have everything have the appropriate event handlers.
|
||||
init: function Search_init() {
|
||||
let self = this;
|
||||
|
||||
iQ("#search").hide();
|
||||
iQ("#searchshade").hide().mousedown(function Search_init_shade_mousedown(event) {
|
||||
if (event.target.id != "searchbox" && !self._blockClick)
|
||||
self.hide();
|
||||
});
|
||||
|
||||
iQ("#searchbox").keyup(function Search_init_box_keyup() {
|
||||
self.perform();
|
||||
})
|
||||
.attr("title", tabviewString("button.searchTabs"));
|
||||
|
||||
iQ("#searchbutton").mousedown(function Search_init_button_mousedown() {
|
||||
self._initiatedBy = "buttonclick";
|
||||
self.ensureShown();
|
||||
self.switchToInMode();
|
||||
})
|
||||
.attr("title", tabviewString("button.searchTabs"));
|
||||
|
||||
window.addEventListener("focus", function Search_init_window_focus() {
|
||||
if (self.isEnabled()) {
|
||||
self._blockClick = true;
|
||||
setTimeout(function() {
|
||||
self._blockClick = false;
|
||||
}, 0);
|
||||
}
|
||||
}, false);
|
||||
|
||||
this.switchToBeforeMode();
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _beforeSearchKeyHandler
|
||||
// Handles all keydown before the search interface is brought up.
|
||||
_beforeSearchKeyHandler: function Search__beforeSearchKeyHandler(event) {
|
||||
// Only match reasonable text-like characters for quick search.
|
||||
if (event.altKey || event.ctrlKey || event.metaKey)
|
||||
return;
|
||||
|
||||
if ((event.keyCode > 0 && event.keyCode <= event.DOM_VK_DELETE) ||
|
||||
event.keyCode == event.DOM_VK_CONTEXT_MENU ||
|
||||
event.keyCode == event.DOM_VK_SLEEP ||
|
||||
(event.keyCode >= event.DOM_VK_F1 &&
|
||||
event.keyCode <= event.DOM_VK_SCROLL_LOCK) ||
|
||||
event.keyCode == event.DOM_VK_META ||
|
||||
event.keyCode == 91 || // 91 = left windows key
|
||||
event.keyCode == 92 || // 92 = right windows key
|
||||
(!event.keyCode && !event.charCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are already in an input field, allow typing as normal.
|
||||
if (event.target.nodeName == "INPUT")
|
||||
return;
|
||||
|
||||
// / is used to activate the search feature so the key shouldn't be entered
|
||||
// into the search box.
|
||||
if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
this.switchToInMode();
|
||||
this._initiatedBy = "keydown";
|
||||
this.ensureShown(true);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _inSearchKeyHandler
|
||||
// Handles all keydown while search mode.
|
||||
_inSearchKeyHandler: function Search__inSearchKeyHandler(event) {
|
||||
let term = iQ("#searchbox").val();
|
||||
if ((event.keyCode == event.DOM_VK_ESCAPE) ||
|
||||
(event.keyCode == event.DOM_VK_BACK_SPACE && term.length <= 1 &&
|
||||
this._initiatedBy == "keydown")) {
|
||||
this.hide(event);
|
||||
return;
|
||||
}
|
||||
|
||||
let matcher = this.createSearchTabMatcher();
|
||||
let matches = matcher.matched();
|
||||
let others = matcher.matchedTabsFromOtherWindows();
|
||||
if (event.keyCode == event.DOM_VK_RETURN &&
|
||||
(matches.length > 0 || others.length > 0)) {
|
||||
this.hide(event);
|
||||
if (matches.length > 0)
|
||||
matches[0].zoomIn();
|
||||
else
|
||||
TabUtils.focus(others[0]);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: switchToBeforeMode
|
||||
// Make sure the event handlers are appropriate for the before-search mode.
|
||||
switchToBeforeMode: function Search_switchToBeforeMode() {
|
||||
let self = this;
|
||||
if (this._currentHandler)
|
||||
iQ(window).unbind("keydown", this._currentHandler);
|
||||
this._currentHandler = function Search_switchToBeforeMode_handler(event) {
|
||||
self._beforeSearchKeyHandler(event);
|
||||
}
|
||||
iQ(window).keydown(this._currentHandler);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: switchToInMode
|
||||
// Make sure the event handlers are appropriate for the in-search mode.
|
||||
switchToInMode: function Search_switchToInMode() {
|
||||
let self = this;
|
||||
if (this._currentHandler)
|
||||
iQ(window).unbind("keydown", this._currentHandler);
|
||||
this._currentHandler = function Search_switchToInMode_handler(event) {
|
||||
self._inSearchKeyHandler(event);
|
||||
}
|
||||
iQ(window).keydown(this._currentHandler);
|
||||
},
|
||||
|
||||
createSearchTabMatcher: function Search_createSearchTabMatcher() {
|
||||
return new TabMatcher(iQ("#searchbox").val());
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: isEnabled
|
||||
// Checks whether search mode is enabled or not.
|
||||
isEnabled: function Search_isEnabled() {
|
||||
return iQ("#search").css("display") != "none";
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: hide
|
||||
// Hides search mode.
|
||||
hide: function Search_hide(event) {
|
||||
if (!this.isEnabled())
|
||||
return;
|
||||
|
||||
iQ("#searchbox").val("");
|
||||
iQ("#searchshade").hide();
|
||||
iQ("#search").hide();
|
||||
|
||||
iQ("#searchbutton").css({ opacity:.8 });
|
||||
|
||||
if (AppConstants.platform == "macosx")
|
||||
UI.setTitlebarColors(true);
|
||||
|
||||
this.perform();
|
||||
this.switchToBeforeMode();
|
||||
|
||||
if (event) {
|
||||
// when hiding the search mode, we need to prevent the keypress handler
|
||||
// in UI__setTabViewFrameKeyHandlers to handle the key press again. e.g. Esc
|
||||
// which is already handled by the key down in this class.
|
||||
if (event.type == "keydown")
|
||||
UI.ignoreKeypressForSearch = true;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// Return focus to the tab window
|
||||
UI.blurAll();
|
||||
gTabViewFrame.contentWindow.focus();
|
||||
|
||||
let newEvent = document.createEvent("Events");
|
||||
newEvent.initEvent("tabviewsearchdisabled", false, false);
|
||||
dispatchEvent(newEvent);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: perform
|
||||
// Performs a search.
|
||||
perform: function Search_perform() {
|
||||
let matcher = this.createSearchTabMatcher();
|
||||
|
||||
// Remove any previous other-window search results and
|
||||
// hide the display area.
|
||||
iQ("#results").empty();
|
||||
iQ("#otherresults").hide();
|
||||
iQ("#otherresults>.label").text(tabviewString("search.otherWindowTabs"));
|
||||
|
||||
matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch, TabHandlers.onOther);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: ensureShown
|
||||
// Ensures the search feature is displayed. If not, display it.
|
||||
// Parameters:
|
||||
// - a boolean indicates whether this is triggered by a keypress or not
|
||||
ensureShown: function Search_ensureShown(activatedByKeypress) {
|
||||
let $search = iQ("#search");
|
||||
let $searchShade = iQ("#searchshade");
|
||||
let $searchbox = iQ("#searchbox");
|
||||
iQ("#searchbutton").css({ opacity: 1 });
|
||||
|
||||
// NOTE: when this function is called by keydown handler, next keypress
|
||||
// event or composition events of IME will be fired on the focused editor.
|
||||
function dispatchTabViewSearchEnabledEvent() {
|
||||
let newEvent = document.createEvent("Events");
|
||||
newEvent.initEvent("tabviewsearchenabled", false, false);
|
||||
dispatchEvent(newEvent);
|
||||
};
|
||||
|
||||
if (!this.isEnabled()) {
|
||||
$searchShade.show();
|
||||
$search.show();
|
||||
|
||||
if (AppConstants.platform == "macosx")
|
||||
UI.setTitlebarColors({active: "#717171", inactive: "#EDEDED"});
|
||||
|
||||
if (activatedByKeypress) {
|
||||
// set the focus so key strokes are entered into the textbox.
|
||||
$searchbox[0].focus();
|
||||
dispatchTabViewSearchEnabledEvent();
|
||||
} else {
|
||||
// marshal the focusing, otherwise it ends up with searchbox[0].focus gets
|
||||
// called before the search button gets the focus after being pressed.
|
||||
setTimeout(function setFocusAndDispatchSearchEnabledEvent() {
|
||||
$searchbox[0].focus();
|
||||
dispatchTabViewSearchEnabledEvent();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,201 +0,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/. */
|
||||
|
||||
// **********
|
||||
// Title: storage.js
|
||||
|
||||
// ##########
|
||||
// Class: Storage
|
||||
// Singleton for permanent storage of TabView data.
|
||||
var Storage = {
|
||||
GROUP_DATA_IDENTIFIER: "tabview-group",
|
||||
GROUPS_DATA_IDENTIFIER: "tabview-groups",
|
||||
TAB_DATA_IDENTIFIER: "tabview-tab",
|
||||
UI_DATA_IDENTIFIER: "tabview-ui",
|
||||
|
||||
// ----------
|
||||
// Function: toString
|
||||
// Prints [Storage] for debug use
|
||||
toString: function Storage_toString() {
|
||||
return "[Storage]";
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: init
|
||||
// Sets up the object.
|
||||
init: function Storage_init() {
|
||||
this._sessionStore =
|
||||
Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: uninit
|
||||
uninit: function Storage_uninit () {
|
||||
this._sessionStore = null;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveTab
|
||||
// Saves the data for a single tab.
|
||||
saveTab: function Storage_saveTab(tab, data) {
|
||||
Utils.assert(tab, "tab");
|
||||
|
||||
this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER,
|
||||
JSON.stringify(data));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: getTabData
|
||||
// Load tab data from session store and return it.
|
||||
getTabData: function Storage_getTabData(tab) {
|
||||
Utils.assert(tab, "tab");
|
||||
|
||||
let existingData = null;
|
||||
|
||||
try {
|
||||
let tabData = this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER);
|
||||
if (tabData != "")
|
||||
existingData = JSON.parse(tabData);
|
||||
} catch (e) {
|
||||
// getTabValue will fail if the property doesn't exist.
|
||||
Utils.log(e);
|
||||
}
|
||||
|
||||
return existingData;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: getTabState
|
||||
// Returns the current state of the given tab.
|
||||
getTabState: function Storage_getTabState(tab) {
|
||||
Utils.assert(tab, "tab");
|
||||
let tabState;
|
||||
|
||||
try {
|
||||
tabState = JSON.parse(this._sessionStore.getTabState(tab));
|
||||
} catch (e) {}
|
||||
|
||||
return tabState;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveGroupItem
|
||||
// Saves the data for a single groupItem, associated with a specific window.
|
||||
saveGroupItem: function Storage_saveGroupItem(win, data) {
|
||||
var id = data.id;
|
||||
var existingData = this.readGroupItemData(win);
|
||||
existingData[id] = data;
|
||||
this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
|
||||
JSON.stringify(existingData));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: deleteGroupItem
|
||||
// Deletes the data for a single groupItem from the given window.
|
||||
deleteGroupItem: function Storage_deleteGroupItem(win, id) {
|
||||
var existingData = this.readGroupItemData(win);
|
||||
delete existingData[id];
|
||||
this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
|
||||
JSON.stringify(existingData));
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readGroupItemData
|
||||
// Returns the data for all groupItems associated with the given window.
|
||||
readGroupItemData: function Storage_readGroupItemData(win) {
|
||||
var existingData = {};
|
||||
let data;
|
||||
try {
|
||||
data = this._sessionStore.getWindowValue(win, this.GROUP_DATA_IDENTIFIER);
|
||||
if (data)
|
||||
existingData = JSON.parse(data);
|
||||
} catch (e) {
|
||||
// getWindowValue will fail if the property doesn't exist
|
||||
Utils.log("Error in readGroupItemData: "+e, data);
|
||||
}
|
||||
return existingData;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readWindowBusyState
|
||||
// Returns the current busyState for the given window.
|
||||
readWindowBusyState: function Storage_readWindowBusyState(win) {
|
||||
let state;
|
||||
|
||||
try {
|
||||
let data = this._sessionStore.getWindowState(win);
|
||||
if (data)
|
||||
state = JSON.parse(data);
|
||||
} catch (e) {
|
||||
Utils.log("Error while parsing window state");
|
||||
}
|
||||
|
||||
return (state && state.windows[0].busy);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveGroupItemsData
|
||||
// Saves the global data for the <GroupItems> singleton for the given window.
|
||||
saveGroupItemsData: function Storage_saveGroupItemsData(win, data) {
|
||||
this.saveData(win, this.GROUPS_DATA_IDENTIFIER, data);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readGroupItemsData
|
||||
// Reads the global data for the <GroupItems> singleton for the given window.
|
||||
readGroupItemsData: function Storage_readGroupItemsData(win) {
|
||||
return this.readData(win, this.GROUPS_DATA_IDENTIFIER);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveUIData
|
||||
// Saves the global data for the <UIManager> singleton for the given window.
|
||||
saveUIData: function Storage_saveUIData(win, data) {
|
||||
this.saveData(win, this.UI_DATA_IDENTIFIER, data);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readUIData
|
||||
// Reads the global data for the <UIManager> singleton for the given window.
|
||||
readUIData: function Storage_readUIData(win) {
|
||||
return this.readData(win, this.UI_DATA_IDENTIFIER);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveVisibilityData
|
||||
// Saves visibility for the given window.
|
||||
saveVisibilityData: function Storage_saveVisibilityData(win, data) {
|
||||
this._sessionStore.setWindowValue(
|
||||
win, win.TabView.VISIBILITY_IDENTIFIER, data);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveData
|
||||
// Generic routine for saving data to a window.
|
||||
saveData: function Storage_saveData(win, id, data) {
|
||||
try {
|
||||
this._sessionStore.setWindowValue(win, id, JSON.stringify(data));
|
||||
} catch (e) {
|
||||
Utils.log("Error in saveData: "+e);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: readData
|
||||
// Generic routine for reading data from a window.
|
||||
readData: function Storage_readData(win, id) {
|
||||
var existingData = {};
|
||||
try {
|
||||
var data = this._sessionStore.getWindowValue(win, id);
|
||||
if (data)
|
||||
existingData = JSON.parse(data);
|
||||
} catch (e) {
|
||||
Utils.log("Error in readData: "+e);
|
||||
}
|
||||
|
||||
return existingData;
|
||||
}
|
||||
};
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,294 +0,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/. */
|
||||
|
||||
/* Platform-independent structural styling for
|
||||
* <strike>Tab Candy</strike> Panorama
|
||||
----------------------------------*/
|
||||
|
||||
html {
|
||||
overflow: hidden;
|
||||
/* image-rendering: -moz-crisp-edges; */
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#content {
|
||||
overflow: -moz-hidden-unscrollable;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -999999;
|
||||
}
|
||||
|
||||
/* Tabs
|
||||
----------------------------------*/
|
||||
|
||||
.tab {
|
||||
position: absolute;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.tab canvas,
|
||||
.cached-thumb {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.tabHidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.favicon {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* Apply crisp rendering for favicons at exactly 2dppx resolution */
|
||||
@media (resolution: 2dppx) {
|
||||
.favicon > img {
|
||||
image-rendering: -moz-crisp-edges;
|
||||
}
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.expander {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.stacked .tab-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stack-trayed .tab-title {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Tab: Zooming
|
||||
----------------------------------*/
|
||||
|
||||
.front {
|
||||
z-index: 999999 !important;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
}
|
||||
|
||||
.front canvas {
|
||||
border: none !important;
|
||||
padding: 1px !important;
|
||||
}
|
||||
|
||||
/* Groups
|
||||
----------------------------------*/
|
||||
|
||||
.groupItem {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.appTabTrayContainer {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
/* We want the title container to leave out width, position of the .close
|
||||
button and space between input and .close button. Keep an eye on LTR and
|
||||
RTL differences in .close. */
|
||||
width: calc(100% - 16px - 6px - 6px);
|
||||
}
|
||||
|
||||
input.name {
|
||||
text-overflow: ellipsis;
|
||||
width: -moz-available;
|
||||
}
|
||||
|
||||
input.name:focus {
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
/* Other Items
|
||||
----------------------------------*/
|
||||
|
||||
.undo {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* Trenches
|
||||
----------------------------------*/
|
||||
|
||||
.guideTrench,
|
||||
.visibleTrench,
|
||||
.activeVisibleTrench {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.guideTrench {
|
||||
z-index: -101;
|
||||
}
|
||||
|
||||
.visibleTrench {
|
||||
z-index: -103;
|
||||
}
|
||||
|
||||
.activeVisibleTrench {
|
||||
z-index: -102;
|
||||
}
|
||||
|
||||
/* Other
|
||||
----------------------------------*/
|
||||
|
||||
.titlebar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.title-shield {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.transparentBorder {
|
||||
border: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
.stackExpander {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.shield {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.banner {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 10px 0;
|
||||
position: absolute;
|
||||
z-index: 1000060;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
opacity: 0;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Resizable
|
||||
----------------------------------*/
|
||||
.resizer {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.iq-resizable-handle {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.iq-resizable-disabled .iq-resizable-handle,
|
||||
.iq-resizable-autohide .iq-resizable-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Search
|
||||
----------------------------------*/
|
||||
#searchshade{
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 1000001;
|
||||
}
|
||||
|
||||
#search{
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
pointer-events: none;
|
||||
z-index: 1000050;
|
||||
}
|
||||
|
||||
html[dir=rtl] #search {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#searchbox{
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
html[dir=rtl] #searchbox {
|
||||
right: auto;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
#actions{
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: -3px;
|
||||
z-index: 1000000;
|
||||
}
|
||||
|
||||
html[dir=rtl] #actions {
|
||||
right: auto;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
#otherresults{
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.onTop{
|
||||
z-index: 1000010 !important;
|
||||
}
|
||||
|
||||
.inlineMatch{
|
||||
display: inline-block;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.inlineMatch>span{
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
|
||||
<head>
|
||||
<title> </title>
|
||||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
||||
<link rel="stylesheet" href="tabview.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://browser/skin/tabview/tabview.css" type="text/css"/>
|
||||
</head>
|
||||
|
||||
<body transparent="true">
|
||||
<div id="content">
|
||||
<div id="bg">
|
||||
</div>
|
||||
<div id="actions">
|
||||
<input id="exit-button" type="button" alt="" groups="0" />
|
||||
<input id="searchbutton" type="button"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="searchshade"></div>
|
||||
<div id="search">
|
||||
<input id="searchbox" type="text"/>
|
||||
<div id="otherresults">
|
||||
<span class="label"></span>
|
||||
<span id="results"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript;version=1.8" src="tabview.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,83 +0,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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
var Cr = Components.results;
|
||||
|
||||
Cu.import("resource:///modules/tabview/utils.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "tabviewBundle", function() {
|
||||
return Services.strings.
|
||||
createBundle("chrome://browser/locale/tabview.properties");
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "tabbrowserBundle", function() {
|
||||
return Services.strings.
|
||||
createBundle("chrome://browser/locale/tabbrowser.properties");
|
||||
});
|
||||
|
||||
function tabviewString(name) {
|
||||
return tabviewBundle.GetStringFromName('tabview.' + name);
|
||||
}
|
||||
function tabbrowserString(name) {
|
||||
return tabbrowserBundle.GetStringFromName(name);
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gPrefBranch", function() {
|
||||
return Services.prefs.getBranch("browser.panorama.");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "gPageThumbnails",
|
||||
"resource://gre/modules/PageThumbs.jsm", "PageThumbs");
|
||||
|
||||
var gWindow = window.parent;
|
||||
var gBrowser = gWindow.gBrowser;
|
||||
var gTabView = gWindow.TabView;
|
||||
var gTabViewDeck = gWindow.document.getElementById("tab-view-deck");
|
||||
var gBrowserPanel = gWindow.document.getElementById("browser-panel");
|
||||
var gTabViewFrame = gWindow.document.getElementById("tab-view");
|
||||
|
||||
var AllTabs = {
|
||||
_events: {
|
||||
attrModified: "TabAttrModified",
|
||||
close: "TabClose",
|
||||
move: "TabMove",
|
||||
open: "TabOpen",
|
||||
select: "TabSelect",
|
||||
pinned: "TabPinned",
|
||||
unpinned: "TabUnpinned"
|
||||
},
|
||||
|
||||
get tabs() {
|
||||
return Array.filter(gBrowser.tabs, tab => Utils.isValidXULTab(tab));
|
||||
},
|
||||
|
||||
register: function AllTabs_register(eventName, callback) {
|
||||
gBrowser.tabContainer.addEventListener(this._events[eventName], callback, false);
|
||||
},
|
||||
|
||||
unregister: function AllTabs_unregister(eventName, callback) {
|
||||
gBrowser.tabContainer.removeEventListener(this._events[eventName], callback, false);
|
||||
}
|
||||
};
|
||||
|
||||
# NB: Certain files need to evaluate before others
|
||||
|
||||
#include iq.js
|
||||
#include storage.js
|
||||
#include items.js
|
||||
#include groupitems.js
|
||||
#include tabitems.js
|
||||
#include favicons.js
|
||||
#include drag.js
|
||||
#include trench.js
|
||||
#include search.js
|
||||
#include telemetry.js
|
||||
#include ui.js
|
|
@ -1,63 +0,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/. */
|
||||
|
||||
/**
|
||||
* Collects telemetry data for Tabview.
|
||||
*/
|
||||
var Telemetry = {
|
||||
TOPIC_GATHER_TELEMETRY: "gather-telemetry",
|
||||
|
||||
/**
|
||||
* Initializes the object.
|
||||
*/
|
||||
init: function Telemetry_init() {
|
||||
Services.obs.addObserver(this, this.TOPIC_GATHER_TELEMETRY, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninitializes the object.
|
||||
*/
|
||||
uninit: function Telemetry_uninit() {
|
||||
Services.obs.removeObserver(this, this.TOPIC_GATHER_TELEMETRY);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds telemetry values to gather usage statistics.
|
||||
*/
|
||||
_collect: function Telemetry_collect() {
|
||||
let stackedGroupsCount = 0;
|
||||
let childCounts = [];
|
||||
|
||||
GroupItems.groupItems.forEach(function (groupItem) {
|
||||
if (!groupItem.isEmpty()) {
|
||||
childCounts.push(groupItem.getChildren().length);
|
||||
|
||||
if (groupItem.isStacked())
|
||||
stackedGroupsCount++;
|
||||
}
|
||||
});
|
||||
|
||||
function addTelemetryValue(aId, aValue) {
|
||||
Services.telemetry.getHistogramById("PANORAMA_" + aId).add(aValue);
|
||||
}
|
||||
function median(aChildCounts) {
|
||||
aChildCounts.sort(function(x, y) { return x - y; });
|
||||
let middle = Math.floor(aChildCounts.length / 2);
|
||||
return aChildCounts[middle];
|
||||
}
|
||||
|
||||
addTelemetryValue("GROUPS_COUNT", GroupItems.groupItems.length);
|
||||
addTelemetryValue("STACKED_GROUPS_COUNT", stackedGroupsCount);
|
||||
addTelemetryValue("MEDIAN_TABS_IN_GROUPS_COUNT", median(childCounts));
|
||||
},
|
||||
|
||||
/**
|
||||
* Observes for gather telemetry topic.
|
||||
*/
|
||||
observe: function Telemetry_observe(aSubject, aTopic, aData) {
|
||||
if (!gWindow.PrivateBrowsingUtils.isWindowPrivate(gWindow))
|
||||
this._collect();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
[DEFAULT]
|
||||
skip-if = e10s # Bug 1092281
|
||||
|
||||
support-files =
|
||||
dummy_page.html
|
||||
head.js
|
||||
search1.html
|
||||
search2.html
|
||||
test_bug600645.html
|
||||
test_bug644097.html
|
||||
test_bug678374.html
|
||||
test_bug678374_icon16.png
|
||||
|
||||
[browser_tabview_alltabs.js]
|
||||
[browser_tabview_apptabs.js]
|
||||
[browser_tabview_bug580412.js]
|
||||
[browser_tabview_bug586553.js]
|
||||
[browser_tabview_bug587043.js]
|
||||
[browser_tabview_bug587231.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tabview_bug587276.js]
|
||||
skip-if = e10s || true # Bug 1091200, bug 1096285
|
||||
[browser_tabview_bug587351.js]
|
||||
[browser_tabview_bug587503.js]
|
||||
[browser_tabview_bug587990.js]
|
||||
[browser_tabview_bug588265.js]
|
||||
[browser_tabview_bug589324.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug590606.js]
|
||||
[browser_tabview_bug591706.js]
|
||||
[browser_tabview_bug593283.js]
|
||||
[browser_tabview_bug595191.js]
|
||||
[browser_tabview_bug595436.js]
|
||||
[browser_tabview_bug595518.js]
|
||||
[browser_tabview_bug595521.js]
|
||||
[browser_tabview_bug595560.js]
|
||||
[browser_tabview_bug595601.js]
|
||||
[browser_tabview_bug595804.js]
|
||||
[browser_tabview_bug595930.js]
|
||||
[browser_tabview_bug595943.js]
|
||||
[browser_tabview_bug595965.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug596781.js]
|
||||
[browser_tabview_bug597360.js]
|
||||
[browser_tabview_bug597399.js]
|
||||
[browser_tabview_bug597980.js]
|
||||
skip-if = true # Bug 711907
|
||||
[browser_tabview_bug598375.js]
|
||||
[browser_tabview_bug598600.js]
|
||||
[browser_tabview_bug599048.js]
|
||||
[browser_tabview_bug599626.js]
|
||||
skip-if = os == 'linux' || e10s # Disabled on Linux: Bug 939620, much fail, so amaze; Disabled for e10s: Bug ??????
|
||||
[browser_tabview_bug600645.js]
|
||||
[browser_tabview_bug600812.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug602432.js]
|
||||
skip-if = true # Bug 704417
|
||||
[browser_tabview_bug604098.js]
|
||||
[browser_tabview_bug606657.js]
|
||||
[browser_tabview_bug606905.js]
|
||||
[browser_tabview_bug607108.js]
|
||||
skip-if = os == 'linux' # Bug 947521
|
||||
[browser_tabview_bug608037.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug608153.js]
|
||||
[browser_tabview_bug608158.js]
|
||||
[browser_tabview_bug608184.js]
|
||||
[browser_tabview_bug608405.js]
|
||||
[browser_tabview_bug610208.js]
|
||||
[browser_tabview_bug610242.js]
|
||||
skip-if = true # Bug 736036
|
||||
[browser_tabview_bug612470.js]
|
||||
[browser_tabview_bug613541.js]
|
||||
skip-if = os == "win" # Bug 951477
|
||||
[browser_tabview_bug616729.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug616967.js]
|
||||
[browser_tabview_bug618816.js]
|
||||
[browser_tabview_bug618828.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tabview_bug619937.js]
|
||||
[browser_tabview_bug622835.js]
|
||||
[browser_tabview_bug623768.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug624265_perwindowpb.js]
|
||||
skip-if = true # Bug 921984, hopefully fixed by bug 930202
|
||||
[browser_tabview_bug624692.js]
|
||||
[browser_tabview_bug624727_perwindowpb.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tabview_bug624847.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug624931.js]
|
||||
[browser_tabview_bug624953.js]
|
||||
[browser_tabview_bug625195.js]
|
||||
[browser_tabview_bug625269.js]
|
||||
[browser_tabview_bug625424.js]
|
||||
[browser_tabview_bug625955.js]
|
||||
[browser_tabview_bug626368.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug626455.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug626525.js]
|
||||
[browser_tabview_bug626791.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tabview_bug627736.js]
|
||||
[browser_tabview_bug628061.js]
|
||||
[browser_tabview_bug628165.js]
|
||||
[browser_tabview_bug628270.js]
|
||||
[browser_tabview_bug628887.js]
|
||||
[browser_tabview_bug629189.js]
|
||||
[browser_tabview_bug629195.js]
|
||||
skip-if = os == 'linux'&&debug # bug 981703
|
||||
[browser_tabview_bug630102.js]
|
||||
[browser_tabview_bug630157.js]
|
||||
skip-if = true # Bug 922422
|
||||
[browser_tabview_bug631662.js]
|
||||
skip-if = true # Bug 922422
|
||||
[browser_tabview_bug631752.js]
|
||||
[browser_tabview_bug633788.js]
|
||||
[browser_tabview_bug634077.js]
|
||||
[browser_tabview_bug634085.js]
|
||||
[browser_tabview_bug634672.js]
|
||||
[browser_tabview_bug635696.js]
|
||||
[browser_tabview_bug637840.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tabview_bug640765.js]
|
||||
[browser_tabview_bug641802.js]
|
||||
[browser_tabview_bug642793.js]
|
||||
[browser_tabview_bug643392.js]
|
||||
[browser_tabview_bug644097.js]
|
||||
[browser_tabview_bug648882.js]
|
||||
skip-if = true # Bug 752862
|
||||
[browser_tabview_bug649006.js]
|
||||
[browser_tabview_bug649307.js]
|
||||
[browser_tabview_bug649319.js]
|
||||
[browser_tabview_bug650280_perwindowpb.js]
|
||||
[browser_tabview_bug650573.js]
|
||||
[browser_tabview_bug651311.js]
|
||||
[browser_tabview_bug654295.js]
|
||||
[browser_tabview_bug654721.js]
|
||||
[browser_tabview_bug654941.js]
|
||||
skip-if = true # Bug 754222
|
||||
[browser_tabview_bug655269.js]
|
||||
[browser_tabview_bug656778.js]
|
||||
skip-if = os == "mac" # Bug 946918
|
||||
[browser_tabview_bug656913.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_bug659594.js]
|
||||
skip-if = os == "mac" || e10s # mac: Bug 939617; e10s - Bug ?????? - "leaked until shutdown [nsGlobalWindow #82 about:blank]"
|
||||
[browser_tabview_bug662266.js]
|
||||
[browser_tabview_bug663421.js]
|
||||
[browser_tabview_bug665502.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tabview_bug669694.js]
|
||||
[browser_tabview_bug673196.js]
|
||||
[browser_tabview_bug673729.js]
|
||||
skip-if = true # Bug 749980
|
||||
[browser_tabview_bug678374.js]
|
||||
skip-if = true # Bug 795265
|
||||
[browser_tabview_bug681599.js]
|
||||
[browser_tabview_bug685476.js]
|
||||
[browser_tabview_bug685692.js]
|
||||
[browser_tabview_bug686654.js]
|
||||
[browser_tabview_bug696602.js]
|
||||
skip-if = true # Bug 736425
|
||||
[browser_tabview_bug697390.js]
|
||||
[browser_tabview_bug705621.js]
|
||||
[browser_tabview_bug706430.js]
|
||||
[browser_tabview_bug706736.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tabview_bug707466.js]
|
||||
[browser_tabview_bug712203.js]
|
||||
[browser_tabview_bug715454.js]
|
||||
[browser_tabview_bug716880.js]
|
||||
[browser_tabview_bug728887.js]
|
||||
[browser_tabview_bug733115.js]
|
||||
[browser_tabview_bug749658.js]
|
||||
[browser_tabview_bug766597.js]
|
||||
[browser_tabview_click_group.js]
|
||||
[browser_tabview_dragdrop.js]
|
||||
[browser_tabview_exit_button.js]
|
||||
[browser_tabview_expander.js]
|
||||
[browser_tabview_firstrun_pref.js]
|
||||
[browser_tabview_group.js]
|
||||
skip-if = os == "mac" || os == "win" # Bug 945687
|
||||
[browser_tabview_launch.js]
|
||||
[browser_tabview_multiwindow_search.js]
|
||||
skip-if = e10s # Bug 1086190
|
||||
[browser_tabview_pending_tabs.js]
|
||||
[browser_tabview_privatebrowsing_perwindowpb.js]
|
||||
skip-if = os == 'linux' || e10s # linux: Bug 944300; e10s: bug ?????? - "leaked until shutdown [nsGlobalWindow #82 about:blank]"
|
||||
[browser_tabview_rtl.js]
|
||||
[browser_tabview_search.js]
|
||||
[browser_tabview_snapping.js]
|
||||
[browser_tabview_startup_transitions.js]
|
||||
[browser_tabview_undo_group.js]
|
|
@ -1,45 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let AllTabs;
|
||||
let newTab = gBrowser.addTab();
|
||||
|
||||
// TabPinned
|
||||
let pinned = function (event) {
|
||||
let tab = event.target;
|
||||
|
||||
is(tab, newTab, "The tabs are the same after the tab is pinned");
|
||||
ok(tab.pinned, "The tab gets pinned");
|
||||
|
||||
gBrowser.unpinTab(tab);
|
||||
};
|
||||
|
||||
// TabUnpinned
|
||||
let unpinned = function (event) {
|
||||
let tab = event.target;
|
||||
|
||||
AllTabs.unregister("pinned", pinned);
|
||||
AllTabs.unregister("unpinned", unpinned);
|
||||
|
||||
is(tab, newTab, "The tabs are the same after the tab is unpinned");
|
||||
ok(!tab.pinned, "The tab gets unpinned");
|
||||
|
||||
// clean up and finish
|
||||
gBrowser.removeTab(tab);
|
||||
hideTabView(finish);
|
||||
};
|
||||
|
||||
showTabView(function () {
|
||||
AllTabs = TabView.getContentWindow().AllTabs;
|
||||
|
||||
AllTabs.register("pinned", pinned);
|
||||
AllTabs.register("unpinned", unpinned);
|
||||
|
||||
ok(!newTab.pinned, "The tab is not pinned");
|
||||
gBrowser.pinTab(newTab);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(onTabViewWindowLoaded);
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = TabView.getContentWindow();
|
||||
|
||||
// establish initial state
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"we start with one group (the default)");
|
||||
is(gBrowser.tabs.length, 1, "we start with one tab");
|
||||
let originalTab = gBrowser.tabs[0];
|
||||
|
||||
// create a group
|
||||
let box = new contentWindow.Rect(20, 20, 180, 180);
|
||||
let groupItemOne = new contentWindow.GroupItem([],
|
||||
{ bounds: box, title: "test1" });
|
||||
is(contentWindow.GroupItems.groupItems.length, 2, "we now have two groups");
|
||||
contentWindow.UI.setActive(groupItemOne);
|
||||
|
||||
// create a tab
|
||||
let xulTab = gBrowser.loadOneTab("about:blank");
|
||||
is(gBrowser.tabs.length, 2, "we now have two tabs");
|
||||
is(groupItemOne._children.length, 1, "the new tab was added to the group");
|
||||
|
||||
// make sure the group has no app tabs
|
||||
is(appTabCount(groupItemOne), 0, "there are no app tab icons");
|
||||
|
||||
// pin the tab, make sure the TabItem goes away and the icon comes on
|
||||
whenAppTabIconAdded(groupItemOne, function () {
|
||||
is(groupItemOne._children.length, 0,
|
||||
"the app tab's TabItem was removed from the group");
|
||||
is(appTabCount(groupItemOne), 1, "there's now one app tab icon");
|
||||
|
||||
// create a second group and make sure it gets the icon too
|
||||
box.offset(box.width + 20, 0);
|
||||
let groupItemTwo = new contentWindow.GroupItem([],
|
||||
{ bounds: box, title: "test2" });
|
||||
whenAppTabIconAdded(groupItemTwo, function() {
|
||||
is(contentWindow.GroupItems.groupItems.length, 3, "we now have three groups");
|
||||
is(appTabCount(groupItemTwo), 1,
|
||||
"there's an app tab icon in the second group");
|
||||
|
||||
// When the tab was pinned, the last active group with an item got the focus.
|
||||
// Therefore, switching the focus back to group item one.
|
||||
contentWindow.UI.setActive(groupItemOne);
|
||||
|
||||
// unpin the tab, make sure the icon goes away and the TabItem comes on
|
||||
gBrowser.unpinTab(xulTab);
|
||||
is(groupItemOne._children.length, 1, "the app tab's TabItem is back");
|
||||
is(appTabCount(groupItemOne), 0, "the icon is gone from group one");
|
||||
is(appTabCount(groupItemTwo), 0, "the icon is gone from group two");
|
||||
|
||||
whenAppTabIconAdded(groupItemOne, function() {
|
||||
// close the second group
|
||||
groupItemTwo.close();
|
||||
|
||||
// find app tab in group and hit it
|
||||
whenTabViewIsHidden(function() {
|
||||
ok(!TabView.isVisible(),
|
||||
"Tab View is hidden because we clicked on the app tab");
|
||||
|
||||
// delete the app tab and make sure its icon goes away
|
||||
gBrowser.removeTab(xulTab);
|
||||
is(appTabCount(groupItemOne), 0, "closing app tab removes its icon");
|
||||
|
||||
// clean up
|
||||
groupItemOne.close();
|
||||
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"we finish with one group");
|
||||
is(gBrowser.tabs.length, 1, "we finish with one tab");
|
||||
ok(!TabView.isVisible(), "we finish with Tab View not visible");
|
||||
|
||||
finish();
|
||||
});
|
||||
|
||||
let appTabIcons = groupItemOne.container.getElementsByClassName("appTabIcon");
|
||||
EventUtils.sendMouseEvent({ type: "click" }, appTabIcons[0], contentWindow);
|
||||
});
|
||||
gBrowser.pinTab(xulTab);
|
||||
});
|
||||
});
|
||||
gBrowser.pinTab(xulTab);
|
||||
}
|
||||
|
||||
function appTabCount(groupItem) {
|
||||
return groupItem.container.getElementsByClassName("appTabIcon").length;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
showTabView(onTabViewShown);
|
||||
}
|
||||
|
||||
function onTabViewShown() {
|
||||
let contentWindow = TabView.getContentWindow();
|
||||
let [originalTab] = gBrowser.visibleTabs;
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "There is only one group");
|
||||
let currentActiveGroup = contentWindow.GroupItems.getActiveGroupItem();
|
||||
|
||||
let endGame = function() {
|
||||
ok(TabView.isVisible(), "TabView is shown");
|
||||
gBrowser.selectedTab = originalTab;
|
||||
|
||||
hideTabView(function () {
|
||||
ok(!TabView.isVisible(), "TabView is hidden");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
// we need to stop the setBounds() css animation or else the test will
|
||||
// fail in single-mode because the group is newly created "ontabshown".
|
||||
let $container = contentWindow.iQ(currentActiveGroup.container);
|
||||
$container.css("transition-property", "none");
|
||||
|
||||
currentActiveGroup.setPosition(40, 40, true);
|
||||
currentActiveGroup.arrange({animate: false});
|
||||
|
||||
// move down 20 so we're far enough away from the top.
|
||||
checkSnap(currentActiveGroup, 0, 20, contentWindow, function(snapped){
|
||||
is(currentActiveGroup.getBounds().top, 60, "group.top is 60px");
|
||||
ok(!snapped,"Move away from the edge");
|
||||
|
||||
// Just pick it up and drop it.
|
||||
checkSnap(currentActiveGroup, 0, 0, contentWindow, function(snapped){
|
||||
is(currentActiveGroup.getBounds().top, 60, "group.top is 60px");
|
||||
ok(!snapped,"Just pick it up and drop it");
|
||||
|
||||
checkSnap(currentActiveGroup, 0, 1, contentWindow, function(snapped){
|
||||
is(currentActiveGroup.getBounds().top, 60, "group.top is 60px");
|
||||
ok(snapped,"Drag one pixel: should snap");
|
||||
|
||||
checkSnap(currentActiveGroup, 0, 5, contentWindow, function(snapped){
|
||||
is(currentActiveGroup.getBounds().top, 65, "group.top is 65px");
|
||||
ok(!snapped,"Moving five pixels: shouldn't snap");
|
||||
endGame();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function simulateDragDrop(item, offsetX, offsetY, contentWindow) {
|
||||
let target = item.container;
|
||||
|
||||
EventUtils.synthesizeMouse(target, 1, 1, {type: "mousedown"}, contentWindow);
|
||||
EventUtils.synthesizeMouse(target, 1 + offsetX, 1 + offsetY, {type: "mousemove"}, contentWindow);
|
||||
EventUtils.synthesizeMouse(target, 1, 1, {type: "mouseup"}, contentWindow);
|
||||
}
|
||||
|
||||
function checkSnap(item, offsetX, offsetY, contentWindow, callback) {
|
||||
let firstTop = item.getBounds().top;
|
||||
let firstLeft = item.getBounds().left;
|
||||
|
||||
simulateDragDrop(item, offsetX, offsetY, contentWindow);
|
||||
|
||||
let snapped = false;
|
||||
if (item.getBounds().top != firstTop + offsetY)
|
||||
snapped = true;
|
||||
if (item.getBounds().left != firstLeft + offsetX)
|
||||
snapped = true;
|
||||
|
||||
callback(snapped);
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
var moves = 0;
|
||||
var contentWindow = null;
|
||||
var newTabs = [];
|
||||
var originalTab = null;
|
||||
|
||||
function onTabMove(e) {
|
||||
let tab = e.target;
|
||||
moves++;
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
originalTab = gBrowser.selectedTab;
|
||||
newTabs = [gBrowser.addTab("about:rights"), gBrowser.addTab("about:mozilla"), gBrowser.addTab("about:license")];
|
||||
|
||||
is(originalTab._tPos, 0, "Original tab is in position 0");
|
||||
is(newTabs[0]._tPos, 1, "Rights is in position 1");
|
||||
is(newTabs[1]._tPos, 2, "Mozilla is in position 2");
|
||||
is(newTabs[2]._tPos, 3, "License is in position 3");
|
||||
|
||||
gBrowser.tabContainer.addEventListener("TabMove", onTabMove, false);
|
||||
|
||||
let groupItem = contentWindow.GroupItems.getActiveGroupItem();
|
||||
|
||||
// move 3 > 0 (and therefore 0 > 1, 1 > 2, 2 > 3)
|
||||
groupItem._children.splice(0, 0, groupItem._children.splice(3, 1)[0]);
|
||||
groupItem.arrange();
|
||||
|
||||
window.addEventListener("tabviewhidden", onTabViewWindowHidden, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowHidden() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewWindowHidden, false);
|
||||
gBrowser.tabContainer.removeEventListener("TabMove", onTabMove, false);
|
||||
|
||||
is(moves, 1, "Only one move should be necessary for this basic move.");
|
||||
|
||||
is(newTabs[2]._tPos, 0, "License is in position 0");
|
||||
is(originalTab._tPos, 1, "Original tab is in position 1");
|
||||
is(newTabs[0]._tPos, 2, "Rights is in position 2");
|
||||
is(newTabs[1]._tPos, 3, "Mozilla is in position 3");
|
||||
|
||||
gBrowser.removeTab(newTabs[0]);
|
||||
gBrowser.removeTab(newTabs[1]);
|
||||
gBrowser.removeTab(newTabs[2]);
|
||||
finish();
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let onTabViewHidden = function() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
|
||||
finish();
|
||||
};
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
document.getElementById("cmd_closeWindow").doCommand();
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var activeTab;
|
||||
var testTab;
|
||||
var testGroup;
|
||||
var contentWindow;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// create new tab
|
||||
testTab = gBrowser.addTab("about:blank");
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
// create group
|
||||
let testGroupRect = new contentWindow.Rect(20, 20, 300, 300);
|
||||
testGroup = new contentWindow.GroupItem([], { bounds: testGroupRect });
|
||||
ok(testGroup.isEmpty(), "This group is empty");
|
||||
|
||||
ok(testTab._tabViewTabItem, "tab item exists");
|
||||
|
||||
// place tab in group
|
||||
let testTabItem = testTab._tabViewTabItem;
|
||||
|
||||
if (testTabItem.parent)
|
||||
testTabItem.parent.remove(testTabItem);
|
||||
testGroup.add(testTabItem);
|
||||
|
||||
ok(testTab._tabViewTabItem, "tab item exists after adding to group");
|
||||
|
||||
// record last update time of tab canvas
|
||||
let initialUpdateTime = testTabItem._lastTabUpdateTime;
|
||||
|
||||
// simulate resize
|
||||
let resizer = contentWindow.iQ('.iq-resizable-handle', testGroup.container)[0];
|
||||
let offsetX = 100;
|
||||
let offsetY = 100;
|
||||
let delay = 500;
|
||||
|
||||
let funcChain = new Array();
|
||||
funcChain.push(function() {
|
||||
EventUtils.synthesizeMouse(
|
||||
resizer, 1, 1, { type: "mousedown" }, contentWindow);
|
||||
setTimeout(funcChain.shift(), delay);
|
||||
});
|
||||
// drag
|
||||
for (let i = 4; i >= 0; i--) {
|
||||
funcChain.push(function() {
|
||||
EventUtils.synthesizeMouse(
|
||||
resizer, Math.round(offsetX/4), Math.round(offsetY/4),
|
||||
{ type: "mousemove" }, contentWindow);
|
||||
setTimeout(funcChain.shift(), delay);
|
||||
});
|
||||
}
|
||||
funcChain.push(function() {
|
||||
EventUtils.synthesizeMouse(resizer, 0, 0, { type: "mouseup" },
|
||||
contentWindow);
|
||||
setTimeout(funcChain.shift(), delay);
|
||||
});
|
||||
funcChain.push(function() {
|
||||
// verify that update time has changed after last update
|
||||
let lastTime = testTabItem._lastTabUpdateTime;
|
||||
let hbTiming = contentWindow.TabItems._heartbeatTiming;
|
||||
ok((lastTime - initialUpdateTime) > hbTiming, "Tab has been updated:"+lastTime+"-"+initialUpdateTime+">"+hbTiming);
|
||||
|
||||
// clean up
|
||||
testGroup.remove(testTab._tabViewTabItem);
|
||||
testTab._tabViewTabItem.close();
|
||||
testGroup.close();
|
||||
|
||||
let currentTabs = contentWindow.TabItems.getItems();
|
||||
ok(currentTabs[0], "A tab item exists to make active");
|
||||
contentWindow.UI.setActive(currentTabs[0]);
|
||||
|
||||
window.addEventListener("tabviewhidden", finishTest, false);
|
||||
TabView.toggle();
|
||||
});
|
||||
setTimeout(funcChain.shift(), delay);
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
window.removeEventListener("tabviewhidden", finishTest, false);
|
||||
ok(!TabView.isVisible(), "Tab View is not visible");
|
||||
|
||||
finish();
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var contentWindow;
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(test1);
|
||||
}
|
||||
|
||||
function test1() {
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
whenTabViewIsHidden(function() {
|
||||
ok(!TabView.isVisible(), "Tab View is not visible");
|
||||
showTabView(test2);
|
||||
});
|
||||
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, contentWindow);
|
||||
}
|
||||
|
||||
function test2() {
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
whenSearchIsEnabled(function() {
|
||||
ok(contentWindow.Search.isEnabled(), "The search is enabled")
|
||||
|
||||
whenSearchIsDisabled(test3);
|
||||
hideSearch();
|
||||
});
|
||||
EventUtils.synthesizeKey("f", { accelKey: true }, contentWindow);
|
||||
}
|
||||
|
||||
function test3() {
|
||||
ok(!contentWindow.Search.isEnabled(), "The search is disabled")
|
||||
|
||||
is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + t is pressed");
|
||||
|
||||
whenTabViewIsHidden(function() {
|
||||
is(gBrowser.tabs.length, 2, "There are two tabs after cmd/ctrl + t is pressed");
|
||||
|
||||
gBrowser.tabs[0].linkedBrowser.loadURI("about:mozilla");
|
||||
gBrowser.tabs[1].linkedBrowser.loadURI("http://example.com/");
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
showTabView(test4);
|
||||
});
|
||||
});
|
||||
EventUtils.synthesizeKey("t", { accelKey: true }, contentWindow);
|
||||
}
|
||||
|
||||
function test4() {
|
||||
is(gBrowser.tabs.length, 2, "There are two tabs");
|
||||
|
||||
let onTabClose = function() {
|
||||
gBrowser.tabContainer.removeEventListener("TabClose", onTabClose, true);
|
||||
executeSoon(function() {
|
||||
is(gBrowser.tabs.length, 1, "There is one tab after removing one");
|
||||
|
||||
EventUtils.synthesizeKey("t", { accelKey: true, shiftKey: true }, contentWindow);
|
||||
is(gBrowser.tabs.length, 2, "There are two tabs after restoring one");
|
||||
|
||||
gBrowser.tabs[1].linkedBrowser.addEventListener("load", function listener() {
|
||||
gBrowser.tabs[1].linkedBrowser.removeEventListener("load", listener, true);
|
||||
|
||||
gBrowser.tabs[0].linkedBrowser.loadURI("about:blank");
|
||||
gBrowser.selectedTab = gBrowser.tabs[0];
|
||||
test8();
|
||||
}, true);
|
||||
});
|
||||
};
|
||||
gBrowser.tabContainer.addEventListener("TabClose", onTabClose, true);
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
}
|
||||
|
||||
// below key combination shouldn't trigger actions in tabview UI
|
||||
function test8() {
|
||||
showTabView(function() {
|
||||
is(gBrowser.tabs.length, 2, "There are two tabs before cmd/ctrl + w is pressed");
|
||||
EventUtils.synthesizeKey("w", { accelKey: true }, contentWindow);
|
||||
is(gBrowser.tabs.length, 2, "There are two tabs after cmd/ctrl + w is pressed");
|
||||
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
test9();
|
||||
});
|
||||
}
|
||||
|
||||
function test9() {
|
||||
let zoomLevel = ZoomManager.zoom;
|
||||
EventUtils.synthesizeKey("+", { accelKey: true }, contentWindow);
|
||||
is(ZoomManager.zoom, zoomLevel, "The zoom level remains unchanged after cmd/ctrl + + is pressed");
|
||||
|
||||
EventUtils.synthesizeKey("-", { accelKey: true }, contentWindow);
|
||||
is(ZoomManager.zoom, zoomLevel, "The zoom level remains unchanged after cmd/ctrl + - is pressed");
|
||||
|
||||
test10();
|
||||
}
|
||||
|
||||
function test10() {
|
||||
is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + shift + a is pressed");
|
||||
// it would open about:addons on a new tab if it passes through the white list.
|
||||
EventUtils.synthesizeKey("a", { accelKey: true, shiftKey: true }, contentWindow);
|
||||
|
||||
executeSoon(function() {
|
||||
is(gBrowser.tabs.length, 1, "There is still one tab after cmd/ctrl + shift + a is pressed");
|
||||
hideTabView(finish);
|
||||
})
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var newTab;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newTab = gBrowser.addTab();
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "Has one group only");
|
||||
|
||||
let tabItems = contentWindow.GroupItems.groupItems[0].getChildren();
|
||||
ok(tabItems.length, 2, "There are two tabItems in the group");
|
||||
|
||||
is(tabItems[1].tab, newTab, "The second tabItem is linked to the new tab");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown", button: 1 }, tabItems[1].container, contentWindow);
|
||||
EventUtils.sendMouseEvent({ type: "mouseup", button: 1 }, tabItems[1].container, contentWindow);
|
||||
|
||||
ok(tabItems.length, 1, "There is only one tabItem in the group");
|
||||
|
||||
let endGame = function() {
|
||||
window.removeEventListener("tabviewhidden", endGame, false);
|
||||
|
||||
finish();
|
||||
};
|
||||
window.addEventListener("tabviewhidden", endGame, false);
|
||||
TabView.toggle();
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
function moveTabOutOfGroup(aTab, aGroup, aCW, aCallback) {
|
||||
let tabPos = aTab.getBounds().center();
|
||||
let groupPos = aGroup.getBounds();
|
||||
let groupPosX = (groupPos.left + groupPos.right) / 2;
|
||||
let groupPosY = groupPos.bottom + 200;
|
||||
let offsetX = Math.round(groupPosX - tabPos.x);
|
||||
let offsetY = Math.round(groupPosY - tabPos.y);
|
||||
|
||||
simulateDragDrop(aTab, offsetX, offsetY, aCW);
|
||||
}
|
||||
|
||||
function moveTabInGroup(aTab, aIndexTo, aGroup, aCW) {
|
||||
let tabTo = aGroup.getChild(aIndexTo);
|
||||
let tabPos = aTab.getBounds().center();
|
||||
let tabToPos = tabTo.getBounds().center();
|
||||
let offsetX = Math.round(tabToPos.x - tabPos.x);
|
||||
let offsetY = Math.round(tabToPos.y - tabPos.y);
|
||||
|
||||
simulateDragDrop(aTab, offsetX, offsetY, aCW);
|
||||
}
|
||||
|
||||
function getTabNumbers(aGroup) {
|
||||
return aGroup.getChildren().map(function (child) {
|
||||
let url = child.tab.linkedBrowser.currentURI.spec;
|
||||
return url.replace("about:blank#", "");
|
||||
}).join(",");
|
||||
}
|
||||
|
||||
function moveTabs(aGroup, aCW) {
|
||||
// Test 1: Move the last tab to the third position.
|
||||
let tab = aGroup.getChild(6);
|
||||
moveTabInGroup(tab, 2, aGroup, aCW);
|
||||
is(getTabNumbers(aGroup), "0,1,6,2,3,4,5", "Validate tab positions in test 1.");
|
||||
|
||||
// Test 2: Move the second tab to the end of the list.
|
||||
tab = aGroup.getChild(1);
|
||||
moveTabInGroup(tab, 6, aGroup, aCW);
|
||||
is(getTabNumbers(aGroup), "0,6,2,3,4,5,1", "Validate tab positions in test 2.");
|
||||
|
||||
// Test 3: Move the fifth tab outside the group.
|
||||
tab = aGroup.getChild(4);
|
||||
moveTabOutOfGroup(tab, aGroup, aCW);
|
||||
is(getTabNumbers(aGroup), "0,6,2,3,5,1", "Validate tab positions in test 3.");
|
||||
is(aCW.GroupItems.groupItems.length, 3, "Validate group count in test 3.");
|
||||
|
||||
// This test is disabled because it is fragile -- see bug 797975
|
||||
if (false) {
|
||||
// Test 4: Move the fifth tab back into the group, on the second row.
|
||||
waitForTransition(tab, function() {
|
||||
moveTabInGroup(tab, 4, aGroup, aCW);
|
||||
is(getTabNumbers(aGroup), "0,6,2,3,4,5,1", "Validate tab positions in test 4.");
|
||||
is(aCW.GroupItems.groupItems.length, 2, "Validate group count in test 4.");
|
||||
closeGroupItem(aGroup, function() { hideTabView(finish) });
|
||||
});
|
||||
} else {
|
||||
closeGroupItem(aGroup, function() { hideTabView(finish) });
|
||||
}
|
||||
}
|
||||
|
||||
function createGroup(win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
let cw = win.TabView.getContentWindow();
|
||||
let group = createGroupItemWithTabs(win, 400, 430, 100, [
|
||||
"about:blank#0", "about:blank#1", "about:blank#2", "about:blank#3",
|
||||
"about:blank#4", "about:blank#5", "about:blank#6"
|
||||
]);
|
||||
let groupSize = group.getChildren().length;
|
||||
|
||||
is(cw.GroupItems.groupItems.length, 2, "Validate group count in tab view.");
|
||||
ok(!group.shouldStack(groupSize), "Check that group should not stack.");
|
||||
is(group._columns, 3, "Check the there should be three columns.");
|
||||
|
||||
moveTabs(group, cw);
|
||||
}
|
||||
|
||||
newWindowWithTabView(createGroup);
|
||||
}
|
||||
|
||||
function simulateDragDrop(aTab, aOffsetX, aOffsetY, aCW) {
|
||||
let target = aTab.container;
|
||||
let rect = target.getBoundingClientRect();
|
||||
let startX = (rect.right - rect.left) / 2;
|
||||
let startY = (rect.bottom - rect.top) / 2;
|
||||
let steps = 2;
|
||||
let incrementX = aOffsetX / steps;
|
||||
let incrementY = aOffsetY / steps;
|
||||
|
||||
EventUtils.synthesizeMouse(
|
||||
target, startX, startY, { type: "mousedown" }, aCW);
|
||||
for (let i = 1; i <= steps; i++) {
|
||||
EventUtils.synthesizeMouse(
|
||||
target, incrementX + startX, incrementY + startY,
|
||||
{ type: "mousemove" }, aCW);
|
||||
};
|
||||
EventUtils.synthesizeMouseAtCenter(target, { type: "mouseup" }, aCW);
|
||||
}
|
||||
|
||||
function waitForTransition(aTab, aCallback) {
|
||||
let groupContainer = aTab.parent.container;
|
||||
groupContainer.addEventListener("transitionend", function onTransitionEnd(aEvent) {
|
||||
if (aEvent.target == groupContainer) {
|
||||
groupContainer.removeEventListener("transitionend", onTransitionEnd);
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let newTab;
|
||||
|
||||
let onLoad = function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
newTab = win.gBrowser.addTab();
|
||||
|
||||
let popup = win.document.getElementById("context_tabViewMenuPopup");
|
||||
win.TabView.updateContextMenu(newTab, popup);
|
||||
};
|
||||
|
||||
let onShow = function (win) {
|
||||
let cw = win.TabView.getContentWindow();
|
||||
is(cw.GroupItems.groupItems.length, 1, "Has only one group");
|
||||
|
||||
let groupItem = cw.GroupItems.groupItems[0];
|
||||
let tabItems = groupItem.getChildren();
|
||||
|
||||
let tab = tabItems[tabItems.length - 1].tab;
|
||||
is(tab, newTab, "The new tab exists in the group");
|
||||
|
||||
finish();
|
||||
};
|
||||
|
||||
waitForExplicitFinish();
|
||||
newWindowWithTabView(onShow, onLoad);
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var contentWindow;
|
||||
var groupItemTwoId;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
while (gBrowser.tabs[1])
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
hideTabView();
|
||||
});
|
||||
gBrowser.loadOneTab("about:blank", { inBackground: true });
|
||||
showTabView(setup);
|
||||
}
|
||||
|
||||
function setup() {
|
||||
registerCleanupFunction(function() {
|
||||
let groupItem = contentWindow.GroupItems.groupItem(groupItemTwoId);
|
||||
if (groupItem)
|
||||
closeGroupItem(groupItem);
|
||||
});
|
||||
|
||||
let contentWindow = TabView.getContentWindow();
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group");
|
||||
|
||||
let groupItemOne = contentWindow.GroupItems.groupItems[0];
|
||||
is(groupItemOne.getChildren().length, 2, "Group one has 2 tab items");
|
||||
|
||||
let groupItemTwo = createGroupItemWithBlankTabs(window, 250, 250, 40, 1);
|
||||
groupItemTwoId = groupItemTwo.id;
|
||||
testGroups(groupItemOne, groupItemTwo, contentWindow);
|
||||
}
|
||||
|
||||
function testGroups(groupItemOne, groupItemTwo, contentWindow) {
|
||||
// check active tab and group
|
||||
is(contentWindow.GroupItems.getActiveGroupItem(), groupItemTwo,
|
||||
"The group two is the active group");
|
||||
is(contentWindow.UI.getActiveTab(), groupItemTwo.getChild(0),
|
||||
"The first tab item in group two is active");
|
||||
|
||||
let tabItem = groupItemOne.getChild(1);
|
||||
tabItem.addSubscriber("tabRemoved", function onTabRemoved() {
|
||||
tabItem.removeSubscriber("tabRemoved", onTabRemoved);
|
||||
|
||||
is(groupItemOne.getChildren().length, 1,
|
||||
"The num of childen in group one is 1");
|
||||
|
||||
// check active group and active tab
|
||||
is(contentWindow.GroupItems.getActiveGroupItem(), groupItemOne,
|
||||
"The group one is the active group");
|
||||
is(contentWindow.UI.getActiveTab(), groupItemOne.getChild(0),
|
||||
"The first tab item in group one is active");
|
||||
|
||||
whenTabViewIsHidden(function() {
|
||||
is(groupItemOne.getChildren().length, 2,
|
||||
"The num of childen in group one is 2");
|
||||
|
||||
// clean up and finish
|
||||
closeGroupItem(groupItemTwo, function() {
|
||||
gBrowser.removeTab(groupItemOne.getChild(1).tab);
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group");
|
||||
is(groupItemOne.getChildren().length, 1,
|
||||
"The num of childen in group one is 1");
|
||||
is(gBrowser.tabs.length, 1, "Has only one tab");
|
||||
|
||||
finish();
|
||||
});
|
||||
});
|
||||
EventUtils.synthesizeKey("t", { accelKey: true });
|
||||
});
|
||||
// close a tab item in group one
|
||||
EventUtils.synthesizeMouseAtCenter(tabItem.$close[0], {}, contentWindow);
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const DUMMY_PAGE_URL = "http://mochi.test:8888/browser/browser/components/tabview/test/dummy_page.html";
|
||||
const DUMMY_PAGE_URL_2 = "http://mochi.test:8888/";
|
||||
|
||||
var state = {
|
||||
windows: [{
|
||||
tabs: [{
|
||||
entries: [{ url: DUMMY_PAGE_URL }],
|
||||
hidden: true,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab":
|
||||
'{"bounds":{"left":21,"top":29,"width":204,"height":153},' +
|
||||
'"userSize":null,"url":"' + DUMMY_PAGE_URL + '","groupID":1,' +
|
||||
'"imageData":null,"title":null}'
|
||||
}
|
||||
},{
|
||||
entries: [{ url: DUMMY_PAGE_URL_2 }],
|
||||
hidden: false,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab":
|
||||
'{"bounds":{"left":315,"top":29,"width":111,"height":84},' +
|
||||
'"userSize":null,"url":"' + DUMMY_PAGE_URL_2 + '","groupID":2,' +
|
||||
'"imageData":null,"title":null}'
|
||||
},
|
||||
}],
|
||||
selected:2,
|
||||
_closedTabs: [],
|
||||
extData: {
|
||||
"tabview-groups": '{"nextID":3,"activeGroupId":2}',
|
||||
"tabview-group":
|
||||
'{"1":{"bounds":{"left":15,"top":5,"width":280,"height":232},' +
|
||||
'"userSize":null,"title":"","id":1},' +
|
||||
'"2":{"bounds":{"left":309,"top":5,"width":267,"height":226},' +
|
||||
'"userSize":null,"title":"","id":2}}',
|
||||
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":788,"height":548}}'
|
||||
}, sizemode:"normal"
|
||||
}]
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false);
|
||||
|
||||
testTabSwitchAfterRestore(function () {
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", true);
|
||||
testTabSwitchAfterRestore(finish);
|
||||
});
|
||||
}
|
||||
|
||||
function testTabSwitchAfterRestore(callback) {
|
||||
newWindowWithState(state, function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
let [firstTab, secondTab] = win.gBrowser.tabs;
|
||||
is(firstTab.linkedBrowser.currentURI.spec, DUMMY_PAGE_URL,
|
||||
"The url of first tab url is dummy_page.html");
|
||||
|
||||
// check the hidden state of both tabs.
|
||||
ok(firstTab.hidden, "The first tab is hidden");
|
||||
ok(!secondTab.hidden, "The second tab is not hidden");
|
||||
is(secondTab, win.gBrowser.selectedTab, "The second tab is selected");
|
||||
|
||||
// when the second tab is hidden, Panorama should be initialized and
|
||||
// the first tab should be visible.
|
||||
let container = win.gBrowser.tabContainer;
|
||||
container.addEventListener("TabHide", function onTabHide() {
|
||||
container.removeEventListener("TabHide", onTabHide, false);
|
||||
|
||||
ok(win.TabView.getContentWindow(), "Panorama is loaded");
|
||||
ok(!firstTab.hidden, "The first tab is not hidden");
|
||||
is(firstTab, win.gBrowser.selectedTab, "The first tab is selected");
|
||||
ok(secondTab.hidden, "The second tab is hidden");
|
||||
|
||||
callback();
|
||||
}, false);
|
||||
|
||||
// switch to another tab
|
||||
win.switchToTabHavingURI(DUMMY_PAGE_URL);
|
||||
});
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var originalTab;
|
||||
var newTabOne;
|
||||
var groupItemTwoId;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
originalTab = gBrowser.visibleTabs[0];
|
||||
// add a tab to the existing group.
|
||||
newTabOne = gBrowser.addTab();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
while (gBrowser.tabs[1])
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
hideTabView();
|
||||
});
|
||||
|
||||
showTabView(function() {
|
||||
let contentWindow = TabView.getContentWindow();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
let groupItem = contentWindow.GroupItems.groupItem(groupItemTwoId);
|
||||
if (groupItem)
|
||||
closeGroupItem(groupItem);
|
||||
});
|
||||
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"There is one group item on startup");
|
||||
let groupItemOne = contentWindow.GroupItems.groupItems[0];
|
||||
is(groupItemOne.getChildren().length, 2,
|
||||
"There should be two tab items in that group.");
|
||||
is(gBrowser.selectedTab, groupItemOne.getChild(0).tab,
|
||||
"The currently selected tab should be the first tab in the groupItemOne");
|
||||
|
||||
// create another group with a tab.
|
||||
let groupItemTwo = createGroupItemWithBlankTabs(window, 300, 300, 200, 1);
|
||||
groupItemTwoId = groupItemTwoId;
|
||||
hideTabView(function() {
|
||||
// start the test
|
||||
testGroupSwitch(contentWindow, groupItemOne, groupItemTwo);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testGroupSwitch(contentWindow, groupItemOne, groupItemTwo) {
|
||||
is(gBrowser.selectedTab, groupItemTwo.getChild(0).tab,
|
||||
"The currently selected tab should be the only tab in the groupItemTwo");
|
||||
|
||||
// switch to groupItemOne
|
||||
let tabItem = contentWindow.GroupItems.getNextGroupItemTab(false);
|
||||
if (tabItem)
|
||||
gBrowser.selectedTab = tabItem.tab;
|
||||
is(gBrowser.selectedTab, groupItemOne.getChild(0).tab,
|
||||
"The currently selected tab should be the first tab in the groupItemOne");
|
||||
|
||||
// switch to the second tab in groupItemOne
|
||||
gBrowser.selectedTab = groupItemOne.getChild(1).tab;
|
||||
|
||||
// switch to groupItemTwo
|
||||
tabItem = contentWindow.GroupItems.getNextGroupItemTab(false);
|
||||
if (tabItem)
|
||||
gBrowser.selectedTab = tabItem.tab;
|
||||
is(gBrowser.selectedTab, groupItemTwo.getChild(0).tab,
|
||||
"The currently selected tab should be the only tab in the groupItemTwo");
|
||||
|
||||
// switch to groupItemOne
|
||||
tabItem = contentWindow.GroupItems.getNextGroupItemTab(false);
|
||||
if (tabItem)
|
||||
gBrowser.selectedTab = tabItem.tab;
|
||||
is(gBrowser.selectedTab, groupItemOne.getChild(1).tab,
|
||||
"The currently selected tab should be the second tab in the groupItemOne");
|
||||
|
||||
// cleanup.
|
||||
gBrowser.removeTab(groupItemTwo.getChild(0).tab);
|
||||
gBrowser.removeTab(newTabOne);
|
||||
|
||||
finish();
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
if (TabView.isVisible())
|
||||
onTabViewWindowLoaded();
|
||||
else
|
||||
TabView.show();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
let [originalTab] = gBrowser.visibleTabs;
|
||||
|
||||
// Create a first tab and orphan it
|
||||
let firstTab = gBrowser.loadOneTab("about:blank#1", {inBackground: true});
|
||||
let firstTabItem = firstTab._tabViewTabItem;
|
||||
let currentGroup = contentWindow.GroupItems.getActiveGroupItem();
|
||||
ok(currentGroup.getChildren().some(child => child == firstTabItem),"The first tab was made in the current group");
|
||||
contentWindow.GroupItems.getActiveGroupItem().remove(firstTabItem);
|
||||
ok(!currentGroup.getChildren().some(child => child == firstTabItem),"The first tab was orphaned");
|
||||
|
||||
// Create a group and make it active
|
||||
let box = new contentWindow.Rect(10, 10, 300, 300);
|
||||
let group = new contentWindow.GroupItem([], { bounds: box });
|
||||
ok(group.isEmpty(), "This group is empty");
|
||||
contentWindow.UI.setActive(group);
|
||||
|
||||
// Create a second tab in this new group
|
||||
let secondTab = gBrowser.loadOneTab("about:blank#2", {inBackground: true});
|
||||
let secondTabItem = secondTab._tabViewTabItem;
|
||||
ok(group.getChildren().some(child => child == secondTabItem),"The second tab was made in our new group");
|
||||
is(group.getChildren().length, 1, "Only one tab in the first group");
|
||||
isnot(firstTab.linkedBrowser.currentURI.spec, secondTab.linkedBrowser.currentURI.spec, "The two tabs must have different locations");
|
||||
|
||||
// Add the first tab to the group *programmatically*, without specifying a dropPos
|
||||
group.add(firstTabItem);
|
||||
is(group.getChildren().length, 2, "Two tabs in the group");
|
||||
|
||||
is(group.getChildren()[0].tab.linkedBrowser.currentURI.spec, secondTab.linkedBrowser.currentURI.spec, "The second tab was there first");
|
||||
is(group.getChildren()[1].tab.linkedBrowser.currentURI.spec, firstTab.linkedBrowser.currentURI.spec, "The first tab was just added and went to the end of the line");
|
||||
|
||||
group.addSubscriber("close", function onClose() {
|
||||
group.removeSubscriber("close", onClose);
|
||||
|
||||
ok(group.isEmpty(), "The group is empty again");
|
||||
|
||||
is(contentWindow.GroupItems.getActiveGroupItem(), currentGroup, "There is an active group");
|
||||
is(gBrowser.tabs.length, 1, "There is only one tab left");
|
||||
is(gBrowser.visibleTabs.length, 1, "There is also only one visible tab");
|
||||
|
||||
let onTabViewHidden = function() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
finish();
|
||||
};
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
gBrowser.selectedTab = originalTab;
|
||||
|
||||
TabView.hide();
|
||||
});
|
||||
|
||||
// Get rid of the group and its children
|
||||
group.closeAll();
|
||||
// close undo group
|
||||
let closeButton = group.$undoContainer.find(".close");
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" }, closeButton[0], contentWindow);
|
||||
}
|
||||
|
||||
function simulateDragDrop(srcElement, offsetX, offsetY, contentWindow) {
|
||||
// enter drag mode
|
||||
let dataTransfer;
|
||||
|
||||
EventUtils.synthesizeMouse(
|
||||
srcElement, 1, 1, { type: "mousedown" }, contentWindow);
|
||||
event = contentWindow.document.createEvent("DragEvents");
|
||||
event.initDragEvent(
|
||||
"dragenter", true, true, contentWindow, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 1, null, dataTransfer);
|
||||
srcElement.dispatchEvent(event);
|
||||
|
||||
// drag over
|
||||
for (let i = 4; i >= 0; i--)
|
||||
EventUtils.synthesizeMouse(
|
||||
srcElement, Math.round(offsetX/5), Math.round(offsetY/4),
|
||||
{ type: "mousemove" }, contentWindow);
|
||||
event = contentWindow.document.createEvent("DragEvents");
|
||||
event.initDragEvent(
|
||||
"dragover", true, true, contentWindow, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
srcElement.dispatchEvent(event);
|
||||
|
||||
// drop
|
||||
EventUtils.synthesizeMouse(srcElement, 0, 0, { type: "mouseup" }, contentWindow);
|
||||
event = contentWindow.document.createEvent("DragEvents");
|
||||
event.initDragEvent(
|
||||
"drop", true, true, contentWindow, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, null, dataTransfer);
|
||||
srcElement.dispatchEvent(event);
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const DUMMY_PAGE_URL = "http://example.com/";
|
||||
|
||||
var state = {
|
||||
windows: [{
|
||||
tabs: [{
|
||||
entries: [{ url: DUMMY_PAGE_URL }],
|
||||
hidden: false,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab": '{"url":"' + DUMMY_PAGE_URL + '","groupID":1,"title":null,"active":true}'
|
||||
}
|
||||
},{
|
||||
entries: [{ url: DUMMY_PAGE_URL }],
|
||||
hidden: false,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab": '{"url":"' + DUMMY_PAGE_URL + '","groupID":1,"title":null}'
|
||||
}
|
||||
},{
|
||||
entries: [{ url: DUMMY_PAGE_URL }],
|
||||
hidden: true,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab": '{"url":"' + DUMMY_PAGE_URL + '","groupID":2,"title":null}'
|
||||
},
|
||||
},{
|
||||
entries: [{ url: DUMMY_PAGE_URL }],
|
||||
hidden: true,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab": '{"url":"' + DUMMY_PAGE_URL + '","groupID":2,"title":null,"active":true}'
|
||||
},
|
||||
}],
|
||||
selected:1,
|
||||
_closedTabs: [],
|
||||
extData: {
|
||||
"tabview-groups": '{"nextID":3,"activeGroupId":2,"totalNumber":2}',
|
||||
"tabview-group":
|
||||
'{"1":{"bounds":{"left":15,"top":28,"width":546,"height":218},' +
|
||||
'"userSize":{"x":546,"y":218},"title":"","id":1},' +
|
||||
'"2":{"bounds":{"left":15,"top":261,"width":546,"height":199},' +
|
||||
'"userSize":{"x":546,"y":199},"title":"","id":2}}',
|
||||
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":976,"height":663}}'
|
||||
}, sizemode:"normal"
|
||||
}]
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithState(state, function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
showTabView(function() {
|
||||
let cw = win.TabView.getContentWindow();
|
||||
let groupItems = cw.GroupItems.groupItems;
|
||||
let groupOne = groupItems[0];
|
||||
let groupTwo = groupItems[1];
|
||||
|
||||
// check the active tab of each group
|
||||
is(groupOne.getActiveTab(), groupOne.getChild(0), "The active tab item of group one is the first one");
|
||||
is(groupTwo.getActiveTab(), groupTwo.getChild(1), "The active tab item of group two is the second one");
|
||||
|
||||
is(cw.UI.getActiveTab(), groupOne.getChild(0), "The hightlighted tab item is the first one in group one");
|
||||
// select a group and the second tab should be hightlighted
|
||||
cw.UI.setActive(groupTwo);
|
||||
is(cw.UI.getActiveTab(), groupTwo.getChild(1), "The hightlighted tab item is the second one in group two");
|
||||
|
||||
finish();
|
||||
}, win);
|
||||
});
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// show the tab view
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
let searchButton = contentWindow.document.getElementById("searchbutton");
|
||||
|
||||
ok(searchButton, "Search button exists");
|
||||
|
||||
let onSearchEnabled = function() {
|
||||
contentWindow.removeEventListener(
|
||||
"tabviewsearchenabled", onSearchEnabled, false);
|
||||
let search = contentWindow.document.getElementById("search");
|
||||
ok(search.style.display != "none", "Search is enabled");
|
||||
escapeTest(contentWindow);
|
||||
}
|
||||
contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled,
|
||||
false);
|
||||
// enter search mode
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, searchButton,
|
||||
contentWindow);
|
||||
}
|
||||
|
||||
function escapeTest(contentWindow) {
|
||||
let onSearchDisabled = function() {
|
||||
contentWindow.removeEventListener(
|
||||
"tabviewsearchdisabled", onSearchDisabled, false);
|
||||
|
||||
let search = contentWindow.document.getElementById("search");
|
||||
ok(search.style.display == "none", "Search is disabled");
|
||||
toggleTabViewTest(contentWindow);
|
||||
}
|
||||
contentWindow.addEventListener("tabviewsearchdisabled", onSearchDisabled,
|
||||
false);
|
||||
EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" }, contentWindow);
|
||||
}
|
||||
|
||||
function toggleTabViewTest(contentWindow) {
|
||||
let onTabViewHidden = function() {
|
||||
contentWindow.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
finish();
|
||||
}
|
||||
contentWindow.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
// Use keyboard shortcut to toggle back to browser view
|
||||
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true,
|
||||
code: "KeyE", keyCode: KeyboardEvent.DOM_VK_E }, contentWindow);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let cw, search, searchButton;
|
||||
|
||||
let assertSearchIsEnabled = function () {
|
||||
isnot(search.style.display, "none", "search is enabled");
|
||||
}
|
||||
|
||||
let assertSearchIsDisabled = function () {
|
||||
is(search.style.display, "none", "search is disabled");
|
||||
}
|
||||
|
||||
let testSearchInitiatedByKeyPress = function () {
|
||||
EventUtils.synthesizeKey("a", {}, cw);
|
||||
assertSearchIsEnabled();
|
||||
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {}, cw);
|
||||
assertSearchIsDisabled();
|
||||
}
|
||||
|
||||
let testSearchInitiatedByMouseClick = function () {
|
||||
EventUtils.sendMouseEvent({type: "mousedown"}, searchButton, cw);
|
||||
assertSearchIsEnabled();
|
||||
|
||||
EventUtils.synthesizeKey("a", {}, cw);
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {}, cw);
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {}, cw);
|
||||
assertSearchIsEnabled();
|
||||
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, cw);
|
||||
assertSearchIsDisabled();
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
cw = win.TabView.getContentWindow();
|
||||
search = cw.document.getElementById("search");
|
||||
searchButton = cw.document.getElementById("searchbutton");
|
||||
|
||||
SimpleTest.waitForFocus(function () {
|
||||
assertSearchIsDisabled();
|
||||
|
||||
testSearchInitiatedByKeyPress();
|
||||
testSearchInitiatedByMouseClick();
|
||||
|
||||
finish();
|
||||
}, cw);
|
||||
});
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// show tab view
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
let onTabViewHidden = function() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
|
||||
// verify exit button worked
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
|
||||
// verify that the exit button no longer has focus
|
||||
is(contentWindow.iQ("#exit-button:focus").length, 0,
|
||||
"The exit button doesn't have the focus");
|
||||
|
||||
// verify that the keyboard combo works (this is the crux of bug 595518)
|
||||
// Prepare the key combo
|
||||
window.addEventListener("tabviewshown", onTabViewShown, false);
|
||||
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true, code: "KeyE", keyCode: KeyboardEvent.DOM_VK_E }, contentWindow);
|
||||
}
|
||||
|
||||
let onTabViewShown = function() {
|
||||
window.removeEventListener("tabviewshown", onTabViewShown, false);
|
||||
|
||||
// test if the key combo worked
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
// clean up
|
||||
let endGame = function() {
|
||||
window.removeEventListener("tabviewhidden", endGame, false);
|
||||
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
finish();
|
||||
}
|
||||
window.addEventListener("tabviewhidden", endGame, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
|
||||
// locate exit button
|
||||
let button = contentWindow.document.getElementById("exit-button");
|
||||
ok(button, "Exit button exists");
|
||||
|
||||
// click exit button
|
||||
button.focus();
|
||||
EventUtils.sendMouseEvent({ type: "click" }, button, contentWindow);
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var fadeAwayUndoButtonDelay;
|
||||
var fadeAwayUndoButtonDuration;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
window.addEventListener("tabviewshown", testCloseLastGroup, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function testCloseLastGroup() {
|
||||
window.removeEventListener("tabviewshown", testCloseLastGroup, false);
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "Has one group only");
|
||||
|
||||
let groupItem = contentWindow.GroupItems.groupItems[0];
|
||||
|
||||
let checkExistence = function() {
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"Still has one group after delay");
|
||||
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" }, groupItem.$undoContainer[0], contentWindow);
|
||||
};
|
||||
|
||||
groupItem.addSubscriber("groupHidden", function onHidden() {
|
||||
groupItem.removeSubscriber("groupHidden", onHidden);
|
||||
// it should still stay after 3 ms.
|
||||
setTimeout(checkExistence, 3);
|
||||
});
|
||||
|
||||
groupItem.addSubscriber("groupShown", function onShown() {
|
||||
groupItem.removeSubscriber("groupShown", onShown);
|
||||
|
||||
let endGame = function() {
|
||||
window.removeEventListener("tabviewhidden", endGame, false);
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
|
||||
groupItem.fadeAwayUndoButtonDelay = fadeAwayUndoButtonDelay;
|
||||
groupItem.fadeAwayUndoButtonDuration = fadeAwayUndoButtonDuration;
|
||||
|
||||
finish();
|
||||
};
|
||||
window.addEventListener("tabviewhidden", endGame, false);
|
||||
|
||||
TabView.toggle();
|
||||
});
|
||||
|
||||
let closeButton = groupItem.container.getElementsByClassName("close");
|
||||
ok(closeButton, "Group item close button exists");
|
||||
|
||||
// store the original values
|
||||
fadeAwayUndoButtonDelay = groupItem.fadeAwayUndoButtonDelay;
|
||||
fadeAwayUndoButtonDuration = groupItem.fadeAwayUndoButtonDuration;
|
||||
|
||||
// set both fade away delay and duration to 1ms
|
||||
groupItem.fadeAwayUndoButtonDelay = 1;
|
||||
groupItem.fadeAwayUndoButtonDuration = 1;
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "click" }, closeButton[0], contentWindow);
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var win;
|
||||
var cw;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let onLoad = function (tvwin) {
|
||||
win = tvwin;
|
||||
registerCleanupFunction(() => win.close());
|
||||
win.gBrowser.loadOneTab("http://mochi.test:8888/", {inBackground: true});
|
||||
};
|
||||
|
||||
let onShow = function () {
|
||||
cw = win.TabView.getContentWindow();
|
||||
ok(win.TabView.isVisible(), "Tab View is visible");
|
||||
afterAllTabItemsUpdated(testOne, win);
|
||||
};
|
||||
|
||||
newWindowWithTabView(onShow, onLoad);
|
||||
}
|
||||
|
||||
function testOne() {
|
||||
hideSearchWhenSearchEnabled(testTwo);
|
||||
// press cmd/ctrl F
|
||||
EventUtils.synthesizeKey("f", {accelKey: true}, cw);
|
||||
}
|
||||
|
||||
function testTwo() {
|
||||
hideSearchWhenSearchEnabled(testThree);
|
||||
// press /
|
||||
EventUtils.synthesizeKey("VK_SLASH", {}, cw);
|
||||
}
|
||||
|
||||
function testThree() {
|
||||
ok(win.TabView.isVisible(), "Tab View is visible");
|
||||
// create another group with a tab.
|
||||
let groupItem = createGroupItemWithBlankTabs(win, 300, 300, 200, 1);
|
||||
is(cw.UI.getActiveTab(), groupItem.getChild(0),
|
||||
"The active tab is newly created tab item");
|
||||
|
||||
whenSearchIsEnabled(function () {
|
||||
let doc = cw.document;
|
||||
let searchBox = cw.iQ("#searchbox");
|
||||
let hasFocus = doc.hasFocus() && doc.activeElement == searchBox[0];
|
||||
ok(hasFocus, "The search box has focus");
|
||||
|
||||
let tab = win.gBrowser.tabs[1];
|
||||
searchBox.val(tab._tabViewTabItem.$tabTitle[0].innerHTML);
|
||||
|
||||
cw.Search.perform();
|
||||
|
||||
whenTabViewIsHidden(function () {
|
||||
is(tab, win.gBrowser.selectedTab, "The search result tab is shown");
|
||||
finish()
|
||||
}, win);
|
||||
|
||||
// use the tabview menu (the same as pressing cmd/ctrl + e)
|
||||
win.document.getElementById("menu_tabview").doCommand();
|
||||
}, win);
|
||||
EventUtils.synthesizeKey("VK_SLASH", {}, cw);
|
||||
}
|
||||
|
||||
function hideSearchWhenSearchEnabled(callback) {
|
||||
whenSearchIsEnabled(function() {
|
||||
hideSearch(callback, win);
|
||||
}, win);
|
||||
}
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
|
||||
const TAB_STATE_NEEDS_RESTORE = 1;
|
||||
const TAB_STATE_RESTORING = 2;
|
||||
|
||||
var stateBackup = ss.getBrowserState();
|
||||
|
||||
var state = {windows:[{tabs:[
|
||||
// first group
|
||||
{entries:[{url:"http://example.com#1"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#1\",\"groupID\":2}"}},
|
||||
{entries:[{url:"http://example.com#2"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#2\",\"groupID\":2}"}},
|
||||
{entries:[{url:"http://example.com#3"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#3\",\"groupID\":2}"}},
|
||||
{entries:[{url:"http://example.com#4"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#4\",\"groupID\":2}"}},
|
||||
|
||||
// second group
|
||||
{entries:[{url:"http://example.com#5"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#5\",\"groupID\":1}"}},
|
||||
{entries:[{url:"http://example.com#6"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#6\",\"groupID\":1}"}},
|
||||
{entries:[{url:"http://example.com#7"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#7\",\"groupID\":1}"}},
|
||||
{entries:[{url:"http://example.com#8"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#8\",\"groupID\":1}"}}
|
||||
],selected:4,extData:{
|
||||
"tabview-groups":"{\"nextID\":8,\"activeGroupId\":1}","tabview-group":"{\"1\":{\"bounds\":{\"left\":15,\"top\":10,\"width\":415,\"height\":367},\"userSize\":{\"x\":415,\"y\":367},\"title\":\"\",\"id\":1},\"2\":{\"bounds\":{\"left\":286,\"top\":488,\"width\":418,\"height\":313},\"title\":\"\",\"id\":2}}",
|
||||
"tabview-ui":"{\"pageBounds\":{\"left\":0,\"top\":0,\"width\":940,\"height\":1075}}"
|
||||
}}]};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false);
|
||||
|
||||
TabsProgressListener.init();
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
TabsProgressListener.uninit();
|
||||
|
||||
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
|
||||
|
||||
gBrowser.selectedTab = gBrowser.tabs[0];
|
||||
ss.setBrowserState(stateBackup);
|
||||
});
|
||||
|
||||
TabView._initFrame(function () {
|
||||
executeSoon(testRestoreWithHiddenTabs);
|
||||
});
|
||||
}
|
||||
|
||||
function testRestoreWithHiddenTabs() {
|
||||
TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
|
||||
if (needsRestore <= 4) {
|
||||
TabsProgressListener.unsetCallback();
|
||||
is(needsRestore, 4, "4/8 tabs restored");
|
||||
}
|
||||
});
|
||||
|
||||
waitForBrowserState(state, 4, function () {
|
||||
is(gBrowser.tabs.length, 8, "there are now eight tabs");
|
||||
is(gBrowser.visibleTabs.length, 4, "four visible tabs");
|
||||
|
||||
let cw = TabView.getContentWindow();
|
||||
is(cw.GroupItems.groupItems.length, 2, "there are now two groupItems");
|
||||
|
||||
testSwitchToInactiveGroup();
|
||||
});
|
||||
}
|
||||
|
||||
function testSwitchToInactiveGroup() {
|
||||
let firstProgress = true;
|
||||
|
||||
TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
|
||||
if (firstProgress) {
|
||||
firstProgress = false;
|
||||
is(isRestoring, 3, "restoring 3 tabs concurrently");
|
||||
} else {
|
||||
ok(isRestoring < 4, "restoring max. 3 tabs concurrently");
|
||||
}
|
||||
|
||||
if (needsRestore)
|
||||
return;
|
||||
|
||||
TabsProgressListener.unsetCallback();
|
||||
|
||||
is(gBrowser.visibleTabs.length, 4, "four visible tabs");
|
||||
waitForFocus(finish);
|
||||
});
|
||||
|
||||
gBrowser.selectedTab = gBrowser.tabs[4];
|
||||
}
|
||||
|
||||
function waitForBrowserState(state, numTabs, callback) {
|
||||
let tabContainer = gBrowser.tabContainer;
|
||||
tabContainer.addEventListener("SSTabRestored", function onRestored() {
|
||||
if (--numTabs <= 0) {
|
||||
tabContainer.removeEventListener("SSTabRestored", onRestored, true);
|
||||
executeSoon(callback);
|
||||
}
|
||||
}, true);
|
||||
|
||||
ss.setBrowserState(JSON.stringify(state));
|
||||
}
|
||||
|
||||
function countTabs() {
|
||||
let needsRestore = 0, isRestoring = 0;
|
||||
let windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
let window = windowsEnum.getNext();
|
||||
if (window.closed)
|
||||
continue;
|
||||
|
||||
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
|
||||
let browser = window.gBrowser.tabs[i].linkedBrowser;
|
||||
if (browser.__SS_restoreState == TAB_STATE_RESTORING)
|
||||
isRestoring++;
|
||||
else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
|
||||
needsRestore++;
|
||||
}
|
||||
}
|
||||
|
||||
return [needsRestore, isRestoring];
|
||||
}
|
||||
|
||||
var TabsProgressListener = {
|
||||
init: function () {
|
||||
gBrowser.addTabsProgressListener(this);
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
this.unsetCallback();
|
||||
gBrowser.removeTabsProgressListener(this);
|
||||
},
|
||||
|
||||
setCallback: function (callback) {
|
||||
this.callback = callback;
|
||||
},
|
||||
|
||||
unsetCallback: function () {
|
||||
delete this.callback;
|
||||
},
|
||||
|
||||
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
let isNetwork = aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
|
||||
let isWindow = aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
|
||||
|
||||
if (!(this.callback && isNetwork && isWindow))
|
||||
return;
|
||||
|
||||
let self = this;
|
||||
let finalize = function () {
|
||||
if (wasRestoring)
|
||||
delete aBrowser.__wasRestoring;
|
||||
|
||||
self.callback.apply(null, countTabs());
|
||||
};
|
||||
|
||||
let isRestoring = aBrowser.__SS_restoreState == TAB_STATE_RESTORING;
|
||||
let wasRestoring = !aBrowser.__SS_restoreState && aBrowser.__wasRestoring;
|
||||
let hasStopped = aStateFlags & Ci.nsIWebProgressListener.STATE_STOP;
|
||||
|
||||
if (isRestoring && !hasStopped)
|
||||
aBrowser.__wasRestoring = true;
|
||||
|
||||
if (hasStopped && (isRestoring || wasRestoring))
|
||||
finalize();
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var prefsBranch = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefService).
|
||||
getBranch("browser.panorama.");
|
||||
|
||||
function animateZoom() {
|
||||
return prefsBranch.getBoolPref("animate_zoom");
|
||||
}
|
||||
|
||||
var contentWindow = null;
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
// reset to default: true
|
||||
prefsBranch.setBoolPref("animate_zoom", true);
|
||||
});
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
waitForExplicitFinish();
|
||||
|
||||
ok(!TabView.isVisible(), "Tab View is not visible");
|
||||
|
||||
window.addEventListener("tabviewshown", startTesting, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function startTesting() {
|
||||
window.removeEventListener("tabviewshown", startTesting, false);
|
||||
|
||||
contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
ok(animateZoom(), "By default, we want to animate");
|
||||
|
||||
function finishOnHidden() {
|
||||
contentWindow.removeEventListener("tabviewhidden", finishOnHidden, false);
|
||||
ok(!TabView.isVisible(), "Tab View is not visible");
|
||||
finish();
|
||||
}
|
||||
|
||||
function wrapup() {
|
||||
contentWindow.addEventListener("tabviewhidden", finishOnHidden, false);
|
||||
TabView.hide();
|
||||
}
|
||||
|
||||
function part2() {
|
||||
prefsBranch.setBoolPref("animate_zoom", false);
|
||||
ok(!animateZoom(), "animate_zoom = false");
|
||||
zoomInAndOut(false, wrapup);
|
||||
}
|
||||
|
||||
function part1() {
|
||||
prefsBranch.setBoolPref("animate_zoom",true);
|
||||
ok(animateZoom(), "animate_zoom = true");
|
||||
zoomInAndOut(true, part2);
|
||||
}
|
||||
|
||||
// start it up!
|
||||
part1();
|
||||
}
|
||||
|
||||
function zoomInAndOut(transitionsExpected, callback) {
|
||||
let transitioned = 0;
|
||||
|
||||
contentWindow.document.addEventListener("transitionend", onTransitionEnd, false);
|
||||
function onTransitionEnd(event) {
|
||||
transitioned++;
|
||||
}
|
||||
|
||||
let onHidden = function() {
|
||||
contentWindow.removeEventListener("tabviewhidden", onHidden, false);
|
||||
if (transitionsExpected)
|
||||
ok(transitioned >= 0, "There can be transitions");
|
||||
else
|
||||
ok(!transitioned, "There should have been no transitions");
|
||||
TabView.toggle();
|
||||
};
|
||||
|
||||
let onShownAgain = function() {
|
||||
contentWindow.removeEventListener("tabviewshown", onShownAgain, false);
|
||||
if (transitionsExpected)
|
||||
ok(transitioned >= 0, "There can be transitions");
|
||||
else
|
||||
ok(!transitioned, "There should have been no transitions");
|
||||
|
||||
contentWindow.document.removeEventListener("transitionend", onTransitionEnd, false);
|
||||
callback();
|
||||
};
|
||||
|
||||
contentWindow.addEventListener("tabviewhidden", onHidden, false);
|
||||
contentWindow.addEventListener("tabviewshown", onShownAgain, false);
|
||||
|
||||
// get this party started by hiding tab view
|
||||
TabView.toggle();
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let [originalTab] = gBrowser.visibleTabs;
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
// create group which we'll close
|
||||
let box1 = new contentWindow.Rect(310, 10, 300, 300);
|
||||
let group1 = new contentWindow.GroupItem([], { bounds: box1 });
|
||||
ok(group1.isEmpty(), "This group is empty");
|
||||
contentWindow.UI.setActive(group1);
|
||||
let tab1 = gBrowser.loadOneTab("about:blank#1", {inBackground: true});
|
||||
let tab1Item = tab1._tabViewTabItem;
|
||||
ok(group1.getChildren().some(child => child == tab1Item), "The tab was made in our new group");
|
||||
is(group1.getChildren().length, 1, "Only one tab in the first group");
|
||||
|
||||
group1.addSubscriber("close", function onClose() {
|
||||
group1.removeSubscriber("close", onClose);
|
||||
|
||||
let onTabViewHidden = function() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
// assert that we're no longer in tab view
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
finish();
|
||||
};
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
|
||||
// delay to give time for hidden group DOM element to be removed so
|
||||
// the appropriate group would get selected when the key
|
||||
// combination is pressed
|
||||
executeSoon(function() {
|
||||
EventUtils.synthesizeKey("e", {accelKey : true, shiftKey: true}, contentWindow);
|
||||
});
|
||||
});
|
||||
|
||||
hideGroupItem(group1, function () {
|
||||
// close undo group
|
||||
let closeButton = group1.$undoContainer.find(".close");
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "click" }, closeButton[0], contentWindow);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Show TabView
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
// establish initial state
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "we start with one group (the default)");
|
||||
is(gBrowser.tabs.length, 1, "we start with one tab");
|
||||
|
||||
let originalTab = gBrowser.tabs[0];
|
||||
ok(contentWindow.GroupItems.groupItems[0]._children[0].tab == originalTab,
|
||||
"the original tab is in the original group");
|
||||
|
||||
// create a second group
|
||||
let box = new contentWindow.Rect(20, 20, 180, 180);
|
||||
let groupItem = new contentWindow.GroupItem([], { bounds: box });
|
||||
is(contentWindow.GroupItems.groupItems.length, 2, "we now have two groups");
|
||||
contentWindow.UI.setActive(groupItem);
|
||||
|
||||
// create a second tab
|
||||
let normalXulTab = gBrowser.loadOneTab("about:blank");
|
||||
is(gBrowser.tabs.length, 2, "we now have two tabs");
|
||||
is(groupItem._children.length, 1, "the new tab was added to the group");
|
||||
|
||||
// create a third tab
|
||||
let appXulTab = gBrowser.loadOneTab("about:blank");
|
||||
is(gBrowser.tabs.length, 3, "we now have three tabs");
|
||||
gBrowser.pinTab(appXulTab);
|
||||
is(groupItem._children.length, 1, "the app tab is not in the group");
|
||||
|
||||
// We now have two groups with one tab each, plus an app tab.
|
||||
// Click into one of the tabs, close it and make sure we don't go back to Tab View.
|
||||
function onTabViewHidden() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
ok(!TabView.isVisible(), "Tab View is hidden because we clicked on the app tab");
|
||||
|
||||
// Remove the tab we're looking at.
|
||||
gBrowser.removeTab(normalXulTab);
|
||||
|
||||
// Make sure we haven't returned to TabView; this is the crux of this test
|
||||
ok(!TabView.isVisible(), "Tab View remains hidden");
|
||||
|
||||
// clean up
|
||||
gBrowser.selectedTab = originalTab;
|
||||
|
||||
gBrowser.unpinTab(appXulTab);
|
||||
gBrowser.removeTab(appXulTab);
|
||||
|
||||
ok(groupItem.closeIfEmpty(), "the second group was empty");
|
||||
|
||||
// Verify ending state
|
||||
is(gBrowser.tabs.length, 1, "we finish with one tab");
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"we finish with one group");
|
||||
ok(!TabView.isVisible(), "we finish with Tab View hidden");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, normalXulTab._tabViewTabItem.container, contentWindow);
|
||||
EventUtils.sendMouseEvent({ type: "mouseup" }, normalXulTab._tabViewTabItem.container, contentWindow);
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* Contributor(s):
|
||||
* Mihai Sucan <mihai.sucan@gmail.com>
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
* Ian Gilman <ian@iangilman.com>
|
||||
*/
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(onTabViewShown);
|
||||
}
|
||||
|
||||
function onTabViewShown(win) {
|
||||
let TabView = win.TabView;
|
||||
let gBrowser = win.gBrowser;
|
||||
let document = win.document;
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
let iQ = contentWindow.iQ;
|
||||
|
||||
// establish initial state
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"we start with one group (the default)");
|
||||
is(gBrowser.tabs.length, 1, "we start with one tab");
|
||||
let originalTab = gBrowser.tabs[0];
|
||||
|
||||
// create a group
|
||||
let box = new contentWindow.Rect(20, 20, 210, 200);
|
||||
let groupItem = new contentWindow.GroupItem([],
|
||||
{ bounds: box, title: "test1" });
|
||||
is(contentWindow.GroupItems.groupItems.length, 2, "we now have two groups");
|
||||
contentWindow.UI.setActive(groupItem);
|
||||
|
||||
// create a tab
|
||||
let xulTabs = [];
|
||||
xulTabs.push(gBrowser.loadOneTab("about:blank"));
|
||||
is(gBrowser.tabs.length, 2, "we now have two tabs");
|
||||
is(groupItem._children.length, 1, "the new tab was added to the group");
|
||||
|
||||
// make sure the group has no app tabs
|
||||
is(appTabCount(groupItem), 0, "there are no app tab icons");
|
||||
|
||||
let tray = groupItem.$appTabTray;
|
||||
let trayContainer = iQ(tray[0].parentNode);
|
||||
|
||||
is(parseInt(trayContainer.css("width")), 0,
|
||||
"$appTabTray container is not visible");
|
||||
|
||||
// pin the tab, make sure the TabItem goes away and the icon comes on
|
||||
whenAppTabIconAdded(groupItem, function () {
|
||||
is(groupItem._children.length, 0,
|
||||
"the app tab's TabItem was removed from the group");
|
||||
is(appTabCount(groupItem), 1, "there's now one app tab icon");
|
||||
|
||||
is(tray.css("-moz-column-count"), 1,
|
||||
"$appTabTray column count is 1");
|
||||
isnot(parseInt(trayContainer.css("width")), 0,
|
||||
"$appTabTray container is visible");
|
||||
|
||||
|
||||
let iconHeight = iQ(iQ(".appTabIcon", tray)[0]).height();
|
||||
let trayHeight = parseInt(trayContainer.css("height"));
|
||||
let rows = Math.floor(trayHeight / iconHeight);
|
||||
let icons = rows * 2;
|
||||
|
||||
function pinnedSomeTabs() {
|
||||
is(appTabCount(groupItem), icons, "number of app tab icons is correct");
|
||||
|
||||
is(tray.css("-moz-column-count"), 2,
|
||||
"$appTabTray column count is 2");
|
||||
|
||||
ok(!trayContainer.hasClass("appTabTrayContainerTruncated"),
|
||||
"$appTabTray container does not have .appTabTrayContainerTruncated");
|
||||
|
||||
// add one more tab
|
||||
xulTabs.push(gBrowser.loadOneTab("about:blank"));
|
||||
whenAppTabIconAdded(groupItem, function () {
|
||||
is(tray.css("-moz-column-count"), 3,
|
||||
"$appTabTray column count is 3");
|
||||
|
||||
ok(trayContainer.hasClass("appTabTrayContainerTruncated"),
|
||||
"$appTabTray container hasClass .appTabTrayContainerTruncated");
|
||||
|
||||
// remove all but one app tabs
|
||||
for (let i = 1; i < xulTabs.length; i++)
|
||||
gBrowser.removeTab(xulTabs[i]);
|
||||
|
||||
is(tray.css("-moz-column-count"), 1,
|
||||
"$appTabTray column count is 1");
|
||||
|
||||
is(appTabCount(groupItem), 1, "there's now one app tab icon");
|
||||
|
||||
ok(!trayContainer.hasClass("appTabTrayContainerTruncated"),
|
||||
"$appTabTray container does not have .appTabTrayContainerTruncated");
|
||||
|
||||
// unpin the last remaining tab
|
||||
gBrowser.unpinTab(xulTabs[0]);
|
||||
|
||||
is(parseInt(trayContainer.css("width")), 0,
|
||||
"$appTabTray container is not visible");
|
||||
|
||||
// When the tab was pinned, the last active group with an item got the focus.
|
||||
// Therefore, switching the focus back to group item one.
|
||||
contentWindow.UI.setActive(groupItem);
|
||||
|
||||
is(appTabCount(groupItem), 0, "there are no app tab icons");
|
||||
|
||||
is(groupItem._children.length, 1, "the normal tab shows in the group");
|
||||
|
||||
gBrowser.removeTab(xulTabs[0]);
|
||||
|
||||
// close the group
|
||||
groupItem.close();
|
||||
|
||||
hideTabView(function() {
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"we finish with one group");
|
||||
is(gBrowser.tabs.length, 1, "we finish with one tab");
|
||||
|
||||
win.close();
|
||||
|
||||
executeSoon(finish);
|
||||
}, win);
|
||||
});
|
||||
win.gBrowser.pinTab(xulTabs[xulTabs.length-1]);
|
||||
};
|
||||
|
||||
// add enough tabs to have two columns
|
||||
let returnCount = 0;
|
||||
for (let i = 1; i < icons; i++) {
|
||||
xulTabs.push(gBrowser.loadOneTab("about:blank"));
|
||||
whenAppTabIconAdded(groupItem, function () {
|
||||
if (++returnCount == (icons - 1))
|
||||
executeSoon(pinnedSomeTabs);
|
||||
});
|
||||
win.gBrowser.pinTab(xulTabs[i]);
|
||||
}
|
||||
});
|
||||
win.gBrowser.pinTab(xulTabs[0]);
|
||||
}
|
||||
|
||||
function appTabCount(groupItem) {
|
||||
return groupItem.container.getElementsByClassName("appTabIcon").length;
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var newTab;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newTab = gBrowser.addTab();
|
||||
gBrowser.pinTab(newTab);
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "Only one group exists");
|
||||
is(gBrowser.tabs.length, 2, "Only one tab exists");
|
||||
ok(newTab.pinned, "The original tab is pinned");
|
||||
|
||||
let groupItem = contentWindow.GroupItems.groupItems[0];
|
||||
is(groupItem.$closeButton[0].style.display, "none",
|
||||
"The close button is hidden");
|
||||
|
||||
gBrowser.unpinTab(newTab);
|
||||
is(groupItem.$closeButton[0].style.display, "",
|
||||
"The close button is visible");
|
||||
|
||||
let onTabViewHidden = function() {
|
||||
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
gBrowser.removeTab(newTab);
|
||||
finish();
|
||||
}
|
||||
window.addEventListener("tabviewhidden", onTabViewHidden, false);
|
||||
TabView.toggle();
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
let cw = win.TabView.getContentWindow();
|
||||
let groupItems = cw.GroupItems.groupItems;
|
||||
let groupItem = groupItems[0];
|
||||
|
||||
is(groupItems.length, 1, "There is one group item on startup");
|
||||
|
||||
whenTabViewIsHidden(function () {
|
||||
is(groupItems.length, 1, "There is still one group item");
|
||||
isnot(groupItem.id, groupItems[0].id,
|
||||
"The initial group item is not the same as the final group item");
|
||||
is(win.gBrowser.tabs.length, 1, "There is only one tab");
|
||||
|
||||
finish();
|
||||
}, win);
|
||||
|
||||
hideGroupItem(groupItem, function () {
|
||||
// create a new tab
|
||||
EventUtils.synthesizeKey("t", { accelKey: true }, cw);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
let contentWindow = document.getElementById("tab-view").contentWindow;
|
||||
let number = -1;
|
||||
|
||||
let onSearchEnabled = function() {
|
||||
// ensure the dom changes (textbox get focused with number entered) complete
|
||||
// before doing a check.
|
||||
executeSoon(function() {
|
||||
let searchBox = contentWindow.document.getElementById("searchbox");
|
||||
is(searchBox.value, number, "The seach box matches the number: " + number);
|
||||
contentWindow.Search.hide(null);
|
||||
});
|
||||
}
|
||||
let onSearchDisabled = function() {
|
||||
if (++number <= 9) {
|
||||
EventUtils.synthesizeKey(String(number), { }, contentWindow);
|
||||
} else {
|
||||
contentWindow.removeEventListener(
|
||||
"tabviewsearchenabled", onSearchEnabled, false);
|
||||
contentWindow.removeEventListener(
|
||||
"tabviewsearchdisabled", onSearchDisabled, false);
|
||||
|
||||
let endGame = function() {
|
||||
window.removeEventListener("tabviewhidden", endGame, false);
|
||||
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
finish();
|
||||
}
|
||||
window.addEventListener("tabviewhidden", endGame, false);
|
||||
TabView.toggle();
|
||||
}
|
||||
}
|
||||
contentWindow.addEventListener(
|
||||
"tabviewsearchenabled", onSearchEnabled, false);
|
||||
contentWindow.addEventListener(
|
||||
"tabviewsearchdisabled", onSearchDisabled, false);
|
||||
|
||||
onSearchDisabled();
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(part1);
|
||||
}
|
||||
|
||||
function part1(win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
let contentWindow = win.TabView.getContentWindow();
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group");
|
||||
|
||||
let originalTab = win.gBrowser.selectedTab;
|
||||
let originalGroup = contentWindow.GroupItems.groupItems[0];
|
||||
let newTab = win.gBrowser.loadOneTab("about:blank", {inBackground: true});
|
||||
|
||||
is(originalGroup.getChildren().length, 2, "The original group now has two tabs");
|
||||
|
||||
// create group two with the new tab
|
||||
let box = new contentWindow.Rect(300,300,150,150);
|
||||
let newGroup = new contentWindow.GroupItem([], {bounds: box, immediately: true});
|
||||
newGroup.add(newTab._tabViewTabItem, {immediately: true});
|
||||
|
||||
// ensure active group item and tab
|
||||
contentWindow.UI.setActive(originalGroup);
|
||||
is(contentWindow.GroupItems.getActiveGroupItem(), originalGroup,
|
||||
"The original group is active");
|
||||
is(contentWindow.UI.getActiveTab(), originalTab._tabViewTabItem,
|
||||
"The original tab is active");
|
||||
|
||||
function checkActive(callback, time) {
|
||||
is(contentWindow.GroupItems.getActiveGroupItem(), newGroup,
|
||||
"The new group is active");
|
||||
is(contentWindow.UI.getActiveTab(), newTab._tabViewTabItem,
|
||||
"The new tab is active");
|
||||
if (time)
|
||||
setTimeout(callback, time);
|
||||
else
|
||||
callback();
|
||||
}
|
||||
|
||||
// click on the new tab, and check that the new tab and group are active
|
||||
// at two times: 10 ms after (still during the animation) and
|
||||
// 500 ms after (after the animation, hopefully). Either way, the new
|
||||
// tab and group should be active.
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
newTab._tabViewTabItem.container, contentWindow);
|
||||
EventUtils.sendMouseEvent({ type: "mouseup" },
|
||||
newTab._tabViewTabItem.container, contentWindow);
|
||||
setTimeout(function() {
|
||||
checkActive(function() {
|
||||
checkActive(function() {
|
||||
win.close();
|
||||
newWindowWithTabView(part2);
|
||||
});
|
||||
}, 490);
|
||||
}, 10)
|
||||
}
|
||||
|
||||
function part2(win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
let newTab = win.gBrowser.loadOneTab("about:blank", {inBackground: true});
|
||||
hideTabView(function() {
|
||||
let selectedTab = win.gBrowser.selectedTab;
|
||||
isnot(selectedTab, newTab, "They are different tabs");
|
||||
|
||||
// switch the selected tab to new tab
|
||||
win.gBrowser.selectedTab = newTab;
|
||||
|
||||
showTabView(function () {
|
||||
hideTabView(function () {
|
||||
is(win.gBrowser.selectedTab, newTab,
|
||||
"The selected tab should be the same as before (new tab)");
|
||||
waitForFocus(finish);
|
||||
}, win);
|
||||
}, win);
|
||||
}, win);
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
let cw = win.TabView.getContentWindow();
|
||||
let groupItem = cw.GroupItems.groupItems[0];
|
||||
groupItem.setBounds(new cw.Rect(cw.innerWidth - 200, 0, 200, 200));
|
||||
|
||||
whenTabViewIsHidden(() => waitForFocus(finish), win);
|
||||
|
||||
waitForFocus(function () {
|
||||
let button = cw.document.getElementById("exit-button");
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, cw);
|
||||
}, cw);
|
||||
});
|
||||
|
||||
// This test relies on the test timing out in order to indicate failure so
|
||||
// let's add a dummy pass.
|
||||
ok(true, "Each test requires at least one pass, fail or todo so here is a pass.");
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var newWin;
|
||||
function test() {
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
|
||||
requestLongerTimeout(2);
|
||||
waitForExplicitFinish();
|
||||
|
||||
// open a new window and setup the window state.
|
||||
newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", "about:blank");
|
||||
whenWindowLoaded(newWin, function () {
|
||||
let newState = {
|
||||
windows: [{
|
||||
tabs: [{
|
||||
entries: [{ "url": "about:blank" }],
|
||||
hidden: true,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab":
|
||||
'{"bounds":{"left":20,"top":35,"width":280,"height":210},' +
|
||||
'"userSize":null,"url":"about:blank","groupID":1,' +
|
||||
'"imageData":null,"title":null}'
|
||||
}
|
||||
},{
|
||||
entries: [{ url: "about:blank" }],
|
||||
index: 1,
|
||||
hidden: false,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab":
|
||||
'{"bounds":{"left":375,"top":35,"width":280,"height":210},' +
|
||||
'"userSize":null,"url":"about:blank","groupID":2,' +
|
||||
'"imageData":null,"title":null}'
|
||||
}
|
||||
}],
|
||||
selected:2,
|
||||
_closedTabs: [],
|
||||
extData: {
|
||||
"tabview-groups": '{"nextID":3,"activeGroupId":2}',
|
||||
"tabview-group":
|
||||
'{"1":{"bounds":{"left":15,"top":10,"width":320,"height":375},' +
|
||||
'"userSize":null,"title":"","id":1},' +
|
||||
'"2":{"bounds":{"left":380,"top":5,"width":320,"height":375},' +
|
||||
'"userSize":null,"title":"","id":2}}',
|
||||
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":875,"height":650}}'
|
||||
}, sizemode:"normal"
|
||||
}]
|
||||
};
|
||||
ss.setWindowState(newWin, JSON.stringify(newState), true);
|
||||
|
||||
// add a new tab.
|
||||
newWin.gBrowser.addTab();
|
||||
is(newWin.gBrowser.tabs.length, 3, "There are 3 browser tabs");
|
||||
|
||||
let onTabViewShow = function() {
|
||||
newWin.removeEventListener("tabviewshown", onTabViewShow, false);
|
||||
|
||||
let contentWindow = newWin.TabView.getContentWindow();
|
||||
is(contentWindow.GroupItems.groupItems.length, 2, "Has two group items");
|
||||
|
||||
// clean up and finish
|
||||
newWin.close();
|
||||
|
||||
finish();
|
||||
}
|
||||
newWin.addEventListener("tabviewshown", onTabViewShow, false);
|
||||
waitForFocus(function() { newWin.TabView.toggle(); });
|
||||
});
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var cw;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(function() {
|
||||
cw = TabView.getContentWindow();
|
||||
|
||||
whenSearchIsEnabled(function() {
|
||||
ok(cw.Search.isEnabled(), "The search is disabled");
|
||||
|
||||
// open a new window and it would have the focus
|
||||
newWindowWithTabView(function(win) {
|
||||
registerCleanupFunction(function() {
|
||||
win.close();
|
||||
hideTabView();
|
||||
});
|
||||
testClickOnSearchShade(win);
|
||||
});
|
||||
});
|
||||
|
||||
EventUtils.synthesizeKey("VK_SLASH", {}, cw);
|
||||
});
|
||||
}
|
||||
|
||||
function testClickOnSearchShade(win) {
|
||||
// click on the window with search enabled.
|
||||
let searchshade = cw.document.getElementById("searchshade");
|
||||
EventUtils.sendMouseEvent({ type: "click" }, searchshade, cw);
|
||||
|
||||
waitForFocus(function() {
|
||||
ok(cw.Search.isEnabled(), "The search is still enabled after the search shade is clicked");
|
||||
testFocusInactiveWindow(win, cw);
|
||||
});
|
||||
}
|
||||
|
||||
function testFocusInactiveWindow(win) {
|
||||
win.focus();
|
||||
// focus inactive window
|
||||
window.focus();
|
||||
|
||||
// need to use exeuteSoon as the _blockClick would be set to false after a setTimeout(,0)
|
||||
executeSoon(function() {
|
||||
ok(cw.Search.isEnabled(), "The search is still enabled when inactive window has focus");
|
||||
|
||||
whenSearchIsDisabled(function() {
|
||||
hideTabView(finish);
|
||||
});
|
||||
|
||||
let searchshade = cw.document.getElementById("searchshade");
|
||||
EventUtils.synthesizeMouseAtCenter(searchshade, {}, cw);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URL = 'data:text/html,<script>window.onbeforeunload=' +
|
||||
'function(e){e.returnValue="?"}</script>';
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
showTabView(onTabViewShown);
|
||||
}
|
||||
|
||||
function onTabViewShown() {
|
||||
let contentWindow = TabView.getContentWindow();
|
||||
let groupItemOne = contentWindow.GroupItems.getActiveGroupItem();
|
||||
let groupItemTwo = createGroupItemWithTabs(window, 300, 300, 10, [TEST_URL]);
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
testStayOnPage(contentWindow, groupItemOne, groupItemTwo);
|
||||
});
|
||||
}
|
||||
|
||||
function testStayOnPage(contentWindow, groupItemOne, groupItemTwo) {
|
||||
// We created a new tab group with a second tab above, so let's
|
||||
// pick that second tab here and wait for its onbeforeunload dialog.
|
||||
let browser = gBrowser.browsers[1];
|
||||
waitForOnBeforeUnloadDialog(browser, function (btnLeave, btnStay) {
|
||||
executeSoon(function () {
|
||||
is(gBrowser.tabs.length, 2,
|
||||
"The total number of tab is 2 when staying on the page");
|
||||
is(contentWindow.TabItems.getItems().length, 2,
|
||||
"The total number of tab items is 2 when staying on the page");
|
||||
|
||||
showTabView(function () {
|
||||
// start the next test
|
||||
testLeavePage(contentWindow, groupItemOne, groupItemTwo);
|
||||
});
|
||||
});
|
||||
|
||||
// stay on page
|
||||
btnStay.click();
|
||||
});
|
||||
|
||||
closeGroupItem(groupItemTwo);
|
||||
}
|
||||
|
||||
function testLeavePage(contentWindow, groupItemOne, groupItemTwo) {
|
||||
// The second tab hasn't been closed yet because we chose to stay. Wait
|
||||
// for the onbeforeunload dialog again and leave the page this time.
|
||||
let browser = gBrowser.browsers[1];
|
||||
waitForOnBeforeUnloadDialog(browser, function (btnLeave, btnStay) {
|
||||
// clean up and finish the test
|
||||
groupItemTwo.addSubscriber("close", function onClose() {
|
||||
groupItemTwo.removeSubscriber("close", onClose);
|
||||
|
||||
is(gBrowser.tabs.length, 1,
|
||||
"The total number of tab is 1 after leaving the page");
|
||||
is(contentWindow.TabItems.getItems().length, 1,
|
||||
"The total number of tab items is 1 after leaving the page");
|
||||
|
||||
hideTabView(finish);
|
||||
});
|
||||
|
||||
// Leave page
|
||||
btnLeave.click();
|
||||
});
|
||||
|
||||
closeGroupItem(groupItemTwo);
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests that, when there is an app tab that references an invalid
|
||||
* favicon, the default favicon appears the group app tab tray, instead of an
|
||||
* empty image that would not be visible.
|
||||
*/
|
||||
|
||||
const fi = Cc["@mozilla.org/browser/favicon-service;1"].
|
||||
getService(Ci.nsIFaviconService);
|
||||
|
||||
var newTab;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newTab = gBrowser.addTab();
|
||||
|
||||
showTabView(function() {
|
||||
let cw = TabView.getContentWindow();
|
||||
whenAppTabIconAdded(cw.GroupItems.groupItems[0], onTabPinned);
|
||||
gBrowser.pinTab(newTab);
|
||||
})
|
||||
}
|
||||
|
||||
function onTabPinned() {
|
||||
let contentWindow = TabView.getContentWindow();
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"There is one group item on startup");
|
||||
|
||||
let groupItem = contentWindow.GroupItems.groupItems[0];
|
||||
let icon = contentWindow.iQ(".appTabIcon", groupItem.$appTabTray)[0];
|
||||
let $icon = contentWindow.iQ(icon);
|
||||
|
||||
is($icon.data("xulTab"), newTab,
|
||||
"The app tab icon has the right tab reference")
|
||||
// check to see whether it's showing the default one or not.
|
||||
is($icon.attr("src"), fi.defaultFavicon.spec,
|
||||
"The icon is showing the default fav icon for blank tab");
|
||||
|
||||
let errorHandler = function(event) {
|
||||
newTab.removeEventListener("error", errorHandler, false);
|
||||
|
||||
// since the browser code and test code are invoked when an error event is
|
||||
// fired, a delay is used here to avoid the test code run before the browser
|
||||
// code.
|
||||
executeSoon(function() {
|
||||
let iconSrc = $icon.attr("src");
|
||||
|
||||
// with moz-anno:favicon automatically redirects to the default favIcon
|
||||
// if the given url is invalid
|
||||
ok(iconSrc.startsWith("moz-anno:favicon:"),
|
||||
"The icon url starts with moz-anno:favicon so the default fav icon would be displayed");
|
||||
|
||||
// At this point, as an additional integrity check we could also verify
|
||||
// that the iconSrc URI does not have any associated favicon data. This
|
||||
// kind of check, however, is not easily supported by the asynchronous
|
||||
// favicon API. Fortunately, the fact that we received the error event
|
||||
// already indicates that the original favicon was not available.
|
||||
// Morevover, since we are using a "moz-anno:favicon:" URI, we know that
|
||||
// we'll not display an empty icon, but the default favicon.
|
||||
|
||||
// clean up
|
||||
gBrowser.removeTab(newTab);
|
||||
let endGame = function() {
|
||||
window.removeEventListener("tabviewhidden", endGame, false);
|
||||
|
||||
ok(!TabView.isVisible(), "Tab View is hidden");
|
||||
finish();
|
||||
}
|
||||
window.addEventListener("tabviewhidden", endGame, false);
|
||||
TabView.toggle();
|
||||
});
|
||||
};
|
||||
newTab.addEventListener("error", errorHandler, false);
|
||||
|
||||
newTab.linkedBrowser.loadURI(
|
||||
"http://mochi.test:8888/browser/browser/components/tabview/test/test_bug600645.html");
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
|
||||
let createGroupItem = function () {
|
||||
let bounds = new cw.Rect(20, 20, 150, 150);
|
||||
let groupItem = new cw.GroupItem([], {bounds: bounds, immediately: true});
|
||||
|
||||
cw.UI.setActive(groupItem);
|
||||
gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
return groupItem;
|
||||
}
|
||||
|
||||
let testVeryQuickDragAndDrop = function () {
|
||||
let sourceGroup = createGroupItem();
|
||||
let targetGroup = cw.GroupItems.groupItems[0];
|
||||
|
||||
sourceGroup.pushAway(true);
|
||||
targetGroup.pushAway(true);
|
||||
|
||||
let sourceTab = sourceGroup.getChild(0).container;
|
||||
EventUtils.synthesizeMouseAtCenter(sourceTab, {type: 'mousedown'}, cw);
|
||||
|
||||
let targetTab = targetGroup.getChild(0).container;
|
||||
EventUtils.synthesizeMouseAtCenter(targetTab, {type: 'mousemove'}, cw);
|
||||
EventUtils.synthesizeMouseAtCenter(targetTab, {type: 'mouseup'}, cw);
|
||||
|
||||
is(targetGroup.getChildren().length, 2, 'target group has two tabs');
|
||||
is(cw.GroupItems.groupItems.length, 1, 'sourceGroup was closed');
|
||||
isnot(cw.GroupItems.groupItems[0], sourceGroup, 'sourceGroup was closed');
|
||||
|
||||
targetGroup.getChild(0).close();
|
||||
hideTabView(finish);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(function () {
|
||||
cw = TabView.getContentWindow();
|
||||
afterAllTabsLoaded(testVeryQuickDragAndDrop);
|
||||
});
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let checkUpdateTimes = function (groupItem) {
|
||||
let children = groupItem.getChildren();
|
||||
let earliestUpdateTime = children.shift()._testLastTabUpdateTime;
|
||||
|
||||
children.forEach(function (tabItem) {
|
||||
let updateTime = tabItem._testLastTabUpdateTime;
|
||||
ok(earliestUpdateTime <= updateTime, "Stacked group item update (" +
|
||||
updateTime + ") > first item (" + earliestUpdateTime + ")");
|
||||
});
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
let numTabsToUpdate = 10;
|
||||
let groupItem = createGroupItemWithBlankTabs(win, 150, 150, 100, numTabsToUpdate, false);
|
||||
ok(groupItem.isStacked(), "groupItem is stacked");
|
||||
|
||||
let cw = win.TabView.getContentWindow();
|
||||
cw.TabItems.pausePainting();
|
||||
|
||||
groupItem.getChildren().forEach(function (tabItem) {
|
||||
tabItem.addSubscriber("updated", function onUpdated() {
|
||||
tabItem.removeSubscriber("updated", onUpdated);
|
||||
tabItem._testLastTabUpdateTime = tabItem._lastTabUpdateTime;
|
||||
|
||||
if (--numTabsToUpdate)
|
||||
return;
|
||||
|
||||
checkUpdateTimes(groupItem);
|
||||
finish();
|
||||
});
|
||||
|
||||
cw.TabItems.update(tabItem.tab);
|
||||
});
|
||||
|
||||
cw.TabItems.resumePainting();
|
||||
});
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var contentWindow;
|
||||
var contentElement;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
if (gBrowser.tabs.length > 1)
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
hideTabView();
|
||||
});
|
||||
|
||||
showTabView(function() {
|
||||
contentWindow = TabView.getContentWindow();
|
||||
contentElement = contentWindow.document.getElementById("content");
|
||||
test1();
|
||||
});
|
||||
}
|
||||
|
||||
function test1() {
|
||||
let groupItems = contentWindow.GroupItems.groupItems;
|
||||
is(groupItems.length, 1, "there is one groupItem");
|
||||
|
||||
whenTabViewIsHidden(function() {
|
||||
gBrowser.selectedTab = gBrowser.tabs[0];
|
||||
is(groupItems.length, 2, "there are two groupItems");
|
||||
closeGroupItem(groupItems[1], finish);
|
||||
});
|
||||
|
||||
// double click
|
||||
doubleClick(contentElement, 0);
|
||||
}
|
||||
|
||||
function doubleClick(targetElement, buttonCode) {
|
||||
EventUtils.sendMouseEvent(
|
||||
{ type: "dblclick", button: buttonCode }, targetElement, contentWindow);
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(function () {
|
||||
let [tab] = gBrowser.tabs;
|
||||
let groupId = tab._tabViewTabItem.parent.id;
|
||||
|
||||
TabView.moveTabTo(tab, groupId);
|
||||
is(tab._tabViewTabItem.parent.id, groupId, 'tab did not change its group');
|
||||
|
||||
hideTabView(finish);
|
||||
});
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let newTabs = []
|
||||
// add enough tabs so the close buttons are hidden and then check the closebuttons attribute
|
||||
do {
|
||||
let newTab = gBrowser.addTab("about:blank", {skipAnimation: true});
|
||||
newTabs.push(newTab);
|
||||
} while (gBrowser.visibleTabs[0].getBoundingClientRect().width > gBrowser.tabContainer.mTabClipWidth)
|
||||
|
||||
// a setTimeout() in addTab is used to trigger adjustTabstrip() so we need a delay here as well.
|
||||
executeSoon(function() {
|
||||
is(gBrowser.tabContainer.getAttribute("closebuttons"), "activetab", "Only show button on selected tab.");
|
||||
|
||||
// move a tab to another group and check the closebuttons attribute
|
||||
TabView._initFrame(function() {
|
||||
TabView.moveTabTo(newTabs[newTabs.length - 1], null);
|
||||
ok(gBrowser.visibleTabs[0].getBoundingClientRect().width > gBrowser.tabContainer.mTabClipWidth,
|
||||
"Tab width is bigger than tab clip width");
|
||||
is(gBrowser.tabContainer.getAttribute("closebuttons"), "", "Show button on all tabs.")
|
||||
|
||||
// clean up and finish
|
||||
newTabs.forEach(function(tab) {
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
finish();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
|
||||
let assertNumberOfGroupItems = function (num) {
|
||||
let groupItems = cw.GroupItems.groupItems;
|
||||
is(groupItems.length, num, "number of groupItems is " + num);
|
||||
};
|
||||
|
||||
let dragTabOutOfGroup = function (groupItem) {
|
||||
let tabItem = groupItem.getChild(0);
|
||||
let target = tabItem.container;
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(target, {type: "mousedown"}, cw);
|
||||
EventUtils.synthesizeMouse(target, 400, 100, {type: "mousemove"}, cw);
|
||||
EventUtils.synthesizeMouseAtCenter(target, {type: "mouseup"}, cw);
|
||||
};
|
||||
|
||||
let testCreateGroup = function (callback) {
|
||||
let content = cw.document.getElementById("content");
|
||||
|
||||
// drag to create a new group
|
||||
EventUtils.synthesizeMouse(content, 400, 50, {type: "mousedown"}, cw);
|
||||
EventUtils.synthesizeMouse(content, 500, 250, {type: "mousemove"}, cw);
|
||||
EventUtils.synthesizeMouse(content, 500, 250, {type: "mouseup"}, cw);
|
||||
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
// enter a title for the new group
|
||||
EventUtils.synthesizeKey("t", {}, cw);
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, cw);
|
||||
|
||||
|
||||
let groupItem = cw.GroupItems.groupItems[1];
|
||||
is(groupItem.getTitle(), "t", "new groupItem's title is correct");
|
||||
|
||||
closeGroupItem(groupItem, callback);
|
||||
};
|
||||
|
||||
let testDragOutOfGroup = function (callback) {
|
||||
assertNumberOfGroupItems(1);
|
||||
|
||||
let groupItem = cw.GroupItems.groupItems[0];
|
||||
dragTabOutOfGroup(groupItem);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
// enter a title for the new group
|
||||
EventUtils.synthesizeKey("t", {}, cw);
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, cw);
|
||||
|
||||
groupItem = cw.GroupItems.groupItems[1];
|
||||
is(groupItem.getTitle(), "t", "new groupItem's title is correct");
|
||||
closeGroupItem(groupItem, callback);
|
||||
};
|
||||
|
||||
let onLoad = function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
|
||||
for (let i = 0; i < 2; i++)
|
||||
win.gBrowser.addTab();
|
||||
};
|
||||
|
||||
let onShow = function (win) {
|
||||
cw = win.TabView.getContentWindow();
|
||||
assertNumberOfGroupItems(1);
|
||||
|
||||
let groupItem = cw.GroupItems.groupItems[0];
|
||||
groupItem.setSize(200, 600, true);
|
||||
|
||||
waitForFocus(function () {
|
||||
testCreateGroup(function () {
|
||||
testDragOutOfGroup(finish);
|
||||
});
|
||||
}, cw);
|
||||
};
|
||||
|
||||
waitForExplicitFinish();
|
||||
newWindowWithTabView(onShow, onLoad);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var tabOne;
|
||||
var tabTwo;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
tabOne = gBrowser.addTab("http://mochi.test:8888/");
|
||||
tabTwo = gBrowser.addTab("http://mochi.test:8888/browser/browser/components/tabview/test/dummy_page.html");
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
// make sure the tab one is selected because undoCloseTab() would remove
|
||||
// the selected tab if it's a blank tab.
|
||||
gBrowser.selectedTab = tabOne;
|
||||
showTabView(onTabViewWindowLoaded);
|
||||
});
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded() {
|
||||
let contentWindow = TabView.getContentWindow();
|
||||
let groupItems = contentWindow.GroupItems.groupItems;
|
||||
is(groupItems.length, 1, "There is only one group");
|
||||
is(groupItems[0].getChildren().length, 3, "The group has three tab items");
|
||||
|
||||
BrowserTestUtils.removeTab(tabTwo).then(() => {
|
||||
ok(TabView.isVisible(), "Tab View is still visible after removing a tab");
|
||||
is(groupItems[0].getChildren().length, 2, "The group has two tab items");
|
||||
|
||||
restoreTab(function (tabTwo) {
|
||||
ok(TabView.isVisible(), "Tab View is still visible after restoring a tab");
|
||||
is(groupItems[0].getChildren().length, 3, "The group still has three tab items");
|
||||
|
||||
// clean up and finish
|
||||
hideTabView(function () {
|
||||
gBrowser.removeTab(tabOne);
|
||||
gBrowser.removeTab(tabTwo);
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let pinnedTab = gBrowser.addTab();
|
||||
gBrowser.pinTab(pinnedTab);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gBrowser.unpinTab(pinnedTab);
|
||||
|
||||
// Don't remove the initial tab.
|
||||
gBrowser.moveTabTo(gBrowser.tabs[1], 0);
|
||||
|
||||
while (gBrowser.tabs[1])
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
hideTabView();
|
||||
});
|
||||
|
||||
showTabView(function() {
|
||||
let cw = TabView.getContentWindow();
|
||||
let groupItemOne = cw.GroupItems.groupItems[0];
|
||||
let groupItemTwo = createGroupItemWithBlankTabs(window, 250, 250, 40, 1);
|
||||
|
||||
is(cw.GroupItems.groupItems.length, 2, "Two group items");
|
||||
|
||||
hideTabView(function() {
|
||||
gBrowser.selectedTab = pinnedTab;
|
||||
is(cw.GroupItems.getActiveGroupItem(), groupItemTwo, "Group two is active");
|
||||
is(gBrowser.selectedTab, pinnedTab, "Selected tab is the pinned tab");
|
||||
|
||||
goToNextGroup();
|
||||
is(cw.GroupItems.getActiveGroupItem(), groupItemOne, "Group one is active");
|
||||
is(gBrowser.selectedTab, pinnedTab, "Selected tab is the pinned tab");
|
||||
|
||||
finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Promise.jsm", this);
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
newWindowWithTabView(onTabViewWindowLoaded);
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded(win) {
|
||||
let contentWindow = win.TabView.getContentWindow();
|
||||
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"There is one group item on startup");
|
||||
is(win.gBrowser.tabs.length, 1, "There is one tab on startup");
|
||||
let groupItem = contentWindow.GroupItems.groupItems[0];
|
||||
|
||||
hideGroupItem(groupItem, function () {
|
||||
hideTabView(() => {
|
||||
is(contentWindow.GroupItems.groupItems.length, 1,
|
||||
"There is still one group item");
|
||||
isnot(groupItem, contentWindow.GroupItems.groupItems[0],
|
||||
"The initial group item is not the same as the final group item");
|
||||
is(win.gBrowser.tabs.length, 1, "There is only one tab");
|
||||
ok(!win.TabView.isVisible(), "Tab View is hidden");
|
||||
promiseWindowClosed(win).then(finish);
|
||||
}, win);
|
||||
});
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let origTab = gBrowser.visibleTabs[0];
|
||||
let newTab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = newTab;
|
||||
let relatedTab = gBrowser.addTab("about:blank", { ownerTab: newTab });
|
||||
|
||||
// init the frame, move the owner tab to a new group and close the related
|
||||
// tab.
|
||||
TabView._initFrame(function() {
|
||||
let newTabGroupItemId = newTab._tabViewTabItem.parent.id;
|
||||
|
||||
is(relatedTab.owner, newTab, "The related tab's owner is the right tab");
|
||||
|
||||
// move current tab to a new group
|
||||
TabView.moveTabTo(newTab, null);
|
||||
|
||||
// close the related tab
|
||||
gBrowser.removeTab(relatedTab);
|
||||
|
||||
is(gBrowser.visibleTabs.length, 1, "The number of visible tabs is 1");
|
||||
is(gBrowser.visibleTabs[0], origTab,
|
||||
"The original tab is the only visible tab");
|
||||
isnot(newTab._tabViewTabItem.parent.id, newTabGroupItemId,
|
||||
"The moved tab item has a new group id");
|
||||
|
||||
// clean up
|
||||
gBrowser.removeTab(newTab);
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
|
||||
let createGroupItem = function () {
|
||||
let bounds = new cw.Rect(20, 20, 200, 200);
|
||||
let groupItem = new cw.GroupItem([], {bounds: bounds, immediately: true});
|
||||
|
||||
cw.UI.setActive(groupItem);
|
||||
gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
return groupItem;
|
||||
}
|
||||
|
||||
let finishTest = function () {
|
||||
ok(!TabView.isVisible(), 'cleanup: tabview is hidden');
|
||||
is(gBrowser.tabs.length, 1, 'cleanup: there is one tab, only');
|
||||
is(cw.GroupItems.groupItems.length, 1, 'cleanup: there is one group, only');
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
let testAddChildFromAnotherGroup = function () {
|
||||
let sourceGroup = cw.GroupItems.groupItems[0];
|
||||
let targetGroup = createGroupItem();
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
// check setup
|
||||
is(sourceGroup.getChildren().length, 1, 'setup: source group has one child');
|
||||
is(targetGroup.getChildren().length, 1, 'setup: target group has one child');
|
||||
|
||||
let tabItem = sourceGroup.getChild(0);
|
||||
targetGroup.add(tabItem);
|
||||
|
||||
// check state after adding tabItem to targetGroup
|
||||
is(tabItem.parent, targetGroup, 'tabItem changed groups');
|
||||
is(cw.GroupItems.groupItems.length, 1, 'source group was closed automatically');
|
||||
is(targetGroup.getChildren().length, 2, 'target group has now two children');
|
||||
|
||||
// cleanup and finish
|
||||
targetGroup.getChild(0).close();
|
||||
hideTabView(finishTest);
|
||||
});
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(function () {
|
||||
cw = TabView.getContentWindow();
|
||||
testAddChildFromAnotherGroup();
|
||||
});
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(4);
|
||||
let cw;
|
||||
let win;
|
||||
let groupItem;
|
||||
|
||||
let next = function () {
|
||||
let test = tests.shift();
|
||||
|
||||
if (test) {
|
||||
test();
|
||||
return;
|
||||
}
|
||||
|
||||
win.close();
|
||||
finish();
|
||||
}
|
||||
|
||||
let closeTabItemManually = function (tabItem) {
|
||||
EventUtils.synthesizeMouseAtCenter(tabItem.container, {button: 1}, cw);
|
||||
}
|
||||
|
||||
let prepareTest = function (testName) {
|
||||
let originalBounds = groupItem.getChild(0).getBounds();
|
||||
|
||||
let tabItem = groupItem.getChild(1);
|
||||
let bounds = tabItem.getBounds();
|
||||
closeTabItemManually(tabItem);
|
||||
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), testName + ': tabs did not change their size');
|
||||
ok(bounds.equals(groupItem.getChild(1).getBounds()), testName + ': third tab is now on second tab\'s previous position');
|
||||
|
||||
return originalBounds;
|
||||
}
|
||||
|
||||
let cleanUpTest = function (testName, originalBounds, callback) {
|
||||
// Use setTimeout here because the groupItem.arrange() call uses
|
||||
// animation to re-arrange the tabItems.
|
||||
win.setTimeout(function () {
|
||||
ok(!originalBounds.equals(groupItem.getChild(0).getBounds()), testName + ': tabs changed their size');
|
||||
|
||||
// cleanup
|
||||
cw.UI.setActive(groupItem);
|
||||
win.gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
afterAllTabsLoaded(callback, win);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
let tests = [];
|
||||
|
||||
// focus group title's input field to cause item arrange
|
||||
let testFocusTitle = function () {
|
||||
let originalBounds = prepareTest('testFocusTitle');
|
||||
|
||||
let target = groupItem.$titleShield[0];
|
||||
EventUtils.synthesizeMouseAtCenter(target, {}, cw);
|
||||
|
||||
cleanUpTest('testFocusTitle', originalBounds, next);
|
||||
}
|
||||
|
||||
// hide tabview to cause item arrange
|
||||
let testHideTabView = function () {
|
||||
let originalBounds = prepareTest('testHideTabView');
|
||||
|
||||
hideTabView(function () {
|
||||
cleanUpTest('testHideTabView', originalBounds, function () {
|
||||
showTabView(next, win);
|
||||
});
|
||||
}, win);
|
||||
}
|
||||
|
||||
// (undo) close a group to cause item arrange
|
||||
let testCloseGroupUndo = function () {
|
||||
let originalBounds = prepareTest('testCloseGroupUndo');
|
||||
|
||||
hideGroupItem(groupItem, function () {
|
||||
unhideGroupItem(groupItem, function () {
|
||||
cleanUpTest('testCloseGroupUndo', originalBounds, next);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// leave the group's container with the mouse to cause item arrange
|
||||
let testMouseOut = function () {
|
||||
let originalBounds = prepareTest('testMouseOut');
|
||||
let doc = cw.document.documentElement;
|
||||
let bounds = groupItem.getBounds();
|
||||
|
||||
EventUtils.synthesizeMouse(doc, bounds.right - 5, bounds.bottom - 5, {type: 'mousemove'}, cw);
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), 'testMouseOut: tabs did not change their size');
|
||||
|
||||
EventUtils.synthesizeMouse(doc, bounds.right + 1, bounds.bottom + 1, {type: 'mousemove'}, cw);
|
||||
cleanUpTest('testMouseOut', originalBounds, next);
|
||||
}
|
||||
|
||||
// sort item (drag it around) in its group to cause item arrange
|
||||
let testSortInGroup = function () {
|
||||
let originalBounds = prepareTest('testSortInGroup');
|
||||
let target = groupItem.getChild(0).container;
|
||||
|
||||
// simulate drag/drop sorting
|
||||
EventUtils.synthesizeMouse(target, 20, 20, {type: 'mousedown'}, cw);
|
||||
EventUtils.synthesizeMouse(target, 40, 20, {type: 'mousemove'}, cw);
|
||||
EventUtils.synthesizeMouse(target, 20, 20, {type: 'mouseup'}, cw);
|
||||
|
||||
cleanUpTest('testSortInGroup', originalBounds, next);
|
||||
}
|
||||
|
||||
// arrange items when the containing group is resized
|
||||
let testResizeGroup = function () {
|
||||
let originalBounds = prepareTest('testResizeGroup');
|
||||
let oldBounds = groupItem.getBounds();
|
||||
let resizer = groupItem.$resizer[0];
|
||||
|
||||
// simulate drag/drop resizing
|
||||
EventUtils.synthesizeMouse(resizer, 5, 5, {type: 'mousedown'}, cw);
|
||||
EventUtils.synthesizeMouse(resizer, 40, 20, {type: 'mousemove'}, cw);
|
||||
EventUtils.synthesizeMouse(resizer, 20, 20, {type: 'mouseup'}, cw);
|
||||
|
||||
// reset group size
|
||||
groupItem.setBounds(oldBounds);
|
||||
groupItem.setUserSize();
|
||||
|
||||
cleanUpTest('testResizeGroup', originalBounds, next);
|
||||
}
|
||||
|
||||
// make sure we don't freeze item size when removing an item from a stack
|
||||
let testRemoveWhileStacked = function () {
|
||||
let oldBounds = groupItem.getBounds();
|
||||
groupItem.setSize(250, 250, true);
|
||||
groupItem.setUserSize();
|
||||
|
||||
ok(!groupItem.isStacked(), 'testRemoveWhileStacked: group is not stacked');
|
||||
|
||||
let originalBounds;
|
||||
let tabItem = groupItem.getChild(0);
|
||||
|
||||
// add new tabs to let the group stack
|
||||
while (!groupItem.isStacked()) {
|
||||
originalBounds = tabItem.getBounds();
|
||||
win.gBrowser.addTab();
|
||||
}
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
tabItem.close();
|
||||
ok(!groupItem.isStacked(), 'testRemoveWhileStacked: group is not stacked');
|
||||
|
||||
let bounds = groupItem.getChild(0).getBounds();
|
||||
ok(originalBounds.equals(bounds), 'testRemoveWhileStacked: tabs did not change their size');
|
||||
|
||||
// reset group size
|
||||
groupItem.setBounds(oldBounds);
|
||||
groupItem.setUserSize();
|
||||
|
||||
next();
|
||||
}, win);
|
||||
}
|
||||
|
||||
// 1) make sure item size is frozen when removing an item in expanded mode
|
||||
// 2) make sure item size stays frozen while moving the mouse in the expanded
|
||||
// layer
|
||||
let testExpandedMode = function () {
|
||||
let oldBounds = groupItem.getBounds();
|
||||
groupItem.setSize(100, 100, true);
|
||||
groupItem.setUserSize();
|
||||
|
||||
ok(groupItem.isStacked(), 'testExpandedMode: group is stacked');
|
||||
|
||||
groupItem.addSubscriber('expanded', function onGroupExpanded() {
|
||||
groupItem.removeSubscriber('expanded', onGroupExpanded);
|
||||
onExpanded();
|
||||
});
|
||||
|
||||
groupItem.addSubscriber('collapsed', function onGroupCollapsed() {
|
||||
groupItem.removeSubscriber('collapsed', onGroupCollapsed);
|
||||
onCollapsed();
|
||||
});
|
||||
|
||||
let onExpanded = function () {
|
||||
let originalBounds = groupItem.getChild(0).getBounds();
|
||||
let tabItem = groupItem.getChild(1);
|
||||
let bounds = tabItem.getBounds();
|
||||
|
||||
while (groupItem.getChildren().length > 2)
|
||||
closeTabItemManually(groupItem.getChild(1));
|
||||
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), 'testExpandedMode: tabs did not change their size');
|
||||
|
||||
// move the mouse over the expanded layer
|
||||
let trayBounds = groupItem.expanded.bounds;
|
||||
let target = groupItem.expanded.$tray[0];
|
||||
EventUtils.synthesizeMouse(target, trayBounds.right - 5, trayBounds.bottom -5, {type: 'mousemove'}, cw);
|
||||
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), 'testExpandedMode: tabs did not change their size');
|
||||
groupItem.collapse();
|
||||
}
|
||||
|
||||
let onCollapsed = function () {
|
||||
// reset group size
|
||||
groupItem.setBounds(oldBounds);
|
||||
groupItem.setUserSize();
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
groupItem.expand();
|
||||
}
|
||||
|
||||
tests.push(testFocusTitle);
|
||||
tests.push(testHideTabView);
|
||||
tests.push(testCloseGroupUndo);
|
||||
tests.push(testMouseOut);
|
||||
tests.push(testSortInGroup);
|
||||
tests.push(testResizeGroup);
|
||||
tests.push(testRemoveWhileStacked);
|
||||
tests.push(testExpandedMode);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (tvwin) {
|
||||
win = tvwin;
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
if (!win.closed)
|
||||
win.close();
|
||||
});
|
||||
|
||||
cw = win.TabView.getContentWindow();
|
||||
|
||||
groupItem = cw.GroupItems.groupItems[0];
|
||||
groupItem.setSize(400, 200, true);
|
||||
groupItem.setUserSize();
|
||||
|
||||
for (let i=0; i<3; i++)
|
||||
win.gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
afterAllTabsLoaded(next, win);
|
||||
});
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(onTabViewWindowLoaded);
|
||||
}
|
||||
|
||||
function onTabViewWindowLoaded(win) {
|
||||
win.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
|
||||
|
||||
ok(win.TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = win.document.getElementById("tab-view").contentWindow;
|
||||
let [originalTab] = win.gBrowser.visibleTabs;
|
||||
|
||||
let currentGroup = contentWindow.GroupItems.getActiveGroupItem();
|
||||
|
||||
// Create a group and make it active
|
||||
let box = new contentWindow.Rect(100, 100, 370, 370);
|
||||
let group = new contentWindow.GroupItem([], { bounds: box });
|
||||
ok(group.isEmpty(), "This group is empty");
|
||||
contentWindow.UI.setActive(group);
|
||||
is(contentWindow.GroupItems.getActiveGroupItem(), group, "new group is active");
|
||||
|
||||
// Create a bunch of tabs in the group
|
||||
let bg = {inBackground: true};
|
||||
let datatext = win.gBrowser.loadOneTab("data:text/plain,bug610242", bg);
|
||||
let datahtml = win.gBrowser.loadOneTab("data:text/html,<h1>hi!</h1>", bg);
|
||||
let mozilla = win.gBrowser.loadOneTab("about:mozilla", bg);
|
||||
let synclog = win.gBrowser.loadOneTab("about:sync-log", bg);
|
||||
let html = win.gBrowser.loadOneTab("http://example.com", bg);
|
||||
let png = win.gBrowser.loadOneTab("http://mochi.test:8888/browser/browser/base/content/test/general/moz.png", bg);
|
||||
let svg = win.gBrowser.loadOneTab("http://mochi.test:8888/browser/browser/base/content/test/general/title_test.svg", bg);
|
||||
|
||||
ok(!group.shouldStack(group._children.length), "Group should not stack.");
|
||||
|
||||
// PREPARE FINISH:
|
||||
group.addSubscriber("close", function onClose() {
|
||||
group.removeSubscriber("close", onClose);
|
||||
|
||||
ok(group.isEmpty(), "The group is empty again");
|
||||
|
||||
contentWindow.UI.setActive(currentGroup);
|
||||
isnot(contentWindow.GroupItems.getActiveGroupItem(), null, "There is an active group");
|
||||
is(win.gBrowser.tabs.length, 1, "There is only one tab left");
|
||||
is(win.gBrowser.visibleTabs.length, 1, "There is also only one visible tab");
|
||||
|
||||
whenTabViewIsHidden(function() {
|
||||
win.close();
|
||||
ok(win.closed, "new window is closed");
|
||||
finish();
|
||||
}, win);
|
||||
win.gBrowser.selectedTab = originalTab;
|
||||
|
||||
win.TabView.hide();
|
||||
});
|
||||
|
||||
function check(tab, label, visible) {
|
||||
let display = contentWindow.getComputedStyle(tab._tabViewTabItem.$fav[0], null).getPropertyValue("display");
|
||||
if (visible) {
|
||||
is(display, "block", label + " has favicon");
|
||||
} else {
|
||||
is(display, "none", label + " has no favicon");
|
||||
}
|
||||
}
|
||||
|
||||
afterAllTabsLoaded(function() {
|
||||
let children = group.getChildren();
|
||||
let len = children.length;
|
||||
let iconUpdateCounter = 0;
|
||||
|
||||
children.forEach(function(tabItem) {
|
||||
tabItem.addSubscriber("iconUpdated", function onIconUpdated() {
|
||||
tabItem.removeSubscriber("iconUpdated", onIconUpdated);
|
||||
|
||||
if (++iconUpdateCounter == len) {
|
||||
check(datatext, "datatext", false);
|
||||
check(datahtml, "datahtml", false);
|
||||
check(mozilla, "about:mozilla", false);
|
||||
check(synclog, "about:sync-log", true);
|
||||
check(html, "html", true);
|
||||
check(png, "png", false);
|
||||
check(svg, "svg", true);
|
||||
|
||||
// Get rid of the group and its children
|
||||
// The group close will trigger a finish().
|
||||
closeGroupItem(group);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
afterAllTabItemsUpdated(function () {}, win);
|
||||
}, win);
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that groups behave properly when closing all tabs but app tabs.
|
||||
|
||||
function test() {
|
||||
let cw, win, groupItem;
|
||||
|
||||
let onLoad = function (tvwin) {
|
||||
win = tvwin;
|
||||
registerCleanupFunction(() => win.close());
|
||||
win.gBrowser.pinTab(win.gBrowser.tabs[0]);
|
||||
win.gBrowser.loadOneTab("about:blank", {inBackground: true});
|
||||
};
|
||||
|
||||
let onShow = function () {
|
||||
cw = win.TabView.getContentWindow();
|
||||
is(cw.GroupItems.groupItems.length, 1, "There's only one group");
|
||||
|
||||
groupItem = createEmptyGroupItem(cw, 200, 200, 20);
|
||||
cw.UI.setActive(groupItem);
|
||||
|
||||
whenTabViewIsHidden(onHide, win);
|
||||
cw.UI.goToTab(win.gBrowser.tabs[0]);
|
||||
};
|
||||
|
||||
let onHide = function () {
|
||||
let tab = win.gBrowser.loadOneTab("about:blank", {inBackground: true});
|
||||
is(groupItem.getChildren().length, 1, "One tab is in the new group");
|
||||
|
||||
executeSoon(function () {
|
||||
is(win.gBrowser.visibleTabs.length, 2, "There are two tabs displayed");
|
||||
win.gBrowser.removeTab(tab);
|
||||
|
||||
is(groupItem.getChildren().length, 0, "No tabs are in the new group");
|
||||
is(win.gBrowser.visibleTabs.length, 1, "There is one tab displayed");
|
||||
is(cw.GroupItems.groupItems.length, 2, "There are two groups still");
|
||||
|
||||
finish();
|
||||
});
|
||||
};
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(onShow, onLoad);
|
||||
}
|
|
@ -1,290 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Promise.jsm", this);
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
let win;
|
||||
let currentTest;
|
||||
|
||||
let getGroupItem = function (index) {
|
||||
return cw.GroupItems.groupItems[index];
|
||||
}
|
||||
|
||||
let createGroupItem = function (numTabs) {
|
||||
let bounds = new cw.Rect(20, 20, 200, 200);
|
||||
let groupItem = new cw.GroupItem([], {bounds: bounds, immediately: true});
|
||||
cw.UI.setActive(groupItem);
|
||||
|
||||
for (let i=0; i<numTabs || 0; i++)
|
||||
win.gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
return groupItem;
|
||||
}
|
||||
|
||||
let tests = [];
|
||||
|
||||
let next = function () {
|
||||
let test = tests.shift();
|
||||
|
||||
if (test) {
|
||||
// check that the previous test left things as expected
|
||||
if (currentTest) {
|
||||
currentTest += ' (post-check)';
|
||||
assertTabViewIsHidden();
|
||||
assertNumberOfGroupItems(1);
|
||||
assertNumberOfTabs(1);
|
||||
}
|
||||
|
||||
currentTest = test.name;
|
||||
showTabView(test.func, win);
|
||||
} else
|
||||
promiseWindowClosed(win).then(finish);
|
||||
}
|
||||
|
||||
let assertTabViewIsHidden = function () {
|
||||
ok(!win.TabView.isVisible(), currentTest + ': tabview is hidden');
|
||||
}
|
||||
|
||||
let assertNumberOfGroupItems = function (num) {
|
||||
is(cw.GroupItems.groupItems.length, num, currentTest + ': number of groupItems is equal to ' + num);
|
||||
}
|
||||
|
||||
let assertNumberOfTabs = function (num) {
|
||||
is(win.gBrowser.tabs.length, num, currentTest + ': number of tabs is equal to ' + num);
|
||||
}
|
||||
|
||||
let assertGroupItemRemoved = function (groupItem) {
|
||||
is(cw.GroupItems.groupItems.indexOf(groupItem), -1, currentTest + ': groupItem was removed');
|
||||
}
|
||||
|
||||
let assertGroupItemExists = function (groupItem) {
|
||||
isnot(cw.GroupItems.groupItems.indexOf(groupItem), -1, currentTest + ': groupItem still exists');
|
||||
}
|
||||
|
||||
// setup: 1 non-empty group
|
||||
// action: close the group
|
||||
// expected: new group with blank tab is created and zoomed into
|
||||
let testSingleGroup1 = function () {
|
||||
let groupItem = getGroupItem(0);
|
||||
closeGroupItem(groupItem, function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemRemoved(groupItem);
|
||||
whenTabViewIsHidden(next, win);
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 1 non-empty group
|
||||
// action: hide the group, exit panorama
|
||||
// expected: new group with blank tab is created and zoomed into
|
||||
let testSingleGroup2 = function () {
|
||||
let groupItem = getGroupItem(0);
|
||||
hideGroupItem(groupItem, function () {
|
||||
hideTabView(function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemRemoved(groupItem);
|
||||
next();
|
||||
}, win);
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 2 non-empty groups
|
||||
// action: close one group
|
||||
// expected: nothing should happen
|
||||
let testNonEmptyGroup1 = function () {
|
||||
let groupItem = getGroupItem(0);
|
||||
let newGroupItem = createGroupItem(1);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
closeGroupItem(groupItem, function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemExists(newGroupItem);
|
||||
hideTabView(next, win);
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 2 non-empty groups
|
||||
// action: hide one group, exit panorama
|
||||
// expected: nothing should happen
|
||||
let testNonEmptyGroup2 = function () {
|
||||
let groupItem = getGroupItem(0);
|
||||
let newGroupItem = createGroupItem(1);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
hideGroupItem(groupItem, function () {
|
||||
hideTabView(function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemRemoved(groupItem);
|
||||
assertGroupItemExists(newGroupItem);
|
||||
next();
|
||||
}, win);
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 1 pinned tab, 1 empty group
|
||||
// action: exit panorama
|
||||
// expected: nothing should happen
|
||||
let testPinnedTab1 = function () {
|
||||
win.gBrowser.pinTab(win.gBrowser.selectedTab);
|
||||
|
||||
let groupItem = getGroupItem(0);
|
||||
hideTabView(function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemExists(groupItem);
|
||||
win.gBrowser.unpinTab(win.gBrowser.selectedTab);
|
||||
next();
|
||||
}, win);
|
||||
}
|
||||
|
||||
// setup: 1 pinned tab
|
||||
// action: exit panorama
|
||||
// expected: new blank group is created
|
||||
let testPinnedTab2 = function () {
|
||||
win.gBrowser.pinTab(win.gBrowser.selectedTab);
|
||||
getGroupItem(0).close();
|
||||
|
||||
hideTabView(function () {
|
||||
assertNumberOfTabs(1);
|
||||
assertNumberOfGroupItems(1);
|
||||
win.gBrowser.unpinTab(win.gBrowser.selectedTab);
|
||||
next();
|
||||
}, win);
|
||||
}
|
||||
|
||||
// setup: 1 pinned tab, 1 empty group, 1 non-empty group
|
||||
// action: close non-empty group
|
||||
// expected: nothing should happen
|
||||
let testPinnedTab3 = function () {
|
||||
win.gBrowser.pinTab(win.gBrowser.selectedTab);
|
||||
|
||||
let groupItem = getGroupItem(0);
|
||||
let newGroupItem = createGroupItem(1);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
closeGroupItem(newGroupItem, function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemExists(groupItem);
|
||||
|
||||
win.gBrowser.unpinTab(win.gBrowser.selectedTab);
|
||||
hideTabView(next, win);
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 1 pinned tab, 1 empty group, 1 non-empty group
|
||||
// action: hide non-empty group, exit panorama
|
||||
// expected: nothing should happen
|
||||
let testPinnedTab4 = function () {
|
||||
win.gBrowser.pinTab(win.gBrowser.selectedTab);
|
||||
|
||||
let groupItem = getGroupItem(0);
|
||||
let newGroupItem = createGroupItem(1);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
hideGroupItem(newGroupItem, function () {
|
||||
hideTabView(function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemExists(groupItem);
|
||||
assertGroupItemRemoved(newGroupItem);
|
||||
win.gBrowser.unpinTab(win.gBrowser.selectedTab);
|
||||
next();
|
||||
}, win);
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 1 non-empty group, 1 empty group
|
||||
// action: close non-empty group
|
||||
// expected: empty group is re-used, new tab is created and zoomed into
|
||||
let testEmptyGroup1 = function () {
|
||||
let groupItem = getGroupItem(0);
|
||||
let newGroupItem = createGroupItem(0);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
closeGroupItem(groupItem, function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemExists(newGroupItem);
|
||||
whenTabViewIsHidden(next, win);
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 1 non-empty group, 1 empty group
|
||||
// action: hide non-empty group, exit panorama
|
||||
// expected: empty group is re-used, new tab is created and zoomed into
|
||||
let testEmptyGroup2 = function () {
|
||||
let groupItem = getGroupItem(0);
|
||||
let newGroupItem = createGroupItem(0);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
hideGroupItem(groupItem, function () {
|
||||
hideTabView(function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemRemoved(groupItem);
|
||||
assertGroupItemExists(newGroupItem);
|
||||
next();
|
||||
}, win);
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 1 hidden group, 1 non-empty group
|
||||
// action: close non-empty group
|
||||
// expected: nothing should happen
|
||||
let testHiddenGroup1 = function () {
|
||||
let groupItem = getGroupItem(0);
|
||||
let hiddenGroupItem = createGroupItem(1);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
hideGroupItem(hiddenGroupItem, function () {
|
||||
closeGroupItem(groupItem, function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemRemoved(groupItem);
|
||||
assertGroupItemExists(hiddenGroupItem);
|
||||
hideTabView(next, win);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// setup: 1 hidden group, 1 non-empty group
|
||||
// action: hide non-empty group, exit panorama
|
||||
// expected: new group with blank tab is created and zoomed into
|
||||
let testHiddenGroup2 = function () {
|
||||
let groupItem = getGroupItem(0);
|
||||
let hiddenGroupItem = createGroupItem(1);
|
||||
assertNumberOfGroupItems(2);
|
||||
|
||||
hideGroupItem(hiddenGroupItem, function () {
|
||||
hideGroupItem(groupItem, function () {
|
||||
hideTabView(function () {
|
||||
assertNumberOfGroupItems(1);
|
||||
assertGroupItemRemoved(groupItem);
|
||||
assertGroupItemRemoved(hiddenGroupItem);
|
||||
next();
|
||||
}, win);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
tests.push({name: 'testSingleGroup1', func: testSingleGroup1});
|
||||
tests.push({name: 'testSingleGroup2', func: testSingleGroup2});
|
||||
|
||||
tests.push({name: 'testNonEmptyGroup1', func: testNonEmptyGroup1});
|
||||
tests.push({name: 'testNonEmptyGroup2', func: testNonEmptyGroup2});
|
||||
|
||||
tests.push({name: 'testPinnedTab1', func: testPinnedTab1});
|
||||
tests.push({name: 'testPinnedTab2', func: testPinnedTab2});
|
||||
tests.push({name: 'testPinnedTab3', func: testPinnedTab3});
|
||||
tests.push({name: 'testPinnedTab4', func: testPinnedTab4});
|
||||
|
||||
tests.push({name: 'testEmptyGroup1', func: testEmptyGroup1});
|
||||
tests.push({name: 'testEmptyGroup2', func: testEmptyGroup2});
|
||||
|
||||
tests.push({name: 'testHiddenGroup1', func: testHiddenGroup1});
|
||||
tests.push({name: 'testHiddenGroup2', func: testHiddenGroup2}),
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(window => {
|
||||
win = window;
|
||||
cw = win.TabView.getContentWindow();
|
||||
next();
|
||||
});
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
|
||||
let createGroupItem = function () {
|
||||
return createGroupItemWithBlankTabs(window, 400, 200, 0, 5);
|
||||
}
|
||||
|
||||
let assertCorrectItemOrder = function (items) {
|
||||
for (let i=1; i<items.length; i++) {
|
||||
if (items[i-1].tab._tPos > items[i].tab._tPos) {
|
||||
ok(false, 'tabs were correctly reordered');
|
||||
return;
|
||||
}
|
||||
}
|
||||
ok(true, 'tabs were correctly reordered');
|
||||
}
|
||||
|
||||
let testVariousTabOrders = function () {
|
||||
let groupItem = createGroupItem();
|
||||
let [tab1, tab2, tab3, tab4, tab5] = groupItem.getChildren();
|
||||
|
||||
// prepare tests
|
||||
let tests = [];
|
||||
tests.push([tab1, tab2, tab3, tab4, tab5]);
|
||||
tests.push([tab5, tab4, tab3, tab2, tab1]);
|
||||
tests.push([tab1, tab2, tab3, tab4]);
|
||||
tests.push([tab4, tab3, tab2, tab1]);
|
||||
tests.push([tab1, tab2, tab3]);
|
||||
tests.push([tab1, tab2, tab3]);
|
||||
tests.push([tab1, tab3, tab2]);
|
||||
tests.push([tab2, tab1, tab3]);
|
||||
tests.push([tab2, tab3, tab1]);
|
||||
tests.push([tab3, tab1, tab2]);
|
||||
tests.push([tab3, tab2, tab1]);
|
||||
tests.push([tab1, tab2]);
|
||||
tests.push([tab2, tab1]);
|
||||
tests.push([tab1]);
|
||||
|
||||
// test reordering of empty groups - removes the last tab and causes
|
||||
// the groupItem to close
|
||||
tests.push([]);
|
||||
|
||||
while (tests.length) {
|
||||
let test = tests.shift();
|
||||
|
||||
// prepare
|
||||
let items = groupItem.getChildren();
|
||||
while (test.length < items.length)
|
||||
items[items.length-1].close();
|
||||
|
||||
let orig = cw.Utils.copy(items);
|
||||
items.sort((a, b) => test.indexOf(a) - test.indexOf(b));
|
||||
|
||||
// order and check
|
||||
groupItem.reorderTabsBasedOnTabItemOrder();
|
||||
assertCorrectItemOrder(items);
|
||||
|
||||
// revert to original item order
|
||||
items.sort((a, b) => orig.indexOf(a) - orig.indexOf(b));
|
||||
groupItem.reorderTabsBasedOnTabItemOrder();
|
||||
}
|
||||
|
||||
testMoveBetweenGroups();
|
||||
}
|
||||
|
||||
let testMoveBetweenGroups = function () {
|
||||
let groupItem = createGroupItem();
|
||||
let groupItem2 = createGroupItem();
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
// move item from group1 to group2
|
||||
let child = groupItem.getChild(2);
|
||||
groupItem.remove(child);
|
||||
|
||||
groupItem2.add(child, {index: 3});
|
||||
groupItem2.reorderTabsBasedOnTabItemOrder();
|
||||
|
||||
assertCorrectItemOrder(groupItem.getChildren());
|
||||
assertCorrectItemOrder(groupItem2.getChildren());
|
||||
|
||||
// move item from group2 to group1
|
||||
child = groupItem2.getChild(1);
|
||||
groupItem2.remove(child);
|
||||
|
||||
groupItem.add(child, {index: 1});
|
||||
groupItem.reorderTabsBasedOnTabItemOrder();
|
||||
|
||||
assertCorrectItemOrder(groupItem.getChildren());
|
||||
assertCorrectItemOrder(groupItem2.getChildren());
|
||||
|
||||
// cleanup
|
||||
closeGroupItem(groupItem, function () {
|
||||
closeGroupItem(groupItem2, function () {
|
||||
hideTabView(finish);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(function () {
|
||||
cw = TabView.getContentWindow();
|
||||
afterAllTabsLoaded(testVariousTabOrders);
|
||||
});
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let branch = Services.prefs.getBranch('browser.panorama.');
|
||||
branch.setBoolPref('experienced_first_run', false);
|
||||
|
||||
newWindowWithTabView(function (win) {
|
||||
is(win.gBrowser.visibleTabs.length, 1, 'There should be one visible tab, only');
|
||||
|
||||
win.TabView._initFrame(function () {
|
||||
is(win.gBrowser.visibleTabs.length, 1, 'There should be one visible tab, only');
|
||||
|
||||
win.close();
|
||||
finish();
|
||||
});
|
||||
});
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче