diff --git a/accessible/tests/mochitest/events/test_focus_general.html b/accessible/tests/mochitest/events/test_focus_general.html index 55f71d5f7b55..e1a09c614956 100644 --- a/accessible/tests/mochitest/events/test_focus_general.html +++ b/accessible/tests/mochitest/events/test_focus_general.html @@ -96,7 +96,7 @@ 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. diff --git a/accessible/tests/mochitest/events/test_menu.xul b/accessible/tests/mochitest/events/test_menu.xul index a286964a1fbb..bae36fb71239 100644 --- a/accessible/tests/mochitest/events/test_menu.xul +++ b/accessible/tests/mochitest/events/test_menu.xul @@ -156,7 +156,7 @@ // 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()); diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index acfd3b339284..4aad82314a93 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -444,11 +444,10 @@ 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 -pref("browser.tabs.drawInTitlebar", true); -#else +#ifdef UNIX_BUT_NOT_MAC pref("browser.tabs.drawInTitlebar", false); +#else +pref("browser.tabs.drawInTitlebar", true); #endif // Where to show tab close buttons: @@ -1324,3 +1323,6 @@ pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%G // Necko IPC security checks only needed for app isolation for cookies/cache/etc: // currently irrelevant for desktop e10s pref("network.disable.ipc.security", true); + +// CustomizableUI debug logging. +pref("browser.uiCustomization.debug", false); diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js index b638f31f9ea2..fe40e408a2a7 100644 --- a/browser/base/content/browser-addons.js +++ b/browser/base/content/browser-addons.js @@ -178,50 +178,6 @@ const gXPInstallObserver = { } }; -/* - * When addons are installed/uninstalled, check and see if the number of items - * on the add-on bar changed: - * - If an add-on was installed, incrementing the count, show the bar. - * - If an add-on was uninstalled, and no more items are left, hide the bar. - */ -let AddonsMgrListener = { - get addonBar() document.getElementById("addon-bar"), - get statusBar() document.getElementById("status-bar"), - getAddonBarItemCount: function() { - // Take into account the contents of the status bar shim for the count. - var itemCount = this.statusBar.childNodes.length; - - var defaultOrNoninteractive = this.addonBar.getAttribute("defaultset") - .split(",") - .concat(["separator", "spacer", "spring"]); - for (let item of this.addonBar.currentSet.split(",")) { - if (defaultOrNoninteractive.indexOf(item) == -1) - itemCount++; - } - - return itemCount; - }, - onInstalling: function(aAddon) { - this.lastAddonBarCount = this.getAddonBarItemCount(); - }, - onInstalled: function(aAddon) { - if (this.getAddonBarItemCount() > this.lastAddonBarCount) - setToolbarVisibility(this.addonBar, true); - }, - onUninstalling: function(aAddon) { - this.lastAddonBarCount = this.getAddonBarItemCount(); - }, - onUninstalled: function(aAddon) { - if (this.getAddonBarItemCount() == 0) - setToolbarVisibility(this.addonBar, false); - }, - onEnabling: function(aAddon) this.onInstalling(), - onEnabled: function(aAddon) this.onInstalled(), - onDisabling: function(aAddon) this.onUninstalling(), - onDisabled: function(aAddon) this.onUninstalled(), -}; - - var LightWeightThemeWebInstaller = { handleEvent: function (event) { switch (event.type) { @@ -415,3 +371,60 @@ var LightWeightThemeWebInstaller = { 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 + ")"); + }, +}; diff --git a/browser/base/content/browser-appmenu.inc b/browser/base/content/browser-appmenu.inc deleted file mode 100644 index 0cd58f2ecf49..000000000000 --- 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/. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#define ID_PREFIX appmenu_developer_ -#define OMIT_ACCESSKEYS -#include browser-charsetmenu.inc -#undef ID_PREFIX -#undef OMIT_ACCESSKEYS - - - - -#define ID_PREFIX appmenu_ -#define OMIT_ACCESSKEYS -#include browser-charsetmenu.inc -#undef ID_PREFIX -#undef OMIT_ACCESSKEYS - -#ifdef MOZ_SERVICES_SYNC - - - -#endif - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#ifdef MOZ_SERVICES_SYNC - -#endif - - - - - - - - - - - - - - - - - - - - - - - - - - - -#ifdef MOZ_SERVICES_HEALTHREPORT - -#endif - - - - - - - - - - - diff --git a/browser/base/content/browser-customization.js b/browser/base/content/browser-customization.js new file mode 100644 index 000000000000..369f6cbd22cf --- /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(); + } +} diff --git a/browser/base/content/browser-feeds.js b/browser/base/content/browser-feeds.js index 7bba39540824..2bdb33cd09eb 100644 --- a/browser/base/content/browser-feeds.js +++ b/browser/base/content/browser-feeds.js @@ -8,66 +8,53 @@ * 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; }, @@ -76,7 +63,7 @@ var FeedHandler = { * 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 diff --git a/browser/base/content/browser-fullScreen.js b/browser/base/content/browser-fullScreen.js index a8ffa754ad02..4e416ef0437e 100644 --- a/browser/base/content/browser-fullScreen.js +++ b/browser/base/content/browser-fullScreen.js @@ -17,7 +17,7 @@ var 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); @@ -517,15 +517,6 @@ var FullScreen = { // 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")); @@ -539,18 +530,10 @@ var FullScreen = { 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 { @@ -571,11 +554,9 @@ var FullScreen = { 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); diff --git a/browser/base/content/browser-fullZoom.js b/browser/base/content/browser-fullZoom.js index d92dfcc625e1..55f02c38b9e6 100644 --- a/browser/base/content/browser-fullZoom.js +++ b/browser/base/content/browser-fullZoom.js @@ -397,6 +397,7 @@ var FullZoom = { * @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) @@ -417,6 +418,7 @@ var FullZoom = { * @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); diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc index 856f81b92f1b..1db9222eb731 100644 --- a/browser/base/content/browser-menubar.inc +++ b/browser/base/content/browser-menubar.inc @@ -185,11 +185,6 @@ accesskey="&viewToolbarsMenu.accesskey;"> - element during a drag. @@ -768,7 +766,7 @@ var PlacesMenuDNDHandler = { 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(); }, @@ -781,7 +779,8 @@ var PlacesMenuDNDHandler = { 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. @@ -807,7 +806,7 @@ var PlacesMenuDNDHandler = { popup.removeAttribute("autoopened"); popup.hidePopup(); } - }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT); + }, this._closeDelayMs, Ci.nsITimer.TYPE_ONE_SHOT); }, /** @@ -875,30 +874,52 @@ let PlacesToolbarHelper = { if (!viewElt || viewElt._placesView) 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; } }; @@ -906,31 +927,33 @@ let PlacesToolbarHelper = { //// 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, @@ -939,9 +962,9 @@ let BookmarkingUI = { 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() @@ -974,6 +997,16 @@ let BookmarkingUI = { 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; @@ -990,14 +1023,6 @@ let BookmarkingUI = { 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")); - } }, /** @@ -1010,29 +1035,31 @@ let BookmarkingUI = { 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"); } }, @@ -1040,9 +1067,9 @@ let BookmarkingUI = { // 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() { @@ -1054,7 +1081,6 @@ let BookmarkingUI = { }, customizeDone: function BUI_customizeDone() { - delete this._button; this.onToolbarVisibilityChange(); this._updateToolbarStyle(); }, @@ -1074,7 +1100,7 @@ let BookmarkingUI = { }, 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; } @@ -1123,17 +1149,17 @@ let BookmarkingUI = { }, _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); } }, @@ -1141,15 +1167,71 @@ let BookmarkingUI = { 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) { @@ -1160,6 +1242,10 @@ let BookmarkingUI = { }, 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) { @@ -1170,6 +1256,10 @@ let BookmarkingUI = { 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 diff --git a/browser/base/content/browser-sets.inc b/browser/base/content/browser-sets.inc index 41af8c405d08..d969516d768e 100644 --- a/browser/base/content/browser-sets.inc +++ b/browser/base/content/browser-sets.inc @@ -32,7 +32,6 @@ - @@ -109,13 +108,13 @@ oncommand="OpenBrowserWindow({private: true});"/> - + + + + + + + + -