browser/components/places/src/PlacesUIUtils.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 this.EXPORTED_SYMBOLS = ["PlacesUIUtils"];
     8 var Ci = Components.interfaces;
     9 var Cc = Components.classes;
    10 var Cr = Components.results;
    11 var Cu = Components.utils;
    13 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    14 Cu.import("resource://gre/modules/Services.jsm");
    16 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
    17                                   "resource://gre/modules/PluralForm.jsm");
    19 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
    20                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
    22 #ifdef MOZ_SERVICES_SYNC
    23 XPCOMUtils.defineLazyModuleGetter(this, "Weave",
    24                                   "resource://services-sync/main.js");
    25 #endif
    27 XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
    28   Cu.import("resource://gre/modules/PlacesUtils.jsm");
    29   return PlacesUtils;
    30 });
    32 this.PlacesUIUtils = {
    33   ORGANIZER_LEFTPANE_VERSION: 7,
    34   ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
    35   ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",
    37   LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar",
    38   DESCRIPTION_ANNO: "bookmarkProperties/description",
    40   TYPE_TAB_DROP: "application/x-moz-tabbrowser-tab",
    42   /**
    43    * Makes a URI from a spec, and do fixup
    44    * @param   aSpec
    45    *          The string spec of the URI
    46    * @returns A URI object for the spec.
    47    */
    48   createFixedURI: function PUIU_createFixedURI(aSpec) {
    49     return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
    50   },
    52   getFormattedString: function PUIU_getFormattedString(key, params) {
    53     return bundle.formatStringFromName(key, params, params.length);
    54   },
    56   /**
    57    * Get a localized plural string for the specified key name and numeric value
    58    * substituting parameters.
    59    *
    60    * @param   aKey
    61    *          String, key for looking up the localized string in the bundle
    62    * @param   aNumber
    63    *          Number based on which the final localized form is looked up
    64    * @param   aParams
    65    *          Array whose items will substitute #1, #2,... #n parameters
    66    *          in the string.
    67    *
    68    * @see https://developer.mozilla.org/en/Localization_and_Plurals
    69    * @return The localized plural string.
    70    */
    71   getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) {
    72     let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey));
    74     // Replace #1 with aParams[0], #2 with aParams[1], and so on.
    75     return str.replace(/\#(\d+)/g, function (matchedId, matchedNumber) {
    76       let param = aParams[parseInt(matchedNumber, 10) - 1];
    77       return param !== undefined ? param : matchedId;
    78     });
    79   },
    81   getString: function PUIU_getString(key) {
    82     return bundle.GetStringFromName(key);
    83   },
    85   get _copyableAnnotations() [
    86     this.DESCRIPTION_ANNO,
    87     this.LOAD_IN_SIDEBAR_ANNO,
    88     PlacesUtils.POST_DATA_ANNO,
    89     PlacesUtils.READ_ONLY_ANNO,
    90   ],
    92   /**
    93    * Get a transaction for copying a uri item (either a bookmark or a history
    94    * entry) from one container to another.
    95    *
    96    * @param   aData
    97    *          JSON object of dropped or pasted item properties
    98    * @param   aContainer
    99    *          The container being copied into
   100    * @param   aIndex
   101    *          The index within the container the item is copied to
   102    * @return A nsITransaction object that performs the copy.
   103    *
   104    * @note Since a copy creates a completely new item, only some internal
   105    *       annotations are synced from the old one.
   106    * @see this._copyableAnnotations for the list of copyable annotations.
   107    */
   108   _getURIItemCopyTransaction:
   109   function PUIU__getURIItemCopyTransaction(aData, aContainer, aIndex)
   110   {
   111     let transactions = [];
   112     if (aData.dateAdded) {
   113       transactions.push(
   114         new PlacesEditItemDateAddedTransaction(null, aData.dateAdded)
   115       );
   116     }
   117     if (aData.lastModified) {
   118       transactions.push(
   119         new PlacesEditItemLastModifiedTransaction(null, aData.lastModified)
   120       );
   121     }
   123     let keyword = aData.keyword || null;
   124     let annos = [];
   125     if (aData.annos) {
   126       annos = aData.annos.filter(function (aAnno) {
   127         return this._copyableAnnotations.indexOf(aAnno.name) != -1;
   128       }, this);
   129     }
   131     return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(aData.uri),
   132                                                aContainer, aIndex, aData.title,
   133                                                keyword, annos, transactions);
   134   },
   136   /**
   137    * Gets a transaction for copying (recursively nesting to include children)
   138    * a folder (or container) and its contents from one folder to another.
   139    *
   140    * @param   aData
   141    *          Unwrapped dropped folder data - Obj containing folder and children
   142    * @param   aContainer
   143    *          The container we are copying into
   144    * @param   aIndex
   145    *          The index in the destination container to insert the new items
   146    * @return A nsITransaction object that will perform the copy.
   147    *
   148    * @note Since a copy creates a completely new item, only some internal
   149    *       annotations are synced from the old one.
   150    * @see this._copyableAnnotations for the list of copyable annotations.
   151    */
   152   _getFolderCopyTransaction:
   153   function PUIU__getFolderCopyTransaction(aData, aContainer, aIndex)
   154   {
   155     function getChildItemsTransactions(aChildren)
   156     {
   157       let transactions = [];
   158       let index = aIndex;
   159       aChildren.forEach(function (node, i) {
   160         // Make sure that items are given the correct index, this will be
   161         // passed by the transaction manager to the backend for the insertion.
   162         // Insertion behaves differently for DEFAULT_INDEX (append).
   163         if (aIndex != PlacesUtils.bookmarks.DEFAULT_INDEX) {
   164           index = i;
   165         }
   167         if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
   168           if (node.livemark && node.annos) {
   169             transactions.push(
   170               PlacesUIUtils._getLivemarkCopyTransaction(node, aContainer, index)
   171             );
   172           }
   173           else {
   174             transactions.push(
   175               PlacesUIUtils._getFolderCopyTransaction(node, aContainer, index)
   176             );
   177           }
   178         }
   179         else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) {
   180           transactions.push(new PlacesCreateSeparatorTransaction(-1, index));
   181         }
   182         else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE) {
   183           transactions.push(
   184             PlacesUIUtils._getURIItemCopyTransaction(node, -1, index)
   185           );
   186         }
   187         else {
   188           throw new Error("Unexpected item under a bookmarks folder");
   189         }
   190       });
   191       return transactions;
   192     }
   194     if (aContainer == PlacesUtils.tagsFolderId) { // Copying a tag folder.
   195       let transactions = [];
   196       if (aData.children) {
   197         aData.children.forEach(function(aChild) {
   198           transactions.push(
   199             new PlacesTagURITransaction(PlacesUtils._uri(aChild.uri),
   200                                         [aData.title])
   201           );
   202         });
   203       }
   204       return new PlacesAggregatedTransaction("addTags", transactions);
   205     }
   207     if (aData.livemark && aData.annos) { // Copying a livemark.
   208       return this._getLivemarkCopyTransaction(aData, aContainer, aIndex);
   209     }
   211     let transactions = getChildItemsTransactions(aData.children);
   212     if (aData.dateAdded) {
   213       transactions.push(
   214         new PlacesEditItemDateAddedTransaction(null, aData.dateAdded)
   215       );
   216     }
   217     if (aData.lastModified) {
   218       transactions.push(
   219         new PlacesEditItemLastModifiedTransaction(null, aData.lastModified)
   220       );
   221     }
   223     let annos = [];
   224     if (aData.annos) {
   225       annos = aData.annos.filter(function (aAnno) {
   226         return this._copyableAnnotations.indexOf(aAnno.name) != -1;
   227       }, this);
   228     }
   230     return new PlacesCreateFolderTransaction(aData.title, aContainer, aIndex,
   231                                              annos, transactions);
   232   },
   234   /**
   235    * Gets a transaction for copying a live bookmark item from one container to
   236    * another.
   237    *
   238    * @param   aData
   239    *          Unwrapped live bookmarkmark data
   240    * @param   aContainer
   241    *          The container we are copying into
   242    * @param   aIndex
   243    *          The index in the destination container to insert the new items
   244    * @return A nsITransaction object that will perform the copy.
   245    *
   246    * @note Since a copy creates a completely new item, only some internal
   247    *       annotations are synced from the old one.
   248    * @see this._copyableAnnotations for the list of copyable annotations.
   249    */
   250   _getLivemarkCopyTransaction:
   251   function PUIU__getLivemarkCopyTransaction(aData, aContainer, aIndex)
   252   {
   253     if (!aData.livemark || !aData.annos) {
   254       throw new Error("node is not a livemark");
   255     }
   257     let feedURI, siteURI;
   258     let annos = [];
   259     if (aData.annos) {
   260       annos = aData.annos.filter(function (aAnno) {
   261         if (aAnno.name == PlacesUtils.LMANNO_FEEDURI) {
   262           feedURI = PlacesUtils._uri(aAnno.value);
   263         }
   264         else if (aAnno.name == PlacesUtils.LMANNO_SITEURI) {
   265           siteURI = PlacesUtils._uri(aAnno.value);
   266         }
   267         return this._copyableAnnotations.indexOf(aAnno.name) != -1
   268       }, this);
   269     }
   271     return new PlacesCreateLivemarkTransaction(feedURI, siteURI, aData.title,
   272                                                aContainer, aIndex, annos);
   273   },
   275   /**
   276    * Constructs a Transaction for the drop or paste of a blob of data into
   277    * a container.
   278    * @param   data
   279    *          The unwrapped data blob of dropped or pasted data.
   280    * @param   type
   281    *          The content type of the data
   282    * @param   container
   283    *          The container the data was dropped or pasted into
   284    * @param   index
   285    *          The index within the container the item was dropped or pasted at
   286    * @param   copy
   287    *          The drag action was copy, so don't move folders or links.
   288    * @returns An object implementing nsITransaction that can perform
   289    *          the move/insert.
   290    */
   291   makeTransaction:
   292   function PUIU_makeTransaction(data, type, container, index, copy)
   293   {
   294     switch (data.type) {
   295       case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER:
   296         if (copy) {
   297           return this._getFolderCopyTransaction(data, container, index);
   298         }
   300         // Otherwise move the item.
   301         return new PlacesMoveItemTransaction(data.id, container, index);
   302         break;
   303       case PlacesUtils.TYPE_X_MOZ_PLACE:
   304         if (copy || data.id == -1) { // Id is -1 if the place is not bookmarked.
   305           return this._getURIItemCopyTransaction(data, container, index);
   306         }
   308         // Otherwise move the item.
   309         return new PlacesMoveItemTransaction(data.id, container, index);
   310         break;
   311       case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR:
   312         if (copy) {
   313           // There is no data in a separator, so copying it just amounts to
   314           // inserting a new separator.
   315           return new PlacesCreateSeparatorTransaction(container, index);
   316         }
   318         // Otherwise move the item.
   319         return new PlacesMoveItemTransaction(data.id, container, index);
   320         break;
   321       default:
   322         if (type == PlacesUtils.TYPE_X_MOZ_URL ||
   323             type == PlacesUtils.TYPE_UNICODE ||
   324             type == this.TYPE_TAB_DROP) {
   325           let title = type != PlacesUtils.TYPE_UNICODE ? data.title
   326                                                        : data.uri;
   327           return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(data.uri),
   328                                                      container, index, title);
   329         }
   330     }
   331     return null;
   332   },
   334   /**
   335    * Shows the bookmark dialog corresponding to the specified info.
   336    *
   337    * @param aInfo
   338    *        Describes the item to be edited/added in the dialog.
   339    *        See documentation at the top of bookmarkProperties.js
   340    * @param aWindow
   341    *        Owner window for the new dialog.
   342    *
   343    * @see documentation at the top of bookmarkProperties.js
   344    * @return true if any transaction has been performed, false otherwise.
   345    */
   346   showBookmarkDialog:
   347   function PUIU_showBookmarkDialog(aInfo, aParentWindow) {
   348     // Preserve size attributes differently based on the fact the dialog has
   349     // a folder picker or not.  If the picker is visible, the dialog should
   350     // be resizable since it may not show enough content for the folders
   351     // hierarchy.
   352     let hasFolderPicker = !("hiddenRows" in aInfo) ||
   353                           aInfo.hiddenRows.indexOf("folderPicker") == -1;
   354     // Use a different chrome url, since this allows to persist different sizes,
   355     // based on resizability of the dialog.
   356     let dialogURL = hasFolderPicker ?
   357                     "chrome://browser/content/places/bookmarkProperties2.xul" :
   358                     "chrome://browser/content/places/bookmarkProperties.xul";
   360     let features =
   361       "centerscreen,chrome,modal,resizable=" + (hasFolderPicker ? "yes" : "no");
   363     aParentWindow.openDialog(dialogURL, "",  features, aInfo);
   364     return ("performed" in aInfo && aInfo.performed);
   365   },
   367   _getTopBrowserWin: function PUIU__getTopBrowserWin() {
   368     return Services.wm.getMostRecentWindow("navigator:browser");
   369   },
   371   /**
   372    * Returns the closet ancestor places view for the given DOM node
   373    * @param aNode
   374    *        a DOM node
   375    * @return the closet ancestor places view if exists, null otherwsie.
   376    */
   377   getViewForNode: function PUIU_getViewForNode(aNode) {
   378     let node = aNode;
   380     // The view for a <menu> of which its associated menupopup is a places
   381     // view, is the menupopup.
   382     if (node.localName == "menu" && !node._placesNode &&
   383         node.lastChild._placesView)
   384       return node.lastChild._placesView;
   386     while (node instanceof Ci.nsIDOMElement) {
   387       if (node._placesView)
   388         return node._placesView;
   389       if (node.localName == "tree" && node.getAttribute("type") == "places")
   390         return node;
   392       node = node.parentNode;
   393     }
   395     return null;
   396   },
   398   /**
   399    * By calling this before visiting an URL, the visit will be associated to a
   400    * TRANSITION_TYPED transition (if there is no a referrer).
   401    * This is used when visiting pages from the history menu, history sidebar,
   402    * url bar, url autocomplete results, and history searches from the places
   403    * organizer.  If this is not called visits will be marked as
   404    * TRANSITION_LINK.
   405    */
   406   markPageAsTyped: function PUIU_markPageAsTyped(aURL) {
   407     PlacesUtils.history.markPageAsTyped(this.createFixedURI(aURL));
   408   },
   410   /**
   411    * By calling this before visiting an URL, the visit will be associated to a
   412    * TRANSITION_BOOKMARK transition.
   413    * This is used when visiting pages from the bookmarks menu,
   414    * personal toolbar, and bookmarks from within the places organizer.
   415    * If this is not called visits will be marked as TRANSITION_LINK.
   416    */
   417   markPageAsFollowedBookmark: function PUIU_markPageAsFollowedBookmark(aURL) {
   418     PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL));
   419   },
   421   /**
   422    * By calling this before visiting an URL, any visit in frames will be
   423    * associated to a TRANSITION_FRAMED_LINK transition.
   424    * This is actually used to distinguish user-initiated visits in frames
   425    * so automatic visits can be correctly ignored.
   426    */
   427   markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) {
   428     PlacesUtils.history.markPageAsFollowedLink(this.createFixedURI(aURL));
   429   },
   431   /**
   432    * Allows opening of javascript/data URI only if the given node is
   433    * bookmarked (see bug 224521).
   434    * @param aURINode
   435    *        a URI node
   436    * @param aWindow
   437    *        a window on which a potential error alert is shown on.
   438    * @return true if it's safe to open the node in the browser, false otherwise.
   439    *
   440    */
   441   checkURLSecurity: function PUIU_checkURLSecurity(aURINode, aWindow) {
   442     if (PlacesUtils.nodeIsBookmark(aURINode))
   443       return true;
   445     var uri = PlacesUtils._uri(aURINode.uri);
   446     if (uri.schemeIs("javascript") || uri.schemeIs("data")) {
   447       const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
   448       var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
   449                            getService(Ci.nsIStringBundleService).
   450                            createBundle(BRANDING_BUNDLE_URI).
   451                            GetStringFromName("brandShortName");
   453       var errorStr = this.getString("load-js-data-url-error");
   454       Services.prompt.alert(aWindow, brandShortName, errorStr);
   455       return false;
   456     }
   457     return true;
   458   },
   460   /**
   461    * Get the description associated with a document, as specified in a <META>
   462    * element.
   463    * @param   doc
   464    *          A DOM Document to get a description for
   465    * @returns A description string if a META element was discovered with a
   466    *          "description" or "httpequiv" attribute, empty string otherwise.
   467    */
   468   getDescriptionFromDocument: function PUIU_getDescriptionFromDocument(doc) {
   469     var metaElements = doc.getElementsByTagName("META");
   470     for (var i = 0; i < metaElements.length; ++i) {
   471       if (metaElements[i].name.toLowerCase() == "description" ||
   472           metaElements[i].httpEquiv.toLowerCase() == "description") {
   473         return metaElements[i].content;
   474       }
   475     }
   476     return "";
   477   },
   479   /**
   480    * Retrieve the description of an item
   481    * @param aItemId
   482    *        item identifier
   483    * @returns the description of the given item, or an empty string if it is
   484    * not set.
   485    */
   486   getItemDescription: function PUIU_getItemDescription(aItemId) {
   487     if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO))
   488       return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO);
   489     return "";
   490   },
   492   /**
   493    * Gives the user a chance to cancel loading lots of tabs at once
   494    */
   495   _confirmOpenInTabs:
   496   function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) {
   497     const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
   498     var reallyOpen = true;
   500     if (Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) {
   501       if (numTabsToOpen >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
   502         // default to true: if it were false, we wouldn't get this far
   503         var warnOnOpen = { value: true };
   505         var messageKey = "tabs.openWarningMultipleBranded";
   506         var openKey = "tabs.openButtonMultiple";
   507         const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
   508         var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
   509                              getService(Ci.nsIStringBundleService).
   510                              createBundle(BRANDING_BUNDLE_URI).
   511                              GetStringFromName("brandShortName");
   513         var buttonPressed = Services.prompt.confirmEx(
   514           aWindow,
   515           this.getString("tabs.openWarningTitle"),
   516           this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]),
   517           (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
   518             (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1),
   519           this.getString(openKey), null, null,
   520           this.getFormattedString("tabs.openWarningPromptMeBranded",
   521                                   [brandShortName]),
   522           warnOnOpen
   523         );
   525         reallyOpen = (buttonPressed == 0);
   526         // don't set the pref unless they press OK and it's false
   527         if (reallyOpen && !warnOnOpen.value)
   528           Services.prefs.setBoolPref(WARN_ON_OPEN_PREF, false);
   529       }
   530     }
   532     return reallyOpen;
   533   },
   535   /** aItemsToOpen needs to be an array of objects of the form:
   536     * {uri: string, isBookmark: boolean}
   537     */
   538   _openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) {
   539     if (!aItemsToOpen.length)
   540       return;
   542     // Prefer the caller window if it's a browser window, otherwise use
   543     // the top browser window.
   544     var browserWindow = null;
   545     browserWindow =
   546       aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ?
   547       aWindow : this._getTopBrowserWin();
   549     var urls = [];
   550     let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow);
   551     for (let item of aItemsToOpen) {
   552       urls.push(item.uri);
   553       if (skipMarking) {
   554         continue;
   555       }
   557       if (item.isBookmark)
   558         this.markPageAsFollowedBookmark(item.uri);
   559       else
   560         this.markPageAsTyped(item.uri);
   561     }
   563     // whereToOpenLink doesn't return "window" when there's no browser window
   564     // open (Bug 630255).
   565     var where = browserWindow ?
   566                 browserWindow.whereToOpenLink(aEvent, false, true) : "window";
   567     if (where == "window") {
   568       // There is no browser window open, thus open a new one.
   569       var uriList = PlacesUtils.toISupportsString(urls.join("|"));
   570       var args = Cc["@mozilla.org/supports-array;1"].
   571                   createInstance(Ci.nsISupportsArray);
   572       args.AppendElement(uriList);      
   573       browserWindow = Services.ww.openWindow(aWindow,
   574                                              "chrome://browser/content/browser.xul",
   575                                              null, "chrome,dialog=no,all", args);
   576       return;
   577     }
   579     var loadInBackground = where == "tabshifted" ? true : false;
   580     // For consistency, we want all the bookmarks to open in new tabs, instead
   581     // of having one of them replace the currently focused tab.  Hence we call
   582     // loadTabs with aReplace set to false.
   583     browserWindow.gBrowser.loadTabs(urls, loadInBackground, false);
   584   },
   586   openContainerNodeInTabs:
   587   function PUIU_openContainerInTabs(aNode, aEvent, aView) {
   588     let window = aView.ownerWindow;
   590     let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
   591     if (!this._confirmOpenInTabs(urlsToOpen.length, window))
   592       return;
   594     this._openTabset(urlsToOpen, aEvent, window);
   595   },
   597   openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) {
   598     let window = aView.ownerWindow;
   600     let urlsToOpen = [];
   601     for (var i=0; i < aNodes.length; i++) {
   602       // Skip over separators and folders.
   603       if (PlacesUtils.nodeIsURI(aNodes[i]))
   604         urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])});
   605     }
   606     this._openTabset(urlsToOpen, aEvent, window);
   607   },
   609   /**
   610    * Loads the node's URL in the appropriate tab or window or as a web
   611    * panel given the user's preference specified by modifier keys tracked by a
   612    * DOM mouse/key event.
   613    * @param   aNode
   614    *          An uri result node.
   615    * @param   aEvent
   616    *          The DOM mouse/key event with modifier keys set that track the
   617    *          user's preferred destination window or tab.
   618    * @param   aView
   619    *          The controller associated with aNode.
   620    */
   621   openNodeWithEvent:
   622   function PUIU_openNodeWithEvent(aNode, aEvent, aView) {
   623     let window = aView.ownerWindow;
   624     this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window);
   625   },
   627   /**
   628    * Loads the node's URL in the appropriate tab or window or as a
   629    * web panel.
   630    * see also openUILinkIn
   631    */
   632   openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView) {
   633     let window = aView.ownerWindow;
   634     this._openNodeIn(aNode, aWhere, window);
   635   },
   637   _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow) {
   638     if (aNode && PlacesUtils.nodeIsURI(aNode) &&
   639         this.checkURLSecurity(aNode, aWindow)) {
   640       let isBookmark = PlacesUtils.nodeIsBookmark(aNode);
   642       if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
   643         if (isBookmark)
   644           this.markPageAsFollowedBookmark(aNode.uri);
   645         else
   646           this.markPageAsTyped(aNode.uri);
   647       }
   649       // Check whether the node is a bookmark which should be opened as
   650       // a web panel
   651       if (aWhere == "current" && isBookmark) {
   652         if (PlacesUtils.annotations
   653                        .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) {
   654           let browserWin = this._getTopBrowserWin();
   655           if (browserWin) {
   656             browserWin.openWebPanel(aNode.title, aNode.uri);
   657             return;
   658           }
   659         }
   660       }
   661       aWindow.openUILinkIn(aNode.uri, aWhere, {
   662         inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground")
   663       });
   664     }
   665   },
   667   /**
   668    * Helper for guessing scheme from an url string.
   669    * Used to avoid nsIURI overhead in frequently called UI functions.
   670    *
   671    * @param aUrlString the url to guess the scheme from.
   672    *
   673    * @return guessed scheme for this url string.
   674    *
   675    * @note this is not supposed be perfect, so use it only for UI purposes.
   676    */
   677   guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) {
   678     return aUrlString.substr(0, aUrlString.indexOf(":"));
   679   },
   681   getBestTitle: function PUIU_getBestTitle(aNode, aDoNotCutTitle) {
   682     var title;
   683     if (!aNode.title && PlacesUtils.nodeIsURI(aNode)) {
   684       // if node title is empty, try to set the label using host and filename
   685       // PlacesUtils._uri() will throw if aNode.uri is not a valid URI
   686       try {
   687         var uri = PlacesUtils._uri(aNode.uri);
   688         var host = uri.host;
   689         var fileName = uri.QueryInterface(Ci.nsIURL).fileName;
   690         // if fileName is empty, use path to distinguish labels
   691         if (aDoNotCutTitle) {
   692           title = host + uri.path;
   693         } else {
   694           title = host + (fileName ?
   695                            (host ? "/" + this.ellipsis + "/" : "") + fileName :
   696                            uri.path);
   697         }
   698       }
   699       catch (e) {
   700         // Use (no title) for non-standard URIs (data:, javascript:, ...)
   701         title = "";
   702       }
   703     }
   704     else
   705       title = aNode.title;
   707     return title || this.getString("noTitle");
   708   },
   710   get leftPaneQueries() {
   711     // build the map
   712     this.leftPaneFolderId;
   713     return this.leftPaneQueries;
   714   },
   716   // Get the folder id for the organizer left-pane folder.
   717   get leftPaneFolderId() {
   718     let leftPaneRoot = -1;
   719     let allBookmarksId;
   721     // Shortcuts to services.
   722     let bs = PlacesUtils.bookmarks;
   723     let as = PlacesUtils.annotations;
   725     // This is the list of the left pane queries.
   726     let queries = {
   727       "PlacesRoot": { title: "" },
   728       "History": { title: this.getString("OrganizerQueryHistory") },
   729       "Downloads": { title: this.getString("OrganizerQueryDownloads") },
   730       "Tags": { title: this.getString("OrganizerQueryTags") },
   731       "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") },
   732       "BookmarksToolbar":
   733         { title: null,
   734           concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"),
   735           concreteId: PlacesUtils.toolbarFolderId },
   736       "BookmarksMenu":
   737         { title: null,
   738           concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"),
   739           concreteId: PlacesUtils.bookmarksMenuFolderId },
   740       "UnfiledBookmarks":
   741         { title: null,
   742           concreteTitle: PlacesUtils.getString("UnsortedBookmarksFolderTitle"),
   743           concreteId: PlacesUtils.unfiledBookmarksFolderId },
   744     };
   745     // All queries but PlacesRoot.
   746     const EXPECTED_QUERY_COUNT = 7;
   748     // Removes an item and associated annotations, ignoring eventual errors.
   749     function safeRemoveItem(aItemId) {
   750       try {
   751         if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) &&
   752             !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) {
   753           // Some extension annotated their roots with our query annotation,
   754           // so we should not delete them.
   755           return;
   756         }
   757         // removeItemAnnotation does not check if item exists, nor the anno,
   758         // so this is safe to do.
   759         as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
   760         as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO);
   761         // This will throw if the annotation is an orphan.
   762         bs.removeItem(aItemId);
   763       }
   764       catch(e) { /* orphan anno */ }
   765     }
   767     // Returns true if item really exists, false otherwise.
   768     function itemExists(aItemId) {
   769       try {
   770         bs.getItemIndex(aItemId);
   771         return true;
   772       }
   773       catch(e) {
   774         return false;
   775       }
   776     }
   778     // Get all items marked as being the left pane folder.
   779     let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO);
   780     if (items.length > 1) {
   781       // Something went wrong, we cannot have more than one left pane folder,
   782       // remove all left pane folders and continue.  We will create a new one.
   783       items.forEach(safeRemoveItem);
   784     }
   785     else if (items.length == 1 && items[0] != -1) {
   786       leftPaneRoot = items[0];
   787       // Check that organizer left pane root is valid.
   788       let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO);
   789       if (version != this.ORGANIZER_LEFTPANE_VERSION ||
   790           !itemExists(leftPaneRoot)) {
   791         // Invalid root, we must rebuild the left pane.
   792         safeRemoveItem(leftPaneRoot);
   793         leftPaneRoot = -1;
   794       }
   795     }
   797     if (leftPaneRoot != -1) {
   798       // A valid left pane folder has been found.
   799       // Build the leftPaneQueries Map.  This is used to quickly access them,
   800       // associating a mnemonic name to the real item ids.
   801       delete this.leftPaneQueries;
   802       this.leftPaneQueries = {};
   804       let items = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO);
   805       // While looping through queries we will also check for their validity.
   806       let queriesCount = 0;
   807       let corrupt = false;
   808       for (let i = 0; i < items.length; i++) {
   809         let queryName = as.getItemAnnotation(items[i], this.ORGANIZER_QUERY_ANNO);
   811         // Some extension did use our annotation to decorate their items
   812         // with icons, so we should check only our elements, to avoid dataloss.
   813         if (!(queryName in queries))
   814           continue;
   816         let query = queries[queryName];
   817         query.itemId = items[i];
   819         if (!itemExists(query.itemId)) {
   820           // Orphan annotation, bail out and create a new left pane root.
   821           corrupt = true;
   822           break;
   823         }
   825         // Check that all queries have valid parents.
   826         let parentId = bs.getFolderIdForItem(query.itemId);
   827         if (items.indexOf(parentId) == -1 && parentId != leftPaneRoot) {
   828           // The parent is not part of the left pane, bail out and create a new
   829           // left pane root.
   830           corrupt = true;
   831           break;
   832         }
   834         // Titles could have been corrupted or the user could have changed his
   835         // locale.  Check title and eventually fix it.
   836         if (bs.getItemTitle(query.itemId) != query.title)
   837           bs.setItemTitle(query.itemId, query.title);
   838         if ("concreteId" in query) {
   839           if (bs.getItemTitle(query.concreteId) != query.concreteTitle)
   840             bs.setItemTitle(query.concreteId, query.concreteTitle);
   841         }
   843         // Add the query to our cache.
   844         this.leftPaneQueries[queryName] = query.itemId;
   845         queriesCount++;
   846       }
   848       // Note: it's not enough to just check for queriesCount, since we may
   849       // find an invalid query just after accounting for a sufficient number of
   850       // valid ones.  As well as we can't just rely on corrupt since we may find
   851       // less valid queries than expected.
   852       if (corrupt || queriesCount != EXPECTED_QUERY_COUNT) {
   853         // Queries number is wrong, so the left pane must be corrupt.
   854         // Note: we can't just remove the leftPaneRoot, because some query could
   855         // have a bad parent, so we have to remove all items one by one.
   856         items.forEach(safeRemoveItem);
   857         safeRemoveItem(leftPaneRoot);
   858       }
   859       else {
   860         // Everything is fine, return the current left pane folder.
   861         delete this.leftPaneFolderId;
   862         return this.leftPaneFolderId = leftPaneRoot;
   863       }
   864     }
   866     // Create a new left pane folder.
   867     var callback = {
   868       // Helper to create an organizer special query.
   869       create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) {
   870         let itemId = bs.insertBookmark(aParentId,
   871                                        PlacesUtils._uri(aQueryUrl),
   872                                        bs.DEFAULT_INDEX,
   873                                        queries[aQueryName].title);
   874         // Mark as special organizer query.
   875         as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName,
   876                              0, as.EXPIRE_NEVER);
   877         // We should never backup this, since it changes between profiles.
   878         as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
   879                              0, as.EXPIRE_NEVER);
   880         // Add to the queries map.
   881         PlacesUIUtils.leftPaneQueries[aQueryName] = itemId;
   882         return itemId;
   883       },
   885       // Helper to create an organizer special folder.
   886       create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) {
   887               // Left Pane Root Folder.
   888         let folderId = bs.createFolder(aParentId,
   889                                        queries[aFolderName].title,
   890                                        bs.DEFAULT_INDEX);
   891         // We should never backup this, since it changes between profiles.
   892         as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
   893                              0, as.EXPIRE_NEVER);
   894         // Disallow manipulating this folder within the organizer UI.
   895         bs.setFolderReadonly(folderId, true);
   897         if (aIsRoot) {
   898           // Mark as special left pane root.
   899           as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
   900                                PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
   901                                0, as.EXPIRE_NEVER);
   902         }
   903         else {
   904           // Mark as special organizer folder.
   905           as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName,
   906                            0, as.EXPIRE_NEVER);
   907           PlacesUIUtils.leftPaneQueries[aFolderName] = folderId;
   908         }
   909         return folderId;
   910       },
   912       runBatched: function CB_runBatched(aUserData) {
   913         delete PlacesUIUtils.leftPaneQueries;
   914         PlacesUIUtils.leftPaneQueries = { };
   916         // Left Pane Root Folder.
   917         leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true);
   919         // History Query.
   920         this.create_query("History", leftPaneRoot,
   921                           "place:type=" +
   922                           Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY +
   923                           "&sort=" +
   924                           Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
   926         // Downloads.
   927         this.create_query("Downloads", leftPaneRoot,
   928                           "place:transition=" +
   929                           Ci.nsINavHistoryService.TRANSITION_DOWNLOAD +
   930                           "&sort=" +
   931                           Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
   933         // Tags Query.
   934         this.create_query("Tags", leftPaneRoot,
   935                           "place:type=" +
   936                           Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
   937                           "&sort=" +
   938                           Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
   940         // All Bookmarks Folder.
   941         allBookmarksId = this.create_folder("AllBookmarks", leftPaneRoot, false);
   943         // All Bookmarks->Bookmarks Toolbar Query.
   944         this.create_query("BookmarksToolbar", allBookmarksId,
   945                           "place:folder=TOOLBAR");
   947         // All Bookmarks->Bookmarks Menu Query.
   948         this.create_query("BookmarksMenu", allBookmarksId,
   949                           "place:folder=BOOKMARKS_MENU");
   951         // All Bookmarks->Unfiled Bookmarks Query.
   952         this.create_query("UnfiledBookmarks", allBookmarksId,
   953                           "place:folder=UNFILED_BOOKMARKS");
   954       }
   955     };
   956     bs.runInBatchMode(callback, null);
   958     delete this.leftPaneFolderId;
   959     return this.leftPaneFolderId = leftPaneRoot;
   960   },
   962   /**
   963    * Get the folder id for the organizer left-pane folder.
   964    */
   965   get allBookmarksFolderId() {
   966     // ensure the left-pane root is initialized;
   967     this.leftPaneFolderId;
   968     delete this.allBookmarksFolderId;
   969     return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"];
   970   },
   972   /**
   973    * If an item is a left-pane query, returns the name of the query
   974    * or an empty string if not.
   975    *
   976    * @param aItemId id of a container
   977    * @returns the name of the query, or empty string if not a left-pane query
   978    */
   979   getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) {
   980     var queryName = "";
   981     // If the let pane hasn't been built, use the annotation service
   982     // directly, to avoid building the left pane too early.
   983     if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) {
   984       try {
   985         queryName = PlacesUtils.annotations.
   986                                 getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO);
   987       }
   988       catch (ex) {
   989         // doesn't have the annotation
   990         queryName = "";
   991       }
   992     }
   993     else {
   994       // If the left pane has already been built, use the name->id map
   995       // cached in PlacesUIUtils.
   996       for (let [name, id] in Iterator(this.leftPaneQueries)) {
   997         if (aItemId == id)
   998           queryName = name;
   999       }
  1001     return queryName;
  1002   },
  1004   shouldShowTabsFromOtherComputersMenuitem: function() {
  1005     // If Sync isn't configured yet, then don't show the menuitem.
  1006     return Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED &&
  1007            Weave.Svc.Prefs.get("firstSync", "") != "notReady";
  1008   },
  1010   shouldEnableTabsFromOtherComputersMenuitem: function() {
  1011     // The tabs engine might never be inited (if services.sync.registerEngines
  1012     // is modified), so make sure we avoid undefined errors.
  1013     return Weave.Service.isLoggedIn &&
  1014            Weave.Service.engineManager.get("tabs") &&
  1015            Weave.Service.engineManager.get("tabs").enabled;
  1016   },
  1017 };
  1019 XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF",
  1020                                    "@mozilla.org/rdf/rdf-service;1",
  1021                                    "nsIRDFService");
  1023 XPCOMUtils.defineLazyGetter(PlacesUIUtils, "localStore", function() {
  1024   return PlacesUIUtils.RDF.GetDataSource("rdf:local-store");
  1025 });
  1027 XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
  1028   return Services.prefs.getComplexValue("intl.ellipsis",
  1029                                         Ci.nsIPrefLocalizedString).data;
  1030 });
  1032 XPCOMUtils.defineLazyGetter(PlacesUIUtils, "useAsyncTransactions", function() {
  1033   try {
  1034     return Services.prefs.getBoolPref("browser.places.useAsyncTransactions");
  1036   catch(ex) { }
  1037   return false;
  1038 });
  1040 XPCOMUtils.defineLazyServiceGetter(this, "URIFixup",
  1041                                    "@mozilla.org/docshell/urifixup;1",
  1042                                    "nsIURIFixup");
  1044 XPCOMUtils.defineLazyGetter(this, "bundle", function() {
  1045   const PLACES_STRING_BUNDLE_URI =
  1046     "chrome://browser/locale/places/places.properties";
  1047   return Cc["@mozilla.org/intl/stringbundle;1"].
  1048          getService(Ci.nsIStringBundleService).
  1049          createBundle(PLACES_STRING_BUNDLE_URI);
  1050 });
  1052 XPCOMUtils.defineLazyServiceGetter(this, "focusManager",
  1053                                    "@mozilla.org/focus-manager;1",
  1054                                    "nsIFocusManager");
  1056 /**
  1057  * This is a compatibility shim for old PUIU.ptm users.
  1059  * If you're looking for transactions and writing new code using them, directly
  1060  * use the transactions objects exported by the PlacesUtils.jsm module.
  1062  * This object will be removed once enough users are converted to the new API.
  1063  */
  1064 XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() {
  1065   // Ensure PlacesUtils is imported in scope.
  1066   PlacesUtils;
  1068   return {
  1069     aggregateTransactions: function(aName, aTransactions)
  1070       new PlacesAggregatedTransaction(aName, aTransactions),
  1072     createFolder: function(aName, aContainer, aIndex, aAnnotations,
  1073                            aChildItemsTransactions)
  1074       new PlacesCreateFolderTransaction(aName, aContainer, aIndex, aAnnotations,
  1075                                         aChildItemsTransactions),
  1077     createItem: function(aURI, aContainer, aIndex, aTitle, aKeyword,
  1078                          aAnnotations, aChildTransactions)
  1079       new PlacesCreateBookmarkTransaction(aURI, aContainer, aIndex, aTitle,
  1080                                           aKeyword, aAnnotations,
  1081                                           aChildTransactions),
  1083     createSeparator: function(aContainer, aIndex)
  1084       new PlacesCreateSeparatorTransaction(aContainer, aIndex),
  1086     createLivemark: function(aFeedURI, aSiteURI, aName, aContainer, aIndex,
  1087                              aAnnotations)
  1088       new PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aName, aContainer,
  1089                                           aIndex, aAnnotations),
  1091     moveItem: function(aItemId, aNewContainer, aNewIndex)
  1092       new PlacesMoveItemTransaction(aItemId, aNewContainer, aNewIndex),
  1094     removeItem: function(aItemId)
  1095       new PlacesRemoveItemTransaction(aItemId),
  1097     editItemTitle: function(aItemId, aNewTitle)
  1098       new PlacesEditItemTitleTransaction(aItemId, aNewTitle),
  1100     editBookmarkURI: function(aItemId, aNewURI)
  1101       new PlacesEditBookmarkURITransaction(aItemId, aNewURI),
  1103     setItemAnnotation: function(aItemId, aAnnotationObject)
  1104       new PlacesSetItemAnnotationTransaction(aItemId, aAnnotationObject),
  1106     setPageAnnotation: function(aURI, aAnnotationObject)
  1107       new PlacesSetPageAnnotationTransaction(aURI, aAnnotationObject),
  1109     editBookmarkKeyword: function(aItemId, aNewKeyword)
  1110       new PlacesEditBookmarkKeywordTransaction(aItemId, aNewKeyword),
  1112     editBookmarkPostData: function(aItemId, aPostData)
  1113       new PlacesEditBookmarkPostDataTransaction(aItemId, aPostData),
  1115     editLivemarkSiteURI: function(aLivemarkId, aSiteURI)
  1116       new PlacesEditLivemarkSiteURITransaction(aLivemarkId, aSiteURI),
  1118     editLivemarkFeedURI: function(aLivemarkId, aFeedURI)
  1119       new PlacesEditLivemarkFeedURITransaction(aLivemarkId, aFeedURI),
  1121     editItemDateAdded: function(aItemId, aNewDateAdded)
  1122       new PlacesEditItemDateAddedTransaction(aItemId, aNewDateAdded),
  1124     editItemLastModified: function(aItemId, aNewLastModified)
  1125       new PlacesEditItemLastModifiedTransaction(aItemId, aNewLastModified),
  1127     sortFolderByName: function(aFolderId)
  1128       new PlacesSortFolderByNameTransaction(aFolderId),
  1130     tagURI: function(aURI, aTags)
  1131       new PlacesTagURITransaction(aURI, aTags),
  1133     untagURI: function(aURI, aTags)
  1134       new PlacesUntagURITransaction(aURI, aTags),
  1136     /**
  1137      * Transaction for setting/unsetting Load-in-sidebar annotation.
  1139      * @param aBookmarkId
  1140      *        id of the bookmark where to set Load-in-sidebar annotation.
  1141      * @param aLoadInSidebar
  1142      *        boolean value.
  1143      * @returns nsITransaction object.
  1144      */
  1145     setLoadInSidebar: function(aItemId, aLoadInSidebar)
  1147       let annoObj = { name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
  1148                       type: Ci.nsIAnnotationService.TYPE_INT32,
  1149                       flags: 0,
  1150                       value: aLoadInSidebar,
  1151                       expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
  1152       return new PlacesSetItemAnnotationTransaction(aItemId, annoObj);
  1153     },
  1155    /**
  1156     * Transaction for editing the description of a bookmark or a folder.
  1158     * @param aItemId
  1159     *        id of the item to edit.
  1160     * @param aDescription
  1161     *        new description.
  1162     * @returns nsITransaction object.
  1163     */
  1164     editItemDescription: function(aItemId, aDescription)
  1166       let annoObj = { name: PlacesUIUtils.DESCRIPTION_ANNO,
  1167                       type: Ci.nsIAnnotationService.TYPE_STRING,
  1168                       flags: 0,
  1169                       value: aDescription,
  1170                       expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
  1171       return new PlacesSetItemAnnotationTransaction(aItemId, annoObj);
  1172     },
  1174     ////////////////////////////////////////////////////////////////////////////
  1175     //// nsITransactionManager forwarders.
  1177     beginBatch: function()
  1178       PlacesUtils.transactionManager.beginBatch(null),
  1180     endBatch: function()
  1181       PlacesUtils.transactionManager.endBatch(false),
  1183     doTransaction: function(txn)
  1184       PlacesUtils.transactionManager.doTransaction(txn),
  1186     undoTransaction: function()
  1187       PlacesUtils.transactionManager.undoTransaction(),
  1189     redoTransaction: function()
  1190       PlacesUtils.transactionManager.redoTransaction(),
  1192     get numberOfUndoItems()
  1193       PlacesUtils.transactionManager.numberOfUndoItems,
  1194     get numberOfRedoItems()
  1195       PlacesUtils.transactionManager.numberOfRedoItems,
  1196     get maxTransactionCount()
  1197       PlacesUtils.transactionManager.maxTransactionCount,
  1198     set maxTransactionCount(val)
  1199       PlacesUtils.transactionManager.maxTransactionCount = val,
  1201     clear: function()
  1202       PlacesUtils.transactionManager.clear(),
  1204     peekUndoStack: function()
  1205       PlacesUtils.transactionManager.peekUndoStack(),
  1207     peekRedoStack: function()
  1208       PlacesUtils.transactionManager.peekRedoStack(),
  1210     getUndoStack: function()
  1211       PlacesUtils.transactionManager.getUndoStack(),
  1213     getRedoStack: function()
  1214       PlacesUtils.transactionManager.getRedoStack(),
  1216     AddListener: function(aListener)
  1217       PlacesUtils.transactionManager.AddListener(aListener),
  1219     RemoveListener: function(aListener)
  1220       PlacesUtils.transactionManager.RemoveListener(aListener)
  1222 });

mercurial