1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/browser-places.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1668 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +//////////////////////////////////////////////////////////////////////////////// 1.9 +//// StarUI 1.10 + 1.11 +var StarUI = { 1.12 + _itemId: -1, 1.13 + uri: null, 1.14 + _batching: false, 1.15 + 1.16 + _element: function(aID) { 1.17 + return document.getElementById(aID); 1.18 + }, 1.19 + 1.20 + // Edit-bookmark panel 1.21 + get panel() { 1.22 + delete this.panel; 1.23 + var element = this._element("editBookmarkPanel"); 1.24 + // initially the panel is hidden 1.25 + // to avoid impacting startup / new window performance 1.26 + element.hidden = false; 1.27 + element.addEventListener("popuphidden", this, false); 1.28 + element.addEventListener("keypress", this, false); 1.29 + return this.panel = element; 1.30 + }, 1.31 + 1.32 + // Array of command elements to disable when the panel is opened. 1.33 + get _blockedCommands() { 1.34 + delete this._blockedCommands; 1.35 + return this._blockedCommands = 1.36 + ["cmd_close", "cmd_closeWindow"].map(function (id) this._element(id), this); 1.37 + }, 1.38 + 1.39 + _blockCommands: function SU__blockCommands() { 1.40 + this._blockedCommands.forEach(function (elt) { 1.41 + // make sure not to permanently disable this item (see bug 409155) 1.42 + if (elt.hasAttribute("wasDisabled")) 1.43 + return; 1.44 + if (elt.getAttribute("disabled") == "true") { 1.45 + elt.setAttribute("wasDisabled", "true"); 1.46 + } else { 1.47 + elt.setAttribute("wasDisabled", "false"); 1.48 + elt.setAttribute("disabled", "true"); 1.49 + } 1.50 + }); 1.51 + }, 1.52 + 1.53 + _restoreCommandsState: function SU__restoreCommandsState() { 1.54 + this._blockedCommands.forEach(function (elt) { 1.55 + if (elt.getAttribute("wasDisabled") != "true") 1.56 + elt.removeAttribute("disabled"); 1.57 + elt.removeAttribute("wasDisabled"); 1.58 + }); 1.59 + }, 1.60 + 1.61 + // nsIDOMEventListener 1.62 + handleEvent: function SU_handleEvent(aEvent) { 1.63 + switch (aEvent.type) { 1.64 + case "popuphidden": 1.65 + if (aEvent.originalTarget == this.panel) { 1.66 + if (!this._element("editBookmarkPanelContent").hidden) 1.67 + this.quitEditMode(); 1.68 + 1.69 + if (this._anchorToolbarButton) { 1.70 + this._anchorToolbarButton.removeAttribute("open"); 1.71 + this._anchorToolbarButton = null; 1.72 + } 1.73 + this._restoreCommandsState(); 1.74 + this._itemId = -1; 1.75 + if (this._batching) { 1.76 + PlacesUtils.transactionManager.endBatch(false); 1.77 + this._batching = false; 1.78 + } 1.79 + 1.80 + switch (this._actionOnHide) { 1.81 + case "cancel": { 1.82 + PlacesUtils.transactionManager.undoTransaction(); 1.83 + break; 1.84 + } 1.85 + case "remove": { 1.86 + // Remove all bookmarks for the bookmark's url, this also removes 1.87 + // the tags for the url. 1.88 + PlacesUtils.transactionManager.beginBatch(null); 1.89 + let itemIds = PlacesUtils.getBookmarksForURI(this._uriForRemoval); 1.90 + for (let i = 0; i < itemIds.length; i++) { 1.91 + let txn = new PlacesRemoveItemTransaction(itemIds[i]); 1.92 + PlacesUtils.transactionManager.doTransaction(txn); 1.93 + } 1.94 + PlacesUtils.transactionManager.endBatch(false); 1.95 + break; 1.96 + } 1.97 + } 1.98 + this._actionOnHide = ""; 1.99 + } 1.100 + break; 1.101 + case "keypress": 1.102 + if (aEvent.defaultPrevented) { 1.103 + // The event has already been consumed inside of the panel. 1.104 + break; 1.105 + } 1.106 + switch (aEvent.keyCode) { 1.107 + case KeyEvent.DOM_VK_ESCAPE: 1.108 + if (!this._element("editBookmarkPanelContent").hidden) 1.109 + this.cancelButtonOnCommand(); 1.110 + break; 1.111 + case KeyEvent.DOM_VK_RETURN: 1.112 + if (aEvent.target.classList.contains("expander-up") || 1.113 + aEvent.target.classList.contains("expander-down") || 1.114 + aEvent.target.id == "editBMPanel_newFolderButton") { 1.115 + //XXX Why is this necessary? The defaultPrevented check should 1.116 + // be enough. 1.117 + break; 1.118 + } 1.119 + this.panel.hidePopup(); 1.120 + break; 1.121 + } 1.122 + break; 1.123 + } 1.124 + }, 1.125 + 1.126 + _overlayLoaded: false, 1.127 + _overlayLoading: false, 1.128 + showEditBookmarkPopup: 1.129 + function SU_showEditBookmarkPopup(aItemId, aAnchorElement, aPosition) { 1.130 + // Performance: load the overlay the first time the panel is opened 1.131 + // (see bug 392443). 1.132 + if (this._overlayLoading) 1.133 + return; 1.134 + 1.135 + if (this._overlayLoaded) { 1.136 + this._doShowEditBookmarkPanel(aItemId, aAnchorElement, aPosition); 1.137 + return; 1.138 + } 1.139 + 1.140 + this._overlayLoading = true; 1.141 + document.loadOverlay( 1.142 + "chrome://browser/content/places/editBookmarkOverlay.xul", 1.143 + (function (aSubject, aTopic, aData) { 1.144 + // Move the header (star, title, button) into the grid, 1.145 + // so that it aligns nicely with the other items (bug 484022). 1.146 + let header = this._element("editBookmarkPanelHeader"); 1.147 + let rows = this._element("editBookmarkPanelGrid").lastChild; 1.148 + rows.insertBefore(header, rows.firstChild); 1.149 + header.hidden = false; 1.150 + 1.151 + this._overlayLoading = false; 1.152 + this._overlayLoaded = true; 1.153 + this._doShowEditBookmarkPanel(aItemId, aAnchorElement, aPosition); 1.154 + }).bind(this) 1.155 + ); 1.156 + }, 1.157 + 1.158 + _doShowEditBookmarkPanel: 1.159 + function SU__doShowEditBookmarkPanel(aItemId, aAnchorElement, aPosition) { 1.160 + if (this.panel.state != "closed") 1.161 + return; 1.162 + 1.163 + this._blockCommands(); // un-done in the popuphiding handler 1.164 + 1.165 + // Set panel title: 1.166 + // if we are batching, i.e. the bookmark has been added now, 1.167 + // then show Page Bookmarked, else if the bookmark did already exist, 1.168 + // we are about editing it, then use Edit This Bookmark. 1.169 + this._element("editBookmarkPanelTitle").value = 1.170 + this._batching ? 1.171 + gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") : 1.172 + gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"); 1.173 + 1.174 + // No description; show the Done, Cancel; 1.175 + this._element("editBookmarkPanelDescription").textContent = ""; 1.176 + this._element("editBookmarkPanelBottomButtons").hidden = false; 1.177 + this._element("editBookmarkPanelContent").hidden = false; 1.178 + 1.179 + // The remove button is shown only if we're not already batching, i.e. 1.180 + // if the cancel button/ESC does not remove the bookmark. 1.181 + this._element("editBookmarkPanelRemoveButton").hidden = this._batching; 1.182 + 1.183 + // The label of the remove button differs if the URI is bookmarked 1.184 + // multiple times. 1.185 + var bookmarks = PlacesUtils.getBookmarksForURI(gBrowser.currentURI); 1.186 + var forms = gNavigatorBundle.getString("editBookmark.removeBookmarks.label"); 1.187 + var label = PluralForm.get(bookmarks.length, forms).replace("#1", bookmarks.length); 1.188 + this._element("editBookmarkPanelRemoveButton").label = label; 1.189 + 1.190 + // unset the unstarred state, if set 1.191 + this._element("editBookmarkPanelStarIcon").removeAttribute("unstarred"); 1.192 + 1.193 + this._itemId = aItemId !== undefined ? aItemId : this._itemId; 1.194 + this.beginBatch(); 1.195 + 1.196 + if (aAnchorElement) { 1.197 + // Set the open=true attribute if the anchor is a 1.198 + // descendent of a toolbarbutton. 1.199 + let parent = aAnchorElement.parentNode; 1.200 + while (parent) { 1.201 + if (parent.localName == "toolbarbutton") { 1.202 + break; 1.203 + } 1.204 + parent = parent.parentNode; 1.205 + } 1.206 + if (parent) { 1.207 + this._anchorToolbarButton = parent; 1.208 + parent.setAttribute("open", "true"); 1.209 + } 1.210 + } 1.211 + this.panel.openPopup(aAnchorElement, aPosition); 1.212 + 1.213 + gEditItemOverlay.initPanel(this._itemId, 1.214 + { hiddenRows: ["description", "location", 1.215 + "loadInSidebar", "keyword"] }); 1.216 + }, 1.217 + 1.218 + panelShown: 1.219 + function SU_panelShown(aEvent) { 1.220 + if (aEvent.target == this.panel) { 1.221 + if (!this._element("editBookmarkPanelContent").hidden) { 1.222 + let fieldToFocus = "editBMPanel_" + 1.223 + gPrefService.getCharPref("browser.bookmarks.editDialog.firstEditField"); 1.224 + var elt = this._element(fieldToFocus); 1.225 + elt.focus(); 1.226 + elt.select(); 1.227 + } 1.228 + else { 1.229 + // Note this isn't actually used anymore, we should remove this 1.230 + // once we decide not to bring back the page bookmarked notification 1.231 + this.panel.focus(); 1.232 + } 1.233 + } 1.234 + }, 1.235 + 1.236 + quitEditMode: function SU_quitEditMode() { 1.237 + this._element("editBookmarkPanelContent").hidden = true; 1.238 + this._element("editBookmarkPanelBottomButtons").hidden = true; 1.239 + gEditItemOverlay.uninitPanel(true); 1.240 + }, 1.241 + 1.242 + cancelButtonOnCommand: function SU_cancelButtonOnCommand() { 1.243 + this._actionOnHide = "cancel"; 1.244 + this.panel.hidePopup(); 1.245 + }, 1.246 + 1.247 + removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() { 1.248 + this._uriForRemoval = PlacesUtils.bookmarks.getBookmarkURI(this._itemId); 1.249 + this._actionOnHide = "remove"; 1.250 + this.panel.hidePopup(); 1.251 + }, 1.252 + 1.253 + beginBatch: function SU_beginBatch() { 1.254 + if (!this._batching) { 1.255 + PlacesUtils.transactionManager.beginBatch(null); 1.256 + this._batching = true; 1.257 + } 1.258 + } 1.259 +} 1.260 + 1.261 +//////////////////////////////////////////////////////////////////////////////// 1.262 +//// PlacesCommandHook 1.263 + 1.264 +var PlacesCommandHook = { 1.265 + /** 1.266 + * Adds a bookmark to the page loaded in the given browser. 1.267 + * 1.268 + * @param aBrowser 1.269 + * a <browser> element. 1.270 + * @param [optional] aParent 1.271 + * The folder in which to create a new bookmark if the page loaded in 1.272 + * aBrowser isn't bookmarked yet, defaults to the unfiled root. 1.273 + * @param [optional] aShowEditUI 1.274 + * whether or not to show the edit-bookmark UI for the bookmark item 1.275 + */ 1.276 + bookmarkPage: function PCH_bookmarkPage(aBrowser, aParent, aShowEditUI) { 1.277 + var uri = aBrowser.currentURI; 1.278 + var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri); 1.279 + if (itemId == -1) { 1.280 + // Copied over from addBookmarkForBrowser: 1.281 + // Bug 52536: We obtain the URL and title from the nsIWebNavigation 1.282 + // associated with a <browser/> rather than from a DOMWindow. 1.283 + // This is because when a full page plugin is loaded, there is 1.284 + // no DOMWindow (?) but information about the loaded document 1.285 + // may still be obtained from the webNavigation. 1.286 + var webNav = aBrowser.webNavigation; 1.287 + var url = webNav.currentURI; 1.288 + var title; 1.289 + var description; 1.290 + var charset; 1.291 + try { 1.292 + let isErrorPage = /^about:(neterror|certerror|blocked)/ 1.293 + .test(webNav.document.documentURI); 1.294 + title = isErrorPage ? PlacesUtils.history.getPageTitle(url) 1.295 + : webNav.document.title; 1.296 + title = title || url.spec; 1.297 + description = PlacesUIUtils.getDescriptionFromDocument(webNav.document); 1.298 + charset = webNav.document.characterSet; 1.299 + } 1.300 + catch (e) { } 1.301 + 1.302 + if (aShowEditUI) { 1.303 + // If we bookmark the page here (i.e. page was not "starred" already) 1.304 + // but open right into the "edit" state, start batching here, so 1.305 + // "Cancel" in that state removes the bookmark. 1.306 + StarUI.beginBatch(); 1.307 + } 1.308 + 1.309 + var parent = aParent != undefined ? 1.310 + aParent : PlacesUtils.unfiledBookmarksFolderId; 1.311 + var descAnno = { name: PlacesUIUtils.DESCRIPTION_ANNO, value: description }; 1.312 + var txn = new PlacesCreateBookmarkTransaction(uri, parent, 1.313 + PlacesUtils.bookmarks.DEFAULT_INDEX, 1.314 + title, null, [descAnno]); 1.315 + PlacesUtils.transactionManager.doTransaction(txn); 1.316 + itemId = txn.item.id; 1.317 + // Set the character-set 1.318 + if (charset && !PrivateBrowsingUtils.isWindowPrivate(aBrowser.contentWindow)) 1.319 + PlacesUtils.setCharsetForURI(uri, charset); 1.320 + } 1.321 + 1.322 + // Revert the contents of the location bar 1.323 + if (gURLBar) 1.324 + gURLBar.handleRevert(); 1.325 + 1.326 + // If it was not requested to open directly in "edit" mode, we are done. 1.327 + if (!aShowEditUI) 1.328 + return; 1.329 + 1.330 + // Try to dock the panel to: 1.331 + // 1. the bookmarks menu button 1.332 + // 2. the page-proxy-favicon 1.333 + // 3. the content area 1.334 + if (BookmarkingUI.anchor) { 1.335 + StarUI.showEditBookmarkPopup(itemId, BookmarkingUI.anchor, 1.336 + "bottomcenter topright"); 1.337 + return; 1.338 + } 1.339 + 1.340 + let pageProxyFavicon = document.getElementById("page-proxy-favicon"); 1.341 + if (isElementVisible(pageProxyFavicon)) { 1.342 + StarUI.showEditBookmarkPopup(itemId, pageProxyFavicon, 1.343 + "bottomcenter topright"); 1.344 + } else { 1.345 + StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap"); 1.346 + } 1.347 + }, 1.348 + 1.349 + /** 1.350 + * Adds a bookmark to the page loaded in the current tab. 1.351 + */ 1.352 + bookmarkCurrentPage: function PCH_bookmarkCurrentPage(aShowEditUI, aParent) { 1.353 + this.bookmarkPage(gBrowser.selectedBrowser, aParent, aShowEditUI); 1.354 + }, 1.355 + 1.356 + /** 1.357 + * Adds a bookmark to the page targeted by a link. 1.358 + * @param aParent 1.359 + * The folder in which to create a new bookmark if aURL isn't 1.360 + * bookmarked. 1.361 + * @param aURL (string) 1.362 + * the address of the link target 1.363 + * @param aTitle 1.364 + * The link text 1.365 + */ 1.366 + bookmarkLink: function PCH_bookmarkLink(aParent, aURL, aTitle) { 1.367 + var linkURI = makeURI(aURL); 1.368 + var itemId = PlacesUtils.getMostRecentBookmarkForURI(linkURI); 1.369 + if (itemId == -1) { 1.370 + PlacesUIUtils.showBookmarkDialog({ action: "add" 1.371 + , type: "bookmark" 1.372 + , uri: linkURI 1.373 + , title: aTitle 1.374 + , hiddenRows: [ "description" 1.375 + , "location" 1.376 + , "loadInSidebar" 1.377 + , "keyword" ] 1.378 + }, window); 1.379 + } 1.380 + else { 1.381 + PlacesUIUtils.showBookmarkDialog({ action: "edit" 1.382 + , type: "bookmark" 1.383 + , itemId: itemId 1.384 + }, window); 1.385 + } 1.386 + }, 1.387 + 1.388 + /** 1.389 + * List of nsIURI objects characterizing the tabs currently open in the 1.390 + * browser, modulo pinned tabs. The URIs will be in the order in which their 1.391 + * corresponding tabs appeared and duplicates are discarded. 1.392 + */ 1.393 + get uniqueCurrentPages() { 1.394 + let uniquePages = {}; 1.395 + let URIs = []; 1.396 + gBrowser.visibleTabs.forEach(function (tab) { 1.397 + let spec = tab.linkedBrowser.currentURI.spec; 1.398 + if (!tab.pinned && !(spec in uniquePages)) { 1.399 + uniquePages[spec] = null; 1.400 + URIs.push(tab.linkedBrowser.currentURI); 1.401 + } 1.402 + }); 1.403 + return URIs; 1.404 + }, 1.405 + 1.406 + /** 1.407 + * Adds a folder with bookmarks to all of the currently open tabs in this 1.408 + * window. 1.409 + */ 1.410 + bookmarkCurrentPages: function PCH_bookmarkCurrentPages() { 1.411 + let pages = this.uniqueCurrentPages; 1.412 + if (pages.length > 1) { 1.413 + PlacesUIUtils.showBookmarkDialog({ action: "add" 1.414 + , type: "folder" 1.415 + , URIList: pages 1.416 + , hiddenRows: [ "description" ] 1.417 + }, window); 1.418 + } 1.419 + }, 1.420 + 1.421 + /** 1.422 + * Updates disabled state for the "Bookmark All Tabs" command. 1.423 + */ 1.424 + updateBookmarkAllTabsCommand: 1.425 + function PCH_updateBookmarkAllTabsCommand() { 1.426 + // There's nothing to do in non-browser windows. 1.427 + if (window.location.href != getBrowserURL()) 1.428 + return; 1.429 + 1.430 + // Disable "Bookmark All Tabs" if there are less than two 1.431 + // "unique current pages". 1.432 + goSetCommandEnabled("Browser:BookmarkAllTabs", 1.433 + this.uniqueCurrentPages.length >= 2); 1.434 + }, 1.435 + 1.436 + /** 1.437 + * Adds a Live Bookmark to a feed associated with the current page. 1.438 + * @param url 1.439 + * The nsIURI of the page the feed was attached to 1.440 + * @title title 1.441 + * The title of the feed. Optional. 1.442 + * @subtitle subtitle 1.443 + * A short description of the feed. Optional. 1.444 + */ 1.445 + addLiveBookmark: function PCH_addLiveBookmark(url, feedTitle, feedSubtitle) { 1.446 + var feedURI = makeURI(url); 1.447 + 1.448 + var doc = gBrowser.contentDocument; 1.449 + var title = (arguments.length > 1) ? feedTitle : doc.title; 1.450 + 1.451 + var description; 1.452 + if (arguments.length > 2) 1.453 + description = feedSubtitle; 1.454 + else 1.455 + description = PlacesUIUtils.getDescriptionFromDocument(doc); 1.456 + 1.457 + var toolbarIP = new InsertionPoint(PlacesUtils.toolbarFolderId, -1); 1.458 + PlacesUIUtils.showBookmarkDialog({ action: "add" 1.459 + , type: "livemark" 1.460 + , feedURI: feedURI 1.461 + , siteURI: gBrowser.currentURI 1.462 + , title: title 1.463 + , description: description 1.464 + , defaultInsertionPoint: toolbarIP 1.465 + , hiddenRows: [ "feedLocation" 1.466 + , "siteLocation" 1.467 + , "description" ] 1.468 + }, window); 1.469 + }, 1.470 + 1.471 + /** 1.472 + * Opens the Places Organizer. 1.473 + * @param aLeftPaneRoot 1.474 + * The query to select in the organizer window - options 1.475 + * are: History, AllBookmarks, BookmarksMenu, BookmarksToolbar, 1.476 + * UnfiledBookmarks, Tags and Downloads. 1.477 + */ 1.478 + showPlacesOrganizer: function PCH_showPlacesOrganizer(aLeftPaneRoot) { 1.479 + var organizer = Services.wm.getMostRecentWindow("Places:Organizer"); 1.480 + if (!organizer) { 1.481 + // No currently open places window, so open one with the specified mode. 1.482 + openDialog("chrome://browser/content/places/places.xul", 1.483 + "", "chrome,toolbar=yes,dialog=no,resizable", aLeftPaneRoot); 1.484 + } 1.485 + else { 1.486 + organizer.PlacesOrganizer.selectLeftPaneContainerByHierarchy(aLeftPaneRoot); 1.487 + organizer.focus(); 1.488 + } 1.489 + } 1.490 +}; 1.491 + 1.492 +//////////////////////////////////////////////////////////////////////////////// 1.493 +//// HistoryMenu 1.494 + 1.495 +XPCOMUtils.defineLazyModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils", 1.496 + "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm"); 1.497 + 1.498 +// View for the history menu. 1.499 +function HistoryMenu(aPopupShowingEvent) { 1.500 + // Workaround for Bug 610187. The sidebar does not include all the Places 1.501 + // views definitions, and we don't need them there. 1.502 + // Defining the prototype inheritance in the prototype itself would cause 1.503 + // browser.js to halt on "PlacesMenu is not defined" error. 1.504 + this.__proto__.__proto__ = PlacesMenu.prototype; 1.505 + PlacesMenu.call(this, aPopupShowingEvent, 1.506 + "place:sort=4&maxResults=15"); 1.507 +} 1.508 + 1.509 +HistoryMenu.prototype = { 1.510 + toggleRecentlyClosedTabs: function HM_toggleRecentlyClosedTabs() { 1.511 + // enable/disable the Recently Closed Tabs sub menu 1.512 + var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedTabsMenu")[0]; 1.513 + 1.514 + // no restorable tabs, so disable menu 1.515 + if (SessionStore.getClosedTabCount(window) == 0) 1.516 + undoMenu.setAttribute("disabled", true); 1.517 + else 1.518 + undoMenu.removeAttribute("disabled"); 1.519 + }, 1.520 + 1.521 + /** 1.522 + * Populate when the history menu is opened 1.523 + */ 1.524 + populateUndoSubmenu: function PHM_populateUndoSubmenu() { 1.525 + var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedTabsMenu")[0]; 1.526 + var undoPopup = undoMenu.firstChild; 1.527 + 1.528 + // remove existing menu items 1.529 + while (undoPopup.hasChildNodes()) 1.530 + undoPopup.removeChild(undoPopup.firstChild); 1.531 + 1.532 + // no restorable tabs, so make sure menu is disabled, and return 1.533 + if (SessionStore.getClosedTabCount(window) == 0) { 1.534 + undoMenu.setAttribute("disabled", true); 1.535 + return; 1.536 + } 1.537 + 1.538 + // enable menu 1.539 + undoMenu.removeAttribute("disabled"); 1.540 + 1.541 + // populate menu 1.542 + let tabsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getTabsFragment(window, "menuitem"); 1.543 + undoPopup.appendChild(tabsFragment); 1.544 + }, 1.545 + 1.546 + toggleRecentlyClosedWindows: function PHM_toggleRecentlyClosedWindows() { 1.547 + // enable/disable the Recently Closed Windows sub menu 1.548 + var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0]; 1.549 + 1.550 + // no restorable windows, so disable menu 1.551 + if (SessionStore.getClosedWindowCount() == 0) 1.552 + undoMenu.setAttribute("disabled", true); 1.553 + else 1.554 + undoMenu.removeAttribute("disabled"); 1.555 + }, 1.556 + 1.557 + /** 1.558 + * Populate when the history menu is opened 1.559 + */ 1.560 + populateUndoWindowSubmenu: function PHM_populateUndoWindowSubmenu() { 1.561 + let undoMenu = this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0]; 1.562 + let undoPopup = undoMenu.firstChild; 1.563 + let menuLabelString = gNavigatorBundle.getString("menuUndoCloseWindowLabel"); 1.564 + let menuLabelStringSingleTab = 1.565 + gNavigatorBundle.getString("menuUndoCloseWindowSingleTabLabel"); 1.566 + 1.567 + // remove existing menu items 1.568 + while (undoPopup.hasChildNodes()) 1.569 + undoPopup.removeChild(undoPopup.firstChild); 1.570 + 1.571 + // no restorable windows, so make sure menu is disabled, and return 1.572 + if (SessionStore.getClosedWindowCount() == 0) { 1.573 + undoMenu.setAttribute("disabled", true); 1.574 + return; 1.575 + } 1.576 + 1.577 + // enable menu 1.578 + undoMenu.removeAttribute("disabled"); 1.579 + 1.580 + // populate menu 1.581 + let windowsFragment = RecentlyClosedTabsAndWindowsMenuUtils.getWindowsFragment(window, "menuitem"); 1.582 + undoPopup.appendChild(windowsFragment); 1.583 + }, 1.584 + 1.585 + toggleTabsFromOtherComputers: function PHM_toggleTabsFromOtherComputers() { 1.586 + // This is a no-op if MOZ_SERVICES_SYNC isn't defined 1.587 +#ifdef MOZ_SERVICES_SYNC 1.588 + // Enable/disable the Tabs From Other Computers menu. Some of the menus handled 1.589 + // by HistoryMenu do not have this menuitem. 1.590 + let menuitem = this._rootElt.getElementsByClassName("syncTabsMenuItem")[0]; 1.591 + if (!menuitem) 1.592 + return; 1.593 + 1.594 + if (!PlacesUIUtils.shouldShowTabsFromOtherComputersMenuitem()) { 1.595 + menuitem.setAttribute("hidden", true); 1.596 + return; 1.597 + } 1.598 + 1.599 + let enabled = PlacesUIUtils.shouldEnableTabsFromOtherComputersMenuitem(); 1.600 + menuitem.setAttribute("disabled", !enabled); 1.601 + menuitem.setAttribute("hidden", false); 1.602 +#endif 1.603 + }, 1.604 + 1.605 + _onPopupShowing: function HM__onPopupShowing(aEvent) { 1.606 + PlacesMenu.prototype._onPopupShowing.apply(this, arguments); 1.607 + 1.608 + // Don't handle events for submenus. 1.609 + if (aEvent.target != aEvent.currentTarget) 1.610 + return; 1.611 + 1.612 + this.toggleRecentlyClosedTabs(); 1.613 + this.toggleRecentlyClosedWindows(); 1.614 + this.toggleTabsFromOtherComputers(); 1.615 + }, 1.616 + 1.617 + _onCommand: function HM__onCommand(aEvent) { 1.618 + let placesNode = aEvent.target._placesNode; 1.619 + if (placesNode) { 1.620 + if (!PrivateBrowsingUtils.isWindowPrivate(window)) 1.621 + PlacesUIUtils.markPageAsTyped(placesNode.uri); 1.622 + openUILink(placesNode.uri, aEvent, { ignoreAlt: true }); 1.623 + } 1.624 + } 1.625 +}; 1.626 + 1.627 +//////////////////////////////////////////////////////////////////////////////// 1.628 +//// BookmarksEventHandler 1.629 + 1.630 +/** 1.631 + * Functions for handling events in the Bookmarks Toolbar and menu. 1.632 + */ 1.633 +var BookmarksEventHandler = { 1.634 + /** 1.635 + * Handler for click event for an item in the bookmarks toolbar or menu. 1.636 + * Menus and submenus from the folder buttons bubble up to this handler. 1.637 + * Left-click is handled in the onCommand function. 1.638 + * When items are middle-clicked (or clicked with modifier), open in tabs. 1.639 + * If the click came through a menu, close the menu. 1.640 + * @param aEvent 1.641 + * DOMEvent for the click 1.642 + * @param aView 1.643 + * The places view which aEvent should be associated with. 1.644 + */ 1.645 + onClick: function BEH_onClick(aEvent, aView) { 1.646 + // Only handle middle-click or left-click with modifiers. 1.647 +#ifdef XP_MACOSX 1.648 + var modifKey = aEvent.metaKey || aEvent.shiftKey; 1.649 +#else 1.650 + var modifKey = aEvent.ctrlKey || aEvent.shiftKey; 1.651 +#endif 1.652 + if (aEvent.button == 2 || (aEvent.button == 0 && !modifKey)) 1.653 + return; 1.654 + 1.655 + var target = aEvent.originalTarget; 1.656 + // If this event bubbled up from a menu or menuitem, close the menus. 1.657 + // Do this before opening tabs, to avoid hiding the open tabs confirm-dialog. 1.658 + if (target.localName == "menu" || target.localName == "menuitem") { 1.659 + for (node = target.parentNode; node; node = node.parentNode) { 1.660 + if (node.localName == "menupopup") 1.661 + node.hidePopup(); 1.662 + else if (node.localName != "menu" && 1.663 + node.localName != "splitmenu" && 1.664 + node.localName != "hbox" && 1.665 + node.localName != "vbox" ) 1.666 + break; 1.667 + } 1.668 + } 1.669 + 1.670 + if (target._placesNode && PlacesUtils.nodeIsContainer(target._placesNode)) { 1.671 + // Don't open the root folder in tabs when the empty area on the toolbar 1.672 + // is middle-clicked or when a non-bookmark item except for Open in Tabs) 1.673 + // in a bookmarks menupopup is middle-clicked. 1.674 + if (target.localName == "menu" || target.localName == "toolbarbutton") 1.675 + PlacesUIUtils.openContainerNodeInTabs(target._placesNode, aEvent, aView); 1.676 + } 1.677 + else if (aEvent.button == 1) { 1.678 + // left-clicks with modifier are already served by onCommand 1.679 + this.onCommand(aEvent, aView); 1.680 + } 1.681 + }, 1.682 + 1.683 + /** 1.684 + * Handler for command event for an item in the bookmarks toolbar. 1.685 + * Menus and submenus from the folder buttons bubble up to this handler. 1.686 + * Opens the item. 1.687 + * @param aEvent 1.688 + * DOMEvent for the command 1.689 + * @param aView 1.690 + * The places view which aEvent should be associated with. 1.691 + */ 1.692 + onCommand: function BEH_onCommand(aEvent, aView) { 1.693 + var target = aEvent.originalTarget; 1.694 + if (target._placesNode) 1.695 + PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent, aView); 1.696 + }, 1.697 + 1.698 + fillInBHTooltip: function BEH_fillInBHTooltip(aDocument, aEvent) { 1.699 + var node; 1.700 + var cropped = false; 1.701 + var targetURI; 1.702 + 1.703 + if (aDocument.tooltipNode.localName == "treechildren") { 1.704 + var tree = aDocument.tooltipNode.parentNode; 1.705 + var row = {}, column = {}; 1.706 + var tbo = tree.treeBoxObject; 1.707 + tbo.getCellAt(aEvent.clientX, aEvent.clientY, row, column, {}); 1.708 + if (row.value == -1) 1.709 + return false; 1.710 + node = tree.view.nodeForTreeIndex(row.value); 1.711 + cropped = tbo.isCellCropped(row.value, column.value); 1.712 + } 1.713 + else { 1.714 + // Check whether the tooltipNode is a Places node. 1.715 + // In such a case use it, otherwise check for targetURI attribute. 1.716 + var tooltipNode = aDocument.tooltipNode; 1.717 + if (tooltipNode._placesNode) 1.718 + node = tooltipNode._placesNode; 1.719 + else { 1.720 + // This is a static non-Places node. 1.721 + targetURI = tooltipNode.getAttribute("targetURI"); 1.722 + } 1.723 + } 1.724 + 1.725 + if (!node && !targetURI) 1.726 + return false; 1.727 + 1.728 + // Show node.label as tooltip's title for non-Places nodes. 1.729 + var title = node ? node.title : tooltipNode.label; 1.730 + 1.731 + // Show URL only for Places URI-nodes or nodes with a targetURI attribute. 1.732 + var url; 1.733 + if (targetURI || PlacesUtils.nodeIsURI(node)) 1.734 + url = targetURI || node.uri; 1.735 + 1.736 + // Show tooltip for containers only if their title is cropped. 1.737 + if (!cropped && !url) 1.738 + return false; 1.739 + 1.740 + var tooltipTitle = aDocument.getElementById("bhtTitleText"); 1.741 + tooltipTitle.hidden = (!title || (title == url)); 1.742 + if (!tooltipTitle.hidden) 1.743 + tooltipTitle.textContent = title; 1.744 + 1.745 + var tooltipUrl = aDocument.getElementById("bhtUrlText"); 1.746 + tooltipUrl.hidden = !url; 1.747 + if (!tooltipUrl.hidden) 1.748 + tooltipUrl.value = url; 1.749 + 1.750 + // Show tooltip. 1.751 + return true; 1.752 + } 1.753 +}; 1.754 + 1.755 +//////////////////////////////////////////////////////////////////////////////// 1.756 +//// PlacesMenuDNDHandler 1.757 + 1.758 +// Handles special drag and drop functionality for Places menus that are not 1.759 +// part of a Places view (e.g. the bookmarks menu in the menubar). 1.760 +var PlacesMenuDNDHandler = { 1.761 + _springLoadDelayMs: 350, 1.762 + _closeDelayMs: 500, 1.763 + _loadTimer: null, 1.764 + _closeTimer: null, 1.765 + _closingTimerNode: null, 1.766 + 1.767 + /** 1.768 + * Called when the user enters the <menu> element during a drag. 1.769 + * @param event 1.770 + * The DragEnter event that spawned the opening. 1.771 + */ 1.772 + onDragEnter: function PMDH_onDragEnter(event) { 1.773 + // Opening menus in a Places popup is handled by the view itself. 1.774 + if (!this._isStaticContainer(event.target)) 1.775 + return; 1.776 + 1.777 + // If we re-enter the same menu or anchor before the close timer runs out, 1.778 + // we should ensure that we do not close: 1.779 + if (this._closeTimer && this._closingTimerNode === event.currentTarget) { 1.780 + this._closeTimer.cancel(); 1.781 + this._closingTimerNode = null; 1.782 + this._closeTimer = null; 1.783 + } 1.784 + 1.785 + PlacesControllerDragHelper.currentDropTarget = event.target; 1.786 + let popup = event.target.lastChild; 1.787 + if (this._loadTimer || popup.state === "showing" || popup.state === "open") 1.788 + return; 1.789 + 1.790 + this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.791 + this._loadTimer.initWithCallback(() => { 1.792 + this._loadTimer = null; 1.793 + popup.setAttribute("autoopened", "true"); 1.794 + popup.showPopup(popup); 1.795 + }, this._springLoadDelayMs, Ci.nsITimer.TYPE_ONE_SHOT); 1.796 + event.preventDefault(); 1.797 + event.stopPropagation(); 1.798 + }, 1.799 + 1.800 + /** 1.801 + * Handles dragleave on the <menu> element. 1.802 + */ 1.803 + onDragLeave: function PMDH_onDragLeave(event) { 1.804 + // Handle menu-button separate targets. 1.805 + if (event.relatedTarget === event.currentTarget || 1.806 + (event.relatedTarget && 1.807 + event.relatedTarget.parentNode === event.currentTarget)) 1.808 + return; 1.809 + 1.810 + // Closing menus in a Places popup is handled by the view itself. 1.811 + if (!this._isStaticContainer(event.target)) 1.812 + return; 1.813 + 1.814 + PlacesControllerDragHelper.currentDropTarget = null; 1.815 + let popup = event.target.lastChild; 1.816 + 1.817 + if (this._loadTimer) { 1.818 + this._loadTimer.cancel(); 1.819 + this._loadTimer = null; 1.820 + } 1.821 + this._closeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.822 + this._closingTimerNode = event.currentTarget; 1.823 + this._closeTimer.initWithCallback(function() { 1.824 + this._closeTimer = null; 1.825 + this._closingTimerNode = null; 1.826 + let node = PlacesControllerDragHelper.currentDropTarget; 1.827 + let inHierarchy = false; 1.828 + while (node && !inHierarchy) { 1.829 + inHierarchy = node == event.target; 1.830 + node = node.parentNode; 1.831 + } 1.832 + if (!inHierarchy && popup && popup.hasAttribute("autoopened")) { 1.833 + popup.removeAttribute("autoopened"); 1.834 + popup.hidePopup(); 1.835 + } 1.836 + }, this._closeDelayMs, Ci.nsITimer.TYPE_ONE_SHOT); 1.837 + }, 1.838 + 1.839 + /** 1.840 + * Determines if a XUL element represents a static container. 1.841 + * @returns true if the element is a container element (menu or 1.842 + *` menu-toolbarbutton), false otherwise. 1.843 + */ 1.844 + _isStaticContainer: function PMDH__isContainer(node) { 1.845 + let isMenu = node.localName == "menu" || 1.846 + (node.localName == "toolbarbutton" && 1.847 + (node.getAttribute("type") == "menu" || 1.848 + node.getAttribute("type") == "menu-button")); 1.849 + let isStatic = !("_placesNode" in node) && node.lastChild && 1.850 + node.lastChild.hasAttribute("placespopup") && 1.851 + !node.parentNode.hasAttribute("placespopup"); 1.852 + return isMenu && isStatic; 1.853 + }, 1.854 + 1.855 + /** 1.856 + * Called when the user drags over the <menu> element. 1.857 + * @param event 1.858 + * The DragOver event. 1.859 + */ 1.860 + onDragOver: function PMDH_onDragOver(event) { 1.861 + let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, 1.862 + PlacesUtils.bookmarks.DEFAULT_INDEX, 1.863 + Ci.nsITreeView.DROP_ON); 1.864 + if (ip && PlacesControllerDragHelper.canDrop(ip, event.dataTransfer)) 1.865 + event.preventDefault(); 1.866 + 1.867 + event.stopPropagation(); 1.868 + }, 1.869 + 1.870 + /** 1.871 + * Called when the user drops on the <menu> element. 1.872 + * @param event 1.873 + * The Drop event. 1.874 + */ 1.875 + onDrop: function PMDH_onDrop(event) { 1.876 + // Put the item at the end of bookmark menu. 1.877 + let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, 1.878 + PlacesUtils.bookmarks.DEFAULT_INDEX, 1.879 + Ci.nsITreeView.DROP_ON); 1.880 + PlacesControllerDragHelper.onDrop(ip, event.dataTransfer); 1.881 + PlacesControllerDragHelper.currentDropTarget = null; 1.882 + event.stopPropagation(); 1.883 + } 1.884 +}; 1.885 + 1.886 +//////////////////////////////////////////////////////////////////////////////// 1.887 +//// PlacesToolbarHelper 1.888 + 1.889 +/** 1.890 + * This object handles the initialization and uninitialization of the bookmarks 1.891 + * toolbar. 1.892 + */ 1.893 +let PlacesToolbarHelper = { 1.894 + _place: "place:folder=TOOLBAR", 1.895 + 1.896 + get _viewElt() { 1.897 + return document.getElementById("PlacesToolbar"); 1.898 + }, 1.899 + 1.900 + get _placeholder() { 1.901 + return document.getElementById("bookmarks-toolbar-placeholder"); 1.902 + }, 1.903 + 1.904 + init: function PTH_init(forceToolbarOverflowCheck) { 1.905 + let viewElt = this._viewElt; 1.906 + if (!viewElt || viewElt._placesView) 1.907 + return; 1.908 + 1.909 + // CustomizableUI.addListener is idempotent, so we can safely 1.910 + // call this multiple times. 1.911 + CustomizableUI.addListener(this); 1.912 + 1.913 + // If the bookmarks toolbar item is: 1.914 + // - not in a toolbar, or; 1.915 + // - the toolbar is collapsed, or; 1.916 + // - the toolbar is hidden some other way: 1.917 + // don't initialize. Also, there is no need to initialize the toolbar if 1.918 + // customizing, because that will happen when the customization is done. 1.919 + let toolbar = this._getParentToolbar(viewElt); 1.920 + if (!toolbar || toolbar.collapsed || this._isCustomizing || 1.921 + getComputedStyle(toolbar, "").display == "none") 1.922 + return; 1.923 + 1.924 + new PlacesToolbar(this._place); 1.925 + if (forceToolbarOverflowCheck) { 1.926 + viewElt._placesView.updateOverflowStatus(); 1.927 + } 1.928 + this._setupPlaceholder(); 1.929 + }, 1.930 + 1.931 + uninit: function PTH_uninit() { 1.932 + CustomizableUI.removeListener(this); 1.933 + }, 1.934 + 1.935 + customizeStart: function PTH_customizeStart() { 1.936 + try { 1.937 + let viewElt = this._viewElt; 1.938 + if (viewElt && viewElt._placesView) 1.939 + viewElt._placesView.uninit(); 1.940 + } finally { 1.941 + this._isCustomizing = true; 1.942 + } 1.943 + this._shouldWrap = this._getShouldWrap(); 1.944 + }, 1.945 + 1.946 + customizeChange: function PTH_customizeChange() { 1.947 + this._setupPlaceholder(); 1.948 + }, 1.949 + 1.950 + _setupPlaceholder: function PTH_setupPlaceholder() { 1.951 + let placeholder = this._placeholder; 1.952 + if (!placeholder) { 1.953 + return; 1.954 + } 1.955 + 1.956 + let shouldWrapNow = this._getShouldWrap(); 1.957 + if (this._shouldWrap != shouldWrapNow) { 1.958 + if (shouldWrapNow) { 1.959 + placeholder.setAttribute("wrap", "true"); 1.960 + } else { 1.961 + placeholder.removeAttribute("wrap"); 1.962 + } 1.963 + this._shouldWrap = shouldWrapNow; 1.964 + } 1.965 + }, 1.966 + 1.967 + customizeDone: function PTH_customizeDone() { 1.968 + this._isCustomizing = false; 1.969 + this.init(true); 1.970 + }, 1.971 + 1.972 + _getShouldWrap: function PTH_getShouldWrap() { 1.973 + let placement = CustomizableUI.getPlacementOfWidget("personal-bookmarks"); 1.974 + let area = placement && placement.area; 1.975 + let areaType = area && CustomizableUI.getAreaType(area); 1.976 + return !area || CustomizableUI.TYPE_MENU_PANEL == areaType; 1.977 + }, 1.978 + 1.979 + onPlaceholderCommand: function () { 1.980 + let widgetGroup = CustomizableUI.getWidget("personal-bookmarks"); 1.981 + let widget = widgetGroup.forWindow(window); 1.982 + if (widget.overflowed || 1.983 + widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) { 1.984 + PlacesCommandHook.showPlacesOrganizer("BookmarksToolbar"); 1.985 + } 1.986 + }, 1.987 + 1.988 + _getParentToolbar: function(element) { 1.989 + while (element) { 1.990 + if (element.localName == "toolbar") { 1.991 + return element; 1.992 + } 1.993 + element = element.parentNode; 1.994 + } 1.995 + return null; 1.996 + }, 1.997 + 1.998 + onWidgetUnderflow: function(aNode, aContainer) { 1.999 + // The view gets broken by being removed and reinserted by the overflowable 1.1000 + // toolbar, so we have to force an uninit and reinit. 1.1001 + let win = aNode.ownerDocument.defaultView; 1.1002 + if (aNode.id == "personal-bookmarks" && win == window) { 1.1003 + this._resetView(); 1.1004 + } 1.1005 + }, 1.1006 + 1.1007 + onWidgetAdded: function(aWidgetId, aArea, aPosition) { 1.1008 + if (aWidgetId == "personal-bookmarks" && !this._isCustomizing) { 1.1009 + // It's possible (with the "Add to Menu", "Add to Toolbar" context 1.1010 + // options) that the Places Toolbar Items have been moved without 1.1011 + // letting us prepare and handle it with with customizeStart and 1.1012 + // customizeDone. If that's the case, we need to reset the views 1.1013 + // since they're probably broken from the DOM reparenting. 1.1014 + this._resetView(); 1.1015 + } 1.1016 + }, 1.1017 + 1.1018 + _resetView: function() { 1.1019 + if (this._viewElt) { 1.1020 + // It's possible that the placesView might not exist, and we need to 1.1021 + // do a full init. This could happen if the Bookmarks Toolbar Items are 1.1022 + // moved to the Menu Panel, and then to the toolbar with the "Add to Toolbar" 1.1023 + // context menu option, outside of customize mode. 1.1024 + if (this._viewElt._placesView) { 1.1025 + this._viewElt._placesView.uninit(); 1.1026 + } 1.1027 + this.init(true); 1.1028 + } 1.1029 + }, 1.1030 +}; 1.1031 + 1.1032 +//////////////////////////////////////////////////////////////////////////////// 1.1033 +//// BookmarkingUI 1.1034 + 1.1035 +/** 1.1036 + * Handles the bookmarks menu-button in the toolbar. 1.1037 + */ 1.1038 + 1.1039 +let BookmarkingUI = { 1.1040 + BOOKMARK_BUTTON_ID: "bookmarks-menu-button", 1.1041 + get button() { 1.1042 + delete this.button; 1.1043 + let widgetGroup = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID); 1.1044 + return this.button = widgetGroup.forWindow(window).node; 1.1045 + }, 1.1046 + 1.1047 + /* Can't make this a self-deleting getter because it's anonymous content 1.1048 + * and might lose/regain bindings at some point. */ 1.1049 + get star() { 1.1050 + return document.getAnonymousElementByAttribute(this.button, "anonid", 1.1051 + "button"); 1.1052 + }, 1.1053 + 1.1054 + get anchor() { 1.1055 + if (!this._shouldUpdateStarState()) { 1.1056 + return null; 1.1057 + } 1.1058 + let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID) 1.1059 + .forWindow(window); 1.1060 + if (widget.overflowed) 1.1061 + return widget.anchor; 1.1062 + 1.1063 + let star = this.star; 1.1064 + return star ? document.getAnonymousElementByAttribute(star, "class", 1.1065 + "toolbarbutton-icon") 1.1066 + : null; 1.1067 + }, 1.1068 + 1.1069 + get notifier() { 1.1070 + delete this.notifier; 1.1071 + return this.notifier = document.getElementById("bookmarked-notification-anchor"); 1.1072 + }, 1.1073 + 1.1074 + get dropmarkerNotifier() { 1.1075 + delete this.dropmarkerNotifier; 1.1076 + return this.dropmarkerNotifier = document.getElementById("bookmarked-notification-dropmarker-anchor"); 1.1077 + }, 1.1078 + 1.1079 + get broadcaster() { 1.1080 + delete this.broadcaster; 1.1081 + let broadcaster = document.getElementById("bookmarkThisPageBroadcaster"); 1.1082 + return this.broadcaster = broadcaster; 1.1083 + }, 1.1084 + 1.1085 + STATUS_UPDATING: -1, 1.1086 + STATUS_UNSTARRED: 0, 1.1087 + STATUS_STARRED: 1, 1.1088 + get status() { 1.1089 + if (!this._shouldUpdateStarState()) { 1.1090 + return this.STATUS_UNSTARRED; 1.1091 + } 1.1092 + if (this._pendingStmt) 1.1093 + return this.STATUS_UPDATING; 1.1094 + return this.button.hasAttribute("starred") ? this.STATUS_STARRED 1.1095 + : this.STATUS_UNSTARRED; 1.1096 + }, 1.1097 + 1.1098 + get _starredTooltip() 1.1099 + { 1.1100 + delete this._starredTooltip; 1.1101 + return this._starredTooltip = 1.1102 + gNavigatorBundle.getString("starButtonOn.tooltip"); 1.1103 + }, 1.1104 + 1.1105 + get _unstarredTooltip() 1.1106 + { 1.1107 + delete this._unstarredTooltip; 1.1108 + return this._unstarredTooltip = 1.1109 + gNavigatorBundle.getString("starButtonOff.tooltip"); 1.1110 + }, 1.1111 + 1.1112 + /** 1.1113 + * The type of the area in which the button is currently located. 1.1114 + * When in the panel, we don't update the button's icon. 1.1115 + */ 1.1116 + _currentAreaType: null, 1.1117 + _shouldUpdateStarState: function() { 1.1118 + return this._currentAreaType == CustomizableUI.TYPE_TOOLBAR; 1.1119 + }, 1.1120 + 1.1121 + /** 1.1122 + * The popup contents must be updated when the user customizes the UI, or 1.1123 + * changes the personal toolbar collapsed status. In such a case, any needed 1.1124 + * change should be handled in the popupshowing helper, for performance 1.1125 + * reasons. 1.1126 + */ 1.1127 + _popupNeedsUpdate: true, 1.1128 + onToolbarVisibilityChange: function BUI_onToolbarVisibilityChange() { 1.1129 + this._popupNeedsUpdate = true; 1.1130 + }, 1.1131 + 1.1132 + onPopupShowing: function BUI_onPopupShowing(event) { 1.1133 + // Don't handle events for submenus. 1.1134 + if (event.target != event.currentTarget) 1.1135 + return; 1.1136 + 1.1137 + // Ideally this code would never be reached, but if you click the outer 1.1138 + // button's border, some cpp code for the menu button's so-called XBL binding 1.1139 + // decides to open the popup even though the dropmarker is invisible. 1.1140 + if (this._currentAreaType == CustomizableUI.TYPE_MENU_PANEL) { 1.1141 + this._showSubview(); 1.1142 + event.preventDefault(); 1.1143 + event.stopPropagation(); 1.1144 + return; 1.1145 + } 1.1146 + 1.1147 + let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID) 1.1148 + .forWindow(window); 1.1149 + if (widget.overflowed) { 1.1150 + // Don't open a popup in the overflow popup, rather just open the Library. 1.1151 + event.preventDefault(); 1.1152 + widget.node.removeAttribute("closemenu"); 1.1153 + PlacesCommandHook.showPlacesOrganizer("BookmarksMenu"); 1.1154 + return; 1.1155 + } 1.1156 + 1.1157 + if (!this._popupNeedsUpdate) 1.1158 + return; 1.1159 + this._popupNeedsUpdate = false; 1.1160 + 1.1161 + let popup = event.target; 1.1162 + let getPlacesAnonymousElement = 1.1163 + aAnonId => document.getAnonymousElementByAttribute(popup.parentNode, 1.1164 + "placesanonid", 1.1165 + aAnonId); 1.1166 + 1.1167 + let viewToolbarMenuitem = getPlacesAnonymousElement("view-toolbar"); 1.1168 + if (viewToolbarMenuitem) { 1.1169 + // Update View bookmarks toolbar checkbox menuitem. 1.1170 + viewToolbarMenuitem.classList.add("subviewbutton"); 1.1171 + let personalToolbar = document.getElementById("PersonalToolbar"); 1.1172 + viewToolbarMenuitem.setAttribute("checked", !personalToolbar.collapsed); 1.1173 + } 1.1174 + }, 1.1175 + 1.1176 + attachPlacesView: function(event, node) { 1.1177 + // If the view is already there, bail out early. 1.1178 + if (node.parentNode._placesView) 1.1179 + return; 1.1180 + 1.1181 + new PlacesMenu(event, "place:folder=BOOKMARKS_MENU", { 1.1182 + extraClasses: { 1.1183 + entry: "subviewbutton", 1.1184 + footer: "panel-subview-footer" 1.1185 + }, 1.1186 + insertionPoint: ".panel-subview-footer" 1.1187 + }); 1.1188 + }, 1.1189 + 1.1190 + /** 1.1191 + * Handles star styling based on page proxy state changes. 1.1192 + */ 1.1193 + onPageProxyStateChanged: function BUI_onPageProxyStateChanged(aState) { 1.1194 + if (!this._shouldUpdateStarState() || !this.star) { 1.1195 + return; 1.1196 + } 1.1197 + 1.1198 + if (aState == "invalid") { 1.1199 + this.star.setAttribute("disabled", "true"); 1.1200 + this.button.removeAttribute("starred"); 1.1201 + this.button.setAttribute("buttontooltiptext", ""); 1.1202 + } 1.1203 + else { 1.1204 + this.star.removeAttribute("disabled"); 1.1205 + this._updateStar(); 1.1206 + } 1.1207 + this._updateToolbarStyle(); 1.1208 + }, 1.1209 + 1.1210 + _updateCustomizationState: function BUI__updateCustomizationState() { 1.1211 + let placement = CustomizableUI.getPlacementOfWidget(this.BOOKMARK_BUTTON_ID); 1.1212 + this._currentAreaType = placement && CustomizableUI.getAreaType(placement.area); 1.1213 + }, 1.1214 + 1.1215 + _updateToolbarStyle: function BUI__updateToolbarStyle() { 1.1216 + let onPersonalToolbar = false; 1.1217 + if (this._currentAreaType == CustomizableUI.TYPE_TOOLBAR) { 1.1218 + let personalToolbar = document.getElementById("PersonalToolbar"); 1.1219 + onPersonalToolbar = this.button.parentNode == personalToolbar || 1.1220 + this.button.parentNode.parentNode == personalToolbar; 1.1221 + } 1.1222 + 1.1223 + if (onPersonalToolbar) 1.1224 + this.button.classList.add("bookmark-item"); 1.1225 + else 1.1226 + this.button.classList.remove("bookmark-item"); 1.1227 + }, 1.1228 + 1.1229 + _uninitView: function BUI__uninitView() { 1.1230 + // When an element with a placesView attached is removed and re-inserted, 1.1231 + // XBL reapplies the binding causing any kind of issues and possible leaks, 1.1232 + // so kill current view and let popupshowing generate a new one. 1.1233 + if (this.button._placesView) 1.1234 + this.button._placesView.uninit(); 1.1235 + 1.1236 + // We have to do the same thing for the "special" views underneath the 1.1237 + // the bookmarks menu. 1.1238 + const kSpecialViewNodeIDs = ["BMB_bookmarksToolbar", "BMB_unsortedBookmarks"]; 1.1239 + for (let viewNodeID of kSpecialViewNodeIDs) { 1.1240 + let elem = document.getElementById(viewNodeID); 1.1241 + if (elem && elem._placesView) { 1.1242 + elem._placesView.uninit(); 1.1243 + } 1.1244 + } 1.1245 + }, 1.1246 + 1.1247 + onCustomizeStart: function BUI_customizeStart(aWindow) { 1.1248 + if (aWindow == window) { 1.1249 + this._uninitView(); 1.1250 + this._isCustomizing = true; 1.1251 + } 1.1252 + }, 1.1253 + 1.1254 + onWidgetAdded: function BUI_widgetAdded(aWidgetId) { 1.1255 + if (aWidgetId == this.BOOKMARK_BUTTON_ID) { 1.1256 + this._onWidgetWasMoved(); 1.1257 + } 1.1258 + }, 1.1259 + 1.1260 + onWidgetRemoved: function BUI_widgetRemoved(aWidgetId) { 1.1261 + if (aWidgetId == this.BOOKMARK_BUTTON_ID) { 1.1262 + this._onWidgetWasMoved(); 1.1263 + } 1.1264 + }, 1.1265 + 1.1266 + onWidgetReset: function BUI_widgetReset(aNode, aContainer) { 1.1267 + if (aNode == this.button) { 1.1268 + this._onWidgetWasMoved(); 1.1269 + } 1.1270 + }, 1.1271 + 1.1272 + onWidgetUndoMove: function BUI_undoWidgetUndoMove(aNode, aContainer) { 1.1273 + if (aNode == this.button) { 1.1274 + this._onWidgetWasMoved(); 1.1275 + } 1.1276 + }, 1.1277 + 1.1278 + _onWidgetWasMoved: function BUI_widgetWasMoved() { 1.1279 + let usedToUpdateStarState = this._shouldUpdateStarState(); 1.1280 + this._updateCustomizationState(); 1.1281 + if (!usedToUpdateStarState && this._shouldUpdateStarState()) { 1.1282 + this.updateStarState(); 1.1283 + } else if (usedToUpdateStarState && !this._shouldUpdateStarState()) { 1.1284 + this._updateStar(); 1.1285 + } 1.1286 + // If we're moved outside of customize mode, we need to uninit 1.1287 + // our view so it gets reconstructed. 1.1288 + if (!this._isCustomizing) { 1.1289 + this._uninitView(); 1.1290 + } 1.1291 + this._updateToolbarStyle(); 1.1292 + }, 1.1293 + 1.1294 + onCustomizeEnd: function BUI_customizeEnd(aWindow) { 1.1295 + if (aWindow == window) { 1.1296 + this._isCustomizing = false; 1.1297 + this.onToolbarVisibilityChange(); 1.1298 + this._updateToolbarStyle(); 1.1299 + } 1.1300 + }, 1.1301 + 1.1302 + init: function() { 1.1303 + CustomizableUI.addListener(this); 1.1304 + this._updateCustomizationState(); 1.1305 + }, 1.1306 + 1.1307 + _hasBookmarksObserver: false, 1.1308 + _itemIds: [], 1.1309 + uninit: function BUI_uninit() { 1.1310 + this._updateBookmarkPageMenuItem(true); 1.1311 + CustomizableUI.removeListener(this); 1.1312 + 1.1313 + this._uninitView(); 1.1314 + 1.1315 + if (this._hasBookmarksObserver) { 1.1316 + PlacesUtils.removeLazyBookmarkObserver(this); 1.1317 + } 1.1318 + 1.1319 + if (this._pendingStmt) { 1.1320 + this._pendingStmt.cancel(); 1.1321 + delete this._pendingStmt; 1.1322 + } 1.1323 + }, 1.1324 + 1.1325 + onLocationChange: function BUI_onLocationChange() { 1.1326 + if (this._uri && gBrowser.currentURI.equals(this._uri)) { 1.1327 + return; 1.1328 + } 1.1329 + this.updateStarState(); 1.1330 + }, 1.1331 + 1.1332 + updateStarState: function BUI_updateStarState() { 1.1333 + // Reset tracked values. 1.1334 + this._uri = gBrowser.currentURI; 1.1335 + this._itemIds = []; 1.1336 + 1.1337 + if (this._pendingStmt) { 1.1338 + this._pendingStmt.cancel(); 1.1339 + delete this._pendingStmt; 1.1340 + } 1.1341 + 1.1342 + // We can load about:blank before the actual page, but there is no point in handling that page. 1.1343 + if (isBlankPageURL(this._uri.spec)) { 1.1344 + return; 1.1345 + } 1.1346 + 1.1347 + this._pendingStmt = PlacesUtils.asyncGetBookmarkIds(this._uri, (aItemIds, aURI) => { 1.1348 + // Safety check that the bookmarked URI equals the tracked one. 1.1349 + if (!aURI.equals(this._uri)) { 1.1350 + Components.utils.reportError("BookmarkingUI did not receive current URI"); 1.1351 + return; 1.1352 + } 1.1353 + 1.1354 + // It's possible that onItemAdded gets called before the async statement 1.1355 + // calls back. For such an edge case, retain all unique entries from both 1.1356 + // arrays. 1.1357 + this._itemIds = this._itemIds.filter( 1.1358 + function (id) aItemIds.indexOf(id) == -1 1.1359 + ).concat(aItemIds); 1.1360 + 1.1361 + this._updateStar(); 1.1362 + 1.1363 + // Start observing bookmarks if needed. 1.1364 + if (!this._hasBookmarksObserver) { 1.1365 + try { 1.1366 + PlacesUtils.addLazyBookmarkObserver(this); 1.1367 + this._hasBookmarksObserver = true; 1.1368 + } catch(ex) { 1.1369 + Components.utils.reportError("BookmarkingUI failed adding a bookmarks observer: " + ex); 1.1370 + } 1.1371 + } 1.1372 + 1.1373 + delete this._pendingStmt; 1.1374 + }); 1.1375 + }, 1.1376 + 1.1377 + _updateStar: function BUI__updateStar() { 1.1378 + if (!this._shouldUpdateStarState()) { 1.1379 + if (this.button.hasAttribute("starred")) { 1.1380 + this.button.removeAttribute("starred"); 1.1381 + this.button.removeAttribute("buttontooltiptext"); 1.1382 + } 1.1383 + return; 1.1384 + } 1.1385 + 1.1386 + if (this._itemIds.length > 0) { 1.1387 + this.button.setAttribute("starred", "true"); 1.1388 + this.button.setAttribute("buttontooltiptext", this._starredTooltip); 1.1389 + if (this.button.getAttribute("overflowedItem") == "true") { 1.1390 + this.button.setAttribute("label", this._starButtonOverflowedStarredLabel); 1.1391 + } 1.1392 + } 1.1393 + else { 1.1394 + this.button.removeAttribute("starred"); 1.1395 + this.button.setAttribute("buttontooltiptext", this._unstarredTooltip); 1.1396 + if (this.button.getAttribute("overflowedItem") == "true") { 1.1397 + this.button.setAttribute("label", this._starButtonOverflowedLabel); 1.1398 + } 1.1399 + } 1.1400 + }, 1.1401 + 1.1402 + /** 1.1403 + * forceReset is passed when we're destroyed and the label should go back 1.1404 + * to the default (Bookmark This Page) for OS X. 1.1405 + */ 1.1406 + _updateBookmarkPageMenuItem: function BUI__updateBookmarkPageMenuItem(forceReset) { 1.1407 + let isStarred = !forceReset && this._itemIds.length > 0; 1.1408 + let label = isStarred ? "editlabel" : "bookmarklabel"; 1.1409 + this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label)); 1.1410 + }, 1.1411 + 1.1412 + onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) { 1.1413 + this._updateBookmarkPageMenuItem(); 1.1414 + PlacesCommandHook.updateBookmarkAllTabsCommand(); 1.1415 + }, 1.1416 + 1.1417 + _showBookmarkedNotification: function BUI_showBookmarkedNotification() { 1.1418 + function getCenteringTransformForRects(rectToPosition, referenceRect) { 1.1419 + let topDiff = referenceRect.top - rectToPosition.top; 1.1420 + let leftDiff = referenceRect.left - rectToPosition.left; 1.1421 + let heightDiff = referenceRect.height - rectToPosition.height; 1.1422 + let widthDiff = referenceRect.width - rectToPosition.width; 1.1423 + return [(leftDiff + .5 * widthDiff) + "px", (topDiff + .5 * heightDiff) + "px"]; 1.1424 + } 1.1425 + 1.1426 + if (this._notificationTimeout) { 1.1427 + clearTimeout(this._notificationTimeout); 1.1428 + } 1.1429 + 1.1430 + if (this.notifier.style.transform == '') { 1.1431 + // Get all the relevant nodes and computed style objects 1.1432 + let dropmarker = document.getAnonymousElementByAttribute(this.button, "anonid", "dropmarker"); 1.1433 + let dropmarkerIcon = document.getAnonymousElementByAttribute(dropmarker, "class", "dropmarker-icon"); 1.1434 + let dropmarkerStyle = getComputedStyle(dropmarkerIcon); 1.1435 + 1.1436 + // Check for RTL and get bounds 1.1437 + let isRTL = getComputedStyle(this.button).direction == "rtl"; 1.1438 + let buttonRect = this.button.getBoundingClientRect(); 1.1439 + let notifierRect = this.notifier.getBoundingClientRect(); 1.1440 + let dropmarkerRect = dropmarkerIcon.getBoundingClientRect(); 1.1441 + let dropmarkerNotifierRect = this.dropmarkerNotifier.getBoundingClientRect(); 1.1442 + 1.1443 + // Compute, but do not set, transform for star icon 1.1444 + let [translateX, translateY] = getCenteringTransformForRects(notifierRect, buttonRect); 1.1445 + let starIconTransform = "translate(" + translateX + ", " + translateY + ")"; 1.1446 + if (isRTL) { 1.1447 + starIconTransform += " scaleX(-1)"; 1.1448 + } 1.1449 + 1.1450 + // Compute, but do not set, transform for dropmarker 1.1451 + [translateX, translateY] = getCenteringTransformForRects(dropmarkerNotifierRect, dropmarkerRect); 1.1452 + let dropmarkerTransform = "translate(" + translateX + ", " + translateY + ")"; 1.1453 + 1.1454 + // Do all layout invalidation in one go: 1.1455 + this.notifier.style.transform = starIconTransform; 1.1456 + this.dropmarkerNotifier.style.transform = dropmarkerTransform; 1.1457 + 1.1458 + let dropmarkerAnimationNode = this.dropmarkerNotifier.firstChild; 1.1459 + dropmarkerAnimationNode.style.MozImageRegion = dropmarkerStyle.MozImageRegion; 1.1460 + dropmarkerAnimationNode.style.listStyleImage = dropmarkerStyle.listStyleImage; 1.1461 + } 1.1462 + 1.1463 + let isInOverflowPanel = this.button.getAttribute("overflowedItem") == "true"; 1.1464 + if (!isInOverflowPanel) { 1.1465 + this.notifier.setAttribute("notification", "finish"); 1.1466 + this.button.setAttribute("notification", "finish"); 1.1467 + this.dropmarkerNotifier.setAttribute("notification", "finish"); 1.1468 + } 1.1469 + 1.1470 + this._notificationTimeout = setTimeout( () => { 1.1471 + this.notifier.removeAttribute("notification"); 1.1472 + this.dropmarkerNotifier.removeAttribute("notification"); 1.1473 + this.button.removeAttribute("notification"); 1.1474 + 1.1475 + this.dropmarkerNotifier.style.transform = ''; 1.1476 + this.notifier.style.transform = ''; 1.1477 + }, 1000); 1.1478 + }, 1.1479 + 1.1480 + _showSubview: function() { 1.1481 + let view = document.getElementById("PanelUI-bookmarks"); 1.1482 + view.addEventListener("ViewShowing", this); 1.1483 + view.addEventListener("ViewHiding", this); 1.1484 + let anchor = document.getElementById(this.BOOKMARK_BUTTON_ID); 1.1485 + anchor.setAttribute("closemenu", "none"); 1.1486 + PanelUI.showSubView("PanelUI-bookmarks", anchor, 1.1487 + CustomizableUI.AREA_PANEL); 1.1488 + }, 1.1489 + 1.1490 + onCommand: function BUI_onCommand(aEvent) { 1.1491 + if (aEvent.target != aEvent.currentTarget) { 1.1492 + return; 1.1493 + } 1.1494 + 1.1495 + // Handle special case when the button is in the panel. 1.1496 + let isBookmarked = this._itemIds.length > 0; 1.1497 + 1.1498 + if (this._currentAreaType == CustomizableUI.TYPE_MENU_PANEL) { 1.1499 + this._showSubview(); 1.1500 + return; 1.1501 + } 1.1502 + let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID) 1.1503 + .forWindow(window); 1.1504 + if (widget.overflowed) { 1.1505 + // Allow to close the panel if the page is already bookmarked, cause 1.1506 + // we are going to open the edit bookmark panel. 1.1507 + if (isBookmarked) 1.1508 + widget.node.removeAttribute("closemenu"); 1.1509 + else 1.1510 + widget.node.setAttribute("closemenu", "none"); 1.1511 + } 1.1512 + 1.1513 + // Ignore clicks on the star if we are updating its state. 1.1514 + if (!this._pendingStmt) { 1.1515 + if (!isBookmarked) 1.1516 + this._showBookmarkedNotification(); 1.1517 + PlacesCommandHook.bookmarkCurrentPage(isBookmarked); 1.1518 + } 1.1519 + }, 1.1520 + 1.1521 + handleEvent: function BUI_handleEvent(aEvent) { 1.1522 + switch (aEvent.type) { 1.1523 + case "ViewShowing": 1.1524 + this.onPanelMenuViewShowing(aEvent); 1.1525 + break; 1.1526 + case "ViewHiding": 1.1527 + this.onPanelMenuViewHiding(aEvent); 1.1528 + break; 1.1529 + } 1.1530 + }, 1.1531 + 1.1532 + onPanelMenuViewShowing: function BUI_onViewShowing(aEvent) { 1.1533 + this._updateBookmarkPageMenuItem(); 1.1534 + // Update checked status of the toolbar toggle. 1.1535 + let viewToolbar = document.getElementById("panelMenu_viewBookmarksToolbar"); 1.1536 + let personalToolbar = document.getElementById("PersonalToolbar"); 1.1537 + if (personalToolbar.collapsed) 1.1538 + viewToolbar.removeAttribute("checked"); 1.1539 + else 1.1540 + viewToolbar.setAttribute("checked", "true"); 1.1541 + // Setup the Places view. 1.1542 + this._panelMenuView = new PlacesPanelMenuView("place:folder=BOOKMARKS_MENU", 1.1543 + "panelMenu_bookmarksMenu", 1.1544 + "panelMenu_bookmarksMenu", { 1.1545 + extraClasses: { 1.1546 + entry: "subviewbutton", 1.1547 + footer: "panel-subview-footer" 1.1548 + } 1.1549 + }); 1.1550 + aEvent.target.removeEventListener("ViewShowing", this); 1.1551 + }, 1.1552 + 1.1553 + onPanelMenuViewHiding: function BUI_onViewHiding(aEvent) { 1.1554 + this._panelMenuView.uninit(); 1.1555 + delete this._panelMenuView; 1.1556 + aEvent.target.removeEventListener("ViewHiding", this); 1.1557 + }, 1.1558 + 1.1559 + onPanelMenuViewCommand: function BUI_onPanelMenuViewCommand(aEvent, aView) { 1.1560 + let target = aEvent.originalTarget; 1.1561 + if (!target._placesNode) 1.1562 + return; 1.1563 + if (PlacesUtils.nodeIsContainer(target._placesNode)) 1.1564 + PlacesCommandHook.showPlacesOrganizer([ "BookmarksMenu", target._placesNode.itemId ]); 1.1565 + else 1.1566 + PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent, aView); 1.1567 + PanelUI.hide(); 1.1568 + }, 1.1569 + 1.1570 + // nsINavBookmarkObserver 1.1571 + onItemAdded: function BUI_onItemAdded(aItemId, aParentId, aIndex, aItemType, 1.1572 + aURI) { 1.1573 + if (aURI && aURI.equals(this._uri)) { 1.1574 + // If a new bookmark has been added to the tracked uri, register it. 1.1575 + if (this._itemIds.indexOf(aItemId) == -1) { 1.1576 + this._itemIds.push(aItemId); 1.1577 + // Only need to update the UI if it wasn't marked as starred before: 1.1578 + if (this._itemIds.length == 1) { 1.1579 + this._updateStar(); 1.1580 + } 1.1581 + } 1.1582 + } 1.1583 + }, 1.1584 + 1.1585 + onItemRemoved: function BUI_onItemRemoved(aItemId) { 1.1586 + let index = this._itemIds.indexOf(aItemId); 1.1587 + // If one of the tracked bookmarks has been removed, unregister it. 1.1588 + if (index != -1) { 1.1589 + this._itemIds.splice(index, 1); 1.1590 + // Only need to update the UI if the page is no longer starred 1.1591 + if (this._itemIds.length == 0) { 1.1592 + this._updateStar(); 1.1593 + } 1.1594 + } 1.1595 + }, 1.1596 + 1.1597 + onItemChanged: function BUI_onItemChanged(aItemId, aProperty, 1.1598 + aIsAnnotationProperty, aNewValue) { 1.1599 + if (aProperty == "uri") { 1.1600 + let index = this._itemIds.indexOf(aItemId); 1.1601 + // If the changed bookmark was tracked, check if it is now pointing to 1.1602 + // a different uri and unregister it. 1.1603 + if (index != -1 && aNewValue != this._uri.spec) { 1.1604 + this._itemIds.splice(index, 1); 1.1605 + // Only need to update the UI if the page is no longer starred 1.1606 + if (this._itemIds.length == 0) { 1.1607 + this._updateStar(); 1.1608 + } 1.1609 + } 1.1610 + // If another bookmark is now pointing to the tracked uri, register it. 1.1611 + else if (index == -1 && aNewValue == this._uri.spec) { 1.1612 + this._itemIds.push(aItemId); 1.1613 + // Only need to update the UI if it wasn't marked as starred before: 1.1614 + if (this._itemIds.length == 1) { 1.1615 + this._updateStar(); 1.1616 + } 1.1617 + } 1.1618 + } 1.1619 + }, 1.1620 + 1.1621 + onBeginUpdateBatch: function () {}, 1.1622 + onEndUpdateBatch: function () {}, 1.1623 + onBeforeItemRemoved: function () {}, 1.1624 + onItemVisited: function () {}, 1.1625 + onItemMoved: function () {}, 1.1626 + 1.1627 + // CustomizableUI events: 1.1628 + _starButtonLabel: null, 1.1629 + get _starButtonOverflowedLabel() { 1.1630 + delete this._starButtonOverflowedLabel; 1.1631 + return this._starButtonOverflowedLabel = 1.1632 + gNavigatorBundle.getString("starButtonOverflowed.label"); 1.1633 + }, 1.1634 + get _starButtonOverflowedStarredLabel() { 1.1635 + delete this._starButtonOverflowedStarredLabel; 1.1636 + return this._starButtonOverflowedStarredLabel = 1.1637 + gNavigatorBundle.getString("starButtonOverflowedStarred.label"); 1.1638 + }, 1.1639 + onWidgetOverflow: function(aNode, aContainer) { 1.1640 + let win = aNode.ownerDocument.defaultView; 1.1641 + if (aNode.id != this.BOOKMARK_BUTTON_ID || win != window) 1.1642 + return; 1.1643 + 1.1644 + let currentLabel = aNode.getAttribute("label"); 1.1645 + if (!this._starButtonLabel) 1.1646 + this._starButtonLabel = currentLabel; 1.1647 + 1.1648 + if (currentLabel == this._starButtonLabel) { 1.1649 + let desiredLabel = this._itemIds.length > 0 ? this._starButtonOverflowedStarredLabel 1.1650 + : this._starButtonOverflowedLabel; 1.1651 + aNode.setAttribute("label", desiredLabel); 1.1652 + } 1.1653 + }, 1.1654 + 1.1655 + onWidgetUnderflow: function(aNode, aContainer) { 1.1656 + let win = aNode.ownerDocument.defaultView; 1.1657 + if (aNode.id != this.BOOKMARK_BUTTON_ID || win != window) 1.1658 + return; 1.1659 + 1.1660 + // The view gets broken by being removed and reinserted. Uninit 1.1661 + // here so popupshowing will generate a new one: 1.1662 + this._uninitView(); 1.1663 + 1.1664 + if (aNode.getAttribute("label") != this._starButtonLabel) 1.1665 + aNode.setAttribute("label", this._starButtonLabel); 1.1666 + }, 1.1667 + 1.1668 + QueryInterface: XPCOMUtils.generateQI([ 1.1669 + Ci.nsINavBookmarkObserver 1.1670 + ]) 1.1671 +};