diff options
author | Thomas Groman <tgroman@nuegia.net> | 2020-04-20 20:49:37 -0700 |
---|---|---|
committer | Thomas Groman <tgroman@nuegia.net> | 2020-04-20 20:49:37 -0700 |
commit | f9cab004186edb425a9b88ad649726605080a17c (patch) | |
tree | e2dae51d3144e83d097a12e7a1499e3ea93f90be /components/places/PlacesUIUtils.jsm | |
parent | f428692de8b59ab89a66502c079e1823dfda8aeb (diff) | |
download | webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.gz webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.lz webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.xz webbrowser-f9cab004186edb425a9b88ad649726605080a17c.zip |
move browser to webbrowser/
Diffstat (limited to 'components/places/PlacesUIUtils.jsm')
-rw-r--r-- | components/places/PlacesUIUtils.jsm | 1373 |
1 files changed, 0 insertions, 1373 deletions
diff --git a/components/places/PlacesUIUtils.jsm b/components/places/PlacesUIUtils.jsm deleted file mode 100644 index e3a9e13..0000000 --- a/components/places/PlacesUIUtils.jsm +++ /dev/null @@ -1,1373 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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/. */ - -this.EXPORTED_SYMBOLS = ["PlacesUIUtils"]; - -var Ci = Components.interfaces; -var Cc = Components.classes; -var Cr = Components.results; -var Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", - "resource://gre/modules/PluralForm.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", - "resource:///modules/RecentWindow.jsm"); - -XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() { - Cu.import("resource://gre/modules/PlacesUtils.jsm"); - return PlacesUtils; -}); - -this.PlacesUIUtils = { - ORGANIZER_LEFTPANE_VERSION: 7, - ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder", - ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery", - - LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar", - DESCRIPTION_ANNO: "bookmarkProperties/description", - - TYPE_TAB_DROP: "application/x-moz-tabbrowser-tab", - - /** - * Makes a URI from a spec, and do fixup - * @param aSpec - * The string spec of the URI - * @returns A URI object for the spec. - */ - createFixedURI: function PUIU_createFixedURI(aSpec) { - return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE); - }, - - getFormattedString: function PUIU_getFormattedString(key, params) { - return bundle.formatStringFromName(key, params, params.length); - }, - - /** - * Get a localized plural string for the specified key name and numeric value - * substituting parameters. - * - * @param aKey - * String, key for looking up the localized string in the bundle - * @param aNumber - * Number based on which the final localized form is looked up - * @param aParams - * Array whose items will substitute #1, #2,... #n parameters - * in the string. - * - * @see https://developer.mozilla.org/en/Localization_and_Plurals - * @return The localized plural string. - */ - getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) { - let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey)); - - // Replace #1 with aParams[0], #2 with aParams[1], and so on. - return str.replace(/\#(\d+)/g, function (matchedId, matchedNumber) { - let param = aParams[parseInt(matchedNumber, 10) - 1]; - return param !== undefined ? param : matchedId; - }); - }, - - getString: function PUIU_getString(key) { - return bundle.GetStringFromName(key); - }, - - get _copyableAnnotations() [ - this.DESCRIPTION_ANNO, - this.LOAD_IN_SIDEBAR_ANNO, - PlacesUtils.POST_DATA_ANNO, - PlacesUtils.READ_ONLY_ANNO, - ], - - /** - * Get a transaction for copying a uri item (either a bookmark or a history - * entry) from one container to another. - * - * @param aData - * JSON object of dropped or pasted item properties - * @param aContainer - * The container being copied into - * @param aIndex - * The index within the container the item is copied to - * @return A nsITransaction object that performs the copy. - * - * @note Since a copy creates a completely new item, only some internal - * annotations are synced from the old one. - * @see this._copyableAnnotations for the list of copyable annotations. - */ - _getURIItemCopyTransaction: - function PUIU__getURIItemCopyTransaction(aData, aContainer, aIndex) - { - let transactions = []; - if (aData.dateAdded) { - transactions.push( - new PlacesEditItemDateAddedTransaction(null, aData.dateAdded) - ); - } - if (aData.lastModified) { - transactions.push( - new PlacesEditItemLastModifiedTransaction(null, aData.lastModified) - ); - } - - let keyword = aData.keyword || null; - let annos = []; - if (aData.annos) { - annos = aData.annos.filter(function (aAnno) { - return this._copyableAnnotations.indexOf(aAnno.name) != -1; - }, this); - } - - return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(aData.uri), - aContainer, aIndex, aData.title, - keyword, annos, transactions); - }, - - /** - * Gets a transaction for copying (recursively nesting to include children) - * a folder (or container) and its contents from one folder to another. - * - * @param aData - * Unwrapped dropped folder data - Obj containing folder and children - * @param aContainer - * The container we are copying into - * @param aIndex - * The index in the destination container to insert the new items - * @return A nsITransaction object that will perform the copy. - * - * @note Since a copy creates a completely new item, only some internal - * annotations are synced from the old one. - * @see this._copyableAnnotations for the list of copyable annotations. - */ - _getFolderCopyTransaction(aData, aContainer, aIndex) { - function getChildItemsTransactions(aRoot) { - let transactions = []; - let index = aIndex; - for (let i = 0; i < aRoot.childCount; ++i) { - let child = aRoot.getChild(i); - // Temporary hacks until we switch to PlacesTransactions.jsm. - let isLivemark = - PlacesUtils.annotations.itemHasAnnotation(child.itemId, - PlacesUtils.LMANNO_FEEDURI); - let [node] = PlacesUtils.unwrapNodes( - PlacesUtils.wrapNode(child, PlacesUtils.TYPE_X_MOZ_PLACE, isLivemark), - PlacesUtils.TYPE_X_MOZ_PLACE - ); - - // Make sure that items are given the correct index, this will be - // passed by the transaction manager to the backend for the insertion. - // Insertion behaves differently for DEFAULT_INDEX (append). - if (aIndex != PlacesUtils.bookmarks.DEFAULT_INDEX) { - index = i; - } - - if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) { - if (node.livemark && node.annos) { - transactions.push( - PlacesUIUtils._getLivemarkCopyTransaction(node, aContainer, index) - ); - } - else { - transactions.push( - PlacesUIUtils._getFolderCopyTransaction(node, aContainer, index) - ); - } - } - else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) { - transactions.push(new PlacesCreateSeparatorTransaction(-1, index)); - } - else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE) { - transactions.push( - PlacesUIUtils._getURIItemCopyTransaction(node, -1, index) - ); - } - else { - throw new Error("Unexpected item under a bookmarks folder"); - } - } - return transactions; - } - - if (aContainer == PlacesUtils.tagsFolderId) { // Copying into a tag folder. - let transactions = []; - if (!aData.livemark && aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) { - let {root} = PlacesUtils.getFolderContents(aData.id, false, false); - let urls = PlacesUtils.getURLsForContainerNode(root); - root.containerOpen = false; - for (let { uri } of urls) { - transactions.push( - new PlacesTagURITransaction(NetUtil.newURI(uri), [aData.title]) - ); - } - } - return new PlacesAggregatedTransaction("addTags", transactions); - } - - if (aData.livemark && aData.annos) { // Copying a livemark. - return this._getLivemarkCopyTransaction(aData, aContainer, aIndex); - } - - let {root} = PlacesUtils.getFolderContents(aData.id, false, false); - let transactions = getChildItemsTransactions(root); - root.containerOpen = false; - - if (aData.dateAdded) { - transactions.push( - new PlacesEditItemDateAddedTransaction(null, aData.dateAdded) - ); - } - if (aData.lastModified) { - transactions.push( - new PlacesEditItemLastModifiedTransaction(null, aData.lastModified) - ); - } - - let annos = []; - if (aData.annos) { - annos = aData.annos.filter(function (aAnno) { - return this._copyableAnnotations.indexOf(aAnno.name) != -1; - }, this); - } - - return new PlacesCreateFolderTransaction(aData.title, aContainer, aIndex, - annos, transactions); - }, - - /** - * Gets a transaction for copying a live bookmark item from one container to - * another. - * - * @param aData - * Unwrapped live bookmarkmark data - * @param aContainer - * The container we are copying into - * @param aIndex - * The index in the destination container to insert the new items - * @return A nsITransaction object that will perform the copy. - * - * @note Since a copy creates a completely new item, only some internal - * annotations are synced from the old one. - * @see this._copyableAnnotations for the list of copyable annotations. - */ - _getLivemarkCopyTransaction: - function PUIU__getLivemarkCopyTransaction(aData, aContainer, aIndex) - { - if (!aData.livemark || !aData.annos) { - throw new Error("node is not a livemark"); - } - - let feedURI, siteURI; - let annos = []; - if (aData.annos) { - annos = aData.annos.filter(function (aAnno) { - if (aAnno.name == PlacesUtils.LMANNO_FEEDURI) { - feedURI = PlacesUtils._uri(aAnno.value); - } - else if (aAnno.name == PlacesUtils.LMANNO_SITEURI) { - siteURI = PlacesUtils._uri(aAnno.value); - } - return this._copyableAnnotations.indexOf(aAnno.name) != -1 - }, this); - } - - return new PlacesCreateLivemarkTransaction(feedURI, siteURI, aData.title, - aContainer, aIndex, annos); - }, - - /** - * Test if a bookmark item = a live bookmark item. - * - * @param aItemId - * item identifier - * @return true if a live bookmark item, false otherwise. - * - * @note Maybe this should be removed later, see bug 1072833. - */ - _isLivemark: - function PUIU__isLivemark(aItemId) - { - // Since this check may be done on each dragover event, it's worth maintaining - // a cache. - let self = PUIU__isLivemark; - if (!("ids" in self)) { - const LIVEMARK_ANNO = PlacesUtils.LMANNO_FEEDURI; - - let idsVec = PlacesUtils.annotations.getItemsWithAnnotation(LIVEMARK_ANNO); - self.ids = new Set(idsVec); - - let obs = Object.freeze({ - QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver), - - onItemAnnotationSet(itemId, annoName) { - if (annoName == LIVEMARK_ANNO) - self.ids.add(itemId); - }, - - onItemAnnotationRemoved(itemId, annoName) { - // If annoName is set to an empty string, the item is gone. - if (annoName == LIVEMARK_ANNO || annoName == "") - self.ids.delete(itemId); - }, - - onPageAnnotationSet() { }, - onPageAnnotationRemoved() { }, - }); - PlacesUtils.annotations.addObserver(obs); - PlacesUtils.registerShutdownFunction(() => { - PlacesUtils.annotations.removeObserver(obs); - }); - } - return self.ids.has(aItemId); - }, - - /** - * Constructs a Transaction for the drop or paste of a blob of data into - * a container. - * @param data - * The unwrapped data blob of dropped or pasted data. - * @param type - * The content type of the data - * @param container - * The container the data was dropped or pasted into - * @param index - * The index within the container the item was dropped or pasted at - * @param copy - * The drag action was copy, so don't move folders or links. - * @returns An object implementing nsITransaction that can perform - * the move/insert. - */ - makeTransaction: - function PUIU_makeTransaction(data, type, container, index, copy) - { - switch (data.type) { - case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER: - if (copy) { - return this._getFolderCopyTransaction(data, container, index); - } - - // Otherwise move the item. - return new PlacesMoveItemTransaction(data.id, container, index); - break; - case PlacesUtils.TYPE_X_MOZ_PLACE: - if (copy || data.id == -1) { // Id is -1 if the place is not bookmarked. - return this._getURIItemCopyTransaction(data, container, index); - } - - // Otherwise move the item. - return new PlacesMoveItemTransaction(data.id, container, index); - break; - case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR: - if (copy) { - // There is no data in a separator, so copying it just amounts to - // inserting a new separator. - return new PlacesCreateSeparatorTransaction(container, index); - } - - // Otherwise move the item. - return new PlacesMoveItemTransaction(data.id, container, index); - break; - default: - if (type == PlacesUtils.TYPE_X_MOZ_URL || - type == PlacesUtils.TYPE_UNICODE || - type == this.TYPE_TAB_DROP) { - let title = type != PlacesUtils.TYPE_UNICODE ? data.title - : data.uri; - return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(data.uri), - container, index, title); - } - } - return null; - }, - - /** - * Shows the bookmark dialog corresponding to the specified info. - * - * @param aInfo - * Describes the item to be edited/added in the dialog. - * See documentation at the top of bookmarkProperties.js - * @param aWindow - * Owner window for the new dialog. - * - * @see documentation at the top of bookmarkProperties.js - * @return true if any transaction has been performed, false otherwise. - */ - showBookmarkDialog: - function PUIU_showBookmarkDialog(aInfo, aParentWindow) { - // Preserve size attributes differently based on the fact the dialog has - // a folder picker or not, since it needs more horizontal space than the - // other controls. - let hasFolderPicker = !("hiddenRows" in aInfo) || - aInfo.hiddenRows.indexOf("folderPicker") == -1; - // Use a different chrome url to persist different sizes. - let dialogURL = hasFolderPicker ? - "chrome://browser/content/places/bookmarkProperties2.xul" : - "chrome://browser/content/places/bookmarkProperties.xul"; - - let features = "centerscreen,chrome,modal,resizable=yes"; - aParentWindow.openDialog(dialogURL, "", features, aInfo); - return ("performed" in aInfo && aInfo.performed); - }, - - _getTopBrowserWin: function PUIU__getTopBrowserWin() { - return RecentWindow.getMostRecentBrowserWindow(); - }, - - /** - * Returns the closet ancestor places view for the given DOM node - * @param aNode - * a DOM node - * @return the closet ancestor places view if exists, null otherwsie. - */ - getViewForNode: function PUIU_getViewForNode(aNode) { - let node = aNode; - - // The view for a <menu> of which its associated menupopup is a places - // view, is the menupopup. - if (node.localName == "menu" && !node._placesNode && - node.lastChild._placesView) - return node.lastChild._placesView; - - while (node instanceof Ci.nsIDOMElement) { - if (node._placesView) - return node._placesView; - if (node.localName == "tree" && node.getAttribute("type") == "places") - return node; - - node = node.parentNode; - } - - return null; - }, - - /** - * By calling this before visiting an URL, the visit will be associated to a - * TRANSITION_TYPED transition (if there is no a referrer). - * This is used when visiting pages from the history menu, history sidebar, - * url bar, url autocomplete results, and history searches from the places - * organizer. If this is not called visits will be marked as - * TRANSITION_LINK. - */ - markPageAsTyped: function PUIU_markPageAsTyped(aURL) { - PlacesUtils.history.markPageAsTyped(this.createFixedURI(aURL)); - }, - - /** - * By calling this before visiting an URL, the visit will be associated to a - * TRANSITION_BOOKMARK transition. - * This is used when visiting pages from the bookmarks menu, - * personal toolbar, and bookmarks from within the places organizer. - * If this is not called visits will be marked as TRANSITION_LINK. - */ - markPageAsFollowedBookmark: function PUIU_markPageAsFollowedBookmark(aURL) { - PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL)); - }, - - /** - * By calling this before visiting an URL, any visit in frames will be - * associated to a TRANSITION_FRAMED_LINK transition. - * This is actually used to distinguish user-initiated visits in frames - * so automatic visits can be correctly ignored. - */ - markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) { - PlacesUtils.history.markPageAsFollowedLink(this.createFixedURI(aURL)); - }, - - /** - * Allows opening of javascript/data URI only if the given node is - * bookmarked (see bug 224521). - * @param aURINode - * a URI node - * @param aWindow - * a window on which a potential error alert is shown on. - * @return true if it's safe to open the node in the browser, false otherwise. - * - */ - checkURLSecurity: function PUIU_checkURLSecurity(aURINode, aWindow) { - if (PlacesUtils.nodeIsBookmark(aURINode)) - return true; - - var uri = PlacesUtils._uri(aURINode.uri); - if (uri.schemeIs("javascript") || uri.schemeIs("data")) { - const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; - var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(BRANDING_BUNDLE_URI). - GetStringFromName("brandShortName"); - - var errorStr = this.getString("load-js-data-url-error"); - Services.prompt.alert(aWindow, brandShortName, errorStr); - return false; - } - return true; - }, - - /** - * Get the description associated with a document, as specified in a <META> - * element. - * @param doc - * A DOM Document to get a description for - * @returns A description string if a META element was discovered with a - * "description" or "httpequiv" attribute, empty string otherwise. - */ - getDescriptionFromDocument: function PUIU_getDescriptionFromDocument(doc) { - var metaElements = doc.getElementsByTagName("META"); - for (var i = 0; i < metaElements.length; ++i) { - if (metaElements[i].name.toLowerCase() == "description" || - metaElements[i].httpEquiv.toLowerCase() == "description") { - return metaElements[i].content; - } - } - return ""; - }, - - /** - * Retrieve the description of an item - * @param aItemId - * item identifier - * @returns the description of the given item, or an empty string if it is - * not set. - */ - getItemDescription: function PUIU_getItemDescription(aItemId) { - if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO)) - return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO); - return ""; - }, - - /** - * Check whether or not the given node represents a removable entry (either in - * history or in bookmarks). - * - * @param aNode - * a node, except the root node of a query. - * @return true if the aNode represents a removable entry, false otherwise. - */ - canUserRemove: function (aNode) { - let parentNode = aNode.parent; - if (!parentNode) - throw new Error("canUserRemove doesn't accept root nodes"); - - // If it's not a bookmark, we can remove it unless it's a child of a - // livemark. - if (aNode.itemId == -1) { - // Rather than executing a db query, checking the existence of the feedURI - // annotation, detect livemark children by the fact that they are the only - // direct non-bookmark children of bookmark folders. - return !PlacesUtils.nodeIsFolder(parentNode); - } - - // Generally it's always possible to remove children of a query. - if (PlacesUtils.nodeIsQuery(parentNode)) - return true; - - // Otherwise it has to be a child of an editable folder. - return !this.isContentsReadOnly(parentNode); - }, - - /** - * DO NOT USE THIS API IN ADDONS. IT IS VERY LIKELY TO CHANGE WHEN THE SWITCH - * TO GUIDS IS COMPLETE (BUG 1071511). - * - * Check whether or not the given node or item-id points to a folder which - * should not be modified by the user (i.e. its children should be unremovable - * and unmovable, new children should be disallowed, etc). - * These semantics are not inherited, meaning that read-only folder may - * contain editable items (for instance, the places root is read-only, but all - * of its direct children aren't). - * - * You should only pass folder item ids or folder nodes for aNodeOrItemId. - * While this is only enforced for the node case (if an item id of a separator - * or a bookmark is passed, false is returned), it's considered the caller's - * job to ensure that it checks a folder. - * Also note that folder-shortcuts should only be passed as result nodes. - * Otherwise they are just treated as bookmarks (i.e. false is returned). - * - * @param aNodeOrItemId - * any item id or result node. - * @throws if aNodeOrItemId is neither an item id nor a folder result node. - * @note livemark "folders" are considered read-only (but see bug 1072833). - * @return true if aItemId points to a read-only folder, false otherwise. - */ - isContentsReadOnly: function (aNodeOrItemId) { - let itemId; - if (typeof(aNodeOrItemId) == "number") { - itemId = aNodeOrItemId; - } - else if (PlacesUtils.nodeIsFolder(aNodeOrItemId)) { - itemId = PlacesUtils.getConcreteItemId(aNodeOrItemId); - } - else { - throw new Error("invalid value for aNodeOrItemId"); - } - - if (itemId == PlacesUtils.placesRootId || this._isLivemark(itemId)) - return true; - - // leftPaneFolderId, and as a result, allBookmarksFolderId, is a lazy getter - // performing at least a synchronous DB query (and on its very first call - // in a fresh profile, it also creates the entire structure). - // Therefore we don't want to this function, which is called very often by - // isCommandEnabled, to ever be the one that invokes it first, especially - // because isCommandEnabled may be called way before the left pane folder is - // even created (for example, if the user only uses the bookmarks menu or - // toolbar for managing bookmarks). To do so, we avoid comparing to those - // special folder if the lazy getter is still in place. This is safe merely - // because the only way to access the left pane contents goes through - // "resolving" the leftPaneFolderId getter. - if ("get" in Object.getOwnPropertyDescriptor(this, "leftPaneFolderId")) - return false; - - return itemId == this.leftPaneFolderId || - itemId == this.allBookmarksFolderId; - }, - - /** - * Gives the user a chance to cancel loading lots of tabs at once - */ - _confirmOpenInTabs: - function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) { - const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen"; - var reallyOpen = true; - - if (Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) { - if (numTabsToOpen >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) { - // default to true: if it were false, we wouldn't get this far - var warnOnOpen = { value: true }; - - var messageKey = "tabs.openWarningMultipleBranded"; - var openKey = "tabs.openButtonMultiple"; - const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; - var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(BRANDING_BUNDLE_URI). - GetStringFromName("brandShortName"); - - var buttonPressed = Services.prompt.confirmEx( - aWindow, - this.getString("tabs.openWarningTitle"), - this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]), - (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) + - (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1), - this.getString(openKey), null, null, - this.getFormattedString("tabs.openWarningPromptMeBranded", - [brandShortName]), - warnOnOpen - ); - - reallyOpen = (buttonPressed == 0); - // don't set the pref unless they press OK and it's false - if (reallyOpen && !warnOnOpen.value) - Services.prefs.setBoolPref(WARN_ON_OPEN_PREF, false); - } - } - - return reallyOpen; - }, - - /** aItemsToOpen needs to be an array of objects of the form: - * {uri: string, isBookmark: boolean} - */ - _openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) { - if (!aItemsToOpen.length) - return; - - // Prefer the caller window if it's a browser window, otherwise use - // the top browser window. - var browserWindow = null; - browserWindow = - aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ? - aWindow : this._getTopBrowserWin(); - - var urls = []; - let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow); - for (let item of aItemsToOpen) { - urls.push(item.uri); - if (skipMarking) { - continue; - } - - if (item.isBookmark) - this.markPageAsFollowedBookmark(item.uri); - else - this.markPageAsTyped(item.uri); - } - - // whereToOpenLink doesn't return "window" when there's no browser window - // open (Bug 630255). - var where = browserWindow ? - browserWindow.whereToOpenLink(aEvent, false, true) : "window"; - if (where == "window") { - // There is no browser window open, thus open a new one. - var uriList = PlacesUtils.toISupportsString(urls.join("|")); - var args = Cc["@mozilla.org/supports-array;1"]. - createInstance(Ci.nsISupportsArray); - args.AppendElement(uriList); - browserWindow = Services.ww.openWindow(aWindow, - "chrome://browser/content/browser.xul", - null, "chrome,dialog=no,all", args); - return; - } - - var loadInBackground = where == "tabshifted" ? true : false; - // For consistency, we want all the bookmarks to open in new tabs, instead - // of having one of them replace the currently focused tab. Hence we call - // loadTabs with aReplace set to false. - browserWindow.gBrowser.loadTabs(urls, loadInBackground, false); - }, - - openLiveMarkNodesInTabs: - function PUIU_openLiveMarkNodesInTabs(aNode, aEvent, aView) { - let window = aView.ownerWindow; - - PlacesUtils.livemarks.getLivemark({id: aNode.itemId}) - .then(aLivemark => { - urlsToOpen = []; - - let nodes = aLivemark.getNodesForContainer(aNode); - for (let node of nodes) { - urlsToOpen.push({uri: node.uri, isBookmark: false}); - } - - if (this._confirmOpenInTabs(urlsToOpen.length, window)) { - this._openTabset(urlsToOpen, aEvent, window); - } - }, Cu.reportError); - }, - - openContainerNodeInTabs: - function PUIU_openContainerInTabs(aNode, aEvent, aView) { - let window = aView.ownerWindow; - - let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode); - if (this._confirmOpenInTabs(urlsToOpen.length, window)) { - this._openTabset(urlsToOpen, aEvent, window); - } - }, - - openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) { - let window = aView.ownerWindow; - - let urlsToOpen = []; - for (var i=0; i < aNodes.length; i++) { - // Skip over separators and folders. - if (PlacesUtils.nodeIsURI(aNodes[i])) - urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])}); - } - this._openTabset(urlsToOpen, aEvent, window); - }, - - /** - * Loads the node's URL in the appropriate tab or window or as a web - * panel given the user's preference specified by modifier keys tracked by a - * DOM mouse/key event. - * @param aNode - * An uri result node. - * @param aEvent - * The DOM mouse/key event with modifier keys set that track the - * user's preferred destination window or tab. - * @param aView - * The controller associated with aNode. - */ - openNodeWithEvent: - function PUIU_openNodeWithEvent(aNode, aEvent, aView) { - let window = aView.ownerWindow; - this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window); - }, - - /** - * Loads the node's URL in the appropriate tab or window or as a - * web panel. - * see also openUILinkIn - */ - openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView, aPrivate) { - let window = aView.ownerWindow; - this._openNodeIn(aNode, aWhere, window, aPrivate); - }, - - _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow, aPrivate=false) { - if (aNode && PlacesUtils.nodeIsURI(aNode) && - this.checkURLSecurity(aNode, aWindow)) { - let isBookmark = PlacesUtils.nodeIsBookmark(aNode); - - if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) { - if (isBookmark) - this.markPageAsFollowedBookmark(aNode.uri); - else - this.markPageAsTyped(aNode.uri); - } - - // Check whether the node is a bookmark which should be opened as - // a web panel - if (aWhere == "current" && isBookmark) { - if (PlacesUtils.annotations - .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) { - let browserWin = this._getTopBrowserWin(); - if (browserWin) { - browserWin.openWebPanel(aNode.title, aNode.uri); - return; - } - } - } - aWindow.openUILinkIn(aNode.uri, aWhere, { - inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground"), - private: aPrivate, - }); - } - }, - - /** - * Helper for guessing scheme from an url string. - * Used to avoid nsIURI overhead in frequently called UI functions. - * - * @param aUrlString the url to guess the scheme from. - * - * @return guessed scheme for this url string. - * - * @note this is not supposed be perfect, so use it only for UI purposes. - */ - guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) { - return aUrlString.substr(0, aUrlString.indexOf(":")); - }, - - getBestTitle: function PUIU_getBestTitle(aNode, aDoNotCutTitle) { - var title; - if (!aNode.title && PlacesUtils.nodeIsURI(aNode)) { - // if node title is empty, try to set the label using host and filename - // PlacesUtils._uri() will throw if aNode.uri is not a valid URI - try { - var uri = PlacesUtils._uri(aNode.uri); - var host = uri.host; - var fileName = uri.QueryInterface(Ci.nsIURL).fileName; - // if fileName is empty, use path to distinguish labels - if (aDoNotCutTitle) { - title = host + uri.path; - } else { - title = host + (fileName ? - (host ? "/" + this.ellipsis + "/" : "") + fileName : - uri.path); - } - } - catch (e) { - // Use (no title) for non-standard URIs (data:, javascript:, ...) - title = ""; - } - } - else - title = aNode.title; - - return title || this.getString("noTitle"); - }, - - get leftPaneQueries() { - // build the map - this.leftPaneFolderId; - return this.leftPaneQueries; - }, - - // Get the folder id for the organizer left-pane folder. - get leftPaneFolderId() { - let leftPaneRoot = -1; - let allBookmarksId; - - // Shortcuts to services. - let bs = PlacesUtils.bookmarks; - let as = PlacesUtils.annotations; - - // This is the list of the left pane queries. - let queries = { - "PlacesRoot": { title: "" }, - "History": { title: this.getString("OrganizerQueryHistory") }, - "Downloads": { title: this.getString("OrganizerQueryDownloads") }, - "Tags": { title: this.getString("OrganizerQueryTags") }, - "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") }, - "BookmarksToolbar": - { title: null, - concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"), - concreteId: PlacesUtils.toolbarFolderId }, - "BookmarksMenu": - { title: null, - concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"), - concreteId: PlacesUtils.bookmarksMenuFolderId }, - "UnfiledBookmarks": - { title: null, - concreteTitle: PlacesUtils.getString("UnsortedBookmarksFolderTitle"), - concreteId: PlacesUtils.unfiledBookmarksFolderId }, - }; - // All queries but PlacesRoot. - const EXPECTED_QUERY_COUNT = 7; - - // Removes an item and associated annotations, ignoring eventual errors. - function safeRemoveItem(aItemId) { - try { - if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) && - !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) { - // Some extension annotated their roots with our query annotation, - // so we should not delete them. - return; - } - // removeItemAnnotation does not check if item exists, nor the anno, - // so this is safe to do. - as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO); - as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO); - // This will throw if the annotation is an orphan. - bs.removeItem(aItemId); - } - catch(e) { /* orphan anno */ } - } - - // Returns true if item really exists, false otherwise. - function itemExists(aItemId) { - try { - bs.getItemIndex(aItemId); - return true; - } - catch(e) { - return false; - } - } - - // Get all items marked as being the left pane folder. - let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO); - if (items.length > 1) { - // Something went wrong, we cannot have more than one left pane folder, - // remove all left pane folders and continue. We will create a new one. - items.forEach(safeRemoveItem); - } - else if (items.length == 1 && items[0] != -1) { - leftPaneRoot = items[0]; - // Check that organizer left pane root is valid. - let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO); - if (version != this.ORGANIZER_LEFTPANE_VERSION || - !itemExists(leftPaneRoot)) { - // Invalid root, we must rebuild the left pane. - safeRemoveItem(leftPaneRoot); - leftPaneRoot = -1; - } - } - - if (leftPaneRoot != -1) { - // A valid left pane folder has been found. - // Build the leftPaneQueries Map. This is used to quickly access them, - // associating a mnemonic name to the real item ids. - delete this.leftPaneQueries; - this.leftPaneQueries = {}; - - let items = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO); - // While looping through queries we will also check for their validity. - let queriesCount = 0; - for (let i = 0; i < items.length; i++) { - let queryName = as.getItemAnnotation(items[i], this.ORGANIZER_QUERY_ANNO); - - // Some extension did use our annotation to decorate their items - // with icons, so we should check only our elements, to avoid dataloss. - if (!(queryName in queries)) - continue; - - let query = queries[queryName]; - query.itemId = items[i]; - - if (!itemExists(query.itemId)) { - // Orphan annotation, bail out and create a new left pane root. - break; - } - - // Check that all queries have valid parents. - let parentId = bs.getFolderIdForItem(query.itemId); - if (items.indexOf(parentId) == -1 && parentId != leftPaneRoot) { - // The parent is not part of the left pane, bail out and create a new - // left pane root. - break; - } - - // Titles could have been corrupted or the user could have changed his - // locale. Check title and eventually fix it. - if (bs.getItemTitle(query.itemId) != query.title) - bs.setItemTitle(query.itemId, query.title); - if ("concreteId" in query) { - if (bs.getItemTitle(query.concreteId) != query.concreteTitle) - bs.setItemTitle(query.concreteId, query.concreteTitle); - } - - // Add the query to our cache. - this.leftPaneQueries[queryName] = query.itemId; - queriesCount++; - } - - if (queriesCount != EXPECTED_QUERY_COUNT) { - // Queries number is wrong, so the left pane must be corrupt. - // Note: we can't just remove the leftPaneRoot, because some query could - // have a bad parent, so we have to remove all items one by one. - items.forEach(safeRemoveItem); - safeRemoveItem(leftPaneRoot); - } - else { - // Everything is fine, return the current left pane folder. - delete this.leftPaneFolderId; - return this.leftPaneFolderId = leftPaneRoot; - } - } - - // Create a new left pane folder. - var callback = { - // Helper to create an organizer special query. - create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) { - let itemId = bs.insertBookmark(aParentId, - PlacesUtils._uri(aQueryUrl), - bs.DEFAULT_INDEX, - queries[aQueryName].title); - // Mark as special organizer query. - as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName, - 0, as.EXPIRE_NEVER); - // We should never backup this, since it changes between profiles. - as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1, - 0, as.EXPIRE_NEVER); - // Add to the queries map. - PlacesUIUtils.leftPaneQueries[aQueryName] = itemId; - return itemId; - }, - - // Helper to create an organizer special folder. - create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) { - // Left Pane Root Folder. - let folderId = bs.createFolder(aParentId, - queries[aFolderName].title, - bs.DEFAULT_INDEX); - // We should never backup this, since it changes between profiles. - as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1, - 0, as.EXPIRE_NEVER); - - if (aIsRoot) { - // Mark as special left pane root. - as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO, - PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, - 0, as.EXPIRE_NEVER); - } - else { - // Mark as special organizer folder. - as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName, - 0, as.EXPIRE_NEVER); - PlacesUIUtils.leftPaneQueries[aFolderName] = folderId; - } - return folderId; - }, - - runBatched: function CB_runBatched(aUserData) { - delete PlacesUIUtils.leftPaneQueries; - PlacesUIUtils.leftPaneQueries = { }; - - // Left Pane Root Folder. - leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true); - - // History Query. - this.create_query("History", leftPaneRoot, - "place:type=" + - Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY + - "&sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING); - - // Downloads. - this.create_query("Downloads", leftPaneRoot, - "place:transition=" + - Ci.nsINavHistoryService.TRANSITION_DOWNLOAD + - "&sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING); - - // Tags Query. - this.create_query("Tags", leftPaneRoot, - "place:type=" + - Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY + - "&sort=" + - Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING); - - // All Bookmarks Folder. - allBookmarksId = this.create_folder("AllBookmarks", leftPaneRoot, false); - - // All Bookmarks->Bookmarks Toolbar Query. - this.create_query("BookmarksToolbar", allBookmarksId, - "place:folder=TOOLBAR"); - - // All Bookmarks->Bookmarks Menu Query. - this.create_query("BookmarksMenu", allBookmarksId, - "place:folder=BOOKMARKS_MENU"); - - // All Bookmarks->Unfiled Bookmarks Query. - this.create_query("UnfiledBookmarks", allBookmarksId, - "place:folder=UNFILED_BOOKMARKS"); - } - }; - bs.runInBatchMode(callback, null); - // Maybe: PlacesUtils.bookmarks.runInBatchMode(callback, null); ? - - delete this.leftPaneFolderId; - return this.leftPaneFolderId = leftPaneRoot; - }, - - /** - * Get the folder id for the organizer left-pane folder. - */ - get allBookmarksFolderId() { - // ensure the left-pane root is initialized; - this.leftPaneFolderId; - delete this.allBookmarksFolderId; - return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"]; - }, - - /** - * If an item is a left-pane query, returns the name of the query - * or an empty string if not. - * - * @param aItemId id of a container - * @returns the name of the query, or empty string if not a left-pane query - */ - getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) { - var queryName = ""; - // If the let pane hasn't been built, use the annotation service - // directly, to avoid building the left pane too early. - if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) { - try { - queryName = PlacesUtils.annotations. - getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO); - } - catch (ex) { - // doesn't have the annotation - queryName = ""; - } - } - else { - // If the left pane has already been built, use the name->id map - // cached in PlacesUIUtils. - for (let [name, id] in Iterator(this.leftPaneQueries)) { - if (aItemId == id) - queryName = name; - } - } - return queryName; - }, - - /** - * Returns the passed URL with a #moz-resolution fragment - * for the specified dimensions and devicePixelRatio. - * - * @param aWindow - * A window from where we want to get the device - * pixel Ratio - * - * @param aURL - * The URL where we should add the fragment - * - * @param aWidth - * The target image width - * - * @param aHeight - * The target image height - * - * @return The URL with the fragment at the end - */ - getImageURLForResolution: - function PUIU_getImageURLForResolution(aWindow, aURL, aWidth, aHeight) { - return aURL; - } -}; - -XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF", - "@mozilla.org/rdf/rdf-service;1", - "nsIRDFService"); - -XPCOMUtils.defineLazyGetter(PlacesUIUtils, "localStore", function() { - return PlacesUIUtils.RDF.GetDataSource("rdf:local-store"); -}); - -XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() { - return Services.prefs.getComplexValue("intl.ellipsis", - Ci.nsIPrefLocalizedString).data; -}); - -XPCOMUtils.defineLazyServiceGetter(this, "URIFixup", - "@mozilla.org/docshell/urifixup;1", - "nsIURIFixup"); - -XPCOMUtils.defineLazyGetter(this, "bundle", function() { - const PLACES_STRING_BUNDLE_URI = - "chrome://browser/locale/places/places.properties"; - return Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(PLACES_STRING_BUNDLE_URI); -}); - -XPCOMUtils.defineLazyServiceGetter(this, "focusManager", - "@mozilla.org/focus-manager;1", - "nsIFocusManager"); - -/** - * This is a compatibility shim for old PUIU.ptm users. - * - * If you're looking for transactions and writing new code using them, directly - * use the transactions objects exported by the PlacesUtils.jsm module. - * - * This object will be removed once enough users are converted to the new API. - */ -XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() { - // Ensure PlacesUtils is imported in scope. - PlacesUtils; - - return { - aggregateTransactions: function(aName, aTransactions) - new PlacesAggregatedTransaction(aName, aTransactions), - - createFolder: function(aName, aContainer, aIndex, aAnnotations, - aChildItemsTransactions) - new PlacesCreateFolderTransaction(aName, aContainer, aIndex, aAnnotations, - aChildItemsTransactions), - - createItem: function(aURI, aContainer, aIndex, aTitle, aKeyword, - aAnnotations, aChildTransactions) - new PlacesCreateBookmarkTransaction(aURI, aContainer, aIndex, aTitle, - aKeyword, aAnnotations, - aChildTransactions), - - createSeparator: function(aContainer, aIndex) - new PlacesCreateSeparatorTransaction(aContainer, aIndex), - - createLivemark: function(aFeedURI, aSiteURI, aName, aContainer, aIndex, - aAnnotations) - new PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aName, aContainer, - aIndex, aAnnotations), - - moveItem: function(aItemId, aNewContainer, aNewIndex) - new PlacesMoveItemTransaction(aItemId, aNewContainer, aNewIndex), - - removeItem: function(aItemId) - new PlacesRemoveItemTransaction(aItemId), - - editItemTitle: function(aItemId, aNewTitle) - new PlacesEditItemTitleTransaction(aItemId, aNewTitle), - - editBookmarkURI: function(aItemId, aNewURI) - new PlacesEditBookmarkURITransaction(aItemId, aNewURI), - - setItemAnnotation: function(aItemId, aAnnotationObject) - new PlacesSetItemAnnotationTransaction(aItemId, aAnnotationObject), - - setPageAnnotation: function(aURI, aAnnotationObject) - new PlacesSetPageAnnotationTransaction(aURI, aAnnotationObject), - - editBookmarkKeyword: function(aItemId, aNewKeyword) - new PlacesEditBookmarkKeywordTransaction(aItemId, aNewKeyword), - - editBookmarkPostData: function(aItemId, aPostData) - new PlacesEditBookmarkPostDataTransaction(aItemId, aPostData), - - editLivemarkSiteURI: function(aLivemarkId, aSiteURI) - new PlacesEditLivemarkSiteURITransaction(aLivemarkId, aSiteURI), - - editLivemarkFeedURI: function(aLivemarkId, aFeedURI) - new PlacesEditLivemarkFeedURITransaction(aLivemarkId, aFeedURI), - - editItemDateAdded: function(aItemId, aNewDateAdded) - new PlacesEditItemDateAddedTransaction(aItemId, aNewDateAdded), - - editItemLastModified: function(aItemId, aNewLastModified) - new PlacesEditItemLastModifiedTransaction(aItemId, aNewLastModified), - - sortFolderByName: function(aFolderId) - new PlacesSortFolderByNameTransaction(aFolderId), - - tagURI: function(aURI, aTags) - new PlacesTagURITransaction(aURI, aTags), - - untagURI: function(aURI, aTags) - new PlacesUntagURITransaction(aURI, aTags), - - /** - * Transaction for setting/unsetting Load-in-sidebar annotation. - * - * @param aBookmarkId - * id of the bookmark where to set Load-in-sidebar annotation. - * @param aLoadInSidebar - * boolean value. - * @returns nsITransaction object. - */ - setLoadInSidebar: function(aItemId, aLoadInSidebar) - { - let annoObj = { name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO, - type: Ci.nsIAnnotationService.TYPE_INT32, - flags: 0, - value: aLoadInSidebar, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - return new PlacesSetItemAnnotationTransaction(aItemId, annoObj); - }, - - /** - * Transaction for editing the description of a bookmark or a folder. - * - * @param aItemId - * id of the item to edit. - * @param aDescription - * new description. - * @returns nsITransaction object. - */ - editItemDescription: function(aItemId, aDescription) - { - let annoObj = { name: PlacesUIUtils.DESCRIPTION_ANNO, - type: Ci.nsIAnnotationService.TYPE_STRING, - flags: 0, - value: aDescription, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - return new PlacesSetItemAnnotationTransaction(aItemId, annoObj); - }, - - //////////////////////////////////////////////////////////////////////////// - //// nsITransactionManager forwarders. - - beginBatch: function() - PlacesUtils.transactionManager.beginBatch(null), - - endBatch: function() - PlacesUtils.transactionManager.endBatch(false), - - doTransaction: function(txn) - PlacesUtils.transactionManager.doTransaction(txn), - - undoTransaction: function() - PlacesUtils.transactionManager.undoTransaction(), - - redoTransaction: function() - PlacesUtils.transactionManager.redoTransaction(), - - get numberOfUndoItems() - PlacesUtils.transactionManager.numberOfUndoItems, - get numberOfRedoItems() - PlacesUtils.transactionManager.numberOfRedoItems, - get maxTransactionCount() - PlacesUtils.transactionManager.maxTransactionCount, - set maxTransactionCount(val) - PlacesUtils.transactionManager.maxTransactionCount = val, - - clear: function() - PlacesUtils.transactionManager.clear(), - - peekUndoStack: function() - PlacesUtils.transactionManager.peekUndoStack(), - - peekRedoStack: function() - PlacesUtils.transactionManager.peekRedoStack(), - - getUndoStack: function() - PlacesUtils.transactionManager.getUndoStack(), - - getRedoStack: function() - PlacesUtils.transactionManager.getRedoStack(), - - AddListener: function(aListener) - PlacesUtils.transactionManager.AddListener(aListener), - - RemoveListener: function(aListener) - PlacesUtils.transactionManager.RemoveListener(aListener) - } -}); |