browser/components/places/content/bookmarkProperties.js

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 /**
     7  * The panel is initialized based on data given in the js object passed
     8  * as window.arguments[0]. The object must have the following fields set:
     9  *   @ action (String). Possible values:
    10  *     - "add" - for adding a new item.
    11  *       @ type (String). Possible values:
    12  *         - "bookmark"
    13  *           @ loadBookmarkInSidebar - optional, the default state for the
    14  *             "Load this bookmark in the sidebar" field.
    15  *         - "folder"
    16  *           @ URIList (Array of nsIURI objects) - optional, list of uris to
    17  *             be bookmarked under the new folder.
    18  *         - "livemark"
    19  *       @ uri (nsIURI object) - optional, the default uri for the new item.
    20  *         The property is not used for the "folder with items" type.
    21  *       @ title (String) - optional, the default title for the new item.
    22  *       @ description (String) - optional, the default description for the new
    23  *         item.
    24  *       @ defaultInsertionPoint (InsertionPoint JS object) - optional, the
    25  *         default insertion point for the new item.
    26  *       @ keyword (String) - optional, the default keyword for the new item.
    27  *       @ postData (String) - optional, POST data to accompany the keyword.
    28  *       @ charSet (String) - optional, character-set to accompany the keyword.
    29  *      Notes:
    30  *        1) If |uri| is set for a bookmark/livemark item and |title| isn't,
    31  *           the dialog will query the history tables for the title associated
    32  *           with the given uri. If the dialog is set to adding a folder with
    33  *           bookmark items under it (see URIList), a default static title is
    34  *           used ("[Folder Name]").
    35  *        2) The index field of the default insertion point is ignored if
    36  *           the folder picker is shown.
    37  *     - "edit" - for editing a bookmark item or a folder.
    38  *       @ type (String). Possible values:
    39  *         - "bookmark"
    40  *           @ itemId (Integer) - the id of the bookmark item.
    41  *         - "folder" (also applies to livemarks)
    42  *           @ itemId (Integer) - the id of the folder.
    43  *   @ hiddenRows (Strings array) - optional, list of rows to be hidden
    44  *     regardless of the item edited or added by the dialog.
    45  *     Possible values:
    46  *     - "title"
    47  *     - "location"
    48  *     - "description"
    49  *     - "keyword"
    50  *     - "tags"
    51  *     - "loadInSidebar"
    52  *     - "feedLocation"
    53  *     - "siteLocation"
    54  *     - "folderPicker" - hides both the tree and the menu.
    55  *   @ readOnly (Boolean) - optional, states if the panel should be read-only
    56  *
    57  * window.arguments[0].performed is set to true if any transaction has
    58  * been performed by the dialog.
    59  */
    61 Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
    62 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
    63                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
    65 const BOOKMARK_ITEM = 0;
    66 const BOOKMARK_FOLDER = 1;
    67 const LIVEMARK_CONTAINER = 2;
    69 const ACTION_EDIT = 0;
    70 const ACTION_ADD = 1;
    72 var BookmarkPropertiesPanel = {
    74   /** UI Text Strings */
    75   __strings: null,
    76   get _strings() {
    77     if (!this.__strings) {
    78       this.__strings = document.getElementById("stringBundle");
    79     }
    80     return this.__strings;
    81   },
    83   _action: null,
    84   _itemType: null,
    85   _itemId: -1,
    86   _uri: null,
    87   _loadInSidebar: false,
    88   _title: "",
    89   _description: "",
    90   _URIs: [],
    91   _keyword: "",
    92   _postData: null,
    93   _charSet: "",
    94   _feedURI: null,
    95   _siteURI: null,
    97   _defaultInsertionPoint: null,
    98   _hiddenRows: [],
    99   _batching: false,
   100   _readOnly: false,
   102   /**
   103    * This method returns the correct label for the dialog's "accept"
   104    * button based on the variant of the dialog.
   105    */
   106   _getAcceptLabel: function BPP__getAcceptLabel() {
   107     if (this._action == ACTION_ADD) {
   108       if (this._URIs.length)
   109         return this._strings.getString("dialogAcceptLabelAddMulti");
   111       if (this._itemType == LIVEMARK_CONTAINER)
   112         return this._strings.getString("dialogAcceptLabelAddLivemark");
   114       if (this._dummyItem || this._loadInSidebar)
   115         return this._strings.getString("dialogAcceptLabelAddItem");
   117       return this._strings.getString("dialogAcceptLabelSaveItem");
   118     }
   119     return this._strings.getString("dialogAcceptLabelEdit");
   120   },
   122   /**
   123    * This method returns the correct title for the current variant
   124    * of this dialog.
   125    */
   126   _getDialogTitle: function BPP__getDialogTitle() {
   127     if (this._action == ACTION_ADD) {
   128       if (this._itemType == BOOKMARK_ITEM)
   129         return this._strings.getString("dialogTitleAddBookmark");
   130       if (this._itemType == LIVEMARK_CONTAINER)
   131         return this._strings.getString("dialogTitleAddLivemark");
   133       // add folder
   134       NS_ASSERT(this._itemType == BOOKMARK_FOLDER, "Unknown item type");
   135       if (this._URIs.length)
   136         return this._strings.getString("dialogTitleAddMulti");
   138       return this._strings.getString("dialogTitleAddFolder");
   139     }
   140     if (this._action == ACTION_EDIT) {
   141       return this._strings.getFormattedString("dialogTitleEdit", [this._title]);
   142     }
   143     return "";
   144   },
   146   /**
   147    * Determines the initial data for the item edited or added by this dialog
   148    */
   149   _determineItemInfo: function BPP__determineItemInfo() {
   150     var dialogInfo = window.arguments[0];
   151     this._action = dialogInfo.action == "add" ? ACTION_ADD : ACTION_EDIT;
   152     this._hiddenRows = dialogInfo.hiddenRows ? dialogInfo.hiddenRows : [];
   153     if (this._action == ACTION_ADD) {
   154       NS_ASSERT("type" in dialogInfo, "missing type property for add action");
   156       if ("title" in dialogInfo)
   157         this._title = dialogInfo.title;
   159       if ("defaultInsertionPoint" in dialogInfo) {
   160         this._defaultInsertionPoint = dialogInfo.defaultInsertionPoint;
   161       }
   162       else
   163         this._defaultInsertionPoint =
   164           new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
   165                              PlacesUtils.bookmarks.DEFAULT_INDEX,
   166                              Ci.nsITreeView.DROP_ON);
   168       switch (dialogInfo.type) {
   169         case "bookmark":
   170           this._itemType = BOOKMARK_ITEM;
   171           if ("uri" in dialogInfo) {
   172             NS_ASSERT(dialogInfo.uri instanceof Ci.nsIURI,
   173                       "uri property should be a uri object");
   174             this._uri = dialogInfo.uri;
   175             if (typeof(this._title) != "string") {
   176               this._title = this._getURITitleFromHistory(this._uri) ||
   177                             this._uri.spec;
   178             }
   179           }
   180           else {
   181             this._uri = PlacesUtils._uri("about:blank");
   182             this._title = this._strings.getString("newBookmarkDefault");
   183             this._dummyItem = true;
   184           }
   186           if ("loadBookmarkInSidebar" in dialogInfo)
   187             this._loadInSidebar = dialogInfo.loadBookmarkInSidebar;
   189           if ("keyword" in dialogInfo) {
   190             this._keyword = dialogInfo.keyword;
   191             this._isAddKeywordDialog = true;
   192             if ("postData" in dialogInfo)
   193               this._postData = dialogInfo.postData;
   194             if ("charSet" in dialogInfo)
   195               this._charSet = dialogInfo.charSet;
   196           }
   197           break;
   199         case "folder":
   200           this._itemType = BOOKMARK_FOLDER;
   201           if (!this._title) {
   202             if ("URIList" in dialogInfo) {
   203               this._title = this._strings.getString("bookmarkAllTabsDefault");
   204               this._URIs = dialogInfo.URIList;
   205             }
   206             else
   207               this._title = this._strings.getString("newFolderDefault");
   208               this._dummyItem = true;
   209           }
   210           break;
   212         case "livemark":
   213           this._itemType = LIVEMARK_CONTAINER;
   214           if ("feedURI" in dialogInfo)
   215             this._feedURI = dialogInfo.feedURI;
   216           if ("siteURI" in dialogInfo)
   217             this._siteURI = dialogInfo.siteURI;
   219           if (!this._title) {
   220             if (this._feedURI) {
   221               this._title = this._getURITitleFromHistory(this._feedURI) ||
   222                             this._feedURI.spec;
   223             }
   224             else
   225               this._title = this._strings.getString("newLivemarkDefault");
   226           }
   227       }
   229       if ("description" in dialogInfo)
   230         this._description = dialogInfo.description;
   231     }
   232     else { // edit
   233       NS_ASSERT("itemId" in dialogInfo);
   234       this._itemId = dialogInfo.itemId;
   235       this._title = PlacesUtils.bookmarks.getItemTitle(this._itemId);
   236       this._readOnly = !!dialogInfo.readOnly;
   238       switch (dialogInfo.type) {
   239         case "bookmark":
   240           this._itemType = BOOKMARK_ITEM;
   242           this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
   243           // keyword
   244           this._keyword = PlacesUtils.bookmarks
   245                                      .getKeywordForBookmark(this._itemId);
   246           // Load In Sidebar
   247           this._loadInSidebar = PlacesUtils.annotations
   248                                            .itemHasAnnotation(this._itemId,
   249                                                               PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
   250           break;
   252         case "folder":
   253           this._itemType = BOOKMARK_FOLDER;
   254           PlacesUtils.livemarks.getLivemark({ id: this._itemId })
   255             .then(aLivemark => {
   256               this._itemType = LIVEMARK_CONTAINER;
   257               this._feedURI = aLivemark.feedURI;
   258               this._siteURI = aLivemark.siteURI;
   259               this._fillEditProperties();
   261               let acceptButton = document.documentElement.getButton("accept");
   262               acceptButton.disabled = !this._inputIsValid();
   264               let newHeight = window.outerHeight +
   265                               this._element("descriptionField").boxObject.height;
   266               window.resizeTo(window.outerWidth, newHeight);
   267             }, () => undefined);
   269           break;
   270       }
   272       // Description
   273       if (PlacesUtils.annotations
   274                      .itemHasAnnotation(this._itemId, PlacesUIUtils.DESCRIPTION_ANNO)) {
   275         this._description = PlacesUtils.annotations
   276                                        .getItemAnnotation(this._itemId,
   277                                                           PlacesUIUtils.DESCRIPTION_ANNO);
   278       }
   279     }
   280   },
   282   /**
   283    * This method returns the title string corresponding to a given URI.
   284    * If none is available from the bookmark service (probably because
   285    * the given URI doesn't appear in bookmarks or history), we synthesize
   286    * a title from the first 100 characters of the URI.
   287    *
   288    * @param aURI
   289    *        nsIURI object for which we want the title
   290    *
   291    * @returns a title string
   292    */
   293   _getURITitleFromHistory: function BPP__getURITitleFromHistory(aURI) {
   294     NS_ASSERT(aURI instanceof Ci.nsIURI);
   296     // get the title from History
   297     return PlacesUtils.history.getPageTitle(aURI);
   298   },
   300   /**
   301    * This method should be called by the onload of the Bookmark Properties
   302    * dialog to initialize the state of the panel.
   303    */
   304   onDialogLoad: function BPP_onDialogLoad() {
   305     this._determineItemInfo();
   307     document.title = this._getDialogTitle();
   308     var acceptButton = document.documentElement.getButton("accept");
   309     acceptButton.label = this._getAcceptLabel();
   311     this._beginBatch();
   313     switch (this._action) {
   314       case ACTION_EDIT:
   315         this._fillEditProperties();
   316         acceptButton.disabled = this._readOnly;
   317         break;
   318       case ACTION_ADD:
   319         this._fillAddProperties();
   320         // if this is an uri related dialog disable accept button until
   321         // the user fills an uri value.
   322         if (this._itemType == BOOKMARK_ITEM)
   323           acceptButton.disabled = !this._inputIsValid();
   324         break;
   325     }
   327     // When collapsible elements change their collapsed attribute we must
   328     // resize the dialog.
   329     // sizeToContent is not usable due to bug 90276, so we'll use resizeTo
   330     // instead and cache the element size. See WSucks in the legacy
   331     // UI code (addBookmark2.js).
   332     if (!this._element("tagsRow").collapsed) {
   333       this._element("tagsSelectorRow")
   334           .addEventListener("DOMAttrModified", this, false);
   335     }
   336     if (!this._element("folderRow").collapsed) {
   337       this._element("folderTreeRow")
   338           .addEventListener("DOMAttrModified", this, false);
   339     }
   341     if (!this._readOnly) {
   342       // Listen on uri fields to enable accept button if input is valid
   343       if (this._itemType == BOOKMARK_ITEM) {
   344         this._element("locationField")
   345             .addEventListener("input", this, false);
   346         if (this._isAddKeywordDialog) {
   347           this._element("keywordField")
   348               .addEventListener("input", this, false);
   349         }
   350       }
   351       else if (this._itemType == LIVEMARK_CONTAINER) {
   352         this._element("feedLocationField")
   353             .addEventListener("input", this, false);
   354         this._element("siteLocationField")
   355             .addEventListener("input", this, false);
   356       }
   357     }
   359     window.sizeToContent();
   360   },
   362   // nsIDOMEventListener
   363   _elementsHeight: [],
   364   handleEvent: function BPP_handleEvent(aEvent) {
   365     var target = aEvent.target;
   366     switch (aEvent.type) {
   367       case "input":
   368         if (target.id == "editBMPanel_locationField" ||
   369             target.id == "editBMPanel_feedLocationField" ||
   370             target.id == "editBMPanel_siteLocationField" ||
   371             target.id == "editBMPanel_keywordField") {
   372           // Check uri fields to enable accept button if input is valid
   373           document.documentElement
   374                   .getButton("accept").disabled = !this._inputIsValid();
   375         }
   376         break;
   378       case "DOMAttrModified":
   379         // this is called when collapsing a node, but also its direct children,
   380         // we only need to resize when the original node changes.
   381         if ((target.id == "editBMPanel_tagsSelectorRow" ||
   382              target.id == "editBMPanel_folderTreeRow") &&
   383             aEvent.attrName == "collapsed" &&
   384             target == aEvent.originalTarget) {
   385           var id = target.id;
   386           var newHeight = window.outerHeight;
   387           if (aEvent.newValue) // is collapsed
   388             newHeight -= this._elementsHeight[id];
   389           else {
   390             this._elementsHeight[id] = target.boxObject.height;
   391             newHeight += this._elementsHeight[id];
   392           }
   394           window.resizeTo(window.outerWidth, newHeight);
   395         }
   396         break;
   397     }
   398   },
   400   _beginBatch: function BPP__beginBatch() {
   401     if (this._batching)
   402       return;
   404     PlacesUtils.transactionManager.beginBatch(null);
   405     this._batching = true;
   406   },
   408   _endBatch: function BPP__endBatch() {
   409     if (!this._batching)
   410       return;
   412     PlacesUtils.transactionManager.endBatch(false);
   413     this._batching = false;
   414   },
   416   _fillEditProperties: function BPP__fillEditProperties() {
   417     gEditItemOverlay.initPanel(this._itemId,
   418                                { hiddenRows: this._hiddenRows,
   419                                  forceReadOnly: this._readOnly });
   420   },
   422   _fillAddProperties: function BPP__fillAddProperties() {
   423     this._createNewItem();
   424     // Edit the new item
   425     gEditItemOverlay.initPanel(this._itemId,
   426                                { hiddenRows: this._hiddenRows });
   427     // Empty location field if the uri is about:blank, this way inserting a new
   428     // url will be easier for the user, Accept button will be automatically
   429     // disabled by the input listener until the user fills the field.
   430     var locationField = this._element("locationField");
   431     if (locationField.value == "about:blank")
   432       locationField.value = "";
   433   },
   435   // nsISupports
   436   QueryInterface: function BPP_QueryInterface(aIID) {
   437     if (aIID.equals(Ci.nsIDOMEventListener) ||
   438         aIID.equals(Ci.nsISupports))
   439       return this;
   441     throw Cr.NS_NOINTERFACE;
   442   },
   444   _element: function BPP__element(aID) {
   445     return document.getElementById("editBMPanel_" + aID);
   446   },
   448   onDialogUnload: function BPP_onDialogUnload() {
   449     // gEditItemOverlay does not exist anymore here, so don't rely on it.
   450     // Calling removeEventListener with arguments which do not identify any
   451     // currently registered EventListener on the EventTarget has no effect.
   452     this._element("tagsSelectorRow")
   453         .removeEventListener("DOMAttrModified", this, false);
   454     this._element("folderTreeRow")
   455         .removeEventListener("DOMAttrModified", this, false);
   456     this._element("locationField")
   457         .removeEventListener("input", this, false);
   458     this._element("feedLocationField")
   459         .removeEventListener("input", this, false);
   460     this._element("siteLocationField")
   461         .removeEventListener("input", this, false);
   462   },
   464   onDialogAccept: function BPP_onDialogAccept() {
   465     // We must blur current focused element to save its changes correctly
   466     document.commandDispatcher.focusedElement.blur();
   467     // The order here is important! We have to uninit the panel first, otherwise
   468     // late changes could force it to commit more transactions.
   469     gEditItemOverlay.uninitPanel(true);
   470     gEditItemOverlay = null;
   471     this._endBatch();
   472     window.arguments[0].performed = true;
   473   },
   475   onDialogCancel: function BPP_onDialogCancel() {
   476     // The order here is important! We have to uninit the panel first, otherwise
   477     // changes done as part of Undo may change the panel contents and by
   478     // that force it to commit more transactions.
   479     gEditItemOverlay.uninitPanel(true);
   480     gEditItemOverlay = null;
   481     this._endBatch();
   482     PlacesUtils.transactionManager.undoTransaction();
   483     window.arguments[0].performed = false;
   484   },
   486   /**
   487    * This method checks to see if the input fields are in a valid state.
   488    *
   489    * @returns  true if the input is valid, false otherwise
   490    */
   491   _inputIsValid: function BPP__inputIsValid() {
   492     if (this._itemType == BOOKMARK_ITEM &&
   493         !this._containsValidURI("locationField"))
   494       return false;
   495     if (this._isAddKeywordDialog && !this._element("keywordField").value.length)
   496       return false;
   498     return true;
   499   },
   501   /**
   502    * Determines whether the XUL textbox with the given ID contains a
   503    * string that can be converted into an nsIURI.
   504    *
   505    * @param aTextboxID
   506    *        the ID of the textbox element whose contents we'll test
   507    *
   508    * @returns true if the textbox contains a valid URI string, false otherwise
   509    */
   510   _containsValidURI: function BPP__containsValidURI(aTextboxID) {
   511     try {
   512       var value = this._element(aTextboxID).value;
   513       if (value) {
   514         PlacesUIUtils.createFixedURI(value);
   515         return true;
   516       }
   517     } catch (e) { }
   518     return false;
   519   },
   521   /**
   522    * [New Item Mode] Get the insertion point details for the new item, given
   523    * dialog state and opening arguments.
   524    *
   525    * The container-identifier and insertion-index are returned separately in
   526    * the form of [containerIdentifier, insertionIndex]
   527    */
   528   _getInsertionPointDetails: function BPP__getInsertionPointDetails() {
   529     var containerId = this._defaultInsertionPoint.itemId;
   530     var indexInContainer = this._defaultInsertionPoint.index;
   532     return [containerId, indexInContainer];
   533   },
   535   /**
   536    * Returns a transaction for creating a new bookmark item representing the
   537    * various fields and opening arguments of the dialog.
   538    */
   539   _getCreateNewBookmarkTransaction:
   540   function BPP__getCreateNewBookmarkTransaction(aContainer, aIndex) {
   541     var annotations = [];
   542     var childTransactions = [];
   544     if (this._description) {
   545       let annoObj = { name   : PlacesUIUtils.DESCRIPTION_ANNO,
   546                       type   : Ci.nsIAnnotationService.TYPE_STRING,
   547                       flags  : 0,
   548                       value  : this._description,
   549                       expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
   550       let editItemTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj);
   551       childTransactions.push(editItemTxn);
   552     }
   554     if (this._loadInSidebar) {
   555       let annoObj = { name   : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
   556                       value  : true };
   557       let setLoadTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj);
   558       childTransactions.push(setLoadTxn);
   559     }
   561     if (this._postData) {
   562       let postDataTxn = new PlacesEditBookmarkPostDataTransaction(-1, this._postData);
   563       childTransactions.push(postDataTxn);
   564     }
   566     //XXX TODO: this should be in a transaction!
   567     if (this._charSet && !PrivateBrowsingUtils.isWindowPrivate(window))
   568       PlacesUtils.setCharsetForURI(this._uri, this._charSet);
   570     let createTxn = new PlacesCreateBookmarkTransaction(this._uri,
   571                                                         aContainer,
   572                                                         aIndex,
   573                                                         this._title,
   574                                                         this._keyword,
   575                                                         annotations,
   576                                                         childTransactions);
   578     return new PlacesAggregatedTransaction(this._getDialogTitle(),
   579                                            [createTxn]);
   580   },
   582   /**
   583    * Returns a childItems-transactions array representing the URIList with
   584    * which the dialog has been opened.
   585    */
   586   _getTransactionsForURIList: function BPP__getTransactionsForURIList() {
   587     var transactions = [];
   588     for (var i = 0; i < this._URIs.length; ++i) {
   589       var uri = this._URIs[i];
   590       var title = this._getURITitleFromHistory(uri);
   591       var createTxn = new PlacesCreateBookmarkTransaction(uri, -1, 
   592                                                           PlacesUtils.bookmarks.DEFAULT_INDEX,
   593                                                           title);
   594       transactions.push(createTxn);
   595     }
   596     return transactions; 
   597   },
   599   /**
   600    * Returns a transaction for creating a new folder item representing the
   601    * various fields and opening arguments of the dialog.
   602    */
   603   _getCreateNewFolderTransaction:
   604   function BPP__getCreateNewFolderTransaction(aContainer, aIndex) {
   605     var annotations = [];
   606     var childItemsTransactions;
   607     if (this._URIs.length)
   608       childItemsTransactions = this._getTransactionsForURIList();
   610     if (this._description)
   611       annotations.push(this._getDescriptionAnnotation(this._description));
   613     return new PlacesCreateFolderTransaction(this._title, aContainer,
   614                                              aIndex, annotations,
   615                                              childItemsTransactions);
   616   },
   618   /**
   619    * Returns a transaction for creating a new live-bookmark item representing
   620    * the various fields and opening arguments of the dialog.
   621    */
   622   _getCreateNewLivemarkTransaction:
   623   function BPP__getCreateNewLivemarkTransaction(aContainer, aIndex) {
   624     return new PlacesCreateLivemarkTransaction(this._feedURI, this._siteURI,
   625                                                this._title,
   626                                                aContainer, aIndex);
   627   },
   629   /**
   630    * Dialog-accept code-path for creating a new item (any type)
   631    */
   632   _createNewItem: function BPP__getCreateItemTransaction() {
   633     var [container, index] = this._getInsertionPointDetails();
   634     var txn;
   636     switch (this._itemType) {
   637       case BOOKMARK_FOLDER:
   638         txn = this._getCreateNewFolderTransaction(container, index);
   639         break;
   640       case LIVEMARK_CONTAINER:
   641         txn = this._getCreateNewLivemarkTransaction(container, index);
   642         break;      
   643       default: // BOOKMARK_ITEM
   644         txn = this._getCreateNewBookmarkTransaction(container, index);
   645     }
   647     PlacesUtils.transactionManager.doTransaction(txn);
   648     this._itemId = PlacesUtils.bookmarks.getIdForItemAt(container, index);
   649   }
   650 };

mercurial