diff options
Diffstat (limited to 'browser/components/places/content/bookmarkProperties.js')
-rw-r--r-- | browser/components/places/content/bookmarkProperties.js | 693 |
1 files changed, 0 insertions, 693 deletions
diff --git a/browser/components/places/content/bookmarkProperties.js b/browser/components/places/content/bookmarkProperties.js deleted file mode 100644 index afcf65736..000000000 --- a/browser/components/places/content/bookmarkProperties.js +++ /dev/null @@ -1,693 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ - -/** - * The panel is initialized based on data given in the js object passed - * as window.arguments[0]. The object must have the following fields set: - * @ action (String). Possible values: - * - "add" - for adding a new item. - * @ type (String). Possible values: - * - "bookmark" - * @ loadBookmarkInSidebar - optional, the default state for the - * "Load this bookmark in the sidebar" field. - * - "folder" - * @ URIList (Array of nsIURI objects) - optional, list of uris to - * be bookmarked under the new folder. - * - "livemark" - * @ uri (nsIURI object) - optional, the default uri for the new item. - * The property is not used for the "folder with items" type. - * @ title (String) - optional, the default title for the new item. - * @ description (String) - optional, the default description for the new - * item. - * @ defaultInsertionPoint (InsertionPoint JS object) - optional, the - * default insertion point for the new item. - * @ keyword (String) - optional, the default keyword for the new item. - * @ postData (String) - optional, POST data to accompany the keyword. - * @ charSet (String) - optional, character-set to accompany the keyword. - * Notes: - * 1) If |uri| is set for a bookmark/livemark item and |title| isn't, - * the dialog will query the history tables for the title associated - * with the given uri. If the dialog is set to adding a folder with - * bookmark items under it (see URIList), a default static title is - * used ("[Folder Name]"). - * 2) The index field of the default insertion point is ignored if - * the folder picker is shown. - * - "edit" - for editing a bookmark item or a folder. - * @ type (String). Possible values: - * - "bookmark" - * @ node (an nsINavHistoryResultNode object) - a node representing - * the bookmark. - * - "folder" (also applies to livemarks) - * @ node (an nsINavHistoryResultNode object) - a node representing - * the folder. - * @ hiddenRows (Strings array) - optional, list of rows to be hidden - * regardless of the item edited or added by the dialog. - * Possible values: - * - "title" - * - "location" - * - "description" - * - "keyword" - * - "tags" - * - "loadInSidebar" - * - "folderPicker" - hides both the tree and the menu. - * - * window.arguments[0].performed is set to true if any transaction has - * been performed by the dialog. - */ - -Components.utils.import('resource://gre/modules/XPCOMUtils.jsm'); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils", - "resource://gre/modules/PromiseUtils.jsm"); - -const BOOKMARK_ITEM = 0; -const BOOKMARK_FOLDER = 1; -const LIVEMARK_CONTAINER = 2; - -const ACTION_EDIT = 0; -const ACTION_ADD = 1; - -var elementsHeight = new Map(); - -var BookmarkPropertiesPanel = { - - /** UI Text Strings */ - __strings: null, - get _strings() { - if (!this.__strings) { - this.__strings = document.getElementById("stringBundle"); - } - return this.__strings; - }, - - _action: null, - _itemType: null, - _itemId: -1, - _uri: null, - _loadInSidebar: false, - _title: "", - _description: "", - _URIs: [], - _keyword: "", - _postData: null, - _charSet: "", - _feedURI: null, - _siteURI: null, - - _defaultInsertionPoint: null, - _hiddenRows: [], - _batching: false, - - /** - * This method returns the correct label for the dialog's "accept" - * button based on the variant of the dialog. - */ - _getAcceptLabel: function BPP__getAcceptLabel() { - if (this._action == ACTION_ADD) { - if (this._URIs.length) - return this._strings.getString("dialogAcceptLabelAddMulti"); - - if (this._itemType == LIVEMARK_CONTAINER) - return this._strings.getString("dialogAcceptLabelAddLivemark"); - - if (this._dummyItem || this._loadInSidebar) - return this._strings.getString("dialogAcceptLabelAddItem"); - - return this._strings.getString("dialogAcceptLabelSaveItem"); - } - return this._strings.getString("dialogAcceptLabelEdit"); - }, - - /** - * This method returns the correct title for the current variant - * of this dialog. - */ - _getDialogTitle: function BPP__getDialogTitle() { - if (this._action == ACTION_ADD) { - if (this._itemType == BOOKMARK_ITEM) - return this._strings.getString("dialogTitleAddBookmark"); - if (this._itemType == LIVEMARK_CONTAINER) - return this._strings.getString("dialogTitleAddLivemark"); - - // add folder - NS_ASSERT(this._itemType == BOOKMARK_FOLDER, "Unknown item type"); - if (this._URIs.length) - return this._strings.getString("dialogTitleAddMulti"); - - return this._strings.getString("dialogTitleAddFolder"); - } - if (this._action == ACTION_EDIT) { - return this._strings.getFormattedString("dialogTitleEdit", [this._title]); - } - return ""; - }, - - /** - * Determines the initial data for the item edited or added by this dialog - */ - _determineItemInfo() { - let dialogInfo = window.arguments[0]; - this._action = dialogInfo.action == "add" ? ACTION_ADD : ACTION_EDIT; - this._hiddenRows = dialogInfo.hiddenRows ? dialogInfo.hiddenRows : []; - if (this._action == ACTION_ADD) { - NS_ASSERT("type" in dialogInfo, "missing type property for add action"); - - if ("title" in dialogInfo) - this._title = dialogInfo.title; - - if ("defaultInsertionPoint" in dialogInfo) { - this._defaultInsertionPoint = dialogInfo.defaultInsertionPoint; - } - else { - this._defaultInsertionPoint = - new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, - PlacesUtils.bookmarks.DEFAULT_INDEX, - Ci.nsITreeView.DROP_ON); - } - - switch (dialogInfo.type) { - case "bookmark": - this._itemType = BOOKMARK_ITEM; - if ("uri" in dialogInfo) { - NS_ASSERT(dialogInfo.uri instanceof Ci.nsIURI, - "uri property should be a uri object"); - this._uri = dialogInfo.uri; - if (typeof(this._title) != "string") { - this._title = this._getURITitleFromHistory(this._uri) || - this._uri.spec; - } - } - else { - this._uri = PlacesUtils._uri("about:blank"); - this._title = this._strings.getString("newBookmarkDefault"); - this._dummyItem = true; - } - - if ("loadBookmarkInSidebar" in dialogInfo) - this._loadInSidebar = dialogInfo.loadBookmarkInSidebar; - - if ("keyword" in dialogInfo) { - this._keyword = dialogInfo.keyword; - this._isAddKeywordDialog = true; - if ("postData" in dialogInfo) - this._postData = dialogInfo.postData; - if ("charSet" in dialogInfo) - this._charSet = dialogInfo.charSet; - } - break; - - case "folder": - this._itemType = BOOKMARK_FOLDER; - if (!this._title) { - if ("URIList" in dialogInfo) { - this._title = this._strings.getString("bookmarkAllTabsDefault"); - this._URIs = dialogInfo.URIList; - } - else - this._title = this._strings.getString("newFolderDefault"); - this._dummyItem = true; - } - break; - - case "livemark": - this._itemType = LIVEMARK_CONTAINER; - if ("feedURI" in dialogInfo) - this._feedURI = dialogInfo.feedURI; - if ("siteURI" in dialogInfo) - this._siteURI = dialogInfo.siteURI; - - if (!this._title) { - if (this._feedURI) { - this._title = this._getURITitleFromHistory(this._feedURI) || - this._feedURI.spec; - } - else - this._title = this._strings.getString("newLivemarkDefault"); - } - } - - if ("description" in dialogInfo) - this._description = dialogInfo.description; - } - else { // edit - this._node = dialogInfo.node; - this._title = this._node.title; - if (PlacesUtils.nodeIsFolder(this._node)) - this._itemType = BOOKMARK_FOLDER; - else if (PlacesUtils.nodeIsURI(this._node)) - this._itemType = BOOKMARK_ITEM; - } - }, - - /** - * This method returns the title string corresponding to a given URI. - * If none is available from the bookmark service (probably because - * the given URI doesn't appear in bookmarks or history), we synthesize - * a title from the first 100 characters of the URI. - * - * @param aURI - * nsIURI object for which we want the title - * - * @returns a title string - */ - _getURITitleFromHistory: function BPP__getURITitleFromHistory(aURI) { - NS_ASSERT(aURI instanceof Ci.nsIURI); - - // get the title from History - return PlacesUtils.history.getPageTitle(aURI); - }, - - /** - * This method should be called by the onload of the Bookmark Properties - * dialog to initialize the state of the panel. - */ - onDialogLoad: Task.async(function* () { - this._determineItemInfo(); - - document.title = this._getDialogTitle(); - var acceptButton = document.documentElement.getButton("accept"); - acceptButton.label = this._getAcceptLabel(); - - // Do not use sizeToContent, otherwise, due to bug 90276, the dialog will - // grow at every opening. - // Since elements can be uncollapsed asynchronously, we must observe their - // mutations and resize the dialog using a cached element size. - this._height = window.outerHeight; - this._mutationObserver = new MutationObserver(mutations => { - for (let mutation of mutations) { - let target = mutation.target; - let id = target.id; - if (!/^editBMPanel_.*(Row|Checkbox)$/.test(id)) - continue; - - let collapsed = target.getAttribute("collapsed") === "true"; - let wasCollapsed = mutation.oldValue === "true"; - if (collapsed == wasCollapsed) - continue; - - if (collapsed) { - this._height -= elementsHeight.get(id); - elementsHeight.delete(id); - } else { - elementsHeight.set(id, target.boxObject.height); - this._height += elementsHeight.get(id); - } - window.resizeTo(window.outerWidth, this._height); - } - }); - - this._mutationObserver.observe(document, - { subtree: true, - attributeOldValue: true, - attributeFilter: ["collapsed"] }); - - // Some controls are flexible and we want to update their cached size when - // the dialog is resized. - window.addEventListener("resize", this); - - this._beginBatch(); - - switch (this._action) { - case ACTION_EDIT: - gEditItemOverlay.initPanel({ node: this._node - , hiddenRows: this._hiddenRows - , focusedElement: "first" }); - acceptButton.disabled = gEditItemOverlay.readOnly; - break; - case ACTION_ADD: - this._node = yield this._promiseNewItem(); - // Edit the new item - gEditItemOverlay.initPanel({ node: this._node - , hiddenRows: this._hiddenRows - , postData: this._postData - , focusedElement: "first" }); - - // Empty location field if the uri is about:blank, this way inserting a new - // url will be easier for the user, Accept button will be automatically - // disabled by the input listener until the user fills the field. - let locationField = this._element("locationField"); - if (locationField.value == "about:blank") - locationField.value = ""; - - // if this is an uri related dialog disable accept button until - // the user fills an uri value. - if (this._itemType == BOOKMARK_ITEM) - acceptButton.disabled = !this._inputIsValid(); - break; - } - - if (!gEditItemOverlay.readOnly) { - // Listen on uri fields to enable accept button if input is valid - if (this._itemType == BOOKMARK_ITEM) { - this._element("locationField") - .addEventListener("input", this, false); - if (this._isAddKeywordDialog) { - this._element("keywordField") - .addEventListener("input", this, false); - } - } - } - }), - - // nsIDOMEventListener - handleEvent: function BPP_handleEvent(aEvent) { - var target = aEvent.target; - switch (aEvent.type) { - case "input": - if (target.id == "editBMPanel_locationField" || - target.id == "editBMPanel_keywordField") { - // Check uri fields to enable accept button if input is valid - document.documentElement - .getButton("accept").disabled = !this._inputIsValid(); - } - break; - case "resize": - for (let [id, oldHeight] of elementsHeight) { - let newHeight = document.getElementById(id).boxObject.height; - this._height += - oldHeight + newHeight; - elementsHeight.set(id, newHeight); - } - break; - } - }, - - // Hack for implementing batched-Undo around the editBookmarkOverlay - // instant-apply code. For all the details see the comment above beginBatch - // in browser-places.js - _batchBlockingDeferred: null, - _beginBatch() { - if (this._batching) - return; - if (PlacesUIUtils.useAsyncTransactions) { - this._batchBlockingDeferred = PromiseUtils.defer(); - PlacesTransactions.batch(function* () { - yield this._batchBlockingDeferred.promise; - }.bind(this)); - } - else { - PlacesUtils.transactionManager.beginBatch(null); - } - this._batching = true; - }, - - _endBatch() { - if (!this._batching) - return; - - if (PlacesUIUtils.useAsyncTransactions) { - this._batchBlockingDeferred.resolve(); - this._batchBlockingDeferred = null; - } - else { - PlacesUtils.transactionManager.endBatch(false); - } - this._batching = false; - }, - - // nsISupports - QueryInterface: function BPP_QueryInterface(aIID) { - if (aIID.equals(Ci.nsIDOMEventListener) || - aIID.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_NOINTERFACE; - }, - - _element: function BPP__element(aID) { - return document.getElementById("editBMPanel_" + aID); - }, - - onDialogUnload() { - // gEditItemOverlay does not exist anymore here, so don't rely on it. - this._mutationObserver.disconnect(); - delete this._mutationObserver; - - window.removeEventListener("resize", this); - - // Calling removeEventListener with arguments which do not identify any - // currently registered EventListener on the EventTarget has no effect. - this._element("locationField") - .removeEventListener("input", this, false); - }, - - onDialogAccept() { - // We must blur current focused element to save its changes correctly - document.commandDispatcher.focusedElement.blur(); - // The order here is important! We have to uninit the panel first, otherwise - // late changes could force it to commit more transactions. - gEditItemOverlay.uninitPanel(true); - this._endBatch(); - window.arguments[0].performed = true; - }, - - onDialogCancel() { - // The order here is important! We have to uninit the panel first, otherwise - // changes done as part of Undo may change the panel contents and by - // that force it to commit more transactions. - gEditItemOverlay.uninitPanel(true); - this._endBatch(); - if (PlacesUIUtils.useAsyncTransactions) - PlacesTransactions.undo().catch(Components.utils.reportError); - else - PlacesUtils.transactionManager.undoTransaction(); - window.arguments[0].performed = false; - }, - - /** - * This method checks to see if the input fields are in a valid state. - * - * @returns true if the input is valid, false otherwise - */ - _inputIsValid: function BPP__inputIsValid() { - if (this._itemType == BOOKMARK_ITEM && - !this._containsValidURI("locationField")) - return false; - if (this._isAddKeywordDialog && !this._element("keywordField").value.length) - return false; - - return true; - }, - - /** - * Determines whether the XUL textbox with the given ID contains a - * string that can be converted into an nsIURI. - * - * @param aTextboxID - * the ID of the textbox element whose contents we'll test - * - * @returns true if the textbox contains a valid URI string, false otherwise - */ - _containsValidURI: function BPP__containsValidURI(aTextboxID) { - try { - var value = this._element(aTextboxID).value; - if (value) { - PlacesUIUtils.createFixedURI(value); - return true; - } - } catch (e) { } - return false; - }, - - /** - * [New Item Mode] Get the insertion point details for the new item, given - * dialog state and opening arguments. - * - * The container-identifier and insertion-index are returned separately in - * the form of [containerIdentifier, insertionIndex] - */ - _getInsertionPointDetails: function BPP__getInsertionPointDetails() { - var containerId = this._defaultInsertionPoint.itemId; - var indexInContainer = this._defaultInsertionPoint.index; - - return [containerId, indexInContainer]; - }, - - /** - * Returns a transaction for creating a new bookmark item representing the - * various fields and opening arguments of the dialog. - */ - _getCreateNewBookmarkTransaction: - function BPP__getCreateNewBookmarkTransaction(aContainer, aIndex) { - var annotations = []; - var childTransactions = []; - - if (this._description) { - let annoObj = { name : PlacesUIUtils.DESCRIPTION_ANNO, - type : Ci.nsIAnnotationService.TYPE_STRING, - flags : 0, - value : this._description, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - let editItemTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj); - childTransactions.push(editItemTxn); - } - - if (this._loadInSidebar) { - let annoObj = { name : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO, - value : true }; - let setLoadTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj); - childTransactions.push(setLoadTxn); - } - - // XXX TODO: this should be in a transaction! - if (this._charSet && !PrivateBrowsingUtils.isWindowPrivate(window)) - PlacesUtils.setCharsetForURI(this._uri, this._charSet); - - let createTxn = new PlacesCreateBookmarkTransaction(this._uri, - aContainer, - aIndex, - this._title, - this._keyword, - annotations, - childTransactions, - this._postData); - - return new PlacesAggregatedTransaction(this._getDialogTitle(), - [createTxn]); - }, - - /** - * Returns a childItems-transactions array representing the URIList with - * which the dialog has been opened. - */ - _getTransactionsForURIList: function BPP__getTransactionsForURIList() { - var transactions = []; - for (let uri of this._URIs) { - // uri should be an object in the form { url, title }. Though add-ons - // could still use the legacy form, where it's an nsIURI. - let [_uri, _title] = uri instanceof Ci.nsIURI ? - [uri, this._getURITitleFromHistory(uri)] : [uri.uri, uri.title]; - - let createTxn = - new PlacesCreateBookmarkTransaction(_uri, -1, - PlacesUtils.bookmarks.DEFAULT_INDEX, - _title); - transactions.push(createTxn); - } - return transactions; - }, - - /** - * Returns a transaction for creating a new folder item representing the - * various fields and opening arguments of the dialog. - */ - _getCreateNewFolderTransaction: - function BPP__getCreateNewFolderTransaction(aContainer, aIndex) { - var annotations = []; - var childItemsTransactions; - if (this._URIs.length) - childItemsTransactions = this._getTransactionsForURIList(); - - if (this._description) - annotations.push(this._getDescriptionAnnotation(this._description)); - - return new PlacesCreateFolderTransaction(this._title, aContainer, - aIndex, annotations, - childItemsTransactions); - }, - - _createNewItem: Task.async(function* () { - let [container, index] = this._getInsertionPointDetails(); - let txn; - switch (this._itemType) { - case BOOKMARK_FOLDER: - txn = this._getCreateNewFolderTransaction(container, index); - break; - case LIVEMARK_CONTAINER: - txn = new PlacesCreateLivemarkTransaction(this._feedURI, this._siteURI, - this._title, container, index); - break; - default: // BOOKMARK_ITEM - txn = this._getCreateNewBookmarkTransaction(container, index); - } - - PlacesUtils.transactionManager.doTransaction(txn); - // This is a temporary hack until we use PlacesTransactions.jsm - if (txn._promise) { - yield txn._promise; - } - - let folderGuid = yield PlacesUtils.promiseItemGuid(container); - let bm = yield PlacesUtils.bookmarks.fetch({ - parentGuid: folderGuid, - index: index - }); - this._itemId = yield PlacesUtils.promiseItemId(bm.guid); - - return Object.freeze({ - itemId: this._itemId, - bookmarkGuid: bm.guid, - title: this._title, - uri: this._uri ? this._uri.spec : "", - type: this._itemType == BOOKMARK_ITEM ? - Ci.nsINavHistoryResultNode.RESULT_TYPE_URI : - Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER - }); - }), - - _promiseNewItem: Task.async(function* () { - if (!PlacesUIUtils.useAsyncTransactions) - return this._createNewItem(); - - let [containerId, index] = this._getInsertionPointDetails(); - let parentGuid = yield PlacesUtils.promiseItemGuid(containerId); - let annotations = []; - if (this._description) { - annotations.push({ name: PlacesUIUtils.DESCRIPTION_ANNO - , value: this._description }); - } - if (this._loadInSidebar) { - annotations.push({ name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO - , value: true }); - } - - let itemGuid; - let info = { parentGuid, index, title: this._title, annotations }; - if (this._itemType == BOOKMARK_ITEM) { - info.url = this._uri; - if (this._keyword) - info.keyword = this._keyword; - if (this._postData) - info.postData = this._postData; - - if (this._charSet && !PrivateBrowsingUtils.isWindowPrivate(window)) - PlacesUtils.setCharsetForURI(this._uri, this._charSet); - - itemGuid = yield PlacesTransactions.NewBookmark(info).transact(); - } - else if (this._itemType == LIVEMARK_CONTAINER) { - info.feedUrl = this._feedURI; - if (this._siteURI) - info.siteUrl = this._siteURI; - - itemGuid = yield PlacesTransactions.NewLivemark(info).transact(); - } - else if (this._itemType == BOOKMARK_FOLDER) { - itemGuid = yield PlacesTransactions.NewFolder(info).transact(); - for (let uri of this._URIs) { - let placeInfo = yield PlacesUtils.promisePlaceInfo(uri); - let title = placeInfo ? placeInfo.title : ""; - yield PlacesTransactions.transact({ parentGuid: itemGuid, uri, title }); - } - } - else { - throw new Error(`unexpected value for _itemType: ${this._itemType}`); - } - - this._itemGuid = itemGuid; - this._itemId = yield PlacesUtils.promiseItemId(itemGuid); - return Object.freeze({ - itemId: this._itemId, - bookmarkGuid: this._itemGuid, - title: this._title, - uri: this._uri ? this._uri.spec : "", - type: this._itemType == BOOKMARK_ITEM ? - Ci.nsINavHistoryResultNode.RESULT_TYPE_URI : - Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER - }); - }) -}; |