browser/components/places/src/PlacesUIUtils.jsm

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

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

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

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

mercurial