mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Automated merge with https://hg.mozilla.org/mozilla-central
This commit is contained in:
commit
8a50677a0d
@ -96,7 +96,7 @@
|
|||||||
gQueue.push(new focusElmWhileSubdocIsFocused("link"));
|
gQueue.push(new focusElmWhileSubdocIsFocused("link"));
|
||||||
|
|
||||||
gQueue.push(new synthTab(editableDoc, new focusChecker(editableDoc)));
|
gQueue.push(new synthTab(editableDoc, new focusChecker(editableDoc)));
|
||||||
if (WIN) {
|
if (WIN || LINUX) {
|
||||||
// Alt key is used to active menubar and focus menu item on Windows,
|
// Alt key is used to active menubar and focus menu item on Windows,
|
||||||
// other platforms requires setting a ui.key.menuAccessKeyFocuses
|
// other platforms requires setting a ui.key.menuAccessKeyFocuses
|
||||||
// preference.
|
// preference.
|
||||||
|
@ -156,7 +156,7 @@
|
|||||||
// Alt key is used to active menubar and focus menu item on Windows,
|
// Alt key is used to active menubar and focus menu item on Windows,
|
||||||
// other platforms requires setting a ui.key.menuAccessKeyFocuses
|
// other platforms requires setting a ui.key.menuAccessKeyFocuses
|
||||||
// preference.
|
// preference.
|
||||||
if (WIN) {
|
if (WIN || LINUX) {
|
||||||
gQueue.push(new focusFileMenu());
|
gQueue.push(new focusFileMenu());
|
||||||
gQueue.push(new focusEditMenu());
|
gQueue.push(new focusEditMenu());
|
||||||
gQueue.push(new leaveMenubar());
|
gQueue.push(new leaveMenubar());
|
||||||
|
@ -11,7 +11,7 @@ module.metadata = {
|
|||||||
|
|
||||||
const { Cu } = require('chrome');
|
const { Cu } = require('chrome');
|
||||||
const { Loader } = require('sdk/test/loader');
|
const { Loader } = require('sdk/test/loader');
|
||||||
const data = require('./fixtures');
|
const { data } = require('sdk/self');
|
||||||
const { open, focus, close } = require('sdk/window/helpers');
|
const { open, focus, close } = require('sdk/window/helpers');
|
||||||
const { setTimeout } = require('sdk/timers');
|
const { setTimeout } = require('sdk/timers');
|
||||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||||
|
@ -444,11 +444,10 @@ pref("browser.tabs.loadDivertedInBackground", false);
|
|||||||
pref("browser.tabs.loadBookmarksInBackground", false);
|
pref("browser.tabs.loadBookmarksInBackground", false);
|
||||||
pref("browser.tabs.tabClipWidth", 140);
|
pref("browser.tabs.tabClipWidth", 140);
|
||||||
pref("browser.tabs.animate", true);
|
pref("browser.tabs.animate", true);
|
||||||
pref("browser.tabs.onTop", true);
|
#ifdef UNIX_BUT_NOT_MAC
|
||||||
#ifdef XP_WIN
|
|
||||||
pref("browser.tabs.drawInTitlebar", true);
|
|
||||||
#else
|
|
||||||
pref("browser.tabs.drawInTitlebar", false);
|
pref("browser.tabs.drawInTitlebar", false);
|
||||||
|
#else
|
||||||
|
pref("browser.tabs.drawInTitlebar", true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Where to show tab close buttons:
|
// Where to show tab close buttons:
|
||||||
@ -1324,3 +1323,6 @@ pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%G
|
|||||||
// Necko IPC security checks only needed for app isolation for cookies/cache/etc:
|
// Necko IPC security checks only needed for app isolation for cookies/cache/etc:
|
||||||
// currently irrelevant for desktop e10s
|
// currently irrelevant for desktop e10s
|
||||||
pref("network.disable.ipc.security", true);
|
pref("network.disable.ipc.security", true);
|
||||||
|
|
||||||
|
// CustomizableUI debug logging.
|
||||||
|
pref("browser.uiCustomization.debug", false);
|
||||||
|
@ -178,50 +178,6 @@ const gXPInstallObserver = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* When addons are installed/uninstalled, check and see if the number of items
|
|
||||||
* on the add-on bar changed:
|
|
||||||
* - If an add-on was installed, incrementing the count, show the bar.
|
|
||||||
* - If an add-on was uninstalled, and no more items are left, hide the bar.
|
|
||||||
*/
|
|
||||||
let AddonsMgrListener = {
|
|
||||||
get addonBar() document.getElementById("addon-bar"),
|
|
||||||
get statusBar() document.getElementById("status-bar"),
|
|
||||||
getAddonBarItemCount: function() {
|
|
||||||
// Take into account the contents of the status bar shim for the count.
|
|
||||||
var itemCount = this.statusBar.childNodes.length;
|
|
||||||
|
|
||||||
var defaultOrNoninteractive = this.addonBar.getAttribute("defaultset")
|
|
||||||
.split(",")
|
|
||||||
.concat(["separator", "spacer", "spring"]);
|
|
||||||
for (let item of this.addonBar.currentSet.split(",")) {
|
|
||||||
if (defaultOrNoninteractive.indexOf(item) == -1)
|
|
||||||
itemCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return itemCount;
|
|
||||||
},
|
|
||||||
onInstalling: function(aAddon) {
|
|
||||||
this.lastAddonBarCount = this.getAddonBarItemCount();
|
|
||||||
},
|
|
||||||
onInstalled: function(aAddon) {
|
|
||||||
if (this.getAddonBarItemCount() > this.lastAddonBarCount)
|
|
||||||
setToolbarVisibility(this.addonBar, true);
|
|
||||||
},
|
|
||||||
onUninstalling: function(aAddon) {
|
|
||||||
this.lastAddonBarCount = this.getAddonBarItemCount();
|
|
||||||
},
|
|
||||||
onUninstalled: function(aAddon) {
|
|
||||||
if (this.getAddonBarItemCount() == 0)
|
|
||||||
setToolbarVisibility(this.addonBar, false);
|
|
||||||
},
|
|
||||||
onEnabling: function(aAddon) this.onInstalling(),
|
|
||||||
onEnabled: function(aAddon) this.onInstalled(),
|
|
||||||
onDisabling: function(aAddon) this.onUninstalling(),
|
|
||||||
onDisabled: function(aAddon) this.onUninstalled(),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var LightWeightThemeWebInstaller = {
|
var LightWeightThemeWebInstaller = {
|
||||||
handleEvent: function (event) {
|
handleEvent: function (event) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
@ -415,3 +371,60 @@ var LightWeightThemeWebInstaller = {
|
|||||||
node.baseURI);
|
node.baseURI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Listen for Lightweight Theme styling changes and update the browser's theme accordingly.
|
||||||
|
*/
|
||||||
|
let LightweightThemeListener = {
|
||||||
|
_modifiedStyles: [],
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
XPCOMUtils.defineLazyGetter(this, "styleSheet", function() {
|
||||||
|
for (let i = document.styleSheets.length - 1; i >= 0; i--) {
|
||||||
|
let sheet = document.styleSheets[i];
|
||||||
|
if (sheet.href == "chrome://browser/skin/browser-lightweightTheme.css")
|
||||||
|
return sheet;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
|
||||||
|
if (document.documentElement.hasAttribute("lwtheme"))
|
||||||
|
this.updateStyleSheet(document.documentElement.style.backgroundImage);
|
||||||
|
},
|
||||||
|
|
||||||
|
uninit: function () {
|
||||||
|
Services.obs.removeObserver(this, "lightweight-theme-styling-update");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the headerImage to the background-image property of all rulesets in
|
||||||
|
* browser-lightweightTheme.css.
|
||||||
|
*
|
||||||
|
* @param headerImage - a string containing a CSS image for the lightweight theme header.
|
||||||
|
*/
|
||||||
|
updateStyleSheet: function(headerImage) {
|
||||||
|
if (!this.styleSheet)
|
||||||
|
return;
|
||||||
|
for (let i = 0; i < this.styleSheet.cssRules.length; i++) {
|
||||||
|
let rule = this.styleSheet.cssRules[i];
|
||||||
|
if (!rule.style.backgroundImage)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!this._modifiedStyles[i])
|
||||||
|
this._modifiedStyles[i] = { backgroundImage: rule.style.backgroundImage };
|
||||||
|
|
||||||
|
rule.style.backgroundImage = this._modifiedStyles[i].backgroundImage + ", " + headerImage;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// nsIObserver
|
||||||
|
observe: function (aSubject, aTopic, aData) {
|
||||||
|
if (aTopic != "lightweight-theme-styling-update" || !this.styleSheet)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let themeData = JSON.parse(aData);
|
||||||
|
if (!themeData)
|
||||||
|
return;
|
||||||
|
this.updateStyleSheet("url(" + themeData.headerURL + ")");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
@ -1,406 +0,0 @@
|
|||||||
# -*- Mode: HTML -*-
|
|
||||||
# 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/.
|
|
||||||
|
|
||||||
<menupopup id="appmenu-popup"
|
|
||||||
onpopupshowing="if (event.target == this) {
|
|
||||||
updateEditUIVisibility();
|
|
||||||
#ifdef MOZ_SERVICES_SYNC
|
|
||||||
gSyncUI.updateUI();
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateCharacterEncodingMenuState();
|
|
||||||
if (event.target.parentNode.parentNode.parentNode.parentNode == this)
|
|
||||||
this._currentPopup = event.target;">
|
|
||||||
<hbox>
|
|
||||||
<vbox id="appmenuPrimaryPane">
|
|
||||||
<splitmenu id="appmenu_newTab"
|
|
||||||
label="&tabCmd.label;"
|
|
||||||
command="cmd_newNavigatorTab">
|
|
||||||
<menupopup>
|
|
||||||
<menuitem id="appmenu_newTab_popup"
|
|
||||||
label="&tabCmd.label;"
|
|
||||||
command="cmd_newNavigatorTab"
|
|
||||||
key="key_newNavigatorTab"/>
|
|
||||||
<menuitem id="appmenu_newNavigator"
|
|
||||||
label="&newNavigatorCmd.label;"
|
|
||||||
command="cmd_newNavigator"
|
|
||||||
key="key_newNavigator"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu_openFile"
|
|
||||||
label="&openFileCmd.label;"
|
|
||||||
command="Browser:OpenFile"
|
|
||||||
key="openFileKb"/>
|
|
||||||
</menupopup>
|
|
||||||
</splitmenu>
|
|
||||||
<menuitem id="appmenu_newPrivateWindow"
|
|
||||||
class="menuitem-iconic menuitem-iconic-tooltip"
|
|
||||||
label="&newPrivateWindow.label;"
|
|
||||||
command="Tools:PrivateBrowsing"
|
|
||||||
key="key_privatebrowsing"/>
|
|
||||||
<menuitem label="&goOfflineCmd.label;"
|
|
||||||
id="appmenu_offlineModeRecovery"
|
|
||||||
type="checkbox"
|
|
||||||
observes="workOfflineMenuitemState"
|
|
||||||
oncommand="BrowserOffline.toggleOfflineStatus();"/>
|
|
||||||
<menuseparator class="appmenu-menuseparator"/>
|
|
||||||
<hbox>
|
|
||||||
<menuitem id="appmenu-edit-label"
|
|
||||||
label="&appMenuEdit.label;"
|
|
||||||
disabled="true"/>
|
|
||||||
<toolbarbutton id="appmenu-cut"
|
|
||||||
class="appmenu-edit-button"
|
|
||||||
command="cmd_cut"
|
|
||||||
onclick="if (!this.disabled) hidePopup();"
|
|
||||||
tooltiptext="&cutButton.tooltip;"/>
|
|
||||||
<toolbarbutton id="appmenu-copy"
|
|
||||||
class="appmenu-edit-button"
|
|
||||||
command="cmd_copy"
|
|
||||||
onclick="if (!this.disabled) hidePopup();"
|
|
||||||
tooltiptext="©Button.tooltip;"/>
|
|
||||||
<toolbarbutton id="appmenu-paste"
|
|
||||||
class="appmenu-edit-button"
|
|
||||||
command="cmd_paste"
|
|
||||||
onclick="if (!this.disabled) hidePopup();"
|
|
||||||
tooltiptext="&pasteButton.tooltip;"/>
|
|
||||||
<spacer flex="1"/>
|
|
||||||
<menu id="appmenu-editmenu">
|
|
||||||
<menupopup id="appmenu-editmenu-menupopup">
|
|
||||||
<menuitem id="appmenu-editmenu-cut"
|
|
||||||
class="menuitem-iconic"
|
|
||||||
label="&cutCmd.label;"
|
|
||||||
key="key_cut"
|
|
||||||
command="cmd_cut"/>
|
|
||||||
<menuitem id="appmenu-editmenu-copy"
|
|
||||||
class="menuitem-iconic"
|
|
||||||
label="©Cmd.label;"
|
|
||||||
key="key_copy"
|
|
||||||
command="cmd_copy"/>
|
|
||||||
<menuitem id="appmenu-editmenu-paste"
|
|
||||||
class="menuitem-iconic"
|
|
||||||
label="&pasteCmd.label;"
|
|
||||||
key="key_paste"
|
|
||||||
command="cmd_paste"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu-editmenu-undo"
|
|
||||||
label="&undoCmd.label;"
|
|
||||||
key="key_undo"
|
|
||||||
command="cmd_undo"/>
|
|
||||||
<menuitem id="appmenu-editmenu-redo"
|
|
||||||
label="&redoCmd.label;"
|
|
||||||
key="key_redo"
|
|
||||||
command="cmd_redo"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu-editmenu-selectAll"
|
|
||||||
label="&selectAllCmd.label;"
|
|
||||||
key="key_selectAll"
|
|
||||||
command="cmd_selectAll"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu-editmenu-delete"
|
|
||||||
label="&deleteCmd.label;"
|
|
||||||
key="key_delete"
|
|
||||||
command="cmd_delete"/>
|
|
||||||
</menupopup>
|
|
||||||
</menu>
|
|
||||||
</hbox>
|
|
||||||
<menuitem id="appmenu_find"
|
|
||||||
class="menuitem-tooltip"
|
|
||||||
label="&appMenuFind.label;"
|
|
||||||
command="cmd_find"
|
|
||||||
key="key_find"/>
|
|
||||||
<menuseparator class="appmenu-menuseparator"/>
|
|
||||||
<menuitem id="appmenu_savePage"
|
|
||||||
class="menuitem-tooltip"
|
|
||||||
label="&savePageCmd.label;"
|
|
||||||
command="Browser:SavePage"
|
|
||||||
key="key_savePage"/>
|
|
||||||
<menuitem id="appmenu_sendLink"
|
|
||||||
label="&emailPageCmd.label;"
|
|
||||||
command="Browser:SendLink"/>
|
|
||||||
<splitmenu id="appmenu_print"
|
|
||||||
iconic="true"
|
|
||||||
label="&printCmd.label;"
|
|
||||||
command="cmd_print">
|
|
||||||
<menupopup>
|
|
||||||
<menuitem id="appmenu_print_popup"
|
|
||||||
class="menuitem-iconic"
|
|
||||||
label="&printCmd.label;"
|
|
||||||
command="cmd_print"
|
|
||||||
key="printKb"/>
|
|
||||||
<menuitem id="appmenu_printPreview"
|
|
||||||
label="&printPreviewCmd.label;"
|
|
||||||
command="cmd_printPreview"/>
|
|
||||||
<menuitem id="appmenu_printSetup"
|
|
||||||
label="&printSetupCmd.label;"
|
|
||||||
command="cmd_pageSetup"/>
|
|
||||||
</menupopup>
|
|
||||||
</splitmenu>
|
|
||||||
<menuseparator class="appmenu-menuseparator"/>
|
|
||||||
<splitmenu id="appmenu_webDeveloper"
|
|
||||||
command="Tools:DevToolbox"
|
|
||||||
label="&appMenuWebDeveloper.label;">
|
|
||||||
<menupopup id="appmenu_webDeveloper_popup">
|
|
||||||
<menuitem id="appmenu_devToolbox"
|
|
||||||
observes="devtoolsMenuBroadcaster_DevToolbox"/>
|
|
||||||
<menuseparator id="appmenu_devtools_separator"/>
|
|
||||||
<menuitem id="appmenu_devToolbar"
|
|
||||||
observes="devtoolsMenuBroadcaster_DevToolbar"/>
|
|
||||||
<menuitem id="appmenu_devAppMgr"
|
|
||||||
observes="devtoolsMenuBroadcaster_DevAppMgr"/>
|
|
||||||
<menuitem id="appmenu_chromeDebugger"
|
|
||||||
observes="devtoolsMenuBroadcaster_ChromeDebugger"/>
|
|
||||||
<menuitem id="appmenu_browserConsole"
|
|
||||||
observes="devtoolsMenuBroadcaster_BrowserConsole"/>
|
|
||||||
<menuitem id="appmenu_responsiveUI"
|
|
||||||
observes="devtoolsMenuBroadcaster_ResponsiveUI"/>
|
|
||||||
<menuitem id="appmenu_scratchpad"
|
|
||||||
observes="devtoolsMenuBroadcaster_Scratchpad"/>
|
|
||||||
<menuitem id="appmenu_pageSource"
|
|
||||||
observes="devtoolsMenuBroadcaster_PageSource"/>
|
|
||||||
<menuitem id="appmenu_errorConsole"
|
|
||||||
observes="devtoolsMenuBroadcaster_ErrorConsole"/>
|
|
||||||
<menuitem id="appmenu_devtools_connect"
|
|
||||||
observes="devtoolsMenuBroadcaster_connect"/>
|
|
||||||
<menuseparator id="appmenu_devToolsEndSeparator"/>
|
|
||||||
<menuitem id="appmenu_getMoreDevtools"
|
|
||||||
observes="devtoolsMenuBroadcaster_GetMoreTools"/>
|
|
||||||
<menuseparator/>
|
|
||||||
#define ID_PREFIX appmenu_developer_
|
|
||||||
#define OMIT_ACCESSKEYS
|
|
||||||
#include browser-charsetmenu.inc
|
|
||||||
#undef ID_PREFIX
|
|
||||||
#undef OMIT_ACCESSKEYS
|
|
||||||
<menuitem label="&goOfflineCmd.label;"
|
|
||||||
type="checkbox"
|
|
||||||
observes="workOfflineMenuitemState"
|
|
||||||
oncommand="BrowserOffline.toggleOfflineStatus();"/>
|
|
||||||
</menupopup>
|
|
||||||
</splitmenu>
|
|
||||||
<menuseparator class="appmenu-menuseparator"/>
|
|
||||||
#define ID_PREFIX appmenu_
|
|
||||||
#define OMIT_ACCESSKEYS
|
|
||||||
#include browser-charsetmenu.inc
|
|
||||||
#undef ID_PREFIX
|
|
||||||
#undef OMIT_ACCESSKEYS
|
|
||||||
<menuitem id="appmenu_fullScreen"
|
|
||||||
class="menuitem-tooltip"
|
|
||||||
label="&fullScreenCmd.label;"
|
|
||||||
type="checkbox"
|
|
||||||
observes="View:FullScreen"
|
|
||||||
key="key_fullScreen"/>
|
|
||||||
#ifdef MOZ_SERVICES_SYNC
|
|
||||||
<!-- only one of sync-setup or sync-syncnow will be showing at once -->
|
|
||||||
<menuitem id="sync-setup-appmenu"
|
|
||||||
label="&syncSetup.label;"
|
|
||||||
observes="sync-setup-state"
|
|
||||||
oncommand="gSyncUI.openSetup()"/>
|
|
||||||
<menuitem id="sync-syncnowitem-appmenu"
|
|
||||||
label="&syncSyncNowItem.label;"
|
|
||||||
observes="sync-syncnow-state"
|
|
||||||
oncommand="gSyncUI.doSync(event);"/>
|
|
||||||
#endif
|
|
||||||
<menuitem id="appmenu-quit"
|
|
||||||
class="menuitem-iconic"
|
|
||||||
#ifdef XP_WIN
|
|
||||||
label="&quitApplicationCmdWin.label;"
|
|
||||||
#else
|
|
||||||
label="&quitApplicationCmd.label;"
|
|
||||||
#endif
|
|
||||||
command="cmd_quitApplication"/>
|
|
||||||
</vbox>
|
|
||||||
<vbox id="appmenuSecondaryPane">
|
|
||||||
<splitmenu id="appmenu_bookmarks"
|
|
||||||
iconic="true"
|
|
||||||
label="&bookmarksMenu.label;"
|
|
||||||
command="Browser:ShowAllBookmarks">
|
|
||||||
<menupopup id="appmenu_bookmarksPopup"
|
|
||||||
placespopup="true"
|
|
||||||
context="placesContext"
|
|
||||||
openInTabs="children"
|
|
||||||
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
|
|
||||||
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
|
|
||||||
onpopupshowing="BookmarkingUI.onPopupShowing(event);
|
|
||||||
if (!this.parentNode._placesView)
|
|
||||||
new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
|
|
||||||
tooltip="bhTooltip"
|
|
||||||
popupsinherittooltip="true">
|
|
||||||
<menuitem id="appmenu_showAllBookmarks"
|
|
||||||
label="&showAllBookmarks2.label;"
|
|
||||||
command="Browser:ShowAllBookmarks"
|
|
||||||
context=""
|
|
||||||
key="manBookmarkKb"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu_bookmarkThisPage"
|
|
||||||
class="menuitem-iconic"
|
|
||||||
label="&bookmarkThisPageCmd.label;"
|
|
||||||
command="Browser:AddBookmarkAs"
|
|
||||||
key="addBookmarkAsKb"/>
|
|
||||||
<menuitem id="appmenu_subscribeToPage"
|
|
||||||
class="menuitem-iconic"
|
|
||||||
label="&subscribeToPageMenuitem.label;"
|
|
||||||
oncommand="return FeedHandler.subscribeToFeed(null, event);"
|
|
||||||
onclick="checkForMiddleClick(this, event);"
|
|
||||||
observes="singleFeedMenuitemState"/>
|
|
||||||
<menu id="appmenu_subscribeToPageMenu"
|
|
||||||
class="menu-iconic"
|
|
||||||
label="&subscribeToPageMenupopup.label;"
|
|
||||||
observes="multipleFeedsMenuState">
|
|
||||||
<menupopup id="appmenu_subscribeToPageMenupopup"
|
|
||||||
onpopupshowing="return FeedHandler.buildFeedList(event.target);"
|
|
||||||
oncommand="return FeedHandler.subscribeToFeed(null, event);"
|
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
|
||||||
</menu>
|
|
||||||
<menuseparator/>
|
|
||||||
<menu id="appmenu_bookmarksToolbar"
|
|
||||||
placesanonid="toolbar-autohide"
|
|
||||||
class="menu-iconic bookmark-item"
|
|
||||||
label="&personalbarCmd.label;"
|
|
||||||
container="true">
|
|
||||||
<menupopup id="appmenu_bookmarksToolbarPopup"
|
|
||||||
placespopup="true"
|
|
||||||
context="placesContext"
|
|
||||||
onpopupshowing="if (!this.parentNode._placesView)
|
|
||||||
new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
|
|
||||||
</menu>
|
|
||||||
<menuseparator/>
|
|
||||||
<!-- Bookmarks menu items -->
|
|
||||||
<menuseparator builder="end"
|
|
||||||
class="hide-if-empty-places-result"/>
|
|
||||||
<menuitem id="appmenu_unsortedBookmarks"
|
|
||||||
label="&appMenuUnsorted.label;"
|
|
||||||
oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
|
|
||||||
class="menuitem-iconic"/>
|
|
||||||
</menupopup>
|
|
||||||
</splitmenu>
|
|
||||||
<splitmenu id="appmenu_history"
|
|
||||||
iconic="true"
|
|
||||||
label="&historyMenu.label;"
|
|
||||||
command="Browser:ShowAllHistory">
|
|
||||||
<menupopup id="appmenu_historyMenupopup"
|
|
||||||
placespopup="true"
|
|
||||||
oncommand="this.parentNode._placesView._onCommand(event);"
|
|
||||||
onclick="checkForMiddleClick(this, event);"
|
|
||||||
onpopupshowing="if (!this.parentNode._placesView)
|
|
||||||
new HistoryMenu(event);"
|
|
||||||
tooltip="bhTooltip"
|
|
||||||
popupsinherittooltip="true">
|
|
||||||
<menuitem id="appmenu_showAllHistory"
|
|
||||||
label="&showAllHistoryCmd2.label;"
|
|
||||||
command="Browser:ShowAllHistory"
|
|
||||||
key="showAllHistoryKb"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu_sanitizeHistory"
|
|
||||||
label="&clearRecentHistory.label;"
|
|
||||||
key="key_sanitize"
|
|
||||||
command="Tools:Sanitize"/>
|
|
||||||
<menuseparator class="hide-if-empty-places-result"/>
|
|
||||||
#ifdef MOZ_SERVICES_SYNC
|
|
||||||
<menuitem id="appmenu_sync-tabs"
|
|
||||||
class="syncTabsMenuItem"
|
|
||||||
label="&syncTabsMenu2.label;"
|
|
||||||
oncommand="BrowserOpenSyncTabs();"
|
|
||||||
disabled="true"/>
|
|
||||||
#endif
|
|
||||||
<menuitem id="appmenu_restoreLastSession"
|
|
||||||
label="&historyRestoreLastSession.label;"
|
|
||||||
command="Browser:RestoreLastSession"/>
|
|
||||||
<menu id="appmenu_recentlyClosedTabsMenu"
|
|
||||||
class="recentlyClosedTabsMenu"
|
|
||||||
label="&historyUndoMenu.label;"
|
|
||||||
disabled="true">
|
|
||||||
<menupopup id="appmenu_recentlyClosedTabsMenupopup"
|
|
||||||
onpopupshowing="document.getElementById('appmenu_history')._placesView.populateUndoSubmenu();"/>
|
|
||||||
</menu>
|
|
||||||
<menu id="appmenu_recentlyClosedWindowsMenu"
|
|
||||||
class="recentlyClosedWindowsMenu"
|
|
||||||
label="&historyUndoWindowMenu.label;"
|
|
||||||
disabled="true">
|
|
||||||
<menupopup id="appmenu_recentlyClosedWindowsMenupopup"
|
|
||||||
onpopupshowing="document.getElementById('appmenu_history')._placesView.populateUndoWindowSubmenu();"/>
|
|
||||||
</menu>
|
|
||||||
<menuseparator/>
|
|
||||||
</menupopup>
|
|
||||||
</splitmenu>
|
|
||||||
<menuitem id="appmenu_downloads"
|
|
||||||
class="menuitem-tooltip"
|
|
||||||
label="&downloads.label;"
|
|
||||||
command="Tools:Downloads"
|
|
||||||
key="key_openDownloads"/>
|
|
||||||
<spacer id="appmenuSecondaryPane-spacer"/>
|
|
||||||
<menuitem id="appmenu_addons"
|
|
||||||
class="menuitem-iconic menuitem-iconic-tooltip"
|
|
||||||
label="&addons.label;"
|
|
||||||
command="Tools:Addons"
|
|
||||||
key="key_openAddons"/>
|
|
||||||
<splitmenu id="appmenu_customize"
|
|
||||||
#ifdef XP_UNIX
|
|
||||||
label="&preferencesCmdUnix.label;"
|
|
||||||
#else
|
|
||||||
label="&preferencesCmd2.label;"
|
|
||||||
#endif
|
|
||||||
oncommand="openPreferences();">
|
|
||||||
<menupopup id="appmenu_customizeMenu"
|
|
||||||
onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('appmenu_toggleToolbarsSeparator'));">
|
|
||||||
<menuitem id="appmenu_preferences"
|
|
||||||
#ifdef XP_UNIX
|
|
||||||
label="&preferencesCmdUnix.label;"
|
|
||||||
#else
|
|
||||||
label="&preferencesCmd2.label;"
|
|
||||||
#endif
|
|
||||||
oncommand="openPreferences();"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuseparator id="appmenu_toggleToolbarsSeparator"/>
|
|
||||||
<menuitem id="appmenu_toggleTabsOnTop"
|
|
||||||
label="&viewTabsOnTop.label;"
|
|
||||||
type="checkbox"
|
|
||||||
command="cmd_ToggleTabsOnTop"/>
|
|
||||||
<menuitem id="appmenu_toolbarLayout"
|
|
||||||
label="&appMenuToolbarLayout.label;"
|
|
||||||
command="cmd_CustomizeToolbars"/>
|
|
||||||
</menupopup>
|
|
||||||
</splitmenu>
|
|
||||||
<splitmenu id="appmenu_help"
|
|
||||||
label="&helpMenu.label;"
|
|
||||||
oncommand="openHelpLink('firefox-help')">
|
|
||||||
<menupopup id="appmenu_helpMenupopup">
|
|
||||||
<menuitem id="appmenu_openHelp"
|
|
||||||
label="&helpMenu.label;"
|
|
||||||
oncommand="openHelpLink('firefox-help')"
|
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
|
||||||
<menuitem id="appmenu_gettingStarted"
|
|
||||||
label="&appMenuGettingStarted.label;"
|
|
||||||
oncommand="gBrowser.loadOneTab('https://www.mozilla.org/firefox/central/', {inBackground: false});"
|
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
|
||||||
<menuitem id="appmenu_keyboardShortcuts"
|
|
||||||
label="&helpKeyboardShortcuts.label;"
|
|
||||||
oncommand="openHelpLink('keyboard-shortcuts')"
|
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
|
||||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
|
||||||
<menuitem id="appmenu_healthReport"
|
|
||||||
label="&healthReport.label;"
|
|
||||||
oncommand="openHealthReport()"
|
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
|
||||||
#endif
|
|
||||||
<menuitem id="appmenu_troubleshootingInfo"
|
|
||||||
label="&helpTroubleshootingInfo.label;"
|
|
||||||
oncommand="openTroubleshootingPage()"
|
|
||||||
onclick="checkForMiddleClick(this,event);"/>
|
|
||||||
<menuitem id="appmenu_feedbackPage"
|
|
||||||
label="&helpFeedbackPage.label;"
|
|
||||||
oncommand="openFeedbackPage()"
|
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu_safeMode"
|
|
||||||
label="&appMenuSafeMode.label;"
|
|
||||||
oncommand="safeModeRestart();"/>
|
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu_about"
|
|
||||||
label="&aboutProduct.label;"
|
|
||||||
oncommand="openAboutDialog();"/>
|
|
||||||
</menupopup>
|
|
||||||
</splitmenu>
|
|
||||||
</vbox>
|
|
||||||
</hbox>
|
|
||||||
</menupopup>
|
|
97
browser/base/content/browser-customization.js
Normal file
97
browser/base/content/browser-customization.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
# 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/.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customization handler prepares this browser window for entering and exiting
|
||||||
|
* customization mode by handling customizationstarting and customizationending
|
||||||
|
* events.
|
||||||
|
*/
|
||||||
|
let CustomizationHandler = {
|
||||||
|
handleEvent: function(aEvent) {
|
||||||
|
switch(aEvent.type) {
|
||||||
|
case "customizationstarting":
|
||||||
|
this._customizationStarting();
|
||||||
|
break;
|
||||||
|
case "customizationending":
|
||||||
|
this._customizationEnding(aEvent.detail);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isCustomizing: function() {
|
||||||
|
return document.documentElement.hasAttribute("customizing") ||
|
||||||
|
document.documentElement.hasAttribute("customize-exiting");
|
||||||
|
},
|
||||||
|
|
||||||
|
_customizationStarting: function() {
|
||||||
|
// Disable the toolbar context menu items
|
||||||
|
let menubar = document.getElementById("main-menubar");
|
||||||
|
for (let childNode of menubar.childNodes)
|
||||||
|
childNode.setAttribute("disabled", true);
|
||||||
|
|
||||||
|
let cmd = document.getElementById("cmd_CustomizeToolbars");
|
||||||
|
cmd.setAttribute("disabled", "true");
|
||||||
|
|
||||||
|
let splitter = document.getElementById("urlbar-search-splitter");
|
||||||
|
if (splitter) {
|
||||||
|
splitter.parentNode.removeChild(splitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
CombinedStopReload.uninit();
|
||||||
|
PlacesToolbarHelper.customizeStart();
|
||||||
|
BookmarkingUI.customizeStart();
|
||||||
|
DownloadsButton.customizeStart();
|
||||||
|
},
|
||||||
|
|
||||||
|
_customizationEnding: function(aDetails) {
|
||||||
|
// Update global UI elements that may have been added or removed
|
||||||
|
if (aDetails.changed) {
|
||||||
|
gURLBar = document.getElementById("urlbar");
|
||||||
|
|
||||||
|
gProxyFavIcon = document.getElementById("page-proxy-favicon");
|
||||||
|
gHomeButton.updateTooltip();
|
||||||
|
gIdentityHandler._cacheElements();
|
||||||
|
XULBrowserWindow.init();
|
||||||
|
|
||||||
|
#ifndef XP_MACOSX
|
||||||
|
updateEditUIVisibility();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Hacky: update the PopupNotifications' object's reference to the iconBox,
|
||||||
|
// if it already exists, since it may have changed if the URL bar was
|
||||||
|
// added/removed.
|
||||||
|
if (!window.__lookupGetter__("PopupNotifications")) {
|
||||||
|
PopupNotifications.iconBox =
|
||||||
|
document.getElementById("notification-popup-box");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PlacesToolbarHelper.customizeDone();
|
||||||
|
BookmarkingUI.customizeDone();
|
||||||
|
DownloadsButton.customizeDone();
|
||||||
|
|
||||||
|
// The url bar splitter state is dependent on whether stop/reload
|
||||||
|
// and the location bar are combined, so we need this ordering
|
||||||
|
CombinedStopReload.init();
|
||||||
|
UpdateUrlbarSearchSplitterState();
|
||||||
|
|
||||||
|
// Update the urlbar
|
||||||
|
if (gURLBar) {
|
||||||
|
URLBarSetURI();
|
||||||
|
XULBrowserWindow.asyncUpdateUI();
|
||||||
|
BookmarkingUI.updateStarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-enable parts of the UI we disabled during the dialog
|
||||||
|
let menubar = document.getElementById("main-menubar");
|
||||||
|
for (let childNode of menubar.childNodes)
|
||||||
|
childNode.setAttribute("disabled", false);
|
||||||
|
let cmd = document.getElementById("cmd_CustomizeToolbars");
|
||||||
|
cmd.removeAttribute("disabled");
|
||||||
|
|
||||||
|
gBrowser.selectedBrowser.focus();
|
||||||
|
}
|
||||||
|
}
|
@ -8,66 +8,53 @@
|
|||||||
* and shows UI when they are discovered.
|
* and shows UI when they are discovered.
|
||||||
*/
|
*/
|
||||||
var FeedHandler = {
|
var FeedHandler = {
|
||||||
/**
|
/** Called when the user clicks on the Subscribe to This Page... menu item,
|
||||||
* The click handler for the Feed icon in the toolbar. Opens the
|
* or when the user clicks the feed button when the page contains multiple
|
||||||
* subscription page if user is not given a choice of feeds.
|
* feeds.
|
||||||
* (Otherwise the list of available feeds will be presented to the
|
|
||||||
* user in a popup menu.)
|
|
||||||
*/
|
|
||||||
onFeedButtonClick: function(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
let feeds = gBrowser.selectedBrowser.feeds || [];
|
|
||||||
// If there are multiple feeds, the menu will open, so no need to do
|
|
||||||
// anything. If there are no feeds, nothing to do either.
|
|
||||||
if (feeds.length != 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (event.eventPhase == Event.AT_TARGET &&
|
|
||||||
(event.button == 0 || event.button == 1)) {
|
|
||||||
this.subscribeToFeed(feeds[0].href, event);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Called when the user clicks on the Subscribe to This Page... menu item.
|
|
||||||
* Builds a menu of unique feeds associated with the page, and if there
|
* Builds a menu of unique feeds associated with the page, and if there
|
||||||
* is only one, shows the feed inline in the browser window.
|
* is only one, shows the feed inline in the browser window.
|
||||||
* @param menuPopup
|
* @param container
|
||||||
* The feed list menupopup to be populated.
|
* The feed list container (menupopup or subview) to be populated.
|
||||||
* @returns true if the menu should be shown, false if there was only
|
* @param isSubview
|
||||||
|
* Whether we're creating a subview (true) or menu (false/undefined)
|
||||||
|
* @returns true if the menu/subview should be shown, false if there was only
|
||||||
* one feed and the feed should be shown inline in the browser
|
* one feed and the feed should be shown inline in the browser
|
||||||
* window (do not show the menupopup).
|
* window (do not show the menupopup/subview).
|
||||||
*/
|
*/
|
||||||
buildFeedList: function(menuPopup) {
|
buildFeedList: function(container, isSubview) {
|
||||||
var feeds = gBrowser.selectedBrowser.feeds;
|
var feeds = gBrowser.selectedBrowser.feeds;
|
||||||
if (feeds == null) {
|
if (!isSubview && feeds == null) {
|
||||||
// XXX hack -- menu opening depends on setting of an "open"
|
// XXX hack -- menu opening depends on setting of an "open"
|
||||||
// attribute, and the menu refuses to open if that attribute is
|
// attribute, and the menu refuses to open if that attribute is
|
||||||
// set (because it thinks it's already open). onpopupshowing gets
|
// set (because it thinks it's already open). onpopupshowing gets
|
||||||
// called after the attribute is unset, and it doesn't get unset
|
// called after the attribute is unset, and it doesn't get unset
|
||||||
// if we return false. so we unset it here; otherwise, the menu
|
// if we return false. so we unset it here; otherwise, the menu
|
||||||
// refuses to work past this point.
|
// refuses to work past this point.
|
||||||
menuPopup.parentNode.removeAttribute("open");
|
container.parentNode.removeAttribute("open");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (menuPopup.firstChild)
|
while (container.firstChild)
|
||||||
menuPopup.removeChild(menuPopup.firstChild);
|
container.removeChild(container.firstChild);
|
||||||
|
|
||||||
if (feeds.length <= 1)
|
if (!feeds || feeds.length <= 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Build the menu showing the available feed choices for viewing.
|
// Build the menu showing the available feed choices for viewing.
|
||||||
|
var itemNodeType = isSubview ? "toolbarbutton" : "menuitem";
|
||||||
for (let feedInfo of feeds) {
|
for (let feedInfo of feeds) {
|
||||||
var menuItem = document.createElement("menuitem");
|
var item = document.createElement(itemNodeType);
|
||||||
var baseTitle = feedInfo.title || feedInfo.href;
|
var baseTitle = feedInfo.title || feedInfo.href;
|
||||||
var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
|
var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
|
||||||
menuItem.setAttribute("class", "feed-menuitem");
|
item.setAttribute("class", "feed-" + itemNodeType);
|
||||||
menuItem.setAttribute("label", labelStr);
|
item.setAttribute("label", labelStr);
|
||||||
menuItem.setAttribute("feed", feedInfo.href);
|
item.setAttribute("feed", feedInfo.href);
|
||||||
menuItem.setAttribute("tooltiptext", feedInfo.href);
|
item.setAttribute("tooltiptext", feedInfo.href);
|
||||||
menuItem.setAttribute("crop", "center");
|
item.setAttribute("crop", "center");
|
||||||
menuPopup.appendChild(menuItem);
|
if (isSubview) {
|
||||||
|
item.setAttribute("tabindex", "0");
|
||||||
|
}
|
||||||
|
container.appendChild(item);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -76,7 +63,7 @@ var FeedHandler = {
|
|||||||
* Subscribe to a given feed. Called when
|
* Subscribe to a given feed. Called when
|
||||||
* 1. Page has a single feed and user clicks feed icon in location bar
|
* 1. Page has a single feed and user clicks feed icon in location bar
|
||||||
* 2. Page has a single feed and user selects Subscribe menu item
|
* 2. Page has a single feed and user selects Subscribe menu item
|
||||||
* 3. Page has multiple feeds and user selects from feed icon popup
|
* 3. Page has multiple feeds and user selects from feed icon popup (or subview)
|
||||||
* 4. Page has multiple feeds and user selects from Subscribe submenu
|
* 4. Page has multiple feeds and user selects from Subscribe submenu
|
||||||
* @param href
|
* @param href
|
||||||
* The feed to subscribe to. May be null, in which case the
|
* The feed to subscribe to. May be null, in which case the
|
||||||
|
@ -17,7 +17,7 @@ var FullScreen = {
|
|||||||
enterFS = !enterFS;
|
enterFS = !enterFS;
|
||||||
|
|
||||||
// Toggle the View:FullScreen command, which controls elements like the
|
// Toggle the View:FullScreen command, which controls elements like the
|
||||||
// fullscreen menuitem, menubars, and the appmenu.
|
// fullscreen menuitem, and menubars.
|
||||||
let fullscreenCommand = document.getElementById("View:FullScreen");
|
let fullscreenCommand = document.getElementById("View:FullScreen");
|
||||||
if (enterFS) {
|
if (enterFS) {
|
||||||
fullscreenCommand.setAttribute("checked", enterFS);
|
fullscreenCommand.setAttribute("checked", enterFS);
|
||||||
@ -517,15 +517,6 @@ var FullScreen = {
|
|||||||
// XXX don't interfere with previously collapsed toolbars
|
// XXX don't interfere with previously collapsed toolbars
|
||||||
if (el.getAttribute("fullscreentoolbar") == "true") {
|
if (el.getAttribute("fullscreentoolbar") == "true") {
|
||||||
if (!aShow) {
|
if (!aShow) {
|
||||||
|
|
||||||
var toolbarMode = el.getAttribute("mode");
|
|
||||||
if (toolbarMode != "text") {
|
|
||||||
el.setAttribute("saved-mode", toolbarMode);
|
|
||||||
el.setAttribute("saved-iconsize", el.getAttribute("iconsize"));
|
|
||||||
el.setAttribute("mode", "icons");
|
|
||||||
el.setAttribute("iconsize", "small");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give the main nav bar and the tab bar the fullscreen context menu,
|
// Give the main nav bar and the tab bar the fullscreen context menu,
|
||||||
// otherwise remove context menu to prevent breakage
|
// otherwise remove context menu to prevent breakage
|
||||||
el.setAttribute("saved-context", el.getAttribute("context"));
|
el.setAttribute("saved-context", el.getAttribute("context"));
|
||||||
@ -539,18 +530,10 @@ var FullScreen = {
|
|||||||
el.setAttribute("inFullscreen", true);
|
el.setAttribute("inFullscreen", true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var restoreAttr = function restoreAttr(attrName) {
|
if (el.hasAttribute("saved-context")) {
|
||||||
var savedAttr = "saved-" + attrName;
|
el.setAttribute("context", el.getAttribute("saved-context"));
|
||||||
if (el.hasAttribute(savedAttr)) {
|
el.removeAttribute("saved-context");
|
||||||
el.setAttribute(attrName, el.getAttribute(savedAttr));
|
|
||||||
el.removeAttribute(savedAttr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreAttr("mode");
|
|
||||||
restoreAttr("iconsize");
|
|
||||||
restoreAttr("context");
|
|
||||||
|
|
||||||
el.removeAttribute("inFullscreen");
|
el.removeAttribute("inFullscreen");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -571,11 +554,9 @@ var FullScreen = {
|
|||||||
document.documentElement.setAttribute("inFullscreen", true);
|
document.documentElement.setAttribute("inFullscreen", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In tabs-on-top mode, move window controls to the tab bar,
|
|
||||||
// and in tabs-on-bottom mode, move them back to the navigation toolbar.
|
|
||||||
var fullscreenctls = document.getElementById("window-controls");
|
var fullscreenctls = document.getElementById("window-controls");
|
||||||
var navbar = document.getElementById("nav-bar");
|
var navbar = document.getElementById("nav-bar");
|
||||||
var ctlsOnTabbar = window.toolbar.visible && (navbar.collapsed || TabsOnTop.enabled);
|
var ctlsOnTabbar = window.toolbar.visible;
|
||||||
if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
|
if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
|
||||||
fullscreenctls.removeAttribute("flex");
|
fullscreenctls.removeAttribute("flex");
|
||||||
document.getElementById("TabsToolbar").appendChild(fullscreenctls);
|
document.getElementById("TabsToolbar").appendChild(fullscreenctls);
|
||||||
|
@ -397,6 +397,7 @@ var FullZoom = {
|
|||||||
* @param browser The zoom of this browser will be saved. Required.
|
* @param browser The zoom of this browser will be saved. Required.
|
||||||
*/
|
*/
|
||||||
_applyZoomToPref: function FullZoom__applyZoomToPref(browser) {
|
_applyZoomToPref: function FullZoom__applyZoomToPref(browser) {
|
||||||
|
Services.obs.notifyObservers(null, "browser-fullZoom:zoomChange", "");
|
||||||
if (!this.siteSpecific ||
|
if (!this.siteSpecific ||
|
||||||
gInPrintPreviewMode ||
|
gInPrintPreviewMode ||
|
||||||
browser.contentDocument.mozSyntheticDocument)
|
browser.contentDocument.mozSyntheticDocument)
|
||||||
@ -417,6 +418,7 @@ var FullZoom = {
|
|||||||
* @param browser The zoom of this browser will be removed. Required.
|
* @param browser The zoom of this browser will be removed. Required.
|
||||||
*/
|
*/
|
||||||
_removePref: function FullZoom__removePref(browser) {
|
_removePref: function FullZoom__removePref(browser) {
|
||||||
|
Services.obs.notifyObservers(null, "browser-fullZoom:zoomReset", "");
|
||||||
if (browser.contentDocument.mozSyntheticDocument)
|
if (browser.contentDocument.mozSyntheticDocument)
|
||||||
return;
|
return;
|
||||||
let ctxt = this._loadContextFromWindow(browser.contentWindow);
|
let ctxt = this._loadContextFromWindow(browser.contentWindow);
|
||||||
|
@ -185,11 +185,6 @@
|
|||||||
accesskey="&viewToolbarsMenu.accesskey;">
|
accesskey="&viewToolbarsMenu.accesskey;">
|
||||||
<menupopup onpopupshowing="onViewToolbarsPopupShowing(event);">
|
<menupopup onpopupshowing="onViewToolbarsPopupShowing(event);">
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
<menuitem id="menu_tabsOnTop"
|
|
||||||
command="cmd_ToggleTabsOnTop"
|
|
||||||
type="checkbox"
|
|
||||||
label="&viewTabsOnTop.label;"
|
|
||||||
accesskey="&viewTabsOnTop.accesskey;"/>
|
|
||||||
<menuitem id="menu_customizeToolbars"
|
<menuitem id="menu_customizeToolbars"
|
||||||
label="&viewCustomizeToolbar.label;"
|
label="&viewCustomizeToolbar.label;"
|
||||||
accesskey="&viewCustomizeToolbar.accesskey;"
|
accesskey="&viewCustomizeToolbar.accesskey;"
|
||||||
|
@ -134,9 +134,6 @@ var StarUI = {
|
|||||||
document.loadOverlay(
|
document.loadOverlay(
|
||||||
"chrome://browser/content/places/editBookmarkOverlay.xul",
|
"chrome://browser/content/places/editBookmarkOverlay.xul",
|
||||||
(function (aSubject, aTopic, aData) {
|
(function (aSubject, aTopic, aData) {
|
||||||
//XXX We just caused localstore.rdf to be re-applied (bug 640158)
|
|
||||||
retrieveToolbarIconsizesFromTheme();
|
|
||||||
|
|
||||||
// Move the header (star, title, button) into the grid,
|
// Move the header (star, title, button) into the grid,
|
||||||
// so that it aligns nicely with the other items (bug 484022).
|
// so that it aligns nicely with the other items (bug 484022).
|
||||||
let header = this._element("editBookmarkPanelHeader");
|
let header = this._element("editBookmarkPanelHeader");
|
||||||
@ -745,9 +742,10 @@ var BookmarksEventHandler = {
|
|||||||
// Handles special drag and drop functionality for Places menus that are not
|
// Handles special drag and drop functionality for Places menus that are not
|
||||||
// part of a Places view (e.g. the bookmarks menu in the menubar).
|
// part of a Places view (e.g. the bookmarks menu in the menubar).
|
||||||
var PlacesMenuDNDHandler = {
|
var PlacesMenuDNDHandler = {
|
||||||
_springLoadDelay: 350, // milliseconds
|
_springLoadDelayMs: 350,
|
||||||
|
_closeDelayMs: 500,
|
||||||
_loadTimer: null,
|
_loadTimer: null,
|
||||||
_closerTimer: null,
|
_closeTimer: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the user enters the <menu> element during a drag.
|
* Called when the user enters the <menu> element during a drag.
|
||||||
@ -768,7 +766,7 @@ var PlacesMenuDNDHandler = {
|
|||||||
this._loadTimer = null;
|
this._loadTimer = null;
|
||||||
popup.setAttribute("autoopened", "true");
|
popup.setAttribute("autoopened", "true");
|
||||||
popup.showPopup(popup);
|
popup.showPopup(popup);
|
||||||
}, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
|
}, this._springLoadDelayMs, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
},
|
},
|
||||||
@ -781,7 +779,8 @@ var PlacesMenuDNDHandler = {
|
|||||||
onDragLeave: function PMDH_onDragLeave(event) {
|
onDragLeave: function PMDH_onDragLeave(event) {
|
||||||
// Handle menu-button separate targets.
|
// Handle menu-button separate targets.
|
||||||
if (event.relatedTarget === event.currentTarget ||
|
if (event.relatedTarget === event.currentTarget ||
|
||||||
event.relatedTarget.parentNode === event.currentTarget)
|
(event.relatedTarget &&
|
||||||
|
event.relatedTarget.parentNode === event.currentTarget))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Closing menus in a Places popup is handled by the view itself.
|
// Closing menus in a Places popup is handled by the view itself.
|
||||||
@ -807,7 +806,7 @@ var PlacesMenuDNDHandler = {
|
|||||||
popup.removeAttribute("autoopened");
|
popup.removeAttribute("autoopened");
|
||||||
popup.hidePopup();
|
popup.hidePopup();
|
||||||
}
|
}
|
||||||
}, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
|
}, this._closeDelayMs, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -875,30 +874,52 @@ let PlacesToolbarHelper = {
|
|||||||
if (!viewElt || viewElt._placesView)
|
if (!viewElt || viewElt._placesView)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If the bookmarks toolbar item is hidden because the parent toolbar is
|
// If the bookmarks toolbar item is:
|
||||||
// collapsed or hidden (i.e. in a popup), spare the initialization. Also,
|
// - not in a toolbar, or;
|
||||||
// there is no need to initialize the toolbar if customizing because
|
// - the toolbar is collapsed, or;
|
||||||
// init() will be called when the customization is done.
|
// - the toolbar is hidden some other way:
|
||||||
let toolbar = viewElt.parentNode.parentNode;
|
// don't initialize. Also, there is no need to initialize the toolbar if
|
||||||
if (toolbar.collapsed ||
|
// customizing, because that will happen when the customization is done.
|
||||||
getComputedStyle(toolbar, "").display == "none" ||
|
let toolbar = this._getParentToolbar(viewElt);
|
||||||
this._isCustomizing)
|
if (!toolbar || toolbar.collapsed || this._isCustomizing ||
|
||||||
|
getComputedStyle(toolbar, "").display == "none")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
new PlacesToolbar(this._place);
|
new PlacesToolbar(this._place);
|
||||||
},
|
},
|
||||||
|
|
||||||
customizeStart: function PTH_customizeStart() {
|
customizeStart: function PTH_customizeStart() {
|
||||||
let viewElt = this._viewElt;
|
try {
|
||||||
if (viewElt && viewElt._placesView)
|
let viewElt = this._viewElt;
|
||||||
viewElt._placesView.uninit();
|
if (viewElt && viewElt._placesView)
|
||||||
|
viewElt._placesView.uninit();
|
||||||
this._isCustomizing = true;
|
} finally {
|
||||||
|
this._isCustomizing = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
customizeDone: function PTH_customizeDone() {
|
customizeDone: function PTH_customizeDone() {
|
||||||
this._isCustomizing = false;
|
this._isCustomizing = false;
|
||||||
this.init();
|
this.init();
|
||||||
|
},
|
||||||
|
|
||||||
|
onPlaceholderCommand: function () {
|
||||||
|
let widgetGroup = CustomizableUI.getWidget("personal-bookmarks");
|
||||||
|
let widget = widgetGroup.forWindow(window);
|
||||||
|
if (widget.overflowed ||
|
||||||
|
widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
|
||||||
|
PlacesCommandHook.showPlacesOrganizer("BookmarksToolbar");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getParentToolbar: function(element) {
|
||||||
|
while (element) {
|
||||||
|
if (element.localName == "toolbar") {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
element = element.parentNode;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -906,31 +927,33 @@ let PlacesToolbarHelper = {
|
|||||||
//// BookmarkingUI
|
//// BookmarkingUI
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the bookmarks star button in the URL bar, as well as the bookmark
|
* Handles the bookmarks menu-button in the toolbar.
|
||||||
* menu button.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let BookmarkingUI = {
|
let BookmarkingUI = {
|
||||||
get button() {
|
get button() {
|
||||||
if (!this._button) {
|
let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
|
||||||
this._button = document.getElementById("bookmarks-menu-button");
|
if (widgetGroup.areaType == CustomizableUI.TYPE_TOOLBAR) {
|
||||||
|
return widgetGroup.forWindow(window).node;
|
||||||
}
|
}
|
||||||
return this._button;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
get star() {
|
get star() {
|
||||||
if (!this._star) {
|
let button = this.button;
|
||||||
this._star = document.getElementById("star-button");
|
return button && document.getAnonymousElementByAttribute(button, "anonid",
|
||||||
}
|
"button");
|
||||||
return this._star;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get anchor() {
|
get anchor() {
|
||||||
if (this.star && isElementVisible(this.star)) {
|
let widget = CustomizableUI.getWidget("bookmarks-menu-button")
|
||||||
// Anchor to the icon, so the panel looks more natural.
|
.forWindow(window);
|
||||||
return this.star;
|
if (widget.overflowed)
|
||||||
}
|
return widget.anchor;
|
||||||
return null;
|
|
||||||
|
let star = this.star;
|
||||||
|
return star && document.getAnonymousElementByAttribute(star, "class",
|
||||||
|
"toolbarbutton-icon");
|
||||||
},
|
},
|
||||||
|
|
||||||
STATUS_UPDATING: -1,
|
STATUS_UPDATING: -1,
|
||||||
@ -939,9 +962,9 @@ let BookmarkingUI = {
|
|||||||
get status() {
|
get status() {
|
||||||
if (this._pendingStmt)
|
if (this._pendingStmt)
|
||||||
return this.STATUS_UPDATING;
|
return this.STATUS_UPDATING;
|
||||||
return this.star &&
|
let button = this.button;
|
||||||
this.star.hasAttribute("starred") ? this.STATUS_STARRED
|
return button && button.hasAttribute("starred") ? this.STATUS_STARRED
|
||||||
: this.STATUS_UNSTARRED;
|
: this.STATUS_UNSTARRED;
|
||||||
},
|
},
|
||||||
|
|
||||||
get _starredTooltip()
|
get _starredTooltip()
|
||||||
@ -974,6 +997,16 @@ let BookmarkingUI = {
|
|||||||
if (event.target != event.currentTarget)
|
if (event.target != event.currentTarget)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
let widget = CustomizableUI.getWidget("bookmarks-menu-button")
|
||||||
|
.forWindow(window);
|
||||||
|
if (widget.overflowed) {
|
||||||
|
// Don't open a popup in the overflow popup, rather just open the Library.
|
||||||
|
event.preventDefault();
|
||||||
|
widget.node.removeAttribute("noautoclose");
|
||||||
|
PlacesCommandHook.showPlacesOrganizer("BookmarksMenu");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._popupNeedsUpdate)
|
if (!this._popupNeedsUpdate)
|
||||||
return;
|
return;
|
||||||
this._popupNeedsUpdate = false;
|
this._popupNeedsUpdate = false;
|
||||||
@ -990,14 +1023,6 @@ let BookmarkingUI = {
|
|||||||
let personalToolbar = document.getElementById("PersonalToolbar");
|
let personalToolbar = document.getElementById("PersonalToolbar");
|
||||||
viewToolbarMenuitem.setAttribute("checked", !personalToolbar.collapsed);
|
viewToolbarMenuitem.setAttribute("checked", !personalToolbar.collapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
let toolbarMenuitem = getPlacesAnonymousElement("toolbar-autohide");
|
|
||||||
if (toolbarMenuitem) {
|
|
||||||
// If bookmarks items are visible, hide Bookmarks Toolbar menu and the
|
|
||||||
// separator after it.
|
|
||||||
toolbarMenuitem.collapsed = toolbarMenuitem.nextSibling.collapsed =
|
|
||||||
isElementVisible(document.getElementById("personal-bookmarks"));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1010,29 +1035,31 @@ let BookmarkingUI = {
|
|||||||
|
|
||||||
if (aState == "invalid") {
|
if (aState == "invalid") {
|
||||||
this.star.setAttribute("disabled", "true");
|
this.star.setAttribute("disabled", "true");
|
||||||
this.star.removeAttribute("starred");
|
this.button.removeAttribute("starred");
|
||||||
|
this.button.setAttribute("buttontooltiptext", "");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.star.removeAttribute("disabled");
|
this.star.removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
|
this._updateToolbarStyle();
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateToolbarStyle: function BUI__updateToolbarStyle() {
|
_updateToolbarStyle: function BUI__updateToolbarStyle() {
|
||||||
if (!this.button) {
|
let button = this.button;
|
||||||
|
if (!button)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
let personalToolbar = document.getElementById("PersonalToolbar");
|
let personalToolbar = document.getElementById("PersonalToolbar");
|
||||||
let onPersonalToolbar = this.button.parentNode == personalToolbar ||
|
let onPersonalToolbar = button.parentNode == personalToolbar ||
|
||||||
this.button.parentNode.parentNode == personalToolbar;
|
button.parentNode.parentNode == personalToolbar;
|
||||||
|
|
||||||
if (onPersonalToolbar) {
|
if (onPersonalToolbar) {
|
||||||
this.button.classList.add("bookmark-item");
|
button.classList.add("bookmark-item");
|
||||||
this.button.classList.remove("toolbarbutton-1");
|
button.classList.remove("toolbarbutton-1");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.button.classList.remove("bookmark-item");
|
button.classList.remove("bookmark-item");
|
||||||
this.button.classList.add("toolbarbutton-1");
|
button.classList.add("toolbarbutton-1");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1040,9 +1067,9 @@ let BookmarkingUI = {
|
|||||||
// When an element with a placesView attached is removed and re-inserted,
|
// When an element with a placesView attached is removed and re-inserted,
|
||||||
// XBL reapplies the binding causing any kind of issues and possible leaks,
|
// XBL reapplies the binding causing any kind of issues and possible leaks,
|
||||||
// so kill current view and let popupshowing generate a new one.
|
// so kill current view and let popupshowing generate a new one.
|
||||||
if (this.button && this.button._placesView) {
|
let button = this.button;
|
||||||
this.button._placesView.uninit();
|
if (button && button._placesView)
|
||||||
}
|
button._placesView.uninit();
|
||||||
},
|
},
|
||||||
|
|
||||||
customizeStart: function BUI_customizeStart() {
|
customizeStart: function BUI_customizeStart() {
|
||||||
@ -1054,7 +1081,6 @@ let BookmarkingUI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
customizeDone: function BUI_customizeDone() {
|
customizeDone: function BUI_customizeDone() {
|
||||||
delete this._button;
|
|
||||||
this.onToolbarVisibilityChange();
|
this.onToolbarVisibilityChange();
|
||||||
this._updateToolbarStyle();
|
this._updateToolbarStyle();
|
||||||
},
|
},
|
||||||
@ -1074,7 +1100,7 @@ let BookmarkingUI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateStarState: function BUI_updateStarState() {
|
updateStarState: function BUI_updateStarState() {
|
||||||
if (!this.star || (this._uri && gBrowser.currentURI.equals(this._uri))) {
|
if (!this.button || (this._uri && gBrowser.currentURI.equals(this._uri))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1123,17 +1149,17 @@ let BookmarkingUI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateStar: function BUI__updateStar() {
|
_updateStar: function BUI__updateStar() {
|
||||||
if (!this.star) {
|
let button = this.button;
|
||||||
|
if (!button)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (this._itemIds.length > 0) {
|
if (this._itemIds.length > 0) {
|
||||||
this.star.setAttribute("starred", "true");
|
button.setAttribute("starred", "true");
|
||||||
this.star.setAttribute("tooltiptext", this._starredTooltip);
|
button.setAttribute("buttontooltiptext", this._starredTooltip);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.star.removeAttribute("starred");
|
button.removeAttribute("starred");
|
||||||
this.star.setAttribute("tooltiptext", this._unstarredTooltip);
|
button.setAttribute("buttontooltiptext", this._unstarredTooltip);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1141,15 +1167,71 @@ let BookmarkingUI = {
|
|||||||
if (aEvent.target != aEvent.currentTarget) {
|
if (aEvent.target != aEvent.currentTarget) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle special case when the button is in the panel.
|
||||||
|
let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
|
||||||
|
let widget = widgetGroup.forWindow(window);
|
||||||
|
if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
|
||||||
|
let view = document.getElementById("PanelUI-bookmarks");
|
||||||
|
view.addEventListener("ViewShowing", this.onPanelMenuViewShowing);
|
||||||
|
view.addEventListener("ViewHiding", this.onPanelMenuViewHiding);
|
||||||
|
widget.node.setAttribute("noautoclose", "true");
|
||||||
|
PanelUI.showSubView("PanelUI-bookmarks", widget.node,
|
||||||
|
CustomizableUI.AREA_PANEL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (widget.overflowed) {
|
||||||
|
// Allow to close the panel if the page is already bookmarked, cause
|
||||||
|
// we are going to open the edit bookmark panel.
|
||||||
|
if (this._itemIds.length > 0)
|
||||||
|
widget.node.removeAttribute("noautoclose");
|
||||||
|
else
|
||||||
|
widget.node.setAttribute("noautoclose", "true");
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore clicks on the star if we are updating its state.
|
// Ignore clicks on the star if we are updating its state.
|
||||||
if (!this._pendingStmt) {
|
if (!this._pendingStmt) {
|
||||||
PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
|
PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onPanelMenuViewShowing: function BUI_onViewShowing(aEvent) {
|
||||||
|
// Update checked status of the toolbar toggle.
|
||||||
|
let viewToolbar = document.getElementById("panelMenu_viewBookmarksToolbar");
|
||||||
|
let personalToolbar = document.getElementById("PersonalToolbar");
|
||||||
|
if (personalToolbar.collapsed)
|
||||||
|
viewToolbar.removeAttribute("checked");
|
||||||
|
else
|
||||||
|
viewToolbar.setAttribute("checked", "true");
|
||||||
|
// Setup the Places view.
|
||||||
|
this._panelMenuView = new PlacesPanelMenuView("place:folder=BOOKMARKS_MENU",
|
||||||
|
"panelMenu_bookmarksMenu",
|
||||||
|
"panelMenu_bookmarksMenu");
|
||||||
|
},
|
||||||
|
|
||||||
|
onPanelMenuViewHiding: function BUI_onViewHiding(aEvent) {
|
||||||
|
this._panelMenuView.uninit();
|
||||||
|
delete this._panelMenuView;
|
||||||
|
},
|
||||||
|
|
||||||
|
onPanelMenuViewCommand: function BUI_onPanelMenuViewCommand(aEvent, aView) {
|
||||||
|
let target = aEvent.originalTarget;
|
||||||
|
if (!target._placesNode)
|
||||||
|
return;
|
||||||
|
if (PlacesUtils.nodeIsContainer(target._placesNode))
|
||||||
|
PlacesCommandHook.showPlacesOrganizer([ "BookmarksMenu", target._placesNode.itemId ]);
|
||||||
|
else
|
||||||
|
PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent, aView);
|
||||||
|
PanelUI.hide();
|
||||||
|
},
|
||||||
|
|
||||||
// nsINavBookmarkObserver
|
// nsINavBookmarkObserver
|
||||||
onItemAdded: function BUI_onItemAdded(aItemId, aParentId, aIndex, aItemType,
|
onItemAdded: function BUI_onItemAdded(aItemId, aParentId, aIndex, aItemType,
|
||||||
aURI) {
|
aURI) {
|
||||||
|
if (!this.button) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (aURI && aURI.equals(this._uri)) {
|
if (aURI && aURI.equals(this._uri)) {
|
||||||
// If a new bookmark has been added to the tracked uri, register it.
|
// If a new bookmark has been added to the tracked uri, register it.
|
||||||
if (this._itemIds.indexOf(aItemId) == -1) {
|
if (this._itemIds.indexOf(aItemId) == -1) {
|
||||||
@ -1160,6 +1242,10 @@ let BookmarkingUI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onItemRemoved: function BUI_onItemRemoved(aItemId) {
|
onItemRemoved: function BUI_onItemRemoved(aItemId) {
|
||||||
|
if (!this.button) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let index = this._itemIds.indexOf(aItemId);
|
let index = this._itemIds.indexOf(aItemId);
|
||||||
// If one of the tracked bookmarks has been removed, unregister it.
|
// If one of the tracked bookmarks has been removed, unregister it.
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
@ -1170,6 +1256,10 @@ let BookmarkingUI = {
|
|||||||
|
|
||||||
onItemChanged: function BUI_onItemChanged(aItemId, aProperty,
|
onItemChanged: function BUI_onItemChanged(aItemId, aProperty,
|
||||||
aIsAnnotationProperty, aNewValue) {
|
aIsAnnotationProperty, aNewValue) {
|
||||||
|
if (!this.button) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (aProperty == "uri") {
|
if (aProperty == "uri") {
|
||||||
let index = this._itemIds.indexOf(aItemId);
|
let index = this._itemIds.indexOf(aItemId);
|
||||||
// If the changed bookmark was tracked, check if it is now pointing to
|
// If the changed bookmark was tracked, check if it is now pointing to
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
<command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
|
<command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
|
||||||
<command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
|
<command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
|
||||||
<command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
|
<command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
|
||||||
<command id="cmd_ToggleTabsOnTop" oncommand="TabsOnTop.toggle()"/>
|
|
||||||
<command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
|
<command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
|
||||||
<command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
|
<command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
|
||||||
|
|
||||||
@ -109,13 +108,13 @@
|
|||||||
oncommand="OpenBrowserWindow({private: true});"/>
|
oncommand="OpenBrowserWindow({private: true});"/>
|
||||||
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
|
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
|
||||||
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
|
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
|
||||||
<command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
|
|
||||||
<command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
|
<command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
|
||||||
<command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();" hidden="true"/>
|
<command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();" hidden="true"/>
|
||||||
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
|
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
|
||||||
<command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
|
<command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
|
||||||
<command id="Social:Toggle" oncommand="Social.toggle();" hidden="true"/>
|
<command id="Social:Toggle" oncommand="Social.toggle();" hidden="true"/>
|
||||||
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
|
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
|
||||||
|
<command id="MenuPanel:Toggle" oncommand="PanelUI.toggle(event);"/>
|
||||||
</commandset>
|
</commandset>
|
||||||
|
|
||||||
<commandset id="placesCommands">
|
<commandset id="placesCommands">
|
||||||
@ -412,6 +411,13 @@
|
|||||||
#endif
|
#endif
|
||||||
<key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>
|
<key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>
|
||||||
|
|
||||||
|
<key id="key_menuButton" command="MenuPanel:Toggle"
|
||||||
|
#ifdef XP_MACOSX
|
||||||
|
key="&toggleMenuPanelMac.key;" modifiers="accel,shift"/>
|
||||||
|
#else
|
||||||
|
key="&toggleMenuPanel.key;" modifiers="accel"/>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef XP_GNOME
|
#ifdef XP_GNOME
|
||||||
#define NUM_SELECT_TAB_MODIFIER alt
|
#define NUM_SELECT_TAB_MODIFIER alt
|
||||||
#else
|
#else
|
||||||
@ -428,8 +434,6 @@
|
|||||||
#expand <key id="key_selectTab8" oncommand="gBrowser.selectTabAtIndex(7, event);" key="8" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
|
#expand <key id="key_selectTab8" oncommand="gBrowser.selectTabAtIndex(7, event);" key="8" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
|
||||||
#expand <key id="key_selectLastTab" oncommand="gBrowser.selectTabAtIndex(-1, event);" key="9" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
|
#expand <key id="key_selectLastTab" oncommand="gBrowser.selectTabAtIndex(-1, event);" key="9" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
|
||||||
|
|
||||||
<key id="key_toggleAddonBar" command="Browser:ToggleAddonBar" key="&toggleAddonBarCmd.key;" modifiers="accel"/>
|
|
||||||
|
|
||||||
</keyset>
|
</keyset>
|
||||||
|
|
||||||
# Used by baseMenuOverlay
|
# Used by baseMenuOverlay
|
||||||
|
@ -795,9 +795,7 @@ SocialShare = {
|
|||||||
iframe.setAttribute("src", shareEndpoint);
|
iframe.setAttribute("src", shareEndpoint);
|
||||||
|
|
||||||
let navBar = document.getElementById("nav-bar");
|
let navBar = document.getElementById("nav-bar");
|
||||||
let anchor = navBar.getAttribute("mode") == "text" ?
|
let anchor = document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-icon");
|
||||||
document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-text") :
|
|
||||||
document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-icon");
|
|
||||||
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
||||||
Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
|
Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
|
||||||
}
|
}
|
||||||
@ -1130,9 +1128,7 @@ SocialToolbar = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let navBar = document.getElementById("nav-bar");
|
let navBar = document.getElementById("nav-bar");
|
||||||
let anchor = navBar.getAttribute("mode") == "text" ?
|
let anchor = document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-badge-container");
|
||||||
document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-text") :
|
|
||||||
document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-badge-container");
|
|
||||||
// Bug 849216 - open the popup in a setTimeout so we avoid the auto-rollup
|
// Bug 849216 - open the popup in a setTimeout so we avoid the auto-rollup
|
||||||
// handling from preventing it being opened in some cases.
|
// handling from preventing it being opened in some cases.
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
@ -426,16 +426,13 @@ let TabView = {
|
|||||||
|
|
||||||
let toolbar = document.getElementById("TabsToolbar");
|
let toolbar = document.getElementById("TabsToolbar");
|
||||||
let currentSet = toolbar.currentSet.split(",");
|
let currentSet = toolbar.currentSet.split(",");
|
||||||
|
|
||||||
let alltabsPos = currentSet.indexOf("alltabs-button");
|
let alltabsPos = currentSet.indexOf("alltabs-button");
|
||||||
if (-1 == alltabsPos)
|
if (-1 == alltabsPos)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
currentSet[alltabsPos] += "," + buttonId;
|
let allTabsBtn = document.getElementById("alltabs-button");
|
||||||
currentSet = currentSet.join(",");
|
let nextItem = allTabsBtn.nextSibling;
|
||||||
toolbar.currentSet = currentSet;
|
toolbar.insertItem(buttonId, nextItem);
|
||||||
toolbar.setAttribute("currentset", currentSet);
|
|
||||||
document.persist(toolbar.id, "currentset");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
|
@ -21,6 +21,63 @@ searchbar {
|
|||||||
-moz-binding: url("chrome://global/content/bindings/remote-browser.xml#remote-browser");
|
-moz-binding: url("chrome://global/content/bindings/remote-browser.xml#remote-browser");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toolbar[customizable="true"] {
|
||||||
|
-moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar");
|
||||||
|
}
|
||||||
|
|
||||||
|
%ifdef XP_MACOSX
|
||||||
|
#toolbar-menubar {
|
||||||
|
-moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-menubar-stub");
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbar[customizable="true"]:not([nowindowdrag="true"]) {
|
||||||
|
-moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-drag");
|
||||||
|
}
|
||||||
|
%endif
|
||||||
|
|
||||||
|
#toolbar-menubar[autohide="true"] {
|
||||||
|
-moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-menubar-autohide");
|
||||||
|
}
|
||||||
|
|
||||||
|
#addon-bar {
|
||||||
|
-moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#addonbar-delegating");
|
||||||
|
visibility: visible;
|
||||||
|
margin: 0;
|
||||||
|
height: 0 !important;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
border: 0 none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#addonbar-closebutton {
|
||||||
|
visibility: visible;
|
||||||
|
height: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status-bar {
|
||||||
|
height: 0 !important;
|
||||||
|
-moz-binding: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
panelmultiview {
|
||||||
|
-moz-binding: url("chrome://browser/content/customizableui/panelUI.xml#panelmultiview");
|
||||||
|
}
|
||||||
|
|
||||||
|
panelview {
|
||||||
|
-moz-binding: url("chrome://browser/content/customizableui/panelUI.xml#panelview");
|
||||||
|
-moz-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-mainview {
|
||||||
|
transition: transform 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
panelview:not([mainview]):not([current]) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
tabbrowser {
|
tabbrowser {
|
||||||
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
|
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
|
||||||
}
|
}
|
||||||
@ -47,21 +104,19 @@ tabbrowser {
|
|||||||
|
|
||||||
.tabbrowser-tab:not([pinned]) {
|
.tabbrowser-tab:not([pinned]) {
|
||||||
-moz-box-flex: 100;
|
-moz-box-flex: 100;
|
||||||
max-width: 250px;
|
max-width: 180px;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
width: 0;
|
width: 0;
|
||||||
transition: min-width 200ms ease-out,
|
transition: min-width 200ms ease-out,
|
||||||
max-width 250ms ease-out,
|
max-width 230ms ease-out;
|
||||||
opacity 50ms ease-out 20ms /* hide the tab for the first 20ms of the max-width transition */;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabbrowser-tab:not([pinned]):not([fadein]) {
|
.tabbrowser-tab:not([pinned]):not([fadein]) {
|
||||||
max-width: 0.1px;
|
max-width: 0.1px;
|
||||||
min-width: 0.1px;
|
min-width: 0.1px;
|
||||||
opacity: 0 !important;
|
visibility: hidden;
|
||||||
transition: min-width 200ms ease-out,
|
transition: min-width 200ms ease-out,
|
||||||
max-width 250ms ease-out,
|
max-width 230ms ease-out;
|
||||||
opacity 50ms ease-out 180ms /* hide the tab for the last 20ms of the max-width transition */;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-throbber:not([fadein]):not([pinned]),
|
.tab-throbber:not([fadein]):not([pinned]),
|
||||||
@ -94,20 +149,13 @@ toolbar[printpreview="true"] {
|
|||||||
-moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
|
-moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
|
||||||
}
|
}
|
||||||
|
|
||||||
#toolbar-menubar {
|
toolbar[overflowable] > .customization-target {
|
||||||
-moz-box-ordinal-group: 5;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
|
toolbar:not([overflowing]) > .overflow-button,
|
||||||
-moz-box-ordinal-group: 50;
|
toolbar[customizing] > .overflow-button {
|
||||||
}
|
display: none;
|
||||||
|
|
||||||
#TabsToolbar {
|
|
||||||
-moz-box-ordinal-group: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
#TabsToolbar[tabsontop="true"] {
|
|
||||||
-moz-box-ordinal-group: 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
%ifdef CAN_DRAW_IN_TITLEBAR
|
%ifdef CAN_DRAW_IN_TITLEBAR
|
||||||
@ -125,45 +173,71 @@ toolbar[printpreview="true"] {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-window[tabsintitlebar] #appmenu-button-container,
|
|
||||||
#main-window[tabsintitlebar] #titlebar-buttonbox {
|
#main-window[tabsintitlebar] #titlebar-buttonbox {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#titlebar-buttonbox {
|
||||||
|
-moz-appearance: -moz-window-button-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
%ifdef XP_MACOSX
|
||||||
|
#titlebar-fullscreen-button {
|
||||||
|
-moz-appearance: -moz-mac-fullscreen-button;
|
||||||
|
}
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
.bookmarks-toolbar-customize,
|
%ifdef XP_WIN
|
||||||
#wrapper-personal-bookmarks > #personal-bookmarks > #PlacesToolbar > hbox > #PlacesToolbarItems {
|
#main-window[sizemode="maximized"] #titlebar-buttonbox {
|
||||||
|
-moz-appearance: -moz-window-button-box-maximized;
|
||||||
|
}
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%endif
|
||||||
|
|
||||||
|
#bookmarks-toolbar-placeholder,
|
||||||
|
toolbarpaletteitem > #personal-bookmarks > #PlacesToolbar,
|
||||||
|
#personal-bookmarks[cui-areatype="menu-panel"] > #PlacesToolbar,
|
||||||
|
#personal-bookmarks[cui-areatype="toolbar"].overflowedItem > #PlacesToolbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#wrapper-personal-bookmarks[place="toolbar"] > #personal-bookmarks > #PlacesToolbar > .bookmarks-toolbar-customize {
|
toolbarpaletteitem > #personal-bookmarks > #bookmarks-toolbar-placeholder,
|
||||||
|
#personal-bookmarks[cui-areatype="menu-panel"] > #bookmarks-toolbar-placeholder,
|
||||||
|
#personal-bookmarks[cui-areatype="toolbar"].overflowedItem > #bookmarks-toolbar-placeholder {
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-window[disablechrome] #navigator-toolbox[tabsontop="true"] > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
|
#zoom-controls[cui-areatype="toolbar"]:not(.overflowedItem) > #zoom-reset-button > .toolbarbutton-text {
|
||||||
|
display: -moz-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper-urlbar-container > #urlbar-container > #urlbar-wrapper > #urlbar > toolbarbutton,
|
||||||
|
#urlbar-reload-button:not([displaystop]) + #urlbar-stop-button,
|
||||||
|
#urlbar-reload-button[displaystop] {
|
||||||
visibility: collapse;
|
visibility: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
#wrapper-urlbar-container #urlbar-container > #urlbar > toolbarbutton,
|
#PanelUI-feeds > .feed-toolbarbutton:-moz-locale-dir(rtl) {
|
||||||
#urlbar-container:not([combined]) > #urlbar > toolbarbutton,
|
|
||||||
#urlbar-container[combined] + #reload-button + #stop-button,
|
|
||||||
#urlbar-container[combined] + #reload-button,
|
|
||||||
toolbar:not([mode="icons"]) > #urlbar-container > #urlbar > toolbarbutton,
|
|
||||||
toolbar[mode="icons"] > #urlbar-container > #urlbar > #urlbar-reload-button:not([displaystop]) + #urlbar-stop-button,
|
|
||||||
toolbar[mode="icons"] > #urlbar-container > #urlbar > #urlbar-reload-button[displaystop],
|
|
||||||
toolbar[mode="icons"] > #reload-button:not([displaystop]) + #stop-button,
|
|
||||||
toolbar[mode="icons"] > #reload-button[displaystop] {
|
|
||||||
visibility: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feed-button > .toolbarbutton-menu-dropmarker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feed-menu > .feed-menuitem:-moz-locale-dir(rtl) {
|
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#panelMenu_bookmarksMenu {
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
#panelMenu_bookmarksMenu > .bookmark-item {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#urlbar-container {
|
||||||
|
min-width: 50ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-container {
|
||||||
|
min-width: 25ch;
|
||||||
|
}
|
||||||
|
|
||||||
#main-window:-moz-lwtheme {
|
#main-window:-moz-lwtheme {
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: top right;
|
background-position: top right;
|
||||||
@ -180,27 +254,6 @@ toolbar[mode="icons"] > #reload-button[displaystop] {
|
|||||||
background-position: bottom left;
|
background-position: bottom left;
|
||||||
}
|
}
|
||||||
|
|
||||||
splitmenu {
|
|
||||||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#splitmenu");
|
|
||||||
}
|
|
||||||
|
|
||||||
.splitmenu-menuitem {
|
|
||||||
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem");
|
|
||||||
list-style-image: inherit;
|
|
||||||
-moz-image-region: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.splitmenu-menuitem[iconic="true"] {
|
|
||||||
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
|
|
||||||
}
|
|
||||||
|
|
||||||
.splitmenu-menu > .menu-text,
|
|
||||||
:-moz-any(.splitmenu-menu, .splitmenu-menuitem) > .menu-accel-container,
|
|
||||||
#appmenu-editmenu > .menu-text,
|
|
||||||
#appmenu-editmenu > .menu-accel-container {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuitem-tooltip {
|
.menuitem-tooltip {
|
||||||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-tooltip");
|
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-tooltip");
|
||||||
}
|
}
|
||||||
@ -211,18 +264,6 @@ splitmenu {
|
|||||||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-iconic-tooltip");
|
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-iconic-tooltip");
|
||||||
}
|
}
|
||||||
|
|
||||||
%ifdef MENUBAR_CAN_AUTOHIDE
|
|
||||||
%ifndef CAN_DRAW_IN_TITLEBAR
|
|
||||||
#appmenu-toolbar-button > .toolbarbutton-text {
|
|
||||||
display: -moz-box;
|
|
||||||
}
|
|
||||||
%endif
|
|
||||||
|
|
||||||
#appmenu_offlineModeRecovery:not([checked=true]) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
%endif
|
|
||||||
|
|
||||||
/* Hide menu elements intended for keyboard access support */
|
/* Hide menu elements intended for keyboard access support */
|
||||||
#main-menubar[openedwithkey=false] .show-only-for-keyboard {
|
#main-menubar[openedwithkey=false] .show-only-for-keyboard {
|
||||||
display: none;
|
display: none;
|
||||||
@ -257,7 +298,7 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#wrapper-urlbar-container > #urlbar-container > #urlbar {
|
#wrapper-urlbar-container > #urlbar-container > #urlbar-wrapper > #urlbar {
|
||||||
-moz-user-input: disabled;
|
-moz-user-input: disabled;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
@ -270,9 +311,7 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a
|
|||||||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-rich-result-popup");
|
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-rich-result-popup");
|
||||||
}
|
}
|
||||||
|
|
||||||
#urlbar-container[combined] > #urlbar > #urlbar-icons > #go-button,
|
#urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon,
|
||||||
#urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon:not(#go-button),
|
|
||||||
#urlbar[pageproxystate="valid"] > #urlbar-icons > #go-button,
|
|
||||||
#urlbar[pageproxystate="invalid"][focused="true"] > #urlbar-go-button ~ toolbarbutton,
|
#urlbar[pageproxystate="invalid"][focused="true"] > #urlbar-go-button ~ toolbarbutton,
|
||||||
#urlbar[pageproxystate="valid"] > #urlbar-go-button,
|
#urlbar[pageproxystate="valid"] > #urlbar-go-button,
|
||||||
#urlbar:not([focused="true"]) > #urlbar-go-button {
|
#urlbar:not([focused="true"]) > #urlbar-go-button {
|
||||||
@ -316,18 +355,20 @@ toolbarbutton.bookmark-item {
|
|||||||
max-width: 13em;
|
max-width: 13em;
|
||||||
}
|
}
|
||||||
|
|
||||||
%ifdef MENUBAR_CAN_AUTOHIDE
|
|
||||||
#toolbar-menubar:not([autohide="true"]) ~ toolbar > #bookmarks-menu-button,
|
|
||||||
#toolbar-menubar:not([autohide="true"]) > #bookmarks-menu-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
%endif
|
|
||||||
|
|
||||||
#editBMPanel_tagsSelector {
|
#editBMPanel_tagsSelector {
|
||||||
/* override default listbox width from xul.css */
|
/* override default listbox width from xul.css */
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The star doesn't make sense as text */
|
||||||
|
toolbar[mode="text"] #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
|
||||||
|
display: -moz-box !important;
|
||||||
|
}
|
||||||
|
toolbar[mode="text"] #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-text,
|
||||||
|
toolbar[mode="full"] #bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button > .toolbarbutton-text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
|
menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -352,7 +393,6 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
|
|||||||
}
|
}
|
||||||
|
|
||||||
#navigator-toolbox ,
|
#navigator-toolbox ,
|
||||||
#status-bar ,
|
|
||||||
#mainPopupSet {
|
#mainPopupSet {
|
||||||
min-width: 1px;
|
min-width: 1px;
|
||||||
}
|
}
|
||||||
@ -451,14 +491,6 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
|
|||||||
min-width: 1px;
|
min-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav-bar[mode="text"] > #window-controls > toolbarbutton > .toolbarbutton-icon {
|
|
||||||
display: -moz-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nav-bar[mode="text"] > #window-controls > toolbarbutton > .toolbarbutton-text {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ::::: Ctrl-Tab Panel ::::: */
|
/* ::::: Ctrl-Tab Panel ::::: */
|
||||||
|
|
||||||
.ctrlTab-preview > html|img,
|
.ctrlTab-preview > html|img,
|
||||||
@ -523,17 +555,6 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
|
|||||||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#plugin-popupnotification-center-item");
|
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#plugin-popupnotification-center-item");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* override hidden="true" for the status bar compatibility shim
|
|
||||||
in case it was persisted for the real status bar */
|
|
||||||
#status-bar {
|
|
||||||
display: -moz-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the resizer from the statusbar compatibility shim */
|
|
||||||
#status-bar[hideresizer] > .statusbar-resizerpanel {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
browser[tabmodalPromptShowing] {
|
browser[tabmodalPromptShowing] {
|
||||||
-moz-user-focus: none !important;
|
-moz-user-focus: none !important;
|
||||||
}
|
}
|
||||||
@ -775,3 +796,14 @@ chatbox:-moz-full-screen-ancestor > .chat-titlebar {
|
|||||||
.contentSelectDropdown-ingroup {
|
.contentSelectDropdown-ingroup {
|
||||||
-moz-margin-start: 2em;
|
-moz-margin-start: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Give this menupopup an arrow panel styling */
|
||||||
|
#BMB_bookmarksPopup {
|
||||||
|
-moz-appearance: none;
|
||||||
|
-moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-arrow");
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
transition: opacity 300ms;
|
||||||
|
/* The popup inherits -moz-image-region from the button, must reset it */
|
||||||
|
-moz-image-region: auto;
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -9,5 +9,6 @@
|
|||||||
<script type="application/javascript" src="chrome://browser/content/browser.js"/>
|
<script type="application/javascript" src="chrome://browser/content/browser.js"/>
|
||||||
<script type="application/javascript" src="chrome://browser/content/downloads/downloads.js"/>
|
<script type="application/javascript" src="chrome://browser/content/downloads/downloads.js"/>
|
||||||
<script type="application/javascript" src="chrome://browser/content/downloads/indicator.js"/>
|
<script type="application/javascript" src="chrome://browser/content/downloads/indicator.js"/>
|
||||||
|
<script type="application/javascript" src="chrome://browser/content/customizableui/panelUI.js"/>
|
||||||
<script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
|
<script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
|
||||||
<script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
|
<script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
label="&newtab.undo.restoreButton;"
|
label="&newtab.undo.restoreButton;"
|
||||||
class="newtab-undo-button" />
|
class="newtab-undo-button" />
|
||||||
<xul:toolbarbutton id="newtab-undo-close-button" tabindex="-1"
|
<xul:toolbarbutton id="newtab-undo-close-button" tabindex="-1"
|
||||||
|
class="close-icon"
|
||||||
tooltiptext="&newtab.undo.closeTooltip;" />
|
tooltiptext="&newtab.undo.closeTooltip;" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,7 +83,7 @@
|
|||||||
<content>
|
<content>
|
||||||
<xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
|
<xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
|
||||||
<xul:toolbarbutton ondblclick="event.stopPropagation();"
|
<xul:toolbarbutton ondblclick="event.stopPropagation();"
|
||||||
class="messageCloseButton tabbable"
|
class="messageCloseButton close-icon tabbable"
|
||||||
xbl:inherits="hidden=hideclose"
|
xbl:inherits="hidden=hideclose"
|
||||||
tooltiptext="&closeNotification.tooltip;"
|
tooltiptext="&closeNotification.tooltip;"
|
||||||
oncommand="document.getBindingParent(this).close()"/>
|
oncommand="document.getBindingParent(this).close()"/>
|
||||||
|
11
browser/base/content/tab-shape.inc.svg
Normal file
11
browser/base/content/tab-shape.inc.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!-- 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/. -->
|
||||||
|
|
||||||
|
<svg:clipPath id="tab-curve-clip-path-start" clipPathUnits="objectBoundingBox">
|
||||||
|
<svg:path d="m 1,0.0625 0.05,0 0,0.938 -1,0 0,-0.028 C 0.32082458,0.95840561 0.4353096,0.81970962 0.48499998,0.5625 0.51819998,0.3905 0.535,0.0659 1,0.0625 z"/>
|
||||||
|
</svg:clipPath>
|
||||||
|
|
||||||
|
<svg:clipPath id="tab-curve-clip-path-end" clipPathUnits="objectBoundingBox">
|
||||||
|
<svg:path d="m 0,0.0625 -0.05,0 0,0.938 1,0 0,-0.028 C 0.67917542,0.95840561 0.56569036,0.81970962 0.51599998,0.5625 0.48279998,0.3905 0.465,0.0659 0,0.0625 z"/>
|
||||||
|
</svg:clipPath>
|
After Width: | Height: | Size: 731 B |
@ -41,6 +41,7 @@ tabpanels {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-icon-image:not([src]):not([pinned]),
|
||||||
.tab-throbber:not([busy]),
|
.tab-throbber:not([busy]),
|
||||||
.tab-throbber[busy] + .tab-icon-image {
|
.tab-throbber[busy] + .tab-icon-image {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -207,8 +207,7 @@
|
|||||||
if (!window.gShowPageResizers)
|
if (!window.gShowPageResizers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var show = document.getElementById("addon-bar").collapsed &&
|
var show = window.windowState == window.STATE_NORMAL;
|
||||||
window.windowState == window.STATE_NORMAL;
|
|
||||||
for (let i = 0; i < this.browsers.length; i++) {
|
for (let i = 0; i < this.browsers.length; i++) {
|
||||||
this.browsers[i].showWindowResizer = show;
|
this.browsers[i].showWindowResizer = show;
|
||||||
}
|
}
|
||||||
@ -999,7 +998,7 @@
|
|||||||
this.mCurrentTab = this.tabContainer.selectedItem;
|
this.mCurrentTab = this.tabContainer.selectedItem;
|
||||||
this.showTab(this.mCurrentTab);
|
this.showTab(this.mCurrentTab);
|
||||||
|
|
||||||
var backForwardContainer = document.getElementById("unified-back-forward-button");
|
var backForwardContainer = document.getElementById("urlbar-container");
|
||||||
if (backForwardContainer) {
|
if (backForwardContainer) {
|
||||||
backForwardContainer.setAttribute("switchingtabs", "true");
|
backForwardContainer.setAttribute("switchingtabs", "true");
|
||||||
window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
|
window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
|
||||||
@ -1495,8 +1494,7 @@
|
|||||||
if (remote)
|
if (remote)
|
||||||
b.setAttribute("remote", "true");
|
b.setAttribute("remote", "true");
|
||||||
|
|
||||||
if (window.gShowPageResizers && document.getElementById("addon-bar").collapsed &&
|
if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
|
||||||
window.windowState == window.STATE_NORMAL) {
|
|
||||||
b.setAttribute("showresizer", "true");
|
b.setAttribute("showresizer", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1920,6 +1918,11 @@
|
|||||||
aTab.closing = true;
|
aTab.closing = true;
|
||||||
this._removingTabs.push(aTab);
|
this._removingTabs.push(aTab);
|
||||||
this._visibleTabs = null; // invalidate cache
|
this._visibleTabs = null; // invalidate cache
|
||||||
|
|
||||||
|
// Invalidate hovered tab state tracking for this closing tab.
|
||||||
|
if (this.tabContainer._hoveredTab == aTab)
|
||||||
|
aTab._mouseleave();
|
||||||
|
|
||||||
if (newTab)
|
if (newTab)
|
||||||
this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
|
this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
|
||||||
else
|
else
|
||||||
@ -3238,6 +3241,28 @@
|
|||||||
return !tab.pinned && !tab.hidden;
|
return !tab.pinned && !tab.hidden;
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
<field name="_tabMarginLeft">null</field>
|
||||||
|
<field name="_tabMarginRight">null</field>
|
||||||
|
<method name="_adjustElementStartAndEnd">
|
||||||
|
<parameter name="aTab"/>
|
||||||
|
<parameter name="tabStart"/>
|
||||||
|
<parameter name="tabEnd"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
if (this._tabMarginLeft === null || this._tabMarginRight === null) {
|
||||||
|
let tabMiddle = document.getAnonymousElementByAttribute(aTab, "class", "tab-background-middle");
|
||||||
|
let tabMiddleStyle = window.getComputedStyle(tabMiddle, null);
|
||||||
|
this._tabMarginLeft = parseFloat(tabMiddleStyle.marginLeft);
|
||||||
|
this._tabMarginRight = parseFloat(tabMiddleStyle.marginRight);
|
||||||
|
}
|
||||||
|
if (this._tabMarginLeft < 0) {
|
||||||
|
tabStart = tabStart + this._tabMarginLeft;
|
||||||
|
}
|
||||||
|
if (this._tabMarginRight < 0) {
|
||||||
|
tabEnd = tabEnd - this._tabMarginRight;
|
||||||
|
}
|
||||||
|
return [tabStart, tabEnd];
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
</implementation>
|
</implementation>
|
||||||
|
|
||||||
<handlers>
|
<handlers>
|
||||||
@ -3259,8 +3284,11 @@
|
|||||||
<handler event="overflow"><![CDATA[
|
<handler event="overflow"><![CDATA[
|
||||||
if (event.detail == 0)
|
if (event.detail == 0)
|
||||||
return; // Ignore vertical events
|
return; // Ignore vertical events
|
||||||
|
|
||||||
var tabs = document.getBindingParent(this);
|
var tabs = document.getBindingParent(this);
|
||||||
|
var numberOfTabs = tabs.tabbrowser.visibleTabs.length;
|
||||||
|
if (numberOfTabs == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
tabs.setAttribute("overflow", "true");
|
tabs.setAttribute("overflow", "true");
|
||||||
tabs._positionPinnedTabs();
|
tabs._positionPinnedTabs();
|
||||||
tabs._handleTabSelect(false);
|
tabs._handleTabSelect(false);
|
||||||
@ -3357,6 +3385,15 @@
|
|||||||
<field name="_afterSelectedTab">null</field>
|
<field name="_afterSelectedTab">null</field>
|
||||||
<field name="_beforeHoveredTab">null</field>
|
<field name="_beforeHoveredTab">null</field>
|
||||||
<field name="_afterHoveredTab">null</field>
|
<field name="_afterHoveredTab">null</field>
|
||||||
|
<field name="_hoveredTab">null</field>
|
||||||
|
|
||||||
|
<property name="_isCustomizing" readonly="true">
|
||||||
|
<getter>
|
||||||
|
let root = document.documentElement;
|
||||||
|
return root.getAttribute("customizing") == "true" ||
|
||||||
|
root.getAttribute("customize-exiting") == "true";
|
||||||
|
</getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
<method name="_setPositionalAttributes">
|
<method name="_setPositionalAttributes">
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
@ -3387,6 +3424,12 @@
|
|||||||
this._lastTab.removeAttribute("last-visible-tab");
|
this._lastTab.removeAttribute("last-visible-tab");
|
||||||
this._lastTab = visibleTabs[lastVisible];
|
this._lastTab = visibleTabs[lastVisible];
|
||||||
this._lastTab.setAttribute("last-visible-tab", "true");
|
this._lastTab.setAttribute("last-visible-tab", "true");
|
||||||
|
|
||||||
|
let hoveredTab = this._hoveredTab;
|
||||||
|
if (hoveredTab) {
|
||||||
|
hoveredTab._mouseleave();
|
||||||
|
hoveredTab._mouseenter();
|
||||||
|
}
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
@ -3465,10 +3508,6 @@
|
|||||||
document.getElementById("menu_close").setAttribute("label",
|
document.getElementById("menu_close").setAttribute("label",
|
||||||
this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close"));
|
this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close"));
|
||||||
|
|
||||||
goSetCommandEnabled("cmd_ToggleTabsOnTop", visible);
|
|
||||||
|
|
||||||
TabsOnTop.syncUI();
|
|
||||||
|
|
||||||
TabsInTitlebar.allowedBy("tabs-visible", visible);
|
TabsInTitlebar.allowedBy("tabs-visible", visible);
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
@ -3820,16 +3859,16 @@
|
|||||||
if (aEvent.target != window)
|
if (aEvent.target != window)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
let sizemode = document.documentElement.getAttribute("sizemode");
|
TabsInTitlebar.updateAppearance();
|
||||||
TabsInTitlebar.allowedBy("sizemode",
|
|
||||||
sizemode == "maximized" || sizemode == "fullscreen");
|
|
||||||
|
|
||||||
var width = this.mTabstrip.boxObject.width;
|
if (this.tabbrowser.visibleTabs.length > 1) {
|
||||||
if (width != this.mTabstripWidth) {
|
var width = this.mTabstrip.boxObject.width;
|
||||||
this.adjustTabstrip();
|
if (width != this.mTabstripWidth) {
|
||||||
this._fillTrailingGap();
|
this.adjustTabstrip();
|
||||||
this._handleTabSelect();
|
this._fillTrailingGap();
|
||||||
this.mTabstripWidth = width;
|
this._handleTabSelect();
|
||||||
|
this.mTabstripWidth = width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tabbrowser.updateWindowResizers();
|
this.tabbrowser.updateWindowResizers();
|
||||||
@ -4123,8 +4162,7 @@
|
|||||||
// When the tabbar has an unified appearance with the titlebar
|
// When the tabbar has an unified appearance with the titlebar
|
||||||
// and menubar, a double-click in it should have the same behavior
|
// and menubar, a double-click in it should have the same behavior
|
||||||
// as double-clicking the titlebar
|
// as double-clicking the titlebar
|
||||||
if (TabsInTitlebar.enabled ||
|
if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
|
||||||
(TabsOnTop.enabled && this.parentNode._dragBindingAlive))
|
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -4247,7 +4285,7 @@
|
|||||||
|
|
||||||
<handler event="dragstart"><![CDATA[
|
<handler event="dragstart"><![CDATA[
|
||||||
var tab = this._getDragTargetTab(event);
|
var tab = this._getDragTargetTab(event);
|
||||||
if (!tab)
|
if (!tab || this._isCustomizing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let dt = event.dataTransfer;
|
let dt = event.dataTransfer;
|
||||||
@ -4491,7 +4529,7 @@
|
|||||||
|
|
||||||
var dt = event.dataTransfer;
|
var dt = event.dataTransfer;
|
||||||
var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||||
if (dt.mozUserCancelled || dt.dropEffect != "none") {
|
if (dt.mozUserCancelled || dt.dropEffect != "none" || this._isCustomizing) {
|
||||||
delete draggedTab._dragData;
|
delete draggedTab._dragData;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4623,7 +4661,7 @@
|
|||||||
role="presentation"/>
|
role="presentation"/>
|
||||||
<xul:toolbarbutton anonid="close-button"
|
<xul:toolbarbutton anonid="close-button"
|
||||||
xbl:inherits="fadein,pinned,selected"
|
xbl:inherits="fadein,pinned,selected"
|
||||||
class="tab-close-button"/>
|
class="tab-close-button close-icon"/>
|
||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
</xul:stack>
|
</xul:stack>
|
||||||
</content>
|
</content>
|
||||||
@ -4644,6 +4682,54 @@
|
|||||||
<field name="mCorrespondingMenuitem">null</field>
|
<field name="mCorrespondingMenuitem">null</field>
|
||||||
<field name="closing">false</field>
|
<field name="closing">false</field>
|
||||||
<field name="lastAccessed">0</field>
|
<field name="lastAccessed">0</field>
|
||||||
|
|
||||||
|
<method name="_mouseenter">
|
||||||
|
<body><![CDATA[
|
||||||
|
if (this.closing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let tabContainer = this.parentNode;
|
||||||
|
let visibleTabs = tabContainer.tabbrowser.visibleTabs;
|
||||||
|
let tabIndex = visibleTabs.indexOf(this);
|
||||||
|
if (tabIndex == 0) {
|
||||||
|
tabContainer._beforeHoveredTab = null;
|
||||||
|
} else {
|
||||||
|
let candidate = visibleTabs[tabIndex - 1];
|
||||||
|
if (!candidate.selected) {
|
||||||
|
tabContainer._beforeHoveredTab = candidate;
|
||||||
|
candidate.setAttribute("beforehovered", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabIndex == visibleTabs.length - 1) {
|
||||||
|
tabContainer._afterHoveredTab = null;
|
||||||
|
} else {
|
||||||
|
let candidate = visibleTabs[tabIndex + 1];
|
||||||
|
if (!candidate.selected) {
|
||||||
|
tabContainer._afterHoveredTab = candidate;
|
||||||
|
candidate.setAttribute("afterhovered", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tabContainer._hoveredTab = this;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_mouseleave">
|
||||||
|
<body><![CDATA[
|
||||||
|
let tabContainer = this.parentNode;
|
||||||
|
if (tabContainer._beforeHoveredTab) {
|
||||||
|
tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
|
||||||
|
tabContainer._beforeHoveredTab = null;
|
||||||
|
}
|
||||||
|
if (tabContainer._afterHoveredTab) {
|
||||||
|
tabContainer._afterHoveredTab.removeAttribute("afterhovered");
|
||||||
|
tabContainer._afterHoveredTab = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabContainer._hoveredTab = null;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
</implementation>
|
</implementation>
|
||||||
|
|
||||||
<handlers>
|
<handlers>
|
||||||
@ -4652,47 +4738,14 @@
|
|||||||
if (anonid == "close-button")
|
if (anonid == "close-button")
|
||||||
this.mOverCloseButton = true;
|
this.mOverCloseButton = true;
|
||||||
|
|
||||||
let tab = event.target;
|
this._mouseenter();
|
||||||
if (tab.closing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let tabContainer = this.parentNode;
|
|
||||||
let visibleTabs = tabContainer.tabbrowser.visibleTabs;
|
|
||||||
let tabIndex = visibleTabs.indexOf(tab);
|
|
||||||
if (tabIndex == 0) {
|
|
||||||
tabContainer._beforeHoveredTab = null;
|
|
||||||
} else {
|
|
||||||
let candidate = visibleTabs[tabIndex - 1];
|
|
||||||
if (!candidate.selected) {
|
|
||||||
tabContainer._beforeHoveredTab = candidate;
|
|
||||||
candidate.setAttribute("beforehovered", "true");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tabIndex == visibleTabs.length - 1) {
|
|
||||||
tabContainer._afterHoveredTab = null;
|
|
||||||
} else {
|
|
||||||
let candidate = visibleTabs[tabIndex + 1];
|
|
||||||
if (!candidate.selected) {
|
|
||||||
tabContainer._afterHoveredTab = candidate;
|
|
||||||
candidate.setAttribute("afterhovered", "true");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]]></handler>
|
]]></handler>
|
||||||
<handler event="mouseout"><![CDATA[
|
<handler event="mouseout"><![CDATA[
|
||||||
let anonid = event.originalTarget.getAttribute("anonid");
|
let anonid = event.originalTarget.getAttribute("anonid");
|
||||||
if (anonid == "close-button")
|
if (anonid == "close-button")
|
||||||
this.mOverCloseButton = false;
|
this.mOverCloseButton = false;
|
||||||
|
|
||||||
let tabContainer = this.parentNode;
|
this._mouseleave();
|
||||||
if (tabContainer._beforeHoveredTab) {
|
|
||||||
tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
|
|
||||||
tabContainer._beforeHoveredTab = null;
|
|
||||||
}
|
|
||||||
if (tabContainer._afterHoveredTab) {
|
|
||||||
tabContainer._afterHoveredTab.removeAttribute("afterhovered");
|
|
||||||
tabContainer._afterHoveredTab = null;
|
|
||||||
}
|
|
||||||
]]></handler>
|
]]></handler>
|
||||||
<handler event="dragstart" phase="capturing">
|
<handler event="dragstart" phase="capturing">
|
||||||
this.style.MozUserFocus = '';
|
this.style.MozUserFocus = '';
|
||||||
|
@ -17,7 +17,6 @@ support-files =
|
|||||||
bug792517.html
|
bug792517.html
|
||||||
bug792517.sjs
|
bug792517.sjs
|
||||||
bug839103.css
|
bug839103.css
|
||||||
disablechrome.html
|
|
||||||
discovery.html
|
discovery.html
|
||||||
domplate_test.js
|
domplate_test.js
|
||||||
download_page.html
|
download_page.html
|
||||||
@ -99,9 +98,6 @@ run-if = crashreporter
|
|||||||
[browser_aboutHome.js]
|
[browser_aboutHome.js]
|
||||||
[browser_aboutSyncProgress.js]
|
[browser_aboutSyncProgress.js]
|
||||||
[browser_addKeywordSearch.js]
|
[browser_addKeywordSearch.js]
|
||||||
[browser_addon_bar_aomlistener.js]
|
|
||||||
[browser_addon_bar_close_button.js]
|
|
||||||
[browser_addon_bar_shortcut.js]
|
|
||||||
[browser_alltabslistener.js]
|
[browser_alltabslistener.js]
|
||||||
[browser_blob-channelname.js]
|
[browser_blob-channelname.js]
|
||||||
[browser_bookmark_titles.js]
|
[browser_bookmark_titles.js]
|
||||||
@ -182,10 +178,7 @@ run-if = toolkit == "cocoa"
|
|||||||
[browser_bug595507.js]
|
[browser_bug595507.js]
|
||||||
[browser_bug596687.js]
|
[browser_bug596687.js]
|
||||||
[browser_bug597218.js]
|
[browser_bug597218.js]
|
||||||
[browser_bug598923.js]
|
|
||||||
[browser_bug599325.js]
|
|
||||||
[browser_bug609700.js]
|
[browser_bug609700.js]
|
||||||
[browser_bug616836.js]
|
|
||||||
[browser_bug623155.js]
|
[browser_bug623155.js]
|
||||||
[browser_bug623893.js]
|
[browser_bug623893.js]
|
||||||
[browser_bug624734.js]
|
[browser_bug624734.js]
|
||||||
@ -231,11 +224,9 @@ run-if = toolkit == "cocoa"
|
|||||||
[browser_contentAreaClick.js]
|
[browser_contentAreaClick.js]
|
||||||
[browser_contextSearchTabPosition.js]
|
[browser_contextSearchTabPosition.js]
|
||||||
[browser_ctrlTab.js]
|
[browser_ctrlTab.js]
|
||||||
[browser_customize.js]
|
|
||||||
[browser_customize_popupNotification.js]
|
[browser_customize_popupNotification.js]
|
||||||
[browser_datareporting_notification.js]
|
[browser_datareporting_notification.js]
|
||||||
run-if = datareporting
|
run-if = datareporting
|
||||||
[browser_disablechrome.js]
|
|
||||||
[browser_discovery.js]
|
[browser_discovery.js]
|
||||||
[browser_duplicateIDs.js]
|
[browser_duplicateIDs.js]
|
||||||
[browser_drag.js]
|
[browser_drag.js]
|
||||||
@ -318,6 +309,7 @@ skip-if = true # disabled until the tree view is added
|
|||||||
[browser_visibleTabs_bookmarkAllTabs.js]
|
[browser_visibleTabs_bookmarkAllTabs.js]
|
||||||
[browser_visibleTabs_contextMenu.js]
|
[browser_visibleTabs_contextMenu.js]
|
||||||
[browser_visibleTabs_tabPreview.js]
|
[browser_visibleTabs_tabPreview.js]
|
||||||
|
[browser_windowopen_reflows.js]
|
||||||
[browser_wyciwyg_urlbarCopying.js]
|
[browser_wyciwyg_urlbarCopying.js]
|
||||||
[browser_zbug569342.js]
|
[browser_zbug569342.js]
|
||||||
[browser_registerProtocolHandler_notification.js]
|
[browser_registerProtocolHandler_notification.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/. */
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
waitForExplicitFinish();
|
|
||||||
|
|
||||||
let addonbar = document.getElementById("addon-bar");
|
|
||||||
ok(addonbar.collapsed, "addon bar is collapsed by default");
|
|
||||||
|
|
||||||
let topMenu, toolbarMenu;
|
|
||||||
|
|
||||||
function onTopMenuShown(event) {
|
|
||||||
ok(1, "top menu popupshown listener called");
|
|
||||||
event.currentTarget.removeEventListener("popupshown", arguments.callee, false);
|
|
||||||
// open the customize or toolbars menu
|
|
||||||
toolbarMenu = document.getElementById("appmenu_customizeMenu") ||
|
|
||||||
document.getElementById("viewToolbarsMenu").firstElementChild;
|
|
||||||
toolbarMenu.addEventListener("popupshown", onToolbarMenuShown, false);
|
|
||||||
toolbarMenu.addEventListener("popuphidden", onToolbarMenuHidden, false);
|
|
||||||
toolbarMenu.openPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTopMenuHidden(event) {
|
|
||||||
ok(1, "top menu popuphidden listener called");
|
|
||||||
event.currentTarget.removeEventListener("popuphidden", arguments.callee, false);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onToolbarMenuShown(event) {
|
|
||||||
ok(1, "sub menu popupshown listener called");
|
|
||||||
event.currentTarget.removeEventListener("popupshown", arguments.callee, false);
|
|
||||||
|
|
||||||
// test the menu item's default state
|
|
||||||
let menuitem = document.getElementById("toggle_addon-bar");
|
|
||||||
ok(menuitem, "found the menu item");
|
|
||||||
is(menuitem.getAttribute("checked"), "false", "menuitem is not checked by default");
|
|
||||||
|
|
||||||
// click on the menu item
|
|
||||||
// TODO: there's got to be a way to check+command in one shot
|
|
||||||
menuitem.setAttribute("checked", "true");
|
|
||||||
menuitem.click();
|
|
||||||
|
|
||||||
// now the addon bar should be visible and the menu checked
|
|
||||||
is(addonbar.getAttribute("collapsed"), "false", "addon bar is visible after executing the command");
|
|
||||||
is(menuitem.getAttribute("checked"), "true", "menuitem is checked after executing the command");
|
|
||||||
|
|
||||||
toolbarMenu.hidePopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onToolbarMenuHidden(event) {
|
|
||||||
ok(1, "toolbar menu popuphidden listener called");
|
|
||||||
event.currentTarget.removeEventListener("popuphidden", arguments.callee, false);
|
|
||||||
topMenu.hidePopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
// open the appmenu or view menu
|
|
||||||
topMenu = document.getElementById("appmenu-popup") ||
|
|
||||||
document.getElementById("menu_viewPopup");
|
|
||||||
topMenu.addEventListener("popupshown", onTopMenuShown, false);
|
|
||||||
topMenu.addEventListener("popuphidden", onTopMenuHidden, false);
|
|
||||||
topMenu.openPopup();
|
|
||||||
}
|
|
@ -1,67 +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/. */
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
|
|
||||||
let addonbar = document.getElementById("addon-bar");
|
|
||||||
ok(addonbar.collapsed, "addon bar is collapsed by default");
|
|
||||||
|
|
||||||
function addItem(id) {
|
|
||||||
let button = document.createElement("toolbarbutton");
|
|
||||||
button.id = id;
|
|
||||||
let palette = document.getElementById("navigator-toolbox").palette;
|
|
||||||
palette.appendChild(button);
|
|
||||||
addonbar.insertItem(id, null, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// call onInstalling
|
|
||||||
AddonsMgrListener.onInstalling();
|
|
||||||
|
|
||||||
// add item to the bar
|
|
||||||
let id = "testbutton";
|
|
||||||
addItem(id);
|
|
||||||
|
|
||||||
// call onInstalled
|
|
||||||
AddonsMgrListener.onInstalled();
|
|
||||||
|
|
||||||
// confirm bar is visible
|
|
||||||
ok(!addonbar.collapsed, "addon bar is not collapsed after toggle");
|
|
||||||
|
|
||||||
// call onUninstalling
|
|
||||||
AddonsMgrListener.onUninstalling();
|
|
||||||
|
|
||||||
// remove item from the bar
|
|
||||||
addonbar.currentSet = addonbar.currentSet.replace("," + id, "");
|
|
||||||
|
|
||||||
// call onUninstalled
|
|
||||||
AddonsMgrListener.onUninstalled();
|
|
||||||
|
|
||||||
// confirm bar is not visible
|
|
||||||
ok(addonbar.collapsed, "addon bar is collapsed after toggle");
|
|
||||||
|
|
||||||
// call onEnabling
|
|
||||||
AddonsMgrListener.onEnabling();
|
|
||||||
|
|
||||||
// add item to the bar
|
|
||||||
let id = "testbutton";
|
|
||||||
addItem(id);
|
|
||||||
|
|
||||||
// call onEnabled
|
|
||||||
AddonsMgrListener.onEnabled();
|
|
||||||
|
|
||||||
// confirm bar is visible
|
|
||||||
ok(!addonbar.collapsed, "addon bar is not collapsed after toggle");
|
|
||||||
|
|
||||||
// call onDisabling
|
|
||||||
AddonsMgrListener.onDisabling();
|
|
||||||
|
|
||||||
// remove item from the bar
|
|
||||||
addonbar.currentSet = addonbar.currentSet.replace("," + id, "");
|
|
||||||
|
|
||||||
// call onDisabled
|
|
||||||
AddonsMgrListener.onDisabled();
|
|
||||||
|
|
||||||
// confirm bar is not visible
|
|
||||||
ok(addonbar.collapsed, "addon bar is collapsed after toggle");
|
|
||||||
}
|
|
@ -1,19 +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/. */
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
let addonbar = document.getElementById("addon-bar");
|
|
||||||
ok(addonbar.collapsed, "addon bar is collapsed by default");
|
|
||||||
|
|
||||||
// make add-on bar visible
|
|
||||||
setToolbarVisibility(addonbar, true);
|
|
||||||
ok(!addonbar.collapsed, "addon bar is not collapsed after toggle");
|
|
||||||
|
|
||||||
// click the close button
|
|
||||||
let closeButton = document.getElementById("addonbar-closebutton");
|
|
||||||
EventUtils.synthesizeMouseAtCenter(closeButton, {});
|
|
||||||
|
|
||||||
// confirm addon bar is closed
|
|
||||||
ok(addonbar.collapsed, "addon bar is collapsed after clicking close button");
|
|
||||||
}
|
|
@ -1,18 +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/. */
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
let addonbar = document.getElementById("addon-bar");
|
|
||||||
ok(addonbar.collapsed, "addon bar is collapsed by default");
|
|
||||||
|
|
||||||
// show the add-on bar
|
|
||||||
EventUtils.synthesizeKey("/", { accelKey: true }, window);
|
|
||||||
ok(!addonbar.collapsed, "addon bar is not collapsed after toggle");
|
|
||||||
|
|
||||||
// hide the add-on bar
|
|
||||||
EventUtils.synthesizeKey("/", { accelKey: true }, window);
|
|
||||||
|
|
||||||
// confirm addon bar is closed
|
|
||||||
ok(addonbar.collapsed, "addon bar is collapsed after toggle");
|
|
||||||
}
|
|
@ -34,16 +34,9 @@ function step3()
|
|||||||
is(gBrowser.selectedTab, tab1, "2nd click on selected tab1 keeps tab selected");
|
is(gBrowser.selectedTab, tab1, "2nd click on selected tab1 keeps tab selected");
|
||||||
isnot(document.activeElement, tab1, "2nd click on selected tab1 does not activate tab");
|
isnot(document.activeElement, tab1, "2nd click on selected tab1 does not activate tab");
|
||||||
|
|
||||||
if (gNavToolbox.getAttribute("tabsontop") == "true") {
|
ok(true, "focusing URLBar then sending 1 Shift+Tab.");
|
||||||
ok(true, "[tabsontop=true] focusing URLBar then sending 1 Shift+Tab.");
|
gURLBar.focus();
|
||||||
gURLBar.focus();
|
EventUtils.synthesizeKey("VK_TAB", {shiftKey: true});
|
||||||
EventUtils.synthesizeKey("VK_TAB", {shiftKey: true});
|
|
||||||
} else {
|
|
||||||
ok(true, "[tabsontop=false] focusing SearchBar then sending Tab(s) until out of nav-bar.");
|
|
||||||
document.getElementById("searchbar").focus();
|
|
||||||
while (focus_in_navbar())
|
|
||||||
EventUtils.synthesizeKey("VK_TAB", { });
|
|
||||||
}
|
|
||||||
is(gBrowser.selectedTab, tab1, "tab key to selected tab1 keeps tab selected");
|
is(gBrowser.selectedTab, tab1, "tab key to selected tab1 keeps tab selected");
|
||||||
is(document.activeElement, tab1, "tab key to selected tab1 activates tab");
|
is(document.activeElement, tab1, "tab key to selected tab1 activates tab");
|
||||||
|
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Test:
|
|
||||||
// * if add-on is installed to the add-on bar, the bar is made visible.
|
|
||||||
// * if add-on is uninstalled from the add-on bar, and no more add-ons there,
|
|
||||||
// the bar is hidden.
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
let aml = AddonsMgrListener;
|
|
||||||
ok(aml, "AddonsMgrListener exists");
|
|
||||||
// check is hidden
|
|
||||||
is(aml.addonBar.collapsed, true, "add-on bar is hidden initially");
|
|
||||||
// aob gets the count
|
|
||||||
AddonsMgrListener.onInstalling();
|
|
||||||
// add an item
|
|
||||||
let element = document.createElement("toolbaritem");
|
|
||||||
element.id = "bug598923-addon-item";
|
|
||||||
aml.addonBar.appendChild(element);
|
|
||||||
// aob checks the count, makes visible
|
|
||||||
AddonsMgrListener.onInstalled();
|
|
||||||
// check is visible
|
|
||||||
is(aml.addonBar.collapsed, false, "add-on bar has been made visible");
|
|
||||||
// aob gets the count
|
|
||||||
AddonsMgrListener.onUninstalling();
|
|
||||||
// remove an item
|
|
||||||
aml.addonBar.removeChild(element);
|
|
||||||
// aob checks the count, makes hidden
|
|
||||||
AddonsMgrListener.onUninstalled();
|
|
||||||
// check is hidden
|
|
||||||
is(aml.addonBar.collapsed, true, "add-on bar is hidden again");
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
function test() {
|
|
||||||
waitForExplicitFinish();
|
|
||||||
|
|
||||||
let addonBar = document.getElementById("addon-bar");
|
|
||||||
ok(addonBar, "got addon bar");
|
|
||||||
ok(!isElementVisible(addonBar), "addon bar initially hidden");
|
|
||||||
|
|
||||||
openToolbarCustomizationUI(function () {
|
|
||||||
ok(isElementVisible(addonBar),
|
|
||||||
"add-on bar is visible during toolbar customization");
|
|
||||||
|
|
||||||
closeToolbarCustomizationUI(onClose);
|
|
||||||
});
|
|
||||||
|
|
||||||
function onClose() {
|
|
||||||
ok(!isElementVisible(addonBar),
|
|
||||||
"addon bar is hidden after toolbar customization");
|
|
||||||
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
function test() {
|
|
||||||
is(document.querySelectorAll("#appmenu-popup [accesskey]").length, 0,
|
|
||||||
"there should be no items with access keys in the app menu popup");
|
|
||||||
}
|
|
@ -4,6 +4,15 @@
|
|||||||
|
|
||||||
// Bug 624734 - Star UI has no tooltip until bookmarked page is visited
|
// Bug 624734 - Star UI has no tooltip until bookmarked page is visited
|
||||||
|
|
||||||
|
function finishTest() {
|
||||||
|
is(BookmarkingUI.button.getAttribute("buttontooltiptext"),
|
||||||
|
BookmarkingUI._unstarredTooltip,
|
||||||
|
"Star icon should have the unstarred tooltip text");
|
||||||
|
|
||||||
|
gBrowser.removeCurrentTab();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
|
|
||||||
@ -11,12 +20,11 @@ function test() {
|
|||||||
tab.linkedBrowser.addEventListener("load", (function(event) {
|
tab.linkedBrowser.addEventListener("load", (function(event) {
|
||||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||||
|
|
||||||
is(BookmarkingUI.star.getAttribute("tooltiptext"),
|
if (BookmarkingUI.status == BookmarkingUI.STATUS_UPDATING) {
|
||||||
BookmarkingUI._unstarredTooltip,
|
waitForCondition(function() BookmarkingUI.status != BookmarkingUI.STATUS_UPDATING, finishTest, "BookmarkingUI was updating for too long");
|
||||||
"Star icon should have the unstarred tooltip text");
|
} else {
|
||||||
|
finishTest();
|
||||||
gBrowser.removeCurrentTab();
|
}
|
||||||
finish();
|
|
||||||
}), true);
|
}), true);
|
||||||
|
|
||||||
tab.linkedBrowser.loadURI("http://example.com/browser/browser/base/content/test/general/dummy_page.html");
|
tab.linkedBrowser.loadURI("http://example.com/browser/browser/base/content/test/general/dummy_page.html");
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
function test() {
|
|
||||||
waitForExplicitFinish();
|
|
||||||
|
|
||||||
openToolbarCustomizationUI(customizationWindowLoaded);
|
|
||||||
}
|
|
||||||
|
|
||||||
function customizationWindowLoaded(win) {
|
|
||||||
let x = win.screenX;
|
|
||||||
let iconModeList = win.document.getElementById("modelist");
|
|
||||||
|
|
||||||
iconModeList.addEventListener("popupshown", function popupshown() {
|
|
||||||
iconModeList.removeEventListener("popupshown", popupshown, false);
|
|
||||||
|
|
||||||
executeSoon(function () {
|
|
||||||
is(win.screenX, x,
|
|
||||||
"toolbar customization window shouldn't move when the iconmode menulist is opened");
|
|
||||||
iconModeList.open = false;
|
|
||||||
|
|
||||||
closeToolbarCustomizationUI(finish);
|
|
||||||
});
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
iconModeList.open = true;
|
|
||||||
}
|
|
@ -1,216 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Tests that the disablechrome attribute gets propogated to the main UI
|
|
||||||
|
|
||||||
const HTTPSRC = "http://example.com/browser/browser/base/content/test/general/";
|
|
||||||
|
|
||||||
function is_element_hidden(aElement) {
|
|
||||||
var style = window.getComputedStyle(document.getElementById("nav-bar"), "");
|
|
||||||
if (style.visibility != "visible" || style.display == "none")
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (aElement.ownerDocument != aElement.parentNode)
|
|
||||||
return is_element_hidden(aElement.parentNode);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_chrome_hidden() {
|
|
||||||
is(document.documentElement.getAttribute("disablechrome"), "true", "Attribute should be set");
|
|
||||||
if (TabsOnTop.enabled)
|
|
||||||
ok(is_element_hidden(document.getElementById("nav-bar")), "Toolbar should be hidden");
|
|
||||||
else
|
|
||||||
ok(!is_element_hidden(document.getElementById("nav-bar")), "Toolbar should not be hidden");
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_chrome_visible() {
|
|
||||||
isnot(document.getElementById("main-window").getAttribute("disablechrome"), "true", "Attribute should not be set");
|
|
||||||
ok(!is_element_hidden(document.getElementById("nav-bar")), "Toolbar should not be hidden");
|
|
||||||
}
|
|
||||||
|
|
||||||
function load_page(aURL, aCanHide, aCallback) {
|
|
||||||
gNewBrowser.addEventListener("pageshow", function() {
|
|
||||||
// Filter out about:blank loads
|
|
||||||
if (gNewBrowser.currentURI.spec != aURL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
gNewBrowser.removeEventListener("pageshow", arguments.callee, false);
|
|
||||||
|
|
||||||
if (aCanHide)
|
|
||||||
is_chrome_hidden();
|
|
||||||
else
|
|
||||||
is_chrome_visible();
|
|
||||||
|
|
||||||
if (aURL == "about:addons") {
|
|
||||||
function check_after_init() {
|
|
||||||
if (aCanHide)
|
|
||||||
is_chrome_hidden();
|
|
||||||
else
|
|
||||||
is_chrome_visible();
|
|
||||||
|
|
||||||
aCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gNewBrowser.contentWindow.gIsInitializing) {
|
|
||||||
gNewBrowser.contentDocument.addEventListener("Initialized", function() {
|
|
||||||
gNewBrowser.contentDocument.removeEventListener("Initialized", arguments.callee, false);
|
|
||||||
|
|
||||||
check_after_init();
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
check_after_init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
executeSoon(aCallback);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
gNewBrowser.loadURI(aURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
var gOldTab;
|
|
||||||
var gNewTab;
|
|
||||||
var gNewBrowser;
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
// Opening the add-ons manager and waiting for it to load the discovery pane
|
|
||||||
// takes more time in windows debug builds
|
|
||||||
requestLongerTimeout(2);
|
|
||||||
|
|
||||||
var gOldTabsOnTop = TabsOnTop.enabled;
|
|
||||||
registerCleanupFunction(function() {
|
|
||||||
TabsOnTop.enabled = gOldTabsOnTop;
|
|
||||||
});
|
|
||||||
|
|
||||||
waitForExplicitFinish();
|
|
||||||
|
|
||||||
gOldTab = gBrowser.selectedTab;
|
|
||||||
gNewTab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
|
||||||
gNewBrowser = gBrowser.selectedBrowser;
|
|
||||||
|
|
||||||
info("Tabs on top");
|
|
||||||
TabsOnTop.enabled = true;
|
|
||||||
|
|
||||||
run_http_test_1();
|
|
||||||
}
|
|
||||||
|
|
||||||
function end_test() {
|
|
||||||
gBrowser.removeTab(gNewTab);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_url(aURL, aCanHide, aNextTest) {
|
|
||||||
is_chrome_visible();
|
|
||||||
info("Page load");
|
|
||||||
load_page(aURL, aCanHide, function() {
|
|
||||||
info("Switch away");
|
|
||||||
gBrowser.selectedTab = gOldTab;
|
|
||||||
is_chrome_visible();
|
|
||||||
|
|
||||||
info("Switch back");
|
|
||||||
gBrowser.selectedTab = gNewTab;
|
|
||||||
if (aCanHide)
|
|
||||||
is_chrome_hidden();
|
|
||||||
else
|
|
||||||
is_chrome_visible();
|
|
||||||
|
|
||||||
gBrowser.removeTab(gNewTab);
|
|
||||||
gNewTab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
|
||||||
gNewBrowser = gBrowser.selectedBrowser;
|
|
||||||
|
|
||||||
gBrowser.selectedTab = gOldTab;
|
|
||||||
|
|
||||||
info("Background load");
|
|
||||||
load_page(aURL, false, function() {
|
|
||||||
info("Switch back");
|
|
||||||
gBrowser.selectedTab = gNewTab;
|
|
||||||
if (aCanHide)
|
|
||||||
is_chrome_hidden();
|
|
||||||
else
|
|
||||||
is_chrome_visible();
|
|
||||||
|
|
||||||
load_page("about:blank", false, aNextTest);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should never hide the chrome
|
|
||||||
function run_http_test_1() {
|
|
||||||
info("HTTP tests");
|
|
||||||
test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should hide the chrome
|
|
||||||
function run_chrome_about_test() {
|
|
||||||
info("Chrome about: tests");
|
|
||||||
test_url("about:addons", true, function() {
|
|
||||||
info("Tabs on bottom");
|
|
||||||
TabsOnTop.enabled = false;
|
|
||||||
run_http_test_2();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should never hide the chrome
|
|
||||||
function run_http_test_2() {
|
|
||||||
info("HTTP tests");
|
|
||||||
test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should not hide the chrome
|
|
||||||
function run_chrome_about_test_2() {
|
|
||||||
info("Chrome about: tests");
|
|
||||||
test_url("about:addons", true, run_http_test3);
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_http_test3() {
|
|
||||||
info("HTTP tests");
|
|
||||||
test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should not hide the chrome
|
|
||||||
function run_chrome_about_test_3() {
|
|
||||||
info("Chrome about: tests");
|
|
||||||
test_url("about:Addons", true, function(){
|
|
||||||
info("Tabs on top");
|
|
||||||
TabsOnTop.enabled = true;
|
|
||||||
run_http_test4();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_http_test4() {
|
|
||||||
info("HTTP tests");
|
|
||||||
test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_4);
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_chrome_about_test_4() {
|
|
||||||
info("Chrome about: tests");
|
|
||||||
test_url("about:Addons", true, run_http_test5);
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_http_test5() {
|
|
||||||
info("HTTP tests");
|
|
||||||
test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_5);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should hide the chrome
|
|
||||||
function run_chrome_about_test_5() {
|
|
||||||
info("Chrome about: tests");
|
|
||||||
test_url("about:preferences", true, function(){
|
|
||||||
info("Tabs on bottom");
|
|
||||||
TabsOnTop.enabled = false;
|
|
||||||
run_http_test6();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_http_test6() {
|
|
||||||
info("HTTP tests");
|
|
||||||
test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_6);
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_chrome_about_test_6() {
|
|
||||||
info("Chrome about: tests");
|
|
||||||
test_url("about:preferences", true, end_test);
|
|
||||||
}
|
|
@ -7,8 +7,8 @@ function rect(ele) ele.getBoundingClientRect();
|
|||||||
function width(ele) rect(ele).width;
|
function width(ele) rect(ele).width;
|
||||||
function left(ele) rect(ele).left;
|
function left(ele) rect(ele).left;
|
||||||
function right(ele) rect(ele).right;
|
function right(ele) rect(ele).right;
|
||||||
function isLeft(ele, msg) is(left(ele), left(scrollbox), msg);
|
function isLeft(ele, msg) is(left(ele) + tabstrip._tabMarginLeft, left(scrollbox), msg);
|
||||||
function isRight(ele, msg) is(right(ele), right(scrollbox), msg);
|
function isRight(ele, msg) is(right(ele) - tabstrip._tabMarginRight, right(scrollbox), msg);
|
||||||
function elementFromPoint(x) tabstrip._elementFromPoint(x);
|
function elementFromPoint(x) tabstrip._elementFromPoint(x);
|
||||||
function nextLeftElement() elementFromPoint(left(scrollbox) - 1);
|
function nextLeftElement() elementFromPoint(left(scrollbox) - 1);
|
||||||
function nextRightElement() elementFromPoint(right(scrollbox) + 1);
|
function nextRightElement() elementFromPoint(right(scrollbox) + 1);
|
||||||
@ -62,7 +62,11 @@ function runOverflowTests(aEvent) {
|
|||||||
EventUtils.synthesizeMouse(upButton, 1, 1, {});
|
EventUtils.synthesizeMouse(upButton, 1, 1, {});
|
||||||
isLeft(element, "Scrolled one tab to the left with a single click");
|
isLeft(element, "Scrolled one tab to the left with a single click");
|
||||||
|
|
||||||
element = elementFromPoint(left(scrollbox) - width(scrollbox));
|
let elementPoint = left(scrollbox) - width(scrollbox);
|
||||||
|
element = elementFromPoint(elementPoint);
|
||||||
|
if (elementPoint == right(element)) {
|
||||||
|
element = element.nextSibling;
|
||||||
|
}
|
||||||
EventUtils.synthesizeMouse(upButton, 1, 1, {clickCount: 2});
|
EventUtils.synthesizeMouse(upButton, 1, 1, {clickCount: 2});
|
||||||
isLeft(element, "Scrolled one page of tabs with a double click");
|
isLeft(element, "Scrolled one page of tabs with a double click");
|
||||||
|
|
||||||
|
115
browser/base/content/test/general/browser_windowopen_reflows.js
Normal file
115
browser/base/content/test/general/browser_windowopen_reflows.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const EXPECTED_REFLOWS = [
|
||||||
|
// handleEvent flushes layout to get the tabstrip width after a resize.
|
||||||
|
"handleEvent@chrome://browser/content/tabbrowser.xml|",
|
||||||
|
|
||||||
|
// Loading a tab causes a reflow.
|
||||||
|
"loadTabs@chrome://browser/content/tabbrowser.xml|" +
|
||||||
|
"loadOneOrMoreURIs@chrome://browser/content/browser.js|" +
|
||||||
|
"gBrowserInit._delayedStartup@chrome://browser/content/browser.js|",
|
||||||
|
|
||||||
|
// Selecting the address bar causes a reflow.
|
||||||
|
"select@chrome://global/content/bindings/textbox.xml|" +
|
||||||
|
"focusAndSelectUrlBar@chrome://browser/content/browser.js|" +
|
||||||
|
"gBrowserInit._delayedStartup@chrome://browser/content/browser.js|",
|
||||||
|
|
||||||
|
// Focusing the content area causes a reflow.
|
||||||
|
"gBrowserInit._delayedStartup@chrome://browser/content/browser.js|",
|
||||||
|
|
||||||
|
// Sometimes sessionstore collects data during this test, which causes a sync reflow
|
||||||
|
// (https://bugzilla.mozilla.org/show_bug.cgi?id=892154 will fix this)
|
||||||
|
"ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm",
|
||||||
|
];
|
||||||
|
|
||||||
|
if (Services.appinfo.OS == "Darwin") {
|
||||||
|
// TabsInTitlebar._update causes a reflow on OS X trying to do calculations
|
||||||
|
// since layout info is already dirty. This doesn't seem to happen before
|
||||||
|
// MozAfterPaint on other platforms.
|
||||||
|
EXPECTED_REFLOWS.push("rect@chrome://browser/content/browser.js|" +
|
||||||
|
"TabsInTitlebar._update@chrome://browser/content/browser.js|" +
|
||||||
|
"updateAppearance@chrome://browser/content/browser.js|" +
|
||||||
|
"handleEvent@chrome://browser/content/tabbrowser.xml|");
|
||||||
|
|
||||||
|
// _onOverflow causes a reflow getting widths.
|
||||||
|
EXPECTED_REFLOWS.push("OverflowableToolbar.prototype._onOverflow@resource:///modules/CustomizableUI.jsm|" +
|
||||||
|
"OverflowableToolbar.prototype.init@resource:///modules/CustomizableUI.jsm|" +
|
||||||
|
"OverflowableToolbar.prototype.observe@resource:///modules/CustomizableUI.jsm|" +
|
||||||
|
"gBrowserInit._delayedStartup@chrome://browser/content/browser.js|");
|
||||||
|
// Same as above since in packaged builds there are no function names and the resource URI includes "app"
|
||||||
|
EXPECTED_REFLOWS.push("@resource://app/modules/CustomizableUI.jsm|" +
|
||||||
|
"@resource://app/modules/CustomizableUI.jsm|" +
|
||||||
|
"@resource://app/modules/CustomizableUI.jsm|" +
|
||||||
|
"gBrowserInit._delayedStartup@chrome://browser/content/browser.js|");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test ensures that there are no unexpected
|
||||||
|
* uninterruptible reflows when opening new windows.
|
||||||
|
*/
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
// Add a reflow observer and open a new window
|
||||||
|
let win = OpenBrowserWindow();
|
||||||
|
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIWebNavigation)
|
||||||
|
.QueryInterface(Ci.nsIDocShell);
|
||||||
|
docShell.addWeakReflowObserver(observer);
|
||||||
|
|
||||||
|
// Wait until the mozafterpaint event occurs.
|
||||||
|
waitForMozAfterPaint(win, function paintListener() {
|
||||||
|
// Remove reflow observer and clean up.
|
||||||
|
docShell.removeWeakReflowObserver(observer);
|
||||||
|
win.close();
|
||||||
|
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let observer = {
|
||||||
|
reflow: function (start, end) {
|
||||||
|
// Gather information about the current code path.
|
||||||
|
let stack = new Error().stack;
|
||||||
|
let path = stack.split("\n").slice(1).map(line => {
|
||||||
|
return line.replace(/:\d+$/, "");
|
||||||
|
}).join("|");
|
||||||
|
let pathWithLineNumbers = (new Error().stack).split("\n").slice(1).join("|");
|
||||||
|
|
||||||
|
// Stack trace is empty. Reflow was triggered by native code.
|
||||||
|
if (path === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is an expected reflow.
|
||||||
|
for (let expectedStack of EXPECTED_REFLOWS) {
|
||||||
|
if (path.startsWith(expectedStack) ||
|
||||||
|
// Accept an empty function name for gBrowserInit._delayedStartup or TabsInTitlebar._update to workaround bug 906578.
|
||||||
|
path.startsWith(expectedStack.replace(/(^|\|)(gBrowserInit\._delayedStartup|TabsInTitlebar\._update)@/, "$1@"))) {
|
||||||
|
ok(true, "expected uninterruptible reflow '" + expectedStack + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(false, "unexpected uninterruptible reflow '" + pathWithLineNumbers + "'");
|
||||||
|
},
|
||||||
|
|
||||||
|
reflowInterruptible: function (start, end) {
|
||||||
|
// We're not interested in interruptible reflows.
|
||||||
|
},
|
||||||
|
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
|
||||||
|
Ci.nsISupportsWeakReference])
|
||||||
|
};
|
||||||
|
|
||||||
|
function waitForMozAfterPaint(win, callback) {
|
||||||
|
win.addEventListener("MozAfterPaint", function onEnd(event) {
|
||||||
|
if (event.target != win)
|
||||||
|
return;
|
||||||
|
win.removeEventListener("MozAfterPaint", onEnd);
|
||||||
|
executeSoon(callback);
|
||||||
|
});
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
<html>
|
|
||||||
<body>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -37,49 +37,27 @@ function updateTabContextMenu(tab) {
|
|||||||
menu.hidePopup();
|
menu.hidePopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
function findToolbarCustomizationWindow(aBrowserWin) {
|
|
||||||
if (!aBrowserWin)
|
|
||||||
aBrowserWin = window;
|
|
||||||
|
|
||||||
let iframe = aBrowserWin.document.getElementById("customizeToolbarSheetIFrame");
|
|
||||||
let win = iframe && iframe.contentWindow;
|
|
||||||
if (win)
|
|
||||||
return win;
|
|
||||||
|
|
||||||
win = findChromeWindowByURI("chrome://global/content/customizeToolbar.xul");
|
|
||||||
if (win && win.opener == aBrowserWin)
|
|
||||||
return win;
|
|
||||||
|
|
||||||
throw Error("Failed to find the customization window");
|
|
||||||
}
|
|
||||||
|
|
||||||
function openToolbarCustomizationUI(aCallback, aBrowserWin) {
|
function openToolbarCustomizationUI(aCallback, aBrowserWin) {
|
||||||
if (!aBrowserWin)
|
if (!aBrowserWin)
|
||||||
aBrowserWin = window;
|
aBrowserWin = window;
|
||||||
|
|
||||||
aBrowserWin.document.getElementById("cmd_CustomizeToolbars").doCommand();
|
aBrowserWin.gCustomizeMode.enter();
|
||||||
|
|
||||||
aBrowserWin.gNavToolbox.addEventListener("beforecustomization", function UI_loaded() {
|
aBrowserWin.gNavToolbox.addEventListener("customizationready", function UI_loaded() {
|
||||||
aBrowserWin.gNavToolbox.removeEventListener("beforecustomization", UI_loaded);
|
aBrowserWin.gNavToolbox.removeEventListener("customizationready", UI_loaded);
|
||||||
|
executeSoon(function() {
|
||||||
let win = findToolbarCustomizationWindow(aBrowserWin);
|
aCallback(aBrowserWin)
|
||||||
waitForFocus(function () {
|
});
|
||||||
aCallback(win);
|
|
||||||
}, win);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeToolbarCustomizationUI(aCallback, aBrowserWin) {
|
function closeToolbarCustomizationUI(aCallback, aBrowserWin) {
|
||||||
let win = findToolbarCustomizationWindow(aBrowserWin);
|
aBrowserWin.gNavToolbox.addEventListener("aftercustomization", function unloaded() {
|
||||||
|
aBrowserWin.gNavToolbox.removeEventListener("aftercustomization", unloaded);
|
||||||
win.addEventListener("unload", function unloaded() {
|
|
||||||
win.removeEventListener("unload", unloaded);
|
|
||||||
executeSoon(aCallback);
|
executeSoon(aCallback);
|
||||||
});
|
});
|
||||||
|
|
||||||
let button = win.document.getElementById("donebutton");
|
aBrowserWin.gCustomizeMode.exit();
|
||||||
button.focus();
|
|
||||||
button.doCommand();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForCondition(condition, nextTest, errorMsg) {
|
function waitForCondition(condition, nextTest, errorMsg) {
|
||||||
|
@ -937,7 +937,7 @@
|
|||||||
<xul:menupopup anonid="menupopup"
|
<xul:menupopup anonid="menupopup"
|
||||||
xbl:inherits="oncommand=menucommand">
|
xbl:inherits="oncommand=menucommand">
|
||||||
<children/>
|
<children/>
|
||||||
<xul:menuitem class="menuitem-iconic popup-notification-closeitem"
|
<xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
|
||||||
label="&closeNotificationItem.label;"
|
label="&closeNotificationItem.label;"
|
||||||
xbl:inherits="oncommand=closeitemcommand"/>
|
xbl:inherits="oncommand=closeitemcommand"/>
|
||||||
</xul:menupopup>
|
</xul:menupopup>
|
||||||
@ -946,7 +946,7 @@
|
|||||||
</xul:vbox>
|
</xul:vbox>
|
||||||
<xul:vbox pack="start">
|
<xul:vbox pack="start">
|
||||||
<xul:toolbarbutton anonid="closebutton"
|
<xul:toolbarbutton anonid="closebutton"
|
||||||
class="messageCloseButton popup-notification-closebutton tabbable"
|
class="messageCloseButton close-icon popup-notification-closebutton tabbable"
|
||||||
xbl:inherits="oncommand=closebuttoncommand"
|
xbl:inherits="oncommand=closebuttoncommand"
|
||||||
tooltiptext="&closeNotification.tooltip;"/>
|
tooltiptext="&closeNotification.tooltip;"/>
|
||||||
</xul:vbox>
|
</xul:vbox>
|
||||||
@ -1169,7 +1169,7 @@
|
|||||||
<xul:menupopup anonid="menupopup"
|
<xul:menupopup anonid="menupopup"
|
||||||
xbl:inherits="oncommand=menucommand">
|
xbl:inherits="oncommand=menucommand">
|
||||||
<children/>
|
<children/>
|
||||||
<xul:menuitem class="menuitem-iconic popup-notification-closeitem"
|
<xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
|
||||||
label="&closeNotificationItem.label;"
|
label="&closeNotificationItem.label;"
|
||||||
xbl:inherits="oncommand=closeitemcommand"/>
|
xbl:inherits="oncommand=closeitemcommand"/>
|
||||||
</xul:menupopup>
|
</xul:menupopup>
|
||||||
@ -1178,7 +1178,7 @@
|
|||||||
</xul:vbox>
|
</xul:vbox>
|
||||||
<xul:vbox pack="start">
|
<xul:vbox pack="start">
|
||||||
<xul:toolbarbutton anonid="closebutton"
|
<xul:toolbarbutton anonid="closebutton"
|
||||||
class="messageCloseButton popup-notification-closebutton tabbable"
|
class="messageCloseButton close-icon popup-notification-closebutton tabbable"
|
||||||
xbl:inherits="oncommand=closebuttoncommand"
|
xbl:inherits="oncommand=closebuttoncommand"
|
||||||
tooltiptext="&closeNotification.tooltip;"/>
|
tooltiptext="&closeNotification.tooltip;"/>
|
||||||
</xul:vbox>
|
</xul:vbox>
|
||||||
@ -1508,7 +1508,7 @@
|
|||||||
<xul:label class="text-link click-to-play-plugins-notification-link" anonid="click-to-play-plugins-notification-link" />
|
<xul:label class="text-link click-to-play-plugins-notification-link" anonid="click-to-play-plugins-notification-link" />
|
||||||
</xul:description>
|
</xul:description>
|
||||||
<xul:toolbarbutton anonid="closebutton"
|
<xul:toolbarbutton anonid="closebutton"
|
||||||
class="messageCloseButton popup-notification-closebutton tabbable"
|
class="messageCloseButton popup-notification-closebutton tabbable close-icon"
|
||||||
xbl:inherits="oncommand=closebuttoncommand"
|
xbl:inherits="oncommand=closebuttoncommand"
|
||||||
tooltiptext="&closeNotification.tooltip;"/>
|
tooltiptext="&closeNotification.tooltip;"/>
|
||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
@ -2027,7 +2027,7 @@
|
|||||||
onclick="document.getBindingParent(this).onLinkClick();"/>
|
onclick="document.getBindingParent(this).onLinkClick();"/>
|
||||||
</xul:description>
|
</xul:description>
|
||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
<xul:toolbarbutton class="panel-promo-closebutton"
|
<xul:toolbarbutton class="panel-promo-closebutton close-icon"
|
||||||
oncommand="document.getBindingParent(this).onCloseButtonCommand();"
|
oncommand="document.getBindingParent(this).onCloseButtonCommand();"
|
||||||
tooltiptext="&closeNotification.tooltip;"/>
|
tooltiptext="&closeNotification.tooltip;"/>
|
||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
|
@ -13,8 +13,7 @@ browser.jar:
|
|||||||
#endif
|
#endif
|
||||||
% overlay chrome://global/content/viewSource.xul chrome://browser/content/viewSourceOverlay.xul
|
% overlay chrome://global/content/viewSource.xul chrome://browser/content/viewSourceOverlay.xul
|
||||||
% overlay chrome://global/content/viewPartialSource.xul chrome://browser/content/viewSourceOverlay.xul
|
% overlay chrome://global/content/viewPartialSource.xul chrome://browser/content/viewSourceOverlay.xul
|
||||||
% style chrome://global/content/customizeToolbar.xul chrome://browser/content/browser.css
|
|
||||||
% style chrome://global/content/customizeToolbar.xul chrome://browser/skin/
|
|
||||||
* content/browser/aboutDialog.xul (content/aboutDialog.xul)
|
* content/browser/aboutDialog.xul (content/aboutDialog.xul)
|
||||||
* content/browser/aboutDialog.js (content/aboutDialog.js)
|
* content/browser/aboutDialog.js (content/aboutDialog.js)
|
||||||
content/browser/aboutDialog.css (content/aboutDialog.css)
|
content/browser/aboutDialog.css (content/aboutDialog.css)
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
# BrandFullNameInternal is used for some registry and file system values
|
# BrandFullNameInternal is used for some registry and file system values
|
||||||
# instead of BrandFullName and typically should not be modified.
|
# instead of BrandFullName and typically should not be modified.
|
||||||
!define BrandFullNameInternal "Nightly"
|
!define BrandFullNameInternal "UX"
|
||||||
!define CompanyName "mozilla.org"
|
!define CompanyName "mozilla.org"
|
||||||
!define URLInfoAbout "http://www.mozilla.org"
|
!define URLInfoAbout "http://www.mozilla.org"
|
||||||
!define URLUpdateInfo "http://www.mozilla.org/projects/firefox"
|
!define URLUpdateInfo "http://www.mozilla.org/projects/firefox"
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
MOZ_APP_DISPLAYNAME=Nightly
|
MOZ_APP_DISPLAYNAME=UX
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
- 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/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
<!ENTITY brandShortName "Nightly">
|
<!ENTITY brandShortName "UX">
|
||||||
<!ENTITY brandFullName "Nightly">
|
<!ENTITY brandFullName "UX">
|
||||||
<!ENTITY vendorShortName "Mozilla">
|
<!ENTITY vendorShortName "Mozilla">
|
||||||
<!ENTITY trademarkInfo.part1 " ">
|
<!ENTITY trademarkInfo.part1 " ">
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
brandShortName=Nightly
|
brandShortName=UX
|
||||||
brandFullName=Nightly
|
brandFullName=UX
|
||||||
vendorShortName=Mozilla
|
vendorShortName=Mozilla
|
||||||
|
|
||||||
syncBrandShortName=Sync
|
syncBrandShortName=Sync
|
||||||
|
@ -92,6 +92,8 @@ static RedirEntry kRedirMap[] = {
|
|||||||
#endif
|
#endif
|
||||||
{ "app-manager", "chrome://browser/content/devtools/app-manager/index.xul",
|
{ "app-manager", "chrome://browser/content/devtools/app-manager/index.xul",
|
||||||
nsIAboutModule::ALLOW_SCRIPT },
|
nsIAboutModule::ALLOW_SCRIPT },
|
||||||
|
{ "customizing", "chrome://browser/content/customizableui/aboutCustomizing.xhtml",
|
||||||
|
nsIAboutModule::ALLOW_SCRIPT },
|
||||||
};
|
};
|
||||||
static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);
|
static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);
|
||||||
|
|
||||||
|
@ -110,6 +110,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
|||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||||
#endif
|
#endif
|
||||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "app-manager", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "app-manager", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||||
|
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "customizing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
{ NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
|
{ NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
|
||||||
#elif defined(XP_MACOSX)
|
#elif defined(XP_MACOSX)
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
<?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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||||
|
%brandDTD;
|
||||||
|
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||||
|
%browserDTD;
|
||||||
|
]>
|
||||||
|
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
disablefastfind="true">
|
||||||
|
<head>
|
||||||
|
<title>&customizeMode.tabTitle;</title>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
@ -0,0 +1,28 @@
|
|||||||
|
<!-- 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/. -->
|
||||||
|
|
||||||
|
<hbox id="customization-container" flex="1" hidden="true">
|
||||||
|
<vbox flex="1" id="customization-palette-container">
|
||||||
|
<label id="customization-header" value="&customizeMode.menuAndToolbars.header;"/>
|
||||||
|
<vbox id="customization-palette" flex="1"/>
|
||||||
|
<hbox pack="start">
|
||||||
|
<button id="customization-reset-button" oncommand="gCustomizeMode.reset();" label="&customizeMode.restoreDefaults;" class="customizationmode-button"/>
|
||||||
|
</hbox>
|
||||||
|
</vbox>
|
||||||
|
<vbox id="customization-panel-container">
|
||||||
|
<vbox id="customization-panelWrapper">
|
||||||
|
<html:style html:type="text/html" scoped="scoped">
|
||||||
|
@import url(chrome://global/skin/popup.css);
|
||||||
|
</html:style>
|
||||||
|
<box class="panel-arrowbox">
|
||||||
|
<box flex="1"/>
|
||||||
|
<image class="panel-arrow" side="top"/>
|
||||||
|
</box>
|
||||||
|
<box class="panel-arrowcontent" side="top" flex="1">
|
||||||
|
<hbox id="customization-panelHolder"/>
|
||||||
|
<box class="panel-inner-arrowcontentfooter" hidden="true"/>
|
||||||
|
</box>
|
||||||
|
</vbox>
|
||||||
|
</vbox>
|
||||||
|
</hbox>
|
11
browser/components/customizableui/content/jar.mn
Normal file
11
browser/components/customizableui/content/jar.mn
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# 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/customizableui/aboutCustomizing.xhtml
|
||||||
|
content/browser/customizableui/panelUI.css
|
||||||
|
content/browser/customizableui/panelUI.js
|
||||||
|
content/browser/customizableui/panelUI.xml
|
||||||
|
content/browser/customizableui/toolbar.xml
|
||||||
|
|
6
browser/components/customizableui/content/moz.build
Normal file
6
browser/components/customizableui/content/moz.build
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# -*- 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/.
|
||||||
|
|
31
browser/components/customizableui/content/panelUI.css
Normal file
31
browser/components/customizableui/content/panelUI.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
.panel-viewstack[viewtype="main"] > .panel-clickcapturer {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-mainview,
|
||||||
|
.panel-viewcontainer,
|
||||||
|
.panel-viewstack {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-viewstack {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-subviews {
|
||||||
|
-moz-stack-sizing: ignore;
|
||||||
|
transform: translateX(0);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-subviews[panelopen] {
|
||||||
|
transition: transform 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-viewcontainer[panelopen]:-moz-any(:not([viewtype="main"]),[transitioning="true"]) {
|
||||||
|
transition: height 150ms;
|
||||||
|
}
|
153
browser/components/customizableui/content/panelUI.inc.xul
Normal file
153
browser/components/customizableui/content/panelUI.inc.xul
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<!-- 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/. -->
|
||||||
|
|
||||||
|
<panel id="PanelUI-popup"
|
||||||
|
role="group"
|
||||||
|
type="arrow"
|
||||||
|
level="top"
|
||||||
|
hidden="true"
|
||||||
|
noautofocus="true">
|
||||||
|
<panelmultiview id="PanelUI-multiView" mainViewId="PanelUI-mainView">
|
||||||
|
<panelview id="PanelUI-mainView" contextmenu="customizationPanelContextMenu">
|
||||||
|
<vbox id="PanelUI-contents-scroller">
|
||||||
|
<vbox id="PanelUI-contents"/>
|
||||||
|
</vbox>
|
||||||
|
|
||||||
|
<footer id="PanelUI-footer">
|
||||||
|
<!-- The parentNode is used so that the footer is presented as the anchor
|
||||||
|
instead of just the button being the anchor. -->
|
||||||
|
<toolbarbutton id="PanelUI-help" label="&helpMenu.label;" tabindex="0"
|
||||||
|
oncommand="PanelUI.showHelpView(this.parentNode);"/>
|
||||||
|
<toolbarbutton id="PanelUI-customize" label="&appMenuCustomize.label;" tabindex="0"
|
||||||
|
oncommand="gCustomizeMode.toggle();"/>
|
||||||
|
<toolbarbutton id="PanelUI-quit" tabindex="0"
|
||||||
|
#ifdef XP_WIN
|
||||||
|
label="&quitApplicationCmdWin.label;"
|
||||||
|
#else
|
||||||
|
label="&quitApplicationCmd.label;"
|
||||||
|
#endif
|
||||||
|
command="cmd_quitApplication"/>
|
||||||
|
</footer>
|
||||||
|
</panelview>
|
||||||
|
|
||||||
|
<panelview id="PanelUI-history" flex="1">
|
||||||
|
<label value="&appMenuHistory.label;"/>
|
||||||
|
<toolbarbutton id="appMenuClearRecentHistory" tabindex="0"
|
||||||
|
label="&appMenuHistory.clearRecent.label;"
|
||||||
|
command="Tools:Sanitize"/>
|
||||||
|
<toolbarbutton id="appMenuRestoreLastSession" tabindex="0"
|
||||||
|
label="&appMenuHistory.restoreSession.label;"
|
||||||
|
command="Browser:RestoreLastSession"/>
|
||||||
|
<menuseparator id="PanelUI-recentlyClosedTabs-separator"/>
|
||||||
|
<vbox id="PanelUI-recentlyClosedTabs"/>
|
||||||
|
<menuseparator id="PanelUI-recentlyClosedWindows-separator"/>
|
||||||
|
<vbox id="PanelUI-recentlyClosedWindows"/>
|
||||||
|
<menuseparator id="PanelUI-historyItems-separator"/>
|
||||||
|
<vbox id="PanelUI-historyItems"/>
|
||||||
|
<label value="&appMenuHistory.showAll.label;"
|
||||||
|
id="PanelUI-historyMore"
|
||||||
|
class="text-link"
|
||||||
|
onclick="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
|
||||||
|
</panelview>
|
||||||
|
|
||||||
|
<panelview id="PanelUI-bookmarks" flex="1">
|
||||||
|
<toolbarbutton id="panelMenuBookmarkThisPage"
|
||||||
|
label="&bookmarkThisPageCmd.label;"
|
||||||
|
command="Browser:AddBookmarkAs"
|
||||||
|
onclick="PanelUI.hide();"/>
|
||||||
|
<toolbarseparator/>
|
||||||
|
<toolbarbutton id="panelMenu_showAllBookmarks"
|
||||||
|
label="&showAllBookmarks2.label;"
|
||||||
|
command="Browser:ShowAllBookmarks"
|
||||||
|
onclick="PanelUI.hide();"/>
|
||||||
|
<toolbarbutton id="panelMenu_viewBookmarksSidebar"
|
||||||
|
label="&viewBookmarksSidebar2.label;"
|
||||||
|
oncommand="toggleSidebar('viewBookmarksSidebar'); PanelUI.hide();">
|
||||||
|
<observes element="viewBookmarksSidebar" attribute="checked"/>
|
||||||
|
</toolbarbutton>
|
||||||
|
<toolbarbutton id="panelMenu_viewBookmarksToolbar"
|
||||||
|
label="&viewBookmarksToolbar.label;"
|
||||||
|
type="checkbox"
|
||||||
|
toolbarId="PersonalToolbar"
|
||||||
|
oncommand="onViewToolbarCommand(event); PanelUI.hide();"/>
|
||||||
|
<toolbarseparator/>
|
||||||
|
<toolbarbutton id="panelMenu_bookmarksToolbar"
|
||||||
|
label="&personalbarCmd.label;"
|
||||||
|
oncommand="PlacesCommandHook.showPlacesOrganizer('BookmarksToolbar'); PanelUI.hide();"/>
|
||||||
|
<toolbarbutton id="panelMenu_unsortedBookmarks"
|
||||||
|
label="&unsortedBookmarksCmd.label;"
|
||||||
|
oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks'); PanelUI.hide();"/>
|
||||||
|
<toolbarseparator/>
|
||||||
|
<toolbaritem id="panelMenu_bookmarksMenu"
|
||||||
|
flex="1"
|
||||||
|
orient="vertical"
|
||||||
|
smoothscroll="false"
|
||||||
|
onclick="if (event.button == 1) BookmarkingUI.onPanelMenuViewCommand(event, this._placesView);"
|
||||||
|
oncommand="BookmarkingUI.onPanelMenuViewCommand(event, this._placesView);"
|
||||||
|
flatList="true"
|
||||||
|
tooltip="bhTooltip">
|
||||||
|
<!-- bookmarks menu items -->
|
||||||
|
</toolbaritem>
|
||||||
|
|
||||||
|
</panelview>
|
||||||
|
|
||||||
|
<panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);"></panelview>
|
||||||
|
|
||||||
|
<panelview id="PanelUI-helpView" flex="1">
|
||||||
|
<label value="&helpMenu.label;"/>
|
||||||
|
<vbox id="PanelUI-helpItems"/>
|
||||||
|
</panelview>
|
||||||
|
|
||||||
|
<panelview id="PanelUI-developer" flex="1">
|
||||||
|
<label value="&webDeveloperMenu.label;"/>
|
||||||
|
<vbox id="PanelUI-developerItems"/>
|
||||||
|
</panelview>
|
||||||
|
|
||||||
|
<panelview id="PanelUI-characterEncodingView" flex="1">
|
||||||
|
<label value="&charsetMenu.label;"/>
|
||||||
|
<toolbarbutton label="&charsetCustomize.label;"
|
||||||
|
oncommand="PanelUI.onCharsetCustomizeCommand();"/>
|
||||||
|
|
||||||
|
<vbox id="PanelUI-characterEncodingView-customlist"
|
||||||
|
class="PanelUI-characterEncodingView-list"/>
|
||||||
|
<vbox>
|
||||||
|
<label value="&charsetMenuAutodet.label;"/>
|
||||||
|
<vbox id="PanelUI-characterEncodingView-autodetect"
|
||||||
|
class="PanelUI-characterEncodingView-list"/>
|
||||||
|
</vbox>
|
||||||
|
</panelview>
|
||||||
|
|
||||||
|
</panelmultiview>
|
||||||
|
<popupset id="customizationContextMenus">
|
||||||
|
<menupopup id="customizationContextMenu">
|
||||||
|
<menuitem oncommand="gCustomizeMode.addToToolbar(document.popupNode)"
|
||||||
|
accesskey="&customizeMenu.addToToolbar.accesskey;"
|
||||||
|
label="&customizeMenu.addToToolbar.label;"/>
|
||||||
|
<menuitem oncommand="gCustomizeMode.removeFromPanel(document.popupNode)"
|
||||||
|
accesskey="&customizeMenu.removeFromMenu.accesskey;"
|
||||||
|
label="&customizeMenu.removeFromMenu.label;"/>
|
||||||
|
<menuseparator/>
|
||||||
|
<menuitem command="cmd_CustomizeToolbars"
|
||||||
|
accesskey="&viewCustomizeToolbar.accesskey;"
|
||||||
|
label="&viewCustomizeToolbar.label;"/>
|
||||||
|
</menupopup>
|
||||||
|
|
||||||
|
<menupopup id="customizationPanelContextMenu">
|
||||||
|
<menuitem command="cmd_CustomizeToolbars"
|
||||||
|
accesskey="&customizeMenu.addMoreItems.accesskey;"
|
||||||
|
label="&customizeMenu.addMoreItems.label;"/>
|
||||||
|
</menupopup>
|
||||||
|
</popupset>
|
||||||
|
</panel>
|
||||||
|
|
||||||
|
<panel id="widget-overflow"
|
||||||
|
role="group"
|
||||||
|
type="arrow"
|
||||||
|
level="top"
|
||||||
|
hidden="true"
|
||||||
|
consumeoutsideclicks="true">
|
||||||
|
<vbox id="widget-overflow-scroller">
|
||||||
|
<vbox id="widget-overflow-list"/>
|
||||||
|
</vbox>
|
||||||
|
</panel>
|
399
browser/components/customizableui/content/panelUI.js
Normal file
399
browser/components/customizableui/content/panelUI.js
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
||||||
|
"resource:///modules/CustomizableUI.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "ScrollbarSampler",
|
||||||
|
"resource:///modules/ScrollbarSampler.jsm");
|
||||||
|
/**
|
||||||
|
* Maintains the state and dispatches events for the main menu panel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const PanelUI = {
|
||||||
|
/** Panel events that we listen for. **/
|
||||||
|
get kEvents() ["popupshowing", "popupshown", "popuphiding", "popuphidden"],
|
||||||
|
/**
|
||||||
|
* Used for lazily getting and memoizing elements from the document. Lazy
|
||||||
|
* getters are set in init, and memoizing happens after the first retrieval.
|
||||||
|
*/
|
||||||
|
get kElements() {
|
||||||
|
return {
|
||||||
|
contents: "PanelUI-contents",
|
||||||
|
mainView: "PanelUI-mainView",
|
||||||
|
multiView: "PanelUI-multiView",
|
||||||
|
helpView: "PanelUI-helpView",
|
||||||
|
menuButton: "PanelUI-menu-button",
|
||||||
|
panel: "PanelUI-popup",
|
||||||
|
scroller: "PanelUI-contents-scroller"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
for (let [k, v] of Iterator(this.kElements)) {
|
||||||
|
// Need to do fresh let-bindings per iteration
|
||||||
|
let getKey = k;
|
||||||
|
let id = v;
|
||||||
|
this.__defineGetter__(getKey, function() {
|
||||||
|
delete this[getKey];
|
||||||
|
return this[getKey] = document.getElementById(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.menuButton.addEventListener("mousedown", this);
|
||||||
|
this.menuButton.addEventListener("keypress", this);
|
||||||
|
},
|
||||||
|
|
||||||
|
_eventListenersAdded: false,
|
||||||
|
_ensureEventListenersAdded: function() {
|
||||||
|
if (this._eventListenersAdded)
|
||||||
|
return;
|
||||||
|
this._addEventListeners();
|
||||||
|
},
|
||||||
|
|
||||||
|
_addEventListeners: function() {
|
||||||
|
for (let event of this.kEvents) {
|
||||||
|
this.panel.addEventListener(event, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.helpView.addEventListener("ViewShowing", this._onHelpViewShow, false);
|
||||||
|
this.helpView.addEventListener("ViewHiding", this._onHelpViewHide, false);
|
||||||
|
this._eventListenersAdded = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
uninit: function() {
|
||||||
|
if (!this._eventListenersAdded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let event of this.kEvents) {
|
||||||
|
this.panel.removeEventListener(event, this);
|
||||||
|
}
|
||||||
|
this.helpView.removeEventListener("ViewShowing", this._onHelpViewShow);
|
||||||
|
this.helpView.removeEventListener("ViewHiding", this._onHelpViewHide);
|
||||||
|
this.menuButton.removeEventListener("mousedown", this);
|
||||||
|
this.menuButton.removeEventListener("keypress", this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize mode extracts the mainView and puts it somewhere else while the
|
||||||
|
* user customizes. Upon completion, this function can be called to put the
|
||||||
|
* panel back to where it belongs in normal browsing mode.
|
||||||
|
*
|
||||||
|
* @param aMainView
|
||||||
|
* The mainView node to put back into place.
|
||||||
|
*/
|
||||||
|
setMainView: function(aMainView) {
|
||||||
|
this._ensureEventListenersAdded();
|
||||||
|
this.multiView.setMainView(aMainView);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the menu panel if it's closed, or closes it if it's
|
||||||
|
* open.
|
||||||
|
*
|
||||||
|
* @param aEvent the event that triggers the toggle.
|
||||||
|
*/
|
||||||
|
toggle: function(aEvent) {
|
||||||
|
// Don't show the panel if the window is in customization mode,
|
||||||
|
// since this button doubles as an exit path for the user in this case.
|
||||||
|
if (document.documentElement.hasAttribute("customizing")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._ensureEventListenersAdded();
|
||||||
|
if (this.panel.state == "open") {
|
||||||
|
this.hide();
|
||||||
|
} else if (this.panel.state == "closed") {
|
||||||
|
this.show(aEvent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the menu panel. If the event target has a child with the
|
||||||
|
* toolbarbutton-icon attribute, the panel will be anchored on that child.
|
||||||
|
* Otherwise, the panel is anchored on the event target itself.
|
||||||
|
*
|
||||||
|
* @param aEvent the event (if any) that triggers showing the menu.
|
||||||
|
*/
|
||||||
|
show: function(aEvent) {
|
||||||
|
if (this.panel.state == "open" || this.panel.state == "showing" ||
|
||||||
|
document.documentElement.hasAttribute("customizing")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ensureReady().then(() => {
|
||||||
|
this.panel.hidden = false;
|
||||||
|
let editControlPlacement = CustomizableUI.getPlacementOfWidget("edit-controls");
|
||||||
|
if (editControlPlacement && editControlPlacement.area == CustomizableUI.AREA_PANEL) {
|
||||||
|
updateEditUIVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
let anchor;
|
||||||
|
if (aEvent.type == "mousedown" ||
|
||||||
|
aEvent.type == "command") {
|
||||||
|
anchor = this.menuButton;
|
||||||
|
} else {
|
||||||
|
anchor = aEvent.target;
|
||||||
|
}
|
||||||
|
let iconAnchor =
|
||||||
|
document.getAnonymousElementByAttribute(anchor, "class",
|
||||||
|
"toolbarbutton-icon");
|
||||||
|
|
||||||
|
// Only focus the panel if it's opened using the keyboard, so that
|
||||||
|
// cut/copy/paste buttons will work for mouse users.
|
||||||
|
let keyboardOpened = aEvent.sourceEvent &&
|
||||||
|
aEvent.sourceEvent.target.localName == "key";
|
||||||
|
this.panel.setAttribute("noautofocus", !keyboardOpened);
|
||||||
|
this.panel.openPopup(iconAnchor || anchor, "bottomcenter topright");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the menu panel is being shown, hide it.
|
||||||
|
*/
|
||||||
|
hide: function() {
|
||||||
|
if (document.documentElement.hasAttribute("customizing")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.panel.hidePopup();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEvent: function(aEvent) {
|
||||||
|
switch (aEvent.type) {
|
||||||
|
case "popupshowing":
|
||||||
|
// Fall through
|
||||||
|
case "popupshown":
|
||||||
|
// Fall through
|
||||||
|
case "popuphiding":
|
||||||
|
// Fall through
|
||||||
|
case "popuphidden": {
|
||||||
|
this._updatePanelButton(aEvent.target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "mousedown":
|
||||||
|
// Fall through
|
||||||
|
case "keypress":
|
||||||
|
this.toggle(aEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registering the menu panel is done lazily for performance reasons. This
|
||||||
|
* method is exposed so that CustomizationMode can force panel-readyness in the
|
||||||
|
* event that customization mode is started before the panel has been opened
|
||||||
|
* by the user.
|
||||||
|
*
|
||||||
|
* @param aCustomizing (optional) set to true if this was called while entering
|
||||||
|
* customization mode. If that's the case, we trust that customization
|
||||||
|
* mode will handle calling beginBatchUpdate and endBatchUpdate.
|
||||||
|
*
|
||||||
|
* @return a Promise that resolves once the panel is ready to roll.
|
||||||
|
*/
|
||||||
|
ensureReady: function(aCustomizing=false) {
|
||||||
|
if (this._readyPromise) {
|
||||||
|
return this._readyPromise;
|
||||||
|
}
|
||||||
|
this._readyPromise = Task.spawn(function() {
|
||||||
|
if (!this._scrollWidth) {
|
||||||
|
// In order to properly center the contents of the panel, while ensuring
|
||||||
|
// that we have enough space on either side to show a scrollbar, we have to
|
||||||
|
// do a bit of hackery. In particular, we calculate a new width for the
|
||||||
|
// scroller, based on the system scrollbar width.
|
||||||
|
this._scrollWidth =
|
||||||
|
(yield ScrollbarSampler.getSystemScrollbarWidth()) + "px";
|
||||||
|
let cstyle = window.getComputedStyle(this.scroller);
|
||||||
|
let widthStr = cstyle.width;
|
||||||
|
// Get the calculated padding on the left and right sides of
|
||||||
|
// the scroller too. We'll use that in our final calculation so
|
||||||
|
// that if a scrollbar appears, we don't have the contents right
|
||||||
|
// up against the edge of the scroller.
|
||||||
|
let paddingLeft = cstyle.paddingLeft;
|
||||||
|
let paddingRight = cstyle.paddingRight;
|
||||||
|
let calcStr = [widthStr, this._scrollWidth,
|
||||||
|
paddingLeft, paddingRight].join(" + ");
|
||||||
|
this.scroller.style.width = "calc(" + calcStr + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aCustomizing) {
|
||||||
|
CustomizableUI.registerMenuPanel(this.contents);
|
||||||
|
} else {
|
||||||
|
this.beginBatchUpdate();
|
||||||
|
CustomizableUI.registerMenuPanel(this.contents);
|
||||||
|
this.endBatchUpdate();
|
||||||
|
}
|
||||||
|
}.bind(this)).then(null, Cu.reportError);
|
||||||
|
|
||||||
|
return this._readyPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch the panel to the main view if it's not already
|
||||||
|
* in that view.
|
||||||
|
*/
|
||||||
|
showMainView: function() {
|
||||||
|
this._ensureEventListenersAdded();
|
||||||
|
this.multiView.showMainView();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch the panel to the help view if it's not already
|
||||||
|
* in that view.
|
||||||
|
*/
|
||||||
|
showHelpView: function(aAnchor) {
|
||||||
|
this._ensureEventListenersAdded();
|
||||||
|
this.multiView.showSubView("PanelUI-helpView", aAnchor);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a subview in the panel with a given ID.
|
||||||
|
*
|
||||||
|
* @param aViewId the ID of the subview to show.
|
||||||
|
* @param aAnchor the element that spawned the subview.
|
||||||
|
* @param aPlacementArea the CustomizableUI area that aAnchor is in.
|
||||||
|
*/
|
||||||
|
showSubView: function(aViewId, aAnchor, aPlacementArea) {
|
||||||
|
this._ensureEventListenersAdded();
|
||||||
|
let viewNode = document.getElementById(aViewId);
|
||||||
|
if (!viewNode) {
|
||||||
|
Cu.reportError("Could not show panel subview with id: " + aViewId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aAnchor) {
|
||||||
|
Cu.reportError("Expected an anchor when opening subview with id: " + aViewId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aPlacementArea == CustomizableUI.AREA_PANEL) {
|
||||||
|
this.multiView.showSubView(aViewId, aAnchor);
|
||||||
|
} else if (!aAnchor.open) {
|
||||||
|
aAnchor.open = true;
|
||||||
|
// Emit the ViewShowing event so that the widget definition has a chance
|
||||||
|
// to lazily populate the subview with things.
|
||||||
|
let evt = document.createEvent("CustomEvent");
|
||||||
|
evt.initCustomEvent("ViewShowing", true, true, viewNode);
|
||||||
|
viewNode.dispatchEvent(evt);
|
||||||
|
if (evt.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tempPanel = document.createElement("panel");
|
||||||
|
tempPanel.setAttribute("type", "arrow");
|
||||||
|
tempPanel.setAttribute("id", "customizationui-widget-panel");
|
||||||
|
tempPanel.setAttribute("level", "top");
|
||||||
|
document.getElementById(CustomizableUI.AREA_NAVBAR).appendChild(tempPanel);
|
||||||
|
|
||||||
|
let multiView = document.createElement("panelmultiview");
|
||||||
|
tempPanel.appendChild(multiView);
|
||||||
|
multiView.setMainView(viewNode);
|
||||||
|
CustomizableUI.addPanelCloseListeners(tempPanel);
|
||||||
|
|
||||||
|
let panelRemover = function() {
|
||||||
|
tempPanel.removeEventListener("popuphidden", panelRemover);
|
||||||
|
CustomizableUI.removePanelCloseListeners(tempPanel);
|
||||||
|
let evt = new CustomEvent("ViewHiding", {detail: viewNode});
|
||||||
|
viewNode.dispatchEvent(evt);
|
||||||
|
aAnchor.open = false;
|
||||||
|
|
||||||
|
this.multiView.appendChild(viewNode);
|
||||||
|
tempPanel.parentElement.removeChild(tempPanel);
|
||||||
|
}.bind(this);
|
||||||
|
tempPanel.addEventListener("popuphidden", panelRemover);
|
||||||
|
|
||||||
|
let iconAnchor =
|
||||||
|
document.getAnonymousElementByAttribute(aAnchor, "class",
|
||||||
|
"toolbarbutton-icon");
|
||||||
|
|
||||||
|
tempPanel.openPopup(iconAnchor || aAnchor, "bottomcenter topright");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used as a command event listener for subviews
|
||||||
|
* so that the panel knows if and when to close itself.
|
||||||
|
*/
|
||||||
|
onCommandHandler: function(aEvent) {
|
||||||
|
if (!aEvent.originalTarget.hasAttribute("noautoclose")) {
|
||||||
|
PanelUI.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a dialog window that allow the user to customize listed character sets.
|
||||||
|
*/
|
||||||
|
onCharsetCustomizeCommand: function() {
|
||||||
|
this.hide();
|
||||||
|
window.openDialog("chrome://global/content/customizeCharset.xul",
|
||||||
|
"PrefWindow",
|
||||||
|
"chrome,modal=yes,resizable=yes",
|
||||||
|
"browser");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal that we're about to make a lot of changes to the contents of the
|
||||||
|
* panels all at once. For performance, we ignore the mutations.
|
||||||
|
*/
|
||||||
|
beginBatchUpdate: function() {
|
||||||
|
this._ensureEventListenersAdded();
|
||||||
|
this.multiView.ignoreMutations = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal that we're done making bulk changes to the panel. We now pay
|
||||||
|
* attention to mutations. This automatically synchronizes the multiview
|
||||||
|
* container with whichever view is displayed if the panel is open.
|
||||||
|
*/
|
||||||
|
endBatchUpdate: function(aReason) {
|
||||||
|
this._ensureEventListenersAdded();
|
||||||
|
this.multiView.ignoreMutations = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the anchor node into the open or closed state, depending
|
||||||
|
* on the state of the panel.
|
||||||
|
*/
|
||||||
|
_updatePanelButton: function() {
|
||||||
|
this.menuButton.open = this.panel.state == "open" ||
|
||||||
|
this.panel.state == "showing";
|
||||||
|
},
|
||||||
|
|
||||||
|
_onHelpViewShow: function(aEvent) {
|
||||||
|
// Call global menu setup function
|
||||||
|
buildHelpMenu();
|
||||||
|
|
||||||
|
let helpMenu = document.getElementById("menu_HelpPopup");
|
||||||
|
let items = this.getElementsByTagName("vbox")[0];
|
||||||
|
let attrs = ["oncommand", "onclick", "label", "key", "disabled"];
|
||||||
|
let NSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
|
||||||
|
// Remove all buttons from the view
|
||||||
|
while (items.firstChild) {
|
||||||
|
items.removeChild(items.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the current set of menuitems of the Help menu to this view
|
||||||
|
let menuItems = Array.prototype.slice.call(helpMenu.getElementsByTagName("menuitem"));
|
||||||
|
let fragment = document.createDocumentFragment();
|
||||||
|
for (let node of menuItems) {
|
||||||
|
if (node.hidden)
|
||||||
|
continue;
|
||||||
|
let button = document.createElementNS(NSXUL, "toolbarbutton");
|
||||||
|
// Copy specific attributes from a menuitem of the Help menu
|
||||||
|
for (let attrName of attrs) {
|
||||||
|
if (!node.hasAttribute(attrName))
|
||||||
|
continue;
|
||||||
|
button.setAttribute(attrName, node.getAttribute(attrName));
|
||||||
|
}
|
||||||
|
fragment.appendChild(button);
|
||||||
|
}
|
||||||
|
items.appendChild(fragment);
|
||||||
|
|
||||||
|
this.addEventListener("command", PanelUI.onCommandHandler);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onHelpViewHide: function(aEvent) {
|
||||||
|
this.removeEventListener("command", PanelUI.onCommandHandler);
|
||||||
|
}
|
||||||
|
};
|
342
browser/components/customizableui/content/panelUI.xml
Normal file
342
browser/components/customizableui/content/panelUI.xml
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
|
<bindings id="browserPanelUIBindings"
|
||||||
|
xmlns="http://www.mozilla.org/xbl"
|
||||||
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
|
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||||
|
|
||||||
|
<binding id="panelmultiview">
|
||||||
|
<resources>
|
||||||
|
<stylesheet src="chrome://browser/content/customizableui/panelUI.css"/>
|
||||||
|
</resources>
|
||||||
|
<content>
|
||||||
|
<xul:box anonid="viewContainer" class="panel-viewcontainer" xbl:inherits="panelopen,viewtype,transitioning">
|
||||||
|
<xul:stack anonid="viewStack" xbl:inherits="viewtype" viewtype="main" class="panel-viewstack">
|
||||||
|
<xul:vbox anonid="mainViewContainer" class="panel-mainview"/>
|
||||||
|
|
||||||
|
<!-- Used to capture click events over the PanelUI-mainView if we're in
|
||||||
|
subview mode. That way, any click on the PanelUI-mainView causes us
|
||||||
|
to revert to the mainView mode, whereupon PanelUI-click-capture then
|
||||||
|
allows click events to go through it. -->
|
||||||
|
<xul:vbox anonid="clickCapturer" class="panel-clickcapturer"/>
|
||||||
|
|
||||||
|
<!-- We manually set display: none (via a CSS attribute selector) on the
|
||||||
|
subviews that are not being displayed. We're using this over a deck
|
||||||
|
because a deck assumes the size of its largest child, regardless of
|
||||||
|
whether or not it is shown. That's not good for our case, since we
|
||||||
|
want to allow each subview to be uniquely sized. -->
|
||||||
|
<xul:vbox anonid="subViews" class="panel-subviews" xbl:inherits="panelopen">
|
||||||
|
<children includes="panelview"/>
|
||||||
|
</xul:vbox>
|
||||||
|
</xul:stack>
|
||||||
|
</xul:box>
|
||||||
|
</content>
|
||||||
|
<implementation implements="nsIDOMEventListener">
|
||||||
|
<field name="_clickCapturer" readonly="true">
|
||||||
|
document.getAnonymousElementByAttribute(this, "anonid", "clickCapturer");
|
||||||
|
</field>
|
||||||
|
<field name="_viewContainer" readonly="true">
|
||||||
|
document.getAnonymousElementByAttribute(this, "anonid", "viewContainer");
|
||||||
|
</field>
|
||||||
|
<field name="_mainViewContainer" readonly="true">
|
||||||
|
document.getAnonymousElementByAttribute(this, "anonid", "mainViewContainer");
|
||||||
|
</field>
|
||||||
|
<field name="_subViews" readonly="true">
|
||||||
|
document.getAnonymousElementByAttribute(this, "anonid", "subViews");
|
||||||
|
</field>
|
||||||
|
<field name="_viewStack" readonly="true">
|
||||||
|
document.getAnonymousElementByAttribute(this, "anonid", "viewStack");
|
||||||
|
</field>
|
||||||
|
<field name="_panel" readonly="true">
|
||||||
|
this.parentNode;
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<field name="_currentSubView">null</field>
|
||||||
|
<field name="_anchorElement">null</field>
|
||||||
|
<field name="_mainViewHeight">0</field>
|
||||||
|
<field name="_subViewObserver">null</field>
|
||||||
|
<field name="__transitioning">false</field>
|
||||||
|
<field name="_ignoreMutations">false</field>
|
||||||
|
|
||||||
|
<property name="showingSubView" readonly="true"
|
||||||
|
onget="return this._viewStack.getAttribute('viewtype') == 'subview'"/>
|
||||||
|
<property name="_mainViewId" onget="return this.getAttribute('mainViewId');" onset="this.setAttribute('mainViewId', val); return val;"/>
|
||||||
|
<property name="_mainView" readonly="true"
|
||||||
|
onget="return this._mainViewId ? document.getElementById(this._mainViewId) : null;"/>
|
||||||
|
|
||||||
|
<property name="ignoreMutations">
|
||||||
|
<getter>
|
||||||
|
return this._ignoreMutations;
|
||||||
|
</getter>
|
||||||
|
<setter><![CDATA[
|
||||||
|
this._ignoreMutations = val;
|
||||||
|
if (!val && this._panel.state == "open") {
|
||||||
|
if (this.showingSubView) {
|
||||||
|
this._syncContainerWithSubView();
|
||||||
|
} else {
|
||||||
|
this._syncContainerWithMainView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></setter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="_transitioning">
|
||||||
|
<getter>
|
||||||
|
return this.__transitioning;
|
||||||
|
</getter>
|
||||||
|
<setter><![CDATA[
|
||||||
|
this.__transitioning = val;
|
||||||
|
if (val) {
|
||||||
|
this.setAttribute("transitioning", "true");
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("transitioning");
|
||||||
|
}
|
||||||
|
]]></setter>
|
||||||
|
</property>
|
||||||
|
<constructor><![CDATA[
|
||||||
|
this._clickCapturer.addEventListener("click", this);
|
||||||
|
this._panel.addEventListener("popupshowing", this);
|
||||||
|
this._panel.addEventListener("popupshown", this);
|
||||||
|
this._panel.addEventListener("popuphidden", this);
|
||||||
|
this._subViews.addEventListener("overflow", this);
|
||||||
|
this._mainViewContainer.addEventListener("overflow", this);
|
||||||
|
|
||||||
|
// Get a MutationObserver ready to react to subview size changes. We
|
||||||
|
// only attach this MutationObserver when a subview is being displayed.
|
||||||
|
this._subViewObserver =
|
||||||
|
new MutationObserver(this._syncContainerWithSubView.bind(this));
|
||||||
|
this._mainViewObserver =
|
||||||
|
new MutationObserver(this._syncContainerWithMainView.bind(this));
|
||||||
|
|
||||||
|
this._mainViewContainer.setAttribute("panelid",
|
||||||
|
this._panel.id);
|
||||||
|
|
||||||
|
if (this._mainView) {
|
||||||
|
this.setMainView(this._mainView);
|
||||||
|
}
|
||||||
|
this.setAttribute("viewtype", "main");
|
||||||
|
]]></constructor>
|
||||||
|
|
||||||
|
<destructor><![CDATA[
|
||||||
|
if (this._mainView) {
|
||||||
|
this._mainView.removeAttribute("mainview");
|
||||||
|
}
|
||||||
|
this._mainViewObserver.disconnect();
|
||||||
|
this._subViewObserver.disconnect();
|
||||||
|
this._panel.removeEventListener("popupshowing", this);
|
||||||
|
this._panel.removeEventListener("popupshown", this);
|
||||||
|
this._panel.removeEventListener("popuphidden", this);
|
||||||
|
this._subViews.removeEventListener("overflow", this);
|
||||||
|
this._mainViewContainer.removeEventListener("overflow", this);
|
||||||
|
this._clickCapturer.removeEventListener("click", this);
|
||||||
|
]]></destructor>
|
||||||
|
|
||||||
|
<method name="setMainView">
|
||||||
|
<parameter name="aNewMainView"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
if (this._mainView) {
|
||||||
|
this._mainViewObserver.disconnect();
|
||||||
|
this._subViews.appendChild(this._mainView);
|
||||||
|
this._mainView.removeAttribute("mainview");
|
||||||
|
}
|
||||||
|
this._mainViewId = aNewMainView.id;
|
||||||
|
aNewMainView.setAttribute("mainview", "true");
|
||||||
|
this._mainViewContainer.appendChild(aNewMainView);
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="showMainView">
|
||||||
|
<body><![CDATA[
|
||||||
|
if (this.showingSubView) {
|
||||||
|
let viewNode = this._currentSubView;
|
||||||
|
let evt = document.createEvent("CustomEvent");
|
||||||
|
evt.initCustomEvent("ViewHiding", true, true, viewNode);
|
||||||
|
viewNode.dispatchEvent(evt);
|
||||||
|
|
||||||
|
viewNode.removeAttribute("current");
|
||||||
|
this._currentSubView = null;
|
||||||
|
|
||||||
|
this._subViewObserver.disconnect();
|
||||||
|
|
||||||
|
this._transitioning = true;
|
||||||
|
|
||||||
|
this._viewContainer.addEventListener("transitionend", function trans() {
|
||||||
|
this._viewContainer.removeEventListener("transitionend", trans);
|
||||||
|
this._transitioning = false;
|
||||||
|
}.bind(this));
|
||||||
|
this._viewContainer.style.height = this._mainViewHeight + "px";
|
||||||
|
|
||||||
|
this.setAttribute("viewtype", "main");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._mainViewObserver.observe(this._mainView, {
|
||||||
|
attributes: true,
|
||||||
|
characterData: true,
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
|
this._shiftMainView();
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="showSubView">
|
||||||
|
<parameter name="aViewId"/>
|
||||||
|
<parameter name="aAnchor"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
let viewNode = this.querySelector("#" + aViewId);
|
||||||
|
viewNode.setAttribute("current", true);
|
||||||
|
// Emit the ViewShowing event so that the widget definition has a chance
|
||||||
|
// to lazily populate the subview with things.
|
||||||
|
let evt = document.createEvent("CustomEvent");
|
||||||
|
evt.initCustomEvent("ViewShowing", true, true, viewNode);
|
||||||
|
viewNode.dispatchEvent(evt);
|
||||||
|
if (evt.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._currentSubView = viewNode;
|
||||||
|
|
||||||
|
// Now we have to transition to transition the panel. There are a few parts
|
||||||
|
// to this:
|
||||||
|
//
|
||||||
|
// 1) The main view content gets shifted so that the center of the anchor
|
||||||
|
// node is at the left-most edge of the panel.
|
||||||
|
// 2) The subview deck slides in so that it takes up almost all of the
|
||||||
|
// panel.
|
||||||
|
// 3) If the subview is taller then the main panel contents, then the panel
|
||||||
|
// must grow to meet that new height. Otherwise, it must shrink.
|
||||||
|
//
|
||||||
|
// All three of these actions make use of CSS transformations, so they
|
||||||
|
// should all occur simultaneously.
|
||||||
|
this.setAttribute("viewtype", "subview");
|
||||||
|
this._shiftMainView(aAnchor);
|
||||||
|
|
||||||
|
this._mainViewHeight = this._viewStack.clientHeight;
|
||||||
|
|
||||||
|
this._transitioning = true;
|
||||||
|
this._viewContainer.addEventListener("transitionend", function trans() {
|
||||||
|
this._viewContainer.removeEventListener("transitionend", trans);
|
||||||
|
this._transitioning = false;
|
||||||
|
}.bind(this));
|
||||||
|
this._viewContainer.style.height = this._subViews.scrollHeight + "px";
|
||||||
|
|
||||||
|
this._subViewObserver.observe(viewNode, {
|
||||||
|
attributes: true,
|
||||||
|
characterData: true,
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_shiftMainView">
|
||||||
|
<parameter name="aAnchor"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
if (aAnchor) {
|
||||||
|
// We need to find the edge of the anchor, relative to the main panel.
|
||||||
|
// Then we need to add half the width of the anchor. This is the target
|
||||||
|
// that we need to transition to.
|
||||||
|
let anchorRect = aAnchor.getBoundingClientRect();
|
||||||
|
let mainViewRect = this._mainViewContainer.getBoundingClientRect();
|
||||||
|
let center = aAnchor.clientWidth / 2;
|
||||||
|
let direction = aAnchor.ownerDocument.defaultView.getComputedStyle(aAnchor, null).direction;
|
||||||
|
let edge, target;
|
||||||
|
if (direction == "ltr") {
|
||||||
|
edge = anchorRect.left - mainViewRect.left;
|
||||||
|
target = "-" + (edge + center);
|
||||||
|
} else {
|
||||||
|
edge = mainViewRect.right - anchorRect.right;
|
||||||
|
target = edge + center;
|
||||||
|
}
|
||||||
|
this._mainViewContainer.style.transform = "translateX(" + target + "px)";
|
||||||
|
aAnchor.classList.add("panel-multiview-anchor");
|
||||||
|
} else {
|
||||||
|
this._mainViewContainer.style.transform = "";
|
||||||
|
if (this.anchorElement)
|
||||||
|
this.anchorElement.classList.remove("panel-multiview-anchor");
|
||||||
|
}
|
||||||
|
this.anchorElement = aAnchor;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="handleEvent">
|
||||||
|
<parameter name="aEvent"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
switch(aEvent.type) {
|
||||||
|
case "click":
|
||||||
|
if (aEvent.originalTarget == this._clickCapturer) {
|
||||||
|
this.showMainView();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "overflow":
|
||||||
|
// Resize the right view on the next tick.
|
||||||
|
if (this.showingSubView) {
|
||||||
|
setTimeout(this._syncContainerWithSubView.bind(this), 0);
|
||||||
|
} else if (!this.transitioning) {
|
||||||
|
setTimeout(this._syncContainerWithMainView.bind(this), 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "popupshowing":
|
||||||
|
this.setAttribute("panelopen", "true");
|
||||||
|
this._syncContainerWithMainView();
|
||||||
|
break;
|
||||||
|
case "popupshown":
|
||||||
|
this._setMaxHeight();
|
||||||
|
break;
|
||||||
|
case "popuphidden":
|
||||||
|
this.removeAttribute("panelopen");
|
||||||
|
this._mainView.style.height = "";
|
||||||
|
this.showMainView();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_setMaxHeight">
|
||||||
|
<body><![CDATA[
|
||||||
|
// Ignore the mutation that'll fire when we set the height of
|
||||||
|
// the main view.
|
||||||
|
this.ignoreMutations = true;
|
||||||
|
this._mainView.style.height =
|
||||||
|
this.getBoundingClientRect().height + "px";
|
||||||
|
this.ignoreMutations = false;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
<method name="_syncContainerWithSubView">
|
||||||
|
<body><![CDATA[
|
||||||
|
if (!this.ignoreMutations && this.showingSubView) {
|
||||||
|
this._viewContainer.style.height =
|
||||||
|
this._subViews.scrollHeight + "px";
|
||||||
|
}
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
<method name="_syncContainerWithMainView">
|
||||||
|
<body><![CDATA[
|
||||||
|
if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
|
||||||
|
this._viewContainer.style.height =
|
||||||
|
this._mainView.scrollHeight + "px";
|
||||||
|
}
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</implementation>
|
||||||
|
</binding>
|
||||||
|
|
||||||
|
<binding id="panelview">
|
||||||
|
<implementation>
|
||||||
|
<property name="panelMultiView" readonly="true">
|
||||||
|
<getter><![CDATA[
|
||||||
|
if (this.parentNode.localName != "panelmultiview") {
|
||||||
|
return document.getBindingParent(this.parentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.parentNode;
|
||||||
|
]]></getter>
|
||||||
|
</property>
|
||||||
|
</implementation>
|
||||||
|
</binding>
|
||||||
|
</bindings>
|
556
browser/components/customizableui/content/toolbar.xml
Normal file
556
browser/components/customizableui/content/toolbar.xml
Normal file
@ -0,0 +1,556 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
|
<bindings id="browserToolbarBindings"
|
||||||
|
xmlns="http://www.mozilla.org/xbl"
|
||||||
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
|
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||||
|
|
||||||
|
<binding id="toolbar">
|
||||||
|
<resources>
|
||||||
|
<stylesheet src="chrome://global/skin/toolbar.css"/>
|
||||||
|
</resources>
|
||||||
|
<implementation implements="nsIAccessibleProvider">
|
||||||
|
<field name="overflowedDuringConstruction">null</field>
|
||||||
|
|
||||||
|
<property name="accessibleType" readonly="true">
|
||||||
|
<getter>
|
||||||
|
return Components.interfaces.nsIAccessibleProvider.XULToolbar;
|
||||||
|
</getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<constructor><![CDATA[
|
||||||
|
let scope = {};
|
||||||
|
Cu.import("resource:///modules/CustomizableUI.jsm", scope);
|
||||||
|
// Add an early overflow event listener that will mark if the
|
||||||
|
// toolbar overflowed during construction.
|
||||||
|
if (scope.CustomizableUI.isAreaOverflowable(this.id)) {
|
||||||
|
this.addEventListener("overflow", this);
|
||||||
|
this.addEventListener("underflow", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState == "complete") {
|
||||||
|
this._init();
|
||||||
|
} else {
|
||||||
|
// Need to wait until XUL overlays are loaded. See bug 554279.
|
||||||
|
let self = this;
|
||||||
|
document.addEventListener("readystatechange", function onReadyStateChange() {
|
||||||
|
if (document.readyState != "complete")
|
||||||
|
return;
|
||||||
|
document.removeEventListener("readystatechange", onReadyStateChange, false);
|
||||||
|
self._init();
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
]]></constructor>
|
||||||
|
|
||||||
|
<method name="_init">
|
||||||
|
<body><![CDATA[
|
||||||
|
let scope = {};
|
||||||
|
Cu.import("resource:///modules/CustomizableUI.jsm", scope);
|
||||||
|
let CustomizableUI = scope.CustomizableUI;
|
||||||
|
|
||||||
|
// Searching for the toolbox palette in the toolbar binding because
|
||||||
|
// toolbars are constructed first.
|
||||||
|
let toolbox = this.toolbox;
|
||||||
|
if (toolbox && !toolbox.palette) {
|
||||||
|
for (let node of toolbox.children) {
|
||||||
|
if (node.localName == "toolbarpalette") {
|
||||||
|
// Hold on to the palette but remove it from the document.
|
||||||
|
toolbox.palette = node;
|
||||||
|
toolbox.removeChild(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass the current set of children for comparison with placements:
|
||||||
|
let children = [node.id for (node of this.childNodes)
|
||||||
|
if (node.getAttribute("skipintoolbarset") != "true" && node.id)];
|
||||||
|
CustomizableUI.registerToolbarNode(this, children);
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="handleEvent">
|
||||||
|
<parameter name="aEvent"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
if (aEvent.type == "overflow" && aEvent.detail > 0) {
|
||||||
|
if (this.overflowable && this.overflowable.initialized) {
|
||||||
|
this.overflowable.onOverflow(aEvent);
|
||||||
|
} else {
|
||||||
|
this.overflowedDuringConstruction = aEvent;
|
||||||
|
}
|
||||||
|
} else if (aEvent.type == "underflow" && aEvent.detail > 0) {
|
||||||
|
this.overflowedDuringConstruction = null;
|
||||||
|
}
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="insertItem">
|
||||||
|
<parameter name="aId"/>
|
||||||
|
<parameter name="aBeforeElt"/>
|
||||||
|
<parameter name="aWrapper"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
if (aWrapper) {
|
||||||
|
Cu.reportError("Can't insert " + aId + ": using insertItem " +
|
||||||
|
"no longer supports wrapper elements.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hack, the customizable UI code makes this be the last position
|
||||||
|
let pos = null;
|
||||||
|
if (aBeforeElt) {
|
||||||
|
let beforeInfo = CustomizableUI.getPlacementOfWidget(aBeforeElt.id);
|
||||||
|
if (beforeInfo.area != this.id) {
|
||||||
|
Cu.reportError("Can't insert " + aId + " before " +
|
||||||
|
aBeforeElt.id + " which isn't in this area (" +
|
||||||
|
this.id + ").");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
pos = beforeInfo.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomizableUI.addWidgetToArea(aId, this.id, pos);
|
||||||
|
return this.ownerDocument.getElementById(aId);
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<property name="toolbarName"
|
||||||
|
onget="return this.getAttribute('toolbarname');"
|
||||||
|
onset="this.setAttribute('toolbarname', val); return val;"/>
|
||||||
|
|
||||||
|
<property name="customizationTarget" readonly="true">
|
||||||
|
<getter><![CDATA[
|
||||||
|
if (this._customizationTarget)
|
||||||
|
return this._customizationTarget;
|
||||||
|
|
||||||
|
let id = this.getAttribute("customizationtarget");
|
||||||
|
if (id)
|
||||||
|
this._customizationTarget = document.getElementById(id);
|
||||||
|
|
||||||
|
if (this._customizationTarget)
|
||||||
|
this._customizationTarget.insertItem = this.insertItem.bind(this);
|
||||||
|
else
|
||||||
|
this._customizationTarget = this;
|
||||||
|
|
||||||
|
return this._customizationTarget;
|
||||||
|
]]></getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="toolbox" readonly="true">
|
||||||
|
<getter><![CDATA[
|
||||||
|
if (this._toolbox)
|
||||||
|
return this._toolbox;
|
||||||
|
|
||||||
|
let toolboxId = this.getAttribute("toolboxid");
|
||||||
|
if (toolboxId) {
|
||||||
|
let toolbox = document.getElementById(toolboxId);
|
||||||
|
if (toolbox) {
|
||||||
|
if (toolbox.externalToolbars.indexOf(this) == -1)
|
||||||
|
toolbox.externalToolbars.push(this);
|
||||||
|
|
||||||
|
this._toolbox = toolbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._toolbox && this.parentNode &&
|
||||||
|
this.parentNode.localName == "toolbox") {
|
||||||
|
this._toolbox = this.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._toolbox;
|
||||||
|
]]></getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<property name="currentSet">
|
||||||
|
<getter><![CDATA[
|
||||||
|
let currentWidgets = new Set();
|
||||||
|
for (let node of this.customizationTarget.children) {
|
||||||
|
let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
|
||||||
|
if (realNode.getAttribute("skipintoolbarset") != "true") {
|
||||||
|
currentWidgets.add(realNode.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.getAttribute("overflowing") == "true") {
|
||||||
|
let overflowTarget = this.getAttribute("overflowtarget");
|
||||||
|
let overflowList = this.ownerDocument.getElementById(overflowTarget);
|
||||||
|
for (let node of overflowList.children) {
|
||||||
|
let realNode = node.localName == "toolbarpaletteitem" ? node.firstChild : node;
|
||||||
|
if (realNode.getAttribute("skipintoolbarset") != "true") {
|
||||||
|
currentWidgets.add(realNode.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let orderedPlacements = CustomizableUI.getWidgetIdsInArea(this.id);
|
||||||
|
return orderedPlacements.filter((x) => currentWidgets.has(x)).join(',');
|
||||||
|
]]></getter>
|
||||||
|
<setter><![CDATA[
|
||||||
|
// Get list of new and old ids:
|
||||||
|
let newVal = (val || '').split(',').filter(x => x);
|
||||||
|
let oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
|
||||||
|
|
||||||
|
// Get a list of items only in the new list
|
||||||
|
let newIds = [id for (id of newVal) if (oldIds.indexOf(id) == -1)];
|
||||||
|
CustomizableUI.beginBatchUpdate();
|
||||||
|
for (let newId of newIds) {
|
||||||
|
oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
|
||||||
|
let nextId = newId;
|
||||||
|
let pos;
|
||||||
|
do {
|
||||||
|
// Get the next item
|
||||||
|
nextId = newVal[newVal.indexOf(nextId) + 1];
|
||||||
|
// Figure out where it is in the old list
|
||||||
|
pos = oldIds.indexOf(nextId);
|
||||||
|
// If it's not in the old list, repeat:
|
||||||
|
} while (pos == -1 && nextId);
|
||||||
|
if (pos == -1) {
|
||||||
|
pos = null; // We didn't find anything, insert at the end
|
||||||
|
}
|
||||||
|
CustomizableUI.addWidgetToArea(newId, this.id, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentIds = this.currentSet.split(',');
|
||||||
|
let removedIds = [id for (id of currentIds) if (newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1)];
|
||||||
|
for (let removedId of removedIds) {
|
||||||
|
CustomizableUI.removeWidgetFromArea(removedId);
|
||||||
|
}
|
||||||
|
CustomizableUI.endBatchUpdate();
|
||||||
|
]]></setter>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
|
||||||
|
</implementation>
|
||||||
|
</binding>
|
||||||
|
|
||||||
|
<binding id="toolbar-menubar-stub">
|
||||||
|
<implementation>
|
||||||
|
<property name="toolbox" readonly="true">
|
||||||
|
<getter><![CDATA[
|
||||||
|
if (this._toolbox)
|
||||||
|
return this._toolbox;
|
||||||
|
|
||||||
|
if (this.parentNode && this.parentNode.localName == "toolbox") {
|
||||||
|
this._toolbox = this.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._toolbox;
|
||||||
|
]]></getter>
|
||||||
|
</property>
|
||||||
|
<property name="currentSet" readonly="true">
|
||||||
|
<getter><![CDATA[
|
||||||
|
return this.getAttribute("defaultset");
|
||||||
|
]]></getter>
|
||||||
|
</property>
|
||||||
|
<method name="insertItem">
|
||||||
|
<body><![CDATA[
|
||||||
|
return null;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
</implementation>
|
||||||
|
</binding>
|
||||||
|
|
||||||
|
<!-- The toolbar-menubar-autohide and toolbar-drag bindings are almost
|
||||||
|
verbatim copies of their toolkit counterparts - they just inherit from
|
||||||
|
the customizableui's toolbar binding instead of toolkit's. We're currently
|
||||||
|
OK with the maintainance burden of having two copies of a binding, since
|
||||||
|
the long term goal is to move the customization framework into toolkit. -->
|
||||||
|
|
||||||
|
<binding id="toolbar-menubar-autohide"
|
||||||
|
extends="chrome://browser/content/customizableui/toolbar.xml#toolbar">
|
||||||
|
<implementation>
|
||||||
|
<constructor>
|
||||||
|
this._setInactive();
|
||||||
|
</constructor>
|
||||||
|
<destructor>
|
||||||
|
this._setActive();
|
||||||
|
</destructor>
|
||||||
|
|
||||||
|
<field name="_inactiveTimeout">null</field>
|
||||||
|
|
||||||
|
<field name="_contextMenuListener"><![CDATA[({
|
||||||
|
toolbar: this,
|
||||||
|
contextMenu: null,
|
||||||
|
|
||||||
|
get active () !!this.contextMenu,
|
||||||
|
|
||||||
|
init: function (event) {
|
||||||
|
let node = event.target;
|
||||||
|
while (node != this.toolbar) {
|
||||||
|
if (node.localName == "menupopup")
|
||||||
|
return;
|
||||||
|
node = node.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
let contextMenuId = this.toolbar.getAttribute("context");
|
||||||
|
if (!contextMenuId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.contextMenu = document.getElementById(contextMenuId);
|
||||||
|
if (!this.contextMenu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.contextMenu.addEventListener("popupshown", this, false);
|
||||||
|
this.contextMenu.addEventListener("popuphiding", this, false);
|
||||||
|
this.toolbar.addEventListener("mousemove", this, false);
|
||||||
|
},
|
||||||
|
handleEvent: function (event) {
|
||||||
|
switch (event.type) {
|
||||||
|
case "popupshown":
|
||||||
|
this.toolbar.removeEventListener("mousemove", this, false);
|
||||||
|
break;
|
||||||
|
case "popuphiding":
|
||||||
|
case "mousemove":
|
||||||
|
this.toolbar._setInactiveAsync();
|
||||||
|
this.toolbar.removeEventListener("mousemove", this, false);
|
||||||
|
this.contextMenu.removeEventListener("popuphiding", this, false);
|
||||||
|
this.contextMenu.removeEventListener("popupshown", this, false);
|
||||||
|
this.contextMenu = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})]]></field>
|
||||||
|
|
||||||
|
<method name="_setInactive">
|
||||||
|
<body><![CDATA[
|
||||||
|
this.setAttribute("inactive", "true");
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_setInactiveAsync">
|
||||||
|
<body><![CDATA[
|
||||||
|
this._inactiveTimeout = setTimeout(function (self) {
|
||||||
|
if (self.getAttribute("autohide") == "true") {
|
||||||
|
self._inactiveTimeout = null;
|
||||||
|
self._setInactive();
|
||||||
|
}
|
||||||
|
}, 0, this);
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_setActive">
|
||||||
|
<body><![CDATA[
|
||||||
|
if (this._inactiveTimeout) {
|
||||||
|
clearTimeout(this._inactiveTimeout);
|
||||||
|
this._inactiveTimeout = null;
|
||||||
|
}
|
||||||
|
this.removeAttribute("inactive");
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
</implementation>
|
||||||
|
|
||||||
|
<handlers>
|
||||||
|
<handler event="DOMMenuBarActive" action="this._setActive();"/>
|
||||||
|
<handler event="popupshowing" action="this._setActive();"/>
|
||||||
|
<handler event="mousedown" button="2" action="this._contextMenuListener.init(event);"/>
|
||||||
|
<handler event="DOMMenuBarInactive"><![CDATA[
|
||||||
|
if (!this._contextMenuListener.active)
|
||||||
|
this._setInactiveAsync();
|
||||||
|
]]></handler>
|
||||||
|
</handlers>
|
||||||
|
</binding>
|
||||||
|
|
||||||
|
<binding id="toolbar-drag"
|
||||||
|
extends="chrome://browser/content/customizableui/toolbar.xml#toolbar">
|
||||||
|
<implementation>
|
||||||
|
<field name="_dragBindingAlive">true</field>
|
||||||
|
<constructor><![CDATA[
|
||||||
|
if (!this._draggableStarted) {
|
||||||
|
this._draggableStarted = true;
|
||||||
|
try {
|
||||||
|
let tmp = {};
|
||||||
|
Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
|
||||||
|
let draggableThis = new tmp.WindowDraggingElement(this);
|
||||||
|
draggableThis.mouseDownCheck = function(e) {
|
||||||
|
return this._dragBindingAlive;
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
]]></constructor>
|
||||||
|
</implementation>
|
||||||
|
</binding>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- This is a peculiar binding. It is here to deal with overlayed/inserted add-on content,
|
||||||
|
and immediately direct such content elsewhere. -->
|
||||||
|
<binding id="addonbar-delegating">
|
||||||
|
<implementation>
|
||||||
|
<constructor><![CDATA[
|
||||||
|
// Reading these immediately so nobody messes with them anymore:
|
||||||
|
this._delegatingToolbar = this.getAttribute("toolbar-delegate");
|
||||||
|
// Leaving those in here to unbreak some code:
|
||||||
|
if (document.readyState == "complete") {
|
||||||
|
this._init();
|
||||||
|
} else {
|
||||||
|
// Need to wait until XUL overlays are loaded. See bug 554279.
|
||||||
|
let self = this;
|
||||||
|
document.addEventListener("readystatechange", function onReadyStateChange() {
|
||||||
|
if (document.readyState != "complete")
|
||||||
|
return;
|
||||||
|
document.removeEventListener("readystatechange", onReadyStateChange, false);
|
||||||
|
self._init();
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
]]></constructor>
|
||||||
|
|
||||||
|
<method name="_init">
|
||||||
|
<body><![CDATA[
|
||||||
|
// Searching for the toolbox palette in the toolbar binding because
|
||||||
|
// toolbars are constructed first.
|
||||||
|
let toolbox = this.toolbox;
|
||||||
|
if (toolbox && !toolbox.palette) {
|
||||||
|
for (let node of toolbox.children) {
|
||||||
|
if (node.localName == "toolbarpalette") {
|
||||||
|
// Hold on to the palette but remove it from the document.
|
||||||
|
toolbox.palette = node;
|
||||||
|
toolbox.removeChild(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass the current set of children for comparison with placements:
|
||||||
|
let children = [node.id for (node of this.childNodes)
|
||||||
|
if (node.getAttribute("skipintoolbarset") != "true" && node.id)];
|
||||||
|
CustomizableUI.registerToolbarNode(this, children);
|
||||||
|
this.evictNodes();
|
||||||
|
// We can't easily use |this| or strong bindings for the observer fn here
|
||||||
|
// because that creates leaky circular references when the node goes away,
|
||||||
|
// and XBL destructors are unreliable.
|
||||||
|
let mutationObserver = new MutationObserver(function(mutations) {
|
||||||
|
if (!mutations.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let toolbar = mutations[0].target;
|
||||||
|
// Can't use our own attribute because we might not have one if we're set to
|
||||||
|
// collapsed
|
||||||
|
let areCustomizing = toolbar.ownerDocument.documentElement.getAttribute("customizing");
|
||||||
|
if (!toolbar._isModifying && !areCustomizing) {
|
||||||
|
toolbar.evictNodes();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mutationObserver.observe(this, {childList: true});
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
<method name="evictNodes">
|
||||||
|
<body><![CDATA[
|
||||||
|
this._isModifying = true;
|
||||||
|
let i = this.childNodes.length;
|
||||||
|
while (i--) {
|
||||||
|
let node = this.childNodes[i];
|
||||||
|
if (this.childNodes[i].id) {
|
||||||
|
this.evictNode(this.childNodes[i]);
|
||||||
|
} else {
|
||||||
|
node.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._isModifying = false;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
<method name="evictNode">
|
||||||
|
<parameter name="aNode"/>
|
||||||
|
<body>
|
||||||
|
<![CDATA[
|
||||||
|
if (this._whiteListed.has(aNode.id) || CustomizableUI.isSpecialWidget(aNode.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const kItemMaxWidth = 100;
|
||||||
|
let oldParent = aNode.parentNode;
|
||||||
|
|
||||||
|
try {
|
||||||
|
aNode.setAttribute("removable", "true");
|
||||||
|
|
||||||
|
let nodeWidth = aNode.getBoundingClientRect().width;
|
||||||
|
if (nodeWidth == 0 || nodeWidth > kItemMaxWidth) {
|
||||||
|
throw new Error(aNode.id + " is too big (" + nodeWidth +
|
||||||
|
"px wide), moving to the palette");
|
||||||
|
}
|
||||||
|
CustomizableUI.addWidgetToArea(aNode.id, this._delegatingToolbar);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
// This will throw if the node is too big, or can't be moved there for
|
||||||
|
// some reason. Try to remove it anyway:
|
||||||
|
try {
|
||||||
|
CustomizableUI.removeWidgetFromArea(aNode.id);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
aNode.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surprise: addWidgetToArea(palette) will get you nothing if the palette
|
||||||
|
// is not constructed yet. Fix:
|
||||||
|
if (aNode.parentNode == oldParent) {
|
||||||
|
let palette = this.toolbox.palette;
|
||||||
|
if (palette && oldParent != palette) {
|
||||||
|
palette.appendChild(aNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
<method name="insertItem">
|
||||||
|
<parameter name="aId"/>
|
||||||
|
<parameter name="aBeforeElt"/>
|
||||||
|
<parameter name="aWrapper"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
if (aWrapper) {
|
||||||
|
Cu.reportError("Can't insert " + aId + ": using insertItem " +
|
||||||
|
"no longer supports wrapper elements.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let widget = CustomizableUI.getWidget(aId);
|
||||||
|
widget = widget && widget.forWindow(window);
|
||||||
|
let node = widget && widget.node;
|
||||||
|
if (!node) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isModifying = true;
|
||||||
|
// Temporarily add it here so it can have a width, then ditch it:
|
||||||
|
this.appendChild(node);
|
||||||
|
this.evictNode(node);
|
||||||
|
this._isModifying = false;
|
||||||
|
// We will now have moved stuff around; kick off an aftercustomization event
|
||||||
|
// so add-ons know we've just moved their stuff:
|
||||||
|
if (window.gCustomizeMode) {
|
||||||
|
window.gCustomizeMode.dispatchToolboxEvent("aftercustomization");
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
<property name="customizationTarget" readonly="true">
|
||||||
|
<getter><![CDATA[
|
||||||
|
return this;
|
||||||
|
]]></getter>
|
||||||
|
</property>
|
||||||
|
<property name="currentSet">
|
||||||
|
<getter><![CDATA[
|
||||||
|
return [node.id for (node of this.children)].join(',');
|
||||||
|
]]></getter>
|
||||||
|
<setter><![CDATA[
|
||||||
|
let v = val.split(',');
|
||||||
|
let newButtons = v.filter(x => x && (!this._whiteListed.has(x) &&
|
||||||
|
!CustomizableUI.isSpecialWidget(x) &&
|
||||||
|
!this._currentSetMigrated.has(x)));
|
||||||
|
for (x of newButtons) {
|
||||||
|
this._currentSetMigrated.add(x);
|
||||||
|
this.insertItem(x);
|
||||||
|
}
|
||||||
|
]]></setter>
|
||||||
|
</property>
|
||||||
|
<property name="toolbox" readonly="true">
|
||||||
|
<getter><![CDATA[
|
||||||
|
if (!this._toolbox && this.parentNode &&
|
||||||
|
this.parentNode.localName == "toolbox") {
|
||||||
|
this._toolbox = this.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._toolbox;
|
||||||
|
]]></getter>
|
||||||
|
</property>
|
||||||
|
<field name="_whiteListed" readonly="true">new Set(["addonbar-closebutton", "status-bar"])</field>
|
||||||
|
<field name="_isModifying">false</field>
|
||||||
|
<field name="_currentSetMigrated">new Set()</field>
|
||||||
|
</implementation>
|
||||||
|
</binding>
|
||||||
|
</bindings>
|
12
browser/components/customizableui/moz.build
Normal file
12
browser/components/customizableui/moz.build
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# -*- 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/.
|
||||||
|
|
||||||
|
PARALLEL_DIRS += [
|
||||||
|
'content',
|
||||||
|
'src',
|
||||||
|
]
|
||||||
|
|
||||||
|
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
2605
browser/components/customizableui/src/CustomizableUI.jsm
Normal file
2605
browser/components/customizableui/src/CustomizableUI.jsm
Normal file
File diff suppressed because it is too large
Load Diff
774
browser/components/customizableui/src/CustomizableWidgets.jsm
Normal file
774
browser/components/customizableui/src/CustomizableWidgets.jsm
Normal file
@ -0,0 +1,774 @@
|
|||||||
|
/* 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";
|
||||||
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ["CustomizableWidgets"];
|
||||||
|
|
||||||
|
Cu.import("resource:///modules/CustomizableUI.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||||
|
"resource://gre/modules/PlacesUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils",
|
||||||
|
"resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "CharsetManager",
|
||||||
|
"@mozilla.org/charset-converter-manager;1",
|
||||||
|
"nsICharsetConverterManager");
|
||||||
|
|
||||||
|
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
|
||||||
|
const kWidePanelItemClass = "panel-wide-item";
|
||||||
|
|
||||||
|
let gModuleName = "[CustomizableWidgets]";
|
||||||
|
#include logging.js
|
||||||
|
|
||||||
|
function setAttributes(aNode, aAttrs) {
|
||||||
|
for (let [name, value] of Iterator(aAttrs)) {
|
||||||
|
if (!value) {
|
||||||
|
if (aNode.hasAttribute(name))
|
||||||
|
aNode.removeAttribute(name);
|
||||||
|
} else {
|
||||||
|
if (name == "label" || name == "tooltiptext")
|
||||||
|
value = CustomizableUI.getLocalizedProperty({id: aAttrs.id}, name);
|
||||||
|
aNode.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCombinedWidgetStyle(aNode, aArea, aModifyAutoclose) {
|
||||||
|
let inPanel = (aArea == CustomizableUI.AREA_PANEL);
|
||||||
|
let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
|
||||||
|
if (!aArea)
|
||||||
|
cls = null;
|
||||||
|
let attrs = {class: cls};
|
||||||
|
if (aModifyAutoclose) {
|
||||||
|
attrs.noautoclose = inPanel ? true : null;
|
||||||
|
}
|
||||||
|
for (let i = 0, l = aNode.childNodes.length; i < l; ++i) {
|
||||||
|
setAttributes(aNode.childNodes[i], attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomizableWidgets = [{
|
||||||
|
id: "history-panelmenu",
|
||||||
|
type: "view",
|
||||||
|
viewId: "PanelUI-history",
|
||||||
|
shortcutId: "key_gotoHistory",
|
||||||
|
removable: true,
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onViewShowing: function(aEvent) {
|
||||||
|
// Populate our list of history
|
||||||
|
const kMaxResults = 15;
|
||||||
|
let doc = aEvent.detail.ownerDocument;
|
||||||
|
|
||||||
|
let options = PlacesUtils.history.getNewQueryOptions();
|
||||||
|
options.excludeQueries = true;
|
||||||
|
options.includeHidden = false;
|
||||||
|
options.resultType = options.RESULTS_AS_URI;
|
||||||
|
options.queryType = options.QUERY_TYPE_HISTORY;
|
||||||
|
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
|
||||||
|
options.maxResults = kMaxResults;
|
||||||
|
let query = PlacesUtils.history.getNewQuery();
|
||||||
|
|
||||||
|
let items = doc.getElementById("PanelUI-historyItems");
|
||||||
|
// Clear previous history items.
|
||||||
|
while (items.firstChild) {
|
||||||
|
items.removeChild(items.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||||
|
.asyncExecuteLegacyQueries([query], 1, options, {
|
||||||
|
handleResult: function (aResultSet) {
|
||||||
|
let onHistoryVisit = function (aUri, aEvent, aItem) {
|
||||||
|
doc.defaultView.openUILink(aUri, aEvent);
|
||||||
|
CustomizableUI.hidePanelForNode(aItem);
|
||||||
|
};
|
||||||
|
let fragment = doc.createDocumentFragment();
|
||||||
|
for (let row, i = 0; (row = aResultSet.getNextRow()); i++) {
|
||||||
|
try {
|
||||||
|
let uri = row.getResultByIndex(1);
|
||||||
|
let title = row.getResultByIndex(2);
|
||||||
|
let icon = row.getResultByIndex(6);
|
||||||
|
|
||||||
|
let item = doc.createElementNS(kNSXUL, "toolbarbutton");
|
||||||
|
item.setAttribute("label", title || uri);
|
||||||
|
item.setAttribute("tabindex", "0");
|
||||||
|
item.addEventListener("command", function (aEvent) {
|
||||||
|
onHistoryVisit(uri, aEvent, item);
|
||||||
|
});
|
||||||
|
item.addEventListener("click", function (aEvent) {
|
||||||
|
onHistoryVisit(uri, aEvent, item);
|
||||||
|
});
|
||||||
|
if (icon)
|
||||||
|
item.setAttribute("image", "moz-anno:favicon:" + icon);
|
||||||
|
fragment.appendChild(item);
|
||||||
|
} catch (e) {
|
||||||
|
ERROR("Error while showing history subview: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items.appendChild(fragment);
|
||||||
|
},
|
||||||
|
handleError: function (aError) {
|
||||||
|
LOG("History view tried to show but had an error: " + aError);
|
||||||
|
},
|
||||||
|
handleCompletion: function (aReason) {
|
||||||
|
LOG("History view is being shown!");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let recentlyClosedTabs = doc.getElementById("PanelUI-recentlyClosedTabs");
|
||||||
|
while (recentlyClosedTabs.firstChild) {
|
||||||
|
recentlyClosedTabs.removeChild(recentlyClosedTabs.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
let recentlyClosedWindows = doc.getElementById("PanelUI-recentlyClosedWindows");
|
||||||
|
while (recentlyClosedWindows.firstChild) {
|
||||||
|
recentlyClosedWindows.removeChild(recentlyClosedWindows.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tabsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getTabsFragment(doc.defaultView, "toolbarbutton");
|
||||||
|
let separator = doc.getElementById("PanelUI-recentlyClosedTabs-separator");
|
||||||
|
separator.hidden = !tabsFragment.childElementCount;
|
||||||
|
recentlyClosedTabs.appendChild(tabsFragment);
|
||||||
|
|
||||||
|
let windowsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getWindowsFragment(doc.defaultView, "toolbarbutton");
|
||||||
|
separator = doc.getElementById("PanelUI-recentlyClosedWindows-separator");
|
||||||
|
separator.hidden = !windowsFragment.childElementCount;
|
||||||
|
recentlyClosedWindows.appendChild(windowsFragment);
|
||||||
|
},
|
||||||
|
onViewHiding: function(aEvent) {
|
||||||
|
LOG("History view is being hidden!");
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "privatebrowsing-button",
|
||||||
|
removable: true,
|
||||||
|
shortcutId: "key_privatebrowsing",
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onCommand: function(e) {
|
||||||
|
if (e.target && e.target.ownerDocument && e.target.ownerDocument.defaultView) {
|
||||||
|
let win = e.target.ownerDocument.defaultView;
|
||||||
|
if (typeof win.OpenBrowserWindow == "function") {
|
||||||
|
win.OpenBrowserWindow({private: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "save-page-button",
|
||||||
|
removable: true,
|
||||||
|
shortcutId: "key_savePage",
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onCommand: function(aEvent) {
|
||||||
|
let win = aEvent.target &&
|
||||||
|
aEvent.target.ownerDocument &&
|
||||||
|
aEvent.target.ownerDocument.defaultView;
|
||||||
|
if (win && typeof win.saveDocument == "function") {
|
||||||
|
win.saveDocument(win.content.document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "find-button",
|
||||||
|
removable: true,
|
||||||
|
shortcutId: "key_find",
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onCommand: function(aEvent) {
|
||||||
|
let win = aEvent.target &&
|
||||||
|
aEvent.target.ownerDocument &&
|
||||||
|
aEvent.target.ownerDocument.defaultView;
|
||||||
|
if (win && win.gFindBar) {
|
||||||
|
win.gFindBar.onFindCommand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "open-file-button",
|
||||||
|
removable: true,
|
||||||
|
shortcutId: "openFileKb",
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onCommand: function(aEvent) {
|
||||||
|
let win = aEvent.target
|
||||||
|
&& aEvent.target.ownerDocument
|
||||||
|
&& aEvent.target.ownerDocument.defaultView;
|
||||||
|
if (win && typeof win.BrowserOpenFileWindow == "function") {
|
||||||
|
win.BrowserOpenFileWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "developer-button",
|
||||||
|
type: "view",
|
||||||
|
viewId: "PanelUI-developer",
|
||||||
|
removable: true,
|
||||||
|
shortcutId: "key_devToolboxMenuItem",
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onViewShowing: function(aEvent) {
|
||||||
|
// Populate the subview with whatever menuitems are in the developer
|
||||||
|
// menu. We skip menu elements, because the menu panel has no way
|
||||||
|
// of dealing with those right now.
|
||||||
|
let doc = aEvent.target.ownerDocument;
|
||||||
|
let win = doc.defaultView;
|
||||||
|
|
||||||
|
let items = doc.getElementById("PanelUI-developerItems");
|
||||||
|
let menu = doc.getElementById("menuWebDeveloperPopup");
|
||||||
|
let attrs = ["oncommand", "onclick", "label", "key", "disabled",
|
||||||
|
"command"];
|
||||||
|
|
||||||
|
let fragment = doc.createDocumentFragment();
|
||||||
|
for (let node of menu.children) {
|
||||||
|
if (node.hidden)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let item;
|
||||||
|
if (node.localName == "menuseparator") {
|
||||||
|
item = doc.createElementNS(kNSXUL, "menuseparator");
|
||||||
|
} else if (node.localName == "menuitem") {
|
||||||
|
item = doc.createElementNS(kNSXUL, "toolbarbutton");
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (let attr of attrs) {
|
||||||
|
let attrVal = node.getAttribute(attr);
|
||||||
|
if (attrVal)
|
||||||
|
item.setAttribute(attr, attrVal);
|
||||||
|
}
|
||||||
|
item.setAttribute("tabindex", "0");
|
||||||
|
fragment.appendChild(item);
|
||||||
|
}
|
||||||
|
items.appendChild(fragment);
|
||||||
|
|
||||||
|
aEvent.target.addEventListener("command", win.PanelUI.onCommandHandler);
|
||||||
|
},
|
||||||
|
onViewHiding: function(aEvent) {
|
||||||
|
let doc = aEvent.target.ownerDocument;
|
||||||
|
let win = doc.defaultView;
|
||||||
|
let items = doc.getElementById("PanelUI-developerItems");
|
||||||
|
let parent = items.parentNode;
|
||||||
|
// We'll take the container out of the document before cleaning it out
|
||||||
|
// to avoid reflowing each time we remove something.
|
||||||
|
parent.removeChild(items);
|
||||||
|
|
||||||
|
while (items.firstChild) {
|
||||||
|
items.firstChild.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.appendChild(items);
|
||||||
|
aEvent.target.removeEventListener("command",
|
||||||
|
win.PanelUI.onCommandHandler);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "add-ons-button",
|
||||||
|
removable: true,
|
||||||
|
shortcutId: "key_openAddons",
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onCommand: function(aEvent) {
|
||||||
|
let win = aEvent.target &&
|
||||||
|
aEvent.target.ownerDocument &&
|
||||||
|
aEvent.target.ownerDocument.defaultView;
|
||||||
|
if (win && typeof win.BrowserOpenAddonsMgr == "function") {
|
||||||
|
win.BrowserOpenAddonsMgr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "preferences-button",
|
||||||
|
removable: true,
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
#ifdef XP_WIN
|
||||||
|
label: "preferences-button.labelWin",
|
||||||
|
tooltiptext: "preferences-button.tooltipWin",
|
||||||
|
#endif
|
||||||
|
onCommand: function(aEvent) {
|
||||||
|
let win = aEvent.target &&
|
||||||
|
aEvent.target.ownerDocument &&
|
||||||
|
aEvent.target.ownerDocument.defaultView;
|
||||||
|
if (win && typeof win.openPreferences == "function") {
|
||||||
|
win.openPreferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "zoom-controls",
|
||||||
|
type: "custom",
|
||||||
|
removable: true,
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onBuild: function(aDocument) {
|
||||||
|
const kPanelId = "PanelUI-popup";
|
||||||
|
let inPanel = (this.currentArea == CustomizableUI.AREA_PANEL);
|
||||||
|
let noautoclose = inPanel ? "true" : null;
|
||||||
|
let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
|
||||||
|
|
||||||
|
if (!this.currentArea)
|
||||||
|
cls = null;
|
||||||
|
|
||||||
|
let buttons = [{
|
||||||
|
id: "zoom-out-button",
|
||||||
|
noautoclose: noautoclose,
|
||||||
|
command: "cmd_fullZoomReduce",
|
||||||
|
class: cls,
|
||||||
|
label: true,
|
||||||
|
tooltiptext: true
|
||||||
|
}, {
|
||||||
|
id: "zoom-reset-button",
|
||||||
|
noautoclose: noautoclose,
|
||||||
|
command: "cmd_fullZoomReset",
|
||||||
|
class: cls,
|
||||||
|
tooltiptext: true
|
||||||
|
}, {
|
||||||
|
id: "zoom-in-button",
|
||||||
|
noautoclose: noautoclose,
|
||||||
|
command: "cmd_fullZoomEnlarge",
|
||||||
|
class: cls,
|
||||||
|
label: true,
|
||||||
|
tooltiptext: true
|
||||||
|
}];
|
||||||
|
|
||||||
|
let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
|
||||||
|
node.setAttribute("id", "zoom-controls");
|
||||||
|
node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
|
||||||
|
// Set this as an attribute in addition to the property to make sure we can style correctly.
|
||||||
|
node.setAttribute("removable", "true");
|
||||||
|
node.classList.add("chromeclass-toolbar-additional");
|
||||||
|
node.classList.add("toolbaritem-combined-buttons");
|
||||||
|
node.classList.add(kWidePanelItemClass);
|
||||||
|
|
||||||
|
buttons.forEach(function(aButton) {
|
||||||
|
let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton");
|
||||||
|
setAttributes(btnNode, aButton);
|
||||||
|
if (inPanel)
|
||||||
|
btnNode.setAttribute("tabindex", "0");
|
||||||
|
node.appendChild(btnNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
// The middle node is the 'Reset Zoom' button.
|
||||||
|
let zoomResetButton = node.childNodes[1];
|
||||||
|
let window = aDocument.defaultView;
|
||||||
|
function updateZoomResetButton() {
|
||||||
|
//XXXgijs in some tests we get called very early, and there's no docShell on the
|
||||||
|
// tabbrowser. This breaks the zoom toolkit code (see bug 897410). Don't let that happen:
|
||||||
|
let zoomFactor = 100;
|
||||||
|
if (window.gBrowser.docShell) {
|
||||||
|
zoomFactor = Math.floor(window.ZoomManager.zoom * 100);
|
||||||
|
}
|
||||||
|
zoomResetButton.setAttribute("label", CustomizableUI.getLocalizedProperty(
|
||||||
|
buttons[1], "label", [zoomFactor]
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register ourselves with the service so we know when the zoom prefs change.
|
||||||
|
Services.obs.addObserver(updateZoomResetButton, "browser-fullZoom:zoomChange", false);
|
||||||
|
Services.obs.addObserver(updateZoomResetButton, "browser-fullZoom:zoomReset", false);
|
||||||
|
|
||||||
|
if (inPanel && this.currentArea) {
|
||||||
|
let panel = aDocument.getElementById(kPanelId);
|
||||||
|
panel.addEventListener("popupshowing", updateZoomResetButton);
|
||||||
|
} else {
|
||||||
|
updateZoomResetButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
let listener = {
|
||||||
|
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
updateCombinedWidgetStyle(node, aArea, true);
|
||||||
|
updateZoomResetButton();
|
||||||
|
|
||||||
|
if (aArea == CustomizableUI.AREA_PANEL) {
|
||||||
|
let panel = aDocument.getElementById(kPanelId);
|
||||||
|
panel.addEventListener("popupshowing", updateZoomResetButton);
|
||||||
|
}
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetRemoved: function(aWidgetId, aPrevArea) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (aPrevArea == CustomizableUI.AREA_PANEL) {
|
||||||
|
let panel = aDocument.getElementById(kPanelId);
|
||||||
|
panel.removeEventListener("popupshowing", updateZoomResetButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a widget is demoted to the palette ('removed'), it's visual
|
||||||
|
// style should change.
|
||||||
|
updateCombinedWidgetStyle(node, null, true);
|
||||||
|
updateZoomResetButton();
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetReset: function(aWidgetId) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
updateCombinedWidgetStyle(node, this.currentArea, true);
|
||||||
|
updateZoomResetButton();
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetMoved: function(aWidgetId, aArea) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
updateCombinedWidgetStyle(node, aArea, true);
|
||||||
|
updateZoomResetButton();
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetInstanceRemoved: function(aWidgetId, aDoc) {
|
||||||
|
if (aWidgetId != this.id || aDoc != aDocument)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CustomizableUI.removeListener(listener);
|
||||||
|
Services.obs.removeObserver(updateZoomResetButton, "browser-fullZoom:zoomChange");
|
||||||
|
Services.obs.removeObserver(updateZoomResetButton, "browser-fullZoom:zoomReset");
|
||||||
|
let panel = aDoc.getElementById(kPanelId);
|
||||||
|
panel.removeEventListener("popupshowing", updateZoomResetButton);
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetDrag: function(aWidgetId, aArea) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
aArea = aArea || this.currentArea;
|
||||||
|
updateCombinedWidgetStyle(node, aArea, true);
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
CustomizableUI.addListener(listener);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "edit-controls",
|
||||||
|
type: "custom",
|
||||||
|
removable: true,
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onBuild: function(aDocument) {
|
||||||
|
let inPanel = (this.currentArea == CustomizableUI.AREA_PANEL);
|
||||||
|
let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
|
||||||
|
|
||||||
|
if (!this.currentArea)
|
||||||
|
cls = null;
|
||||||
|
|
||||||
|
let buttons = [{
|
||||||
|
id: "cut-button",
|
||||||
|
command: "cmd_cut",
|
||||||
|
class: cls,
|
||||||
|
label: true,
|
||||||
|
tooltiptext: true
|
||||||
|
}, {
|
||||||
|
id: "copy-button",
|
||||||
|
command: "cmd_copy",
|
||||||
|
class: cls,
|
||||||
|
label: true,
|
||||||
|
tooltiptext: true
|
||||||
|
}, {
|
||||||
|
id: "paste-button",
|
||||||
|
command: "cmd_paste",
|
||||||
|
class: cls,
|
||||||
|
label: true,
|
||||||
|
tooltiptext: true
|
||||||
|
}];
|
||||||
|
|
||||||
|
let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
|
||||||
|
node.setAttribute("id", "edit-controls");
|
||||||
|
node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
|
||||||
|
// Set this as an attribute in addition to the property to make sure we can style correctly.
|
||||||
|
node.setAttribute("removable", "true");
|
||||||
|
node.classList.add("chromeclass-toolbar-additional");
|
||||||
|
node.classList.add("toolbaritem-combined-buttons");
|
||||||
|
node.classList.add(kWidePanelItemClass);
|
||||||
|
|
||||||
|
buttons.forEach(function(aButton) {
|
||||||
|
let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton");
|
||||||
|
setAttributes(btnNode, aButton);
|
||||||
|
if (inPanel)
|
||||||
|
btnNode.setAttribute("tabindex", "0");
|
||||||
|
node.appendChild(btnNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
let listener = {
|
||||||
|
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
updateCombinedWidgetStyle(node, aArea);
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetRemoved: function(aWidgetId, aPrevArea) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
// When a widget is demoted to the palette ('removed'), it's visual
|
||||||
|
// style should change.
|
||||||
|
updateCombinedWidgetStyle(node);
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetReset: function(aWidgetId) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
updateCombinedWidgetStyle(node, this.currentArea);
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetMoved: function(aWidgetId, aArea) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
updateCombinedWidgetStyle(node, aArea);
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetInstanceRemoved: function(aWidgetId, aDoc) {
|
||||||
|
if (aWidgetId != this.id || aDoc != aDocument)
|
||||||
|
return;
|
||||||
|
CustomizableUI.removeListener(listener);
|
||||||
|
}.bind(this),
|
||||||
|
|
||||||
|
onWidgetDrag: function(aWidgetId, aArea) {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
aArea = aArea || this.currentArea;
|
||||||
|
updateCombinedWidgetStyle(node, aArea);
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
CustomizableUI.addListener(listener);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "feed-button",
|
||||||
|
type: "view",
|
||||||
|
viewId: "PanelUI-feeds",
|
||||||
|
removable: true,
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
onClick: function(aEvent) {
|
||||||
|
let win = aEvent.target.ownerDocument.defaultView;
|
||||||
|
let feeds = win.gBrowser.selectedBrowser.feeds;
|
||||||
|
|
||||||
|
// Here, we only care about the case where we have exactly 1 feed and the
|
||||||
|
// user clicked...
|
||||||
|
let isClick = (aEvent.button == 0 || aEvent.button == 1);
|
||||||
|
if (feeds && feeds.length == 1 && isClick) {
|
||||||
|
aEvent.preventDefault();
|
||||||
|
aEvent.stopPropagation();
|
||||||
|
win.FeedHandler.subscribeToFeed(feeds[0].href, aEvent);
|
||||||
|
CustomizableUI.hidePanelForNode(aEvent.target);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onViewShowing: function(aEvent) {
|
||||||
|
let doc = aEvent.detail.ownerDocument;
|
||||||
|
let container = doc.getElementById("PanelUI-feeds");
|
||||||
|
let gotView = doc.defaultView.FeedHandler.buildFeedList(container, true);
|
||||||
|
|
||||||
|
// For no feeds or only a single one, don't show the panel.
|
||||||
|
if (!gotView) {
|
||||||
|
aEvent.preventDefault();
|
||||||
|
aEvent.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCreated: function(node) {
|
||||||
|
let win = node.ownerDocument.defaultView;
|
||||||
|
let selectedBrowser = win.gBrowser.selectedBrowser;
|
||||||
|
let feeds = selectedBrowser && selectedBrowser.feeds;
|
||||||
|
if (!feeds || !feeds.length) {
|
||||||
|
node.setAttribute("disabled", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "characterencoding-button",
|
||||||
|
type: "view",
|
||||||
|
viewId: "PanelUI-characterEncodingView",
|
||||||
|
removable: true,
|
||||||
|
defaultArea: CustomizableUI.AREA_PANEL,
|
||||||
|
maybeDisableMenu: function(aDocument) {
|
||||||
|
let window = aDocument.defaultView;
|
||||||
|
return !(window.gBrowser &&
|
||||||
|
window.gBrowser.docShell &&
|
||||||
|
window.gBrowser.docShell.mayEnableCharacterEncodingMenu);
|
||||||
|
},
|
||||||
|
getCharsetList: function(aSection, aDocument) {
|
||||||
|
let currCharset = aDocument.defaultView.content.document.characterSet;
|
||||||
|
|
||||||
|
let list = "";
|
||||||
|
try {
|
||||||
|
let pref = "intl.charsetmenu.browser." + aSection;
|
||||||
|
list = Services.prefs.getComplexValue(pref,
|
||||||
|
Ci.nsIPrefLocalizedString).data;
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
list = list.trim();
|
||||||
|
if (!list)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
list = list.split(",");
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
for (let charset of list) {
|
||||||
|
charset = charset.trim();
|
||||||
|
|
||||||
|
let notForBrowser = false;
|
||||||
|
try {
|
||||||
|
notForBrowser = CharsetManager.getCharsetData(charset,
|
||||||
|
"notForBrowser");
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (notForBrowser)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let title = charset;
|
||||||
|
try {
|
||||||
|
title = CharsetManager.getCharsetTitle(charset);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
items.push({value: charset, name: title, current: charset == currCharset});
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
},
|
||||||
|
getAutoDetectors: function(aDocument) {
|
||||||
|
let detectorEnum = CharsetManager.GetCharsetDetectorList();
|
||||||
|
let currDetector;
|
||||||
|
try {
|
||||||
|
currDetector = Services.prefs.getComplexValue(
|
||||||
|
"intl.charset.detector", Ci.nsIPrefLocalizedString).data;
|
||||||
|
} catch (e) {}
|
||||||
|
if (!currDetector)
|
||||||
|
currDetector = "off";
|
||||||
|
currDetector = "chardet." + currDetector;
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
|
||||||
|
while (detectorEnum.hasMore()) {
|
||||||
|
let detector = detectorEnum.getNext();
|
||||||
|
|
||||||
|
let title = detector;
|
||||||
|
try {
|
||||||
|
title = CharsetManager.getCharsetTitle(detector);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
items.push({value: detector, name: title, current: detector == currDetector});
|
||||||
|
}
|
||||||
|
|
||||||
|
items.sort((aItem1, aItem2) => {
|
||||||
|
return aItem1.name.localeCompare(aItem2.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
},
|
||||||
|
populateList: function(aDocument, aContainerId, aSection) {
|
||||||
|
let containerElem = aDocument.getElementById(aContainerId);
|
||||||
|
|
||||||
|
while (containerElem.firstChild) {
|
||||||
|
containerElem.removeChild(containerElem.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
containerElem.addEventListener("command", this.onCommand, false);
|
||||||
|
|
||||||
|
let list = [];
|
||||||
|
if (aSection == "autodetect") {
|
||||||
|
list = this.getAutoDetectors(aDocument);
|
||||||
|
} else if (aSection == "browser") {
|
||||||
|
let staticList = this.getCharsetList("static", aDocument);
|
||||||
|
let cacheList = this.getCharsetList("cache", aDocument);
|
||||||
|
// Combine lists, and de-duplicate.
|
||||||
|
let checkedIn = new Set();
|
||||||
|
for (let item of staticList.concat(cacheList)) {
|
||||||
|
let itemName = item.name.toLowerCase();
|
||||||
|
if (!checkedIn.has(itemName)) {
|
||||||
|
list.push(item);
|
||||||
|
checkedIn.add(itemName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the appearance of the buttons when it's not possible to
|
||||||
|
// customize encoding.
|
||||||
|
let disabled = this.maybeDisableMenu(aDocument);
|
||||||
|
for (let item of list) {
|
||||||
|
let elem = aDocument.createElementNS(kNSXUL, "toolbarbutton");
|
||||||
|
elem.setAttribute("label", item.name);
|
||||||
|
elem.section = aSection;
|
||||||
|
elem.value = item.value;
|
||||||
|
if (item.current)
|
||||||
|
elem.setAttribute("current", "true");
|
||||||
|
if (disabled)
|
||||||
|
elem.setAttribute("disabled", "true");
|
||||||
|
containerElem.appendChild(elem);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onViewShowing: function(aEvent) {
|
||||||
|
let document = aEvent.target.ownerDocument;
|
||||||
|
|
||||||
|
this.populateList(document,
|
||||||
|
"PanelUI-characterEncodingView-customlist",
|
||||||
|
"browser");
|
||||||
|
this.populateList(document,
|
||||||
|
"PanelUI-characterEncodingView-autodetect",
|
||||||
|
"autodetect");
|
||||||
|
},
|
||||||
|
onCommand: function(aEvent) {
|
||||||
|
let node = aEvent.target;
|
||||||
|
if (!node.hasAttribute || !node.section) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomizableUI.hidePanelForNode(node);
|
||||||
|
let window = node.ownerDocument.defaultView;
|
||||||
|
let section = node.section;
|
||||||
|
let value = node.value;
|
||||||
|
|
||||||
|
// The behavior as implemented here is directly based off of the
|
||||||
|
// `MultiplexHandler()` method in browser.js.
|
||||||
|
if (section == "browser") {
|
||||||
|
window.BrowserSetForcedCharacterSet(value);
|
||||||
|
} else if (section == "autodetect") {
|
||||||
|
value = value.replace(/^chardet\./, "");
|
||||||
|
if (value == "off") {
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
// Set the detector pref.
|
||||||
|
try {
|
||||||
|
let str = Cc["@mozilla.org/supports-string;1"]
|
||||||
|
.createInstance(Ci.nsISupportsString);
|
||||||
|
str.data = value;
|
||||||
|
Services.prefs.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError("Failed to set the intl.charset.detector preference.");
|
||||||
|
}
|
||||||
|
// Prepare a browser page reload with a changed charset.
|
||||||
|
window.BrowserCharsetReload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCreated: function(aNode) {
|
||||||
|
const kPanelId = "PanelUI-popup";
|
||||||
|
let document = aNode.ownerDocument;
|
||||||
|
|
||||||
|
let updateButton = () => {
|
||||||
|
if (this.maybeDisableMenu(document))
|
||||||
|
aNode.setAttribute("disabled", "true");
|
||||||
|
else
|
||||||
|
aNode.removeAttribute("disabled");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.currentArea == CustomizableUI.AREA_PANEL) {
|
||||||
|
let panel = document.getElementById(kPanelId);
|
||||||
|
panel.addEventListener("popupshowing", updateButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
let listener = {
|
||||||
|
onWidgetAdded: (aWidgetId, aArea) => {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
if (aArea == CustomizableUI.AREA_PANEL) {
|
||||||
|
let panel = document.getElementById(kPanelId);
|
||||||
|
panel.addEventListener("popupshowing", updateButton);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onWidgetRemoved: (aWidgetId, aPrevArea) => {
|
||||||
|
if (aWidgetId != this.id)
|
||||||
|
return;
|
||||||
|
if (aPrevArea == CustomizableUI.AREA_PANEL) {
|
||||||
|
let panel = document.getElementById(kPanelId);
|
||||||
|
panel.removeEventListener("popupshowing", updateButton);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onWidgetInstanceRemoved: (aWidgetId, aDoc) => {
|
||||||
|
if (aWidgetId != this.id || aDoc != document)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CustomizableUI.removeListener(listener);
|
||||||
|
let panel = aDoc.getElementById(kPanelId);
|
||||||
|
panel.removeEventListener("popupshowing", updateButton);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CustomizableUI.addListener(listener);
|
||||||
|
}
|
||||||
|
}];
|
1432
browser/components/customizableui/src/CustomizeMode.jsm
Normal file
1432
browser/components/customizableui/src/CustomizeMode.jsm
Normal file
File diff suppressed because it is too large
Load Diff
152
browser/components/customizableui/src/PanelWideWidgetTracker.jsm
Normal file
152
browser/components/customizableui/src/PanelWideWidgetTracker.jsm
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/* 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";
|
||||||
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ["PanelWideWidgetTracker"];
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
||||||
|
"resource:///modules/CustomizableUI.jsm");
|
||||||
|
|
||||||
|
let gModuleName = "[PanelWideWidgetTracker]";
|
||||||
|
#include logging.js
|
||||||
|
|
||||||
|
let gPanel = CustomizableUI.AREA_PANEL;
|
||||||
|
// We keep track of the widget placements for the panel locally:
|
||||||
|
let gPanelPlacements = [];
|
||||||
|
|
||||||
|
// All the wide widgets we know of:
|
||||||
|
let gWideWidgets = new Set();
|
||||||
|
// All the widgets we know of:
|
||||||
|
let gSeenWidgets = new Set();
|
||||||
|
|
||||||
|
// The class by which we recognize wide widgets:
|
||||||
|
const kWidePanelItemClass = "panel-wide-item";
|
||||||
|
|
||||||
|
// TODO(bug 885574): Merge this constant with the one in CustomizeMode.jsm,
|
||||||
|
// maybe just use a pref for this.
|
||||||
|
const kColumnsInMenuPanel = 3;
|
||||||
|
|
||||||
|
let PanelWideWidgetTracker = {
|
||||||
|
// Listeners used to validate panel contents whenever they change:
|
||||||
|
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
|
||||||
|
if (aArea == gPanel) {
|
||||||
|
gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
|
||||||
|
let moveForward = this.shouldMoveForward(aWidgetId, aPosition);
|
||||||
|
this.adjustWidgets(aWidgetId, moveForward);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onWidgetMoved: function(aWidgetId, aArea, aOldPosition, aNewPosition) {
|
||||||
|
if (aArea == gPanel) {
|
||||||
|
gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
|
||||||
|
let moveForward = this.shouldMoveForward(aWidgetId, aNewPosition);
|
||||||
|
this.adjustWidgets(aWidgetId, moveForward);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onWidgetRemoved: function(aWidgetId, aPrevArea) {
|
||||||
|
if (aPrevArea == gPanel) {
|
||||||
|
gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
|
||||||
|
let pos = gPanelPlacements.indexOf(aWidgetId);
|
||||||
|
this.adjustWidgets(aWidgetId, false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onWidgetReset: function(aWidgetId) {
|
||||||
|
gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
|
||||||
|
},
|
||||||
|
// Listener to keep abreast of any new nodes. We use the DOM one because
|
||||||
|
// we need access to the actual node's classlist, so we can't use the ones above.
|
||||||
|
// Furthermore, onWidgetCreated only fires for API-based widgets, not for XUL ones.
|
||||||
|
onWidgetAfterDOMChange: function(aNode, aNextNode, aContainer) {
|
||||||
|
if (!gSeenWidgets.has(aNode.id)) {
|
||||||
|
if (aNode.classList.contains(kWidePanelItemClass)) {
|
||||||
|
gWideWidgets.add(aNode.id);
|
||||||
|
}
|
||||||
|
gSeenWidgets.add(aNode.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// When widgets get destroyed, we remove them from our sets of stuff we care about:
|
||||||
|
onWidgetDestroyed: function(aWidgetId) {
|
||||||
|
gSeenWidgets.delete(aWidgetId);
|
||||||
|
gWideWidgets.delete(aWidgetId);
|
||||||
|
},
|
||||||
|
shouldMoveForward: function(aWidgetId, aPosition) {
|
||||||
|
let currentWidgetAtPosition = gPanelPlacements[aPosition + 1];
|
||||||
|
return gWideWidgets.has(currentWidgetAtPosition) && !gWideWidgets.has(aWidgetId);
|
||||||
|
},
|
||||||
|
adjustWidgets: function(aWidgetId, aMoveForwards) {
|
||||||
|
if (this.adjusting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.adjusting = true;
|
||||||
|
let widgetsAffected = [w for (w of gPanelPlacements) if (gWideWidgets.has(w))];
|
||||||
|
// If we're moving the wide widgets forwards (down/to the right in the panel)
|
||||||
|
// we want to start with the last widgets. Otherwise we move widgets over other wide
|
||||||
|
// widgets, which might mess up their order. Likewise, if moving backwards we should start with
|
||||||
|
// the first widget and work our way down/right from there.
|
||||||
|
let compareFn = aMoveForwards ? (function(a, b) a < b) : (function(a, b) a > b)
|
||||||
|
widgetsAffected.sort(function(a, b) compareFn(gPanelPlacements.indexOf(a),
|
||||||
|
gPanelPlacements.indexOf(b)));
|
||||||
|
for (let widget of widgetsAffected) {
|
||||||
|
this.adjustPosition(widget, aMoveForwards);
|
||||||
|
}
|
||||||
|
this.adjusting = false;
|
||||||
|
},
|
||||||
|
// This function is called whenever an item gets moved in the menu panel. It
|
||||||
|
// adjusts the position of widgets within the panel to prevent "gaps" between
|
||||||
|
// wide widgets that could be filled up with single column widgets
|
||||||
|
adjustPosition: function(aWidgetId, aMoveForwards) {
|
||||||
|
// Make sure that there are n % columns = 0 narrow buttons before the widget.
|
||||||
|
let placementIndex = gPanelPlacements.indexOf(aWidgetId);
|
||||||
|
let prevSiblingCount = 0;
|
||||||
|
let fixedPos = null;
|
||||||
|
while (placementIndex--) {
|
||||||
|
let thisWidgetId = gPanelPlacements[placementIndex];
|
||||||
|
if (gWideWidgets.has(thisWidgetId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let widgetWrapper = CustomizableUI.getWidget(gPanelPlacements[placementIndex]);
|
||||||
|
// This widget might not actually exist:
|
||||||
|
if (!widgetWrapper) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// This widget might still not actually exist:
|
||||||
|
if (widgetWrapper.provider == CustomizableUI.PROVIDER_XUL &&
|
||||||
|
widgetWrapper.instances.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or it might only be there some of the time:
|
||||||
|
if (widgetWrapper.provider == CustomizableUI.PROVIDER_API &&
|
||||||
|
widgetWrapper.showInPrivateBrowsing === false) {
|
||||||
|
if (!fixedPos) {
|
||||||
|
fixedPos = placementIndex;
|
||||||
|
} else {
|
||||||
|
fixedPos = Math.min(fixedPos, placementIndex);
|
||||||
|
}
|
||||||
|
// We want to position ourselves before this item:
|
||||||
|
prevSiblingCount = 0;
|
||||||
|
} else {
|
||||||
|
prevSiblingCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixedPos !== null || prevSiblingCount % kColumnsInMenuPanel) {
|
||||||
|
let desiredPos = (fixedPos !== null) ? fixedPos : gPanelPlacements.indexOf(aWidgetId);
|
||||||
|
let desiredChange = -(prevSiblingCount % kColumnsInMenuPanel);
|
||||||
|
if (aMoveForwards && fixedPos == null) {
|
||||||
|
// +1 because otherwise we'd count ourselves:
|
||||||
|
desiredChange = kColumnsInMenuPanel + desiredChange + 1;
|
||||||
|
}
|
||||||
|
desiredPos += desiredChange;
|
||||||
|
CustomizableUI.moveWidgetWithinArea(aWidgetId, desiredPos);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init: function() {
|
||||||
|
// Initialize our local placements copy and register the listener
|
||||||
|
gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
|
||||||
|
CustomizableUI.addListener(this);
|
||||||
|
},
|
||||||
|
};
|
66
browser/components/customizableui/src/ScrollbarSampler.jsm
Normal file
66
browser/components/customizableui/src/ScrollbarSampler.jsm
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* 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";
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ["ScrollbarSampler"];
|
||||||
|
|
||||||
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||||
|
"resource://gre/modules/Promise.jsm");
|
||||||
|
|
||||||
|
let gSystemScrollbarWidth = null;
|
||||||
|
|
||||||
|
this.ScrollbarSampler = {
|
||||||
|
getSystemScrollbarWidth: function() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
|
||||||
|
if (gSystemScrollbarWidth !== null) {
|
||||||
|
deferred.resolve(gSystemScrollbarWidth);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sampleSystemScrollbarWidth().then(function(systemScrollbarWidth) {
|
||||||
|
gSystemScrollbarWidth = systemScrollbarWidth;
|
||||||
|
deferred.resolve(gSystemScrollbarWidth);
|
||||||
|
});
|
||||||
|
return deferred.promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sampleSystemScrollbarWidth: function() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
let hwin = Services.appShell.hiddenDOMWindow;
|
||||||
|
let hdoc = hwin.document.documentElement;
|
||||||
|
let iframe = hwin.document.createElementNS("http://www.w3.org/1999/xhtml",
|
||||||
|
"html:iframe");
|
||||||
|
iframe.setAttribute("srcdoc", '<body style="overflow-y: scroll"></body>');
|
||||||
|
hdoc.appendChild(iframe);
|
||||||
|
|
||||||
|
let cwindow = iframe.contentWindow;
|
||||||
|
let utils = cwindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils);
|
||||||
|
|
||||||
|
cwindow.addEventListener("load", function onLoad(aEvent) {
|
||||||
|
cwindow.removeEventListener("load", onLoad);
|
||||||
|
let sbWidth = {};
|
||||||
|
try {
|
||||||
|
utils.getScrollbarSize(true, sbWidth, {});
|
||||||
|
} catch(e) {
|
||||||
|
Cu.reportError("Could not sample scrollbar size: " + e + " -- " +
|
||||||
|
e.stack);
|
||||||
|
sbWidth.value = 0;
|
||||||
|
}
|
||||||
|
// Minimum width of 10 so that we have enough padding:
|
||||||
|
sbWidth.value = Math.max(sbWidth.value, 10);
|
||||||
|
deferred.resolve(sbWidth.value);
|
||||||
|
iframe.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.freeze(this.ScrollbarSampler);
|
25
browser/components/customizableui/src/logging.js
Normal file
25
browser/components/customizableui/src/logging.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#if 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/. */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||||
|
"resource://gre/modules/devtools/Console.jsm");
|
||||||
|
|
||||||
|
let gDebug = false;
|
||||||
|
try {
|
||||||
|
gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
function LOG(...args) {
|
||||||
|
if (gDebug) {
|
||||||
|
args.unshift(gModuleName);
|
||||||
|
console.log.apply(console, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ERROR(...args) {
|
||||||
|
args.unshift(gModuleName);
|
||||||
|
console.error.apply(console, args);
|
||||||
|
}
|
16
browser/components/customizableui/src/moz.build
Normal file
16
browser/components/customizableui/src/moz.build
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# -*- 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 += [
|
||||||
|
'ScrollbarSampler.jsm',
|
||||||
|
]
|
||||||
|
|
||||||
|
EXTRA_PP_JS_MODULES += [
|
||||||
|
'CustomizableUI.jsm',
|
||||||
|
'CustomizableWidgets.jsm',
|
||||||
|
'CustomizeMode.jsm',
|
||||||
|
'PanelWideWidgetTracker.jsm',
|
||||||
|
]
|
36
browser/components/customizableui/test/browser.ini
Normal file
36
browser/components/customizableui/test/browser.ini
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
support-files =
|
||||||
|
head.js
|
||||||
|
|
||||||
|
[browser_873501_handle_specials.js]
|
||||||
|
[browser_876926_customize_mode_wrapping.js]
|
||||||
|
[browser_876944_customize_mode_create_destroy.js]
|
||||||
|
[browser_877006_missing_view.js]
|
||||||
|
[browser_877178_unregisterArea.js]
|
||||||
|
[browser_877447_skip_missing_ids.js]
|
||||||
|
[browser_878452_drag_to_panel.js]
|
||||||
|
[browser_880382_drag_wide_widgets_in_panel.js]
|
||||||
|
[browser_885530_showInPrivateBrowsing.js]
|
||||||
|
[browser_886323_buildArea_removable_nodes.js]
|
||||||
|
[browser_887438_currentset_shim.js]
|
||||||
|
[browser_888817_currentset_updating.js]
|
||||||
|
[browser_890140_orphaned_placeholders.js]
|
||||||
|
[browser_890262_destroyWidget_after_add_to_panel.js]
|
||||||
|
[browser_892955_isWidgetRemovable_for_removed_widgets.js]
|
||||||
|
[browser_892956_destroyWidget_defaultPlacements.js]
|
||||||
|
[browser_909779_overflow_toolbars_new_window.js]
|
||||||
|
[browser_913972_currentset_overflow.js]
|
||||||
|
|
||||||
|
[browser_914138_widget_API_overflowable_toolbar.js]
|
||||||
|
# Because of the specific widths, this test is fragile and won't necessarily pass on other platforms
|
||||||
|
run-if = os == "mac"
|
||||||
|
|
||||||
|
[browser_914863_disabled_help_quit_buttons.js]
|
||||||
|
[browser_918049_skipintoolbarset_dnd.js]
|
||||||
|
[browser_923857_customize_mode_event_wrapping_during_reset.js]
|
||||||
|
[browser_927717_customize_drag_empty_toolbar.js]
|
||||||
|
|
||||||
|
[browser_934113_menubar_removable.js]
|
||||||
|
# Because this test is about the menubar, it can't be run on mac
|
||||||
|
skip-if = os == "mac"
|
||||||
|
|
@ -0,0 +1,91 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kToolbarName = "test-specials-toolbar";
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Add a toolbar with two springs and the downloads button.",
|
||||||
|
run: function() {
|
||||||
|
// Create the toolbar with a single spring:
|
||||||
|
createToolbarWithPlacements(kToolbarName, ["spring"]);
|
||||||
|
ok(document.getElementById(kToolbarName), "Toolbar should be created.");
|
||||||
|
|
||||||
|
// Check it's there with a generated ID:
|
||||||
|
assertAreaPlacements(kToolbarName, [/customizableui-special-spring\d+/]);
|
||||||
|
let [springId] = getAreaWidgetIds(kToolbarName);
|
||||||
|
|
||||||
|
// Add a second spring, check if that's there and doesn't share IDs
|
||||||
|
CustomizableUI.addWidgetToArea("spring", kToolbarName);
|
||||||
|
assertAreaPlacements(kToolbarName, [springId,
|
||||||
|
/customizableui-special-spring\d+/]);
|
||||||
|
let [, spring2Id] = getAreaWidgetIds(kToolbarName);
|
||||||
|
|
||||||
|
isnot(springId, spring2Id, "Springs shouldn't have identical IDs.");
|
||||||
|
|
||||||
|
// Try moving the downloads button to this new toolbar, between the two springs:
|
||||||
|
CustomizableUI.addWidgetToArea("downloads-button", kToolbarName, 1);
|
||||||
|
assertAreaPlacements(kToolbarName, [springId, "downloads-button", spring2Id]);
|
||||||
|
},
|
||||||
|
teardown: removeCustomToolbars
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Add separators around the downloads button.",
|
||||||
|
run: function() {
|
||||||
|
createToolbarWithPlacements(kToolbarName, ["separator"]);
|
||||||
|
ok(document.getElementById(kToolbarName), "Toolbar should be created.");
|
||||||
|
|
||||||
|
// Check it's there with a generated ID:
|
||||||
|
assertAreaPlacements(kToolbarName, [/customizableui-special-separator\d+/]);
|
||||||
|
let [separatorId] = getAreaWidgetIds(kToolbarName);
|
||||||
|
|
||||||
|
CustomizableUI.addWidgetToArea("separator", kToolbarName);
|
||||||
|
assertAreaPlacements(kToolbarName, [separatorId,
|
||||||
|
/customizableui-special-separator\d+/]);
|
||||||
|
let [, separator2Id] = getAreaWidgetIds(kToolbarName);
|
||||||
|
|
||||||
|
isnot(separatorId, separator2Id, "Separator ids shouldn't be equal.");
|
||||||
|
|
||||||
|
CustomizableUI.addWidgetToArea("downloads-button", kToolbarName, 1);
|
||||||
|
assertAreaPlacements(kToolbarName, [separatorId, "downloads-button", separator2Id]);
|
||||||
|
},
|
||||||
|
teardown: removeCustomToolbars
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Add spacers around the downloads button.",
|
||||||
|
run: function() {
|
||||||
|
createToolbarWithPlacements(kToolbarName, ["spacer"]);
|
||||||
|
ok(document.getElementById(kToolbarName), "Toolbar should be created.");
|
||||||
|
|
||||||
|
// Check it's there with a generated ID:
|
||||||
|
assertAreaPlacements(kToolbarName, [/customizableui-special-spacer\d+/]);
|
||||||
|
let [spacerId] = getAreaWidgetIds(kToolbarName);
|
||||||
|
|
||||||
|
CustomizableUI.addWidgetToArea("spacer", kToolbarName);
|
||||||
|
assertAreaPlacements(kToolbarName, [spacerId,
|
||||||
|
/customizableui-special-spacer\d+/]);
|
||||||
|
let [, spacer2Id] = getAreaWidgetIds(kToolbarName);
|
||||||
|
|
||||||
|
isnot(spacerId, spacer2Id, "Spacer ids shouldn't be equal.");
|
||||||
|
|
||||||
|
CustomizableUI.addWidgetToArea("downloads-button", kToolbarName, 1);
|
||||||
|
assertAreaPlacements(kToolbarName, [spacerId, "downloads-button", spacer2Id]);
|
||||||
|
},
|
||||||
|
teardown: removeCustomToolbars
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
removeCustomToolbars();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
registerCleanupFunction(cleanup);
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,173 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kXULWidgetId = "sync-button";
|
||||||
|
const kAPIWidgetId = "feed-button";
|
||||||
|
const kPanel = CustomizableUI.AREA_PANEL;
|
||||||
|
const kToolbar = CustomizableUI.AREA_NAVBAR;
|
||||||
|
const kVisiblePalette = "customization-palette";
|
||||||
|
const kPlaceholderClass = "panel-customization-placeholder";
|
||||||
|
|
||||||
|
function checkWrapper(id) {
|
||||||
|
is(document.querySelectorAll("#wrapper-" + id).length, 1, "There should be exactly 1 wrapper for " + id + " in the customizing window.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let move = {
|
||||||
|
"drag": function(id, target) {
|
||||||
|
let targetNode = document.getElementById(target);
|
||||||
|
if (targetNode.customizationTarget) {
|
||||||
|
targetNode = targetNode.customizationTarget;
|
||||||
|
}
|
||||||
|
simulateItemDrag(document.getElementById(id), targetNode);
|
||||||
|
},
|
||||||
|
"dragToItem": function(id, target) {
|
||||||
|
let targetNode = document.getElementById(target);
|
||||||
|
if (targetNode.customizationTarget) {
|
||||||
|
targetNode = targetNode.customizationTarget;
|
||||||
|
}
|
||||||
|
let items = targetNode.querySelectorAll("toolbarpaletteitem:not(." + kPlaceholderClass + ")");
|
||||||
|
if (target == kPanel) {
|
||||||
|
targetNode = items[items.length - 1];
|
||||||
|
} else {
|
||||||
|
targetNode = items[0];
|
||||||
|
}
|
||||||
|
simulateItemDrag(document.getElementById(id), targetNode);
|
||||||
|
},
|
||||||
|
"API": function(id, target) {
|
||||||
|
if (target == kVisiblePalette) {
|
||||||
|
return CustomizableUI.removeWidgetFromArea(id);
|
||||||
|
}
|
||||||
|
return CustomizableUI.addWidgetToArea(id, target, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLast(containerId, defaultPlacements, id) {
|
||||||
|
assertAreaPlacements(containerId, defaultPlacements.concat([id]));
|
||||||
|
is(document.getElementById(containerId).customizationTarget.lastChild.firstChild.id, id,
|
||||||
|
"Widget " + id + " should be in " + containerId + " in customizing window.");
|
||||||
|
is(otherWin.document.getElementById(containerId).customizationTarget.lastChild.id, id,
|
||||||
|
"Widget " + id + " should be in " + containerId + " in other window.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFirst(containerId, defaultPlacements, id) {
|
||||||
|
assertAreaPlacements(containerId, [id].concat(defaultPlacements));
|
||||||
|
is(document.getElementById(containerId).customizationTarget.firstChild.firstChild.id, id,
|
||||||
|
"Widget " + id + " should be in " + containerId + " in customizing window.");
|
||||||
|
is(otherWin.document.getElementById(containerId).customizationTarget.firstChild.id, id,
|
||||||
|
"Widget " + id + " should be in " + containerId + " in other window.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkToolbar(id, method) {
|
||||||
|
// Place at start of the toolbar:
|
||||||
|
let toolbarPlacements = getAreaWidgetIds(kToolbar);
|
||||||
|
move[method](id, kToolbar);
|
||||||
|
if (method == "dragToItem") {
|
||||||
|
isFirst(kToolbar, toolbarPlacements, id);
|
||||||
|
} else {
|
||||||
|
isLast(kToolbar, toolbarPlacements, id);
|
||||||
|
}
|
||||||
|
checkWrapper(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPanel(id, method) {
|
||||||
|
let panelPlacements = getAreaWidgetIds(kPanel);
|
||||||
|
move[method](id, kPanel);
|
||||||
|
let children = document.getElementById(kPanel).querySelectorAll("toolbarpaletteitem:not(." + kPlaceholderClass + ")");
|
||||||
|
let otherChildren = otherWin.document.getElementById(kPanel).children;
|
||||||
|
let newPlacements = panelPlacements.concat([id]);
|
||||||
|
// Relative position of the new item from the end:
|
||||||
|
let position = -1;
|
||||||
|
// For the drag to item case, we drag to the last item, making the dragged item the
|
||||||
|
// penultimate item. We can't well use the first item because the panel has complicated
|
||||||
|
// rules about rearranging wide items (which, by default, the first two items are).
|
||||||
|
if (method == "dragToItem") {
|
||||||
|
newPlacements.pop();
|
||||||
|
newPlacements.splice(panelPlacements.length - 1, 0, id);
|
||||||
|
position = -2;
|
||||||
|
}
|
||||||
|
assertAreaPlacements(kPanel, newPlacements);
|
||||||
|
is(children[children.length + position].firstChild.id, id,
|
||||||
|
"Widget " + id + " should be in " + kPanel + " in customizing window.");
|
||||||
|
is(otherChildren[otherChildren.length + position].id, id,
|
||||||
|
"Widget " + id + " should be in " + kPanel + " in other window.");
|
||||||
|
checkWrapper(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPalette(id, method) {
|
||||||
|
// Move back to palette:
|
||||||
|
move[method](id, kVisiblePalette);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should end in default state");
|
||||||
|
let visibleChildren = gCustomizeMode.visiblePalette.children;
|
||||||
|
let expectedChild = method == "dragToItem" ? visibleChildren[0] : visibleChildren[visibleChildren.length - 1];
|
||||||
|
is(expectedChild.firstChild.id, id, "Widget " + id + " was moved using " + method + " and should now be wrapped in palette in customizing window.");
|
||||||
|
if (id == kXULWidgetId) {
|
||||||
|
ok(otherWin.gNavToolbox.palette.querySelector("#" + id), "Widget " + id + " should be in invisible palette in other window.");
|
||||||
|
}
|
||||||
|
checkWrapper(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let otherWin;
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Moving widgets in two windows, one with customize mode and one without, should work",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
otherWin = yield openAndLoadWindow(null, true);
|
||||||
|
// Open panel to force its construction:
|
||||||
|
yield afterPanelOpen(otherWin);
|
||||||
|
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should start in default state");
|
||||||
|
|
||||||
|
for (let widgetId of [kXULWidgetId, kAPIWidgetId]) {
|
||||||
|
for (let method of ["API", "drag", "dragToItem"]) {
|
||||||
|
info("Moving widget " + widgetId + " using " + method);
|
||||||
|
checkToolbar(widgetId, method);
|
||||||
|
checkPanel(widgetId, method);
|
||||||
|
checkPalette(widgetId, method);
|
||||||
|
checkPanel(widgetId, method);
|
||||||
|
checkToolbar(widgetId, method);
|
||||||
|
checkPalette(widgetId, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
otherWin.close();
|
||||||
|
otherWin = null;
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
if (otherWin) {
|
||||||
|
otherWin.close();
|
||||||
|
}
|
||||||
|
yield endCustomizing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function afterPanelOpen(win) {
|
||||||
|
let panelEl = win.PanelUI.panel;
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
function onPanelClose(e) {
|
||||||
|
panelEl.removeEventListener("popuphidden", onPanelClose);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
function onPanelOpen(e) {
|
||||||
|
panelEl.removeEventListener("popupshown", onPanelOpen);
|
||||||
|
panelEl.addEventListener("popuphidden", onPanelClose);
|
||||||
|
win.PanelUI.toggle({type: "command"});
|
||||||
|
};
|
||||||
|
panelEl.addEventListener("popupshown", onPanelOpen);
|
||||||
|
win.PanelUI.toggle({type: "command"});
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kTestWidget1 = "test-customize-mode-create-destroy1";
|
||||||
|
const kTestWidget2 = "test-customize-mode-create-destroy2";
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Creating and destroying a widget should correctly wrap/unwrap stuff",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
CustomizableUI.createWidget({id: kTestWidget1, label: 'Pretty label', tooltiptext: 'Pretty tooltip'});
|
||||||
|
let elem = document.getElementById(kTestWidget1);
|
||||||
|
let wrapper = document.getElementById("wrapper-" + kTestWidget1);
|
||||||
|
ok(elem, "There should be an item");
|
||||||
|
ok(wrapper, "There should be a wrapper");
|
||||||
|
is(wrapper.firstChild.id, kTestWidget1, "Wrapper should have test widget");
|
||||||
|
is(wrapper.parentNode.id, "customization-palette", "Wrapper should be in palette");
|
||||||
|
CustomizableUI.destroyWidget(kTestWidget1);
|
||||||
|
wrapper = document.getElementById("wrapper-" + kTestWidget1);
|
||||||
|
ok(!wrapper, "There should be a wrapper");
|
||||||
|
let item = document.getElementById(kTestWidget1);
|
||||||
|
ok(!item, "There should no longer be an item");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Creating and destroying a widget should correctly deal with panel placeholders",
|
||||||
|
run: function() {
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
is(panel.querySelectorAll(".panel-customization-placeholder").length, 3, "The number of placeholders should be correct.");
|
||||||
|
CustomizableUI.createWidget({id: kTestWidget2, label: 'Pretty label', tooltiptext: 'Pretty tooltip', defaultArea: CustomizableUI.AREA_PANEL});
|
||||||
|
let elem = document.getElementById(kTestWidget2);
|
||||||
|
let wrapper = document.getElementById("wrapper-" + kTestWidget2);
|
||||||
|
ok(elem, "There should be an item");
|
||||||
|
ok(wrapper, "There should be a wrapper");
|
||||||
|
is(wrapper.firstChild.id, kTestWidget2, "Wrapper should have test widget");
|
||||||
|
is(wrapper.parentNode, panel, "Wrapper should be in panel");
|
||||||
|
is(panel.querySelectorAll(".panel-customization-placeholder").length, 2, "The number of placeholders should be correct.");
|
||||||
|
CustomizableUI.destroyWidget(kTestWidget2);
|
||||||
|
wrapper = document.getElementById("wrapper-" + kTestWidget2);
|
||||||
|
ok(!wrapper, "There should be a wrapper");
|
||||||
|
let item = document.getElementById(kTestWidget2);
|
||||||
|
ok(!item, "There should no longer be an item");
|
||||||
|
},
|
||||||
|
teardown: endCustomizing
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield endCustomizing();
|
||||||
|
try {
|
||||||
|
CustomizableUI.destroyWidget(kTestWidget1);
|
||||||
|
} catch (ex) {}
|
||||||
|
try {
|
||||||
|
CustomizableUI.destroyWidget(kTestWidget2);
|
||||||
|
} catch (ex) {}
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Should be able to add broken view widget",
|
||||||
|
run: function() {
|
||||||
|
const kWidgetId = 'test-877006-broken-widget';
|
||||||
|
let widgetSpec = {
|
||||||
|
id: kWidgetId,
|
||||||
|
type: 'view',
|
||||||
|
viewId: 'idontexist',
|
||||||
|
/* Empty handler so we try to attach it maybe? */
|
||||||
|
onViewShowing: function() {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let noError = true;
|
||||||
|
try {
|
||||||
|
CustomizableUI.createWidget(widgetSpec);
|
||||||
|
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
noError = false;
|
||||||
|
}
|
||||||
|
ok(noError, "Should not throw an exception trying to add a broken view widget.");
|
||||||
|
|
||||||
|
noError = true;
|
||||||
|
try {
|
||||||
|
CustomizableUI.destroyWidget(kWidgetId);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
noError = false;
|
||||||
|
}
|
||||||
|
ok(noError, "Should not throw an exception trying to remove the broken view widget.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Sanity checks",
|
||||||
|
run: function() {
|
||||||
|
SimpleTest.doesThrow(function() CustomizableUI.registerArea("@foo"),
|
||||||
|
"Registering areas with an invalid ID should throw.");
|
||||||
|
|
||||||
|
SimpleTest.doesThrow(function() CustomizableUI.registerArea([]),
|
||||||
|
"Registering areas with an invalid ID should throw.");
|
||||||
|
|
||||||
|
SimpleTest.doesThrow(function() CustomizableUI.registerArea(CustomizableUI.AREA_NAVBAR),
|
||||||
|
"Registering an area with an ID that's already registered should throw.");
|
||||||
|
|
||||||
|
SimpleTest.doesThrow(function() CustomizableUI.unregisterArea("@foo"),
|
||||||
|
"Unregistering areas with an invalid ID should throw.");
|
||||||
|
|
||||||
|
SimpleTest.doesThrow(function() CustomizableUI.unregisterArea([]),
|
||||||
|
"Unregistering areas with an invalid ID should throw.");
|
||||||
|
|
||||||
|
SimpleTest.doesThrow(function() CustomizableUI.unregisterArea("unknown"),
|
||||||
|
"Unregistering an area that's not registered should throw.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Check areas are loaded with their default placements.",
|
||||||
|
run: function() {
|
||||||
|
ok(CustomizableUI.inDefaultState, "Everything should be in its default state.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Check registering and unregistering a new area.",
|
||||||
|
run: function() {
|
||||||
|
const kToolbarId = "test-registration-toolbar";
|
||||||
|
const kButtonId = "test-registration-button";
|
||||||
|
createDummyXULButton(kButtonId);
|
||||||
|
createToolbarWithPlacements(kToolbarId, ["spring", kButtonId, "spring"]);
|
||||||
|
assertAreaPlacements(kToolbarId,
|
||||||
|
[/customizableui-special-spring\d+/,
|
||||||
|
kButtonId,
|
||||||
|
/customizableui-special-spring\d+/]);
|
||||||
|
ok(CustomizableUI.inDefaultState, "With a new toolbar and default placements, " +
|
||||||
|
"everything should still be in a default state.");
|
||||||
|
removeCustomToolbars(); // Will call unregisterArea for us
|
||||||
|
ok(CustomizableUI.inDefaultState, "When the toolbar is unregistered, " +
|
||||||
|
"everything should still be in a default state.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
removeCustomToolbars();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
registerCleanupFunction(cleanup);
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
run: function() {
|
||||||
|
const kButtonId = "look-at-me-disappear-button";
|
||||||
|
CustomizableUI.reset();
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in the default state.");
|
||||||
|
let btn = createDummyXULButton(kButtonId, "Gone!");
|
||||||
|
CustomizableUI.addWidgetToArea(kButtonId, CustomizableUI.AREA_NAVBAR);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in the default state.");
|
||||||
|
is(btn.parentNode.parentNode.id, CustomizableUI.AREA_NAVBAR, "Button should be in navbar");
|
||||||
|
btn.remove();
|
||||||
|
is(btn.parentNode, null, "Button is no longer in the navbar");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be back in the default state, " +
|
||||||
|
"despite unknown button ID in placements.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
removeCustomToolbars();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
registerCleanupFunction(cleanup);
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Dragging an item from the palette to another button in the panel should work.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let btn = document.getElementById("developer-button");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
|
||||||
|
|
||||||
|
let lastButtonIndex = placements.length - 1;
|
||||||
|
let lastButton = placements[lastButtonIndex];
|
||||||
|
let placementsAfterInsert = placements.slice(0, lastButtonIndex).concat(["developer-button", lastButton]);
|
||||||
|
let lastButtonNode = document.getElementById(lastButton);
|
||||||
|
simulateItemDrag(btn, lastButtonNode);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterInsert);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
simulateItemDrag(btn, palette);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging an item from the palette to the panel itself should also work.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let btn = document.getElementById("developer-button");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
|
||||||
|
|
||||||
|
let placementsAfterAppend = placements.concat(["developer-button"]);
|
||||||
|
simulateItemDrag(btn, panel);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
simulateItemDrag(btn, palette);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging an item from the palette to an empty panel should also work.",
|
||||||
|
setup: function() {
|
||||||
|
let widgetIds = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
|
||||||
|
while (widgetIds.length) {
|
||||||
|
CustomizableUI.removeWidgetFromArea(widgetIds.shift());
|
||||||
|
}
|
||||||
|
return startCustomizing()
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
let btn = document.getElementById("developer-button");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
|
||||||
|
assertAreaPlacements(panel.id, []);
|
||||||
|
|
||||||
|
let placementsAfterAppend = ["developer-button"];
|
||||||
|
simulateItemDrag(btn, panel);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
simulateItemDrag(btn, palette);
|
||||||
|
assertAreaPlacements(panel.id, []);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield endCustomizing();
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,424 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Dragging the zoom controls to be before the print button " +
|
||||||
|
"should not move any controls.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
let printButton = document.getElementById("print-button");
|
||||||
|
let placementsAfterMove = ["edit-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"zoom-controls",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(zoomControls, printButton);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
let newWindowButton = document.getElementById("new-window-button");
|
||||||
|
simulateItemDrag(zoomControls, newWindowButton);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the zoom controls to be before the save button " +
|
||||||
|
"should not move any controls.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
let savePageButton = document.getElementById("save-page-button");
|
||||||
|
let placementsAfterMove = ["edit-controls",
|
||||||
|
"zoom-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(zoomControls, savePageButton);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the zoom controls to be before the new-window " +
|
||||||
|
"button should not move any widgets.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
let newWindowButton = document.getElementById("new-window-button");
|
||||||
|
let placementsAfterMove = ["edit-controls",
|
||||||
|
"zoom-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(zoomControls, newWindowButton);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the zoom controls to be before the history-panelmenu " +
|
||||||
|
"should move the zoom-controls in to the row higher than the " +
|
||||||
|
"history-panelmenu.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
let historyPanelMenu = document.getElementById("history-panelmenu");
|
||||||
|
let placementsAfterMove = ["edit-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"zoom-controls",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(zoomControls, historyPanelMenu);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
let newWindowButton = document.getElementById("new-window-button");
|
||||||
|
simulateItemDrag(zoomControls, newWindowButton);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the zoom controls to be before the preferences-button " +
|
||||||
|
"should move the zoom-controls in to the row higher than the " +
|
||||||
|
"preferences-button.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
let preferencesButton = document.getElementById("preferences-button");
|
||||||
|
let placementsAfterMove = ["edit-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"zoom-controls",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(zoomControls, preferencesButton);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
let newWindowButton = document.getElementById("new-window-button");
|
||||||
|
simulateItemDrag(zoomControls, newWindowButton);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging an item from the palette to before the zoom-controls " +
|
||||||
|
"should move it and two other buttons before the zoom controls.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let developerButton = document.getElementById("developer-button");
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
let placementsAfterInsert = ["edit-controls",
|
||||||
|
"developer-button",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"zoom-controls",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(developerButton, zoomControls);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterInsert);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
// Check that the palette items are re-wrapped correctly.
|
||||||
|
let feedWrapper = document.getElementById("wrapper-feed-button");
|
||||||
|
let feedButton = document.getElementById("feed-button");
|
||||||
|
is(feedButton.parentNode, feedWrapper,
|
||||||
|
"feed-button should be a child of wrapper-feed-button");
|
||||||
|
is(feedWrapper.getAttribute("place"), "palette",
|
||||||
|
"The feed-button wrapper should have it's place set to 'palette'");
|
||||||
|
simulateItemDrag(developerButton, palette);
|
||||||
|
is(developerButton.parentNode.tagName, "toolbarpaletteitem",
|
||||||
|
"The developer-button should be wrapped by a toolbarpaletteitem");
|
||||||
|
let newWindowButton = document.getElementById("new-window-button");
|
||||||
|
simulateItemDrag(zoomControls, newWindowButton);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging an item from the palette to before the edit-controls " +
|
||||||
|
"should move it and two other buttons before the edit and zoom controls.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let developerButton = document.getElementById("developer-button");
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
let placementsAfterInsert = ["developer-button",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"edit-controls",
|
||||||
|
"zoom-controls",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(developerButton, editControls);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterInsert);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
// Check that the palette items are re-wrapped correctly.
|
||||||
|
let feedWrapper = document.getElementById("wrapper-feed-button");
|
||||||
|
let feedButton = document.getElementById("feed-button");
|
||||||
|
is(feedButton.parentNode, feedWrapper,
|
||||||
|
"feed-button should be a child of wrapper-feed-button");
|
||||||
|
is(feedWrapper.getAttribute("place"), "palette",
|
||||||
|
"The feed-button wrapper should have it's place set to 'palette'");
|
||||||
|
simulateItemDrag(developerButton, palette);
|
||||||
|
is(developerButton.parentNode.tagName, "toolbarpaletteitem",
|
||||||
|
"The developer-button should be wrapped by a toolbarpaletteitem");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the edit-controls to be before the zoom-controls button " +
|
||||||
|
"should not move any widgets.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
let placementsAfterMove = ["edit-controls",
|
||||||
|
"zoom-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(editControls, zoomControls);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the edit-controls to be before the new-window-button should " +
|
||||||
|
"move the zoom-controls before the edit-controls.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
let newWindowButton = document.getElementById("new-window-button");
|
||||||
|
let placementsAfterMove = ["zoom-controls",
|
||||||
|
"edit-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(editControls, newWindowButton);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
simulateItemDrag(editControls, zoomControls);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the edit-controls to be before the privatebrowsing-button " +
|
||||||
|
"should move the edit-controls in to the row higher than the " +
|
||||||
|
"privatebrowsing-button.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
let privateBrowsingButton = document.getElementById("privatebrowsing-button");
|
||||||
|
let placementsAfterMove = ["zoom-controls",
|
||||||
|
"edit-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(editControls, privateBrowsingButton);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
simulateItemDrag(editControls, zoomControls);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the edit-controls to be before the save-page-button " +
|
||||||
|
"should move the edit-controls in to the row higher than the " +
|
||||||
|
"save-page-button.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
let savePageButton = document.getElementById("save-page-button");
|
||||||
|
let placementsAfterMove = ["zoom-controls",
|
||||||
|
"edit-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
simulateItemDrag(editControls, savePageButton);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
simulateItemDrag(editControls, zoomControls);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the edit-controls to the panel itself should append " +
|
||||||
|
"the edit controls to the bottom of the panel.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
let placementsAfterMove = ["zoom-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button",
|
||||||
|
"edit-controls"];
|
||||||
|
simulateItemDrag(editControls, panel);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
simulateItemDrag(editControls, zoomControls);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the edit-controls to the customization-palette and " +
|
||||||
|
"back should work.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
let placementsAfterMove = ["zoom-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button"];
|
||||||
|
let paletteChildElementCount = palette.childElementCount;
|
||||||
|
simulateItemDrag(editControls, palette);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
is(paletteChildElementCount + 1, palette.childElementCount,
|
||||||
|
"The palette should have a new child, congratulations!");
|
||||||
|
is(editControls.parentNode.id, "wrapper-edit-controls",
|
||||||
|
"The edit-controls should be properly wrapped.");
|
||||||
|
is(editControls.parentNode.getAttribute("place"), "palette",
|
||||||
|
"The edit-controls should have the place of 'palette'.");
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
simulateItemDrag(editControls, zoomControls);
|
||||||
|
is(paletteChildElementCount, palette.childElementCount,
|
||||||
|
"The palette child count should have returned to its prior value.");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the edit-controls to each of the panel placeholders " +
|
||||||
|
"should append the edit-controls to the bottom of the panel.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
// NB: We can't just iterate over all of the placeholders
|
||||||
|
// because each drag-drop action recreates them.
|
||||||
|
let placeholder = panel.getElementsByClassName("panel-customization-placeholder")[i];
|
||||||
|
let placementsAfterMove = ["zoom-controls",
|
||||||
|
"new-window-button",
|
||||||
|
"privatebrowsing-button",
|
||||||
|
"save-page-button",
|
||||||
|
"print-button",
|
||||||
|
"history-panelmenu",
|
||||||
|
"fullscreen-button",
|
||||||
|
"find-button",
|
||||||
|
"preferences-button",
|
||||||
|
"add-ons-button",
|
||||||
|
"edit-controls"];
|
||||||
|
simulateItemDrag(editControls, placeholder);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterMove);
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
simulateItemDrag(editControls, zoomControls);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Dragging the developer-button back on to itself should work.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let developerButton = document.getElementById("developer-button");
|
||||||
|
is(developerButton.parentNode.tagName, "toolbarpaletteitem",
|
||||||
|
"developer-button should be wrapped by a toolbarpaletteitem");
|
||||||
|
simulateItemDrag(developerButton, developerButton);
|
||||||
|
is(developerButton.parentNode.tagName, "toolbarpaletteitem",
|
||||||
|
"developer-button should be wrapped by a toolbarpaletteitem");
|
||||||
|
let editControls = document.getElementById("edit-controls");
|
||||||
|
is(editControls.parentNode.tagName, "toolbarpaletteitem",
|
||||||
|
"edit-controls should be wrapped by a toolbarpaletteitem");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield endCustomizing();
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
|
waitForExplicitFinish();
|
||||||
|
requestLongerTimeout(5);
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,151 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kWidgetId = "some-widget";
|
||||||
|
|
||||||
|
function assertWidgetExists(aWindow, aExists) {
|
||||||
|
if (aExists) {
|
||||||
|
ok(aWindow.document.getElementById(kWidgetId),
|
||||||
|
"Should have found test widget in the window");
|
||||||
|
} else {
|
||||||
|
is(aWindow.document.getElementById(kWidgetId), null,
|
||||||
|
"Should not have found test widget in the window");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "A widget that is created with showInPrivateBrowsing undefined should " +
|
||||||
|
"have that value default to false.",
|
||||||
|
run: function() {
|
||||||
|
let wrapper = CustomizableUI.createWidget({
|
||||||
|
id: kWidgetId
|
||||||
|
});
|
||||||
|
ok(wrapper.showInPrivateBrowsing,
|
||||||
|
"showInPrivateBrowsing should have defaulted to true.");
|
||||||
|
CustomizableUI.destroyWidget(kWidgetId);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Add a widget via the API with showInPrivateBrowsing set to false " +
|
||||||
|
"and ensure it does not appear in pre-existing or newly created " +
|
||||||
|
"private windows.",
|
||||||
|
run: function() {
|
||||||
|
let plain = yield openAndLoadWindow();
|
||||||
|
let private = yield openAndLoadWindow({private: true});
|
||||||
|
|
||||||
|
CustomizableUI.createWidget({
|
||||||
|
id: kWidgetId,
|
||||||
|
removable: true,
|
||||||
|
showInPrivateBrowsing: false
|
||||||
|
});
|
||||||
|
CustomizableUI.addWidgetToArea(kWidgetId,
|
||||||
|
CustomizableUI.AREA_NAVBAR);
|
||||||
|
assertWidgetExists(plain, true);
|
||||||
|
assertWidgetExists(private, false);
|
||||||
|
|
||||||
|
// Now open up some new windows. The widget should exist in the new
|
||||||
|
// plain window, but not the new private window.
|
||||||
|
let plain2 = yield openAndLoadWindow();
|
||||||
|
let private2 = yield openAndLoadWindow({private: true});
|
||||||
|
|
||||||
|
assertWidgetExists(plain2, true);
|
||||||
|
assertWidgetExists(private2, false);
|
||||||
|
|
||||||
|
// Try moving the widget around and make sure it doesn't get added
|
||||||
|
// to the private windows. We'll start by appending it to the tabstrip.
|
||||||
|
CustomizableUI.addWidgetToArea(kWidgetId,
|
||||||
|
CustomizableUI.AREA_TABSTRIP);
|
||||||
|
assertWidgetExists(plain, true);
|
||||||
|
assertWidgetExists(plain2, true);
|
||||||
|
assertWidgetExists(private, false);
|
||||||
|
assertWidgetExists(private2, false);
|
||||||
|
|
||||||
|
// And then move it to the beginning of the tabstrip.
|
||||||
|
CustomizableUI.moveWidgetWithinArea(kWidgetId, 0);
|
||||||
|
assertWidgetExists(plain, true);
|
||||||
|
assertWidgetExists(plain2, true);
|
||||||
|
assertWidgetExists(private, false);
|
||||||
|
assertWidgetExists(private2, false);
|
||||||
|
|
||||||
|
CustomizableUI.removeWidgetFromArea("some-widget");
|
||||||
|
assertWidgetExists(plain, false);
|
||||||
|
assertWidgetExists(plain2, false);
|
||||||
|
assertWidgetExists(private, false);
|
||||||
|
assertWidgetExists(private2, false);
|
||||||
|
|
||||||
|
plain.close();
|
||||||
|
plain2.close();
|
||||||
|
private.close();
|
||||||
|
private2.close();
|
||||||
|
|
||||||
|
CustomizableUI.destroyWidget("some-widget");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Add a widget via the API with showInPrivateBrowsing set to true, " +
|
||||||
|
"and ensure that it appears in pre-existing or newly created " +
|
||||||
|
"private browsing windows.",
|
||||||
|
run: function() {
|
||||||
|
let plain = yield openAndLoadWindow();
|
||||||
|
let private = yield openAndLoadWindow({private: true});
|
||||||
|
|
||||||
|
CustomizableUI.createWidget({
|
||||||
|
id: kWidgetId,
|
||||||
|
removable: true,
|
||||||
|
showInPrivateBrowsing: true
|
||||||
|
});
|
||||||
|
CustomizableUI.addWidgetToArea(kWidgetId,
|
||||||
|
CustomizableUI.AREA_NAVBAR);
|
||||||
|
assertWidgetExists(plain, true);
|
||||||
|
assertWidgetExists(private, true);
|
||||||
|
|
||||||
|
// Now open up some new windows. The widget should exist in the new
|
||||||
|
// plain window, but not the new private window.
|
||||||
|
let plain2 = yield openAndLoadWindow();
|
||||||
|
let private2 = yield openAndLoadWindow({private: true});
|
||||||
|
|
||||||
|
assertWidgetExists(plain2, true);
|
||||||
|
assertWidgetExists(private2, true);
|
||||||
|
|
||||||
|
// Try moving the widget around and make sure it doesn't get added
|
||||||
|
// to the private windows. We'll start by appending it to the tabstrip.
|
||||||
|
CustomizableUI.addWidgetToArea(kWidgetId,
|
||||||
|
CustomizableUI.AREA_TABSTRIP);
|
||||||
|
assertWidgetExists(plain, true);
|
||||||
|
assertWidgetExists(plain2, true);
|
||||||
|
assertWidgetExists(private, true);
|
||||||
|
assertWidgetExists(private2, true);
|
||||||
|
|
||||||
|
// And then move it to the beginning of the tabstrip.
|
||||||
|
CustomizableUI.moveWidgetWithinArea(kWidgetId, 0);
|
||||||
|
assertWidgetExists(plain, true);
|
||||||
|
assertWidgetExists(plain2, true);
|
||||||
|
assertWidgetExists(private, true);
|
||||||
|
assertWidgetExists(private2, true);
|
||||||
|
|
||||||
|
CustomizableUI.removeWidgetFromArea("some-widget");
|
||||||
|
assertWidgetExists(plain, false);
|
||||||
|
assertWidgetExists(plain2, false);
|
||||||
|
assertWidgetExists(private, false);
|
||||||
|
assertWidgetExists(private2, false);
|
||||||
|
|
||||||
|
plain.close();
|
||||||
|
plain2.close();
|
||||||
|
private.close();
|
||||||
|
private2.close();
|
||||||
|
|
||||||
|
CustomizableUI.destroyWidget("some-widget");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kButtonId = "test-886323-removable-moved-node";
|
||||||
|
const kLazyAreaId = "test-886323-lazy-area-for-removability-testing";
|
||||||
|
|
||||||
|
let gNavBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||||
|
let gLazyArea;
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Removable nodes shouldn't be moved by buildArea",
|
||||||
|
setup: function() {
|
||||||
|
let dummyBtn = createDummyXULButton(kButtonId, "Dummy");
|
||||||
|
dummyBtn.setAttribute("removable", "true");
|
||||||
|
gNavBar.customizationTarget.appendChild(dummyBtn);
|
||||||
|
let popupSet = document.getElementById("mainPopupSet");
|
||||||
|
gLazyArea = document.createElementNS(kNSXUL, "panel");
|
||||||
|
gLazyArea.id = kLazyAreaId;
|
||||||
|
gLazyArea.setAttribute("hidden", "true");
|
||||||
|
popupSet.appendChild(gLazyArea);
|
||||||
|
CustomizableUI.registerArea(kLazyAreaId, {
|
||||||
|
type: CustomizableUI.TYPE_MENU_PANEL,
|
||||||
|
defaultPlacements: []
|
||||||
|
});
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
CustomizableUI.addWidgetToArea(kButtonId, kLazyAreaId);
|
||||||
|
assertAreaPlacements(kLazyAreaId, [kButtonId],
|
||||||
|
"Placements should have changed because widget is removable.");
|
||||||
|
let btn = document.getElementById(kButtonId);
|
||||||
|
btn.setAttribute("removable", "false");
|
||||||
|
gLazyArea.customizationTarget = gLazyArea;
|
||||||
|
CustomizableUI.registerToolbarNode(gLazyArea, []);
|
||||||
|
assertAreaPlacements(kLazyAreaId, [], "Placements should no longer include widget.");
|
||||||
|
is(btn.parentNode.id, gNavBar.customizationTarget.id,
|
||||||
|
"Button shouldn't actually have moved as it's not removable");
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
let btn = document.getElementById(kButtonId);
|
||||||
|
if (btn) btn.remove();
|
||||||
|
CustomizableUI.removeWidgetFromArea(kButtonId);
|
||||||
|
CustomizableUI.unregisterArea(kLazyAreaId);
|
||||||
|
gLazyArea.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let navbar = document.getElementById("nav-bar")
|
||||||
|
let navbarCT = navbar.customizationTarget;
|
||||||
|
let overflowPanelList = document.getElementById("widget-overflow-list");
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Reading currentset",
|
||||||
|
run: function() {
|
||||||
|
let nodeIds = [];
|
||||||
|
for (let node of navbarCT.childNodes) {
|
||||||
|
if (node.getAttribute("skipintoolbarset") != "true") {
|
||||||
|
nodeIds.push(node.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let node of overflowPanelList.childNodes) {
|
||||||
|
if (node.getAttribute("skipintoolbarset") != "true") {
|
||||||
|
nodeIds.push(node.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let currentSet = navbar.currentSet;
|
||||||
|
is(currentSet.split(',').length, nodeIds.length, "Should be just as many nodes as there are.");
|
||||||
|
is(currentSet, nodeIds.join(','), "Current set and node IDs should match.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Insert, then remove items",
|
||||||
|
run: function() {
|
||||||
|
let currentSet = navbar.currentSet;
|
||||||
|
let newCurrentSet = currentSet.replace('home-button', 'feed-button,sync-button,home-button');
|
||||||
|
navbar.currentSet = newCurrentSet;
|
||||||
|
is(newCurrentSet, navbar.currentSet, "Current set should match expected current set.");
|
||||||
|
let feedBtn = document.getElementById("feed-button");
|
||||||
|
let syncBtn = document.getElementById("sync-button");
|
||||||
|
ok(feedBtn, "Feed button should have been added.");
|
||||||
|
ok(syncBtn, "Sync button should have been added.");
|
||||||
|
if (feedBtn && syncBtn) {
|
||||||
|
let feedParent = feedBtn.parentNode;
|
||||||
|
let syncParent = syncBtn.parentNode;
|
||||||
|
ok(feedParent == navbarCT || feedParent == overflowPanelList,
|
||||||
|
"Feed button should be in navbar or overflow");
|
||||||
|
ok(syncParent == navbarCT || syncParent == overflowPanelList,
|
||||||
|
"Feed button should be in navbar or overflow");
|
||||||
|
is(feedBtn.nextElementSibling, syncBtn, "Feed button should be next to sync button.");
|
||||||
|
let homeBtn = document.getElementById("home-button");
|
||||||
|
is(syncBtn.nextElementSibling, homeBtn, "Sync button should be next to home button.");
|
||||||
|
}
|
||||||
|
navbar.currentSet = currentSet;
|
||||||
|
is(currentSet, navbar.currentSet, "Should be able to remove the added items.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Simultaneous insert/remove:",
|
||||||
|
run: function() {
|
||||||
|
let currentSet = navbar.currentSet;
|
||||||
|
let newCurrentSet = currentSet.replace('home-button', 'feed-button');
|
||||||
|
navbar.currentSet = newCurrentSet;
|
||||||
|
is(newCurrentSet, navbar.currentSet, "Current set should match expected current set.");
|
||||||
|
let feedBtn = document.getElementById("feed-button");
|
||||||
|
ok(feedBtn, "Feed button should have been added.");
|
||||||
|
let homeBtn = document.getElementById("home-button");
|
||||||
|
ok(!homeBtn, "Home button should have been removed.");
|
||||||
|
if (feedBtn) {
|
||||||
|
let feedParent = feedBtn.parentNode;
|
||||||
|
ok(feedParent == navbarCT || feedParent == overflowPanelList,
|
||||||
|
"Feed button should be in navbar or overflow");
|
||||||
|
}
|
||||||
|
navbar.currentSet = currentSet;
|
||||||
|
is(currentSet, navbar.currentSet, "Should be able to return to original state.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Adding, moving and removing items should update the relevant currentset attributes",
|
||||||
|
setup: function() {
|
||||||
|
let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
|
||||||
|
setToolbarVisibility(personalbar, true);
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in the default state when we start");
|
||||||
|
|
||||||
|
let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||||
|
let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
|
||||||
|
let navbarCurrentset = navbar.getAttribute("currentset") || navbar.currentSet;
|
||||||
|
let personalbarCurrentset = personalbar.getAttribute("currentset") || personalbar.currentSet;
|
||||||
|
|
||||||
|
let otherWin = yield openAndLoadWindow();
|
||||||
|
let otherNavbar = otherWin.document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||||
|
let otherPersonalbar = otherWin.document.getElementById(CustomizableUI.AREA_BOOKMARKS);
|
||||||
|
|
||||||
|
CustomizableUI.moveWidgetWithinArea("home-button", 0);
|
||||||
|
navbarCurrentset = "home-button," + navbarCurrentset.replace(",home-button", "");
|
||||||
|
is(navbar.getAttribute("currentset"), navbarCurrentset,
|
||||||
|
"Should have updated currentSet after move.");
|
||||||
|
is(otherNavbar.getAttribute("currentset"), navbarCurrentset,
|
||||||
|
"Should have updated other window's currentSet after move.");
|
||||||
|
|
||||||
|
CustomizableUI.addWidgetToArea("home-button", CustomizableUI.AREA_BOOKMARKS);
|
||||||
|
navbarCurrentset = navbarCurrentset.replace("home-button,", "");
|
||||||
|
personalbarCurrentset = personalbarCurrentset + ",home-button";
|
||||||
|
is(navbar.getAttribute("currentset"), navbarCurrentset,
|
||||||
|
"Should have updated navbar currentSet after implied remove.");
|
||||||
|
is(otherNavbar.getAttribute("currentset"), navbarCurrentset,
|
||||||
|
"Should have updated other window's navbar currentSet after implied remove.");
|
||||||
|
is(personalbar.getAttribute("currentset"), personalbarCurrentset,
|
||||||
|
"Should have updated personalbar currentSet after add.");
|
||||||
|
is(otherPersonalbar.getAttribute("currentset"), personalbarCurrentset,
|
||||||
|
"Should have updated other window's personalbar currentSet after add.");
|
||||||
|
|
||||||
|
CustomizableUI.removeWidgetFromArea("home-button");
|
||||||
|
personalbarCurrentset = personalbarCurrentset.replace(",home-button", "");
|
||||||
|
is(personalbar.getAttribute("currentset"), personalbarCurrentset,
|
||||||
|
"Should have updated currentSet after remove.");
|
||||||
|
is(otherPersonalbar.getAttribute("currentset"), personalbarCurrentset,
|
||||||
|
"Should have updated other window's currentSet after remove.");
|
||||||
|
|
||||||
|
otherWin.close();
|
||||||
|
// Reset in asyncCleanup will put our button back for us.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
let personalbar = document.getElementById(CustomizableUI.AREA_BOOKMARKS);
|
||||||
|
setToolbarVisibility(personalbar, false);
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "One orphaned item should have two placeholders next to it.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let btn = document.getElementById("developer-button");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
|
||||||
|
|
||||||
|
let placementsAfterAppend = placements.concat(["developer-button"]);
|
||||||
|
simulateItemDrag(btn, panel);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
is(getVisiblePlaceholderCount(panel), 2, "Should only have 2 visible placeholders before exiting");
|
||||||
|
|
||||||
|
yield endCustomizing();
|
||||||
|
yield startCustomizing();
|
||||||
|
is(getVisiblePlaceholderCount(panel), 2, "Should only have 2 visible placeholders after re-entering");
|
||||||
|
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
simulateItemDrag(btn, palette);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Two orphaned items should have one placeholder next to them (case 1).",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let btn = document.getElementById("developer-button");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
|
||||||
|
|
||||||
|
let placementsAfterAppend = placements.concat(["developer-button", "sync-button"]);
|
||||||
|
simulateItemDrag(btn, panel);
|
||||||
|
btn = document.getElementById("sync-button");
|
||||||
|
simulateItemDrag(btn, panel);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholders before exiting");
|
||||||
|
|
||||||
|
yield endCustomizing();
|
||||||
|
yield startCustomizing();
|
||||||
|
is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholders after re-entering");
|
||||||
|
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
simulateItemDrag(btn, palette);
|
||||||
|
btn = document.getElementById("developer-button");
|
||||||
|
simulateItemDrag(btn, palette);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Two orphaned items should have one placeholder next to them (case 2).",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let btn = document.getElementById("add-ons-button");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
|
||||||
|
|
||||||
|
let placementsAfterAppend = placements.filter(p => p != btn.id);
|
||||||
|
simulateItemDrag(btn, palette);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholders before exiting");
|
||||||
|
|
||||||
|
yield endCustomizing();
|
||||||
|
yield startCustomizing();
|
||||||
|
is(getVisiblePlaceholderCount(panel), 1, "Should only have 1 visible placeholders after re-entering");
|
||||||
|
|
||||||
|
simulateItemDrag(btn, panel);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placements);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "A wide widget at the bottom of the panel should have three placeholders after it.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let btn = document.getElementById("edit-controls");
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
let placements = getAreaWidgetIds(CustomizableUI.AREA_PANEL);
|
||||||
|
|
||||||
|
let placementsAfterAppend = placements.concat([placements.shift()]);
|
||||||
|
simulateItemDrag(btn, panel);
|
||||||
|
assertAreaPlacements(CustomizableUI.AREA_PANEL, placementsAfterAppend);
|
||||||
|
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||||
|
is(getVisiblePlaceholderCount(panel), 3, "Should have 3 visible placeholders before exiting");
|
||||||
|
|
||||||
|
yield endCustomizing();
|
||||||
|
yield startCustomizing();
|
||||||
|
is(getVisiblePlaceholderCount(panel), 3, "Should have 3 visible placeholders after re-entering");
|
||||||
|
|
||||||
|
let zoomControls = document.getElementById("zoom-controls");
|
||||||
|
simulateItemDrag(btn, zoomControls);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state again.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "The default placements should have three placeholders at the bottom.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let panel = document.getElementById(CustomizableUI.AREA_PANEL);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state.");
|
||||||
|
is(getVisiblePlaceholderCount(panel), 3, "Should have 3 visible placeholders before exiting");
|
||||||
|
|
||||||
|
yield endCustomizing();
|
||||||
|
yield startCustomizing();
|
||||||
|
is(getVisiblePlaceholderCount(panel), 3, "Should have 3 visible placeholders after re-entering");
|
||||||
|
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield endCustomizing();
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVisiblePlaceholderCount(aPanel) {
|
||||||
|
let visiblePlaceholders = aPanel.querySelectorAll(".panel-customization-placeholder:not([hidden=true])");
|
||||||
|
return visiblePlaceholders.length;
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kLazyAreaId = "test-890262-lazy-area";
|
||||||
|
const kWidget1Id = "test-890262-widget1";
|
||||||
|
const kWidget2Id = "test-890262-widget2";
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Destroying a widget after defaulting it to a non-legacy area should work.",
|
||||||
|
run: function() {
|
||||||
|
CustomizableUI.createWidget({
|
||||||
|
id: kWidget1Id,
|
||||||
|
removable: true,
|
||||||
|
defaultArea: kLazyAreaId
|
||||||
|
});
|
||||||
|
let noError = true;
|
||||||
|
try {
|
||||||
|
CustomizableUI.destroyWidget(kWidget1Id);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
noError = false;
|
||||||
|
}
|
||||||
|
ok(noError, "Shouldn't throw an exception for a widget that was created in a not-yet-constructed area");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Destroying a widget after moving it to a non-legacy area should work.",
|
||||||
|
run: function() {
|
||||||
|
CustomizableUI.createWidget({
|
||||||
|
id: kWidget2Id,
|
||||||
|
removable: true,
|
||||||
|
defaultArea: CustomizableUI.AREA_NAVBAR
|
||||||
|
});
|
||||||
|
|
||||||
|
CustomizableUI.addWidgetToArea(kWidget2Id, kLazyAreaId);
|
||||||
|
let noError = true;
|
||||||
|
try {
|
||||||
|
CustomizableUI.destroyWidget(kWidget2Id);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
noError = false;
|
||||||
|
}
|
||||||
|
ok(noError, "Shouldn't throw an exception for a widget that was added to a not-yet-constructed area");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
let lazyArea = document.getElementById(kLazyAreaId);
|
||||||
|
if (lazyArea) {
|
||||||
|
lazyArea.remove();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
CustomizableUI.unregisterArea(kLazyAreaId);
|
||||||
|
} catch (ex) {} // If we didn't register successfully for some reason
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupArea() {
|
||||||
|
let lazyArea = document.createElementNS(kNSXUL, "hbox");
|
||||||
|
lazyArea.id = kLazyAreaId;
|
||||||
|
document.getElementById("nav-bar").appendChild(lazyArea);
|
||||||
|
CustomizableUI.registerArea(kLazyAreaId, {
|
||||||
|
type: CustomizableUI.TYPE_TOOLBAR,
|
||||||
|
defaultPlacements: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
setupArea();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kWidgetId = "test-892955-remove-widget";
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Removing a destroyed widget should work.",
|
||||||
|
run: function() {
|
||||||
|
let widgetSpec = {
|
||||||
|
id: kWidgetId,
|
||||||
|
defaultArea: CustomizableUI.AREA_NAVBAR
|
||||||
|
};
|
||||||
|
|
||||||
|
CustomizableUI.createWidget(widgetSpec);
|
||||||
|
CustomizableUI.destroyWidget(kWidgetId);
|
||||||
|
let noError = true;
|
||||||
|
try {
|
||||||
|
CustomizableUI.removeWidgetFromArea(kWidgetId);
|
||||||
|
} catch (ex) {
|
||||||
|
noError = false;
|
||||||
|
Cu.reportError(ex);
|
||||||
|
}
|
||||||
|
ok(noError, "Shouldn't throw an error removing a destroyed widget.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kWidgetId = "test-892956-destroyWidget-defaultPlacement";
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "destroyWidget should clean up defaultPlacements if the widget had a defaultArea",
|
||||||
|
run: function() {
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in the default state when we start");
|
||||||
|
|
||||||
|
let widgetSpec = {
|
||||||
|
id: kWidgetId,
|
||||||
|
defaultArea: CustomizableUI.AREA_NAVBAR
|
||||||
|
};
|
||||||
|
CustomizableUI.createWidget(widgetSpec);
|
||||||
|
CustomizableUI.destroyWidget(kWidgetId);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in the default state when we finish");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Resize to a small window, open a new window, check that new window handles overflow properly",
|
||||||
|
run: function() {
|
||||||
|
let originalWindowWidth = window.outerWidth;
|
||||||
|
let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
|
||||||
|
let oldChildCount = navbar.customizationTarget.childElementCount;
|
||||||
|
window.resizeTo(400, window.outerHeight);
|
||||||
|
yield waitForCondition(() => navbar.hasAttribute("overflowing"));
|
||||||
|
ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
|
||||||
|
|
||||||
|
ok(navbar.customizationTarget.childElementCount < oldChildCount, "Should have fewer children.");
|
||||||
|
let newWindow = yield openAndLoadWindow();
|
||||||
|
let otherNavBar = newWindow.document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||||
|
yield waitForCondition(() => otherNavBar.hasAttribute("overflowing"));
|
||||||
|
ok(otherNavBar.hasAttribute("overflowing"), "Other window should have an overflowing toolbar.");
|
||||||
|
newWindow.close();
|
||||||
|
|
||||||
|
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||||
|
yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should no longer have an overflowing toolbar.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Resize to a small window, resize back, shouldn't affect currentSet",
|
||||||
|
run: function() {
|
||||||
|
let originalWindowWidth = window.outerWidth;
|
||||||
|
let oldCurrentSet = navbar.currentSet;
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should start in default state.");
|
||||||
|
let oldChildCount = navbar.customizationTarget.childElementCount;
|
||||||
|
window.resizeTo(400, window.outerHeight);
|
||||||
|
yield waitForCondition(() => navbar.hasAttribute("overflowing"));
|
||||||
|
ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
|
||||||
|
is(navbar.currentSet, oldCurrentSet, "Currentset should be the same when overflowing.");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state when overflowing.");
|
||||||
|
ok(navbar.customizationTarget.childElementCount < oldChildCount, "Should have fewer children.");
|
||||||
|
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||||
|
yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should no longer have an overflowing toolbar.");
|
||||||
|
is(navbar.currentSet, oldCurrentSet, "Currentset should still be the same now we're no longer overflowing.");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state now we're no longer overflowing.");
|
||||||
|
|
||||||
|
// Verify actual physical placements match those of the placement array:
|
||||||
|
let placementCounter = 0;
|
||||||
|
let placements = CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_NAVBAR);
|
||||||
|
for (let node of navbar.customizationTarget.childNodes) {
|
||||||
|
if (node.getAttribute("skipintoolbarset") == "true") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
is(placements[placementCounter++], node.id, "Nodes should match after overflow");
|
||||||
|
}
|
||||||
|
is(placements.length, placementCounter, "Should have as many nodes as expected");
|
||||||
|
is(navbar.customizationTarget.childElementCount, oldChildCount, "Number of nodes should match");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Enter and exit customization mode, check that currentSet works",
|
||||||
|
run: function() {
|
||||||
|
let oldCurrentSet = navbar.currentSet;
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should start in default state.");
|
||||||
|
yield startCustomizing();
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state in customization mode.");
|
||||||
|
is(navbar.currentSet, oldCurrentSet, "Currentset should be the same in customization mode.");
|
||||||
|
yield endCustomizing();
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be in default state after customization mode.");
|
||||||
|
is(navbar.currentSet, oldCurrentSet, "Currentset should be the same after customization mode.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,171 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||||
|
let overflowList = document.getElementById(navbar.getAttribute("overflowtarget"));
|
||||||
|
|
||||||
|
const kTestBtn1 = "test-addWidgetToArea-overflow";
|
||||||
|
const kTestBtn2 = "test-removeWidgetFromArea-overflow";
|
||||||
|
const kHomeBtn = "home-button";
|
||||||
|
const kDownloadsBtn = "downloads-button";
|
||||||
|
const kSearchBox = "search-container";
|
||||||
|
const kStarBtn = "bookmarks-menu-button";
|
||||||
|
|
||||||
|
let originalWindowWidth;
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Adding a widget should add it next to the widget it's being inserted next to.",
|
||||||
|
setup: function() {
|
||||||
|
originalWindowWidth = window.outerWidth;
|
||||||
|
createDummyXULButton(kTestBtn1, "Test");
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should start in default state.");
|
||||||
|
|
||||||
|
window.resizeTo(400, window.outerHeight);
|
||||||
|
yield waitForCondition(() => navbar.hasAttribute("overflowing"));
|
||||||
|
ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
|
||||||
|
ok(!navbar.querySelector("#" + kHomeBtn), "Home button should no longer be in the navbar");
|
||||||
|
ok(overflowList.querySelector("#" + kHomeBtn), "Home button should be overflowing");
|
||||||
|
|
||||||
|
let placementOfHomeButton = CustomizableUI.getWidgetIdsInArea(navbar.id).indexOf(kHomeBtn);
|
||||||
|
CustomizableUI.addWidgetToArea(kTestBtn1, navbar.id, placementOfHomeButton);
|
||||||
|
ok(!navbar.querySelector("#" + kTestBtn1), "New button should not be in the navbar");
|
||||||
|
ok(overflowList.querySelector("#" + kTestBtn1), "New button should be overflowing");
|
||||||
|
let nextEl = document.getElementById(kTestBtn1).nextSibling;
|
||||||
|
is(nextEl && nextEl.id, kHomeBtn, "Test button should be next to home button.");
|
||||||
|
|
||||||
|
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||||
|
yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
|
||||||
|
ok(navbar.querySelector("#" + kHomeBtn), "Home button should be in the navbar");
|
||||||
|
ok(!overflowList.querySelector("#" + kHomeBtn), "Home button should no longer be overflowing");
|
||||||
|
ok(navbar.querySelector("#" + kTestBtn1), "Test button should be in the navbar");
|
||||||
|
ok(!overflowList.querySelector("#" + kTestBtn1), "Test button should no longer be overflowing");
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
let el = document.getElementById(kTestBtn1);
|
||||||
|
if (el) {
|
||||||
|
CustomizableUI.removeWidgetFromArea(kTestBtn1);
|
||||||
|
el.remove();
|
||||||
|
}
|
||||||
|
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Removing a widget from the toolbar should try to move items back.",
|
||||||
|
setup: function() {
|
||||||
|
// This is pretty weird. We're going to try to move only the home button into the overlay:
|
||||||
|
let downloadsBtn = document.getElementById(kDownloadsBtn);
|
||||||
|
// Guarantee overflow of too much stuff:
|
||||||
|
window.resizeTo(700, window.outerHeight);
|
||||||
|
let inc = 15;
|
||||||
|
while (window.outerWidth < originalWindowWidth &&
|
||||||
|
downloadsBtn.parentNode != navbar.customizationTarget) {
|
||||||
|
window.resizeTo(window.outerWidth + inc, window.outerHeight);
|
||||||
|
yield waitFor(500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
ok(overflowList.querySelector("#home-button"), "Home button should be overflowing");
|
||||||
|
CustomizableUI.removeWidgetFromArea("downloads-button");
|
||||||
|
is(document.getElementById("home-button").parentNode, navbar.customizationTarget, "Home button should move back.");
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Navbar is no longer overflowing");
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||||
|
yield waitForCondition(function() !navbar.hasAttribute("overflowing"));
|
||||||
|
CustomizableUI.reset();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Removing a widget should remove it from the overflow list if that is where it is, and update it accordingly.",
|
||||||
|
setup: function() {
|
||||||
|
createDummyXULButton(kTestBtn2, "Test");
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should start in default state.");
|
||||||
|
CustomizableUI.addWidgetToArea(kTestBtn2, navbar.id);
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should still have a non-overflowing toolbar.");
|
||||||
|
|
||||||
|
window.resizeTo(400, window.outerHeight);
|
||||||
|
yield waitForCondition(() => navbar.hasAttribute("overflowing"));
|
||||||
|
ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
|
||||||
|
ok(!navbar.querySelector("#" + kTestBtn2), "Test button should not be in the navbar");
|
||||||
|
ok(overflowList.querySelector("#" + kTestBtn2), "Test button should be overflowing");
|
||||||
|
|
||||||
|
CustomizableUI.removeWidgetFromArea(kTestBtn2);
|
||||||
|
|
||||||
|
ok(!overflowList.querySelector("#" + kTestBtn2), "Test button should not be overflowing.");
|
||||||
|
ok(!navbar.querySelector("#" + kTestBtn2), "Test button should not be in the navbar");
|
||||||
|
ok(gNavToolbox.palette.querySelector("#" + kTestBtn2), "Test button should be in the palette");
|
||||||
|
|
||||||
|
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||||
|
yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
let el = document.getElementById(kTestBtn2);
|
||||||
|
if (el) {
|
||||||
|
CustomizableUI.removeWidgetFromArea(kTestBtn2);
|
||||||
|
el.remove();
|
||||||
|
}
|
||||||
|
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Overflow everything that can, then reorganize that list",
|
||||||
|
setup: function() {
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should start in default state.");
|
||||||
|
|
||||||
|
window.resizeTo(480, window.outerHeight);
|
||||||
|
yield waitForCondition(() => navbar.hasAttribute("overflowing"));
|
||||||
|
ok(!navbar.querySelector("#" + kSearchBox), "Search container should be overflowing");
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
let placements = CustomizableUI.getWidgetIdsInArea(navbar.id);
|
||||||
|
let searchboxPlacement = placements.indexOf(kSearchBox);
|
||||||
|
CustomizableUI.moveWidgetWithinArea(kHomeBtn, searchboxPlacement);
|
||||||
|
yield waitForCondition(() => navbar.querySelector("#" + kHomeBtn));
|
||||||
|
ok(navbar.querySelector("#" + kHomeBtn), "Home button should have moved back");
|
||||||
|
let inc = 15;
|
||||||
|
window.resizeTo(640, window.outerHeight);
|
||||||
|
while (window.outerWidth < originalWindowWidth &&
|
||||||
|
!navbar.querySelector("#" + kSearchBox)) {
|
||||||
|
window.resizeTo(window.outerWidth + inc, window.outerHeight);
|
||||||
|
yield waitFor(500);
|
||||||
|
}
|
||||||
|
ok(!navbar.querySelector("#" + kStarBtn), "Star button should still be overflowed");
|
||||||
|
CustomizableUI.moveWidgetWithinArea(kStarBtn);
|
||||||
|
let starButtonOverflowed = overflowList.querySelector("#" + kStarBtn);
|
||||||
|
ok(starButtonOverflowed && !starButtonOverflowed.nextSibling, "Star button should be last item");
|
||||||
|
window.resizeTo(window.outerWidth + 15, window.outerHeight);
|
||||||
|
yield waitForCondition(() => navbar.querySelector("#" + kDownloadsBtn) && navbar.hasAttribute("overflowing"));
|
||||||
|
ok(navbar.hasAttribute("overflowing"), "navbar should still be overflowing");
|
||||||
|
CustomizableUI.moveWidgetWithinArea(kHomeBtn);
|
||||||
|
let homeButtonOverflowed = overflowList.querySelector("#" + kHomeBtn);
|
||||||
|
ok(homeButtonOverflowed, "Home button should be in overflow list");
|
||||||
|
ok(navbar.hasAttribute("overflowing"), "navbar should still be overflowing");
|
||||||
|
ok(homeButtonOverflowed && !homeButtonOverflowed.nextSibling, "Home button should be last item");
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
window.resizeTo(originalWindowWidth, window.outerHeight);
|
||||||
|
yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
|
||||||
|
ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Entering then exiting customization mode should reenable the Help and Exit buttons.",
|
||||||
|
run: function() {
|
||||||
|
yield startCustomizing();
|
||||||
|
let helpButton = document.getElementById("PanelUI-help");
|
||||||
|
let quitButton = document.getElementById("PanelUI-quit");
|
||||||
|
ok(helpButton.getAttribute("disabled") == "true", "Help button should be disabled while in customization mode.");
|
||||||
|
ok(quitButton.getAttribute("disabled") == "true", "Quit button should be disabled while in customization mode.");
|
||||||
|
yield endCustomizing();
|
||||||
|
|
||||||
|
ok(!helpButton.hasAttribute("disabled"), "Help button should not be disabled.");
|
||||||
|
ok(!quitButton.hasAttribute("disabled"), "Quit button should not be disabled.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let navbar;
|
||||||
|
let skippedItem;
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Attempting to drag a skipintoolbarset item should work.",
|
||||||
|
setup: function() {
|
||||||
|
navbar = document.getElementById("nav-bar");
|
||||||
|
skippedItem = document.createElement("toolbarbutton");
|
||||||
|
skippedItem.id = "test-skipintoolbarset-item";
|
||||||
|
skippedItem.setAttribute("label", "Test");
|
||||||
|
skippedItem.setAttribute("skipintoolbarset", "true");
|
||||||
|
navbar.customizationTarget.appendChild(skippedItem);
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
let downloadsButton = document.getElementById("downloads-button");
|
||||||
|
yield startCustomizing();
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state");
|
||||||
|
simulateItemDrag(skippedItem, downloadsButton);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state");
|
||||||
|
let skippedItemWrapper = skippedItem.parentNode;
|
||||||
|
is(skippedItemWrapper.nextSibling && skippedItemWrapper.nextSibling.id,
|
||||||
|
downloadsButton.parentNode.id, "Should be next to downloads button");
|
||||||
|
simulateItemDrag(downloadsButton, skippedItem);
|
||||||
|
let downloadWrapper = downloadsButton.parentNode;
|
||||||
|
is(downloadWrapper.nextSibling && downloadWrapper.nextSibling.id,
|
||||||
|
skippedItem.parentNode.id, "Should be next to skipintoolbarset item");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should still be in default state");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield endCustomizing();
|
||||||
|
skippedItem.remove();
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Customize mode reset button should revert correctly",
|
||||||
|
setup: function() {
|
||||||
|
yield startCustomizing();
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
let devButton = document.getElementById("developer-button");
|
||||||
|
let downloadsButton = document.getElementById("downloads-button");
|
||||||
|
let searchBox = document.getElementById("search-container");
|
||||||
|
let palette = document.getElementById("customization-palette");
|
||||||
|
ok(devButton && downloadsButton && searchBox && palette, "Stuff should exist");
|
||||||
|
simulateItemDrag(devButton, downloadsButton);
|
||||||
|
simulateItemDrag(searchBox, palette);
|
||||||
|
gCustomizeMode.reset();
|
||||||
|
yield waitForCondition(function() !gCustomizeMode.resetting);
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should be back in default state");
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
yield endCustomizing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function asyncCleanup() {
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const kTestToolbarId = "test-empty-drag";
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Attempting to drag an item to an empty container should work.",
|
||||||
|
setup: function() {
|
||||||
|
createToolbarWithPlacements(kTestToolbarId, "");
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
yield startCustomizing();
|
||||||
|
let downloadButton = document.getElementById("downloads-button");
|
||||||
|
let customToolbar = document.getElementById(kTestToolbarId);
|
||||||
|
simulateItemDrag(downloadButton, customToolbar);
|
||||||
|
assertAreaPlacements(kTestToolbarId, ["downloads-button"]);
|
||||||
|
ok(downloadButton.parentNode && downloadButton.parentNode.parentNode == customToolbar,
|
||||||
|
"Button should really be in toolbar");
|
||||||
|
},
|
||||||
|
teardown: function() {
|
||||||
|
yield endCustomizing();
|
||||||
|
removeCustomToolbars();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield endCustomizing();
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
let gTests = [
|
||||||
|
{
|
||||||
|
desc: "Attempting to drag the menubar to the navbar shouldn't work.",
|
||||||
|
setup: startCustomizing,
|
||||||
|
run: function() {
|
||||||
|
let menuItems = document.getElementById("menubar-items");
|
||||||
|
let navbar = document.getElementById("nav-bar");
|
||||||
|
let menubar = document.getElementById("toolbar-menubar");
|
||||||
|
simulateItemDrag(menuItems, navbar.customizationTarget);
|
||||||
|
is(getAreaWidgetIds("nav-bar").indexOf("menubar-items"), -1, "Menu bar shouldn't be in the navbar.");
|
||||||
|
ok(!navbar.querySelector("#menubar-items"), "Shouldn't find menubar items in the navbar.");
|
||||||
|
ok(menubar.querySelector("#menubar-items"), "Should find menubar items in the menubar.");
|
||||||
|
isnot(getAreaWidgetIds("toolbar-menubar").indexOf("menubar-items"), -1, "Menubar items shouldn't be missing from the navbar.");
|
||||||
|
},
|
||||||
|
teardown: endCustomizing
|
||||||
|
},
|
||||||
|
];
|
||||||
|
function asyncCleanup() {
|
||||||
|
yield endCustomizing();
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck");
|
||||||
|
yield resetCustomization();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||||
|
waitForExplicitFinish();
|
||||||
|
runTests(gTests, asyncCleanup);
|
||||||
|
}
|
232
browser/components/customizableui/test/head.js
Normal file
232
browser/components/customizableui/test/head.js
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
|
||||||
|
// Avoid leaks by using tmp for imports...
|
||||||
|
let tmp = {};
|
||||||
|
Cu.import("resource://gre/modules/Promise.jsm", tmp);
|
||||||
|
Cu.import("resource://gre/modules/Task.jsm", tmp);
|
||||||
|
Cu.import("resource:///modules/CustomizableUI.jsm", tmp);
|
||||||
|
let {Promise, Task, CustomizableUI} = tmp;
|
||||||
|
|
||||||
|
let ChromeUtils = {};
|
||||||
|
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
|
||||||
|
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||||
|
|
||||||
|
let {synthesizeDragStart, synthesizeDrop} = ChromeUtils;
|
||||||
|
|
||||||
|
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
|
||||||
|
function createDummyXULButton(id, label) {
|
||||||
|
let btn = document.createElementNS(kNSXUL, "toolbarbutton");
|
||||||
|
btn.id = id;
|
||||||
|
btn.setAttribute("label", label || id);
|
||||||
|
btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
|
||||||
|
window.gNavToolbox.palette.appendChild(btn);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
let gAddedToolbars = new Set();
|
||||||
|
|
||||||
|
function createToolbarWithPlacements(id, placements) {
|
||||||
|
gAddedToolbars.add(id);
|
||||||
|
let tb = document.createElementNS(kNSXUL, "toolbar");
|
||||||
|
tb.id = id;
|
||||||
|
tb.setAttribute("customizable", "true");
|
||||||
|
CustomizableUI.registerArea(id, {
|
||||||
|
type: CustomizableUI.TYPE_TOOLBAR,
|
||||||
|
defaultPlacements: placements
|
||||||
|
});
|
||||||
|
gNavToolbox.appendChild(tb);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeCustomToolbars() {
|
||||||
|
CustomizableUI.reset();
|
||||||
|
for (let toolbarId of gAddedToolbars) {
|
||||||
|
CustomizableUI.unregisterArea(toolbarId);
|
||||||
|
document.getElementById(toolbarId).remove();
|
||||||
|
}
|
||||||
|
gAddedToolbars.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetCustomization() {
|
||||||
|
return CustomizableUI.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertAreaPlacements(areaId, expectedPlacements) {
|
||||||
|
let actualPlacements = getAreaWidgetIds(areaId);
|
||||||
|
is(actualPlacements.length, expectedPlacements.length,
|
||||||
|
"Area " + areaId + " should have " + expectedPlacements.length + " items.");
|
||||||
|
let minItems = Math.min(expectedPlacements.length, actualPlacements.length);
|
||||||
|
for (let i = 0; i < minItems; i++) {
|
||||||
|
if (typeof expectedPlacements[i] == "string") {
|
||||||
|
is(actualPlacements[i], expectedPlacements[i],
|
||||||
|
"Item " + i + " in " + areaId + " should match expectations.");
|
||||||
|
} else if (expectedPlacements[i] instanceof RegExp) {
|
||||||
|
ok(expectedPlacements[i].test(actualPlacements[i]),
|
||||||
|
"Item " + i + " (" + actualPlacements[i] + ") in " +
|
||||||
|
areaId + " should match " + expectedPlacements[i]);
|
||||||
|
} else {
|
||||||
|
ok(false, "Unknown type of expected placement passed to " +
|
||||||
|
" assertAreaPlacements. Is your test broken?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function todoAssertAreaPlacements(areaId, expectedPlacements) {
|
||||||
|
let actualPlacements = getAreaWidgetIds(areaId);
|
||||||
|
let isPassing = actualPlacements.length == expectedPlacements.length;
|
||||||
|
let minItems = Math.min(expectedPlacements.length, actualPlacements.length);
|
||||||
|
for (let i = 0; i < minItems; i++) {
|
||||||
|
if (typeof expectedPlacements[i] == "string") {
|
||||||
|
isPassing = isPassing && actualPlacements[i] == expectedPlacements[i];
|
||||||
|
} else if (expectedPlacements[i] instanceof RegExp) {
|
||||||
|
isPassing = isPassing && expectedPlacements[i].test(actualPlacements[i]);
|
||||||
|
} else {
|
||||||
|
ok(false, "Unknown type of expected placement passed to " +
|
||||||
|
" assertAreaPlacements. Is your test broken?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
todo(isPassing, "The area placements for " + areaId +
|
||||||
|
" should equal the expected placements.")
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAreaWidgetIds(areaId) {
|
||||||
|
return CustomizableUI.getWidgetIdsInArea(areaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function simulateItemDrag(toDrag, target) {
|
||||||
|
let docId = toDrag.ownerDocument.documentElement.id;
|
||||||
|
let dragData = [[{type: 'text/toolbarwrapper-id/' + docId,
|
||||||
|
data: toDrag.id}]];
|
||||||
|
synthesizeDragStart(toDrag.parentNode, dragData);
|
||||||
|
synthesizeDrop(target, target, dragData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function endCustomizing() {
|
||||||
|
if (document.documentElement.getAttribute("customizing") != "true") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let deferredEndCustomizing = Promise.defer();
|
||||||
|
function onCustomizationEnds() {
|
||||||
|
window.gNavToolbox.removeEventListener("aftercustomization", onCustomizationEnds);
|
||||||
|
deferredEndCustomizing.resolve();
|
||||||
|
}
|
||||||
|
window.gNavToolbox.addEventListener("aftercustomization", onCustomizationEnds);
|
||||||
|
window.gCustomizeMode.exit();
|
||||||
|
|
||||||
|
return deferredEndCustomizing.promise.then(function() {
|
||||||
|
let deferredLoadNewTab = Promise.defer();
|
||||||
|
|
||||||
|
//XXXgijs so some tests depend on this tab being about:blank. Make it so.
|
||||||
|
let newTabBrowser = window.gBrowser.selectedBrowser;
|
||||||
|
newTabBrowser.stop();
|
||||||
|
|
||||||
|
// If we stop early enough, this might actually be about:blank.
|
||||||
|
if (newTabBrowser.contentDocument.location.href == "about:blank") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, make it be about:blank, and wait for that to be done.
|
||||||
|
function onNewTabLoaded(e) {
|
||||||
|
newTabBrowser.removeEventListener("load", onNewTabLoaded, true);
|
||||||
|
deferredLoadNewTab.resolve();
|
||||||
|
}
|
||||||
|
newTabBrowser.addEventListener("load", onNewTabLoaded, true);
|
||||||
|
newTabBrowser.contentDocument.location.replace("about:blank");
|
||||||
|
return deferredLoadNewTab.promise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function startCustomizing() {
|
||||||
|
if (document.documentElement.getAttribute("customizing") == "true") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
function onCustomizing() {
|
||||||
|
window.gNavToolbox.removeEventListener("customizationready", onCustomizing);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
window.gNavToolbox.addEventListener("customizationready", onCustomizing);
|
||||||
|
window.gCustomizeMode.enter();
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAndLoadWindow(aOptions, aWaitForDelayedStartup=false) {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
let win = OpenBrowserWindow(aOptions);
|
||||||
|
if (aWaitForDelayedStartup) {
|
||||||
|
Services.obs.addObserver(function onDS(aSubject, aTopic, aData) {
|
||||||
|
if (aSubject != win) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Services.obs.removeObserver(onDS, "browser-delayed-startup-finished");
|
||||||
|
deferred.resolve(win);
|
||||||
|
}, "browser-delayed-startup-finished", false);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
win.addEventListener("load", function onLoad() {
|
||||||
|
win.removeEventListener("load", onLoad);
|
||||||
|
deferred.resolve(win);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
|
||||||
|
function tryNow() {
|
||||||
|
tries++;
|
||||||
|
if (aConditionFn()) {
|
||||||
|
deferred.resolve();
|
||||||
|
} else if (tries < aMaxTries) {
|
||||||
|
tryAgain();
|
||||||
|
} else {
|
||||||
|
deferred.reject("Condition timed out: " + aConditionFn.toSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function tryAgain() {
|
||||||
|
setTimeout(tryNow, aCheckInterval);
|
||||||
|
}
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
let tries = 0;
|
||||||
|
tryAgain();
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitFor(aTimeout=100) {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
setTimeout(function() deferred.resolve(), aTimeout);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRunner(testAry, asyncCleanup) {
|
||||||
|
Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
|
||||||
|
for (let test of testAry) {
|
||||||
|
info(test.desc);
|
||||||
|
|
||||||
|
if (test.setup)
|
||||||
|
yield test.setup();
|
||||||
|
|
||||||
|
info("Running test");
|
||||||
|
yield test.run();
|
||||||
|
info("Cleanup");
|
||||||
|
if (test.teardown)
|
||||||
|
yield test.teardown();
|
||||||
|
ok(!document.getElementById(CustomizableUI.AREA_NAVBAR).hasAttribute("overflowing"), "Shouldn't overflow");
|
||||||
|
}
|
||||||
|
if (asyncCleanup) {
|
||||||
|
yield asyncCleanup();
|
||||||
|
}
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should remain in default state");
|
||||||
|
Services.prefs.clearUserPref("browser.uiCustomization.disableAnimation");
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests(testAry, asyncCleanup) {
|
||||||
|
Task.spawn(testRunner(gTests, asyncCleanup)).then(finish, ex => {
|
||||||
|
// The stack of ok() here is misleading due to Promises. The stack of the
|
||||||
|
// actual exception is likely much more valuable, hence concatentating it.
|
||||||
|
ok(false, "Unexpected exception: " + ex + " With stack: " + ex.stack);
|
||||||
|
finish();
|
||||||
|
}).then(null, Cu.reportError);
|
||||||
|
}
|
@ -641,11 +641,6 @@ const DownloadsOverlayLoader = {
|
|||||||
this._overlayLoading = false;
|
this._overlayLoading = false;
|
||||||
this._loadedOverlays[aOverlay] = true;
|
this._loadedOverlays[aOverlay] = true;
|
||||||
|
|
||||||
// Loading the overlay causes all the persisted XUL attributes to be
|
|
||||||
// reapplied, including "iconsize" on the toolbars. Until bug 640158 is
|
|
||||||
// fixed, we must recalculate the correct "iconsize" attributes manually.
|
|
||||||
retrieveToolbarIconsizesFromTheme();
|
|
||||||
|
|
||||||
this.processPendingRequests();
|
this.processPendingRequests();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,10 +110,12 @@ const DownloadsButton = {
|
|||||||
|
|
||||||
indicator.open = this._anchorRequested;
|
indicator.open = this._anchorRequested;
|
||||||
|
|
||||||
// Determine if we're located on an invisible toolbar.
|
let widget = CustomizableUI.getWidget("downloads-button")
|
||||||
if (!isElementVisible(indicator.parentNode)) {
|
.forWindow(window);
|
||||||
return null;
|
// Determine if the indicator is located on an invisible toolbar.
|
||||||
}
|
if (!isElementVisible(indicator.parentNode) && !widget.overflowed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return DownloadsIndicatorView.indicatorAnchor;
|
return DownloadsIndicatorView.indicatorAnchor;
|
||||||
},
|
},
|
||||||
@ -317,10 +319,20 @@ const DownloadsIndicatorView = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the anchor is not there or its container is hidden, don't show
|
|
||||||
// a notification
|
|
||||||
let anchor = DownloadsButton._placeholder;
|
let anchor = DownloadsButton._placeholder;
|
||||||
|
let widgetGroup = CustomizableUI.getWidget("downloads-button");
|
||||||
|
let widgetInWindow = widgetGroup.forWindow(window);
|
||||||
|
if (widgetInWindow.overflowed || widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
|
||||||
|
if (anchor && isElementVisible(anchor.parentNode)) {
|
||||||
|
// If the panel is open, don't do anything:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, try to use the anchor of the panel:
|
||||||
|
anchor = widgetInWindow.anchor;
|
||||||
|
}
|
||||||
if (!anchor || !isElementVisible(anchor.parentNode)) {
|
if (!anchor || !isElementVisible(anchor.parentNode)) {
|
||||||
|
// Our container isn't visible, so can't show the animation:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +493,14 @@ const DownloadsIndicatorView = {
|
|||||||
|
|
||||||
onCommand: function DIV_onCommand(aEvent)
|
onCommand: function DIV_onCommand(aEvent)
|
||||||
{
|
{
|
||||||
DownloadsPanel.showPanel();
|
// If the downloads button is in the menu panel, open the Library
|
||||||
|
let widgetGroup = CustomizableUI.getWidget("downloads-button");
|
||||||
|
if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
|
||||||
|
DownloadsPanel.showDownloadsHistory();
|
||||||
|
} else {
|
||||||
|
DownloadsPanel.showPanel();
|
||||||
|
}
|
||||||
|
|
||||||
aEvent.stopPropagation();
|
aEvent.stopPropagation();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -512,7 +531,6 @@ const DownloadsIndicatorView = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_indicator: null,
|
_indicator: null,
|
||||||
_indicatorAnchor: null,
|
|
||||||
__indicatorCounter: null,
|
__indicatorCounter: null,
|
||||||
__indicatorProgress: null,
|
__indicatorProgress: null,
|
||||||
|
|
||||||
@ -536,8 +554,12 @@ const DownloadsIndicatorView = {
|
|||||||
|
|
||||||
get indicatorAnchor()
|
get indicatorAnchor()
|
||||||
{
|
{
|
||||||
return this._indicatorAnchor ||
|
let widget = CustomizableUI.getWidget("downloads-button")
|
||||||
(this._indicatorAnchor = document.getElementById("downloads-indicator-anchor"));
|
.forWindow(window);
|
||||||
|
if (widget.overflowed) {
|
||||||
|
return widget.anchor;
|
||||||
|
}
|
||||||
|
return document.getElementById("downloads-indicator-anchor");
|
||||||
},
|
},
|
||||||
|
|
||||||
get _indicatorCounter()
|
get _indicatorCounter()
|
||||||
@ -560,7 +582,6 @@ const DownloadsIndicatorView = {
|
|||||||
|
|
||||||
_onCustomizedAway: function() {
|
_onCustomizedAway: function() {
|
||||||
this._indicator = null;
|
this._indicator = null;
|
||||||
this._indicatorAnchor = null;
|
|
||||||
this.__indicatorCounter = null;
|
this.__indicatorCounter = null;
|
||||||
this.__indicatorProgress = null;
|
this.__indicatorProgress = null;
|
||||||
},
|
},
|
||||||
|
@ -3,3 +3,4 @@ support-files = head.js
|
|||||||
|
|
||||||
[browser_basic_functionality.js]
|
[browser_basic_functionality.js]
|
||||||
[browser_first_download_panel.js]
|
[browser_first_download_panel.js]
|
||||||
|
[browser_overflow_anchor.js]
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the downloads button and indicator overflows into the nav-bar
|
||||||
|
* chevron properly, and then when those buttons are clicked in the overflow
|
||||||
|
* panel that the downloads panel anchors to the chevron.
|
||||||
|
*/
|
||||||
|
function test_task() {
|
||||||
|
try {
|
||||||
|
// Ensure that state is reset in case previous tests didn't finish.
|
||||||
|
yield task_resetState();
|
||||||
|
|
||||||
|
// Record the original width of the window so we can put it back when
|
||||||
|
// this test finishes.
|
||||||
|
let oldWidth = window.outerWidth;
|
||||||
|
|
||||||
|
// The downloads button should not be overflowed to begin with.
|
||||||
|
let button = CustomizableUI.getWidget("downloads-button")
|
||||||
|
.forWindow(window);
|
||||||
|
ok(!button.overflowed, "Downloads button should not be overflowed.");
|
||||||
|
|
||||||
|
// Hack - we lock the size of the default flex-y items in the nav-bar,
|
||||||
|
// namely, the URL and search inputs. That way we can resize the
|
||||||
|
// window without worrying about them flexing.
|
||||||
|
const kFlexyItems = ["urlbar-container", "search-container"];
|
||||||
|
registerCleanupFunction(() => unlockWidth(kFlexyItems));
|
||||||
|
lockWidth(kFlexyItems);
|
||||||
|
|
||||||
|
// Resize the window to half of its original size. That should
|
||||||
|
// be enough to overflow the downloads button.
|
||||||
|
window.resizeTo(oldWidth / 2, window.outerHeight);
|
||||||
|
yield waitForOverflowed(button, true);
|
||||||
|
|
||||||
|
let promise = promisePanelOpened();
|
||||||
|
button.node.doCommand();
|
||||||
|
yield promise;
|
||||||
|
|
||||||
|
let panel = DownloadsPanel.panel;
|
||||||
|
let chevron = document.getElementById("nav-bar-overflow-button");
|
||||||
|
is(panel.anchorNode, chevron, "Panel should be anchored to the chevron.");
|
||||||
|
|
||||||
|
DownloadsPanel.hidePanel();
|
||||||
|
|
||||||
|
// Unlock the widths on the flex-y items.
|
||||||
|
unlockWidth(kFlexyItems);
|
||||||
|
|
||||||
|
// Put the window back to its original dimensions.
|
||||||
|
window.resizeTo(oldWidth, window.outerHeight);
|
||||||
|
|
||||||
|
// The downloads button should eventually be un-overflowed.
|
||||||
|
yield waitForOverflowed(button, false);
|
||||||
|
|
||||||
|
// Now try opening the panel again.
|
||||||
|
promise = promisePanelOpened();
|
||||||
|
button.node.doCommand();
|
||||||
|
yield promise;
|
||||||
|
|
||||||
|
is(panel.anchorNode.id, "downloads-indicator-anchor");
|
||||||
|
|
||||||
|
DownloadsPanel.hidePanel();
|
||||||
|
} finally {
|
||||||
|
// Clean up when the test finishes.
|
||||||
|
yield task_resetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some node IDs, finds the nodes and sets their min-width's to their
|
||||||
|
* current width, preventing them from flex-shrinking.
|
||||||
|
*
|
||||||
|
* @param aItemIDs an array of item IDs to set min-width on.
|
||||||
|
*/
|
||||||
|
function lockWidth(aItemIDs) {
|
||||||
|
for (let itemID of aItemIDs) {
|
||||||
|
let item = document.getElementById(itemID);
|
||||||
|
let curWidth = item.getBoundingClientRect().width + "px";
|
||||||
|
item.style.minWidth = curWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the min-width's set on a set of IDs by lockWidth.
|
||||||
|
*
|
||||||
|
* @param aItemIDs an array of ItemIDs to remove min-width on.
|
||||||
|
*/
|
||||||
|
function unlockWidth(aItemIDs) {
|
||||||
|
for (let itemID of aItemIDs) {
|
||||||
|
let item = document.getElementById(itemID);
|
||||||
|
item.style.minWidth = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for a node to enter or exit the overflowed state.
|
||||||
|
*
|
||||||
|
* @param aItem the node to wait for.
|
||||||
|
* @param aIsOverflowed if we're waiting for the item to be overflowed.
|
||||||
|
*/
|
||||||
|
function waitForOverflowed(aItem, aIsOverflowed) {
|
||||||
|
let deferOverflow = Promise.defer();
|
||||||
|
if (aItem.overflowed == aIsOverflowed) {
|
||||||
|
return deferOverflow.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let observer = new MutationObserver(function(aMutations) {
|
||||||
|
if (aItem.overflowed == aIsOverflowed) {
|
||||||
|
observer.disconnect();
|
||||||
|
deferOverflow.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(aItem.node, {attributes: true});
|
||||||
|
|
||||||
|
return deferOverflow.promise;
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
PARALLEL_DIRS += [
|
PARALLEL_DIRS += [
|
||||||
'about',
|
'about',
|
||||||
'certerror',
|
'certerror',
|
||||||
|
'customizableui',
|
||||||
'dirprovider',
|
'dirprovider',
|
||||||
'downloads',
|
'downloads',
|
||||||
'feeds',
|
'feeds',
|
||||||
|
@ -1285,49 +1285,17 @@ BrowserGlue.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_migrateUI: function BG__migrateUI() {
|
_migrateUI: function BG__migrateUI() {
|
||||||
const UI_VERSION = 14;
|
const UI_VERSION = 17;
|
||||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
|
const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
|
||||||
|
|
||||||
let wasCustomizedAndOnAustralis = Services.prefs.prefHasUserValue("browser.uiCustomization.state");
|
|
||||||
let currentUIVersion = 0;
|
let currentUIVersion = 0;
|
||||||
try {
|
try {
|
||||||
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
|
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
|
||||||
} catch(ex) {}
|
} catch(ex) {}
|
||||||
if (!wasCustomizedAndOnAustralis && currentUIVersion >= UI_VERSION)
|
if (currentUIVersion >= UI_VERSION)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
|
this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
|
||||||
this._dataSource = this._rdf.GetDataSource("rdf:local-store");
|
this._dataSource = this._rdf.GetDataSource("rdf:local-store");
|
||||||
|
|
||||||
// No version check for this as this code should run until we have Australis everywhere:
|
|
||||||
if (wasCustomizedAndOnAustralis) {
|
|
||||||
// This profile's been on australis! If it's missing the back/fwd button
|
|
||||||
// or go/stop/reload button, then put them back:
|
|
||||||
let currentsetResource = this._rdf.GetResource("currentset");
|
|
||||||
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
|
||||||
let currentset = this._getPersist(toolbarResource, currentsetResource);
|
|
||||||
if (currentset.indexOf("unified-back-forward-button") == -1) {
|
|
||||||
currentset = currentset.replace("urlbar-container",
|
|
||||||
"unified-back-forward-button,urlbar-container");
|
|
||||||
}
|
|
||||||
if (currentset.indexOf("reload-button") == -1) {
|
|
||||||
currentset = currentset.replace("urlbar-container", "urlbar-container,reload-button");
|
|
||||||
}
|
|
||||||
if (currentset.indexOf("stop-button") == -1) {
|
|
||||||
currentset = currentset.replace("reload-button", "reload-button,stop-button");
|
|
||||||
}
|
|
||||||
this._setPersist(toolbarResource, currentsetResource, currentset);
|
|
||||||
Services.prefs.clearUserPref("browser.uiCustomization.state");
|
|
||||||
|
|
||||||
// If we don't have anything else to do, we can bail here:
|
|
||||||
if (currentUIVersion >= UI_VERSION) {
|
|
||||||
delete this._rdf;
|
|
||||||
delete this._dataSource;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this._dirty = false;
|
this._dirty = false;
|
||||||
|
|
||||||
if (currentUIVersion < 2) {
|
if (currentUIVersion < 2) {
|
||||||
@ -1511,6 +1479,60 @@ BrowserGlue.prototype = {
|
|||||||
OS.File.remove(path);
|
OS.File.remove(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentUIVersion < 15) {
|
||||||
|
// Migrate users from text or text&icons mode to icons mode.
|
||||||
|
let updateToolbars = function (aToolbarIds, aResourceName, aResourceValue) {
|
||||||
|
let resource = this._rdf.GetResource(aResourceName);
|
||||||
|
for (toolbarId of aToolbarIds) {
|
||||||
|
let toolbar = this._rdf.GetResource(BROWSER_DOCURL + toolbarId);
|
||||||
|
let oldValue = this._getPersist(toolbar, resource);
|
||||||
|
if (oldValue && oldValue != aResourceValue) {
|
||||||
|
this._setPersist(toolbar, resource, aResourceValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
updateToolbars(["navigator-toolbox", "nav-bar", "PersonalToolbar", "addon-bar"], "mode", "icons");
|
||||||
|
// Exclude PersonalToolbar and addon-bar since they have lockiconsize="true".
|
||||||
|
updateToolbars(["navigator-toolbox", "nav-bar"], "iconsize", "large");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUIVersion < 16) {
|
||||||
|
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
||||||
|
let collapsedResource = this._rdf.GetResource("collapsed");
|
||||||
|
let isCollapsed = this._getPersist(toolbarResource, collapsedResource);
|
||||||
|
if (isCollapsed == "true") {
|
||||||
|
this._setPersist(toolbarResource, collapsedResource, "false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the bookmarks-menu-button into the nav-bar if it isn't already
|
||||||
|
// there.
|
||||||
|
if (currentUIVersion < 17) {
|
||||||
|
let currentsetResource = this._rdf.GetResource("currentset");
|
||||||
|
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
|
||||||
|
let currentset = this._getPersist(toolbarResource, currentsetResource);
|
||||||
|
// Need to migrate only if toolbar is customized.
|
||||||
|
if (currentset) {
|
||||||
|
if (!currentset.contains("bookmarks-menu-button")) {
|
||||||
|
// The button isn't in the nav-bar, so let's look for an appropriate
|
||||||
|
// place to put it.
|
||||||
|
if (currentset.contains("downloads-button")) {
|
||||||
|
currentset = currentset.replace(/(^|,)downloads-button($|,)/,
|
||||||
|
"$1bookmarks-menu-button,downloads-button$2");
|
||||||
|
} else if (currentset.contains("home-button")) {
|
||||||
|
currentset = currentset.replace(/(^|,)home-button($|,)/,
|
||||||
|
"$1bookmarks-menu-button,home-button$2");
|
||||||
|
} else {
|
||||||
|
// Just append.
|
||||||
|
currentset = currentset.replace(/(^|,)window-controls($|,)/,
|
||||||
|
"$1bookmarks-menu-button,window-controls$2")
|
||||||
|
}
|
||||||
|
this._setPersist(toolbarResource, currentsetResource, currentset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this._dirty)
|
if (this._dirty)
|
||||||
this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
|
this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
|
||||||
|
|
||||||
|
@ -1745,3 +1745,157 @@ PlacesMenu.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function PlacesPanelMenuView(aPlace, aViewId, aRootId) {
|
||||||
|
this._viewElt = document.getElementById(aViewId);
|
||||||
|
this._rootElt = document.getElementById(aRootId);
|
||||||
|
this._viewElt._placesView = this;
|
||||||
|
|
||||||
|
PlacesViewBase.call(this, aPlace);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlacesPanelMenuView.prototype = {
|
||||||
|
__proto__: PlacesViewBase.prototype,
|
||||||
|
|
||||||
|
QueryInterface: function PAMV_QueryInterface(aIID) {
|
||||||
|
return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
uninit: function PAMV_uninit() {
|
||||||
|
PlacesViewBase.prototype.uninit.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
_insertNewItem:
|
||||||
|
function PAMV__insertNewItem(aChild, aBefore) {
|
||||||
|
this._domNodes.delete(aChild);
|
||||||
|
|
||||||
|
let type = aChild.type;
|
||||||
|
let button;
|
||||||
|
if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
|
||||||
|
button = document.createElement("toolbarseparator");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
button = document.createElement("toolbarbutton");
|
||||||
|
button.className = "bookmark-item";
|
||||||
|
button.setAttribute("label", aChild.title);
|
||||||
|
let icon = aChild.icon;
|
||||||
|
if (icon)
|
||||||
|
button.setAttribute("image", icon);
|
||||||
|
|
||||||
|
if (PlacesUtils.containerTypes.indexOf(type) != -1) {
|
||||||
|
button.setAttribute("container", "true");
|
||||||
|
|
||||||
|
if (PlacesUtils.nodeIsQuery(aChild)) {
|
||||||
|
button.setAttribute("query", "true");
|
||||||
|
if (PlacesUtils.nodeIsTagQuery(aChild))
|
||||||
|
button.setAttribute("tagContainer", "true");
|
||||||
|
}
|
||||||
|
else if (PlacesUtils.nodeIsFolder(aChild)) {
|
||||||
|
PlacesUtils.livemarks.getLivemark(
|
||||||
|
{ id: aChild.itemId },
|
||||||
|
function (aStatus, aLivemark) {
|
||||||
|
if (Components.isSuccessCode(aStatus)) {
|
||||||
|
button.setAttribute("livemark", "true");
|
||||||
|
this.controller.cacheLivemarkInfo(aChild, aLivemark);
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PlacesUtils.nodeIsURI(aChild)) {
|
||||||
|
button.setAttribute("scheme",
|
||||||
|
PlacesUIUtils.guessUrlSchemeForUI(aChild.uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button._placesNode = aChild;
|
||||||
|
if (!this._domNodes.has(aChild))
|
||||||
|
this._domNodes.set(aChild, button);
|
||||||
|
|
||||||
|
this._rootElt.insertBefore(button, aBefore);
|
||||||
|
},
|
||||||
|
|
||||||
|
nodeInserted:
|
||||||
|
function PAMV_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
|
||||||
|
let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
|
||||||
|
if (parentElt != this._rootElt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let children = this._rootElt.childNodes;
|
||||||
|
this._insertNewItem(aPlacesNode,
|
||||||
|
aIndex < children.length ? children[aIndex] : null);
|
||||||
|
},
|
||||||
|
|
||||||
|
nodeRemoved:
|
||||||
|
function PAMV_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
|
||||||
|
let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
|
||||||
|
if (parentElt != this._rootElt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
|
||||||
|
this._removeChild(elt);
|
||||||
|
},
|
||||||
|
|
||||||
|
nodeMoved:
|
||||||
|
function PAMV_nodeMoved(aPlacesNode,
|
||||||
|
aOldParentPlacesNode, aOldIndex,
|
||||||
|
aNewParentPlacesNode, aNewIndex) {
|
||||||
|
let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
|
||||||
|
if (parentElt != this._rootElt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
|
||||||
|
this._removeChild(elt);
|
||||||
|
this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]);
|
||||||
|
},
|
||||||
|
|
||||||
|
nodeAnnotationChanged:
|
||||||
|
function PAMV_nodeAnnotationChanged(aPlacesNode, aAnno) {
|
||||||
|
let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
|
||||||
|
// There's no UI representation for the root node.
|
||||||
|
if (elt == this._rootElt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (elt.parentNode != this._rootElt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// All livemarks have a feedURI, so use it as our indicator.
|
||||||
|
if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
|
||||||
|
elt.setAttribute("livemark", true);
|
||||||
|
|
||||||
|
PlacesUtils.livemarks.getLivemark(
|
||||||
|
{ id: aPlacesNode.itemId },
|
||||||
|
function (aStatus, aLivemark) {
|
||||||
|
if (Components.isSuccessCode(aStatus)) {
|
||||||
|
this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
|
||||||
|
this.invalidateContainer(aPlacesNode);
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
nodeTitleChanged: function PAMV_nodeTitleChanged(aPlacesNode, aNewTitle) {
|
||||||
|
let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
|
||||||
|
|
||||||
|
// There's no UI representation for the root node.
|
||||||
|
if (elt == this._rootElt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PlacesViewBase.prototype.nodeTitleChanged.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
invalidateContainer: function PAMV_invalidateContainer(aPlacesNode) {
|
||||||
|
let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
|
||||||
|
if (elt != this._rootElt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Container is the toolbar itself.
|
||||||
|
while (this._rootElt.hasChildNodes()) {
|
||||||
|
this._rootElt.removeChild(this._rootElt.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._resultNode.childCount; ++i) {
|
||||||
|
this._insertNewItem(this._resultNode.getChild(i), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -80,8 +80,8 @@
|
|||||||
elt = elt.parentNode;
|
elt = elt.parentNode;
|
||||||
|
|
||||||
// Calculate positions taking care of arrowscrollbox
|
// Calculate positions taking care of arrowscrollbox
|
||||||
let eventY = aEvent.layerY;
|
|
||||||
let scrollbox = this._scrollBox;
|
let scrollbox = this._scrollBox;
|
||||||
|
let eventY = aEvent.layerY + (scrollbox.boxObject.y - this.boxObject.y);
|
||||||
let scrollboxOffset = scrollbox.scrollBoxObject.y -
|
let scrollboxOffset = scrollbox.scrollBoxObject.y -
|
||||||
(scrollbox.boxObject.y - this.boxObject.y);
|
(scrollbox.boxObject.y - this.boxObject.y);
|
||||||
let eltY = elt.boxObject.y - scrollboxOffset;
|
let eltY = elt.boxObject.y - scrollboxOffset;
|
||||||
@ -485,4 +485,121 @@
|
|||||||
|
|
||||||
</handlers>
|
</handlers>
|
||||||
</binding>
|
</binding>
|
||||||
|
|
||||||
|
<!-- Most of this is copied from the arrowpanel binding in popup.xml -->
|
||||||
|
<binding id="places-popup-arrow"
|
||||||
|
extends="chrome://browser/content/places/menu.xml#places-popup-base">
|
||||||
|
<content flip="both" side="top" position="bottomcenter topleft">
|
||||||
|
<xul:box anonid="container" class="panel-arrowcontainer" flex="1"
|
||||||
|
xbl:inherits="side,panelopen">
|
||||||
|
<xul:box anonid="arrowbox" class="panel-arrowbox">
|
||||||
|
<xul:image anonid="arrow" class="panel-arrow" xbl:inherits="side"/>
|
||||||
|
</xul:box>
|
||||||
|
<xul:box class="panel-arrowcontent" xbl:inherits="side,align,dir,orient,pack" flex="1">
|
||||||
|
<xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
|
||||||
|
<xul:image class="menupopup-drop-indicator" mousethrough="always"/>
|
||||||
|
</xul:vbox>
|
||||||
|
<xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
|
||||||
|
smoothscroll="false">
|
||||||
|
<children/>
|
||||||
|
</xul:arrowscrollbox>
|
||||||
|
</xul:box>
|
||||||
|
</xul:box>
|
||||||
|
</content>
|
||||||
|
|
||||||
|
<implementation>
|
||||||
|
<method name="adjustArrowPosition">
|
||||||
|
<body><![CDATA[
|
||||||
|
var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
|
||||||
|
|
||||||
|
var anchor = this.anchorNode;
|
||||||
|
if (!anchor) {
|
||||||
|
arrow.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
|
||||||
|
var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
|
||||||
|
|
||||||
|
// if this panel has a "sliding" arrow, we may have previously set margins...
|
||||||
|
arrowbox.style.removeProperty("margin");
|
||||||
|
|
||||||
|
var position = this.alignmentPosition;
|
||||||
|
if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
|
||||||
|
container.orient = "";
|
||||||
|
arrowbox.orient = "vertical";
|
||||||
|
if (position.indexOf("_after") > 0) {
|
||||||
|
arrowbox.pack = "end";
|
||||||
|
arrowbox.style.marginBottom = this.alignmentOffset + "px";
|
||||||
|
} else {
|
||||||
|
arrowbox.pack = "start";
|
||||||
|
arrowbox.style.marginTop = this.alignmentOffset + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
// The assigned side stays the same regardless of direction.
|
||||||
|
var isRTL = (window.getComputedStyle(this).direction == "rtl");
|
||||||
|
|
||||||
|
if (position.indexOf("start_") == 0) {
|
||||||
|
container.dir = "reverse";
|
||||||
|
this.setAttribute("side", isRTL ? "left" : "right");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
container.dir = "";
|
||||||
|
this.setAttribute("side", isRTL ? "right" : "left");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (position.indexOf("before_") == 0 || position.indexOf("after_") == 0) {
|
||||||
|
container.orient = "vertical";
|
||||||
|
arrowbox.orient = "";
|
||||||
|
if (position.indexOf("_end") > 0) {
|
||||||
|
arrowbox.pack = "end";
|
||||||
|
arrowbox.style.marginRight = this.alignmentOffset + "px";
|
||||||
|
} else {
|
||||||
|
arrowbox.pack = "start";
|
||||||
|
arrowbox.style.marginLeft = this.alignmentOffset + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.indexOf("before_") == 0) {
|
||||||
|
container.dir = "reverse";
|
||||||
|
this.setAttribute("side", "bottom");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
container.dir = "";
|
||||||
|
this.setAttribute("side", "top");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arrow.hidden = false;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
</implementation>
|
||||||
|
|
||||||
|
<handlers>
|
||||||
|
<handler event="popupshowing" phase="target"><![CDATA[
|
||||||
|
this.adjustArrowPosition();
|
||||||
|
]]></handler>
|
||||||
|
<handler event="popupshown" phase="target"><![CDATA[
|
||||||
|
this.setAttribute("panelopen", "true");
|
||||||
|
|
||||||
|
// Allow anchoring to a specified element inside the anchor.
|
||||||
|
var anchorClass = this.getAttribute("anonanchorclass");
|
||||||
|
if (anchorClass && this.anchorNode) {
|
||||||
|
let anchor =
|
||||||
|
document.getAnonymousElementByAttribute(this.anchorNode, "class",
|
||||||
|
anchorClass);
|
||||||
|
if (anchor) {
|
||||||
|
let offsetX = anchor.boxObject.width / 2;
|
||||||
|
if (this.alignmentPosition.endsWith("_end"))
|
||||||
|
offsetX *= -1;
|
||||||
|
this.popupBoxObject.moveToAnchor(anchor, this.alignmentPosition,
|
||||||
|
offsetX, 0,
|
||||||
|
false);
|
||||||
|
this.adjustArrowPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></handler>
|
||||||
|
<handler event="popuphidden" phase="target"><![CDATA[
|
||||||
|
this.removeAttribute("panelopen");
|
||||||
|
]]></handler>
|
||||||
|
</handlers>
|
||||||
|
</binding>
|
||||||
</bindings>
|
</bindings>
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
Cu.import("resource:///modules/CustomizableUI.jsm");
|
||||||
|
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
let cw;
|
let cw;
|
||||||
let win;
|
let win;
|
||||||
@ -31,10 +34,7 @@ function test() {
|
|||||||
let pos = currentSet.indexOf(buttonId);
|
let pos = currentSet.indexOf(buttonId);
|
||||||
|
|
||||||
if (-1 < pos) {
|
if (-1 < pos) {
|
||||||
currentSet.splice(pos, 1);
|
CustomizableUI.removeWidgetFromArea("tabview-button");
|
||||||
toolbar.setAttribute("currentset", currentSet.join(","));
|
|
||||||
toolbar.currentSet = currentSet.join(",");
|
|
||||||
win.document.persist(toolbar.id, "currentset");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user