author | Gijs Kruitbosch <gijskruitbosch@gmail.com> |
Tue, 08 Oct 2013 09:09:32 +0200 | |
changeset 170396 | ffbb0944e1de21bedbcd39d507f3a98bd7cb871a |
parent 163877 | 56b0a41985f3475c13a7571d81292fb2983134b6 (current diff) |
parent 170395 | b6b9e744baebe0e9bff937ce9e0b6f3139067ecd (diff) |
child 170397 | aa03fbc1149f56b60a48f836908a7fb0a7673779 |
push id | 3224 |
push user | lsblakk@mozilla.com |
push date | Tue, 04 Feb 2014 01:06:49 +0000 |
treeherder | mozilla-beta@60c04d0987f1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 27.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- 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 @@ -442,21 +442,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); @@ -1315,8 +1314,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 @@ -396,16 +396,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), { @@ -416,16 +417,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; @@ -825,19 +822,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. @@ -848,30 +846,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; @@ -887,17 +886,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) { @@ -955,83 +954,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"); }, @@ -1054,97 +1077,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(); @@ -1154,17 +1180,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) { @@ -1203,63 +1229,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 @@ -784,19 +784,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"); @@ -1119,19 +1117,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 @@ -413,26 +413,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,75 @@ 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 -.bookmarks-toolbar-customize, -#wrapper-personal-bookmarks > #personal-bookmarks > #PlacesToolbar > hbox > #PlacesToolbarItems { +%ifdef XP_WIN +#main-window[sizemode="maximized"] #titlebar-buttonbox { + -moz-appearance: -moz-window-button-box-maximized; +} +%endif + +%endif + +#bookmarks-toolbar-placeholder, +toolbarpaletteitem > #personal-bookmarks > #PlacesToolbar, +#personal-bookmarks[customizableui-areatype="menu-panel"] > #PlacesToolbar, +#personal-bookmarks[customizableui-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[customizableui-areatype="menu-panel"] > #bookmarks-toolbar-placeholder, +#personal-bookmarks[customizableui-areatype="toolbar"].overflowedItem > #bookmarks-toolbar-placeholder { display: -moz-box; } -#main-window[disablechrome] #navigator-toolbox[tabsontop="true"] > toolbar:not(#toolbar-menubar):not(#TabsToolbar) { +#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 +245,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 +289,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 +346,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 +384,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 +482,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 +546,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 +787,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 @@ -79,16 +79,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); @@ -147,16 +153,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 @@ -309,39 +316,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; @@ -917,38 +917,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; }, @@ -1040,17 +1031,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(); @@ -1091,21 +1088,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(); @@ -1177,54 +1169,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); } @@ -1315,18 +1278,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"); @@ -1367,18 +1328,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) @@ -1393,17 +1355,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"); } @@ -2192,60 +2154,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) { @@ -2639,35 +2588,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; @@ -2679,21 +2623,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; @@ -2760,45 +2699,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) { @@ -3306,163 +3216,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, @@ -3481,36 +3256,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 @@ -3530,44 +3296,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. @@ -3589,18 +3340,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; @@ -3619,18 +3370,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 }, @@ -3744,20 +3493,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 @@ -3792,20 +3537,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 : ""; @@ -3862,26 +3603,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"), @@ -3912,16 +3643,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) @@ -3929,22 +3671,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, @@ -4072,31 +3810,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; @@ -4417,31 +4142,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); } } @@ -4455,210 +4178,300 @@ 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) parseInt(cstyle.marginBottom, 10) + parseInt(cstyle.marginTop, 10); 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; +#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 = parseInt(window.getComputedStyle($("nav-bar")).marginTop, 10); + 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 = parseInt(deckStyle.paddingTop, 10); + } + + // 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 += parseInt(paddingBottom, 10); + } + 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 = parseInt(paddingBottom, 10); + // 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; + titlebarContent.style.marginBottom = extraMargin + "px"; + 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 @@ -6910,39 +6723,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; @@ -7150,21 +6956,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,52 +4,64 @@ # # 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 <window id="main-window" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" + xmlns:html="http://www.w3.org/1999/xhtml" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="gBrowserInit.onLoad()" onunload="gBrowserInit.onUnload()" onclose="return WindowIsClosing();" title="&mainWindow.title;@PRE_RELEASE_SUFFIX@" title_normal="&mainWindow.title;@PRE_RELEASE_SUFFIX@" #ifdef XP_MACOSX title_privatebrowsing="&mainWindow.title;@PRE_RELEASE_SUFFIX@&mainWindow.titlemodifiermenuseparator;&mainWindow.titlePrivateBrowsingSuffix;" titledefault="&mainWindow.title;@PRE_RELEASE_SUFFIX@" titlemodifier="" 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"/> @@ -240,20 +252,16 @@ rolluponmousewheel="true" consumeoutsideclicks="false" 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);"> @@ -361,20 +369,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;"/> @@ -387,44 +391,43 @@ <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" @@ -433,330 +436,456 @@ <!-- Menu --> <toolbar type="menubar" id="toolbar-menubar" class="chromeclass-menubar" customizable="true" defaultset="menubar-items" mode="icons" iconsize="small" defaulticonsize="small" lockiconsize="true" #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" + customizableui-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" lockmode="true" + iconsize="small" defaulticonsize="small" lockiconsize="true" + 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" + customizableui-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)" + customizableui-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" + customizableui-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;" + customizableui-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" + defaultset="urlbar-container,search-container,webrtc-status-button,bookmarks-menu-button,downloads-button,home-button,social-share-button,social-toolbar-item" + customizationtarget="nav-bar-customizationtarget" + overflowable="true" + overflowbutton="nav-bar-overflow-button" + overflowtarget="widget-overflow-list" + flex="1" 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-customizationtarget" class="customization-target" flex="501"> + <toolbaritem id="urlbar-container" align="center" flex="400" persist="width" + forwarddisabled="true" title="&locationItem.title;" removable="false" + customizableui-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"> + <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="alert-plugins-notification-icon" class="notification-anchor-icon" role="button"/> + <image id="blocked-plugins-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="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="alert-plugins-notification-icon" class="notification-anchor-icon" role="button"/> - <image id="blocked-plugins-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="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" + customizableui-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;" + customizableui-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);" + customizableui-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" + customizableui-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);" + customizableui-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;" + customizableui-areatype="toolbar" + command="Social:SharePage"/> + + <toolbaritem id="social-toolbar-item" + class="chromeclass-toolbar-additional" + removable="false" + title="&socialToolbar.title;" + hidden="true" + overflows="false" + customizableui-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 chevron 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();"/> @@ -772,28 +901,33 @@ mode="icons" iconsize="small" defaulticonsize="small" lockiconsize="true" 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;" + customizableui-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" @@ -811,250 +945,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" disablehistory="true" - 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" disablehistory="true" + 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> @@ -1101,66 +1097,43 @@ 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"> + <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> + #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. -->
--- 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"); }); } @@ -1485,18 +1484,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")); @@ -1890,16 +1888,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 @@ -3225,18 +3228,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" @@ -3323,16 +3329,17 @@ 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> <method name="_setPositionalAttributes"> <body><![CDATA[ let visibleTabs = this.tabbrowser.visibleTabs; if (!visibleTabs.length) return; @@ -3353,16 +3360,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) { @@ -3431,20 +3444,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; @@ -3786,26 +3795,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; @@ -4089,18 +4098,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 @@ -4521,17 +4529,17 @@ * with the tabbar as its event target (and explicit/originalTarget), * which treats that as a mouse gesture for opening a new tab. * In this context, we're manually blocking the dblclick event * (see dblclick handler). */ var clickedOnce = false; function enableDblClick(event) { var target = event.originalTarget; - if (target.className == 'tab-close-button') + if (target.classList.contains("tab-close-button")) target._ignoredClick = true; if (!clickedOnce) { clickedOnce = true; return; } tabContainer._blockDblClick = false; tabContainer.removeEventListener("click", enableDblClick, true); } @@ -4577,17 +4585,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"; @@ -4598,65 +4606,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 @@ -81,19 +80,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] @@ -154,20 +150,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] @@ -202,19 +195,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] @@ -275,10 +266,11 @@ 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]
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
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/browser_windowopen_reflows.js @@ -0,0 +1,111 @@ +/* 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|", +]; + +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/> @@ -2016,17 +2016,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" 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,154 @@ +<!-- 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" + consumeoutsideclicks="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> + + <vbox id="PanelUI-mainView-spring"/> + + <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 +#ifdef XP_MACOSX + label="&quitApplicationCmdMac.label;" +#else + label="&quitApplicationCmd.label;" +#endif +#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"/> + <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-list"> + </vbox> +</panel>
new file mode 100644 --- /dev/null +++ b/browser/components/customizableui/content/panelUI.js @@ -0,0 +1,405 @@ +/* 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, "Promise", + "resource://gre/modules/Promise.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. 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 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.ensureReady().then(function() { + 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"); + }.bind(this)); + } + }, + + /** + * If the menu panel is being shown, hide it. + */ + hide: function() { + 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) { + return 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 sample the system scrollbar width, + // and then use that to calculate a new width for the scroller. + this._scrollWidth = (yield this._sampleScrollbarWidth()) + "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); + }, + + /** + * 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); + }, + + _sampleScrollbarWidth: function() { + let deferred = Promise.defer(); + let hwin = Services.appShell.hiddenDOMWindow; + let hdoc = hwin.document.documentElement; + let iframe = hwin.document.createElementNS("http://www.w3.org/1999/xhtml", + "html:iframe"); + iframe.setAttribute("srcdoc", '<body style="overflow-y: scroll"></body>'); + hdoc.appendChild(iframe); + + let cwindow = iframe.contentWindow; + let utils = cwindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + + cwindow.addEventListener("load", function onLoad(aEvent) { + cwindow.removeEventListener("load", onLoad); + let sbWidth = {}; + try { + utils.getScrollbarSize(true, sbWidth, {}); + } catch(e) { + Components.utils.reportError("Could not sample scrollbar size: " + e + + " -- " + e.stack); + sbWidth.value = 0; + } + deferred.resolve(sbWidth.value); + iframe.remove(); + }); + + return deferred.promise; + } +};
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,542 @@ +<?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="_initialChildCount">0</field> + + <property name="accessibleType" readonly="true"> + <getter> + return Components.interfaces.nsIAccessibleProvider.XULToolbar; + </getter> + </property> + + <constructor><![CDATA[ + // Stash the initial child count so that later, after overlays are + // loaded, we can report to CustomizableUI's registerToolbar function + // whether or not we've been overlayed by comparing child counts. + this._initialChildCount = this.childElementCount; + 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; + } + } + } + + // If the child count does not match the child count that we recorded + // when the constructor fired, then we were overlayed. + let isOverlayed = this._initialChildCount != this.childElementCount; + CustomizableUI.registerToolbar(this, isOverlayed); + ]]></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> + <field name="_initialChildCount">0</field> + + <constructor><![CDATA[ + // Reading these immediately so nobody messes with them anymore: + this._delegatingToolbar = this.getAttribute("toolbar-delegate"); + // Stash the initial child count so that later, after overlays are + // loaded, we can report to CustomizableUI's registerToolbar function + // whether or not we've been overlayed by comparing child counts. + this._initialChildCount = this.childElementCount; + // 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); + } + } + } + + // If the child count does not match the child count that we recorded + // when the constructor fired, then we were overlayed. + let isOverlayed = this._initialChildCount != this.childElementCount; + CustomizableUI.registerToolbar(this, isOverlayed); + 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,2489 @@ +/* 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.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); + }, + + registerToolbar: function(aToolbar, aIsOverlayed) { + 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); + } + + // If the number of children of this toolbar exceeds the length of the + // placements, then an add-on probably overlayed the toolbar, so we mark + // it dirty. + if (aIsOverlayed) { + 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.notifyListeners("onWidgetBeforeDOMChange", node, currentNode, container); + this.insertWidgetBefore(node, currentNode, container, aArea); + this._addParentFlex(node); + this.notifyListeners("onWidgetAfterDOMChange", node, currentNode, container); + 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) { + 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) { + let areaNodes = gBuildAreas.get(aArea); + if (!areaNodes) { + return; + } + + let placements = gPlacements.get(aArea); + if (!placements) { + ERROR("Could not find any placements for " + aArea + + " when adding a widget."); + return; + } + + let area = gAreas.get(aArea); + let showInPrivateBrowsing = gPalette.has(aWidgetId) + ? gPalette.get(aWidgetId).showInPrivateBrowsing + : true; + 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) { + let window = areaNode.ownerDocument.defaultView; + if (!showInPrivateBrowsing && PrivateBrowsingUtils.isWindowPrivate(window)) { + continue; + } + + let container = areaNode.customizationTarget; + let [provider, widgetNode] = this.getWidgetNode(aWidgetId, window); + + + this.ensureButtonContextMenu(widgetNode, aArea == CustomizableUI.AREA_PANEL); + if (widgetNode.localName == "toolbarbutton" && aArea == CustomizableUI.AREA_PANEL) { + widgetNode.setAttribute("tabindex", "0"); + if (!widgetNode.hasAttribute("type")) { + widgetNode.setAttribute("type", "wrap"); + } + } + + let nextNode = nextNodeId ? container.querySelector(idToSelector(nextNodeId)) + : null; + + this.notifyListeners("onWidgetBeforeDOMChange", widgetNode, nextNode, container); + this.insertWidgetBefore(widgetNode, nextNode, container, aArea); + this._addParentFlex(widgetNode); + this.notifyListeners("onWidgetAfterDOMChange", widgetNode, nextNode, container); + + if (area.get("type") == CustomizableUI.TYPE_TOOLBAR) { + areaNode.setAttribute("currentset", areaNode.currentSet); + } + + } + }, + + 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); + this._removeParentFlex(widgetNode); + + 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"); + } + //XXXgijs: this won't work when removing widgets from the panel (fix in bug 902100) + areaNode.toolbox.palette.appendChild(widgetNode); + } + this.notifyListeners("onWidgetAfterDOMChange", widgetNode, null, container); + + 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) { + 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 area = gAreas.get(aArea); + let showInPrivateBrowsing = gPalette.has(aWidgetId) + ? gPalette.get(aWidgetId).showInPrivateBrowsing + : true; + + let nextNodeId = placements[aNewPosition + 1]; + + for (let areaNode of areaNodes) { + let window = areaNode.ownerDocument.defaultView; + if (!showInPrivateBrowsing && + PrivateBrowsingUtils.isWindowPrivate(window)) { + continue; + } + + let container = areaNode.customizationTarget; + let [provider, widgetNode] = this.getWidgetNode(aWidgetId, window); + if (!widgetNode) { + ERROR("Widget not found, unable to move"); + continue; + } + + let nextNode = nextNodeId ? container.querySelector(idToSelector(nextNodeId)) + : null; + + this.notifyListeners("onWidgetBeforeDOMChange", widgetNode, nextNode, container); + this.insertWidgetBefore(widgetNode, nextNode, container, aArea); + this.notifyListeners("onWidgetAfterDOMChange", widgetNode, nextNode, container); + + if (area.get("type") == CustomizableUI.TYPE_TOOLBAR) { + areaNode.setAttribute("currentset", areaNode.currentSet); + } + + } + }, + + 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("customizableui-areatype", props.get("type") || ""); + let anchor = props.get("anchor"); + if (anchor) { + aNode.setAttribute("customizableui-anchorid", anchor); + } + }, + + removeLocationAttributes: function(aNode) { + aNode.removeAttribute("customizableui-areatype"); + aNode.removeAttribute("customizableui-anchorid"); + }, + + insertWidgetBefore: function(aNode, aNextNode, aContainer, aArea) { + this.setLocationAttributes(aNode, aArea); + aContainer.insertBefore(aNode, aNextNode); + }, + + 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) { + // Due to timers resolution Date.now() can be the same for + // elements created in small timeframes. So ids are + // differentiated through a unique count suffix. + return kSpecialWidgetPfx + aId + Date.now() + (++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 && 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")) { + // 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")); + node.setAttribute("tooltiptext", this.getLocalizedProperty(aWidget, "tooltiptext")); + //XXXunf Need to hook this up to a <key> element or something. + let shortcut = this.getLocalizedProperty(aWidget, "shortcut", null, "none"); + if (shortcut != "none") { + node.setAttribute("acceltext", shortcut); + } + 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(); + } + }, + + maybeAutoHidePanel: function(aEvent) { + if (aEvent.type == "keypress") { + if (aEvent.keyCode != aEvent.DOM_VK_ENTER && + aEvent.keyCode != aEvent.DOM_VK_RETURN) { + return; + } + // If the user hit enter/return, we don't check preventDefault - it makes sense + // that this was prevented, but we probably still want to close the panel. + // If consumers don't want this to happen, they should specify noautoclose. + + } else if (aEvent.type != "command") { // mouse events: + if (aEvent.defaultPrevented || aEvent.button != 0) { + return; + } + let isInteractive = this._isOnInteractiveElement(aEvent); + LOG("maybeAutoHidePanel: interactive ? " + isInteractive); + if (isInteractive) { + return; + } + } + + if (aEvent.target.getAttribute("noautoclose") == "true" || + aEvent.target.getAttribute("widget-type") == "view") { + return; + } + + // If we get here, we can actually hide the popup: + this.hidePanelForNode(aEvent.target); + }, + + getUnusedWidgets: function(aWindowPalette) { + // We use a Set because there can be overlap between the widgets in + // gPalette and the items in the palette, especially after the first + // customization, since programmatically generated widgets will remain + // in the toolbox palette. + let widgets = new Set(); + + // It's possible that some widgets have been defined programmatically and + // have not been overlayed into the palette. We can find those inside + // gPalette. + for (let [id, widget] of gPalette) { + if (!widget.currentArea) { + widgets.add(id); + } + } + + LOG("Iterating the actual nodes of the window palette"); + for (let node of aWindowPalette.children) { + LOG("In palette children: " + node.id); + if (node.id && !this.getPlacementOfWidget(node.id)) { + widgets.add(node.id); + } + } + + return [...widgets]; + }, + + getPlacementOfWidget: function(aWidgetId, aOnlyRegistered) { + if (aOnlyRegistered && !this.widgetExists(aWidgetId)) { + return null; + } + + for (let [area, placements] of gPlacements) { + let index = placements.indexOf(aWidgetId); + if (index != -1) { + return { area: area, position: index }; + } + } + + return null; + }, + + widgetExists: function(aWidgetId) { + if (gPalette.has(aWidgetId) || this.isSpecialWidget(aWidgetId)) { + return true; + } + + // Destroyed API widgets are in gSeenWidgets, but not in gPalette: + if (gSeenWidgets.has(aWidgetId)) { + return false; + } + + // We're assuming XUL widgets always exist, as it's much harder to check, + // and checking would be much more error prone. + return true; + }, + + addWidgetToArea: function(aWidgetId, aArea, aPosition, aInitialAdd) { + if (!gAreas.has(aArea)) { + throw new Error("Unknown customization area: " + aArea); + } + + // If this is a lazy area that hasn't been restored yet, we can't yet modify + // it - would would at least like to add to it. So we keep track of it in + // gFuturePlacements, and use that to add it when restoring the area. We + // throw away aPosition though, as that can only be bogus if the area hasn't + // yet been restorted (caller can't possibly know where its putting the + // widget in relation to other widgets). + if (this.isAreaLazy(aArea)) { + gFuturePlacements.get(aArea).add(aWidgetId); + return; + } + + if (this.isSpecialWidget(aWidgetId)) { + aWidgetId = this.ensureSpecialWidgetId(aWidgetId); + } + + let oldPlacement = this.getPlacementOfWidget(aWidgetId); + if (oldPlacement && oldPlacement.area == aArea) { + this.moveWidgetWithinArea(aWidgetId, aPosition); + return; + } + + // Do nothing if the widget is not allowed to move to the target area. + if (!this.canWidgetMoveToArea(aWidgetId, aArea)) { + return; + } + + if (oldPlacement) { + this.removeWidgetFromArea(aWidgetId); + } + + if (!gPlacements.has(aArea)) { + gPlacements.set(aArea, [aWidgetId]); + aPosition = 0; + } else { + let placements = gPlacements.get(aArea); + if (typeof aPosition != "number") { + aPosition = placements.length; + } + if (aPosition < 0) { + aPosition = 0; + } + placements.splice(aPosition, 0, aWidgetId); + } + + let widget = gPalette.get(aWidgetId); + if (widget) { + widget.currentArea = aArea; + widget.currentPosition = aPosition; + } + + // We initially set placements with addWidgetToArea, so in that case + // we don't consider the area "dirtied". + if (!aInitialAdd) { + gDirtyAreaCache.add(aArea); + } + + gDirty = true; + this.saveState(); + + this.notifyListeners("onWidgetAdded", aWidgetId, aArea, aPosition); + }, + + removeWidgetFromArea: function(aWidgetId) {