--- a/accessible/tests/mochitest/events/test_focus_general.html
+++ b/accessible/tests/mochitest/events/test_focus_general.html
@@ -91,17 +91,17 @@
gQueue = new eventQueue();
gQueue.push(new synthFocus("editablearea"));
gQueue.push(new synthFocus("navarea"));
gQueue.push(new synthTab("navarea", new focusChecker(frameDoc)));
gQueue.push(new focusElmWhileSubdocIsFocused("link"));
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,
// other platforms requires setting a ui.key.menuAccessKeyFocuses
// preference.
gQueue.push(new toggleTopMenu(editableDoc, new topMenuChecker()));
gQueue.push(new toggleTopMenu(editableDoc, new focusChecker(editableDoc)));
}
gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
--- a/accessible/tests/mochitest/events/test_menu.xul
+++ b/accessible/tests/mochitest/events/test_menu.xul
@@ -151,17 +151,17 @@
gQueue.push(new openFileMenu());
gQueue.push(new openEditMenu());
gQueue.push(new closeEditMenu());
// Alt key is used to active menubar and focus menu item on Windows,
// other platforms requires setting a ui.key.menuAccessKeyFocuses
// preference.
- if (WIN) {
+ if (WIN || LINUX) {
gQueue.push(new focusFileMenu());
gQueue.push(new focusEditMenu());
gQueue.push(new leaveMenubar());
}
gQueue.invoke(); // Will call SimpleTest.finish();
}
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -439,21 +439,20 @@ pref("browser.tabs.warnOnCloseOtherTabs"
pref("browser.tabs.warnOnOpen", true);
pref("browser.tabs.maxOpenBeforeWarn", 15);
pref("browser.tabs.loadInBackground", true);
pref("browser.tabs.opentabfor.middleclick", true);
pref("browser.tabs.loadDivertedInBackground", false);
pref("browser.tabs.loadBookmarksInBackground", false);
pref("browser.tabs.tabClipWidth", 140);
pref("browser.tabs.animate", true);
-pref("browser.tabs.onTop", true);
-#ifdef XP_WIN
+#ifdef UNIX_BUT_NOT_MAC
+pref("browser.tabs.drawInTitlebar", false);
+#else
pref("browser.tabs.drawInTitlebar", true);
-#else
-pref("browser.tabs.drawInTitlebar", false);
#endif
// Where to show tab close buttons:
// 0 on active tab only
// 1 on all tabs until tabClipWidth is reached, then active tab only
// 2 no close buttons at all
// 3 at the end of the tabstrip
pref("browser.tabs.closeButtons", 1);
@@ -1317,8 +1316,11 @@ pref("plain_text.wrap_long_lines", true)
pref("dom.debug.propagate_gesture_events_through_content", false);
// The request URL of the GeoLocation backend.
pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%GOOGLE_API_KEY%");
// Necko IPC security checks only needed for app isolation for cookies/cache/etc:
// currently irrelevant for desktop e10s
pref("network.disable.ipc.security", true);
+
+// CustomizableUI debug logging.
+pref("browser.uiCustomization.debug", false);
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -173,60 +173,16 @@ const gXPInstallObserver = {
PopupNotifications.show(browser, notificationID, messageString, anchorID,
action, null, options);
break;
}
}
};
-/*
- * 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 = {
handleEvent: function (event) {
switch (event.type) {
case "InstallBrowserTheme":
case "PreviewBrowserTheme":
case "ResetBrowserThemePreview":
// ignore requests from background tabs
if (event.target.ownerDocument.defaultView.top != content)
@@ -410,8 +366,65 @@ var LightWeightThemeWebInstaller = {
return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
},
_getThemeFromNode: function (node) {
return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
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 + ")");
+ },
+};
deleted file mode 100644
--- a/browser/base/content/browser-appmenu.inc
+++ /dev/null
@@ -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>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-customization.js
@@ -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();
+ }
+}
--- a/browser/base/content/browser-feeds.js
+++ b/browser/base/content/browser-feeds.js
@@ -3,85 +3,72 @@
# 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/.
/**
* The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
* and shows UI when they are discovered.
*/
var FeedHandler = {
- /**
- * The click handler for the Feed icon in the toolbar. Opens the
- * subscription page if user is not given a choice of 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.
+ /** Called when the user clicks on the Subscribe to This Page... menu item,
+ * or when the user clicks the feed button when the page contains multiple
+ * feeds.
* Builds a menu of unique feeds associated with the page, and if there
* is only one, shows the feed inline in the browser window.
- * @param menuPopup
- * The feed list menupopup to be populated.
- * @returns true if the menu should be shown, false if there was only
+ * @param container
+ * The feed list container (menupopup or subview) to be populated.
+ * @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
- * 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;
- if (feeds == null) {
+ if (!isSubview && feeds == null) {
// XXX hack -- menu opening depends on setting of an "open"
// attribute, and the menu refuses to open if that attribute is
// set (because it thinks it's already open). onpopupshowing gets
// called after the attribute is unset, and it doesn't get unset
// if we return false. so we unset it here; otherwise, the menu
// refuses to work past this point.
- menuPopup.parentNode.removeAttribute("open");
+ container.parentNode.removeAttribute("open");
return false;
}
- while (menuPopup.firstChild)
- menuPopup.removeChild(menuPopup.firstChild);
+ while (container.firstChild)
+ container.removeChild(container.firstChild);
- if (feeds.length <= 1)
+ if (!feeds || feeds.length <= 1)
return false;
// Build the menu showing the available feed choices for viewing.
+ var itemNodeType = isSubview ? "toolbarbutton" : "menuitem";
for (let feedInfo of feeds) {
- var menuItem = document.createElement("menuitem");
+ var item = document.createElement(itemNodeType);
var baseTitle = feedInfo.title || feedInfo.href;
var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
- menuItem.setAttribute("class", "feed-menuitem");
- menuItem.setAttribute("label", labelStr);
- menuItem.setAttribute("feed", feedInfo.href);
- menuItem.setAttribute("tooltiptext", feedInfo.href);
- menuItem.setAttribute("crop", "center");
- menuPopup.appendChild(menuItem);
+ item.setAttribute("class", "feed-" + itemNodeType);
+ item.setAttribute("label", labelStr);
+ item.setAttribute("feed", feedInfo.href);
+ item.setAttribute("tooltiptext", feedInfo.href);
+ item.setAttribute("crop", "center");
+ if (isSubview) {
+ item.setAttribute("tabindex", "0");
+ }
+ container.appendChild(item);
}
return true;
},
/**
* Subscribe to a given feed. Called when
* 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
- * 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
* @param href
* The feed to subscribe to. May be null, in which case the
* event target's feed attribute is examined.
* @param event
* The event this method is handling. Used to decide where
* to open the preview UI. (Optional, unless href is null)
*/
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -12,17 +12,17 @@ var FullScreen = {
toggle: function (event) {
var enterFS = window.fullScreen;
// We get the fullscreen event _before_ the window transitions into or out of FS mode.
if (event && event.type == "fullscreen")
enterFS = !enterFS;
// 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");
if (enterFS) {
fullscreenCommand.setAttribute("checked", enterFS);
} else {
fullscreenCommand.removeAttribute("checked");
}
#ifdef XP_MACOSX
@@ -512,50 +512,33 @@ var FullScreen = {
showXULChrome: function(aTag, aShow)
{
var els = document.getElementsByTagNameNS(this._XULNS, aTag);
for (let el of els) {
// XXX don't interfere with previously collapsed toolbars
if (el.getAttribute("fullscreentoolbar") == "true") {
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,
// otherwise remove context menu to prevent breakage
el.setAttribute("saved-context", el.getAttribute("context"));
if (el.id == "nav-bar" || el.id == "TabsToolbar")
el.setAttribute("context", "autohide-context");
else
el.removeAttribute("context");
// Set the inFullscreen attribute to allow specific styling
// in fullscreen mode
el.setAttribute("inFullscreen", true);
}
else {
- var restoreAttr = function restoreAttr(attrName) {
- var savedAttr = "saved-" + attrName;
- if (el.hasAttribute(savedAttr)) {
- el.setAttribute(attrName, el.getAttribute(savedAttr));
- el.removeAttribute(savedAttr);
- }
+ if (el.hasAttribute("saved-context")) {
+ el.setAttribute("context", el.getAttribute("saved-context"));
+ el.removeAttribute("saved-context");
}
-
- restoreAttr("mode");
- restoreAttr("iconsize");
- restoreAttr("context");
-
el.removeAttribute("inFullscreen");
}
} else {
// use moz-collapsed so it doesn't persist hidden/collapsed,
// so that new windows don't have missing toolbars
if (aShow)
el.removeAttribute("moz-collapsed");
else
@@ -566,21 +549,19 @@ var FullScreen = {
if (aShow) {
gNavToolbox.removeAttribute("inFullscreen");
document.documentElement.removeAttribute("inFullscreen");
} else {
gNavToolbox.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 navbar = document.getElementById("nav-bar");
- var ctlsOnTabbar = window.toolbar.visible && (navbar.collapsed || TabsOnTop.enabled);
+ var ctlsOnTabbar = window.toolbar.visible;
if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
fullscreenctls.removeAttribute("flex");
document.getElementById("TabsToolbar").appendChild(fullscreenctls);
}
else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) {
fullscreenctls.setAttribute("flex", "1");
navbar.appendChild(fullscreenctls);
}
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -392,16 +392,17 @@ var FullZoom = {
/**
* Saves the zoom level of the page in the given browser to the content
* prefs store.
*
* @param browser The zoom of this browser will be saved. Required.
*/
_applyZoomToPref: function FullZoom__applyZoomToPref(browser) {
+ Services.obs.notifyObservers(null, "browser-fullZoom:zoomChange", "");
if (!this.siteSpecific ||
gInPrintPreviewMode ||
browser.contentDocument.mozSyntheticDocument)
return;
this._cps2.set(browser.currentURI.spec, this.name,
ZoomManager.getZoomForBrowser(browser),
this._loadContextFromWindow(browser.contentWindow), {
@@ -412,16 +413,17 @@ var FullZoom = {
},
/**
* Removes from the content prefs store the zoom level of the given browser.
*
* @param browser The zoom of this browser will be removed. Required.
*/
_removePref: function FullZoom__removePref(browser) {
+ Services.obs.notifyObservers(null, "browser-fullZoom:zoomReset", "");
if (browser.contentDocument.mozSyntheticDocument)
return;
let ctxt = this._loadContextFromWindow(browser.contentWindow);
this._cps2.removeByDomainAndName(browser.currentURI.spec, this.name, ctxt, {
handleCompletion: function () {
this._isNextContentPrefChangeInternal = true;
}.bind(this),
});
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -180,21 +180,16 @@
accesskey="&viewMenu.accesskey;">
<menupopup id="menu_viewPopup"
onpopupshowing="updateCharacterEncodingMenuState();">
<menu id="viewToolbarsMenu"
label="&viewToolbarsMenu.label;"
accesskey="&viewToolbarsMenu.accesskey;">
<menupopup onpopupshowing="onViewToolbarsPopupShowing(event);">
<menuseparator/>
- <menuitem id="menu_tabsOnTop"
- command="cmd_ToggleTabsOnTop"
- type="checkbox"
- label="&viewTabsOnTop.label;"
- accesskey="&viewTabsOnTop.accesskey;"/>
<menuitem id="menu_customizeToolbars"
label="&viewCustomizeToolbar.label;"
accesskey="&viewCustomizeToolbar.accesskey;"
command="cmd_CustomizeToolbars"/>
</menupopup>
</menu>
<menu id="viewSidebarMenuMenu"
label="&viewSidebarMenu.label;"
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -129,19 +129,16 @@ var StarUI = {
this._doShowEditBookmarkPanel(aItemId, aAnchorElement, aPosition);
return;
}
this._overlayLoading = true;
document.loadOverlay(
"chrome://browser/content/places/editBookmarkOverlay.xul",
(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,
// so that it aligns nicely with the other items (bug 484022).
let header = this._element("editBookmarkPanelHeader");
let rows = this._element("editBookmarkPanelGrid").lastChild;
rows.insertBefore(header, rows.firstChild);
header.hidden = false;
this._overlayLoading = false;
@@ -740,19 +737,20 @@ var BookmarksEventHandler = {
};
////////////////////////////////////////////////////////////////////////////////
//// PlacesMenuDNDHandler
// 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).
var PlacesMenuDNDHandler = {
- _springLoadDelay: 350, // milliseconds
+ _springLoadDelayMs: 350,
+ _closeDelayMs: 500,
_loadTimer: null,
- _closerTimer: null,
+ _closeTimer: null,
/**
* Called when the user enters the <menu> element during a drag.
* @param event
* The DragEnter event that spawned the opening.
*/
onDragEnter: function PMDH_onDragEnter(event) {
// Opening menus in a Places popup is handled by the view itself.
@@ -763,30 +761,31 @@ var PlacesMenuDNDHandler = {
if (this._loadTimer || popup.state === "showing" || popup.state === "open")
return;
this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._loadTimer.initWithCallback(() => {
this._loadTimer = null;
popup.setAttribute("autoopened", "true");
popup.showPopup(popup);
- }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+ }, this._springLoadDelayMs, Ci.nsITimer.TYPE_ONE_SHOT);
event.preventDefault();
event.stopPropagation();
},
/**
* Handles dragleave on the <menu> element.
* @returns true if the element is a container element (menu or
* menu-toolbarbutton), false otherwise.
*/
onDragLeave: function PMDH_onDragLeave(event) {
// Handle menu-button separate targets.
if (event.relatedTarget === event.currentTarget ||
- event.relatedTarget.parentNode === event.currentTarget)
+ (event.relatedTarget &&
+ event.relatedTarget.parentNode === event.currentTarget))
return;
// Closing menus in a Places popup is handled by the view itself.
if (!this._isStaticContainer(event.target))
return;
let popup = event.target.lastChild;
@@ -802,17 +801,17 @@ var PlacesMenuDNDHandler = {
while (node && !inHierarchy) {
inHierarchy = node == event.target;
node = node.parentNode;
}
if (!inHierarchy && popup && popup.hasAttribute("autoopened")) {
popup.removeAttribute("autoopened");
popup.hidePopup();
}
- }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+ }, this._closeDelayMs, Ci.nsITimer.TYPE_ONE_SHOT);
},
/**
* Determines if a XUL element represents a static container.
* @returns true if the element is a container element (menu or
*` menu-toolbarbutton), false otherwise.
*/
_isStaticContainer: function PMDH__isContainer(node) {
@@ -870,83 +869,107 @@ let PlacesToolbarHelper = {
return document.getElementById("PlacesToolbar");
},
init: function PTH_init() {
let viewElt = this._viewElt;
if (!viewElt || viewElt._placesView)
return;
- // If the bookmarks toolbar item is hidden because the parent toolbar is
- // collapsed or hidden (i.e. in a popup), spare the initialization. Also,
- // there is no need to initialize the toolbar if customizing because
- // init() will be called when the customization is done.
- let toolbar = viewElt.parentNode.parentNode;
- if (toolbar.collapsed ||
- getComputedStyle(toolbar, "").display == "none" ||
- this._isCustomizing)
+ // If the bookmarks toolbar item is:
+ // - not in a toolbar, or;
+ // - the toolbar is collapsed, or;
+ // - the toolbar is hidden some other way:
+ // don't initialize. Also, there is no need to initialize the toolbar if
+ // customizing, because that will happen when the customization is done.
+ let toolbar = this._getParentToolbar(viewElt);
+ if (!toolbar || toolbar.collapsed || this._isCustomizing ||
+ getComputedStyle(toolbar, "").display == "none")
return;
new PlacesToolbar(this._place);
},
customizeStart: function PTH_customizeStart() {
- let viewElt = this._viewElt;
- if (viewElt && viewElt._placesView)
- viewElt._placesView.uninit();
-
- this._isCustomizing = true;
+ try {
+ let viewElt = this._viewElt;
+ if (viewElt && viewElt._placesView)
+ viewElt._placesView.uninit();
+ } finally {
+ this._isCustomizing = true;
+ }
},
customizeDone: function PTH_customizeDone() {
this._isCustomizing = false;
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;
}
};
////////////////////////////////////////////////////////////////////////////////
//// BookmarkingUI
/**
- * Handles the bookmarks star button in the URL bar, as well as the bookmark
- * menu button.
+ * Handles the bookmarks menu-button in the toolbar.
*/
let BookmarkingUI = {
get button() {
- if (!this._button) {
- this._button = document.getElementById("bookmarks-menu-button");
+ let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
+ if (widgetGroup.areaType == CustomizableUI.TYPE_TOOLBAR) {
+ return widgetGroup.forWindow(window).node;
}
- return this._button;
+ return null;
},
get star() {
- if (!this._star) {
- this._star = document.getElementById("star-button");
- }
- return this._star;
+ let button = this.button;
+ return button && document.getAnonymousElementByAttribute(button, "anonid",
+ "button");
},
get anchor() {
- if (this.star && isElementVisible(this.star)) {
- // Anchor to the icon, so the panel looks more natural.
- return this.star;
- }
- return null;
+ let widget = CustomizableUI.getWidget("bookmarks-menu-button")
+ .forWindow(window);
+ if (widget.overflowed)
+ return widget.anchor;
+
+ let star = this.star;
+ return star && document.getAnonymousElementByAttribute(star, "class",
+ "toolbarbutton-icon");
},
STATUS_UPDATING: -1,
STATUS_UNSTARRED: 0,
STATUS_STARRED: 1,
get status() {
if (this._pendingStmt)
return this.STATUS_UPDATING;
- return this.star &&
- this.star.hasAttribute("starred") ? this.STATUS_STARRED
- : this.STATUS_UNSTARRED;
+ let button = this.button;
+ return button && button.hasAttribute("starred") ? this.STATUS_STARRED
+ : this.STATUS_UNSTARRED;
},
get _starredTooltip()
{
delete this._starredTooltip;
return this._starredTooltip =
gNavigatorBundle.getString("starButtonOn.tooltip");
},
@@ -969,97 +992,100 @@ let BookmarkingUI = {
this._popupNeedsUpdate = true;
},
onPopupShowing: function BUI_onPopupShowing(event) {
// Don't handle events for submenus.
if (event.target != event.currentTarget)
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)
return;
this._popupNeedsUpdate = false;
let popup = event.target;
let getPlacesAnonymousElement =
aAnonId => document.getAnonymousElementByAttribute(popup.parentNode,
"placesanonid",
aAnonId);
let viewToolbarMenuitem = getPlacesAnonymousElement("view-toolbar");
if (viewToolbarMenuitem) {
// Update View bookmarks toolbar checkbox menuitem.
let personalToolbar = document.getElementById("PersonalToolbar");
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"));
- }
},
/**
* Handles star styling based on page proxy state changes.
*/
onPageProxyStateChanged: function BUI_onPageProxyStateChanged(aState) {
if (!this.star) {
return;
}
if (aState == "invalid") {
this.star.setAttribute("disabled", "true");
- this.star.removeAttribute("starred");
+ this.button.removeAttribute("starred");
+ this.button.setAttribute("buttontooltiptext", "");
}
else {
this.star.removeAttribute("disabled");
}
+ this._updateToolbarStyle();
},
_updateToolbarStyle: function BUI__updateToolbarStyle() {
- if (!this.button) {
+ let button = this.button;
+ if (!button)
return;
- }
let personalToolbar = document.getElementById("PersonalToolbar");
- let onPersonalToolbar = this.button.parentNode == personalToolbar ||
- this.button.parentNode.parentNode == personalToolbar;
+ let onPersonalToolbar = button.parentNode == personalToolbar ||
+ button.parentNode.parentNode == personalToolbar;
if (onPersonalToolbar) {
- this.button.classList.add("bookmark-item");
- this.button.classList.remove("toolbarbutton-1");
+ button.classList.add("bookmark-item");
+ button.classList.remove("toolbarbutton-1");
}
else {
- this.button.classList.remove("bookmark-item");
- this.button.classList.add("toolbarbutton-1");
+ button.classList.remove("bookmark-item");
+ button.classList.add("toolbarbutton-1");
}
},
_uninitView: function BUI__uninitView() {
// When an element with a placesView attached is removed and re-inserted,
// XBL reapplies the binding causing any kind of issues and possible leaks,
// so kill current view and let popupshowing generate a new one.
- if (this.button && this.button._placesView) {
- this.button._placesView.uninit();
- }
+ let button = this.button;
+ if (button && button._placesView)
+ button._placesView.uninit();
},
customizeStart: function BUI_customizeStart() {
this._uninitView();
},
customizeChange: function BUI_customizeChange() {
this._updateToolbarStyle();
},
customizeDone: function BUI_customizeDone() {
- delete this._button;
this.onToolbarVisibilityChange();
this._updateToolbarStyle();
},
_hasBookmarksObserver: false,
uninit: function BUI_uninit() {
this._uninitView();
@@ -1069,17 +1095,17 @@ let BookmarkingUI = {
if (this._pendingStmt) {
this._pendingStmt.cancel();
delete this._pendingStmt;
}
},
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;
}
// Reset tracked values.
this._uri = gBrowser.currentURI;
this._itemIds = [];
if (this._pendingStmt) {
@@ -1118,63 +1144,127 @@ let BookmarkingUI = {
}
}
delete this._pendingStmt;
}, this);
},
_updateStar: function BUI__updateStar() {
- if (!this.star) {
+ let button = this.button;
+ if (!button)
return;
- }
if (this._itemIds.length > 0) {
- this.star.setAttribute("starred", "true");
- this.star.setAttribute("tooltiptext", this._starredTooltip);
+ button.setAttribute("starred", "true");
+ button.setAttribute("buttontooltiptext", this._starredTooltip);
}
else {
- this.star.removeAttribute("starred");
- this.star.setAttribute("tooltiptext", this._unstarredTooltip);
+ button.removeAttribute("starred");
+ button.setAttribute("buttontooltiptext", this._unstarredTooltip);
}
},
onCommand: function BUI_onCommand(aEvent) {
if (aEvent.target != aEvent.currentTarget) {
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.
if (!this._pendingStmt) {
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
onItemAdded: function BUI_onItemAdded(aItemId, aParentId, aIndex, aItemType,
aURI) {
+ if (!this.button) {
+ return;
+ }
+
if (aURI && aURI.equals(this._uri)) {
// If a new bookmark has been added to the tracked uri, register it.
if (this._itemIds.indexOf(aItemId) == -1) {
this._itemIds.push(aItemId);
this._updateStar();
}
}
},
onItemRemoved: function BUI_onItemRemoved(aItemId) {
+ if (!this.button) {
+ return;
+ }
+
let index = this._itemIds.indexOf(aItemId);
// If one of the tracked bookmarks has been removed, unregister it.
if (index != -1) {
this._itemIds.splice(index, 1);
this._updateStar();
}
},
onItemChanged: function BUI_onItemChanged(aItemId, aProperty,
aIsAnnotationProperty, aNewValue) {
+ if (!this.button) {
+ return;
+ }
+
if (aProperty == "uri") {
let index = this._itemIds.indexOf(aItemId);
// If the changed bookmark was tracked, check if it is now pointing to
// a different uri and unregister it.
if (index != -1 && aNewValue != this._uri.spec) {
this._itemIds.splice(index, 1);
this._updateStar();
}
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -27,17 +27,16 @@
<command id="Browser:SendLink"
oncommand="MailIntegration.sendLinkForWindow(window.content);"/>
<command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
<command id="cmd_print" oncommand="PrintUtils.print();"/>
<command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
<command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
<command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
- <command id="cmd_ToggleTabsOnTop" oncommand="TabsOnTop.toggle()"/>
<command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
<command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
<commandset id="editMenuCommands"/>
<command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(content.document);" observes="isImage"/>
<command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
@@ -104,23 +103,23 @@
<command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" disabled="true" hidden="true"/>
<command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)" disabled="true" hidden="true"/>
<command id="Tools:Sanitize"
oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
<command id="Tools:PrivateBrowsing"
oncommand="OpenBrowserWindow({private: true});"/>
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
- <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
<command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
<command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();" 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:Toggle" oncommand="Social.toggle();" hidden="true"/>
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
+ <command id="MenuPanel:Toggle" oncommand="PanelUI.toggle(event);"/>
</commandset>
<commandset id="placesCommands">
<command id="Browser:ShowAllBookmarks"
oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
<command id="Browser:ShowAllHistory"
oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
</commandset>
@@ -407,33 +406,38 @@
<key id="key_quitApplication" key="&quitApplicationCmdUnix.key;" command="cmd_quitApplication" modifiers="accel"/>
#endif
#ifdef FULL_BROWSER_WINDOW
<key id="key_undoCloseTab" command="History:UndoCloseTab" key="&tabCmd.commandkey;" modifiers="accel,shift"/>
#endif
<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
#define NUM_SELECT_TAB_MODIFIER alt
#else
#define NUM_SELECT_TAB_MODIFIER accel
#endif
#expand <key id="key_selectTab1" oncommand="gBrowser.selectTabAtIndex(0, event);" key="1" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab2" oncommand="gBrowser.selectTabAtIndex(1, event);" key="2" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab3" oncommand="gBrowser.selectTabAtIndex(2, event);" key="3" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab4" oncommand="gBrowser.selectTabAtIndex(3, event);" key="4" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab5" oncommand="gBrowser.selectTabAtIndex(4, event);" key="5" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab6" oncommand="gBrowser.selectTabAtIndex(5, event);" key="6" modifiers="__NUM_SELECT_TAB_MODIFIER__"/>
#expand <key id="key_selectTab7" oncommand="gBrowser.selectTabAtIndex(6, event);" key="7" 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__"/>
- <key id="key_toggleAddonBar" command="Browser:ToggleAddonBar" key="&toggleAddonBarCmd.key;" modifiers="accel"/>
-
</keyset>
# Used by baseMenuOverlay
#ifdef XP_MACOSX
<commandset id="baseMenuCommandSet" />
#endif
<keyset id="baseMenuKeyset" />
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -790,19 +790,17 @@ SocialShare = {
}, true);
}
// always ensure that origin belongs to the endpoint
let uri = Services.io.newURI(shareEndpoint, null, null);
iframe.setAttribute("origin", provider.origin);
iframe.setAttribute("src", shareEndpoint);
let navBar = document.getElementById("nav-bar");
- let anchor = navBar.getAttribute("mode") == "text" ?
- document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-text") :
- document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-icon");
+ let anchor = document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-icon");
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
}
};
SocialMenu = {
populate: function SocialMenu_populate() {
let submenu = document.getElementById("menu_social-statusarea-popup");
@@ -1125,19 +1123,17 @@ SocialToolbar = {
setTimeout(function() {
dispatchPanelEvent("socialFrameShow");
}, 0);
}, true);
}
});
let navBar = document.getElementById("nav-bar");
- let anchor = navBar.getAttribute("mode") == "text" ?
- document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-text") :
- document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-badge-container");
+ let anchor = document.getAnonymousElementByAttribute(aToolbarButton, "class", "toolbarbutton-badge-container");
// Bug 849216 - open the popup in a setTimeout so we avoid the auto-rollup
// handling from preventing it being opened in some cases.
setTimeout(function() {
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
}, 0);
},
setPanelErrorMessage: function SocialToolbar_setPanelErrorMessage(aNotificationFrame) {
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -421,26 +421,23 @@ let TabView = {
_addToolbarButton: function TabView__addToolbarButton() {
let buttonId = "tabview-button";
if (document.getElementById(buttonId))
return;
let toolbar = document.getElementById("TabsToolbar");
let currentSet = toolbar.currentSet.split(",");
-
let alltabsPos = currentSet.indexOf("alltabs-button");
if (-1 == alltabsPos)
return;
- currentSet[alltabsPos] += "," + buttonId;
- currentSet = currentSet.join(",");
- toolbar.currentSet = currentSet;
- toolbar.setAttribute("currentset", currentSet);
- document.persist(toolbar.id, "currentset");
+ let allTabsBtn = document.getElementById("alltabs-button");
+ let nextItem = allTabsBtn.nextSibling;
+ toolbar.insertItem(buttonId, nextItem);
},
// ----------
// Function: updateGroupNumberBroadcaster
// Updates the group number broadcaster.
updateGroupNumberBroadcaster: function TabView_updateGroupNumberBroadcaster(number) {
let groupsNumber = document.getElementById("tabviewGroupsNumber");
groupsNumber.setAttribute("groups", number);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -16,16 +16,73 @@
searchbar {
-moz-binding: url("chrome://browser/content/search/search.xml#searchbar");
}
.browserStack > browser[remote="true"] {
-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 {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
}
.tabbrowser-tabs {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
}
@@ -42,31 +99,29 @@ tabbrowser {
}
.tabbrowser-tab {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
}
.tabbrowser-tab:not([pinned]) {
-moz-box-flex: 100;
- max-width: 250px;
+ max-width: 180px;
min-width: 100px;
width: 0;
transition: min-width 200ms ease-out,
- max-width 250ms ease-out,
- opacity 50ms ease-out 20ms /* hide the tab for the first 20ms of the max-width transition */;
+ max-width 230ms ease-out;
}
.tabbrowser-tab:not([pinned]):not([fadein]) {
max-width: 0.1px;
min-width: 0.1px;
- opacity: 0 !important;
+ visibility: hidden;
transition: min-width 200ms ease-out,
- max-width 250ms ease-out,
- opacity 50ms ease-out 180ms /* hide the tab for the last 20ms of the max-width transition */;
+ max-width 230ms ease-out;
}
.tab-throbber:not([fadein]):not([pinned]),
.tab-label:not([fadein]):not([pinned]),
.tab-icon-image:not([fadein]):not([pinned]),
.tab-close-button:not([fadein]):not([pinned]) {
display: none;
}
@@ -89,30 +144,23 @@ tabbrowser {
#alltabs-popup {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup");
}
toolbar[printpreview="true"] {
-moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
}
-#toolbar-menubar {
- -moz-box-ordinal-group: 5;
+toolbar[overflowable] > .customization-target {
+ overflow: hidden;
}
-#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
- -moz-box-ordinal-group: 50;
-}
-
-#TabsToolbar {
- -moz-box-ordinal-group: 100;
-}
-
-#TabsToolbar[tabsontop="true"] {
- -moz-box-ordinal-group: 10;
+toolbar:not([overflowing]) > .overflow-button,
+toolbar[customizing] > .overflow-button {
+ display: none;
}
%ifdef CAN_DRAW_IN_TITLEBAR
#main-window[inFullscreen] > #titlebar,
#main-window[inFullscreen] .titlebar-placeholder,
#main-window:not([tabsintitlebar]) .titlebar-placeholder {
display: none;
}
@@ -120,53 +168,79 @@ toolbar[printpreview="true"] {
#titlebar {
-moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
}
#titlebar-spacer {
pointer-events: none;
}
-#main-window[tabsintitlebar] #appmenu-button-container,
#main-window[tabsintitlebar] #titlebar-buttonbox {
position: relative;
}
+
+#titlebar-buttonbox {
+ -moz-appearance: -moz-window-button-box;
+}
+
+%ifdef XP_MACOSX
+#titlebar-fullscreen-button {
+ -moz-appearance: -moz-mac-fullscreen-button;
+}
+%endif
+
+%ifdef XP_WIN
+#main-window[sizemode="maximized"] #titlebar-buttonbox {
+ -moz-appearance: -moz-window-button-box-maximized;
+}
%endif
-.bookmarks-toolbar-customize,
-#wrapper-personal-bookmarks > #personal-bookmarks > #PlacesToolbar > hbox > #PlacesToolbarItems {
+%endif
+
+#bookmarks-toolbar-placeholder,
+toolbarpaletteitem > #personal-bookmarks > #PlacesToolbar,
+#personal-bookmarks[cui-areatype="menu-panel"] > #PlacesToolbar,
+#personal-bookmarks[cui-areatype="toolbar"].overflowedItem > #PlacesToolbar {
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;
}
-#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;
}
-#wrapper-urlbar-container #urlbar-container > #urlbar > toolbarbutton,
-#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;
+#PanelUI-feeds > .feed-toolbarbutton:-moz-locale-dir(rtl) {
+ direction: rtl;
}
-#feed-button > .toolbarbutton-menu-dropmarker {
- display: none;
+#panelMenu_bookmarksMenu {
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+#panelMenu_bookmarksMenu > .bookmark-item {
+ max-width: none;
}
-#feed-menu > .feed-menuitem:-moz-locale-dir(rtl) {
- direction: rtl;
+#urlbar-container {
+ min-width: 50ch;
+}
+
+#search-container {
+ min-width: 25ch;
}
#main-window:-moz-lwtheme {
background-repeat: no-repeat;
background-position: top right;
}
%ifdef XP_MACOSX
@@ -175,59 +249,26 @@ toolbar[mode="icons"] > #reload-button[d
}
%endif
#browser-bottombox[lwthemefooter="true"] {
background-repeat: no-repeat;
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 {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-tooltip");
}
.menuitem-iconic-tooltip,
.menuitem-tooltip[type="checkbox"],
.menuitem-tooltip[type="radio"] {
-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 */
#main-menubar[openedwithkey=false] .show-only-for-keyboard {
display: none;
}
/* ::::: location bar ::::: */
#urlbar {
-moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
@@ -252,32 +293,30 @@ panel[noactions] > richlistbox > richlis
panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .ac-url > .ac-url-text {
visibility: visible;
}
#urlbar:not([actiontype]) > #urlbar-display-box {
display: none;
}
-#wrapper-urlbar-container > #urlbar-container > #urlbar {
+#wrapper-urlbar-container > #urlbar-container > #urlbar-wrapper > #urlbar {
-moz-user-input: disabled;
cursor: grab;
}
#PopupAutoComplete {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#browser-autocomplete-result-popup");
}
#PopupAutoCompleteRichResult {
-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:not(#go-button),
-#urlbar[pageproxystate="valid"] > #urlbar-icons > #go-button,
+#urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon,
#urlbar[pageproxystate="invalid"][focused="true"] > #urlbar-go-button ~ toolbarbutton,
#urlbar[pageproxystate="valid"] > #urlbar-go-button,
#urlbar:not([focused="true"]) > #urlbar-go-button {
visibility: collapse;
}
#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon-labels {
visibility: collapse;
@@ -311,28 +350,30 @@ panel[noactions] > richlistbox > richlis
.unified-nav-current {
font-weight: bold;
}
toolbarbutton.bookmark-item {
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 {
/* override default listbox width from xul.css */
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 {
display: none;
}
menuitem.spell-suggestion {
font-weight: bold;
}
@@ -347,17 +388,16 @@ window[sizemode="maximized"] #content .n
/* Hide extension toolbars that neglected to set the proper class */
window[chromehidden~="location"][chromehidden~="toolbar"] toolbar:not(.chromeclass-menubar),
window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-menubar) {
display: none;
}
#navigator-toolbox ,
-#status-bar ,
#mainPopupSet {
min-width: 1px;
}
%ifdef MOZ_SERVICES_SYNC
/* Sync notification UI */
#sync-notifications {
-moz-binding: url("chrome://browser/content/sync/notification.xml#notificationbox");
@@ -446,24 +486,16 @@ window[chromehidden~="toolbar"] toolbar:
#full-screen-domain-text,
#full-screen-remember-decision > .checkbox-label-box > .checkbox-label {
word-wrap: break-word;
/* We must specify a min-width, otherwise word-wrap:break-word doesn't work. Bug 630864. */
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 ::::: */
.ctrlTab-preview > html|img,
.ctrlTab-preview > html|canvas {
min-width: inherit;
max-width: inherit;
min-height: inherit;
max-height: inherit;
@@ -518,27 +550,16 @@ window[chromehidden~="toolbar"] toolbar:
#click-to-play-plugins-notification {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#click-to-play-plugins-notification");
}
.plugin-popupnotification-centeritem {
-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] {
-moz-user-focus: none !important;
}
/* Status panel */
statuspanel {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#statuspanel");
@@ -770,8 +791,19 @@ chatbox:-moz-full-screen-ancestor > .cha
font-weight: bold;
/* color: menutext used to overwrite the disabled color */
color: menutext;
}
.contentSelectDropdown-ingroup {
-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;
+}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -80,16 +80,22 @@ this.__defineGetter__("PluralForm", func
this.__defineSetter__("PluralForm", function (val) {
delete this.PluralForm;
return this.PluralForm = val;
});
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
"resource://gre/modules/TelemetryStopwatch.jsm");
+XPCOMUtils.defineLazyGetter(this, "gCustomizeMode", function() {
+ let scope = {};
+ Cu.import("resource:///modules/CustomizeMode.jsm", scope);
+ return new scope.CustomizeMode(window);
+});
+
#ifdef MOZ_SERVICES_SYNC
XPCOMUtils.defineLazyModuleGetter(this, "Weave",
"resource://services-sync/main.js");
#endif
XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
let tmp = {};
Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
@@ -148,16 +154,17 @@ let gInitialPages = [
"about:newtab",
"about:home",
"about:privatebrowsing",
"about:welcomeback",
"about:sessionrestore"
];
#include browser-addons.js
+#include browser-customization.js
#include browser-feeds.js
#include browser-fullScreen.js
#include browser-fullZoom.js
#include browser-places.js
#include browser-plugins.js
#include browser-safebrowsing.js
#include browser-social.js
#include browser-tabPreviews.js
@@ -310,39 +317,32 @@ function SetClickAndHoldHandlers() {
}
}
function _addClickAndHoldListenersOnElement(aElm) {
aElm.addEventListener("mousedown", mousedownHandler, true);
aElm.addEventListener("click", clickHandler, true);
}
- // Bug 414797: Clone unified-back-forward-button's context menu into both the
- // back and the forward buttons.
- var unifiedButton = document.getElementById("unified-back-forward-button");
- if (unifiedButton && !unifiedButton._clickHandlersAttached) {
- unifiedButton._clickHandlersAttached = true;
-
- let popup = document.getElementById("backForwardMenu").cloneNode(true);
- popup.removeAttribute("id");
- // Prevent the context attribute on unified-back-forward-button from being
- // inherited.
- popup.setAttribute("context", "");
-
- let backButton = document.getElementById("back-button");
- backButton.setAttribute("type", "menu");
- backButton.appendChild(popup);
- _addClickAndHoldListenersOnElement(backButton);
-
- let forwardButton = document.getElementById("forward-button");
- popup = popup.cloneNode(true);
- forwardButton.setAttribute("type", "menu");
- forwardButton.appendChild(popup);
- _addClickAndHoldListenersOnElement(forwardButton);
- }
+ // Bug 414797: Clone the back/forward buttons' context menu into both buttons.
+ let popup = document.getElementById("backForwardMenu").cloneNode(true);
+ popup.removeAttribute("id");
+ // Prevent the back/forward buttons' context attributes from being inherited.
+ popup.setAttribute("context", "");
+
+ let backButton = document.getElementById("back-button");
+ backButton.setAttribute("type", "menu");
+ backButton.appendChild(popup);
+ _addClickAndHoldListenersOnElement(backButton);
+
+ let forwardButton = document.getElementById("forward-button");
+ popup = popup.cloneNode(true);
+ forwardButton.setAttribute("type", "menu");
+ forwardButton.appendChild(popup);
+ _addClickAndHoldListenersOnElement(forwardButton);
}
const gSessionHistoryObserver = {
observe: function(subject, topic, data)
{
if (topic != "browser:purge-session-history")
return;
@@ -732,16 +732,18 @@ const gFormSubmitObserver = {
position = "after_start";
}
this.panel.openPopup(element, position, offset, 0);
}
};
var gBrowserInit = {
+ delayedStartupFinished: false,
+
onLoad: function() {
gMultiProcessBrowser = gPrefService.getBoolPref("browser.tabs.remote");
var mustLoadSidebar = false;
if (!gMultiProcessBrowser) {
// There is a Content:Click message manually sent from content.
Cc["@mozilla.org/eventlistenerservice;1"]
@@ -884,38 +886,29 @@ var gBrowserInit = {
// border. Use 28px as a guess (titlebar + bottom window border)
defaultHeight -= 28;
#endif
}
document.documentElement.setAttribute("width", defaultWidth);
document.documentElement.setAttribute("height", defaultHeight);
}
- if (!gShowPageResizers)
- document.getElementById("status-bar").setAttribute("hideresizer", "true");
-
if (!window.toolbar.visible) {
// adjust browser UI for popups
if (gURLBar) {
gURLBar.setAttribute("readonly", "true");
gURLBar.setAttribute("enablehistory", "false");
}
goSetCommandEnabled("cmd_newNavigatorTab", false);
}
-#ifdef MENUBAR_CAN_AUTOHIDE
- updateAppButtonDisplay();
-#endif
-
// Misc. inits.
CombinedStopReload.init();
- TabsOnTop.init();
gPrivateBrowsingUI.init();
TabsInTitlebar.init();
- retrieveToolbarIconsizesFromTheme();
// Wait until chrome is painted before executing code not critical to making the window visible
this._boundDelayedStartup = this._delayedStartup.bind(this, mustLoadSidebar);
window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
this._loadHandled = true;
},
@@ -1007,17 +1000,23 @@ var gBrowserInit = {
Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
BrowserOffline.init();
OfflineApps.init();
IndexedDBPromptHelper.init();
gFormSubmitObserver.init();
SocialUI.init();
- AddonManager.addAddonListener(AddonsMgrListener);
+
+ // Initialize the full zoom setting.
+ // We do this before the session restore service gets initialized so we can
+ // apply full zoom settings to tabs restored by the session restore service.
+ FullZoom.init();
+ PanelUI.init();
+ LightweightThemeListener.init();
WebrtcIndicator.init();
// Ensure login manager is up and running.
Services.logins;
#ifdef MOZ_CRASHREPORTER
if (gMultiProcessBrowser)
TabCrashReporter.init();
@@ -1058,21 +1057,16 @@ var gBrowserInit = {
document.getElementById("textfieldDirection-swap").hidden = false;
}
// Setup click-and-hold gestures access to the session history
// menus if global click-and-hold isn't turned on
if (!getBoolPref("ui.click_hold_context_menus", false))
SetClickAndHoldHandlers();
- // Initialize the full zoom setting.
- // We do this before the session restore service gets initialized so we can
- // apply full zoom settings to tabs restored by the session restore service.
- FullZoom.init();
-
// Bug 666804 - NetworkPrioritizer support for e10s
if (!gMultiProcessBrowser) {
let NP = {};
Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
NP.trackBrowserWindow(window);
}
PlacesToolbarHelper.init();
@@ -1133,54 +1127,25 @@ var gBrowserInit = {
#endif
#ifdef MOZ_DATA_REPORTING
gDataNotificationInfoBar.init();
#endif
gBrowserThumbnails.init();
- setUrlAndSearchBarWidthForConditionalForwardButton();
- window.addEventListener("resize", function resizeHandler(event) {
- if (event.target == window)
- setUrlAndSearchBarWidthForConditionalForwardButton();
- });
-
-#ifdef MENUBAR_CAN_AUTOHIDE
- // If the user (or the locale) hasn't enabled the top-level "Character
- // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
- // hide it.
- if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
- Ci.nsIPrefLocalizedString).data)
- document.getElementById("appmenu_charsetMenu").hidden = true;
-#endif
-
// Add Devtools menuitems and listeners
gDevToolsBrowser.registerBrowserWindow(window);
- let appMenuButton = document.getElementById("appmenu-button");
- let appMenuPopup = document.getElementById("appmenu-popup");
- if (appMenuButton && appMenuPopup) {
- let appMenuOpening = null;
- appMenuButton.addEventListener("mousedown", function(event) {
- if (event.button == 0)
- appMenuOpening = new Date();
- }, false);
- appMenuPopup.addEventListener("popupshown", function(event) {
- if (event.target != appMenuPopup || !appMenuOpening)
- return;
- let duration = new Date() - appMenuOpening;
- appMenuOpening = null;
- Services.telemetry.getHistogramById("FX_APP_MENU_OPEN_MS").add(duration);
- }, false);
- }
-
window.addEventListener("mousemove", MousePosTracker, false);
window.addEventListener("dragover", MousePosTracker, false);
+ gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
+ gNavToolbox.addEventListener("customizationending", CustomizationHandler);
+
// End startup crash tracking after a delay to catch crashes while restoring
// tabs and to postpone saving the pref to disk.
try {
const startupCrashEndDelay = 30 * 1000;
setTimeout(Services.startup.trackStartupCrashEnd, startupCrashEndDelay);
} catch (ex) {
Cu.reportError("Could not end startup crash tracking: " + ex);
}
@@ -1204,16 +1169,17 @@ var gBrowserInit = {
SessionStore.promiseInitialized.then(() => {
// Enable the Restore Last Session command if needed
RestoreLastSessionObserver.init();
TabView.init();
setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
});
+ this.delayedStartupFinished = true;
Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
TelemetryTimestamps.add("delayedStartupFinished");
},
// Returns the URI(s) to load at startup.
_getUriToLoad: function () {
// window.arguments[0]: URI to load (string), or an nsISupportsArray of
@@ -1269,18 +1235,16 @@ var gBrowserInit = {
try {
gBrowser.removeProgressListener(window.XULBrowserWindow);
gBrowser.removeTabsProgressListener(window.TabsProgressListener);
} catch (ex) {
}
BookmarkingUI.uninit();
- TabsOnTop.uninit();
-
TabsInTitlebar.uninit();
var enumerator = Services.wm.getEnumerator(null);
enumerator.getNext();
if (!enumerator.hasMoreElements()) {
document.persist("sidebar-box", "sidebarcommand");
document.persist("sidebar-box", "width");
document.persist("sidebar-box", "src");
@@ -1321,18 +1285,19 @@ var gBrowserInit = {
Services.prefs.removeObserver(prefName, gMetroPrefs);
});
#endif
#endif
BrowserOffline.uninit();
OfflineApps.uninit();
IndexedDBPromptHelper.uninit();
- AddonManager.removeAddonListener(AddonsMgrListener);
SocialUI.uninit();
+ LightweightThemeListener.uninit();
+ PanelUI.uninit();
}
// Final window teardown, do this last.
window.XULBrowserWindow = null;
window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -1347,17 +1312,17 @@ var gBrowserInit = {
// macBrowserOverlay
nonBrowserWindowStartup: function() {
// Disable inappropriate commands / submenus
var disabledItems = ['Browser:SavePage',
'Browser:SendLink', 'cmd_pageSetup', 'cmd_print', 'cmd_find', 'cmd_findAgain',
'viewToolbarsMenu', 'viewSidebarMenuMenu', 'Browser:Reload',
'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu', 'View:PageSource', 'View:FullScreen',
'viewHistorySidebar', 'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs',
- 'View:PageInfo', 'Browser:ToggleTabView', 'Browser:ToggleAddonBar'];
+ 'View:PageInfo', 'Browser:ToggleTabView'];
var element;
for (let disabledItem of disabledItems) {
element = document.getElementById(disabledItem);
if (element)
element.setAttribute("disabled", "true");
}
@@ -2146,60 +2111,47 @@ function losslessDecodeURI(aURI) {
return value;
}
function UpdateUrlbarSearchSplitterState()
{
var splitter = document.getElementById("urlbar-search-splitter");
var urlbar = document.getElementById("urlbar-container");
var searchbar = document.getElementById("search-container");
- var stop = document.getElementById("stop-button");
+
+ // If the splitter is already in the right place, we don't need to do anything:
+ if (splitter &&
+ ((splitter.nextSibling == searchbar && splitter.previousSibling == urlbar) ||
+ (splitter.nextSibling == urlbar && splitter.previousSibling == searchbar))) {
+ return;
+ }
var ibefore = null;
if (urlbar && searchbar) {
- if (urlbar.nextSibling == searchbar ||
- urlbar.getAttribute("combined") &&
- stop && stop.nextSibling == searchbar)
+ if (urlbar.nextSibling == searchbar)
ibefore = searchbar;
else if (searchbar.nextSibling == urlbar)
ibefore = urlbar;
}
if (ibefore) {
if (!splitter) {
splitter = document.createElement("splitter");
splitter.id = "urlbar-search-splitter";
splitter.setAttribute("resizebefore", "flex");
splitter.setAttribute("resizeafter", "flex");
splitter.setAttribute("skipintoolbarset", "true");
+ splitter.setAttribute("overflows", "false");
splitter.className = "chromeclass-toolbar-additional";
}
urlbar.parentNode.insertBefore(splitter, ibefore);
} else if (splitter)
splitter.parentNode.removeChild(splitter);
}
-function setUrlAndSearchBarWidthForConditionalForwardButton() {
- // Workaround for bug 694084: Showing/hiding the conditional forward button resizes
- // the search bar when the url/search bar splitter hasn't been used.
- var urlbarContainer = document.getElementById("urlbar-container");
- var searchbarContainer = document.getElementById("search-container");
- if (!urlbarContainer ||
- !searchbarContainer ||
- urlbarContainer.hasAttribute("width") ||
- searchbarContainer.hasAttribute("width") ||
- urlbarContainer.parentNode != searchbarContainer.parentNode)
- return;
- urlbarContainer.style.width = searchbarContainer.style.width = "";
- var urlbarWidth = urlbarContainer.clientWidth;
- var searchbarWidth = searchbarContainer.clientWidth;
- urlbarContainer.style.width = urlbarWidth + "px";
- searchbarContainer.style.width = searchbarWidth + "px";
-}
-
function UpdatePageProxyState()
{
if (gURLBar && gURLBar.value != gLastValidURLStr)
SetPageProxyState("invalid");
}
function SetPageProxyState(aState)
{
@@ -2589,35 +2541,30 @@ var PrintPreviewListener = {
if (gInPrintPreviewMode)
this._hideChrome();
else
this._showChrome();
if (this._chromeState.sidebarOpen)
toggleSidebar(this._sidebarCommand);
-#ifdef MENUBAR_CAN_AUTOHIDE
- updateAppButtonDisplay();
-#endif
+ TabsInTitlebar.allowedBy("print-preview", !gInPrintPreviewMode);
},
_hideChrome: function () {
this._chromeState = {};
var sidebar = document.getElementById("sidebar-box");
this._chromeState.sidebarOpen = !sidebar.hidden;
this._sidebarCommand = sidebar.getAttribute("sidebarcommand");
var notificationBox = gBrowser.getNotificationBox();
this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
notificationBox.notificationsHidden = true;
document.getElementById("sidebar").setAttribute("src", "about:blank");
- var addonBar = document.getElementById("addon-bar");
- this._chromeState.addonBarOpen = !addonBar.collapsed;
- addonBar.collapsed = true;
gBrowser.updateWindowResizers();
this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
if (gFindBarInitialized)
gFindBar.close();
var globalNotificationBox = document.getElementById("global-notificationbox");
this._chromeState.globalNotificationsOpen = !globalNotificationBox.notificationsHidden;
@@ -2629,21 +2576,16 @@ var PrintPreviewListener = {
this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
syncNotifications.notificationsHidden = true;
}
},
_showChrome: function () {
if (this._chromeState.notificationsOpen)
gBrowser.getNotificationBox().notificationsHidden = false;
- if (this._chromeState.addonBarOpen) {
- document.getElementById("addon-bar").collapsed = false;
- gBrowser.updateWindowResizers();
- }
-
if (this._chromeState.findOpen)
gFindBar.open();
if (this._chromeState.globalNotificationsOpen)
document.getElementById("global-notificationbox").notificationsHidden = false;
if (this._chromeState.syncNotificationsOpen)
document.getElementById("sync-notifications").notificationsHidden = false;
@@ -2710,45 +2652,16 @@ function openHomeDialog(aURL)
gPrefService.setComplexValue("browser.startup.homepage",
Components.interfaces.nsISupportsString, str);
} catch (ex) {
dump("Failed to set the home page.\n"+ex+"\n");
}
}
}
-var bookmarksButtonObserver = {
- onDrop: function (aEvent)
- {
- let name = { };
- let url = browserDragAndDrop.drop(aEvent, name);
- try {
- PlacesUIUtils.showBookmarkDialog({ action: "add"
- , type: "bookmark"
- , uri: makeURI(url)
- , title: name
- , hiddenRows: [ "description"
- , "location"
- , "loadInSidebar"
- , "keyword" ]
- }, window);
- } catch(ex) { }
- },
-
- onDragOver: function (aEvent)
- {
- browserDragAndDrop.dragOver(aEvent);
- aEvent.dropEffect = "link";
- },
-
- onDragExit: function (aEvent)
- {
- }
-}
-
var newTabButtonObserver = {
onDragOver: function (aEvent)
{
browserDragAndDrop.dragOver(aEvent);
},
onDragExit: function (aEvent)
{
@@ -3281,163 +3194,28 @@ function OpenBrowserWindow(options)
else // forget about the charset information.
{
win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no" + extraFeatures, defaultArgs);
}
return win;
}
-var gCustomizeSheet = false;
+//XXXunf Are these still useful to keep around?
function BrowserCustomizeToolbar() {
- // Disable the toolbar context menu items
- var menubar = document.getElementById("main-menubar");
- for (let childNode of menubar.childNodes)
- childNode.setAttribute("disabled", true);
-
- var cmd = document.getElementById("cmd_CustomizeToolbars");
- cmd.setAttribute("disabled", "true");
-
- var splitter = document.getElementById("urlbar-search-splitter");
- if (splitter)
- splitter.parentNode.removeChild(splitter);
-
- CombinedStopReload.uninit();
-
- PlacesToolbarHelper.customizeStart();
- BookmarkingUI.customizeStart();
- DownloadsButton.customizeStart();
-
- TabsInTitlebar.allowedBy("customizing-toolbars", false);
-
- var customizeURL = "chrome://global/content/customizeToolbar.xul";
- gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false);
-
- if (gCustomizeSheet) {
- let sheetFrame = document.createElement("iframe");
- let panel = document.getElementById("customizeToolbarSheetPopup");
- sheetFrame.id = "customizeToolbarSheetIFrame";
- sheetFrame.toolbox = gNavToolbox;
- sheetFrame.panel = panel;
- sheetFrame.setAttribute("style", panel.getAttribute("sheetstyle"));
- panel.appendChild(sheetFrame);
-
- // Open the panel, but make it invisible until the iframe has loaded so
- // that the user doesn't see a white flash.
- panel.style.visibility = "hidden";
- gNavToolbox.addEventListener("beforecustomization", function onBeforeCustomization() {
- gNavToolbox.removeEventListener("beforecustomization", onBeforeCustomization, false);
- panel.style.removeProperty("visibility");
- }, false);
-
- sheetFrame.setAttribute("src", customizeURL);
-
- panel.openPopup(gNavToolbox, "after_start", 0, 0);
- } else {
- window.openDialog(customizeURL,
- "CustomizeToolbar",
- "chrome,titlebar,toolbar,location,resizable,dependent",
- gNavToolbox);
- }
+ gCustomizeMode.enter();
}
function BrowserToolboxCustomizeDone(aToolboxChanged) {
- if (gCustomizeSheet) {
- document.getElementById("customizeToolbarSheetPopup").hidePopup();
- let iframe = document.getElementById("customizeToolbarSheetIFrame");
- iframe.parentNode.removeChild(iframe);
- }
-
- // Update global UI elements that may have been added or removed
- if (aToolboxChanged) {
- gURLBar = document.getElementById("urlbar");
-
- gProxyFavIcon = document.getElementById("page-proxy-favicon");
- gHomeButton.updateTooltip();
- gIdentityHandler._cacheElements();
- window.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();
- setUrlAndSearchBarWidthForConditionalForwardButton();
-
- // Update the urlbar
- if (gURLBar) {
- URLBarSetURI();
- XULBrowserWindow.asyncUpdateUI();
- BookmarkingUI.updateStarState();
- SocialUI.updateState();
- }
-
- TabsInTitlebar.allowedBy("customizing-toolbars", true);
-
- // Re-enable parts of the UI we disabled during the dialog
- var menubar = document.getElementById("main-menubar");
- for (let childNode of menubar.childNodes)
- childNode.setAttribute("disabled", false);
- var cmd = document.getElementById("cmd_CustomizeToolbars");
- cmd.removeAttribute("disabled");
-
- // make sure to re-enable click-and-hold
- if (!getBoolPref("ui.click_hold_context_menus", false))
- SetClickAndHoldHandlers();
-
- gBrowser.selectedBrowser.focus();
+ gCustomizeMode.exit(aToolboxChanged);
}
function BrowserToolboxCustomizeChange(aType) {
- switch (aType) {
- case "iconsize":
- case "mode":
- retrieveToolbarIconsizesFromTheme();
- break;
- default:
- gHomeButton.updatePersonalToolbarStyle();
- BookmarkingUI.customizeChange();
- }
-}
-
-/**
- * Allows themes to override the "iconsize" attribute on toolbars.
- */
-function retrieveToolbarIconsizesFromTheme() {
- function retrieveToolbarIconsize(aToolbar) {
- if (aToolbar.localName != "toolbar")
- return;
-
- // The theme indicates that it wants to override the "iconsize" attribute
- // by specifying a special value for the "counter-reset" property on the
- // toolbar. A custom property cannot be used because getComputedStyle can
- // only return the values of standard CSS properties.
- let counterReset = getComputedStyle(aToolbar).counterReset;
- if (counterReset == "smallicons 0")
- aToolbar.setAttribute("iconsize", "small");
- else if (counterReset == "largeicons 0")
- aToolbar.setAttribute("iconsize", "large");
- }
-
- Array.forEach(gNavToolbox.childNodes, retrieveToolbarIconsize);
- gNavToolbox.externalToolbars.forEach(retrieveToolbarIconsize);
+ gHomeButton.updatePersonalToolbarStyle();
+ BookmarksMenuButton.customizeChange();
}
/**
* Update the global flag that tracks whether or not any edit UI (the Edit menu,
* edit-related items in the context menu, and edit-related toolbar buttons
* is visible, then update the edit commands' enabled state accordingly. We use
* this flag to skip updating the edit commands on focus or selection changes
* when no UI is visible to improve performance (including pageload performance,
@@ -3456,36 +3234,27 @@ function retrieveToolbarIconsizesFromThe
* is a no op.
*/
function updateEditUIVisibility()
{
#ifndef XP_MACOSX
let editMenuPopupState = document.getElementById("menu_EditPopup").state;
let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
let placesContextMenuPopupState = document.getElementById("placesContext").state;
-#ifdef MENUBAR_CAN_AUTOHIDE
- let appMenuPopupState = document.getElementById("appmenu-popup").state;
-#endif
// The UI is visible if the Edit menu is opening or open, if the context menu
// is open, or if the toolbar has been customized to include the Cut, Copy,
// or Paste toolbar buttons.
gEditUIVisible = editMenuPopupState == "showing" ||
editMenuPopupState == "open" ||
contextMenuPopupState == "showing" ||
contextMenuPopupState == "open" ||
placesContextMenuPopupState == "showing" ||
placesContextMenuPopupState == "open" ||
-#ifdef MENUBAR_CAN_AUTOHIDE
- appMenuPopupState == "showing" ||
- appMenuPopupState == "open" ||
-#endif
- document.getElementById("cut-button") ||
- document.getElementById("copy-button") ||
- document.getElementById("paste-button") ? true : false;
+ document.getElementById("edit-controls") ? true : false;
// If UI is visible, update the edit commands' enabled state to reflect
// whether or not they are actually enabled for the current focus/selection.
if (gEditUIVisible)
goUpdateGlobalEditMenuItems();
// Otherwise, enable all commands, so that keyboard shortcuts still work,
// then lazily determine their actual enabled state when the user presses
@@ -3505,44 +3274,29 @@ function updateEditUIVisibility()
/**
* Makes the Character Encoding menu enabled or disabled as appropriate.
* To be called when the View menu or the app menu is opened.
*/
function updateCharacterEncodingMenuState()
{
let charsetMenu = document.getElementById("charsetMenu");
- let appCharsetMenu = document.getElementById("appmenu_charsetMenu");
- let appDevCharsetMenu =
- document.getElementById("appmenu_developer_charsetMenu");
// gBrowser is null on Mac when the menubar shows in the context of
// non-browser windows. The above elements may be null depending on
// what parts of the menubar are present. E.g. no app menu on Mac.
if (gBrowser &&
gBrowser.docShell &&
gBrowser.docShell.mayEnableCharacterEncodingMenu) {
if (charsetMenu) {
charsetMenu.removeAttribute("disabled");
}
- if (appCharsetMenu) {
- appCharsetMenu.removeAttribute("disabled");
- }
- if (appDevCharsetMenu) {
- appDevCharsetMenu.removeAttribute("disabled");
- }
} else {
if (charsetMenu) {
charsetMenu.setAttribute("disabled", "true");
}
- if (appCharsetMenu) {
- appCharsetMenu.setAttribute("disabled", "true");
- }
- if (appDevCharsetMenu) {
- appDevCharsetMenu.setAttribute("disabled", "true");
- }
}
}
/**
* Returns true if |aMimeType| is text-based, false otherwise.
*
* @param aMimeType
* The MIME type to check.
@@ -3564,18 +3318,18 @@ function mimeTypeIsTextBased(aMimeType)
var XULBrowserWindow = {
// Stored Status, Link and Loading values
status: "",
defaultStatus: "",
overLink: "",
startTime: 0,
statusText: "",
isBusy: false,
- inContentWhitelist: ["about:addons", "about:downloads", "about:permissions",
- "about:sync-progress", "about:preferences"],
+ // Left here for add-on compatibility, see bug 752434
+ inContentWhitelist: [],
QueryInterface: function (aIID) {
if (aIID.equals(Ci.nsIWebProgressListener) ||
aIID.equals(Ci.nsIWebProgressListener2) ||
aIID.equals(Ci.nsISupportsWeakReference) ||
aIID.equals(Ci.nsIXULBrowserWindow) ||
aIID.equals(Ci.nsISupports))
return this;
@@ -3594,18 +3348,16 @@ var XULBrowserWindow = {
return gBrowser.getStatusPanel();
},
get isImage () {
delete this.isImage;
return this.isImage = document.getElementById("isImage");
},
init: function () {
- this.throbberElement = document.getElementById("navigator-throbber");
-
// Initialize the security button's state and tooltip text.
var securityUI = gBrowser.securityUI;
this.onSecurityChange(null, null, securityUI.state);
},
setJSStatus: function () {
// unsupported
},
@@ -3719,20 +3471,16 @@ var XULBrowserWindow = {
gBrowser.selectedBrowser.engines = null;
}
this.isBusy = true;
if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
this._busyUI = true;
- // Turn the throbber on.
- if (this.throbberElement)
- this.throbberElement.setAttribute("busy", "true");
-
// XXX: This needs to be based on window activity...
this.stopCommand.removeAttribute("disabled");
CombinedStopReload.switchToStop();
}
}
else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
// This (thanks to the filter) is a network stop or the last
// request stop outside of loading the document, stop throbbers
@@ -3767,20 +3515,16 @@ var XULBrowserWindow = {
this.isImage.setAttribute('disabled', 'true');
}
this.isBusy = false;
if (this._busyUI) {
this._busyUI = false;
- // Turn the throbber off.
- if (this.throbberElement)
- this.throbberElement.removeAttribute("busy");
-
this.stopCommand.setAttribute("disabled", "true");
CombinedStopReload.switchToReload(aRequest instanceof Ci.nsIRequest);
}
}
},
onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) {
var location = aLocationURI ? aLocationURI.spec : "";
@@ -3837,26 +3581,16 @@ var XULBrowserWindow = {
if (gURLBar) {
URLBarSetURI(aLocationURI);
// Update starring UI
BookmarkingUI.updateStarState();
SocialUI.updateState();
}
- // Show or hide browser chrome based on the whitelist
- if (this.hideChromeForLocation(location)) {
- document.documentElement.setAttribute("disablechrome", "true");
- } else {
- if (SessionStore.getTabValue(gBrowser.selectedTab, "appOrigin"))
- document.documentElement.setAttribute("disablechrome", "true");
- else
- document.documentElement.removeAttribute("disablechrome");
- }
-
// Utility functions for disabling find
var shouldDisableFind = function shouldDisableFind(aDocument) {
let docElt = aDocument.documentElement;
return docElt && docElt.getAttribute("disablefastfind") == "true";
}
var disableFindCommands = function disableFindCommands(aDisable) {
let findCommands = [document.getElementById("cmd_find"),
@@ -3887,16 +3621,27 @@ var XULBrowserWindow = {
if (content.document.readyState == "interactive" || content.document.readyState == "complete")
disableFindCommands(shouldDisableFind(content.document));
else {
content.document.addEventListener("readystatechange", onContentRSChange);
}
}
} else
disableFindCommands(false);
+
+ // Try not to instantiate gCustomizeMode as much as possible,
+ // so don't use CustomizeMode.jsm to check for URI or customizing.
+ let customizingURI = "about:customizing";
+ if (location == customizingURI &&
+ !CustomizationHandler.isCustomizing()) {
+ gCustomizeMode.enter();
+ } else if (location != customizingURI &&
+ CustomizationHandler.isCustomizing()) {
+ gCustomizeMode.exit();
+ }
}
UpdateBackForwardCommands(gBrowser.webNavigation);
gGestureSupport.restoreRotationState();
// See bug 358202, when tabs are switched during a drag operation,
// timers don't fire on windows (bug 203573)
if (aRequest)
@@ -3904,22 +3649,18 @@ var XULBrowserWindow = {
else
this.asyncUpdateUI();
},
asyncUpdateUI: function () {
FeedHandler.updateFeeds();
},
- hideChromeForLocation: function(aLocation) {
- aLocation = aLocation.toLowerCase();
- return this.inContentWhitelist.some(function(aSpec) {
- return aSpec == aLocation;
- });
- },
+ // Left here for add-on compatibility, see bug 752434
+ hideChromeForLocation: function() {},
onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
this.status = aMessage;
this.updateStatusField();
},
// Properties used to cache security state used to update the UI
_state: null,
@@ -4047,31 +3788,18 @@ var LinkTargetDisplay = {
}
};
var CombinedStopReload = {
init: function () {
if (this._initialized)
return;
- var urlbar = document.getElementById("urlbar-container");
- var reload = document.getElementById("reload-button");
- var stop = document.getElementById("stop-button");
-
- if (urlbar) {
- if (urlbar.parentNode.getAttribute("mode") != "icons" ||
- !reload || urlbar.nextSibling != reload ||
- !stop || reload.nextSibling != stop)
- urlbar.removeAttribute("combined");
- else {
- urlbar.setAttribute("combined", "true");
- reload = document.getElementById("urlbar-reload-button");
- stop = document.getElementById("urlbar-stop-button");
- }
- }
+ let reload = document.getElementById("urlbar-reload-button");
+ let stop = document.getElementById("urlbar-stop-button");
if (!stop || !reload || reload.nextSibling != stop)
return;
this._initialized = true;
if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true")
reload.setAttribute("displaystop", "true");
stop.addEventListener("click", this, false);
this.reload = reload;
@@ -4393,31 +4121,29 @@ function onViewToolbarsPopupShowing(aEve
var deadItem = popup.childNodes[i];
if (deadItem.hasAttribute("toolbarId"))
popup.removeChild(deadItem);
}
var firstMenuItem = aInsertPoint || popup.firstChild;
let toolbarNodes = Array.slice(gNavToolbox.childNodes);
- toolbarNodes.push(document.getElementById("addon-bar"));
for (let toolbar of toolbarNodes) {
let toolbarName = toolbar.getAttribute("toolbarname");
if (toolbarName) {
let menuItem = document.createElement("menuitem");
let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
"autohide" : "collapsed";
menuItem.setAttribute("id", "toggle_" + toolbar.id);
menuItem.setAttribute("toolbarId", toolbar.id);
menuItem.setAttribute("type", "checkbox");
menuItem.setAttribute("label", toolbarName);
menuItem.setAttribute("checked", toolbar.getAttribute(hidingAttribute) != "true");
- if (popup.id != "appmenu_customizeMenu")
- menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
+ menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
if (popup.id != "toolbar-context-menu")
menuItem.setAttribute("key", toolbar.getAttribute("key"));
popup.insertBefore(menuItem, firstMenuItem);
menuItem.addEventListener("command", onViewToolbarCommand, false);
}
}
@@ -4431,210 +4157,311 @@ function onViewToolbarCommand(aEvent) {
}
function setToolbarVisibility(toolbar, isVisible) {
var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
"autohide" : "collapsed";
toolbar.setAttribute(hidingAttribute, !isVisible);
document.persist(toolbar.id, hidingAttribute);
+ let eventParams = {
+ detail: {
+ visible: isVisible
+ },
+ bubbles: true
+ };
+ let event = new CustomEvent("toolbarvisibilitychange", eventParams);
+ toolbar.dispatchEvent(event);
PlacesToolbarHelper.init();
BookmarkingUI.onToolbarVisibilityChange();
gBrowser.updateWindowResizers();
-
-#ifdef MENUBAR_CAN_AUTOHIDE
- updateAppButtonDisplay();
-#endif
-}
-
-var TabsOnTop = {
- init: function TabsOnTop_init() {
- Services.prefs.addObserver(this._prefName, this, false);
-
- // Only show the toggle UI if the user disabled tabs on top.
- if (Services.prefs.getBoolPref(this._prefName)) {
- for (let item of document.querySelectorAll("menuitem[command=cmd_ToggleTabsOnTop]"))
- item.parentNode.removeChild(item);
- }
- },
-
- uninit: function TabsOnTop_uninit() {
- Services.prefs.removeObserver(this._prefName, this);
- },
-
- toggle: function () {
- this.enabled = !Services.prefs.getBoolPref(this._prefName);
- },
-
- syncUI: function () {
- let userEnabled = Services.prefs.getBoolPref(this._prefName);
- let enabled = userEnabled && gBrowser.tabContainer.visible;
-
- document.getElementById("cmd_ToggleTabsOnTop")
- .setAttribute("checked", userEnabled);
-
- document.documentElement.setAttribute("tabsontop", enabled);
- document.getElementById("navigator-toolbox").setAttribute("tabsontop", enabled);
- document.getElementById("TabsToolbar").setAttribute("tabsontop", enabled);
- document.getElementById("nav-bar").setAttribute("tabsontop", enabled);
- gBrowser.tabContainer.setAttribute("tabsontop", enabled);
- TabsInTitlebar.allowedBy("tabs-on-top", enabled);
- },
-
- get enabled () {
- return gNavToolbox.getAttribute("tabsontop") == "true";
- },
-
- set enabled (val) {
- Services.prefs.setBoolPref(this._prefName, !!val);
- return val;
- },
-
- observe: function (subject, topic, data) {
- if (topic == "nsPref:changed")
- this.syncUI();
- },
-
- _prefName: "browser.tabs.onTop"
}
var TabsInTitlebar = {
init: function () {
#ifdef CAN_DRAW_IN_TITLEBAR
this._readPref();
Services.prefs.addObserver(this._prefName, this, false);
- // Don't trust the initial value of the sizemode attribute; wait for
- // the resize event (handled in tabbrowser.xml).
- this.allowedBy("sizemode", false);
-
+ // We need to update the appearance of the titlebar when the menu changes
+ // from the active to the inactive state. We can't, however, rely on
+ // DOMMenuBarInactive, because the menu fires this event and then removes
+ // the inactive attribute after an event-loop spin.
+ //
+ // Because updating the appearance involves sampling the heights and margins
+ // of various elements, it's important that the layout be more or less
+ // settled before updating the titlebar. So instead of listening to
+ // DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
+ // watch the "invalid" attribute directly.
+ let menu = document.getElementById("toolbar-menubar");
+ this._menuObserver = new MutationObserver(this._onMenuMutate);
+ this._menuObserver.observe(menu, {attributes: true});
+
+ gNavToolbox.addEventListener("customization-transitionend", this);
this._initialized = true;
#endif
},
allowedBy: function (condition, allow) {
#ifdef CAN_DRAW_IN_TITLEBAR
if (allow) {
if (condition in this._disallowed) {
delete this._disallowed[condition];
- this._update();
+ this._update(true);
}
} else {
if (!(condition in this._disallowed)) {
this._disallowed[condition] = null;
- this._update();
+ this._update(true);
}
}
#endif
},
+ updateAppearance: function updateAppearance(aForce) {
+#ifdef CAN_DRAW_IN_TITLEBAR
+ this._update(aForce);
+#endif
+ },
+
get enabled() {
return document.documentElement.getAttribute("tabsintitlebar") == "true";
},
#ifdef CAN_DRAW_IN_TITLEBAR
observe: function (subject, topic, data) {
if (topic == "nsPref:changed")
this._readPref();
},
+ handleEvent: function(ev) {
+ if (ev.type == "customization-transitionend") {
+ this._update(true);
+ }
+ },
+
+ _onMenuMutate: function (aMutations) {
+ for (let mutation of aMutations) {
+ if (mutation.attributeName == "inactive" ||
+ mutation.attributeName == "autohide") {
+ TabsInTitlebar._update(true);
+ return;
+ }
+ }
+ },
+
_initialized: false,
_disallowed: {},
_prefName: "browser.tabs.drawInTitlebar",
+ _lastSizeMode: null,
_readPref: function () {
this.allowedBy("pref",
Services.prefs.getBoolPref(this._prefName));
},
- _update: function () {
+ _update: function (aForce=false) {
function $(id) document.getElementById(id);
function rect(ele) ele.getBoundingClientRect();
+ function verticalMargins(cstyle) parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
if (!this._initialized || window.fullScreen)
return;
let allowed = true;
+
+ if (!aForce) {
+ // _update is called on resize events, because the window is not ready
+ // after sizemode events. However, we only care about the event when the
+ // sizemode is different from the last time we updated the appearance of
+ // the tabs in the titlebar.
+ let sizemode = document.documentElement.getAttribute("sizemode");
+ if (this._lastSizeMode == sizemode) {
+ return;
+ }
+ this._lastSizeMode = sizemode;
+ }
+
for (let something in this._disallowed) {
allowed = false;
break;
}
- if (allowed == this.enabled)
- return;
-
let titlebar = $("titlebar");
+ let titlebarContent = $("titlebar-content");
+ let menubar = $("toolbar-menubar");
if (allowed) {
- let tabsToolbar = $("TabsToolbar");
-
-#ifdef MENUBAR_CAN_AUTOHIDE
- let appmenuButtonBox = $("appmenu-button-container");
- this._sizePlaceholder("appmenu-button", rect(appmenuButtonBox).width);
+ // We set the tabsintitlebar attribute first so that our CSS for
+ // tabsintitlebar manifests before we do our measurements.
+ document.documentElement.setAttribute("tabsintitlebar", "true");
+ updateTitlebarDisplay();
+
+ // Try to avoid reflows in this code by calculating dimensions first and
+ // then later set the properties affecting layout together in a batch.
+
+ // Buttons first:
+ let captionButtonsBoxWidth = rect($("titlebar-buttonbox")).width;
+#ifdef XP_MACOSX
+ let fullscreenButtonWidth = rect($("titlebar-fullscreen-button")).width;
+ // No need to look up the menubar stuff on OS X:
+ let menuHeight = 0;
+ let fullMenuHeight = 0;
+ // Instead, look up the titlebar padding:
+ let titlebarPadding = parseInt(window.getComputedStyle(titlebar).paddingTop, 10);
+#else
+ // Otherwise, get the height and margins separately for the menubar
+ let menuHeight = rect(menubar).height;
+ let menuStyles = window.getComputedStyle(menubar);
+ let fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
#endif
- let captionButtonsBox = $("titlebar-buttonbox");
- this._sizePlaceholder("caption-buttons", rect(captionButtonsBox).width);
-
- let tabsToolbarRect = rect(tabsToolbar);
- let titlebarTop = rect($("titlebar-content")).top;
- titlebar.style.marginBottom = - Math.min(tabsToolbarRect.top - titlebarTop,
- tabsToolbarRect.height) + "px";
-
- document.documentElement.setAttribute("tabsintitlebar", "true");
-
- if (!this._draghandle) {
+ // Get the full height of the tabs toolbar:
+ let tabsToolbar = $("TabsToolbar");
+ let tabsStyles = window.getComputedStyle(tabsToolbar);
+ let fullTabsHeight = rect(tabsToolbar).height + verticalMargins(tabsStyles);
+
+ // If the navbar overlaps the tabbar using negative margins, we need to take those into
+ // account so we don't overlap it
+ let navbarMarginTop = parseFloat(window.getComputedStyle($("nav-bar")).marginTop);
+ navbarMarginTop = Math.min(navbarMarginTop, 0);
+
+ // And get the height of what's in the titlebar:
+ let titlebarContentHeight = rect(titlebarContent).height;
+
+ // Padding surrounds the tab-view-deck when we are in customization mode,
+ // so take that into account:
+ let areCustomizing = document.documentElement.hasAttribute("customizing") ||
+ document.documentElement.hasAttribute("customize-exiting");
+ let customizePadding = 0;
+ if (areCustomizing) {
+ let deckStyle = window.getComputedStyle($("tab-view-deck"));
+ customizePadding = parseFloat(deckStyle.paddingTop);
+ }
+
+ // Begin setting CSS properties which will cause a reflow
+
+ // If the menubar is around (menuHeight is non-zero), try to adjust
+ // its full height (i.e. including margins) to match the titlebar,
+ // by changing the menubar's bottom padding
+ if (menuHeight) {
+ // Calculate the difference between the titlebar's height and that of the menubar
+ let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
+ let paddingBottom;
+ // The titlebar is bigger:
+ if (menuTitlebarDelta > 0) {
+ fullMenuHeight += menuTitlebarDelta;
+ // If there is already padding on the menubar, we need to add that
+ // to the difference so the total padding is correct:
+ if ((paddingBottom = menuStyles.paddingBottom)) {
+ menuTitlebarDelta += parseFloat(paddingBottom);
+ }
+ menubar.style.paddingBottom = menuTitlebarDelta + "px";
+ // The menubar is bigger, but has bottom padding we can remove:
+ } else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
+ let existingPadding = parseFloat(paddingBottom);
+ // menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
+ let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
+ menubar.style.paddingBottom = desiredPadding + "px";
+ // We've changed the menu height now:
+ fullMenuHeight += desiredPadding - existingPadding;
+ }
+ }
+
+ // Next, we calculate how much we need to stretch the titlebar down to
+ // go all the way to the bottom of the tab strip, if necessary.
+ let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;
+ // Oh, and don't forget customization mode:
+ if (areCustomizing) {
+ tabAndMenuHeight += customizePadding;
+ }
+
+ if (tabAndMenuHeight > titlebarContentHeight) {
+ // We need to increase the titlebar content's outer height (ie including margins)
+ // to match the tab and menu height:
+ let extraMargin = tabAndMenuHeight - titlebarContentHeight;
+ // We need to reduce the height by the amount of navbar overlap
+ // (this value is 0 or negative):
+ extraMargin += navbarMarginTop;
+ // On non-OSX, we can just use bottom margin:
+#ifndef XP_MACOSX
+ titlebarContent.style.marginBottom = extraMargin + "px";
+#else
+ // Otherwise, center the content. This means taking the titlebar's
+ // padding into account:
+ let halfMargin = (extraMargin - titlebarPadding) / 2;
+ titlebarContent.style.marginTop = halfMargin + "px";
+ titlebarContent.style.marginBottom = (titlebarPadding + halfMargin) + "px";
+#endif
+ titlebarContentHeight += extraMargin;
+ }
+
+ // Then we bring up the titlebar by the same amount, but we add any negative margin:
+ titlebar.style.marginBottom = "-" + titlebarContentHeight + "px";
+
+
+ // Finally, size the placeholders:
+#ifdef XP_MACOSX
+ this._sizePlaceholder("fullscreen-button", fullscreenButtonWidth);
+#endif
+ this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
+
+ if (!this._draghandles) {
+ this._draghandles = {};
let tmp = {};
Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
- this._draghandle = new tmp.WindowDraggingElement(tabsToolbar);
- this._draghandle.mouseDownCheck = function () {
+
+ let mouseDownCheck = function () {
return !this._dragBindingAlive && TabsInTitlebar.enabled;
};
+
+ this._draghandles.tabsToolbar = new tmp.WindowDraggingElement(tabsToolbar);
+ this._draghandles.tabsToolbar.mouseDownCheck = mouseDownCheck;
+
+ this._draghandles.navToolbox = new tmp.WindowDraggingElement(gNavToolbox);
+ this._draghandles.navToolbox.mouseDownCheck = mouseDownCheck;
}
} else {
document.documentElement.removeAttribute("tabsintitlebar");
-
+ updateTitlebarDisplay();
+
+ // Reset the margins and padding that might have been modified:
+ titlebarContent.style.marginBottom = "";
titlebar.style.marginBottom = "";
+ menubar.style.paddingBottom = "";
}
},
_sizePlaceholder: function (type, width) {
Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
function (node) { node.width = width; });
},
#endif
uninit: function () {
#ifdef CAN_DRAW_IN_TITLEBAR
this._initialized = false;
Services.prefs.removeObserver(this._prefName, this);
+ this._menuObserver.disconnect();
#endif
}
};
-#ifdef MENUBAR_CAN_AUTOHIDE
-function updateAppButtonDisplay() {
- var displayAppButton =
- !gInPrintPreviewMode &&
- window.menubar.visible &&
- document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
-
#ifdef CAN_DRAW_IN_TITLEBAR
- document.getElementById("titlebar").hidden = !displayAppButton;
-
- if (displayAppButton)
+function updateTitlebarDisplay() {
+ document.getElementById("titlebar").hidden = !TabsInTitlebar.enabled;
+
+ if (TabsInTitlebar.enabled)
+#ifdef XP_WIN
document.documentElement.setAttribute("chromemargin", "0,2,2,2");
+#else
+ document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
+#endif
else
document.documentElement.removeAttribute("chromemargin");
-
- TabsInTitlebar.allowedBy("drawing-in-titlebar", displayAppButton);
-#else
- document.getElementById("appmenu-toolbar-button").hidden =
- !displayAppButton;
-#endif
}
#endif
#ifdef CAN_DRAW_IN_TITLEBAR
function onTitlebarMaxClick() {
if (window.windowState == window.STATE_MAXIMIZED)
window.restore();
else
@@ -6889,39 +6716,32 @@ let gPrivateBrowsingUI = {
return;
}
// Disable the Clear Recent History... menu item when in PB mode
// temporary fix until bug 463607 is fixed
document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");
if (window.location.href == getBrowserURL()) {
-#ifdef XP_MACOSX
- if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
- document.documentElement.setAttribute("drawintitlebar", true);
- }
-#endif
-
// Adjust the window's title
let docElement = document.documentElement;
if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
docElement.setAttribute("title",
docElement.getAttribute("title_privatebrowsing"));
docElement.setAttribute("titlemodifier",
docElement.getAttribute("titlemodifier_privatebrowsing"));
}
docElement.setAttribute("privatebrowsingmode",
PrivateBrowsingUtils.permanentPrivateBrowsing ? "permanent" : "temporary");
gBrowser.updateTitlebar();
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
// Adjust the New Window menu entries
[
{ normal: "menu_newNavigator", private: "menu_newPrivateWindow" },
- { normal: "appmenu_newNavigator", private: "appmenu_newPrivateWindow" },
].forEach(function(menu) {
let newWindow = document.getElementById(menu.normal);
let newPrivateWindow = document.getElementById(menu.private);
if (newWindow && newPrivateWindow) {
newPrivateWindow.hidden = true;
newWindow.label = newPrivateWindow.label;
newWindow.accessKey = newPrivateWindow.accessKey;
newWindow.command = newPrivateWindow.command;
@@ -7156,21 +6976,16 @@ function duplicateTabIn(aTab, where, del
// A background tab has been opened, nothing else to do here.
break;
case "tab":
gBrowser.selectedTab = newTab;
break;
}
}
-function toggleAddonBar() {
- let addonBar = document.getElementById("addon-bar");
- setToolbarVisibility(addonBar, addonBar.collapsed);
-}
-
var Scratchpad = {
openScratchpad: function SP_openScratchpad() {
return this.ScratchpadManager.openScratchpad();
}
};
XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
let tmp = {};
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -4,17 +4,19 @@
#
# 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/.
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/customizableui/panelUIOverlay.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/browser-lightweightTheme.css" type="text/css"?>
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
# All DTD information is stored in a separate file so that it can be shared by
# hiddenWindow.xul.
#include browser-doctype.inc
@@ -34,23 +36,32 @@
titlemodifier_normal=""
titlemodifier_privatebrowsing="&mainWindow.titlePrivateBrowsingSuffix;"
#else
title_privatebrowsing="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@ &mainWindow.titlePrivateBrowsingSuffix;"
titlemodifier="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@"
titlemodifier_normal="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@"
titlemodifier_privatebrowsing="&mainWindow.titlemodifier;@PRE_RELEASE_SUFFIX@ &mainWindow.titlePrivateBrowsingSuffix;"
#endif
+#ifdef CAN_DRAW_IN_TITLEBAR
+#ifdef XP_WIN
+ chromemargin="0,2,2,2"
+#else
+ chromemargin="0,-1,-1,-1"
+#endif
+ tabsintitlebar="true"
+#endif
titlemenuseparator="&mainWindow.titlemodifiermenuseparator;"
lightweightthemes="true"
lightweightthemesfooter="browser-bottombox"
windowtype="navigator:browser"
macanimationtype="document"
screenX="4" screenY="4"
fullscreenbutton="true"
+ sizemode="normal"
persist="screenX screenY width height sizemode">
# All JS files which are not content (only) dependent that browser.xul
# wishes to include *must* go into the global-scripts.inc file
# so that they can be shared by macBrowserOverlay.xul.
#include global-scripts.inc
<script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
@@ -252,20 +263,16 @@
flip="slide"
rolluponmousewheel="true"
noautofocus="true"
position="topcenter topright"/>
<menupopup id="toolbar-context-menu"
onpopupshowing="onViewToolbarsPopupShowing(event);">
<menuseparator/>
- <menuitem command="cmd_ToggleTabsOnTop"
- type="checkbox"
- label="&viewTabsOnTop.label;"
- accesskey="&viewTabsOnTop.accesskey;"/>
<menuitem command="cmd_CustomizeToolbars"
label="&viewCustomizeToolbar.label;"
accesskey="&viewCustomizeToolbar.accesskey;"/>
</menupopup>
<menupopup id="blockedPopupOptions"
onpopupshowing="gPopupBlockerObserver.fillPopupList(event);"
onpopuphiding="gPopupBlockerObserver.onPopupHiding(event);">
@@ -376,20 +383,16 @@
<hbox pack="center">
<button id="ctrlTab-showAll" class="ctrlTab-preview" noicon="true"/>
</hbox>
</panel>
<!-- Bookmarks and history tooltip -->
<tooltip id="bhTooltip"/>
- <panel id="customizeToolbarSheetPopup"
- noautohide="true"
- sheetstyle="&dialog.dimensions;"/>
-
<tooltip id="tabbrowser-tab-tooltip" onpopupshowing="gBrowser.createTooltip(event);"/>
<tooltip id="back-button-tooltip">
<label class="tooltip-label" value="&backButton.tooltip;"/>
#ifdef XP_MACOSX
<label class="tooltip-label" value="&backForwardButtonMenuMac.tooltip;"/>
#else
<label class="tooltip-label" value="&backForwardButtonMenu.tooltip;"/>
@@ -402,374 +405,495 @@
<label class="tooltip-label" value="&backForwardButtonMenuMac.tooltip;"/>
#else
<label class="tooltip-label" value="&backForwardButtonMenu.tooltip;"/>
#endif
</tooltip>
#include popup-notifications.inc
+#include ../../components/customizableui/content/panelUI.inc.xul
+
<hbox id="downloads-animation-container" mousethrough="always">
<vbox id="downloads-notification-anchor">
<vbox id="downloads-indicator-notification"/>
</vbox>
</hbox>
</popupset>
#ifdef CAN_DRAW_IN_TITLEBAR
<vbox id="titlebar">
<hbox id="titlebar-content">
-#ifdef MENUBAR_CAN_AUTOHIDE
- <hbox id="appmenu-button-container">
- <button id="appmenu-button"
- type="menu"
- label="&brandShortName;"
- style="-moz-user-focus: ignore;">
-#include browser-appmenu.inc
- </button>
- </hbox>
+ <spacer id="titlebar-spacer" flex="1"/>
+ <hbox id="titlebar-buttonbox-container" align="start"
+#ifdef XP_MACOSX
+ ordinal="0"
#endif
- <spacer id="titlebar-spacer" flex="1"/>
- <hbox id="titlebar-buttonbox-container" align="start">
+ >
<hbox id="titlebar-buttonbox">
<toolbarbutton class="titlebar-button" id="titlebar-min" oncommand="window.minimize();"/>
<toolbarbutton class="titlebar-button" id="titlebar-max" oncommand="onTitlebarMaxClick();"/>
<toolbarbutton class="titlebar-button" id="titlebar-close" command="cmd_closeWindow"/>
</hbox>
</hbox>
+#ifdef XP_MACOSX
+ <hbox id="titlebar-fullscreen-button" ordinal="1000"/>
+#endif
</hbox>
</vbox>
#endif
<deck flex="1" id="tab-view-deck">
<vbox flex="1" id="browser-panel">
- <toolbox id="navigator-toolbox"
- defaultmode="icons" mode="icons"
- iconsize="large">
+ <toolbox id="navigator-toolbox">
<!-- Menu -->
<toolbar type="menubar" id="toolbar-menubar" class="chromeclass-menubar" customizable="true"
defaultset="menubar-items"
- mode="icons" iconsize="small" defaulticonsize="small"
- lockiconsize="true"
+ mode="icons" iconsize="small"
#ifdef MENUBAR_CAN_AUTOHIDE
toolbarname="&menubarCmd.label;"
accesskey="&menubarCmd.accesskey;"
+#ifdef XP_LINUX
+ autohide="true"
+#endif
#endif
context="toolbar-context-menu">
- <toolbaritem id="menubar-items" align="center">
+ <toolbaritem id="menubar-items" align="center"
+ cui-areatype="toolbar">
# The entire main menubar is placed into browser-menubar.inc, so that it can be shared by
# hiddenWindow.xul.
#include browser-menubar.inc
</toolbaritem>
#ifdef CAN_DRAW_IN_TITLEBAR
- <hbox class="titlebar-placeholder" type="appmenu-button" ordinal="0"/>
- <hbox class="titlebar-placeholder" type="caption-buttons" ordinal="1000"/>
+#ifndef XP_MACOSX
+ <hbox class="titlebar-placeholder" type="caption-buttons" ordinal="1000"
+ id="titlebar-placeholder-on-menubar-for-caption-buttons" persist="width"
+ skipintoolbarset="true"/>
+#endif
+#endif
+ </toolbar>
+
+ <toolbar id="TabsToolbar"
+ class="toolbar-primary"
+ fullscreentoolbar="true"
+ customizable="true"
+ mode="icons"
+ iconsize="small"
+ aria-label="&tabsToolbar.label;"
+ context="toolbar-context-menu"
+ defaultset="tabbrowser-tabs,new-tab-button,alltabs-button,tabs-closebutton"
+ collapsed="true">
+
+ <tabs id="tabbrowser-tabs"
+ class="tabbrowser-tabs"
+ tabbrowser="content"
+ flex="1"
+ setfocus="false"
+ tooltip="tabbrowser-tab-tooltip"
+ cui-areatype="toolbar"
+ stopwatchid="FX_TAB_CLICK_MS">
+ <tab class="tabbrowser-tab" selected="true" fadein="true"/>
+ </tabs>
+
+ <toolbarbutton id="new-tab-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&tabCmd.label;"
+ command="cmd_newNavigatorTab"
+ onclick="checkForMiddleClick(this, event);"
+ tooltiptext="&newTabButton.tooltip;"
+ ondrop="newTabButtonObserver.onDrop(event)"
+ ondragover="newTabButtonObserver.onDragOver(event)"
+ ondragenter="newTabButtonObserver.onDragOver(event)"
+ ondragexit="newTabButtonObserver.onDragExit(event)"
+ cui-areatype="toolbar"
+ removable="true"/>
+
+ <toolbarbutton id="alltabs-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-button"
+ type="menu"
+ label="&listAllTabs.label;"
+ tooltiptext="&listAllTabs.label;"
+ removable="false">
+ <menupopup id="alltabs-popup"
+ position="after_end">
+ <menuitem id="menu_tabview"
+ class="menuitem-iconic"
+ key="key_tabview"
+ label="&viewTabGroups.label;"
+ command="Browser:ToggleTabView"
+ cui-areatype="toolbar"
+ observes="tabviewGroupsNumber"/>
+ <menuseparator id="alltabs-popup-separator"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbarbutton id="tabs-closebutton"
+ class="close-button tabs-closebutton close-icon"
+ command="cmd_close"
+ label="&closeTab.label;"
+ cui-areatype="toolbar"
+ tooltiptext="&closeTab.label;"/>
+
+#ifdef CAN_DRAW_IN_TITLEBAR
+ <hbox class="titlebar-placeholder" type="caption-buttons"
+ id="titlebar-placeholder-on-TabsToolbar-for-captions-buttons" persist="width"
+#ifndef XP_MACOSX
+ ordinal="1000"
+#endif
+ skipintoolbarset="true"/>
+
+#ifdef XP_MACOSX
+ <hbox class="titlebar-placeholder" type="fullscreen-button"
+ id="titlebar-placeholder-on-TabsToolbar-for-fullscreen-button" persist="width"
+ skipintoolbarset="true"/>
+#endif
#endif
</toolbar>
+ <!--
+ CAVEAT EMPTOR
+ Should you need to add items to the toolbar here, make sure to also add them
+ to the default placements of buttons in CustomizableUI.jsm, so the
+ customization code doesn't get confused.
+ -->
<toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
- toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
+ aria-label="&navbarCmd.label;"
fullscreentoolbar="true" mode="icons" customizable="true"
- iconsize="large"
- defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,webrtc-status-button,bookmarks-menu-button,downloads-button,home-button,window-controls"
+ iconsize="small"
+ defaultset="urlbar-container,search-container,webrtc-status-button,bookmarks-menu-button,downloads-button,home-button,social-share-button,social-toolbar-item"
+ customizationtarget="nav-bar-customization-target"
+ overflowable="true"
+ overflowbutton="nav-bar-overflow-button"
+ overflowtarget="widget-overflow-list"
context="toolbar-context-menu">
- <toolbaritem id="unified-back-forward-button" class="chromeclass-toolbar-additional"
- context="backForwardMenu" removable="true"
- forwarddisabled="true"
- title="&backForwardItem.title;">
- <toolbarbutton id="back-button" class="toolbarbutton-1"
- label="&backCmd.label;"
- command="Browser:BackOrBackDuplicate"
- onclick="checkForMiddleClick(this, event);"
- tooltip="back-button-tooltip"/>
- <toolbarbutton id="forward-button" class="toolbarbutton-1"
- label="&forwardCmd.label;"
- command="Browser:ForwardOrForwardDuplicate"
- onclick="checkForMiddleClick(this, event);"
- tooltip="forward-button-tooltip"/>
- <dummyobservertarget hidden="true"
- onbroadcast="if (this.getAttribute('disabled') == 'true')
- this.parentNode.setAttribute('forwarddisabled', 'true');
- else
- this.parentNode.removeAttribute('forwarddisabled');">
- <observes element="Browser:ForwardOrForwardDuplicate" attribute="disabled"/>
- </dummyobservertarget>
- </toolbaritem>
+ <hbox id="nav-bar-customization-target" class="customization-target" flex="1">
+ <toolbaritem id="urlbar-container" flex="400" persist="width"
+ forwarddisabled="true" title="&locationItem.title;" removable="false"
+ cui-areatype="toolbar"
+ class="chromeclass-location" overflows="false">
+ <toolbarbutton id="back-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&backCmd.label;"
+ command="Browser:BackOrBackDuplicate"
+ onclick="checkForMiddleClick(this, event);"
+ tooltip="back-button-tooltip"
+ context="backForwardMenu"/>
+ <toolbarbutton id="forward-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&forwardCmd.label;"
+ command="Browser:ForwardOrForwardDuplicate"
+ onclick="checkForMiddleClick(this, event);"
+ tooltip="forward-button-tooltip"
+ context="backForwardMenu"/>
+ <dummyobservertarget hidden="true"
+ onbroadcast="if (this.getAttribute('disabled') == 'true')
+ this.parentNode.setAttribute('forwarddisabled', 'true');
+ else
+ this.parentNode.removeAttribute('forwarddisabled');">
+ <observes element="Browser:ForwardOrForwardDuplicate" attribute="disabled"/>
+ </dummyobservertarget>
+ <hbox id="urlbar-wrapper" flex="1" align="center">
+ <textbox id="urlbar" flex="1"
+ placeholder="&urlbar.placeholder2;"
+ type="autocomplete"
+ autocompletesearch="urlinline history"
+ autocompletesearchparam="enable-actions"
+ autocompletepopup="PopupAutoCompleteRichResult"
+ completeselectedindex="true"
+ tabscrolling="true"
+ showcommentcolumn="true"
+ showimagecolumn="true"
+ enablehistory="true"
+ maxrows="6"
+ newlines="stripsurroundingwhitespace"
+ oninput="gBrowser.userTypedValue = this.value;"
+ ontextentered="this.handleCommand(param);"
+ ontextreverted="return this.handleRevert();"
+ pageproxystate="invalid"
+ onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
+ onblur="setTimeout(function() document.getElementById('identity-box').style.MozUserFocus = '', 0);">
+ <box id="notification-popup-box" hidden="true" align="center">
+ <image id="default-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="identity-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="webapps-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="plugin-install-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="mixed-content-blocked-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="servicesInstall-notification-icon" class="notification-anchor-icon" role="button"/>
+ </box>
+ <!-- Use onclick instead of normal popup= syntax since the popup
+ code fires onmousedown, and hence eats our favicon drag events.
+ We only add the identity-box button to the tab order when the location bar
+ has focus, otherwise pressing F6 focuses it instead of the location bar -->
+ <box id="identity-box" role="button"
+ align="center"
+ onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
+ onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
+ ondragstart="gIdentityHandler.onDragStart(event);">
+ <image id="page-proxy-favicon"
+ onclick="PageProxyClickHandler(event);"
+ pageproxystate="invalid"/>
+ <hbox id="identity-icon-labels">
+ <label id="identity-icon-label" class="plain" flex="1"/>
+ <label id="identity-icon-country-label" class="plain"/>
+ </hbox>
+ </box>
+ <box id="urlbar-display-box" align="center">
+ <label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
+ </box>
+ <hbox id="urlbar-icons">
+ <image id="page-report-button"
+ class="urlbar-icon"
+ hidden="true"
+ tooltiptext="&pageReportIcon.tooltip;"
+ onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
+ <image id="star-button"
+ class="urlbar-icon"
+ onclick="if (event.button === 0) BookmarkingUI.onCommand(event);"/>
+ </hbox>
+ <toolbarbutton id="urlbar-go-button"
+ class="chromeclass-toolbar-additional"
+ onclick="gURLBar.handleCommand(event);"
+ tooltiptext="&goEndCap.tooltip;"/>
+ <toolbarbutton id="urlbar-reload-button"
+ class="chromeclass-toolbar-additional"
+ command="Browser:ReloadOrDuplicate"
+ onclick="checkForMiddleClick(this, event);"
+ tooltiptext="&reloadButton.tooltip;"/>
+ <toolbarbutton id="urlbar-stop-button"
+ class="chromeclass-toolbar-additional"
+ command="Browser:Stop"
+ tooltiptext="&stopButton.tooltip;"/>
+ </textbox>
+ </hbox>
+ </toolbaritem>
- <toolbaritem id="urlbar-container" align="center" flex="400" persist="width" combined="true"
- title="&locationItem.title;" class="chromeclass-location" removable="true">
- <textbox id="urlbar" flex="1"
- placeholder="&urlbar.placeholder2;"
- type="autocomplete"
- autocompletesearch="urlinline history"
- autocompletesearchparam="enable-actions"
- autocompletepopup="PopupAutoCompleteRichResult"
- completeselectedindex="true"
- tabscrolling="true"
- showcommentcolumn="true"
- showimagecolumn="true"
- enablehistory="true"
- maxrows="6"
- newlines="stripsurroundingwhitespace"
- oninput="gBrowser.userTypedValue = this.value;"
- ontextentered="this.handleCommand(param);"
- ontextreverted="return this.handleRevert();"
- pageproxystate="invalid"
- onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
- onblur="setTimeout(function() document.getElementById('identity-box').style.MozUserFocus = '', 0);">
- <box id="notification-popup-box" hidden="true" align="center">
- <image id="default-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="identity-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="webapps-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="plugin-install-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="mixed-content-blocked-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"/>
- <image id="servicesInstall-notification-icon" class="notification-anchor-icon" role="button"/>
- </box>
- <!-- Use onclick instead of normal popup= syntax since the popup
- code fires onmousedown, and hence eats our favicon drag events.
- We only add the identity-box button to the tab order when the location bar
- has focus, otherwise pressing F6 focuses it instead of the location bar -->
- <box id="identity-box" role="button"
- align="center"
- onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
- onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
- ondragstart="gIdentityHandler.onDragStart(event);">
- <image id="page-proxy-favicon"
- onclick="PageProxyClickHandler(event);"
- pageproxystate="invalid"/>
- <hbox id="identity-icon-labels">
- <label id="identity-icon-label" class="plain" flex="1"/>
- <label id="identity-icon-country-label" class="plain"/>
- </hbox>
- </box>
- <box id="urlbar-display-box" align="center">
- <label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
- </box>
- <hbox id="urlbar-icons">
- <image id="page-report-button"
- class="urlbar-icon"
- hidden="true"
- tooltiptext="&pageReportIcon.tooltip;"
- onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
- <image id="star-button"
- class="urlbar-icon"
- onclick="if (event.button === 0) BookmarkingUI.onCommand(event);"/>
- <image id="go-button"
- class="urlbar-icon"
- tooltiptext="&goEndCap.tooltip;"
- onclick="gURLBar.handleCommand(event);"/>
- </hbox>
- <toolbarbutton id="urlbar-go-button"
- class="chromeclass-toolbar-additional"
- onclick="gURLBar.handleCommand(event);"
- tooltiptext="&goEndCap.tooltip;"/>
- <toolbarbutton id="urlbar-reload-button"
- class="chromeclass-toolbar-additional"
- command="Browser:ReloadOrDuplicate"
- onclick="checkForMiddleClick(this, event);"
- tooltiptext="&reloadButton.tooltip;"/>
- <toolbarbutton id="urlbar-stop-button"
- class="chromeclass-toolbar-additional"
- command="Browser:Stop"
- tooltiptext="&stopButton.tooltip;"/>
- </textbox>
- </toolbaritem>
+ <toolbaritem id="search-container" title="&searchItem.title;"
+ align="center" class="chromeclass-toolbar-additional panel-wide-item"
+ cui-areatype="toolbar"
+ flex="100" persist="width" removable="true">
+ <searchbar id="searchbar" flex="1"/>
+ </toolbaritem>
- <toolbarbutton id="reload-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- label="&reloadCmd.label;" removable="true"
- command="Browser:ReloadOrDuplicate"
- onclick="checkForMiddleClick(this, event);"
- tooltiptext="&reloadButton.tooltip;"/>
-
- <toolbarbutton id="stop-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- label="&stopCmd.label;" removable="true"
- command="Browser:Stop"
- tooltiptext="&stopButton.tooltip;"/>
+ <toolbarbutton id="webrtc-status-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ type="menu"
+ hidden="true"
+ orient="horizontal"
+ label="&webrtcIndicatorButton.label;"
+ tooltiptext="&webrtcIndicatorButton.tooltip;"
+ cui-areatype="toolbar"
+ overflows="false">
+ <menupopup onpopupshowing="WebrtcIndicator.fillPopup(this);"
+ onpopuphiding="WebrtcIndicator.clearPopup(this);"
+ oncommand="WebrtcIndicator.menuCommand(event.target);"/>
+ </toolbarbutton>
- <toolbaritem id="search-container" title="&searchItem.title;"
- align="center" class="chromeclass-toolbar-additional"
- flex="100" persist="width" removable="true">
- <searchbar id="searchbar" flex="1"/>
- </toolbaritem>
-
- <toolbarbutton id="webrtc-status-button"
- class="toolbarbutton-1 chromeclass-toolbar-additional"
- type="menu"
- hidden="true"
- orient="horizontal"
- label="&webrtcIndicatorButton.label;"
- tooltiptext="&webrtcIndicatorButton.tooltip;">
- <menupopup onpopupshowing="WebrtcIndicator.fillPopup(this);"
- onpopuphiding="WebrtcIndicator.clearPopup(this);"
- oncommand="WebrtcIndicator.menuCommand(event.target);"/>
- </toolbarbutton>
-
- <toolbarbutton id="bookmarks-menu-button"
- class="toolbarbutton-1 chromeclass-toolbar-additional"
- persist="class"
- removable="true"
- type="menu"
- label="&bookmarksMenuButton.label;"
- tooltiptext="&bookmarksMenuButton.tooltip;"
- ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
- ondragover="PlacesMenuDNDHandler.onDragOver(event);"
- ondragleave="PlacesMenuDNDHandler.onDragLeave(event);"
- ondrop="PlacesMenuDNDHandler.onDrop(event);">
- <menupopup id="BMB_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="BMB_viewBookmarksToolbar"
- placesanonid="view-toolbar"
- toolbarId="PersonalToolbar"
- type="checkbox"
- oncommand="onViewToolbarCommand(event)"
- label="&viewBookmarksToolbar.label;"/>
- <menuseparator/>
- <menuitem id="BMB_bookmarksShowAll"
- label="&showAllBookmarks2.label;"
- command="Browser:ShowAllBookmarks"
- key="manBookmarkKb"/>
- <menuseparator/>
- <menuitem id="BMB_bookmarkThisPage"
+ <toolbarbutton id="bookmarks-menu-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ persist="class"
+ removable="true"
+ type="menu-button"
+ label="&bookmarksMenuButton.label;"
+ tooltiptext="&bookmarksMenuButton.tooltip;"
+ ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
+ ondragover="PlacesMenuDNDHandler.onDragOver(event);"
+ ondragleave="PlacesMenuDNDHandler.onDragLeave(event);"
+ ondrop="PlacesMenuDNDHandler.onDrop(event);"
+ cui-areatype="toolbar"
+ oncommand="BookmarkingUI.onCommand(event);">
+ <menupopup id="BMB_bookmarksPopup"
+ placespopup="true"
+ context="placesContext"
+ openInTabs="children"
+ anonanchorclass="toolbarbutton-menubutton-dropmarker"
+ 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="BMB_bookmarksShowAll"
+ label="&showAllBookmarks2.label;"
+ command="Browser:ShowAllBookmarks"
+ key="manBookmarkKb"/>
+ <menuitem id="BMB_viewBookmarksSidebar"
+ label="&viewBookmarksSidebar2.label;"
+ type="checkbox"
+ oncommand="toggleSidebar('viewBookmarksSidebar');">
+ <observes element="viewBookmarksSidebar" attribute="checked"/>
+ </menuitem>
+ <menuseparator/>
+ <menuitem id="BMB_subscribeToPageMenuitem"
#ifndef XP_MACOSX
- class="menuitem-iconic"
+ class="menuitem-iconic"
#endif
- label="&bookmarkThisPageCmd.label;"
- command="Browser:AddBookmarkAs"
- key="addBookmarkAsKb"/>
- <menuitem id="BMB_subscribeToPageMenuitem"
+ label="&subscribeToPageMenuitem.label;"
+ oncommand="return FeedHandler.subscribeToFeed(null, event);"
+ onclick="checkForMiddleClick(this, event);"
+ observes="singleFeedMenuitemState"/>
+ <menu id="BMB_subscribeToPageMenupopup"
#ifndef XP_MACOSX
- class="menuitem-iconic"
+ class="menu-iconic"
#endif
- label="&subscribeToPageMenuitem.label;"
- oncommand="return FeedHandler.subscribeToFeed(null, event);"
- onclick="checkForMiddleClick(this, event);"
- observes="singleFeedMenuitemState"/>
- <menu id="BMB_subscribeToPageMenupopup"
-#ifndef XP_MACOSX
- class="menu-iconic"
-#endif
- label="&subscribeToPageMenupopup.label;"
- observes="multipleFeedsMenuState">
- <menupopup id="BMB_subscribeToPageSubmenuMenupopup"
- onpopupshowing="return FeedHandler.buildFeedList(event.target);"
- oncommand="return FeedHandler.subscribeToFeed(null, event);"
- onclick="checkForMiddleClick(this, event);"/>
- </menu>
- <menuseparator/>
- <menu id="BMB_bookmarksToolbar"
- placesanonid="toolbar-autohide"
- class="menu-iconic bookmark-item"
- label="&personalbarCmd.label;"
- container="true">
- <menupopup id="BMB_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="BMB_unsortedBookmarks"
- label="&bookmarksMenuButton.unsorted.label;"
- oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
- class="menuitem-iconic"/>
- </menupopup>
- </toolbarbutton>
+ label="&subscribeToPageMenupopup.label;"
+ observes="multipleFeedsMenuState">
+ <menupopup id="BMB_subscribeToPageSubmenuMenupopup"
+ onpopupshowing="return FeedHandler.buildFeedList(event.target);"
+ oncommand="return FeedHandler.subscribeToFeed(null, event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ </menu>
+ <menuseparator/>
+ <menu id="BMB_bookmarksToolbar"
+ class="menu-iconic bookmark-item"
+ label="&personalbarCmd.label;"
+ container="true">
+ <menupopup id="BMB_bookmarksToolbarPopup"
+ placespopup="true"
+ context="placesContext"
+ onpopupshowing="if (!this.parentNode._placesView)
+ new PlacesMenu(event, 'place:folder=TOOLBAR');">
+ <menuitem id="BMB_viewBookmarksToolbar"
+ placesanonid="view-toolbar"
+ toolbarId="PersonalToolbar"
+ type="checkbox"
+ oncommand="onViewToolbarCommand(event)"
+ label="&viewBookmarksToolbar.label;"/>
+ <menuseparator/>
+ <!-- Bookmarks toolbar items -->
+ </menupopup>
+ </menu>
+ <menu id="BMB_unsortedBookmarks"
+ class="menu-iconic bookmark-item"
+ label="&bookmarksMenuButton.unsorted.label;"
+ container="true">
+ <menupopup id="BMB_unsortedBookmarksPopup"
+ placespopup="true"
+ context="placesContext"
+ onpopupshowing="if (!this.parentNode._placesView)
+ new PlacesMenu(event, 'place:folder=UNFILED_BOOKMARKS');"/>
+ </menu>
+ <menuseparator/>
+ <!-- Bookmarks menu items -->
+ </menupopup>
+ </toolbarbutton>
- <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- persist="class" removable="true"
- label="&homeButton.label;"
- ondragover="homeButtonObserver.onDragOver(event)"
- ondragenter="homeButtonObserver.onDragOver(event)"
- ondrop="homeButtonObserver.onDrop(event)"
- ondragexit="homeButtonObserver.onDragExit(event)"
- onclick="BrowserGoHome(event);"
- aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
+ <!-- This is a placeholder for the Downloads Indicator. It is visible
+ during the customization of the toolbar, in the palette, and before
+ the Downloads Indicator overlay is loaded. -->
+ <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+ oncommand="DownloadsIndicatorView.onCommand(event);"
+ ondrop="DownloadsIndicatorView.onDrop(event);"
+ ondragover="DownloadsIndicatorView.onDragOver(event);"
+ ondragenter="DownloadsIndicatorView.onDragOver(event);"
+ label="&downloads.label;"
+ removable="true"
+ cui-areatype="toolbar"
+ tooltiptext="&downloads.tooltip;"/>
- <toolbarbutton id="social-share-button"
- class="toolbarbutton-1 chromeclass-toolbar-additional"
- hidden="true"
- label="&sharePageCmd.label;"
- tooltiptext="&sharePageCmd.label;"
- command="Social:SharePage"/>
+ <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+ persist="class" removable="true"
+ label="&homeButton.label;"
+ ondragover="homeButtonObserver.onDragOver(event)"
+ ondragenter="homeButtonObserver.onDragOver(event)"
+ ondrop="homeButtonObserver.onDrop(event)"
+ ondragexit="homeButtonObserver.onDragExit(event)"
+ onclick="BrowserGoHome(event);"
+ cui-areatype="toolbar"
+ aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
+
- <toolbaritem id="social-toolbar-item"
- class="chromeclass-toolbar-additional"
- removable="false"
- title="&socialToolbar.title;"
- hidden="true"
- skipintoolbarset="true"
- observes="socialActiveBroadcaster">
+ <toolbarbutton id="social-share-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ hidden="true"
+ overflows="false"
+ label="&sharePageCmd.label;"
+ tooltiptext="&sharePageCmd.label;"
+ cui-areatype="toolbar"
+ command="Social:SharePage"/>
+
+ <toolbaritem id="social-toolbar-item"
+ class="chromeclass-toolbar-additional toolbaritem-with-separator"
+ removable="false"
+ title="&socialToolbar.title;"
+ hidden="true"
+ overflows="false"
+ cui-areatype="toolbar"
+ observes="socialActiveBroadcaster">
<toolbarbutton id="social-notification-icon" class="default-notification-icon toolbarbutton-1 notification-anchor-icon"
oncommand="PopupNotifications._reshowNotifications(this,
document.getElementById('social-sidebar-browser'));"/>
- <toolbarbutton id="social-provider-button"
- class="toolbarbutton-1"
- type="menu">
- <menupopup id="social-statusarea-popup">
- <menuitem class="social-statusarea-user menuitem-iconic" pack="start" align="center"
- observes="socialBroadcaster_userDetails"
- oncommand="SocialUI.showProfile(); document.getElementById('social-statusarea-popup').hidePopup();">
- <image class="social-statusarea-user-portrait"
- observes="socialBroadcaster_userDetails"/>
- <vbox>
- <label class="social-statusarea-loggedInStatus"
+ <toolbarbutton id="social-provider-button"
+ class="toolbarbutton-1"
+ type="menu">
+ <menupopup id="social-statusarea-popup">
+ <menuitem class="social-statusarea-user menuitem-iconic" pack="start" align="center"
+ observes="socialBroadcaster_userDetails"
+ oncommand="SocialUI.showProfile(); document.getElementById('social-statusarea-popup').hidePopup();">
+ <image class="social-statusarea-user-portrait"
observes="socialBroadcaster_userDetails"/>
- </vbox>
- </menuitem>
+ <vbox>
+ <label class="social-statusarea-loggedInStatus"
+ observes="socialBroadcaster_userDetails"/>
+ </vbox>
+ </menuitem>
#ifndef XP_WIN
- <menuseparator class="social-statusarea-separator"/>
+ <menuseparator class="social-statusarea-separator"/>
#endif
- <menuitem class="social-toggle-sidebar-menuitem"
- type="checkbox"
- autocheck="false"
- command="Social:ToggleSidebar"
- label="&social.toggleSidebar.label;"
- accesskey="&social.toggleSidebar.accesskey;"/>
- <menuitem class="social-toggle-notifications-menuitem"
- type="checkbox"
- autocheck="false"
- command="Social:ToggleNotifications"
- label="&social.toggleNotifications.label;"
- accesskey="&social.toggleNotifications.accesskey;"/>
- <menuitem class="social-toggle-menuitem" command="Social:Toggle"/>
- <menuseparator/>
- <menuseparator class="social-provider-menu" hidden="true"/>
- <menuitem class="social-addons-menuitem" command="Social:Addons"
- label="&social.addons.label;"/>
- <menuitem label="&social.learnMore.label;"
- accesskey="&social.learnMore.accesskey;"
- oncommand="SocialUI.showLearnMore();"/>
- </menupopup>
- </toolbarbutton>
+ <menuitem class="social-toggle-sidebar-menuitem"
+ type="checkbox"
+ autocheck="false"
+ command="Social:ToggleSidebar"
+ label="&social.toggleSidebar.label;"
+ accesskey="&social.toggleSidebar.accesskey;"/>
+ <menuitem class="social-toggle-notifications-menuitem"
+ type="checkbox"
+ autocheck="false"
+ command="Social:ToggleNotifications"
+ label="&social.toggleNotifications.label;"
+ accesskey="&social.toggleNotifications.accesskey;"/>
+ <menuitem class="social-toggle-menuitem" command="Social:Toggle"/>
+ <menuseparator/>
+ <menuseparator class="social-provider-menu" hidden="true"/>
+ <menuitem class="social-addons-menuitem" command="Social:Addons"
+ label="&social.addons.label;"/>
+ <menuitem label="&social.learnMore.label;"
+ accesskey="&social.learnMore.accesskey;"
+ oncommand="SocialUI.showLearnMore();"/>
+ </menupopup>
+ </toolbarbutton>
+ </toolbaritem>
+ </hbox>
+
+ <toolbarbutton id="nav-bar-overflow-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional overflow-button"
+ skipintoolbarset="true"
+ tooltiptext="&navbarOverflow.label;"/>
+
+ <toolbaritem id="PanelUI-button"
+ class="chromeclass-toolbar-additional"
+ removable="false"
+ title="&appmenu.title;">
+ <toolbarbutton id="PanelUI-menu-button"
+ class="toolbarbutton-1"
+ label="&brandShortName;"
+ tooltiptext="&appmenu.title;"/>
</toolbaritem>
- <hbox id="window-controls" hidden="true" pack="end">
+ <hbox id="window-controls" hidden="true" pack="end" skipintoolbarset="true"
+ ordinal="1000">
<toolbarbutton id="minimize-button"
tooltiptext="&fullScreenMinimize.tooltip;"
oncommand="window.minimize();"/>
<toolbarbutton id="restore-button"
tooltiptext="&fullScreenRestore.tooltip;"
oncommand="BrowserFullScreen();"/>
@@ -777,36 +901,40 @@
tooltiptext="&fullScreenClose.tooltip;"
oncommand="BrowserTryToCloseWindow();"/>
</hbox>
</toolbar>
<toolbarset id="customToolbars" context="toolbar-context-menu"/>
<toolbar id="PersonalToolbar"
- mode="icons" iconsize="small" defaulticonsize="small"
- lockiconsize="true"
+ mode="icons" iconsize="small"
class="chromeclass-directories"
context="toolbar-context-menu"
defaultset="personal-bookmarks"
toolbarname="&personalbarCmd.label;" accesskey="&personalbarCmd.accesskey;"
collapsed="true"
customizable="true">
- <toolbaritem flex="1" id="personal-bookmarks" title="&bookmarksItem.title;"
+ <toolbaritem id="personal-bookmarks"
+ flex="1"
+ title="&bookmarksToolbarItem.label;"
+ cui-areatype="toolbar"
removable="true">
+ <toolbarbutton id="bookmarks-toolbar-placeholder"
+ type="wrap"
+ mousethrough="never"
+ label="&bookmarksToolbarItem.label;"
+ oncommand="PlacesToolbarHelper.onPlaceholderCommand();"/>
<hbox flex="1"
id="PlacesToolbar"
context="placesContext"
onclick="BookmarksEventHandler.onClick(event, this._placesView);"
oncommand="BookmarksEventHandler.onCommand(event, this._placesView);"
tooltip="bhTooltip"
popupsinherittooltip="true">
- <toolbarbutton class="bookmark-item bookmarks-toolbar-customize"
- mousethrough="never"
- label="&bookmarksToolbarItem.label;"/>
<hbox flex="1">
<hbox align="center">
<image id="PlacesToolbarDropIndicator"
mousethrough="always"
collapsed="true"/>
</hbox>
<scrollbox orient="horizontal"
id="PlacesToolbarItems"
@@ -824,250 +952,112 @@
tooltip="bhTooltip" popupsinherittooltip="true"
context="placesContext"/>
</toolbarbutton>
</hbox>
</hbox>
</toolbaritem>
</toolbar>
-#ifdef MENUBAR_CAN_AUTOHIDE
-#ifndef CAN_DRAW_IN_TITLEBAR
-#define APPMENU_ON_TABBAR
-#endif
-#endif
-
-
- <toolbar id="TabsToolbar"
- class="toolbar-primary"
- fullscreentoolbar="true"
- customizable="true"
- mode="icons" lockmode="true"
- iconsize="small" defaulticonsize="small" lockiconsize="true"
- aria-label="&tabsToolbar.label;"
- context="toolbar-context-menu"
-#ifdef APPMENU_ON_TABBAR
- defaultset="appmenu-toolbar-button,tabbrowser-tabs,new-tab-button,alltabs-button,tabs-closebutton"
-#else
- defaultset="tabbrowser-tabs,new-tab-button,alltabs-button,tabs-closebutton"
-#endif
- collapsed="true">
-
-#ifdef APPMENU_ON_TABBAR
- <toolbarbutton id="appmenu-toolbar-button"
- class="chromeclass-toolbar-additional"
- type="menu"
- label="&brandShortName;"
- tooltiptext="&appMenuButton.tooltip;">
-#include browser-appmenu.inc
- </toolbarbutton>
-#endif
-
- <tabs id="tabbrowser-tabs"
- class="tabbrowser-tabs"
- tabbrowser="content"
- flex="1"
- setfocus="false"
- tooltip="tabbrowser-tab-tooltip"
- stopwatchid="FX_TAB_CLICK_MS">
- <tab class="tabbrowser-tab" selected="true" fadein="true"/>
- </tabs>
-
- <toolbarbutton id="new-tab-button"
- class="toolbarbutton-1 chromeclass-toolbar-additional"
- label="&tabCmd.label;"
- command="cmd_newNavigatorTab"
- onclick="checkForMiddleClick(this, event);"
- tooltiptext="&newTabButton.tooltip;"
- ondrop="newTabButtonObserver.onDrop(event)"
- ondragover="newTabButtonObserver.onDragOver(event)"
- ondragenter="newTabButtonObserver.onDragOver(event)"
- ondragexit="newTabButtonObserver.onDragExit(event)"
- removable="true"/>
-
- <toolbarbutton id="alltabs-button"
- class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-button"
- type="menu"
- label="&listAllTabs.label;"
- tooltiptext="&listAllTabs.label;"
- removable="true">
- <menupopup id="alltabs-popup"
- position="after_end">
- <menuitem id="menu_tabview"
- class="menuitem-iconic"
- key="key_tabview"
- label="&viewTabGroups.label;"
- command="Browser:ToggleTabView"
- observes="tabviewGroupsNumber"/>
- <menuseparator id="alltabs-popup-separator"/>
- </menupopup>
- </toolbarbutton>
-
- <toolbarbutton id="tabs-closebutton"
- class="close-button tabs-closebutton"
- command="cmd_close"
- label="&closeTab.label;"
- tooltiptext="&closeTab.label;"/>
-
-#ifdef CAN_DRAW_IN_TITLEBAR
- <hbox class="titlebar-placeholder" type="appmenu-button" ordinal="0"/>
- <hbox class="titlebar-placeholder" type="caption-buttons" ordinal="1000"/>
-#endif
+ <!-- This is a shim which will go away ASAP. See bug 749804 for details -->
+ <toolbar id="addon-bar" toolbar-delegate="nav-bar">
+ <hbox id="addonbar-closebutton"/>
+ <statusbar id="status-bar"/>
</toolbar>
<toolbarpalette id="BrowserToolbarPalette">
# Update primaryToolbarButtons in browser/themes/shared/browser.inc when adding
# or removing default items with the toolbarbutton-1 class.
<toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- label="&printButton.label;" command="cmd_print"
- tooltiptext="&printButton.tooltip;"/>
+#ifdef XP_MACOSX
+ command="cmd_print"
+#else
+ command="cmd_printPreview"
+#endif
+ label="&printButton.label;" tooltiptext="&printButton.tooltip;"/>
- <!-- This is a placeholder for the Downloads Indicator. It is visible
- during the customization of the toolbar, in the palette, and before
- the Downloads Indicator overlay is loaded. -->
- <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- oncommand="DownloadsIndicatorView.onCommand(event);"
- ondrop="DownloadsIndicatorView.onDrop(event);"
- ondragover="DownloadsIndicatorView.onDragOver(event);"
- ondragenter="DownloadsIndicatorView.onDragOver(event);"
- label="&downloads.label;"
- tooltiptext="&downloads.tooltip;"/>
-
- <toolbarbutton id="history-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- observes="viewHistorySidebar" label="&historyButton.label;"
- tooltiptext="&historyButton.tooltip;"/>
-
- <toolbarbutton id="bookmarks-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- observes="viewBookmarksSidebar"
- tooltiptext="&bookmarksButton.tooltip;"
- ondrop="bookmarksButtonObserver.onDrop(event)"
- ondragover="bookmarksButtonObserver.onDragOver(event)"
- ondragenter="bookmarksButtonObserver.onDragOver(event)"
- ondragexit="bookmarksButtonObserver.onDragExit(event)"/>
<toolbarbutton id="new-window-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
label="&newNavigatorCmd.label;"
command="key_newNavigator"
tooltiptext="&newWindowButton.tooltip;"
ondrop="newWindowButtonObserver.onDrop(event)"
ondragover="newWindowButtonObserver.onDragOver(event)"
ondragenter="newWindowButtonObserver.onDragOver(event)"
ondragexit="newWindowButtonObserver.onDragExit(event)"/>
<toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
observes="View:FullScreen"
type="checkbox"
label="&fullScreenCmd.label;"
tooltiptext="&fullScreenButton.tooltip;"/>
- <toolbaritem id="zoom-controls" class="chromeclass-toolbar-additional"
- title="&zoomControls.label;">
- <toolbarbutton id="zoom-out-button" class="toolbarbutton-1"
- label="&fullZoomReduceCmd.label;"
- command="cmd_fullZoomReduce"
- tooltiptext="&zoomOutButton.tooltip;"/>
- <toolbarbutton id="zoom-in-button" class="toolbarbutton-1"
- label="&fullZoomEnlargeCmd.label;"
- command="cmd_fullZoomEnlarge"
- tooltiptext="&zoomInButton.tooltip;"/>
- </toolbaritem>
-
- <toolbarbutton id="feed-button"
- type="menu"
- class="toolbarbutton-1 chromeclass-toolbar-additional"
- disabled="true"
- label="&feedButton.label;"
- tooltiptext="&feedButton.tooltip;"
- onclick="return FeedHandler.onFeedButtonClick(event);">
- <menupopup position="after_end"
- id="feed-menu"
- onpopupshowing="return FeedHandler.buildFeedList(this);"
- oncommand="return FeedHandler.subscribeToFeed(null, event);"
- onclick="checkForMiddleClick(this, event);"/>
- </toolbarbutton>
-
- <toolbarbutton id="cut-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- label="&cutCmd.label;"
- command="cmd_cut"
- tooltiptext="&cutButton.tooltip;"/>
-
- <toolbarbutton id="copy-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- label="©Cmd.label;"
- command="cmd_copy"
- tooltiptext="©Button.tooltip;"/>
-
- <toolbarbutton id="paste-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- label="&pasteCmd.label;"
- command="cmd_paste"
- tooltiptext="&pasteButton.tooltip;"/>
-
#ifdef MOZ_SERVICES_SYNC
<toolbarbutton id="sync-button"
class="toolbarbutton-1 chromeclass-toolbar-additional"
label="&syncToolbarButton.label;"
oncommand="gSyncUI.handleToolbarButton()"/>
#endif
- <toolbaritem id="navigator-throbber" title="&throbberItem.title;" align="center" pack="center"
- mousethrough="always">
- <image/>
- </toolbaritem>
-
<toolbarbutton id="tabview-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
label="&tabGroupsButton.label;"
command="Browser:ToggleTabView"
tooltiptext="&tabGroupsButton.tooltip;"
observes="tabviewGroupsNumber"/>
</toolbarpalette>
</toolbox>
<hbox id="fullscr-toggler" collapsed="true"/>
- <hbox flex="1" id="browser">
- <vbox id="browser-border-start" hidden="true" layer="true"/>
- <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
- <sidebarheader id="sidebar-header" align="center">
- <label id="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
- <image id="sidebar-throbber"/>
- <toolbarbutton class="tabs-closebutton" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="toggleSidebar();"/>
- </sidebarheader>
- <browser id="sidebar" flex="1" autoscroll="false" disablehistory="true"
- style="min-width: 14em; width: 18em; max-width: 36em;"/>
- </vbox>
+ <deck id="content-deck" flex="1">
+ <hbox flex="1" id="browser">
+ <vbox id="browser-border-start" hidden="true" layer="true"/>
+ <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
+ <sidebarheader id="sidebar-header" align="center">
+ <label id="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
+ <image id="sidebar-throbber"/>
+ <toolbarbutton class="tabs-closebutton close-icon" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="toggleSidebar();"/>
+ </sidebarheader>
+ <browser id="sidebar" flex="1" autoscroll="false" disablehistory="true"
+ style="min-width: 14em; width: 18em; max-width: 36em;"/>
+ </vbox>
- <splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
- <vbox id="appcontent" flex="1">
- <tabbrowser id="content"
- flex="1" contenttooltip="aHTMLTooltip"
- tabcontainer="tabbrowser-tabs"
- contentcontextmenu="contentAreaContextMenu"
- autocompletepopup="PopupAutoComplete"
- selectpopup="ContentSelectDropdown"/>
- <chatbar id="pinnedchats" layer="true" mousethrough="always" hidden="true"/>
- </vbox>
- <splitter id="social-sidebar-splitter"
- class="chromeclass-extrachrome sidebar-splitter"
- observes="socialSidebarBroadcaster"/>
- <vbox id="social-sidebar-box"
- class="chromeclass-extrachrome"
- observes="socialSidebarBroadcaster"
- persist="width">
- <browser id="social-sidebar-browser"
- type="content"
- context="contentAreaContextMenu"
- disableglobalhistory="true"
- tooltip="aHTMLTooltip"
+ <splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
+ <vbox id="appcontent" flex="1">
+ <tabbrowser id="content"
+ flex="1" contenttooltip="aHTMLTooltip"
+ tabcontainer="tabbrowser-tabs"
+ contentcontextmenu="contentAreaContextMenu"
+ autocompletepopup="PopupAutoComplete"
+ selectpopup="ContentSelectDropdown"
+ onclick="contentAreaClick(event, false);"/>
+ <chatbar id="pinnedchats" layer="true" mousethrough="always" hidden="true"/>
+ </vbox>
+ <splitter id="social-sidebar-splitter"
+ class="chromeclass-extrachrome sidebar-splitter"
+ observes="socialSidebarBroadcaster"/>
+ <vbox id="social-sidebar-box"
+ class="chromeclass-extrachrome"
+ observes="socialSidebarBroadcaster"
+ persist="width">
+ <browser id="social-sidebar-browser"
+ type="content"
+ context="contentAreaContextMenu"
+ disableglobalhistory="true"
+ tooltip="aHTMLTooltip"
popupnotificationanchor="social-notification-icon"
- flex="1"
- style="min-width: 14em; width: 18em; max-width: 36em;"/>
- </vbox>
- <vbox id="browser-border-end" hidden="true" layer="true"/>
- </hbox>
+ flex="1"
+ style="min-width: 14em; width: 18em; max-width: 36em;"/>
+ </vbox>
+ <vbox id="browser-border-end" hidden="true" layer="true"/>
+ </hbox>
+#include ../../components/customizableui/content/customizeMode.inc.xul
+ </deck>
<hbox id="full-screen-warning-container" hidden="true" fadeout="true">
<hbox style="width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. -->
<vbox id="full-screen-warning-message" align="center">
<description id="full-screen-domain-text"/>
<description class="full-screen-description" value="&fullscreenExitHint2.value;"/>
<vbox id="full-screen-approval-pane" align="center">
<hbox>
@@ -1114,66 +1104,38 @@
tooltiptext="&devToolbarToolsButton.tooltip;"/>
#ifndef XP_MACOSX
<toolbarbutton id="developer-toolbar-closebutton"
class="devtools-closebutton"
oncommand="DeveloperToolbar.hide();"
tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
#endif
</toolbar>
-
- <toolbar id="addon-bar"
- toolbarname="&addonBarCmd.label;" accesskey="&addonBarCmd.accesskey;"
- collapsed="true"
- class="toolbar-primary chromeclass-toolbar"
- context="toolbar-context-menu" toolboxid="navigator-toolbox"
- mode="icons" iconsize="small" defaulticonsize="small"
- lockiconsize="true"
- defaultset="addonbar-closebutton,spring,status-bar"
- customizable="true"
- key="key_toggleAddonBar">
- <toolbarbutton id="addonbar-closebutton"
- tooltiptext="&addonBarCloseButton.tooltip;"
- oncommand="setToolbarVisibility(this.parentNode, false);"/>
- <statusbar id="status-bar" ordinal="1000"/>
- </toolbar>
</vbox>
+ <svg:svg height="0">
+#include tab-shape.inc.svg
+
#ifndef XP_UNIX
- <svg:svg height="0">
<svg:clipPath id="windows-keyhole-forward-clip-path" clipPathUnits="objectBoundingBox">
<svg:path d="M 0,0 C 0.16,0.11 0.28,0.29 0.28,0.5 0.28,0.71 0.16,0.89 0,1 L 1,1 1,0 0,0 z"/>
</svg:clipPath>
<svg:clipPath id="windows-urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
- <svg:path d="M 0,0 0,7.8 C 2.5,11 4,14 4,18 4,22 2.5,25 0,28 l 0,22 10000,0 0,-50 L 0,0 z"/>
+ <svg:path d="m 0,-5 l 0,7.8 c 2.5,3.2 4,6.2 4,10.2 c 0,4 -1.5,7 -4,10 l 0,22l10000,0 l 0,-50 l -10000,0 z"/>
</svg:clipPath>
- </svg:svg>
#endif
#ifdef XP_MACOSX
- <svg:svg height="0">
<svg:clipPath id="osx-keyhole-forward-clip-path" clipPathUnits="objectBoundingBox">
<svg:path d="M 0,0 C 0.15,0.12 0.25,0.3 0.25,0.5 0.25,0.7 0.15,0.88 0,1 L 1,1 1,0 0,0 z"/>
</svg:clipPath>
<svg:clipPath id="osx-urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
- <svg:path d="m 0,-5 0,4.03 C 3.6,1.8 6,6.1 6,11 6,16 3.6,20 0,23 l 0,27 10000,0 0,-55 L 0,-5 z"/>
- </svg:clipPath>
- <svg:clipPath id="osx-tab-ontop-left-curve-clip-path" clipPathUnits="userSpaceOnUse">
- <svg:path d="M 9,0 C 7.3,0 6,1.3 6,3 l 0,14 c 0,3 -2.2,5 -5,5 l -1,0 0,1 12,0 0,-1 0,-19 0,-3 -3,0 z"/>
- </svg:clipPath>
- <svg:clipPath id="osx-tab-ontop-right-curve-clip-path" clipPathUnits="userSpaceOnUse">
- <svg:path d="m 0,0 0,3 0,19 0,1 12,0 0,-1 -1,0 C 8.2,22 6,20 6,17 L 6,3 C 6,1.3 4.7,0 3,0 L 0,0 z"/>
+ <svg:path d="m -3,-10 l -0.1,7.7 c 6.6,1.8 8.8,7.6 8.8,12.5 c 0,5 -1.9,11.5 -8.25,13.25 l 0.05,25.75 l 10000,0 l 0,-55 l -10000,-4.2 z"/>
</svg:clipPath>
- <svg:clipPath id="osx-tab-onbottom-left-curve-clip-path" clipPathUnits="userSpaceOnUse">
- <svg:path d="m 0,0 0,1 1,0 c 2.8,0 5,2.2 5,5 l 0,14 c 0,2 1.3,3 3,3 l 3,0 0,-3 L 12,1 12,0 0,0 z"/>
- </svg:clipPath>
- <svg:clipPath id="osx-tab-onbottom-right-curve-clip-path" clipPathUnits="userSpaceOnUse">
- <svg:path d="m 0,0 0,1 0,19 0,3 3,0 c 1.7,0 3,-1 3,-3 L 6,6 C 6,3.2 8.2,1 11,1 L 12,1 12,0 0,0 z"/>
- </svg:clipPath>
+#endif
</svg:svg>
-#endif
</vbox>
# <iframe id="tab-view"> is dynamically appended as the 2nd child of #tab-view-deck.
# Introducing the iframe dynamically, as needed, was found to be better than
# starting with an empty iframe here in browser.xul from a Ts standpoint.
</deck>
</window>
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -4,10 +4,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<script type="application/javascript" src="chrome://global/content/printUtils.js"/>
<script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
<script type="application/javascript" src="chrome://browser/content/places/browserPlacesViews.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/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/viewSourceUtils.js"/>
--- a/browser/base/content/newtab/newTab.xul
+++ b/browser/base/content/newtab/newTab.xul
@@ -26,16 +26,17 @@
value="&newtab.undo.removedLabel;" />
<xul:button id="newtab-undo-button" tabindex="-1"
label="&newtab.undo.undoButton;"
class="newtab-undo-button" />
<xul:button id="newtab-undo-restore-button" tabindex="-1"
label="&newtab.undo.restoreButton;"
class="newtab-undo-button" />
<xul:toolbarbutton id="newtab-undo-close-button" tabindex="-1"
+ class="close-icon"
tooltiptext="&newtab.undo.closeTooltip;" />
</div>
</div>
<div id="newtab-horizontal-margin">
<div class="newtab-side-margin"/>
<div id="newtab-grid">
--- a/browser/base/content/sync/notification.xml
+++ b/browser/base/content/sync/notification.xml
@@ -78,17 +78,17 @@
</implementation>
</binding>
<binding id="notification" extends="chrome://global/content/bindings/notification.xml#notification">
<content>
<xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
<xul:toolbarbutton ondblclick="event.stopPropagation();"
- class="messageCloseButton tabbable"
+ class="messageCloseButton close-icon tabbable"
xbl:inherits="hidden=hideclose"
tooltiptext="&closeNotification.tooltip;"
oncommand="document.getBindingParent(this).close()"/>
<xul:hbox anonid="details" align="center" flex="1">
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image"/>
<xul:description anonid="messageText" class="messageText" xbl:inherits="xbl:text=label"/>
<!-- The children are the buttons defined by the notification. -->
new file mode 100644
--- /dev/null
+++ b/browser/base/content/tab-shape.inc.svg
@@ -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>
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -36,16 +36,17 @@ tabpanels {
background-color: transparent;
}
.tab-drop-indicator {
position: relative;
z-index: 2;
}
+.tab-icon-image:not([src]):not([pinned]),
.tab-throbber:not([busy]),
.tab-throbber[busy] + .tab-icon-image {
display: none;
}
.closing-tabs-spacer {
pointer-events: none;
}
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -202,18 +202,17 @@
]]></body>
</method>
<method name="updateWindowResizers">
<body><![CDATA[
if (!window.gShowPageResizers)
return;
- var show = document.getElementById("addon-bar").collapsed &&
- window.windowState == window.STATE_NORMAL;
+ var show = window.windowState == window.STATE_NORMAL;
for (let i = 0; i < this.browsers.length; i++) {
this.browsers[i].showWindowResizer = show;
}
]]></body>
</method>
<method name="_setCloseKeyState">
<parameter name="aEnabled"/>
@@ -994,17 +993,17 @@
newBrowser.setAttribute("type", "content-primary");
newBrowser.docShellIsActive =
(window.windowState != window.STATE_MINIMIZED);
this.mCurrentBrowser = newBrowser;
this.mCurrentTab = this.tabContainer.selectedItem;
this.showTab(this.mCurrentTab);
- var backForwardContainer = document.getElementById("unified-back-forward-button");
+ var backForwardContainer = document.getElementById("urlbar-container");
if (backForwardContainer) {
backForwardContainer.setAttribute("switchingtabs", "true");
window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
backForwardContainer.removeAttribute("switchingtabs");
});
}
@@ -1490,18 +1489,17 @@
b.setAttribute("type", "content-targetable");
b.setAttribute("message", "true");
b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
if (remote)
b.setAttribute("remote", "true");
- if (window.gShowPageResizers && document.getElementById("addon-bar").collapsed &&
- window.windowState == window.STATE_NORMAL) {
+ if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
b.setAttribute("showresizer", "true");
}
if (this.hasAttribute("autocompletepopup"))
b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
if (this.hasAttribute("selectpopup"))
b.setAttribute("selectpopup", this.getAttribute("selectpopup"));
@@ -1915,16 +1913,21 @@
return null;
newTab = true;
}
aTab.closing = true;
this._removingTabs.push(aTab);
this._visibleTabs = null; // invalidate cache
+
+ // Invalidate hovered tab state tracking for this closing tab.
+ if (this.tabContainer._hoveredTab == aTab)
+ aTab._mouseleave();
+
if (newTab)
this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
else
this.tabContainer.updateVisibility();
// We're committed to closing the tab now.
// Dispatch a notification.
// We dispatch it before any teardown so that event listeners can
@@ -3233,16 +3236,38 @@
]]></body>
</method>
<method name="_canScrollToElement">
<parameter name="tab"/>
<body><![CDATA[
return !tab.pinned && !tab.hidden;
]]></body>
</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>
<handlers>
<handler event="underflow" phase="capturing"><![CDATA[
if (event.detail == 0)
return; // Ignore vertical events
var tabs = document.getBindingParent(this);
@@ -3254,18 +3279,21 @@
tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab,
tabs.tabbrowser);
tabs._positionPinnedTabs();
]]></handler>
<handler event="overflow"><![CDATA[
if (event.detail == 0)
return; // Ignore vertical events
-
var tabs = document.getBindingParent(this);
+ var numberOfTabs = tabs.tabbrowser.visibleTabs.length;
+ if (numberOfTabs == 1)
+ return;
+
tabs.setAttribute("overflow", "true");
tabs._positionPinnedTabs();
tabs._handleTabSelect(false);
]]></handler>
</handlers>
</binding>
<binding id="tabbrowser-tabs"
@@ -3352,16 +3380,25 @@
document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
</field>
<field name="_firstTab">null</field>
<field name="_lastTab">null</field>
<field name="_afterSelectedTab">null</field>
<field name="_beforeHoveredTab">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">
<body><![CDATA[
let visibleTabs = this.tabbrowser.visibleTabs;
if (!visibleTabs.length)
return;
@@ -3382,16 +3419,22 @@
if (this._firstTab)
this._firstTab.removeAttribute("first-visible-tab");
this._firstTab = visibleTabs[0];
this._firstTab.setAttribute("first-visible-tab", "true");
if (this._lastTab)
this._lastTab.removeAttribute("last-visible-tab");
this._lastTab = visibleTabs[lastVisible];
this._lastTab.setAttribute("last-visible-tab", "true");
+
+ let hoveredTab = this._hoveredTab;
+ if (hoveredTab) {
+ hoveredTab._mouseleave();
+ hoveredTab._mouseenter();
+ }
]]></body>
</method>
<field name="_prefObserver"><![CDATA[({
tabContainer: this,
observe: function (subject, topic, data) {
switch (data) {
@@ -3460,20 +3503,16 @@
<method name="_propagateVisibility">
<body><![CDATA[
let visible = this.visible;
document.getElementById("menu_closeWindow").hidden = !visible;
document.getElementById("menu_close").setAttribute("label",
this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close"));
- goSetCommandEnabled("cmd_ToggleTabsOnTop", visible);
-
- TabsOnTop.syncUI();
-
TabsInTitlebar.allowedBy("tabs-visible", visible);
]]></body>
</method>
<method name="updateVisibility">
<body><![CDATA[
if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
this.visible = window.toolbar.visible;
@@ -3815,26 +3854,26 @@
switch (aEvent.type) {
case "load":
this.updateVisibility();
break;
case "resize":
if (aEvent.target != window)
break;
- let sizemode = document.documentElement.getAttribute("sizemode");
- TabsInTitlebar.allowedBy("sizemode",
- sizemode == "maximized" || sizemode == "fullscreen");
-
- var width = this.mTabstrip.boxObject.width;
- if (width != this.mTabstripWidth) {
- this.adjustTabstrip();
- this._fillTrailingGap();
- this._handleTabSelect();
- this.mTabstripWidth = width;
+ TabsInTitlebar.updateAppearance();
+
+ if (this.tabbrowser.visibleTabs.length > 1) {
+ var width = this.mTabstrip.boxObject.width;
+ if (width != this.mTabstripWidth) {
+ this.adjustTabstrip();
+ this._fillTrailingGap();
+ this._handleTabSelect();
+ this.mTabstripWidth = width;
+ }
}
this.tabbrowser.updateWindowResizers();
break;
case "mouseout":
// If the "related target" (the node to which the pointer went) is not
// a child of the current document, the mouse just left the window.
let relatedTarget = aEvent.relatedTarget;
@@ -4118,18 +4157,17 @@
}
]]></handler>
<handler event="dblclick"><![CDATA[
#ifndef XP_MACOSX
// When the tabbar has an unified appearance with the titlebar
// and menubar, a double-click in it should have the same behavior
// as double-clicking the titlebar
- if (TabsInTitlebar.enabled ||
- (TabsOnTop.enabled && this.parentNode._dragBindingAlive))
+ if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
return;
#endif
if (event.button != 0 ||
event.originalTarget.localName != "box")
return;
// See hack note in the tabbrowser-close-tab-button binding
@@ -4242,17 +4280,17 @@
return;
}
event.stopPropagation();
event.preventDefault();
]]></handler>
<handler event="dragstart"><![CDATA[
var tab = this._getDragTargetTab(event);
- if (!tab)
+ if (!tab || this._isCustomizing)
return;
let dt = event.dataTransfer;
dt.mozSetDataAt(TAB_DROP_TYPE, tab, 0);
let browser = tab.linkedBrowser;
// We must not set text/x-moz-url or text/plain data here,
// otherwise trying to deatch the tab by dropping it on the desktop
@@ -4486,17 +4524,17 @@
// Note: while this case is correctly handled here, this event
// isn't dispatched when the tab is moved within the tabstrip,
// see bug 460801.
this._finishAnimateTabMove();
var dt = event.dataTransfer;
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;
return;
}
// Disable detach within the browser toolbox
var eX = event.screenX;
var eY = event.screenY;
var wX = window.screenX;
@@ -4618,17 +4656,17 @@
validate="never"
role="presentation"/>
<xul:label flex="1"
xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected"
class="tab-text tab-label"
role="presentation"/>
<xul:toolbarbutton anonid="close-button"
xbl:inherits="fadein,pinned,selected"
- class="tab-close-button"/>
+ class="tab-close-button close-icon"/>
</xul:hbox>
</xul:stack>
</content>
<implementation>
<property name="pinned" readonly="true">
<getter>
return this.getAttribute("pinned") == "true";
@@ -4639,65 +4677,80 @@
return this.getAttribute("hidden") == "true";
</getter>
</property>
<field name="mOverCloseButton">false</field>
<field name="mCorrespondingMenuitem">null</field>
<field name="closing">false</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>
<handlers>
<handler event="mouseover"><![CDATA[
let anonid = event.originalTarget.getAttribute("anonid");
if (anonid == "close-button")
this.mOverCloseButton = true;
- let tab = event.target;
- 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");
- }
- }
+ this._mouseenter();
]]></handler>
<handler event="mouseout"><![CDATA[
let anonid = event.originalTarget.getAttribute("anonid");
if (anonid == "close-button")
this.mOverCloseButton = false;
- let tabContainer = this.parentNode;
- if (tabContainer._beforeHoveredTab) {
- tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
- tabContainer._beforeHoveredTab = null;
- }
- if (tabContainer._afterHoveredTab) {
- tabContainer._afterHoveredTab.removeAttribute("afterhovered");
- tabContainer._afterHoveredTab = null;
- }
+ this._mouseleave();
]]></handler>
<handler event="dragstart" phase="capturing">
this.style.MozUserFocus = '';
</handler>
<handler event="mousedown" phase="capturing">
<![CDATA[
if (this.selected) {
this.style.MozUserFocus = 'ignore';
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -12,17 +12,16 @@ support-files =
bug564387.html
bug564387_video1.ogv
bug564387_video1.ogv^headers^
bug592338.html
bug792517-2.html
bug792517.html
bug792517.sjs
bug839103.css
- disablechrome.html
discovery.html
domplate_test.js
download_page.html
dummy_page.html
feed_tab.html
file_bug550565_favicon.ico
file_bug550565_popup.html
file_bug822367_1.html
@@ -89,19 +88,16 @@ support-files =
[browser_CTP_drag_drop.js]
[browser_CTP_nonplugins.js]
[browser_CTP_resize.js]
[browser_URLBarSetURI.js]
[browser_aboutHealthReport.js]
[browser_aboutHome.js]
[browser_aboutSyncProgress.js]
[browser_addKeywordSearch.js]
-[browser_addon_bar_aomlistener.js]
-[browser_addon_bar_close_button.js]
-[browser_addon_bar_shortcut.js]
[browser_alltabslistener.js]
[browser_blob-channelname.js]
[browser_bug304198.js]
[browser_bug329212.js]
[browser_bug356571.js]
[browser_bug380960.js]
[browser_bug386835.js]
[browser_bug405137.js]
@@ -162,20 +158,17 @@ support-files =
[browser_bug585785.js]
[browser_bug585830.js]
[browser_bug590206.js]
[browser_bug592338.js]
[browser_bug594131.js]
[browser_bug595507.js]
[browser_bug596687.js]
[browser_bug597218.js]
-[browser_bug598923.js]
-[browser_bug599325.js]
[browser_bug609700.js]
-[browser_bug616836.js]
[browser_bug623155.js]
[browser_bug623893.js]
[browser_bug624734.js]
[browser_bug647886.js]
[browser_bug655584.js]
[browser_bug664672.js]
[browser_bug676619.js]
[browser_bug678392-1.html]
@@ -211,19 +204,17 @@ support-files =
[browser_bug902156.js]
[browser_canonizeURL.js]
[browser_clearplugindata.html]
[browser_clearplugindata.js]
[browser_clearplugindata_noage.html]
[browser_contentAreaClick.js]
[browser_contextSearchTabPosition.js]
[browser_ctrlTab.js]
-[browser_customize.js]
[browser_customize_popupNotification.js]
-[browser_disablechrome.js]
[browser_discovery.js]
[browser_duplicateIDs.js]
[browser_findbarClose.js]
[browser_fullscreen-window-open.js]
[browser_gestureSupport.js]
[browser_getshortcutoruri.js]
[browser_hide_removing.js]
[browser_homeDrop.js]
@@ -284,13 +275,14 @@ support-files =
[browser_urlbar_search_healthreport.js]
[browser_utilityOverlay.js]
[browser_visibleFindSelection.js]
[browser_visibleTabs.js]
[browser_visibleTabs_bookmarkAllPages.js]
[browser_visibleTabs_bookmarkAllTabs.js]
[browser_visibleTabs_contextMenu.js]
[browser_visibleTabs_tabPreview.js]
+[browser_windowopen_reflows.js]
[browser_wyciwyg_urlbarCopying.js]
[browser_zbug569342.js]
[browser_registerProtocolHandler_notification.js]
[browser_registerProtocolHandler_notification.html]
[browser_no_mcb_on_http_site.js]
deleted file mode 100644
--- a/browser/base/content/test/general/browser_addon_bar.js
+++ /dev/null
@@ -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();
-}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_addon_bar_aomlistener.js
+++ /dev/null
@@ -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");
-}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_addon_bar_close_button.js
+++ /dev/null
@@ -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");
-}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_addon_bar_shortcut.js
+++ /dev/null
@@ -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");
-}
--- a/browser/base/content/test/general/browser_bug462289.js
+++ b/browser/base/content/test/general/browser_bug462289.js
@@ -29,26 +29,19 @@ function step2()
setTimeout(step3, 0);
}
function step3()
{
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");
- if (gNavToolbox.getAttribute("tabsontop") == "true") {
- ok(true, "[tabsontop=true] focusing URLBar then sending 1 Shift+Tab.");
- gURLBar.focus();
- 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", { });
- }
+ ok(true, "focusing URLBar then sending 1 Shift+Tab.");
+ gURLBar.focus();
+ EventUtils.synthesizeKey("VK_TAB", {shiftKey: true});
is(gBrowser.selectedTab, tab1, "tab key to selected tab1 keeps tab selected");
is(document.activeElement, tab1, "tab key to selected tab1 activates tab");
EventUtils.synthesizeMouseAtCenter(tab1, {});
setTimeout(step4, 0);
}
function step4()
deleted file mode 100644
--- a/browser/base/content/test/general/browser_bug598923.js
+++ /dev/null
@@ -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");
-}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_bug599325.js
+++ /dev/null
@@ -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();
- }
-}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_bug616836.js
+++ /dev/null
@@ -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");
-}
--- a/browser/base/content/test/general/browser_bug624734.js
+++ b/browser/base/content/test/general/browser_bug624734.js
@@ -1,23 +1,31 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// 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() {
waitForExplicitFinish();
let tab = gBrowser.selectedTab = gBrowser.addTab();
tab.linkedBrowser.addEventListener("load", (function(event) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
- is(BookmarkingUI.star.getAttribute("tooltiptext"),
- BookmarkingUI._unstarredTooltip,
- "Star icon should have the unstarred tooltip text");
-
- gBrowser.removeCurrentTab();
- finish();
+ if (BookmarkingUI.status == BookmarkingUI.STATUS_UPDATING) {
+ waitForCondition(function() BookmarkingUI.status != BookmarkingUI.STATUS_UPDATING, finishTest, "BookmarkingUI was updating for too long");
+ } else {
+ finishTest();
+ }
}), true);
tab.linkedBrowser.loadURI("http://example.com/browser/browser/base/content/test/general/dummy_page.html");
}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_customize.js
+++ /dev/null
@@ -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;
-}
deleted file mode 100644
--- a/browser/base/content/test/general/browser_disablechrome.js
+++ /dev/null
@@ -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);
-}
\ No newline at end of file
--- a/browser/base/content/test/general/browser_overflowScroll.js
+++ b/browser/base/content/test/general/browser_overflowScroll.js
@@ -2,18 +2,18 @@ var tabstrip = gBrowser.tabContainer.mTa
var scrollbox = tabstrip._scrollbox;
var originalSmoothScroll = tabstrip.smoothScroll;
var tabs = gBrowser.tabs;
function rect(ele) ele.getBoundingClientRect();
function width(ele) rect(ele).width;
function left(ele) rect(ele).left;
function right(ele) rect(ele).right;
-function isLeft(ele, msg) is(left(ele), left(scrollbox), msg);
-function isRight(ele, msg) is(right(ele), right(scrollbox), msg);
+function isLeft(ele, msg) is(left(ele) + tabstrip._tabMarginLeft, left(scrollbox), msg);
+function isRight(ele, msg) is(right(ele) - tabstrip._tabMarginRight, right(scrollbox), msg);
function elementFromPoint(x) tabstrip._elementFromPoint(x);
function nextLeftElement() elementFromPoint(left(scrollbox) - 1);
function nextRightElement() elementFromPoint(right(scrollbox) + 1);
function firstScrollable() tabs[gBrowser._numPinnedTabs];
function test() {
requestLongerTimeout(2);
waitForExplicitFinish();
@@ -57,17 +57,21 @@ function runOverflowTests(aEvent) {
gBrowser.selectedTab = tabs[tabs.length - 1];
ok(right(gBrowser.selectedTab) <= right(scrollbox), "Selecting the last tab scrolls it into view " +
"(" + right(gBrowser.selectedTab) + " <= " + right(scrollbox) + ")");
element = nextLeftElement();
EventUtils.synthesizeMouse(upButton, 1, 1, {});
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});
isLeft(element, "Scrolled one page of tabs with a double click");
EventUtils.synthesizeMouse(upButton, 1, 1, {clickCount: 3});
var firstScrollableLeft = left(firstScrollable());
ok(left(scrollbox) <= firstScrollableLeft, "Scrolled to the start with a triple click " +
"(" + left(scrollbox) + " <= " + firstScrollableLeft + ")");
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_windowopen_reflows.js
@@ -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);
+ });
+}
deleted file mode 100644
--- a/browser/base/content/test/general/disablechrome.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<html>
-<body>
-</body>
-</html>
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -32,59 +32,37 @@ function updateTabContextMenu(tab) {
tab = gBrowser.selectedTab;
var evt = new Event("");
tab.dispatchEvent(evt);
menu.openPopup(tab, "end_after", 0, 0, true, false, evt);
is(TabContextMenu.contextTab, tab, "TabContextMenu context is the expected tab");
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) {
if (!aBrowserWin)
aBrowserWin = window;
- aBrowserWin.document.getElementById("cmd_CustomizeToolbars").doCommand();
-
- aBrowserWin.gNavToolbox.addEventListener("beforecustomization", function UI_loaded() {
- aBrowserWin.gNavToolbox.removeEventListener("beforecustomization", UI_loaded);
+ aBrowserWin.gCustomizeMode.enter();
- let win = findToolbarCustomizationWindow(aBrowserWin);
- waitForFocus(function () {
- aCallback(win);
- }, win);
+ aBrowserWin.gNavToolbox.addEventListener("customizationready", function UI_loaded() {
+ aBrowserWin.gNavToolbox.removeEventListener("customizationready", UI_loaded);
+ executeSoon(function() {
+ aCallback(aBrowserWin)
+ });
});
}
function closeToolbarCustomizationUI(aCallback, aBrowserWin) {
- let win = findToolbarCustomizationWindow(aBrowserWin);
-
- win.addEventListener("unload", function unloaded() {
- win.removeEventListener("unload", unloaded);
+ aBrowserWin.gNavToolbox.addEventListener("aftercustomization", function unloaded() {
+ aBrowserWin.gNavToolbox.removeEventListener("aftercustomization", unloaded);
executeSoon(aCallback);
});
- let button = win.document.getElementById("donebutton");
- button.focus();
- button.doCommand();
+ aBrowserWin.gCustomizeMode.exit();
}
function waitForCondition(condition, nextTest, errorMsg) {
var tries = 0;
var interval = setInterval(function() {
if (tries >= 30) {
ok(false, errorMsg);
moveOn();
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -932,26 +932,26 @@
pack="end" align="center">
<xul:button anonid="button"
class="popup-notification-menubutton"
type="menu-button"
xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
<xul:menupopup anonid="menupopup"
xbl:inherits="oncommand=menucommand">
<children/>
- <xul:menuitem class="menuitem-iconic popup-notification-closeitem"
+ <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
label="&closeNotificationItem.label;"
xbl:inherits="oncommand=closeitemcommand"/>
</xul:menupopup>
</xul:button>
</xul:hbox>
</xul:vbox>
<xul:vbox pack="start">
<xul:toolbarbutton anonid="closebutton"
- class="messageCloseButton popup-notification-closebutton tabbable"
+ class="messageCloseButton close-icon popup-notification-closebutton tabbable"
xbl:inherits="oncommand=closebuttoncommand"
tooltiptext="&closeNotification.tooltip;"/>
</xul:vbox>
</content>
<implementation>
<constructor><![CDATA[
this.cancelbtn.setAttribute("tooltiptext", gNavigatorBundle.getString("addonDownloadCancelTooltip"));
@@ -1164,26 +1164,26 @@
style="visibility:hidden" width="16" height="16"/>
<xul:button anonid="button"
type="menu-button"
class="popup-notification-menubutton"
xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
<xul:menupopup anonid="menupopup"
xbl:inherits="oncommand=menucommand">
<children/>
- <xul:menuitem class="menuitem-iconic popup-notification-closeitem"
+ <xul:menuitem class="menuitem-iconic popup-notification-closeitem close-icon"
label="&closeNotificationItem.label;"
xbl:inherits="oncommand=closeitemcommand"/>
</xul:menupopup>
</xul:button>
</xul:hbox>
</xul:vbox>
<xul:vbox pack="start">
<xul:toolbarbutton anonid="closebutton"
- class="messageCloseButton popup-notification-closebutton tabbable"
+ class="messageCloseButton close-icon popup-notification-closebutton tabbable"
xbl:inherits="oncommand=closebuttoncommand"
tooltiptext="&closeNotification.tooltip;"/>
</xul:vbox>
</content>
<implementation>
<constructor><![CDATA[
// this.notification.options.identity is used to pass identity-specific info to the binding
let origin = this.identity.origin
@@ -1503,17 +1503,17 @@
<xul:vbox flex="1" align="stretch" class="popup-notification-main-box"
xbl:inherits="popupid">
<xul:hbox class="click-to-play-plugins-notification-description-box" flex="1" align="start">
<xul:description class="click-to-play-plugins-outer-description" flex="1">
<html:span anonid="click-to-play-plugins-notification-description" />
<xul:label class="text-link click-to-play-plugins-notification-link" anonid="click-to-play-plugins-notification-link" />
</xul:description>
<xul:toolbarbutton anonid="closebutton"
- class="messageCloseButton popup-notification-closebutton tabbable"
+ class="messageCloseButton popup-notification-closebutton tabbable close-icon"
xbl:inherits="oncommand=closebuttoncommand"
tooltiptext="&closeNotification.tooltip;"/>
</xul:hbox>
<xul:grid anonid="click-to-play-plugins-notification-center-box"
class="click-to-play-plugins-notification-center-box">
<xul:columns>
<xul:column flex="1"/>
<xul:column/>
@@ -2022,17 +2022,17 @@
<xul:hbox align="center" flex="1">
<xul:image class="panel-promo-icon"/>
<xul:description anonid="promo-message" class="panel-promo-message" flex="1">
<xul:description anonid="promo-link"
class="plain text-link inline-link"
onclick="document.getBindingParent(this).onLinkClick();"/>
</xul:description>
</xul:hbox>
- <xul:toolbarbutton class="panel-promo-closebutton"
+ <xul:toolbarbutton class="panel-promo-closebutton close-icon"
oncommand="document.getBindingParent(this).onCloseButtonCommand();"
tooltiptext="&closeNotification.tooltip;"/>
</xul:hbox>
</content>
<implementation implements="nsIDOMEventListener">
<constructor><![CDATA[
this._panel.addEventListener("popupshowing", this, false);
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -8,18 +8,17 @@ browser.jar:
% overlay chrome://global/content/console.xul chrome://browser/content/jsConsoleOverlay.xul
% overlay chrome://mozapps/content/update/updates.xul chrome://browser/content/softwareUpdateOverlay.xul
#endif
#ifdef XP_WIN
% overlay chrome://browser/content/browser.xul chrome://browser/content/win6BrowserOverlay.xul os=WINNT osversion>=6
#endif
% overlay chrome://global/content/viewSource.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.js (content/aboutDialog.js)
content/browser/aboutDialog.css (content/aboutDialog.css)
content/browser/aboutRobots.xhtml (content/aboutRobots.xhtml)
content/browser/abouthome/aboutHome.xhtml (content/abouthome/aboutHome.xhtml)
content/browser/abouthome/aboutHome.js (content/abouthome/aboutHome.js)
* content/browser/abouthome/aboutHome.css (content/abouthome/aboutHome.css)
content/browser/abouthome/snippet1.png (content/abouthome/snippet1.png)
--- a/browser/branding/nightly/branding.nsi
+++ b/browser/branding/nightly/branding.nsi
@@ -3,17 +3,17 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# NSIS branding defines for nightly builds.
# The official release build branding.nsi is located in other-license/branding/firefox/
# The unofficial build branding.nsi is located in browser/branding/unofficial/
# BrandFullNameInternal is used for some registry and file system values
# instead of BrandFullName and typically should not be modified.
-!define BrandFullNameInternal "Nightly"
+!define BrandFullNameInternal "UX"
!define CompanyName "mozilla.org"
!define URLInfoAbout "http://www.mozilla.org"
!define URLUpdateInfo "http://www.mozilla.org/projects/firefox"
!define URLStubDownload "http://download.mozilla.org/?product=firefox-nightly-latest&os=win&lang=${AB_CD}"
!define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=nightly&installer_lang=${AB_CD}"
!define Channel "nightly"
--- a/browser/branding/nightly/configure.sh
+++ b/browser/branding/nightly/configure.sh
@@ -1,5 +1,5 @@
# 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/.
-MOZ_APP_DISPLAYNAME=Nightly
+MOZ_APP_DISPLAYNAME=UX
--- a/browser/branding/nightly/locales/en-US/brand.dtd
+++ b/browser/branding/nightly/locales/en-US/brand.dtd
@@ -1,8 +1,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/. -->
-<!ENTITY brandShortName "Nightly">
-<!ENTITY brandFullName "Nightly">
+<!ENTITY brandShortName "UX">
+<!ENTITY brandFullName "UX">
<!ENTITY vendorShortName "Mozilla">
<!ENTITY trademarkInfo.part1 " ">
--- a/browser/branding/nightly/locales/en-US/brand.properties
+++ b/browser/branding/nightly/locales/en-US/brand.properties
@@ -1,9 +1,9 @@
# 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/.
-brandShortName=Nightly
-brandFullName=Nightly
+brandShortName=UX
+brandFullName=UX
vendorShortName=Mozilla
syncBrandShortName=Sync
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -87,16 +87,18 @@ static RedirEntry kRedirMap[] = {
{ "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
nsIAboutModule::ALLOW_SCRIPT },
#ifdef MOZ_SERVICES_HEALTHREPORT
{ "healthreport", "chrome://browser/content/abouthealthreport/abouthealth.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
#endif
{ "app-manager", "chrome://browser/content/devtools/app-manager/index.xul",
nsIAboutModule::ALLOW_SCRIPT },
+ { "customizing", "chrome://browser/content/customizableui/aboutCustomizing.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT },
};
static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);
static nsAutoCString
GetAboutModuleName(nsIURI *aURI)
{
nsAutoCString path;
aURI->GetPath(path);
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -105,16 +105,17 @@ static const mozilla::Module::ContractID
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
#ifdef MOZ_SERVICES_HEALTHREPORT
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
#endif
{ 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)
{ NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
#elif defined(XP_MACOSX)
{ NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
#endif
{ nullptr }
};
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/aboutCustomizing.xhtml
@@ -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>
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/customizeMode.inc.xul
@@ -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>
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/jar.mn
@@ -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
+
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/moz.build
@@ -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/.
+
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/panelUI.css
@@ -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;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -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>
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/panelUI.js
@@ -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);
+ }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/panelUI.xml
@@ -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>
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/content/toolbar.xml
@@ -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>
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/moz.build
@@ -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']
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -0,0 +1,2605 @@
+/* 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 = ["CustomizableUI"];
+
+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, "PanelWideWidgetTracker",
+ "resource:///modules/PanelWideWidgetTracker.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CustomizableWidgets",
+ "resource:///modules/CustomizableWidgets.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
+ "resource://gre/modules/DeferredTask.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm");
+XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
+ const kUrl = "chrome://browser/locale/customizableui/customizableWidgets.properties";
+ return Services.strings.createBundle(kUrl);
+});
+XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
+ "resource://gre/modules/ShortcutUtils.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, "gELS",
+ "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
+
+const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+const kSpecialWidgetPfx = "customizableui-special-";
+
+const kCustomizationContextMenu = "customizationContextMenu";
+
+
+const kPrefCustomizationState = "browser.uiCustomization.state";
+const kPrefCustomizationAutoAdd = "browser.uiCustomization.autoAdd";
+const kPrefCustomizationDebug = "browser.uiCustomization.debug";
+
+/**
+ * The keys are the handlers that are fired when the event type (the value)
+ * is fired on the subview. A widget that provides a subview has the option
+ * of providing onViewShowing and onViewHiding event handlers.
+ */
+const kSubviewEvents = [
+ "ViewShowing",
+ "ViewHiding"
+];
+
+/**
+ * gPalette is a map of every widget that CustomizableUI.jsm knows about, keyed
+ * on their IDs.
+ */
+let gPalette = new Map();
+
+/**
+ * gAreas maps area IDs to Sets of properties about those areas. An area is a
+ * place where a widget can be put.
+ */
+let gAreas = new Map();
+
+/**
+ * gPlacements maps area IDs to Arrays of widget IDs, indicating that the widgets
+ * are placed within that area (either directly in the area node, or in the
+ * customizationTarget of the node).
+ */
+let gPlacements = new Map();
+
+/**
+ * gFuturePlacements represent placements that will happen for areas that have
+ * not yet loaded (due to lazy-loading). This can occur when add-ons register
+ * widgets.
+ */
+let gFuturePlacements = new Map();
+
+//XXXunf Temporary. Need a nice way to abstract functions to build widgets
+// of these types.
+let gSupportedWidgetTypes = new Set(["button", "view", "custom"]);
+
+/**
+ * gPanelsForWindow is a list of known panels in a window which we may need to close
+ * should command events fire which target them.
+ */
+let gPanelsForWindow = new WeakMap();
+
+/**
+ * gSeenWidgets remembers which widgets the user has seen for the first time
+ * before. This way, if a new widget is created, and the user has not seen it
+ * before, it can be put in its default location. Otherwise, it remains in the
+ * palette.
+ */
+let gSeenWidgets = new Set();
+
+/**
+ * gDirtyAreaCache is a set of area IDs for areas where items have been added,
+ * moved or removed at least once. This set is persisted, and is used to
+ * optimize building of toolbars in the default case where no toolbars should
+ * be "dirty".
+ */
+let gDirtyAreaCache = new Set();
+
+let gSavedState = null;
+let gRestoring = false;
+let gDirty = false;
+let gInBatchStack = 0;
+let gResetting = false;
+
+/**
+ * gBuildAreas maps area IDs to actual area nodes within browser windows.
+ */
+let gBuildAreas = new Map();
+
+/**
+ * gBuildWindows is a map of windows that have registered build areas, mapped
+ * to a Set of known toolboxes in that window.
+ */
+let gBuildWindows = new Map();
+
+let gNewElementCount = 0;
+let gGroupWrapperCache = new Map();
+let gSingleWrapperCache = new WeakMap();
+let gListeners = new Set();
+
+let gModuleName = "[CustomizableUI]";
+#include logging.js
+
+let CustomizableUIInternal = {
+ initialize: function() {
+ LOG("Initializing");
+
+ this.addListener(this);
+ this._defineBuiltInWidgets();
+ this.loadSavedState();
+
+ let panelPlacements = [
+ "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",
+ ];
+ let showCharacterEncoding = Services.prefs.getComplexValue(
+ "browser.menu.showCharacterEncoding",
+ Ci.nsIPrefLocalizedString
+ ).data;
+ if (showCharacterEncoding == "true") {
+ panelPlacements.push("characterencoding-button");
+ }
+
+ this.registerArea(CustomizableUI.AREA_PANEL, {
+ anchor: "PanelUI-menu-button",
+ type: CustomizableUI.TYPE_MENU_PANEL,
+ defaultPlacements: panelPlacements
+ });
+ PanelWideWidgetTracker.init();
+
+ this.registerArea(CustomizableUI.AREA_NAVBAR, {
+ legacy: true,
+ type: CustomizableUI.TYPE_TOOLBAR,
+ overflowable: true,
+ defaultPlacements: [
+ "urlbar-container",
+ "search-container",
+ "webrtc-status-button",
+ "bookmarks-menu-button",
+ "downloads-button",
+ "home-button",
+ "social-share-button",
+ "social-toolbar-item",
+ ]
+ });
+#ifndef XP_MACOSX
+ this.registerArea(CustomizableUI.AREA_MENUBAR, {
+ legacy: true,
+ type: CustomizableUI.TYPE_TOOLBAR,
+ defaultPlacements: [
+ "menubar-items",
+ ]
+ });
+#endif
+ this.registerArea(CustomizableUI.AREA_TABSTRIP, {
+ legacy: true,
+ type: CustomizableUI.TYPE_TOOLBAR,
+ defaultPlacements: [
+ "tabbrowser-tabs",
+ "new-tab-button",
+ "alltabs-button",
+ "tabs-closebutton",
+ ]
+ });
+ this.registerArea(CustomizableUI.AREA_BOOKMARKS, {
+ legacy: true,
+ type: CustomizableUI.TYPE_TOOLBAR,
+ defaultPlacements: [
+ "personal-bookmarks",
+ ]
+ });
+
+ this.registerArea(CustomizableUI.AREA_ADDONBAR, {
+ type: CustomizableUI.TYPE_TOOLBAR,
+ legacy: true,
+ defaultPlacements: ["addonbar-closebutton", "status-bar"]
+ });
+ },
+
+ _defineBuiltInWidgets: function() {
+ //XXXunf Need to figure out how to auto-add new builtin widgets in new
+ // app versions to already customized areas.
+ for (let widgetDefinition of CustomizableWidgets) {
+ this.createBuiltinWidget(widgetDefinition);
+ }
+ },
+
+ wrapWidget: function(aWidgetId) {
+ if (gGroupWrapperCache.has(aWidgetId)) {
+ return gGroupWrapperCache.get(aWidgetId);
+ }
+
+ let provider = this.getWidgetProvider(aWidgetId);
+ if (!provider) {
+ return null;
+ }
+
+ if (provider == CustomizableUI.PROVIDER_API) {
+ let widget = gPalette.get(aWidgetId);
+ if (!widget.wrapper) {
+ widget.wrapper = new WidgetGroupWrapper(widget);
+ gGroupWrapperCache.set(aWidgetId, widget.wrapper);
+ }
+ return widget.wrapper;
+ }
+
+ // PROVIDER_SPECIAL gets treated the same as PROVIDER_XUL.
+ let wrapper = new XULWidgetGroupWrapper(aWidgetId);
+ gGroupWrapperCache.set(aWidgetId, wrapper);
+ return wrapper;
+ },
+
+ registerArea: function(aName, aProperties) {
+ if (typeof aName != "string" || !/^[a-z0-9-_]{1,}$/i.test(aName)) {
+ throw new Error("Invalid area name");
+ }
+ if (gAreas.has(aName)) {
+ throw new Error("Area already registered");
+ }
+
+ let props = new Map();
+ for (let key in aProperties) {
+ //XXXgijs for special items, we need to make sure they have an appropriate ID
+ // so we aren't perpetually in a non-default state:
+ if (key == "defaultPlacements" && Array.isArray(aProperties[key])) {
+ props.set(key, aProperties[key].map(x => this.isSpecialWidget(x) ? this.ensureSpecialWidgetId(x) : x ));
+ } else {
+ props.set(key, aProperties[key]);
+ }
+ }
+ gAreas.set(aName, props);
+
+ if (props.get("legacy")) {
+ // Guarantee this area exists in gFuturePlacements, to avoid checking it in
+ // various places elsewhere.
+ gFuturePlacements.set(aName, new Set());
+ } else {
+ this.restoreStateForArea(aName);
+ }
+ },
+
+ unregisterArea: function(aName) {
+ if (typeof aName != "string" || !/^[a-z0-9-_]{1,}$/i.test(aName)) {
+ throw new Error("Invalid area name");
+ }
+ if (!gAreas.has(aName)) {
+ throw new Error("Area not registered");
+ }
+
+ // Move all the widgets out
+ this.beginBatchUpdate();
+ let placements = gPlacements.get(aName);
+ placements.forEach(this.removeWidgetFromArea, this);
+
+ // Delete all remaining traces.
+ gAreas.delete(aName);
+ gPlacements.delete(aName);
+ gFuturePlacements.delete(aName);
+ gBuildAreas.delete(aName);
+ this.endBatchUpdate(true);
+ },
+
+ registerToolbarNode: function(aToolbar, aExistingChildren) {
+ let area = aToolbar.id;
+ if (gBuildAreas.has(area) && gBuildAreas.get(area).has(aToolbar)) {
+ return;
+ }
+ this.beginBatchUpdate();
+ let document = aToolbar.ownerDocument;
+ let areaProperties = gAreas.get(area);
+
+ if (!areaProperties) {
+ throw new Error("Unknown customization area: " + area);
+ }
+
+ let placements = gPlacements.get(area);
+ if (!placements && areaProperties.has("legacy")) {
+ let legacyState = aToolbar.getAttribute("currentset");
+ if (legacyState) {
+ legacyState = legacyState.split(",").filter(s => s);
+ }
+
+ // Manually restore the state here, so the legacy state can be converted.
+ this.restoreStateForArea(area, legacyState);
+ placements = gPlacements.get(area);
+ }
+
+ // Check that the current children and the current placements match. If
+ // not, mark it as dirty:
+ if (aExistingChildren.length != placements.length ||
+ aExistingChildren.every((id, i) => id == placements[i])) {
+ gDirtyAreaCache.add(area);
+ }
+
+ if (areaProperties.has("overflowable")) {
+ aToolbar.overflowable = new OverflowableToolbar(aToolbar);
+ }
+
+ this.registerBuildArea(area, aToolbar);
+
+ // We only build the toolbar if it's been marked as "dirty". Dirty means
+ // one of the following things:
+ // 1) Items have been added, moved or removed from this toolbar before.
+ // 2) The number of children of the toolbar does not match the length of
+ // the placements array for that area.
+ //
+ // This notion of being "dirty" is stored in a cache which is persisted
+ // in the saved state.
+ if (gDirtyAreaCache.has(area)) {
+ this.buildArea(area, placements, aToolbar);
+ }
+ aToolbar.setAttribute("currentset", placements.join(","));
+ this.endBatchUpdate();
+ },
+
+ buildArea: function(aArea, aPlacements, aAreaNode) {
+ let document = aAreaNode.ownerDocument;
+ let window = document.defaultView;
+ let inPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(window);
+ let container = aAreaNode.customizationTarget;
+
+ if (!container) {
+ throw new Error("Expected area " + aArea
+ + " to have a customizationTarget attribute.");
+ }
+
+ this.beginBatchUpdate();
+
+ let currentNode = container.firstChild;
+ let placementsToRemove = new Set();
+ for (let id of aPlacements) {
+ while (currentNode && currentNode.getAttribute("skipintoolbarset") == "true") {
+ currentNode = currentNode.nextSibling;
+ }
+
+ if (currentNode && currentNode.id == id) {
+ currentNode = currentNode.nextSibling;
+ continue;
+ }
+
+ let [provider, node] = this.getWidgetNode(id, window);
+ if (!node) {
+ LOG("Unknown widget: " + id);
+ continue;
+ }
+
+ // If the placements have items in them which are (now) no longer removable,
+ // we shouldn't be moving them:
+ if (node.parentNode != container && !this.isWidgetRemovable(node)) {
+ placementsToRemove.add(id);
+ continue;
+ }
+
+ if (inPrivateWindow && provider == CustomizableUI.PROVIDER_API) {
+ let widget = gPalette.get(id);
+ if (!widget.showInPrivateBrowsing && inPrivateWindow) {
+ continue;
+ }
+ }
+
+ this.ensureButtonContextMenu(node, aArea == CustomizableUI.AREA_PANEL);
+ if (node.localName == "toolbarbutton" && aArea == CustomizableUI.AREA_PANEL) {
+ node.setAttribute("tabindex", "0");
+ if (!node.hasAttribute("type")) {
+ node.setAttribute("type", "wrap");
+ }
+ }
+
+ this.insertWidgetBefore(node, currentNode, container, aArea);
+ if (gResetting) {
+ this.notifyListeners("onWidgetReset", id);
+ }
+ }
+
+ if (currentNode) {
+ let palette = aAreaNode.toolbox ? aAreaNode.toolbox.palette : null;
+ let limit = currentNode.previousSibling;
+ let node = container.lastChild;
+ while (node && node != limit) {
+ let previousSibling = node.previousSibling;
+ // Nodes opt-in to removability. If they're removable, and we haven't
+ // seen them in the placements array, then we toss them into the palette
+ // if one exists. If no palette exists, we just remove the node. If the
+ // node is not removable, we leave it where it is. However, we can only
+ // safely touch elements that have an ID - both because we depend on
+ // IDs, and because such elements are not intended to be widgets
+ // (eg, titlebar-placeholder elements).
+ if (node.id && node.getAttribute("skipintoolbarset") != "true") {
+ if (this.isWidgetRemovable(node)) {
+ if (palette && !this.isSpecialWidget(node.id)) {
+ palette.appendChild(node);
+ this.removeLocationAttributes(node);
+ } else {
+ container.removeChild(node);
+ }
+ } else {
+ this.setLocationAttributes(currentNode, aArea);
+ node.setAttribute("removable", false);
+ LOG("Adding non-removable widget to placements of " + aArea + ": " +
+ node.id);
+ gPlacements.get(aArea).push(node.id);
+ gDirty = true;
+ }
+ }
+ node = previousSibling;
+ }
+ }
+
+ // If there are placements in here which aren't removable from their original area,
+ // we remove them from this area's placement array. They will (have) be(en) added
+ // to their original area's placements array in the block above this one.
+ if (placementsToRemove.size) {
+ let placementAry = gPlacements.get(aArea);
+ for (let id of placementsToRemove) {
+ let index = placementAry.indexOf(id);
+ placementAry.splice(index, 1);
+ }
+ }
+
+ this.endBatchUpdate();
+ },
+
+ addPanelCloseListeners: function(aPanel) {
+ gELS.addSystemEventListener(aPanel, "click", this, false);
+ gELS.addSystemEventListener(aPanel, "keypress", this, false);
+ let win = aPanel.ownerDocument.defaultView;
+ if (!gPanelsForWindow.has(win)) {
+ gPanelsForWindow.set(win, new Set());
+ }
+ gPanelsForWindow.get(win).add(this._getPanelForNode(aPanel));
+ },
+
+ removePanelCloseListeners: function(aPanel) {
+ gELS.removeSystemEventListener(aPanel, "click", this, false);
+ gELS.removeSystemEventListener(aPanel, "keypress", this, false);
+ let win = aPanel.ownerDocument.defaultView;
+ let panels = gPanelsForWindow.get(win);
+ if (panels) {
+ panels.delete(this._getPanelForNode(aPanel));
+ }
+ },
+
+ ensureButtonContextMenu: function(aNode, aShouldHaveCustomizationMenu) {
+ let currentContextMenu = aNode.getAttribute("context") ||
+ aNode.getAttribute("contextmenu");
+ if (aShouldHaveCustomizationMenu) {
+ if (!currentContextMenu)
+ aNode.setAttribute("context", kCustomizationContextMenu);
+ } else {
+ if (currentContextMenu == kCustomizationContextMenu)
+ aNode.removeAttribute("context");
+ }
+ },
+
+ getWidgetProvider: function(aWidgetId) {
+ if (this.isSpecialWidget(aWidgetId)) {
+ return CustomizableUI.PROVIDER_SPECIAL;
+ }
+ if (gPalette.has(aWidgetId)) {
+ return CustomizableUI.PROVIDER_API;
+ }
+ // If this was an API widget that was destroyed, return null:
+ if (gSeenWidgets.has(aWidgetId)) {
+ return null;
+ }
+
+ // We fall back to the XUL provider, but we don't know for sure (at this
+ // point) whether it exists there either. So the API is technically lying.
+ // Ideally, it would be able to return an error value (or throw an
+ // exception) if it really didn't exist. Our code calling this function
+ // handles that fine, but this is a public API.
+ return CustomizableUI.PROVIDER_XUL;
+ },
+
+ getWidgetNode: function(aWidgetId, aWindow) {
+ let document = aWindow.document;
+
+ if (this.isSpecialWidget(aWidgetId)) {
+ let widgetNode = document.getElementById(aWidgetId) ||
+ this.createSpecialWidget(aWidgetId, document);
+ return [ CustomizableUI.PROVIDER_SPECIAL, widgetNode];
+ }
+
+ let widget = gPalette.get(aWidgetId);
+ if (widget) {
+ // If we have an instance of this widget already, just use that.
+ if (widget.instances.has(document)) {
+ LOG("An instance of widget " + aWidgetId + " already exists in this "
+ + "document. Reusing.");
+ return [ CustomizableUI.PROVIDER_API,
+ widget.instances.get(document) ];
+ }
+
+ return [ CustomizableUI.PROVIDER_API,
+ this.buildWidget(document, widget) ];
+ }
+
+ LOG("Searching for " + aWidgetId + " in toolbox.");
+ let node = this.findWidgetInWindow(aWidgetId, aWindow);
+ if (node) {
+ return [ CustomizableUI.PROVIDER_XUL, node ];
+ }
+
+ LOG("No node for " + aWidgetId + " found.");
+ return [];
+ },
+
+ registerMenuPanel: function(aPanel) {
+ if (gBuildAreas.has(CustomizableUI.AREA_PANEL) &&
+ gBuildAreas.get(CustomizableUI.AREA_PANEL).has(aPanel)) {
+ return;
+ }
+
+ let document = aPanel.ownerDocument;
+
+ for (let btn of aPanel.querySelectorAll("toolbarbutton")) {
+ btn.setAttribute("tabindex", "0");
+ this.ensureButtonContextMenu(btn, true);
+ if (!btn.hasAttribute("type")) {
+ btn.setAttribute("type", "wrap");
+ }
+ }
+
+ aPanel.toolbox = document.getElementById("navigator-toolbox");
+ aPanel.customizationTarget = aPanel;
+
+ this.addPanelCloseListeners(aPanel);
+
+ let placements = gPlacements.get(CustomizableUI.AREA_PANEL);
+ this.buildArea(CustomizableUI.AREA_PANEL, placements, aPanel);
+ this.registerBuildArea(CustomizableUI.AREA_PANEL, aPanel);
+ },
+
+ onWidgetAdded: function(aWidgetId, aArea, aPosition) {
+ this.insertNode(aWidgetId, aArea, aPosition, true);
+ },
+
+ onWidgetRemoved: function(aWidgetId, aArea) {
+ let areaNodes = gBuildAreas.get(aArea);
+ if (!areaNodes) {
+ return;
+ }
+
+ let area = gAreas.get(aArea);
+ let showInPrivateBrowsing = gPalette.has(aWidgetId)
+ ? gPalette.get(aWidgetId).showInPrivateBrowsing
+ : true;
+
+ for (let areaNode of areaNodes) {
+ let window = areaNode.ownerDocument.defaultView;
+ if (!showInPrivateBrowsing &&
+ PrivateBrowsingUtils.isWindowPrivate(window)) {
+ continue;
+ }
+
+ let container = areaNode.customizationTarget;
+ let widgetNode = container.ownerDocument.getElementById(aWidgetId);
+ if (!widgetNode) {
+ ERROR("Widget not found, unable to remove");
+ continue;
+ }
+
+ this.notifyListeners("onWidgetBeforeDOMChange", widgetNode, null, container, true);
+
+ if (gPalette.has(aWidgetId) || this.isSpecialWidget(aWidgetId)) {
+ container.removeChild(widgetNode);
+ } else {
+ this.removeLocationAttributes(widgetNode);
+ widgetNode.removeAttribute("tabindex");
+ if (widgetNode.getAttribute("type") == "wrap") {
+ widgetNode.removeAttribute("type");
+ }
+ areaNode.toolbox.palette.appendChild(widgetNode);
+ }
+ this.notifyListeners("onWidgetAfterDOMChange", widgetNode, null, container, true);
+
+ if (area.get("type") == CustomizableUI.TYPE_TOOLBAR) {
+ areaNode.setAttribute("currentset", areaNode.currentSet);
+ }
+
+ let windowCache = gSingleWrapperCache.get(window);
+ if (windowCache) {
+ windowCache.delete(aWidgetId);
+ }
+ }
+ },
+
+ onWidgetMoved: function(aWidgetId, aArea, aOldPosition, aNewPosition) {
+ this.insertNode(aWidgetId, aArea, aNewPosition);
+ },
+
+ registerBuildArea: function(aArea, aNode) {
+ // We ensure that the window is registered to have its customization data
+ // cleaned up when unloading.
+ let window = aNode.ownerDocument.defaultView;
+ if (window.closed) {
+ return;
+ }
+ this.registerBuildWindow(window);
+
+ // Also register this build area's toolbox.
+ if (aNode.toolbox) {
+ gBuildWindows.get(window).add(aNode.toolbox);
+ }
+
+ if (!gBuildAreas.has(aArea)) {
+ gBuildAreas.set(aArea, new Set());
+ }
+
+ gBuildAreas.get(aArea).add(aNode);
+ },
+
+ registerBuildWindow: function(aWindow) {
+ if (!gBuildWindows.has(aWindow)) {
+ gBuildWindows.set(aWindow, new Set());
+
+ aWindow.addEventListener("unload", this);
+ aWindow.addEventListener("command", this, true);
+ }
+ },
+
+ unregisterBuildWindow: function(aWindow) {
+ aWindow.removeEventListener("unload", this);
+ aWindow.removeEventListener("command", this, true);
+ gPanelsForWindow.delete(aWindow);
+ gBuildWindows.delete(aWindow);
+ gSingleWrapperCache.delete(aWindow);
+ let document = aWindow.document;
+
+ for (let [areaId, areaNodes] of gBuildAreas) {
+ let areaProperties = gAreas.get(areaId);
+ for (let node of areaNodes) {
+ if (node.ownerDocument == document) {
+ if (areaProperties.has("overflowable")) {
+ node.overflowable.uninit();
+ node.overflowable = null;
+ }
+ areaNodes.delete(node);
+ }
+ }
+ }
+
+ for (let [,widget] of gPalette) {
+ widget.instances.delete(document);
+ this.notifyListeners("onWidgetInstanceRemoved", widget.id, document);
+ }
+ },
+
+ setLocationAttributes: function(aNode, aArea) {
+ let props = gAreas.get(aArea);
+ if (!props) {
+ throw new Error("Expected area " + aArea + " to have a properties Map " +
+ "associated with it.");
+ }
+
+ aNode.setAttribute("cui-areatype", props.get("type") || "");
+ let anchor = props.get("anchor");
+ if (anchor) {
+ aNode.setAttribute("cui-anchorid", anchor);
+ }
+ },
+
+ removeLocationAttributes: function(aNode) {
+ aNode.removeAttribute("cui-areatype");
+ aNode.removeAttribute("cui-anchorid");
+ },
+
+ insertNode: function(aWidgetId, aArea, aPosition, isNew) {
+ let areaNodes = gBuildAreas.get(aArea);
+ if (!areaNodes) {
+ return;
+ }
+
+ let placements = gPlacements.get(aArea);
+ if (!placements) {
+ ERROR("Could not find any placements for " + aArea +
+ " when moving a widget.");
+ return;
+ }
+
+ let nextNodeId = placements[aPosition + 1];
+ // Go through each of the nodes associated with this area and move the
+ // widget to the requested location.
+ for (let areaNode of areaNodes) {
+ this.insertNodeInWindow(aWidgetId, areaNode, nextNodeId, isNew);
+ }
+ },
+
+ insertNodeInWindow: function(aWidgetId, aAreaNode, aNextNodeId, isNew) {
+ let window = aAreaNode.ownerDocument.defaultView;
+ let showInPrivateBrowsing = gPalette.has(aWidgetId)
+ ? gPalette.get(aWidgetId).showInPrivateBrowsing
+ : true;
+
+ if (!showInPrivateBrowsing && PrivateBrowsingUtils.isWindowPrivate(window)) {
+ return;
+ }
+
+ let [, widgetNode] = this.getWidgetNode(aWidgetId, window);
+ if (!widgetNode) {
+ ERROR("Widget '" + aWidgetId + "' not found, unable to move");
+ return;
+ }
+
+ let areaId = aAreaNode.id;
+ if (isNew) {
+ this.ensureButtonContextMenu(widgetNode, areaId == CustomizableUI.AREA_PANEL);
+ if (widgetNode.localName == "toolbarbutton" && areaId == CustomizableUI.AREA_PANEL) {
+ widgetNode.setAttribute("tabindex", "0");
+ if (!widgetNode.hasAttribute("type")) {
+ widgetNode.setAttribute("type", "wrap");
+ }
+ }
+ }
+
+ let container = aAreaNode.customizationTarget;
+ let [insertionContainer, nextNode] = this.findInsertionPoints(widgetNode, aNextNodeId, aAreaNode);
+ this.insertWidgetBefore(widgetNode, nextNode, insertionContainer, areaId);
+
+ if (gAreas.get(areaId).get("type") == CustomizableUI.TYPE_TOOLBAR) {
+ aAreaNode.setAttribute("currentset", aAreaNode.currentSet);
+ }
+ },
+
+ findInsertionPoints: function(aNode, aNextNodeId, aAreaNode) {
+ let props = gAreas.get(aAreaNode.id);
+ if (props.get("type") == CustomizableUI.TYPE_TOOLBAR && props.get("overflowable") &&
+ aAreaNode.getAttribute("overflowing") == "true") {
+ return aAreaNode.overflowable.getOverflowedInsertionPoints(aNode, aNextNodeId);
+ }
+ let nextNode = null;
+ if (aNextNodeId) {
+ nextNode = aAreaNode.customizationTarget.querySelector(idToSelector(aNextNodeId));
+ }
+ return [aAreaNode.customizationTarget, nextNode];
+ },
+
+ insertWidgetBefore: function(aNode, aNextNode, aContainer, aArea) {
+ this.notifyListeners("onWidgetBeforeDOMChange", aNode, aNextNode, aContainer);
+ this.setLocationAttributes(aNode, aArea);
+ aContainer.insertBefore(aNode, aNextNode);
+ this.notifyListeners("onWidgetAfterDOMChange", aNode, aNextNode, aContainer);
+ },
+
+ handleEvent: function(aEvent) {
+ switch (aEvent.type) {
+ case "command":
+ if (!this._originalEventInPanel(aEvent)) {
+ break;
+ }
+ aEvent = aEvent.sourceEvent;
+ // Fall through
+ case "click":
+ case "keypress":
+ this.maybeAutoHidePanel(aEvent);
+ break;
+ case "unload":
+ this.unregisterBuildWindow(aEvent.currentTarget);
+ break;
+ }
+ },
+
+ _originalEventInPanel: function(aEvent) {
+ let e = aEvent.sourceEvent;
+ if (!e) {
+ return false;
+ }
+ let node = this._getPanelForNode(e.target);
+ if (!node) {
+ return false;
+ }
+ let win = e.view;
+ let panels = gPanelsForWindow.get(win);
+ return !!panels && panels.has(node);
+ },
+
+ isSpecialWidget: function(aId) {
+ return (aId.startsWith(kSpecialWidgetPfx) ||
+ aId.startsWith("separator") ||
+ aId.startsWith("spring") ||
+ aId.startsWith("spacer"));
+ },
+
+ ensureSpecialWidgetId: function(aId) {
+ let nodeType = aId.match(/spring|spacer|separator/)[0];
+ // If the ID we were passed isn't a generated one, generate one now:
+ if (nodeType == aId) {
+ // Ids are differentiated through a unique count suffix.
+ return kSpecialWidgetPfx + aId + (++gNewElementCount);
+ }
+ return aId;
+ },
+
+ createSpecialWidget: function(aId, aDocument) {
+ let nodeName = "toolbar" + aId.match(/spring|spacer|separator/)[0];
+ let node = aDocument.createElementNS(kNSXUL, nodeName);
+ node.id = this.ensureSpecialWidgetId(aId);
+ if (nodeName == "toolbarspring") {
+ node.flex = 1;
+ }
+ return node;
+ },
+
+ /* Find a XUL-provided widget in a window. Don't try to use this
+ * for an API-provided widget or a special widget.
+ */
+ findWidgetInWindow: function(aId, aWindow) {
+ if (!gBuildWindows.has(aWindow)) {
+ throw new Error("Build window not registered");
+ }
+
+ if (!aId) {
+ ERROR("findWidgetInWindow was passed an empty string.");
+ return null;
+ }
+
+ let document = aWindow.document;
+
+ // look for a node with the same id, as the node may be
+ // in a different toolbar.
+ let node = document.getElementById(aId);
+ if (node) {
+ let parent = node.parentNode;
+ while (parent && !(parent.customizationTarget ||
+ parent.localName == "toolbarpaletteitem")) {
+ parent = parent.parentNode;
+ }
+
+ if (parent && ((parent.customizationTarget == node.parentNode &&
+ gBuildWindows.get(aWindow).has(parent.toolbox)) ||
+ parent.localName == "toolbarpaletteitem")) {
+ // Normalize the removable attribute. For backwards compat, if
+ // the widget is not defined in a toolbox palette then absence
+ // of the "removable" attribute means it is not removable.
+ if (!node.hasAttribute("removable")) {
+ parent = parent.localName == "toolbarpaletteitem" ? parent.parentNode : parent;
+ // If we first see this in customization mode, it may be in the
+ // customization palette instead of the toolbox palette.
+ node.setAttribute("removable", !parent.customizationTarget);
+ }
+
+ return node;
+ }
+ }
+
+ let toolboxes = gBuildWindows.get(aWindow);
+ for (let toolbox of toolboxes) {
+ if (toolbox.palette) {
+ // Attempt to locate a node with a matching ID within
+ // the palette.
+ let node = toolbox.palette.querySelector(idToSelector(aId));
+ if (node) {
+ // Normalize the removable attribute. For backwards compat, this
+ // is optional if the widget is defined in the toolbox palette,
+ // and defaults to *true*, unlike if it was defined elsewhere.
+ if (!node.hasAttribute("removable")) {
+ node.setAttribute("removable", true);
+ }
+ return node;
+ }
+ }
+ }
+ return null;
+ },
+
+ buildWidget: function(aDocument, aWidget) {
+ if (typeof aWidget == "string") {
+ aWidget = gPalette.get(aWidget);
+ }
+ if (!aWidget) {
+ throw new Error("buildWidget was passed a non-widget to build.");
+ }
+
+ LOG("Building " + aWidget.id + " of type " + aWidget.type);
+
+ let node;
+ if (aWidget.type == "custom") {
+ if (aWidget.onBuild) {
+ try {
+ node = aWidget.onBuild(aDocument);
+ } catch (ex) {
+ ERROR("Custom widget with id " + aWidget.id + " threw an error: " + ex.message);
+ }
+ }
+ if (!node || !(node instanceof aDocument.defaultView.XULElement))
+ ERROR("Custom widget with id " + aWidget.id + " does not return a valid node");
+ }
+ else {
+ node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
+
+ node.setAttribute("id", aWidget.id);
+ node.setAttribute("widget-id", aWidget.id);
+ node.setAttribute("widget-type", aWidget.type);
+ if (aWidget.disabled) {
+ node.setAttribute("disabled", true);
+ }
+ node.setAttribute("removable", aWidget.removable);
+ node.setAttribute("overflows", aWidget.overflows);
+ node.setAttribute("label", this.getLocalizedProperty(aWidget, "label"));
+ let additionalTooltipArguments = [];
+ if (aWidget.shortcutId) {
+ let keyEl = aDocument.getElementById(aWidget.shortcutId);
+ if (keyEl) {
+ additionalTooltipArguments.push(ShortcutUtils.prettifyShortcut(keyEl));
+ } else {
+ ERROR("Key element with id '" + aWidget.shortcutId + "' for widget '" + aWidget.id +
+ "' not found!");
+ }
+ }
+ let tooltip = this.getLocalizedProperty(aWidget, "tooltiptext", additionalTooltipArguments);
+ node.setAttribute("tooltiptext", tooltip);
+ node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional");
+
+ let commandHandler = this.handleWidgetCommand.bind(this, aWidget, node);
+ node.addEventListener("command", commandHandler, false);
+ let clickHandler = this.handleWidgetClick.bind(this, aWidget, node);
+ node.addEventListener("click", clickHandler, false);
+
+ // If the widget has a view, and has view showing / hiding listeners,
+ // hook those up to this widget.
+ if (aWidget.type == "view" &&
+ (aWidget.onViewShowing || aWidget.onViewHiding)) {
+ LOG("Widget " + aWidget.id + " has a view with showing and hiding events. Auto-registering event handlers.");
+ let viewNode = aDocument.getElementById(aWidget.viewId);
+
+ if (viewNode) {
+ // PanelUI relies on the .PanelUI-subView class to be able to show only
+ // one sub-view at a time.
+ viewNode.classList.add("PanelUI-subView");
+
+ for (let eventName of kSubviewEvents) {
+ let handler = "on" + eventName;
+ if (typeof aWidget[handler] == "function") {
+ viewNode.addEventListener(eventName, aWidget[handler], false);
+ }
+ }
+
+ LOG("Widget " + aWidget.id + " showing and hiding event handlers set.");
+ } else {
+ ERROR("Could not find the view node with id: " + aWidget.viewId +
+ ", for widget: " + aWidget.id + ".");
+ }
+ }
+
+ if (aWidget.onCreated) {
+ aWidget.onCreated(node);
+ }
+ }
+
+ aWidget.instances.set(aDocument, node);
+ return node;
+ },
+
+ getLocalizedProperty: function(aWidget, aProp, aFormatArgs, aDef) {
+ if (typeof aWidget == "string") {
+ aWidget = gPalette.get(aWidget);
+ }
+ if (!aWidget) {
+ throw new Error("getLocalizedProperty was passed a non-widget to work with.");
+ }
+ let def, name;
+ // Let widgets pass their own string identifiers or strings, so that
+ // we can use strings which aren't the default (in case string ids change)
+ // and so that non-builtin-widgets can also provide labels, tooltips, etc.
+ if (aWidget[aProp]) {
+ name = aWidget[aProp];
+ // By using this as the default, if a widget provides a full string rather
+ // than a string ID for localization, we will fall back to that string
+ // and return that.
+ def = aDef || name;
+ } else {
+ name = aWidget.id + "." + aProp;
+ def = aDef || "";
+ }
+ try {
+ if (Array.isArray(aFormatArgs) && aFormatArgs.length) {
+ return gWidgetsBundle.formatStringFromName(name, aFormatArgs,
+ aFormatArgs.length) || def;
+ }
+ return gWidgetsBundle.GetStringFromName(name) || def;
+ } catch(ex) {
+ if (!def) {
+ ERROR("Could not localize property '" + name + "'.");
+ }
+ }
+ return def;
+ },
+
+ handleWidgetCommand: function(aWidget, aNode, aEvent) {
+ LOG("handleWidgetCommand");
+
+ if (aWidget.type == "button") {
+ this.maybeAutoHidePanel(aEvent);
+
+ if (aWidget.onCommand) {
+ try {
+ aWidget.onCommand.call(null, aEvent);
+ } catch (e) {
+ ERROR(e);
+ }
+ } else {
+ //XXXunf Need to think this through more, and formalize.
+ Services.obs.notifyObservers(aNode,
+ "customizedui-widget-command",
+ aWidget.id);
+ }
+ } else if (aWidget.type == "view") {
+ let ownerWindow = aNode.ownerDocument.defaultView;
+ ownerWindow.PanelUI.showSubView(aWidget.viewId, aNode,
+ this.getPlacementOfWidget(aNode.id).area);
+ }
+ },
+
+ handleWidgetClick: function(aWidget, aNode, aEvent) {
+ LOG("handleWidgetClick");
+ if (aWidget.type == "button") {
+ this.maybeAutoHidePanel(aEvent);
+ }
+
+ if (aWidget.onClick) {
+ try {
+ aWidget.onClick.call(null, aEvent);
+ } catch(e) {
+ Cu.reportError(e);
+ }
+ } else {
+ //XXXunf Need to think this through more, and formalize.
+ Services.obs.notifyObservers(aNode, "customizedui-widget-click", aWidget.id);
+ }
+ },
+
+ _getPanelForNode: function(aNode) {
+ let panel = aNode;
+ while (panel && panel.localName != "panel")
+ panel = panel.parentNode;
+ return panel;
+ },
+
+ /*
+ * If people put things in the panel which need more than single-click interaction,
+ * we don't want to close it. Right now we check for text inputs and menu buttons.
+ * Anything else we should take care of?
+ */
+ _isOnInteractiveElement: function(aEvent) {
+ let target = aEvent.originalTarget;
+ let panel = aEvent.currentTarget;
+ let inInput = false;
+ let inMenu = false;
+ while (!inInput && !inMenu && target != aEvent.currentTarget) {
+ inInput = target.localName == "input";
+ inMenu = target.type == "menu";
+ target = target.parentNode;
+ }
+ return inMenu || inInput;
+ },
+
+ hidePanelForNode: function(aNode) {
+ let panel = this._getPanelForNode(aNode);
+ if (panel) {
+ panel.hidePopup();
+ }
+ },