1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/places/src/PlacesUIUtils.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1222 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +this.EXPORTED_SYMBOLS = ["PlacesUIUtils"]; 1.10 + 1.11 +var Ci = Components.interfaces; 1.12 +var Cc = Components.classes; 1.13 +var Cr = Components.results; 1.14 +var Cu = Components.utils; 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/Services.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", 1.20 + "resource://gre/modules/PluralForm.jsm"); 1.21 + 1.22 +XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", 1.23 + "resource://gre/modules/PrivateBrowsingUtils.jsm"); 1.24 + 1.25 +#ifdef MOZ_SERVICES_SYNC 1.26 +XPCOMUtils.defineLazyModuleGetter(this, "Weave", 1.27 + "resource://services-sync/main.js"); 1.28 +#endif 1.29 + 1.30 +XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() { 1.31 + Cu.import("resource://gre/modules/PlacesUtils.jsm"); 1.32 + return PlacesUtils; 1.33 +}); 1.34 + 1.35 +this.PlacesUIUtils = { 1.36 + ORGANIZER_LEFTPANE_VERSION: 7, 1.37 + ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder", 1.38 + ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery", 1.39 + 1.40 + LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar", 1.41 + DESCRIPTION_ANNO: "bookmarkProperties/description", 1.42 + 1.43 + TYPE_TAB_DROP: "application/x-moz-tabbrowser-tab", 1.44 + 1.45 + /** 1.46 + * Makes a URI from a spec, and do fixup 1.47 + * @param aSpec 1.48 + * The string spec of the URI 1.49 + * @returns A URI object for the spec. 1.50 + */ 1.51 + createFixedURI: function PUIU_createFixedURI(aSpec) { 1.52 + return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE); 1.53 + }, 1.54 + 1.55 + getFormattedString: function PUIU_getFormattedString(key, params) { 1.56 + return bundle.formatStringFromName(key, params, params.length); 1.57 + }, 1.58 + 1.59 + /** 1.60 + * Get a localized plural string for the specified key name and numeric value 1.61 + * substituting parameters. 1.62 + * 1.63 + * @param aKey 1.64 + * String, key for looking up the localized string in the bundle 1.65 + * @param aNumber 1.66 + * Number based on which the final localized form is looked up 1.67 + * @param aParams 1.68 + * Array whose items will substitute #1, #2,... #n parameters 1.69 + * in the string. 1.70 + * 1.71 + * @see https://developer.mozilla.org/en/Localization_and_Plurals 1.72 + * @return The localized plural string. 1.73 + */ 1.74 + getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) { 1.75 + let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey)); 1.76 + 1.77 + // Replace #1 with aParams[0], #2 with aParams[1], and so on. 1.78 + return str.replace(/\#(\d+)/g, function (matchedId, matchedNumber) { 1.79 + let param = aParams[parseInt(matchedNumber, 10) - 1]; 1.80 + return param !== undefined ? param : matchedId; 1.81 + }); 1.82 + }, 1.83 + 1.84 + getString: function PUIU_getString(key) { 1.85 + return bundle.GetStringFromName(key); 1.86 + }, 1.87 + 1.88 + get _copyableAnnotations() [ 1.89 + this.DESCRIPTION_ANNO, 1.90 + this.LOAD_IN_SIDEBAR_ANNO, 1.91 + PlacesUtils.POST_DATA_ANNO, 1.92 + PlacesUtils.READ_ONLY_ANNO, 1.93 + ], 1.94 + 1.95 + /** 1.96 + * Get a transaction for copying a uri item (either a bookmark or a history 1.97 + * entry) from one container to another. 1.98 + * 1.99 + * @param aData 1.100 + * JSON object of dropped or pasted item properties 1.101 + * @param aContainer 1.102 + * The container being copied into 1.103 + * @param aIndex 1.104 + * The index within the container the item is copied to 1.105 + * @return A nsITransaction object that performs the copy. 1.106 + * 1.107 + * @note Since a copy creates a completely new item, only some internal 1.108 + * annotations are synced from the old one. 1.109 + * @see this._copyableAnnotations for the list of copyable annotations. 1.110 + */ 1.111 + _getURIItemCopyTransaction: 1.112 + function PUIU__getURIItemCopyTransaction(aData, aContainer, aIndex) 1.113 + { 1.114 + let transactions = []; 1.115 + if (aData.dateAdded) { 1.116 + transactions.push( 1.117 + new PlacesEditItemDateAddedTransaction(null, aData.dateAdded) 1.118 + ); 1.119 + } 1.120 + if (aData.lastModified) { 1.121 + transactions.push( 1.122 + new PlacesEditItemLastModifiedTransaction(null, aData.lastModified) 1.123 + ); 1.124 + } 1.125 + 1.126 + let keyword = aData.keyword || null; 1.127 + let annos = []; 1.128 + if (aData.annos) { 1.129 + annos = aData.annos.filter(function (aAnno) { 1.130 + return this._copyableAnnotations.indexOf(aAnno.name) != -1; 1.131 + }, this); 1.132 + } 1.133 + 1.134 + return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(aData.uri), 1.135 + aContainer, aIndex, aData.title, 1.136 + keyword, annos, transactions); 1.137 + }, 1.138 + 1.139 + /** 1.140 + * Gets a transaction for copying (recursively nesting to include children) 1.141 + * a folder (or container) and its contents from one folder to another. 1.142 + * 1.143 + * @param aData 1.144 + * Unwrapped dropped folder data - Obj containing folder and children 1.145 + * @param aContainer 1.146 + * The container we are copying into 1.147 + * @param aIndex 1.148 + * The index in the destination container to insert the new items 1.149 + * @return A nsITransaction object that will perform the copy. 1.150 + * 1.151 + * @note Since a copy creates a completely new item, only some internal 1.152 + * annotations are synced from the old one. 1.153 + * @see this._copyableAnnotations for the list of copyable annotations. 1.154 + */ 1.155 + _getFolderCopyTransaction: 1.156 + function PUIU__getFolderCopyTransaction(aData, aContainer, aIndex) 1.157 + { 1.158 + function getChildItemsTransactions(aChildren) 1.159 + { 1.160 + let transactions = []; 1.161 + let index = aIndex; 1.162 + aChildren.forEach(function (node, i) { 1.163 + // Make sure that items are given the correct index, this will be 1.164 + // passed by the transaction manager to the backend for the insertion. 1.165 + // Insertion behaves differently for DEFAULT_INDEX (append). 1.166 + if (aIndex != PlacesUtils.bookmarks.DEFAULT_INDEX) { 1.167 + index = i; 1.168 + } 1.169 + 1.170 + if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) { 1.171 + if (node.livemark && node.annos) { 1.172 + transactions.push( 1.173 + PlacesUIUtils._getLivemarkCopyTransaction(node, aContainer, index) 1.174 + ); 1.175 + } 1.176 + else { 1.177 + transactions.push( 1.178 + PlacesUIUtils._getFolderCopyTransaction(node, aContainer, index) 1.179 + ); 1.180 + } 1.181 + } 1.182 + else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) { 1.183 + transactions.push(new PlacesCreateSeparatorTransaction(-1, index)); 1.184 + } 1.185 + else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE) { 1.186 + transactions.push( 1.187 + PlacesUIUtils._getURIItemCopyTransaction(node, -1, index) 1.188 + ); 1.189 + } 1.190 + else { 1.191 + throw new Error("Unexpected item under a bookmarks folder"); 1.192 + } 1.193 + }); 1.194 + return transactions; 1.195 + } 1.196 + 1.197 + if (aContainer == PlacesUtils.tagsFolderId) { // Copying a tag folder. 1.198 + let transactions = []; 1.199 + if (aData.children) { 1.200 + aData.children.forEach(function(aChild) { 1.201 + transactions.push( 1.202 + new PlacesTagURITransaction(PlacesUtils._uri(aChild.uri), 1.203 + [aData.title]) 1.204 + ); 1.205 + }); 1.206 + } 1.207 + return new PlacesAggregatedTransaction("addTags", transactions); 1.208 + } 1.209 + 1.210 + if (aData.livemark && aData.annos) { // Copying a livemark. 1.211 + return this._getLivemarkCopyTransaction(aData, aContainer, aIndex); 1.212 + } 1.213 + 1.214 + let transactions = getChildItemsTransactions(aData.children); 1.215 + if (aData.dateAdded) { 1.216 + transactions.push( 1.217 + new PlacesEditItemDateAddedTransaction(null, aData.dateAdded) 1.218 + ); 1.219 + } 1.220 + if (aData.lastModified) { 1.221 + transactions.push( 1.222 + new PlacesEditItemLastModifiedTransaction(null, aData.lastModified) 1.223 + ); 1.224 + } 1.225 + 1.226 + let annos = []; 1.227 + if (aData.annos) { 1.228 + annos = aData.annos.filter(function (aAnno) { 1.229 + return this._copyableAnnotations.indexOf(aAnno.name) != -1; 1.230 + }, this); 1.231 + } 1.232 + 1.233 + return new PlacesCreateFolderTransaction(aData.title, aContainer, aIndex, 1.234 + annos, transactions); 1.235 + }, 1.236 + 1.237 + /** 1.238 + * Gets a transaction for copying a live bookmark item from one container to 1.239 + * another. 1.240 + * 1.241 + * @param aData 1.242 + * Unwrapped live bookmarkmark data 1.243 + * @param aContainer 1.244 + * The container we are copying into 1.245 + * @param aIndex 1.246 + * The index in the destination container to insert the new items 1.247 + * @return A nsITransaction object that will perform the copy. 1.248 + * 1.249 + * @note Since a copy creates a completely new item, only some internal 1.250 + * annotations are synced from the old one. 1.251 + * @see this._copyableAnnotations for the list of copyable annotations. 1.252 + */ 1.253 + _getLivemarkCopyTransaction: 1.254 + function PUIU__getLivemarkCopyTransaction(aData, aContainer, aIndex) 1.255 + { 1.256 + if (!aData.livemark || !aData.annos) { 1.257 + throw new Error("node is not a livemark"); 1.258 + } 1.259 + 1.260 + let feedURI, siteURI; 1.261 + let annos = []; 1.262 + if (aData.annos) { 1.263 + annos = aData.annos.filter(function (aAnno) { 1.264 + if (aAnno.name == PlacesUtils.LMANNO_FEEDURI) { 1.265 + feedURI = PlacesUtils._uri(aAnno.value); 1.266 + } 1.267 + else if (aAnno.name == PlacesUtils.LMANNO_SITEURI) { 1.268 + siteURI = PlacesUtils._uri(aAnno.value); 1.269 + } 1.270 + return this._copyableAnnotations.indexOf(aAnno.name) != -1 1.271 + }, this); 1.272 + } 1.273 + 1.274 + return new PlacesCreateLivemarkTransaction(feedURI, siteURI, aData.title, 1.275 + aContainer, aIndex, annos); 1.276 + }, 1.277 + 1.278 + /** 1.279 + * Constructs a Transaction for the drop or paste of a blob of data into 1.280 + * a container. 1.281 + * @param data 1.282 + * The unwrapped data blob of dropped or pasted data. 1.283 + * @param type 1.284 + * The content type of the data 1.285 + * @param container 1.286 + * The container the data was dropped or pasted into 1.287 + * @param index 1.288 + * The index within the container the item was dropped or pasted at 1.289 + * @param copy 1.290 + * The drag action was copy, so don't move folders or links. 1.291 + * @returns An object implementing nsITransaction that can perform 1.292 + * the move/insert. 1.293 + */ 1.294 + makeTransaction: 1.295 + function PUIU_makeTransaction(data, type, container, index, copy) 1.296 + { 1.297 + switch (data.type) { 1.298 + case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER: 1.299 + if (copy) { 1.300 + return this._getFolderCopyTransaction(data, container, index); 1.301 + } 1.302 + 1.303 + // Otherwise move the item. 1.304 + return new PlacesMoveItemTransaction(data.id, container, index); 1.305 + break; 1.306 + case PlacesUtils.TYPE_X_MOZ_PLACE: 1.307 + if (copy || data.id == -1) { // Id is -1 if the place is not bookmarked. 1.308 + return this._getURIItemCopyTransaction(data, container, index); 1.309 + } 1.310 + 1.311 + // Otherwise move the item. 1.312 + return new PlacesMoveItemTransaction(data.id, container, index); 1.313 + break; 1.314 + case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR: 1.315 + if (copy) { 1.316 + // There is no data in a separator, so copying it just amounts to 1.317 + // inserting a new separator. 1.318 + return new PlacesCreateSeparatorTransaction(container, index); 1.319 + } 1.320 + 1.321 + // Otherwise move the item. 1.322 + return new PlacesMoveItemTransaction(data.id, container, index); 1.323 + break; 1.324 + default: 1.325 + if (type == PlacesUtils.TYPE_X_MOZ_URL || 1.326 + type == PlacesUtils.TYPE_UNICODE || 1.327 + type == this.TYPE_TAB_DROP) { 1.328 + let title = type != PlacesUtils.TYPE_UNICODE ? data.title 1.329 + : data.uri; 1.330 + return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(data.uri), 1.331 + container, index, title); 1.332 + } 1.333 + } 1.334 + return null; 1.335 + }, 1.336 + 1.337 + /** 1.338 + * Shows the bookmark dialog corresponding to the specified info. 1.339 + * 1.340 + * @param aInfo 1.341 + * Describes the item to be edited/added in the dialog. 1.342 + * See documentation at the top of bookmarkProperties.js 1.343 + * @param aWindow 1.344 + * Owner window for the new dialog. 1.345 + * 1.346 + * @see documentation at the top of bookmarkProperties.js 1.347 + * @return true if any transaction has been performed, false otherwise. 1.348 + */ 1.349 + showBookmarkDialog: 1.350 + function PUIU_showBookmarkDialog(aInfo, aParentWindow) { 1.351 + // Preserve size attributes differently based on the fact the dialog has 1.352 + // a folder picker or not. If the picker is visible, the dialog should 1.353 + // be resizable since it may not show enough content for the folders 1.354 + // hierarchy. 1.355 + let hasFolderPicker = !("hiddenRows" in aInfo) || 1.356 + aInfo.hiddenRows.indexOf("folderPicker") == -1; 1.357 + // Use a different chrome url, since this allows to persist different sizes, 1.358 + // based on resizability of the dialog. 1.359 + let dialogURL = hasFolderPicker ? 1.360 + "chrome://browser/content/places/bookmarkProperties2.xul" : 1.361 + "chrome://browser/content/places/bookmarkProperties.xul"; 1.362 + 1.363 + let features = 1.364 + "centerscreen,chrome,modal,resizable=" + (hasFolderPicker ? "yes" : "no"); 1.365 + 1.366 + aParentWindow.openDialog(dialogURL, "", features, aInfo); 1.367 + return ("performed" in aInfo && aInfo.performed); 1.368 + }, 1.369 + 1.370 + _getTopBrowserWin: function PUIU__getTopBrowserWin() { 1.371 + return Services.wm.getMostRecentWindow("navigator:browser"); 1.372 + }, 1.373 + 1.374 + /** 1.375 + * Returns the closet ancestor places view for the given DOM node 1.376 + * @param aNode 1.377 + * a DOM node 1.378 + * @return the closet ancestor places view if exists, null otherwsie. 1.379 + */ 1.380 + getViewForNode: function PUIU_getViewForNode(aNode) { 1.381 + let node = aNode; 1.382 + 1.383 + // The view for a <menu> of which its associated menupopup is a places 1.384 + // view, is the menupopup. 1.385 + if (node.localName == "menu" && !node._placesNode && 1.386 + node.lastChild._placesView) 1.387 + return node.lastChild._placesView; 1.388 + 1.389 + while (node instanceof Ci.nsIDOMElement) { 1.390 + if (node._placesView) 1.391 + return node._placesView; 1.392 + if (node.localName == "tree" && node.getAttribute("type") == "places") 1.393 + return node; 1.394 + 1.395 + node = node.parentNode; 1.396 + } 1.397 + 1.398 + return null; 1.399 + }, 1.400 + 1.401 + /** 1.402 + * By calling this before visiting an URL, the visit will be associated to a 1.403 + * TRANSITION_TYPED transition (if there is no a referrer). 1.404 + * This is used when visiting pages from the history menu, history sidebar, 1.405 + * url bar, url autocomplete results, and history searches from the places 1.406 + * organizer. If this is not called visits will be marked as 1.407 + * TRANSITION_LINK. 1.408 + */ 1.409 + markPageAsTyped: function PUIU_markPageAsTyped(aURL) { 1.410 + PlacesUtils.history.markPageAsTyped(this.createFixedURI(aURL)); 1.411 + }, 1.412 + 1.413 + /** 1.414 + * By calling this before visiting an URL, the visit will be associated to a 1.415 + * TRANSITION_BOOKMARK transition. 1.416 + * This is used when visiting pages from the bookmarks menu, 1.417 + * personal toolbar, and bookmarks from within the places organizer. 1.418 + * If this is not called visits will be marked as TRANSITION_LINK. 1.419 + */ 1.420 + markPageAsFollowedBookmark: function PUIU_markPageAsFollowedBookmark(aURL) { 1.421 + PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL)); 1.422 + }, 1.423 + 1.424 + /** 1.425 + * By calling this before visiting an URL, any visit in frames will be 1.426 + * associated to a TRANSITION_FRAMED_LINK transition. 1.427 + * This is actually used to distinguish user-initiated visits in frames 1.428 + * so automatic visits can be correctly ignored. 1.429 + */ 1.430 + markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) { 1.431 + PlacesUtils.history.markPageAsFollowedLink(this.createFixedURI(aURL)); 1.432 + }, 1.433 + 1.434 + /** 1.435 + * Allows opening of javascript/data URI only if the given node is 1.436 + * bookmarked (see bug 224521). 1.437 + * @param aURINode 1.438 + * a URI node 1.439 + * @param aWindow 1.440 + * a window on which a potential error alert is shown on. 1.441 + * @return true if it's safe to open the node in the browser, false otherwise. 1.442 + * 1.443 + */ 1.444 + checkURLSecurity: function PUIU_checkURLSecurity(aURINode, aWindow) { 1.445 + if (PlacesUtils.nodeIsBookmark(aURINode)) 1.446 + return true; 1.447 + 1.448 + var uri = PlacesUtils._uri(aURINode.uri); 1.449 + if (uri.schemeIs("javascript") || uri.schemeIs("data")) { 1.450 + const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; 1.451 + var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. 1.452 + getService(Ci.nsIStringBundleService). 1.453 + createBundle(BRANDING_BUNDLE_URI). 1.454 + GetStringFromName("brandShortName"); 1.455 + 1.456 + var errorStr = this.getString("load-js-data-url-error"); 1.457 + Services.prompt.alert(aWindow, brandShortName, errorStr); 1.458 + return false; 1.459 + } 1.460 + return true; 1.461 + }, 1.462 + 1.463 + /** 1.464 + * Get the description associated with a document, as specified in a <META> 1.465 + * element. 1.466 + * @param doc 1.467 + * A DOM Document to get a description for 1.468 + * @returns A description string if a META element was discovered with a 1.469 + * "description" or "httpequiv" attribute, empty string otherwise. 1.470 + */ 1.471 + getDescriptionFromDocument: function PUIU_getDescriptionFromDocument(doc) { 1.472 + var metaElements = doc.getElementsByTagName("META"); 1.473 + for (var i = 0; i < metaElements.length; ++i) { 1.474 + if (metaElements[i].name.toLowerCase() == "description" || 1.475 + metaElements[i].httpEquiv.toLowerCase() == "description") { 1.476 + return metaElements[i].content; 1.477 + } 1.478 + } 1.479 + return ""; 1.480 + }, 1.481 + 1.482 + /** 1.483 + * Retrieve the description of an item 1.484 + * @param aItemId 1.485 + * item identifier 1.486 + * @returns the description of the given item, or an empty string if it is 1.487 + * not set. 1.488 + */ 1.489 + getItemDescription: function PUIU_getItemDescription(aItemId) { 1.490 + if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO)) 1.491 + return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO); 1.492 + return ""; 1.493 + }, 1.494 + 1.495 + /** 1.496 + * Gives the user a chance to cancel loading lots of tabs at once 1.497 + */ 1.498 + _confirmOpenInTabs: 1.499 + function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) { 1.500 + const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen"; 1.501 + var reallyOpen = true; 1.502 + 1.503 + if (Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) { 1.504 + if (numTabsToOpen >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) { 1.505 + // default to true: if it were false, we wouldn't get this far 1.506 + var warnOnOpen = { value: true }; 1.507 + 1.508 + var messageKey = "tabs.openWarningMultipleBranded"; 1.509 + var openKey = "tabs.openButtonMultiple"; 1.510 + const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; 1.511 + var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. 1.512 + getService(Ci.nsIStringBundleService). 1.513 + createBundle(BRANDING_BUNDLE_URI). 1.514 + GetStringFromName("brandShortName"); 1.515 + 1.516 + var buttonPressed = Services.prompt.confirmEx( 1.517 + aWindow, 1.518 + this.getString("tabs.openWarningTitle"), 1.519 + this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]), 1.520 + (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) + 1.521 + (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1), 1.522 + this.getString(openKey), null, null, 1.523 + this.getFormattedString("tabs.openWarningPromptMeBranded", 1.524 + [brandShortName]), 1.525 + warnOnOpen 1.526 + ); 1.527 + 1.528 + reallyOpen = (buttonPressed == 0); 1.529 + // don't set the pref unless they press OK and it's false 1.530 + if (reallyOpen && !warnOnOpen.value) 1.531 + Services.prefs.setBoolPref(WARN_ON_OPEN_PREF, false); 1.532 + } 1.533 + } 1.534 + 1.535 + return reallyOpen; 1.536 + }, 1.537 + 1.538 + /** aItemsToOpen needs to be an array of objects of the form: 1.539 + * {uri: string, isBookmark: boolean} 1.540 + */ 1.541 + _openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) { 1.542 + if (!aItemsToOpen.length) 1.543 + return; 1.544 + 1.545 + // Prefer the caller window if it's a browser window, otherwise use 1.546 + // the top browser window. 1.547 + var browserWindow = null; 1.548 + browserWindow = 1.549 + aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ? 1.550 + aWindow : this._getTopBrowserWin(); 1.551 + 1.552 + var urls = []; 1.553 + let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow); 1.554 + for (let item of aItemsToOpen) { 1.555 + urls.push(item.uri); 1.556 + if (skipMarking) { 1.557 + continue; 1.558 + } 1.559 + 1.560 + if (item.isBookmark) 1.561 + this.markPageAsFollowedBookmark(item.uri); 1.562 + else 1.563 + this.markPageAsTyped(item.uri); 1.564 + } 1.565 + 1.566 + // whereToOpenLink doesn't return "window" when there's no browser window 1.567 + // open (Bug 630255). 1.568 + var where = browserWindow ? 1.569 + browserWindow.whereToOpenLink(aEvent, false, true) : "window"; 1.570 + if (where == "window") { 1.571 + // There is no browser window open, thus open a new one. 1.572 + var uriList = PlacesUtils.toISupportsString(urls.join("|")); 1.573 + var args = Cc["@mozilla.org/supports-array;1"]. 1.574 + createInstance(Ci.nsISupportsArray); 1.575 + args.AppendElement(uriList); 1.576 + browserWindow = Services.ww.openWindow(aWindow, 1.577 + "chrome://browser/content/browser.xul", 1.578 + null, "chrome,dialog=no,all", args); 1.579 + return; 1.580 + } 1.581 + 1.582 + var loadInBackground = where == "tabshifted" ? true : false; 1.583 + // For consistency, we want all the bookmarks to open in new tabs, instead 1.584 + // of having one of them replace the currently focused tab. Hence we call 1.585 + // loadTabs with aReplace set to false. 1.586 + browserWindow.gBrowser.loadTabs(urls, loadInBackground, false); 1.587 + }, 1.588 + 1.589 + openContainerNodeInTabs: 1.590 + function PUIU_openContainerInTabs(aNode, aEvent, aView) { 1.591 + let window = aView.ownerWindow; 1.592 + 1.593 + let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode); 1.594 + if (!this._confirmOpenInTabs(urlsToOpen.length, window)) 1.595 + return; 1.596 + 1.597 + this._openTabset(urlsToOpen, aEvent, window); 1.598 + }, 1.599 + 1.600 + openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) { 1.601 + let window = aView.ownerWindow; 1.602 + 1.603 + let urlsToOpen = []; 1.604 + for (var i=0; i < aNodes.length; i++) { 1.605 + // Skip over separators and folders. 1.606 + if (PlacesUtils.nodeIsURI(aNodes[i])) 1.607 + urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])}); 1.608 + } 1.609 + this._openTabset(urlsToOpen, aEvent, window); 1.610 + }, 1.611 + 1.612 + /** 1.613 + * Loads the node's URL in the appropriate tab or window or as a web 1.614 + * panel given the user's preference specified by modifier keys tracked by a 1.615 + * DOM mouse/key event. 1.616 + * @param aNode 1.617 + * An uri result node. 1.618 + * @param aEvent 1.619 + * The DOM mouse/key event with modifier keys set that track the 1.620 + * user's preferred destination window or tab. 1.621 + * @param aView 1.622 + * The controller associated with aNode. 1.623 + */ 1.624 + openNodeWithEvent: 1.625 + function PUIU_openNodeWithEvent(aNode, aEvent, aView) { 1.626 + let window = aView.ownerWindow; 1.627 + this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window); 1.628 + }, 1.629 + 1.630 + /** 1.631 + * Loads the node's URL in the appropriate tab or window or as a 1.632 + * web panel. 1.633 + * see also openUILinkIn 1.634 + */ 1.635 + openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView) { 1.636 + let window = aView.ownerWindow; 1.637 + this._openNodeIn(aNode, aWhere, window); 1.638 + }, 1.639 + 1.640 + _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow) { 1.641 + if (aNode && PlacesUtils.nodeIsURI(aNode) && 1.642 + this.checkURLSecurity(aNode, aWindow)) { 1.643 + let isBookmark = PlacesUtils.nodeIsBookmark(aNode); 1.644 + 1.645 + if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) { 1.646 + if (isBookmark) 1.647 + this.markPageAsFollowedBookmark(aNode.uri); 1.648 + else 1.649 + this.markPageAsTyped(aNode.uri); 1.650 + } 1.651 + 1.652 + // Check whether the node is a bookmark which should be opened as 1.653 + // a web panel 1.654 + if (aWhere == "current" && isBookmark) { 1.655 + if (PlacesUtils.annotations 1.656 + .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) { 1.657 + let browserWin = this._getTopBrowserWin(); 1.658 + if (browserWin) { 1.659 + browserWin.openWebPanel(aNode.title, aNode.uri); 1.660 + return; 1.661 + } 1.662 + } 1.663 + } 1.664 + aWindow.openUILinkIn(aNode.uri, aWhere, { 1.665 + inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground") 1.666 + }); 1.667 + } 1.668 + }, 1.669 + 1.670 + /** 1.671 + * Helper for guessing scheme from an url string. 1.672 + * Used to avoid nsIURI overhead in frequently called UI functions. 1.673 + * 1.674 + * @param aUrlString the url to guess the scheme from. 1.675 + * 1.676 + * @return guessed scheme for this url string. 1.677 + * 1.678 + * @note this is not supposed be perfect, so use it only for UI purposes. 1.679 + */ 1.680 + guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) { 1.681 + return aUrlString.substr(0, aUrlString.indexOf(":")); 1.682 + }, 1.683 + 1.684 + getBestTitle: function PUIU_getBestTitle(aNode, aDoNotCutTitle) { 1.685 + var title; 1.686 + if (!aNode.title && PlacesUtils.nodeIsURI(aNode)) { 1.687 + // if node title is empty, try to set the label using host and filename 1.688 + // PlacesUtils._uri() will throw if aNode.uri is not a valid URI 1.689 + try { 1.690 + var uri = PlacesUtils._uri(aNode.uri); 1.691 + var host = uri.host; 1.692 + var fileName = uri.QueryInterface(Ci.nsIURL).fileName; 1.693 + // if fileName is empty, use path to distinguish labels 1.694 + if (aDoNotCutTitle) { 1.695 + title = host + uri.path; 1.696 + } else { 1.697 + title = host + (fileName ? 1.698 + (host ? "/" + this.ellipsis + "/" : "") + fileName : 1.699 + uri.path); 1.700 + } 1.701 + } 1.702 + catch (e) { 1.703 + // Use (no title) for non-standard URIs (data:, javascript:, ...) 1.704 + title = ""; 1.705 + } 1.706 + } 1.707 + else 1.708 + title = aNode.title; 1.709 + 1.710 + return title || this.getString("noTitle"); 1.711 + }, 1.712 + 1.713 + get leftPaneQueries() { 1.714 + // build the map 1.715 + this.leftPaneFolderId; 1.716 + return this.leftPaneQueries; 1.717 + }, 1.718 + 1.719 + // Get the folder id for the organizer left-pane folder. 1.720 + get leftPaneFolderId() { 1.721 + let leftPaneRoot = -1; 1.722 + let allBookmarksId; 1.723 + 1.724 + // Shortcuts to services. 1.725 + let bs = PlacesUtils.bookmarks; 1.726 + let as = PlacesUtils.annotations; 1.727 + 1.728 + // This is the list of the left pane queries. 1.729 + let queries = { 1.730 + "PlacesRoot": { title: "" }, 1.731 + "History": { title: this.getString("OrganizerQueryHistory") }, 1.732 + "Downloads": { title: this.getString("OrganizerQueryDownloads") }, 1.733 + "Tags": { title: this.getString("OrganizerQueryTags") }, 1.734 + "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") }, 1.735 + "BookmarksToolbar": 1.736 + { title: null, 1.737 + concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"), 1.738 + concreteId: PlacesUtils.toolbarFolderId }, 1.739 + "BookmarksMenu": 1.740 + { title: null, 1.741 + concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"), 1.742 + concreteId: PlacesUtils.bookmarksMenuFolderId }, 1.743 + "UnfiledBookmarks": 1.744 + { title: null, 1.745 + concreteTitle: PlacesUtils.getString("UnsortedBookmarksFolderTitle"), 1.746 + concreteId: PlacesUtils.unfiledBookmarksFolderId }, 1.747 + }; 1.748 + // All queries but PlacesRoot. 1.749 + const EXPECTED_QUERY_COUNT = 7; 1.750 + 1.751 + // Removes an item and associated annotations, ignoring eventual errors. 1.752 + function safeRemoveItem(aItemId) { 1.753 + try { 1.754 + if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) && 1.755 + !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) { 1.756 + // Some extension annotated their roots with our query annotation, 1.757 + // so we should not delete them. 1.758 + return; 1.759 + } 1.760 + // removeItemAnnotation does not check if item exists, nor the anno, 1.761 + // so this is safe to do. 1.762 + as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO); 1.763 + as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO); 1.764 + // This will throw if the annotation is an orphan. 1.765 + bs.removeItem(aItemId); 1.766 + } 1.767 + catch(e) { /* orphan anno */ } 1.768 + } 1.769 + 1.770 + // Returns true if item really exists, false otherwise. 1.771 + function itemExists(aItemId) { 1.772 + try { 1.773 + bs.getItemIndex(aItemId); 1.774 + return true; 1.775 + } 1.776 + catch(e) { 1.777 + return false; 1.778 + } 1.779 + } 1.780 + 1.781 + // Get all items marked as being the left pane folder. 1.782 + let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO); 1.783 + if (items.length > 1) { 1.784 + // Something went wrong, we cannot have more than one left pane folder, 1.785 + // remove all left pane folders and continue. We will create a new one. 1.786 + items.forEach(safeRemoveItem); 1.787 + } 1.788 + else if (items.length == 1 && items[0] != -1) { 1.789 + leftPaneRoot = items[0]; 1.790 + // Check that organizer left pane root is valid. 1.791 + let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO); 1.792 + if (version != this.ORGANIZER_LEFTPANE_VERSION || 1.793 + !itemExists(leftPaneRoot)) { 1.794 + // Invalid root, we must rebuild the left pane. 1.795 + safeRemoveItem(leftPaneRoot); 1.796 + leftPaneRoot = -1; 1.797 + } 1.798 + } 1.799 + 1.800 + if (leftPaneRoot != -1) { 1.801 + // A valid left pane folder has been found. 1.802 + // Build the leftPaneQueries Map. This is used to quickly access them, 1.803 + // associating a mnemonic name to the real item ids. 1.804 + delete this.leftPaneQueries; 1.805 + this.leftPaneQueries = {}; 1.806 + 1.807 + let items = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO); 1.808 + // While looping through queries we will also check for their validity. 1.809 + let queriesCount = 0; 1.810 + let corrupt = false; 1.811 + for (let i = 0; i < items.length; i++) { 1.812 + let queryName = as.getItemAnnotation(items[i], this.ORGANIZER_QUERY_ANNO); 1.813 + 1.814 + // Some extension did use our annotation to decorate their items 1.815 + // with icons, so we should check only our elements, to avoid dataloss. 1.816 + if (!(queryName in queries)) 1.817 + continue; 1.818 + 1.819 + let query = queries[queryName]; 1.820 + query.itemId = items[i]; 1.821 + 1.822 + if (!itemExists(query.itemId)) { 1.823 + // Orphan annotation, bail out and create a new left pane root. 1.824 + corrupt = true; 1.825 + break; 1.826 + } 1.827 + 1.828 + // Check that all queries have valid parents. 1.829 + let parentId = bs.getFolderIdForItem(query.itemId); 1.830 + if (items.indexOf(parentId) == -1 && parentId != leftPaneRoot) { 1.831 + // The parent is not part of the left pane, bail out and create a new 1.832 + // left pane root. 1.833 + corrupt = true; 1.834 + break; 1.835 + } 1.836 + 1.837 + // Titles could have been corrupted or the user could have changed his 1.838 + // locale. Check title and eventually fix it. 1.839 + if (bs.getItemTitle(query.itemId) != query.title) 1.840 + bs.setItemTitle(query.itemId, query.title); 1.841 + if ("concreteId" in query) { 1.842 + if (bs.getItemTitle(query.concreteId) != query.concreteTitle) 1.843 + bs.setItemTitle(query.concreteId, query.concreteTitle); 1.844 + } 1.845 + 1.846 + // Add the query to our cache. 1.847 + this.leftPaneQueries[queryName] = query.itemId; 1.848 + queriesCount++; 1.849 + } 1.850 + 1.851 + // Note: it's not enough to just check for queriesCount, since we may 1.852 + // find an invalid query just after accounting for a sufficient number of 1.853 + // valid ones. As well as we can't just rely on corrupt since we may find 1.854 + // less valid queries than expected. 1.855 + if (corrupt || queriesCount != EXPECTED_QUERY_COUNT) { 1.856 + // Queries number is wrong, so the left pane must be corrupt. 1.857 + // Note: we can't just remove the leftPaneRoot, because some query could 1.858 + // have a bad parent, so we have to remove all items one by one. 1.859 + items.forEach(safeRemoveItem); 1.860 + safeRemoveItem(leftPaneRoot); 1.861 + } 1.862 + else { 1.863 + // Everything is fine, return the current left pane folder. 1.864 + delete this.leftPaneFolderId; 1.865 + return this.leftPaneFolderId = leftPaneRoot; 1.866 + } 1.867 + } 1.868 + 1.869 + // Create a new left pane folder. 1.870 + var callback = { 1.871 + // Helper to create an organizer special query. 1.872 + create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) { 1.873 + let itemId = bs.insertBookmark(aParentId, 1.874 + PlacesUtils._uri(aQueryUrl), 1.875 + bs.DEFAULT_INDEX, 1.876 + queries[aQueryName].title); 1.877 + // Mark as special organizer query. 1.878 + as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName, 1.879 + 0, as.EXPIRE_NEVER); 1.880 + // We should never backup this, since it changes between profiles. 1.881 + as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1, 1.882 + 0, as.EXPIRE_NEVER); 1.883 + // Add to the queries map. 1.884 + PlacesUIUtils.leftPaneQueries[aQueryName] = itemId; 1.885 + return itemId; 1.886 + }, 1.887 + 1.888 + // Helper to create an organizer special folder. 1.889 + create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) { 1.890 + // Left Pane Root Folder. 1.891 + let folderId = bs.createFolder(aParentId, 1.892 + queries[aFolderName].title, 1.893 + bs.DEFAULT_INDEX); 1.894 + // We should never backup this, since it changes between profiles. 1.895 + as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1, 1.896 + 0, as.EXPIRE_NEVER); 1.897 + // Disallow manipulating this folder within the organizer UI. 1.898 + bs.setFolderReadonly(folderId, true); 1.899 + 1.900 + if (aIsRoot) { 1.901 + // Mark as special left pane root. 1.902 + as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO, 1.903 + PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, 1.904 + 0, as.EXPIRE_NEVER); 1.905 + } 1.906 + else { 1.907 + // Mark as special organizer folder. 1.908 + as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName, 1.909 + 0, as.EXPIRE_NEVER); 1.910 + PlacesUIUtils.leftPaneQueries[aFolderName] = folderId; 1.911 + } 1.912 + return folderId; 1.913 + }, 1.914 + 1.915 + runBatched: function CB_runBatched(aUserData) { 1.916 + delete PlacesUIUtils.leftPaneQueries; 1.917 + PlacesUIUtils.leftPaneQueries = { }; 1.918 + 1.919 + // Left Pane Root Folder. 1.920 + leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true); 1.921 + 1.922 + // History Query. 1.923 + this.create_query("History", leftPaneRoot, 1.924 + "place:type=" + 1.925 + Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY + 1.926 + "&sort=" + 1.927 + Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING); 1.928 + 1.929 + // Downloads. 1.930 + this.create_query("Downloads", leftPaneRoot, 1.931 + "place:transition=" + 1.932 + Ci.nsINavHistoryService.TRANSITION_DOWNLOAD + 1.933 + "&sort=" + 1.934 + Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING); 1.935 + 1.936 + // Tags Query. 1.937 + this.create_query("Tags", leftPaneRoot, 1.938 + "place:type=" + 1.939 + Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY + 1.940 + "&sort=" + 1.941 + Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING); 1.942 + 1.943 + // All Bookmarks Folder. 1.944 + allBookmarksId = this.create_folder("AllBookmarks", leftPaneRoot, false); 1.945 + 1.946 + // All Bookmarks->Bookmarks Toolbar Query. 1.947 + this.create_query("BookmarksToolbar", allBookmarksId, 1.948 + "place:folder=TOOLBAR"); 1.949 + 1.950 + // All Bookmarks->Bookmarks Menu Query. 1.951 + this.create_query("BookmarksMenu", allBookmarksId, 1.952 + "place:folder=BOOKMARKS_MENU"); 1.953 + 1.954 + // All Bookmarks->Unfiled Bookmarks Query. 1.955 + this.create_query("UnfiledBookmarks", allBookmarksId, 1.956 + "place:folder=UNFILED_BOOKMARKS"); 1.957 + } 1.958 + }; 1.959 + bs.runInBatchMode(callback, null); 1.960 + 1.961 + delete this.leftPaneFolderId; 1.962 + return this.leftPaneFolderId = leftPaneRoot; 1.963 + }, 1.964 + 1.965 + /** 1.966 + * Get the folder id for the organizer left-pane folder. 1.967 + */ 1.968 + get allBookmarksFolderId() { 1.969 + // ensure the left-pane root is initialized; 1.970 + this.leftPaneFolderId; 1.971 + delete this.allBookmarksFolderId; 1.972 + return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"]; 1.973 + }, 1.974 + 1.975 + /** 1.976 + * If an item is a left-pane query, returns the name of the query 1.977 + * or an empty string if not. 1.978 + * 1.979 + * @param aItemId id of a container 1.980 + * @returns the name of the query, or empty string if not a left-pane query 1.981 + */ 1.982 + getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) { 1.983 + var queryName = ""; 1.984 + // If the let pane hasn't been built, use the annotation service 1.985 + // directly, to avoid building the left pane too early. 1.986 + if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) { 1.987 + try { 1.988 + queryName = PlacesUtils.annotations. 1.989 + getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO); 1.990 + } 1.991 + catch (ex) { 1.992 + // doesn't have the annotation 1.993 + queryName = ""; 1.994 + } 1.995 + } 1.996 + else { 1.997 + // If the left pane has already been built, use the name->id map 1.998 + // cached in PlacesUIUtils. 1.999 + for (let [name, id] in Iterator(this.leftPaneQueries)) { 1.1000 + if (aItemId == id) 1.1001 + queryName = name; 1.1002 + } 1.1003 + } 1.1004 + return queryName; 1.1005 + }, 1.1006 + 1.1007 + shouldShowTabsFromOtherComputersMenuitem: function() { 1.1008 + // If Sync isn't configured yet, then don't show the menuitem. 1.1009 + return Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED && 1.1010 + Weave.Svc.Prefs.get("firstSync", "") != "notReady"; 1.1011 + }, 1.1012 + 1.1013 + shouldEnableTabsFromOtherComputersMenuitem: function() { 1.1014 + // The tabs engine might never be inited (if services.sync.registerEngines 1.1015 + // is modified), so make sure we avoid undefined errors. 1.1016 + return Weave.Service.isLoggedIn && 1.1017 + Weave.Service.engineManager.get("tabs") && 1.1018 + Weave.Service.engineManager.get("tabs").enabled; 1.1019 + }, 1.1020 +}; 1.1021 + 1.1022 +XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF", 1.1023 + "@mozilla.org/rdf/rdf-service;1", 1.1024 + "nsIRDFService"); 1.1025 + 1.1026 +XPCOMUtils.defineLazyGetter(PlacesUIUtils, "localStore", function() { 1.1027 + return PlacesUIUtils.RDF.GetDataSource("rdf:local-store"); 1.1028 +}); 1.1029 + 1.1030 +XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() { 1.1031 + return Services.prefs.getComplexValue("intl.ellipsis", 1.1032 + Ci.nsIPrefLocalizedString).data; 1.1033 +}); 1.1034 + 1.1035 +XPCOMUtils.defineLazyGetter(PlacesUIUtils, "useAsyncTransactions", function() { 1.1036 + try { 1.1037 + return Services.prefs.getBoolPref("browser.places.useAsyncTransactions"); 1.1038 + } 1.1039 + catch(ex) { } 1.1040 + return false; 1.1041 +}); 1.1042 + 1.1043 +XPCOMUtils.defineLazyServiceGetter(this, "URIFixup", 1.1044 + "@mozilla.org/docshell/urifixup;1", 1.1045 + "nsIURIFixup"); 1.1046 + 1.1047 +XPCOMUtils.defineLazyGetter(this, "bundle", function() { 1.1048 + const PLACES_STRING_BUNDLE_URI = 1.1049 + "chrome://browser/locale/places/places.properties"; 1.1050 + return Cc["@mozilla.org/intl/stringbundle;1"]. 1.1051 + getService(Ci.nsIStringBundleService). 1.1052 + createBundle(PLACES_STRING_BUNDLE_URI); 1.1053 +}); 1.1054 + 1.1055 +XPCOMUtils.defineLazyServiceGetter(this, "focusManager", 1.1056 + "@mozilla.org/focus-manager;1", 1.1057 + "nsIFocusManager"); 1.1058 + 1.1059 +/** 1.1060 + * This is a compatibility shim for old PUIU.ptm users. 1.1061 + * 1.1062 + * If you're looking for transactions and writing new code using them, directly 1.1063 + * use the transactions objects exported by the PlacesUtils.jsm module. 1.1064 + * 1.1065 + * This object will be removed once enough users are converted to the new API. 1.1066 + */ 1.1067 +XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() { 1.1068 + // Ensure PlacesUtils is imported in scope. 1.1069 + PlacesUtils; 1.1070 + 1.1071 + return { 1.1072 + aggregateTransactions: function(aName, aTransactions) 1.1073 + new PlacesAggregatedTransaction(aName, aTransactions), 1.1074 + 1.1075 + createFolder: function(aName, aContainer, aIndex, aAnnotations, 1.1076 + aChildItemsTransactions) 1.1077 + new PlacesCreateFolderTransaction(aName, aContainer, aIndex, aAnnotations, 1.1078 + aChildItemsTransactions), 1.1079 + 1.1080 + createItem: function(aURI, aContainer, aIndex, aTitle, aKeyword, 1.1081 + aAnnotations, aChildTransactions) 1.1082 + new PlacesCreateBookmarkTransaction(aURI, aContainer, aIndex, aTitle, 1.1083 + aKeyword, aAnnotations, 1.1084 + aChildTransactions), 1.1085 + 1.1086 + createSeparator: function(aContainer, aIndex) 1.1087 + new PlacesCreateSeparatorTransaction(aContainer, aIndex), 1.1088 + 1.1089 + createLivemark: function(aFeedURI, aSiteURI, aName, aContainer, aIndex, 1.1090 + aAnnotations) 1.1091 + new PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aName, aContainer, 1.1092 + aIndex, aAnnotations), 1.1093 + 1.1094 + moveItem: function(aItemId, aNewContainer, aNewIndex) 1.1095 + new PlacesMoveItemTransaction(aItemId, aNewContainer, aNewIndex), 1.1096 + 1.1097 + removeItem: function(aItemId) 1.1098 + new PlacesRemoveItemTransaction(aItemId), 1.1099 + 1.1100 + editItemTitle: function(aItemId, aNewTitle) 1.1101 + new PlacesEditItemTitleTransaction(aItemId, aNewTitle), 1.1102 + 1.1103 + editBookmarkURI: function(aItemId, aNewURI) 1.1104 + new PlacesEditBookmarkURITransaction(aItemId, aNewURI), 1.1105 + 1.1106 + setItemAnnotation: function(aItemId, aAnnotationObject) 1.1107 + new PlacesSetItemAnnotationTransaction(aItemId, aAnnotationObject), 1.1108 + 1.1109 + setPageAnnotation: function(aURI, aAnnotationObject) 1.1110 + new PlacesSetPageAnnotationTransaction(aURI, aAnnotationObject), 1.1111 + 1.1112 + editBookmarkKeyword: function(aItemId, aNewKeyword) 1.1113 + new PlacesEditBookmarkKeywordTransaction(aItemId, aNewKeyword), 1.1114 + 1.1115 + editBookmarkPostData: function(aItemId, aPostData) 1.1116 + new PlacesEditBookmarkPostDataTransaction(aItemId, aPostData), 1.1117 + 1.1118 + editLivemarkSiteURI: function(aLivemarkId, aSiteURI) 1.1119 + new PlacesEditLivemarkSiteURITransaction(aLivemarkId, aSiteURI), 1.1120 + 1.1121 + editLivemarkFeedURI: function(aLivemarkId, aFeedURI) 1.1122 + new PlacesEditLivemarkFeedURITransaction(aLivemarkId, aFeedURI), 1.1123 + 1.1124 + editItemDateAdded: function(aItemId, aNewDateAdded) 1.1125 + new PlacesEditItemDateAddedTransaction(aItemId, aNewDateAdded), 1.1126 + 1.1127 + editItemLastModified: function(aItemId, aNewLastModified) 1.1128 + new PlacesEditItemLastModifiedTransaction(aItemId, aNewLastModified), 1.1129 + 1.1130 + sortFolderByName: function(aFolderId) 1.1131 + new PlacesSortFolderByNameTransaction(aFolderId), 1.1132 + 1.1133 + tagURI: function(aURI, aTags) 1.1134 + new PlacesTagURITransaction(aURI, aTags), 1.1135 + 1.1136 + untagURI: function(aURI, aTags) 1.1137 + new PlacesUntagURITransaction(aURI, aTags), 1.1138 + 1.1139 + /** 1.1140 + * Transaction for setting/unsetting Load-in-sidebar annotation. 1.1141 + * 1.1142 + * @param aBookmarkId 1.1143 + * id of the bookmark where to set Load-in-sidebar annotation. 1.1144 + * @param aLoadInSidebar 1.1145 + * boolean value. 1.1146 + * @returns nsITransaction object. 1.1147 + */ 1.1148 + setLoadInSidebar: function(aItemId, aLoadInSidebar) 1.1149 + { 1.1150 + let annoObj = { name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO, 1.1151 + type: Ci.nsIAnnotationService.TYPE_INT32, 1.1152 + flags: 0, 1.1153 + value: aLoadInSidebar, 1.1154 + expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; 1.1155 + return new PlacesSetItemAnnotationTransaction(aItemId, annoObj); 1.1156 + }, 1.1157 + 1.1158 + /** 1.1159 + * Transaction for editing the description of a bookmark or a folder. 1.1160 + * 1.1161 + * @param aItemId 1.1162 + * id of the item to edit. 1.1163 + * @param aDescription 1.1164 + * new description. 1.1165 + * @returns nsITransaction object. 1.1166 + */ 1.1167 + editItemDescription: function(aItemId, aDescription) 1.1168 + { 1.1169 + let annoObj = { name: PlacesUIUtils.DESCRIPTION_ANNO, 1.1170 + type: Ci.nsIAnnotationService.TYPE_STRING, 1.1171 + flags: 0, 1.1172 + value: aDescription, 1.1173 + expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; 1.1174 + return new PlacesSetItemAnnotationTransaction(aItemId, annoObj); 1.1175 + }, 1.1176 + 1.1177 + //////////////////////////////////////////////////////////////////////////// 1.1178 + //// nsITransactionManager forwarders. 1.1179 + 1.1180 + beginBatch: function() 1.1181 + PlacesUtils.transactionManager.beginBatch(null), 1.1182 + 1.1183 + endBatch: function() 1.1184 + PlacesUtils.transactionManager.endBatch(false), 1.1185 + 1.1186 + doTransaction: function(txn) 1.1187 + PlacesUtils.transactionManager.doTransaction(txn), 1.1188 + 1.1189 + undoTransaction: function() 1.1190 + PlacesUtils.transactionManager.undoTransaction(), 1.1191 + 1.1192 + redoTransaction: function() 1.1193 + PlacesUtils.transactionManager.redoTransaction(), 1.1194 + 1.1195 + get numberOfUndoItems() 1.1196 + PlacesUtils.transactionManager.numberOfUndoItems, 1.1197 + get numberOfRedoItems() 1.1198 + PlacesUtils.transactionManager.numberOfRedoItems, 1.1199 + get maxTransactionCount() 1.1200 + PlacesUtils.transactionManager.maxTransactionCount, 1.1201 + set maxTransactionCount(val) 1.1202 + PlacesUtils.transactionManager.maxTransactionCount = val, 1.1203 + 1.1204 + clear: function() 1.1205 + PlacesUtils.transactionManager.clear(), 1.1206 + 1.1207 + peekUndoStack: function() 1.1208 + PlacesUtils.transactionManager.peekUndoStack(), 1.1209 + 1.1210 + peekRedoStack: function() 1.1211 + PlacesUtils.transactionManager.peekRedoStack(), 1.1212 + 1.1213 + getUndoStack: function() 1.1214 + PlacesUtils.transactionManager.getUndoStack(), 1.1215 + 1.1216 + getRedoStack: function() 1.1217 + PlacesUtils.transactionManager.getRedoStack(), 1.1218 + 1.1219 + AddListener: function(aListener) 1.1220 + PlacesUtils.transactionManager.AddListener(aListener), 1.1221 + 1.1222 + RemoveListener: function(aListener) 1.1223 + PlacesUtils.transactionManager.RemoveListener(aListener) 1.1224 + } 1.1225 +});