utils.js
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is the Places Command Controller.
16  *
17  * The Initial Developer of the Original Code is Google Inc.
18  * Portions created by the Initial Developer are Copyright (C) 2005
19  * the Initial Developer. All Rights Reserved.
20  *
21  * Contributor(s):
22  * Ben Goodger <beng@google.com>
23  * Myk Melez <myk@mozilla.org>
24  * Asaf Romano <mano@mozilla.com>
25  * Sungjoon Steve Won <stevewon@gmail.com>
26  * Dietrich Ayala <dietrich@mozilla.com>
27  * Marco Bonardo <mak77@bonardo.net>
28  *
29  * Alternatively, the contents of this file may be used under the terms of
30  * either the GNU General Public License Version 2 or later (the "GPL"), or
31  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32  * in which case the provisions of the GPL or the LGPL are applicable instead
33  * of those above. If you wish to allow use of your version of this file only
34  * under the terms of either the GPL or the LGPL, and not to allow others to
35  * use your version of this file under the terms of the MPL, indicate your
36  * decision by deleting the provisions above and replace them with the notice
37  * and other provisions required by the GPL or the LGPL. If you do not delete
38  * the provisions above, a recipient may use your version of this file under
39  * the terms of any one of the MPL, the GPL or the LGPL.
40  *
41  * ***** END LICENSE BLOCK ***** */
42 
43 function LOG(str) {
44  dump("*** " + str + "\n");
45 }
46 
47 var Ci = Components.interfaces;
48 var Cc = Components.classes;
49 var Cr = Components.results;
50 
51 __defineGetter__("PlacesUtils", function() {
52  delete this.PlacesUtils
53  var tmpScope = {};
54  Components.utils.import("resource://gre/modules/utils.js", tmpScope);
55  return this.PlacesUtils = tmpScope.PlacesUtils;
56 });
57 
58 const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
59 const DESCRIPTION_ANNO = "bookmarkProperties/description";
60 const GUID_ANNO = "placesInternal/GUID";
61 const LMANNO_FEEDURI = "livemark/feedURI";
62 const LMANNO_SITEURI = "livemark/siteURI";
63 const ORGANIZER_FOLDER_ANNO = "PlacesOrganizer/OrganizerFolder";
64 const ORGANIZER_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
66 const EXCLUDE_FROM_BACKUP_ANNO = "places/excludeFromBackup";
67 
68 #ifdef XP_MACOSX
69 // On Mac OSX, the transferable system converts "\r\n" to "\n\n", where we
70 // really just want "\n".
71 const NEWLINE= "\n";
72 #else
73 // On other platforms, the transferable system converts "\r\n" to "\n".
74 const NEWLINE = "\r\n";
75 #endif
76 
77 function QI_node(aNode, aIID) {
78  return aNode.QueryInterface(aIID);
79 }
80 function asVisit(aNode) { return QI_node(aNode, Ci.nsINavHistoryVisitResultNode); }
81 function asFullVisit(aNode){ return QI_node(aNode, Ci.nsINavHistoryFullVisitResultNode);}
82 function asContainer(aNode){ return QI_node(aNode, Ci.nsINavHistoryContainerResultNode);}
83 function asQuery(aNode) { return QI_node(aNode, Ci.nsINavHistoryQueryResultNode); }
84 
89  get microsummaries() {
90  delete this.microsummaries;
91  return this.microsummaries = Cc["@mozilla.org/microsummary/service;1"].
92  getService(Ci.nsIMicrosummaryService);
93  },
94 
95  get RDF() {
96  delete this.RDF;
97  return this.RDF = Cc["@mozilla.org/rdf/rdf-service;1"].
98  getService(Ci.nsIRDFService);
99  },
100 
101  get localStore() {
102  delete this.localStore;
103  return this.localStore = this.RDF.GetDataSource("rdf:local-store");
104  },
105 
106  get ptm() {
107  delete this.ptm;
108  return this.ptm = Cc["@mozilla.org/browser/placesTransactionsService;1"].
109  getService(Ci.nsIPlacesTransactionsService);
110  },
111 
112  get clipboard() {
113  delete this.clipboard;
114  return this.clipboard = Cc["@mozilla.org/widget/clipboard;1"].
115  getService(Ci.nsIClipboard);
116  },
117 
118  get URIFixup() {
119  delete this.URIFixup;
120  return this.URIFixup = Cc["@mozilla.org/docshell/urifixup;1"].
121  getService(Ci.nsIURIFixup);
122  },
123 
124  get ellipsis() {
125  delete this.ellipsis;
126  var pref = Cc["@mozilla.org/preferences-service;1"].
127  getService(Ci.nsIPrefBranch);
128  return this.ellipsis = pref.getComplexValue("intl.ellipsis",
129  Ci.nsIPrefLocalizedString).data;
130  },
131 
132  get privateBrowsing() {
133  delete this.privateBrowsing;
134  return this.privateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
135  getService(Ci.nsIPrivateBrowsingService);
136  },
137 
144  createFixedURI: function PU_createFixedURI(aSpec) {
145  return this.URIFixup.createFixupURI(aSpec, 0);
146  },
147 
154  _wrapString: function PU__wrapString(aString) {
155  var s = Cc["@mozilla.org/supports-string;1"].
156  createInstance(Ci.nsISupportsString);
157  s.data = aString;
158  return s;
159  },
160 
164  get _bundle() {
165  const PLACES_STRING_BUNDLE_URI =
166  "chrome://browser/locale/places/places.properties";
167  delete this._bundle;
168  return this._bundle = Cc["@mozilla.org/intl/stringbundle;1"].
169  getService(Ci.nsIStringBundleService).
170  createBundle(PLACES_STRING_BUNDLE_URI);
171  },
172 
173  getFormattedString: function PU_getFormattedString(key, params) {
174  return this._bundle.formatStringFromName(key, params, params.length);
175  },
176 
177  getString: function PU_getString(key) {
178  return this._bundle.GetStringFromName(key);
179  },
180 
192  _getURIItemCopyTransaction: function (aData, aContainer, aIndex) {
193  return this.ptm.createItem(PlacesUtils._uri(aData.uri), aContainer, aIndex,
194  aData.title, "");
195  },
196 
211  _getBookmarkItemCopyTransaction:
212  function PU__getBookmarkItemCopyTransaction(aData, aContainer, aIndex,
213  aExcludeAnnotations) {
214  var itemURL = PlacesUtils._uri(aData.uri);
215  var itemTitle = aData.title;
216  var keyword = aData.keyword || null;
217  var annos = aData.annos || [];
218  // always exclude GUID when copying any item
219  var excludeAnnos = [GUID_ANNO];
220  if (aExcludeAnnotations)
221  excludeAnnos = excludeAnnos.concat(aExcludeAnnotations);
222  annos = annos.filter(function(aValue, aIndex, aArray) {
223  return excludeAnnos.indexOf(aValue.name) == -1;
224  });
225  var childTxns = [];
226  if (aData.dateAdded)
227  childTxns.push(this.ptm.editItemDateAdded(null, aData.dateAdded));
228  if (aData.lastModified)
229  childTxns.push(this.ptm.editItemLastModified(null, aData.lastModified));
230  if (aData.tags) {
231  var tags = aData.tags.split(", ");
232  // filter out tags already present, so that undo doesn't remove them
233  // from pre-existing bookmarks
234  var storedTags = PlacesUtils.tagging.getTagsForURI(itemURL, {});
235  tags = tags.filter(function (aTag) {
236  return (storedTags.indexOf(aTag) == -1);
237  }, this);
238  if (tags.length)
239  childTxns.push(this.ptm.tagURI(itemURL, tags));
240  }
241 
242  return this.ptm.createItem(itemURL, aContainer, aIndex, itemTitle, keyword,
243  annos, childTxns);
244  },
245 
258  _getFolderCopyTransaction:
259  function PU__getFolderCopyTransaction(aData, aContainer, aIndex) {
260  var self = this;
261  function getChildItemsTransactions(aChildren) {
262  var childItemsTransactions = [];
263  var cc = aChildren.length;
264  var index = aIndex;
265  for (var i = 0; i < cc; ++i) {
266  var txn = null;
267  var node = aChildren[i];
268 
269  // Make sure that items are given the correct index, this will be
270  // passed by the transaction manager to the backend for the insertion.
271  // Insertion behaves differently if index == DEFAULT_INDEX (append)
272  if (aIndex != PlacesUtils.bookmarks.DEFAULT_INDEX)
273  index = i;
274 
275  if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
276  if (node.livemark && node.annos) // node is a livemark
277  txn = self._getLivemarkCopyTransaction(node, aContainer, index);
278  else
279  txn = self._getFolderCopyTransaction(node, aContainer, index);
280  }
281  else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR)
282  txn = self.ptm.createSeparator(-1, index);
283  else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE)
284  txn = self._getBookmarkItemCopyTransaction(node, -1, index);
285 
286  NS_ASSERT(txn, "Unexpected item under a bookmarks folder");
287  if (txn)
288  childItemsTransactions.push(txn);
289  }
290  return childItemsTransactions;
291  }
292 
293  // tag folders use tag transactions
294  if (aContainer == PlacesUtils.tagsFolderId) {
295  var txns = [];
296  if (aData.children) {
297  aData.children.forEach(function(aChild) {
298  txns.push(this.ptm.tagURI(PlacesUtils._uri(aChild.uri), [aData.title]));
299  }, this);
300  }
301  return this.ptm.aggregateTransactions("addTags", txns);
302  }
303  else if (aData.livemark && aData.annos) {
304  // Place is a Livemark Container
305  return this._getLivemarkCopyTransaction(aData, aContainer, aIndex);
306  }
307  else {
308  var childItems = getChildItemsTransactions(aData.children);
309  if (aData.dateAdded)
310  childItems.push(this.ptm.editItemDateAdded(null, aData.dateAdded));
311  if (aData.lastModified)
312  childItems.push(this.ptm.editItemLastModified(null, aData.lastModified));
313 
314  var annos = aData.annos || [];
315  annos = annos.filter(function(aAnno) {
316  // always exclude GUID when copying any item
317  return aAnno.name != GUID_ANNO;
318  });
319  return this.ptm.createFolder(aData.title, aContainer, aIndex, annos, childItems);
320  }
321  },
322 
323  _getLivemarkCopyTransaction:
324  function PU__getLivemarkCopyTransaction(aData, aContainer, aIndex) {
325  NS_ASSERT(aData.livemark && aData.annos, "node is not a livemark");
326  // Place is a Livemark Container
327  var feedURI = null;
328  var siteURI = null;
329  aData.annos = aData.annos.filter(function(aAnno) {
330  if (aAnno.name == LMANNO_FEEDURI) {
331  feedURI = PlacesUtils._uri(aAnno.value);
332  return false;
333  }
334  else if (aAnno.name == LMANNO_SITEURI) {
335  siteURI = PlacesUtils._uri(aAnno.value);
336  return false;
337  }
338  // always exclude GUID when copying any item
339  return aAnno.name != GUID_ANNO;
340  });
341  return this.ptm.createLivemark(feedURI, siteURI, aData.title, aContainer,
342  aIndex, aData.annos);
343  },
344 
361  makeTransaction: function PU_makeTransaction(data, type, container,
362  index, copy) {
363  switch (data.type) {
364  case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER:
365  if (copy)
366  return this._getFolderCopyTransaction(data, container, index);
367  // Otherwise move the item.
368  return this.ptm.moveItem(data.id, container, index);
369  break;
370  case PlacesUtils.TYPE_X_MOZ_PLACE:
371  if (data.id == -1) // Not bookmarked.
372  return this._getURIItemCopyTransaction(data, container, index);
373 
374  if (copy)
375  return this._getBookmarkItemCopyTransaction(data, container, index);
376  // Otherwise move the item.
377  return this.ptm.moveItem(data.id, container, index);
378  break;
379  case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR:
380  // There is no data in a separator, so copying it just amounts to
381  // inserting a new separator.
382  if (copy)
383  return this.ptm.createSeparator(container, index);
384  // Otherwise move the item.
385  return this.ptm.moveItem(data.id, container, index);
386  break;
387  default:
388  if (type == PlacesUtils.TYPE_X_MOZ_URL ||
389  type == PlacesUtils.TYPE_UNICODE ||
390  type == TAB_DROP_TYPE) {
391  var title = (type != PlacesUtils.TYPE_UNICODE) ? data.title :
392  data.uri;
393  return this.ptm.createItem(PlacesUtils._uri(data.uri),
394  container, index, title);
395  }
396  }
397  return null;
398  },
399 
444  showAddBookmarkUI: function PU_showAddBookmarkUI(aURI,
445  aTitle,
446  aDescription,
447  aDefaultInsertionPoint,
448  aShowPicker,
449  aLoadInSidebar,
450  aKeyword,
451  aPostData,
452  aCharSet) {
453  var info = {
454  action: "add",
455  type: "bookmark"
456  };
457 
458  if (aURI)
459  info.uri = aURI;
460 
461  // allow default empty title
462  if (typeof(aTitle) == "string")
463  info.title = aTitle;
464 
465  if (aDescription)
466  info.description = aDescription;
467 
468  if (aDefaultInsertionPoint) {
469  info.defaultInsertionPoint = aDefaultInsertionPoint;
470  if (!aShowPicker)
471  info.hiddenRows = ["folderPicker"];
472  }
473 
474  if (aLoadInSidebar)
475  info.loadBookmarkInSidebar = true;
476 
477  if (typeof(aKeyword) == "string") {
478  info.keyword = aKeyword;
479  if (typeof(aPostData) == "string")
480  info.postData = aPostData;
481  if (typeof(aCharSet) == "string")
482  info.charSet = aCharSet;
483  }
484 
485  return this._showBookmarkDialog(info);
486  },
487 
499  showMinimalAddBookmarkUI:
500  function PU_showMinimalAddBookmarkUI(aURI, aTitle, aDescription,
501  aDefaultInsertionPoint, aShowPicker,
502  aLoadInSidebar, aKeyword, aPostData,
503  aCharSet) {
504  var info = {
505  action: "add",
506  type: "bookmark",
507  hiddenRows: ["description"]
508  };
509  if (aURI)
510  info.uri = aURI;
511 
512  // allow default empty title
513  if (typeof(aTitle) == "string")
514  info.title = aTitle;
515 
516  if (aDescription)
517  info.description = aDescription;
518 
519  if (aDefaultInsertionPoint) {
520  info.defaultInsertionPoint = aDefaultInsertionPoint;
521  if (!aShowPicker)
522  info.hiddenRows.push("folderPicker");
523  }
524 
525  if (aLoadInSidebar)
526  info.loadBookmarkInSidebar = true;
527  else
528  info.hiddenRows = info.hiddenRows.concat(["location", "loadInSidebar"]);
529 
530  if (typeof(aKeyword) == "string") {
531  info.keyword = aKeyword;
532  // Hide the Tags field if we are adding a keyword.
533  info.hiddenRows.push("tags");
534  // Keyword related params.
535  if (typeof(aPostData) == "string")
536  info.postData = aPostData;
537  if (typeof(aCharSet) == "string")
538  info.charSet = aCharSet;
539  }
540  else
541  info.hiddenRows.push("keyword");
542 
543  this._showBookmarkDialog(info, true);
544  },
545 
568  showAddLivemarkUI: function PU_showAddLivemarkURI(aFeedURI,
569  aSiteURI,
570  aTitle,
571  aDescription,
572  aDefaultInsertionPoint,
573  aShowPicker) {
574  var info = {
575  action: "add",
576  type: "livemark"
577  };
578 
579  if (aFeedURI)
580  info.feedURI = aFeedURI;
581  if (aSiteURI)
582  info.siteURI = aSiteURI;
583 
584  // allow default empty title
585  if (typeof(aTitle) == "string")
586  info.title = aTitle;
587 
588  if (aDescription)
589  info.description = aDescription;
590 
591  if (aDefaultInsertionPoint) {
592  info.defaultInsertionPoint = aDefaultInsertionPoint;
593  if (!aShowPicker)
594  info.hiddenRows = ["folderPicker"];
595  }
596  return this._showBookmarkDialog(info);
597  },
598 
607  showMinimalAddLivemarkUI:
608  function PU_showMinimalAddLivemarkURI(aFeedURI, aSiteURI, aTitle,
609  aDescription, aDefaultInsertionPoint,
610  aShowPicker) {
611  var info = {
612  action: "add",
613  type: "livemark",
614  hiddenRows: ["feedLocation", "siteLocation", "description"]
615  };
616 
617  if (aFeedURI)
618  info.feedURI = aFeedURI;
619  if (aSiteURI)
620  info.siteURI = aSiteURI;
621 
622  // allow default empty title
623  if (typeof(aTitle) == "string")
624  info.title = aTitle;
625 
626  if (aDescription)
627  info.description = aDescription;
628 
629  if (aDefaultInsertionPoint) {
630  info.defaultInsertionPoint = aDefaultInsertionPoint;
631  if (!aShowPicker)
632  info.hiddenRows.push("folderPicker");
633  }
634  this._showBookmarkDialog(info, true);
635  },
636 
646  showMinimalAddMultiBookmarkUI: function PU_showAddMultiBookmarkUI(aURIList) {
647  NS_ASSERT(aURIList.length,
648  "showAddMultiBookmarkUI expects a list of nsIURI objects");
649  var info = {
650  action: "add",
651  type: "folder",
652  hiddenRows: ["description"],
653  URIList: aURIList
654  };
655  this._showBookmarkDialog(info, true);
656  },
657 
669  showItemProperties: function PU_showItemProperties(aItemId, aType, aReadOnly) {
670  var info = {
671  action: "edit",
672  type: aType,
673  itemId: aItemId,
674  readOnly: aReadOnly
675  };
676  return this._showBookmarkDialog(info);
677  },
678 
693  showAddFolderUI:
694  function PU_showAddFolderUI(aTitle, aDefaultInsertionPoint, aShowPicker) {
695  var info = {
696  action: "add",
697  type: "folder",
698  hiddenRows: []
699  };
700 
701  // allow default empty title
702  if (typeof(aTitle) == "string")
703  info.title = aTitle;
704 
705  if (aDefaultInsertionPoint) {
706  info.defaultInsertionPoint = aDefaultInsertionPoint;
707  if (!aShowPicker)
708  info.hiddenRows.push("folderPicker");
709  }
710  return this._showBookmarkDialog(info);
711  },
712 
725  _showBookmarkDialog: function PU__showBookmarkDialog(aInfo, aMinimalUI) {
726  var dialogURL = aMinimalUI ?
727  "chrome://browser/content/places/bookmarkProperties2.xul" :
728  "chrome://browser/content/places/bookmarkProperties.xul";
729 
730  var features;
731  if (aMinimalUI)
732  features = "centerscreen,chrome,dialog,resizable,modal";
733  else
734  features = "centerscreen,chrome,modal,resizable=no";
735  window.openDialog(dialogURL, "", features, aInfo);
736  return ("performed" in aInfo && aInfo.performed);
737  },
738 
745  getViewForNode: function PU_getViewForNode(aNode) {
746  var node = aNode;
747 
748  // the view for a <menu> of which its associated menupopup is a places view,
749  // is the menupopup
750  if (node.localName == "menu" && !node.node &&
751  node.firstChild.getAttribute("type") == "places")
752  return node.firstChild;
753 
754  while (node) {
755  // XXXmano: Use QueryInterface(nsIPlacesView) once we implement it...
756  if (node.getAttribute("type") == "places")
757  return node;
758 
759  node = node.parentNode;
760  }
761 
762  return null;
763  },
764 
773  markPageAsTyped: function PU_markPageAsTyped(aURL) {
774  PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory)
775  .markPageAsTyped(this.createFixedURI(aURL));
776  },
777 
785  markPageAsFollowedBookmark: function PU_markPageAsFollowedBookmark(aURL) {
786  PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL));
787  },
788 
797  checkURLSecurity: function PU_checkURLSecurity(aURINode) {
798  if (!PlacesUtils.nodeIsBookmark(aURINode)) {
799  var uri = PlacesUtils._uri(aURINode.uri);
800  if (uri.schemeIs("javascript") || uri.schemeIs("data")) {
801  const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
802  var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
803  getService(Ci.nsIStringBundleService).
804  createBundle(BRANDING_BUNDLE_URI).
805  GetStringFromName("brandShortName");
806  var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
807  getService(Ci.nsIPromptService);
808 
809  var errorStr = this.getString("load-js-data-url-error");
810  promptService.alert(window, brandShortName, errorStr);
811  return false;
812  }
813  }
814  return true;
815  },
816 
825  getDescriptionFromDocument: function PU_getDescriptionFromDocument(doc) {
826  var metaElements = doc.getElementsByTagName("META");
827  for (var i = 0; i < metaElements.length; ++i) {
828  if (metaElements[i].name.toLowerCase() == "description" ||
829  metaElements[i].httpEquiv.toLowerCase() == "description") {
830  return metaElements[i].content;
831  }
832  }
833  return "";
834  },
835 
843  getItemDescription: function PU_getItemDescription(aItemId) {
844  if (PlacesUtils.annotations.itemHasAnnotation(aItemId, DESCRIPTION_ANNO))
845  return PlacesUtils.annotations.getItemAnnotation(aItemId, DESCRIPTION_ANNO);
846  return "";
847  },
848 
852  _confirmOpenInTabs: function PU__confirmOpenInTabs(numTabsToOpen) {
853  var pref = Cc["@mozilla.org/preferences-service;1"].
854  getService(Ci.nsIPrefBranch);
855 
856  const kWarnOnOpenPref = "browser.tabs.warnOnOpen";
857  var reallyOpen = true;
858  if (pref.getBoolPref(kWarnOnOpenPref)) {
859  if (numTabsToOpen >= pref.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
860  var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
861  getService(Ci.nsIPromptService);
862 
863  // default to true: if it were false, we wouldn't get this far
864  var warnOnOpen = { value: true };
865 
866  var messageKey = "tabs.openWarningMultipleBranded";
867  var openKey = "tabs.openButtonMultiple";
868  const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
869  var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
870  getService(Ci.nsIStringBundleService).
871  createBundle(BRANDING_BUNDLE_URI).
872  GetStringFromName("brandShortName");
873 
874  var buttonPressed = promptService.confirmEx(window,
875  this.getString("tabs.openWarningTitle"),
876  this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]),
877  (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0)
878  + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
879  this.getString(openKey), null, null,
880  this.getFormattedString("tabs.openWarningPromptMeBranded",
881  [brandShortName]), warnOnOpen);
882 
883  reallyOpen = (buttonPressed == 0);
884  // don't set the pref unless they press OK and it's false
885  if (reallyOpen && !warnOnOpen.value)
886  pref.setBoolPref(kWarnOnOpenPref, false);
887  }
888  }
889  return reallyOpen;
890  },
891 
895  _openTabset: function PU__openTabset(aItemsToOpen, aEvent) {
896  if (!aItemsToOpen.length)
897  return;
898 
899  var urls = [];
900  for (var i = 0; i < aItemsToOpen.length; i++) {
901  var item = aItemsToOpen[i];
902  if (item.isBookmark)
903  this.markPageAsFollowedBookmark(item.uri);
904  else
905  this.markPageAsTyped(item.uri);
906 
907  urls.push(item.uri);
908  }
909 
910  var browserWindow = getTopWin();
911  var where = browserWindow ?
912  whereToOpenLink(aEvent, false, true) : "window";
913  if (where == "window") {
914  window.openDialog(getBrowserURL(), "_blank",
915  "chrome,all,dialog=no", urls.join("|"));
916  return;
917  }
918 
919  var loadInBackground = where == "tabshifted" ? true : false;
920  var replaceCurrentTab = where == "tab" ? false : true;
921  browserWindow.getBrowser().loadTabs(urls, loadInBackground,
922  replaceCurrentTab);
923  },
924 
925  openContainerNodeInTabs: function PU_openContainerInTabs(aNode, aEvent) {
926  var urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
927  if (!this._confirmOpenInTabs(urlsToOpen.length))
928  return;
929 
930  this._openTabset(urlsToOpen, aEvent);
931  },
932 
933  openURINodesInTabs: function PU_openURINodesInTabs(aNodes, aEvent) {
934  var urlsToOpen = [];
935  for (var i=0; i < aNodes.length; i++) {
936  // skip over separators and folders
937  if (PlacesUtils.nodeIsURI(aNodes[i]))
938  urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])});
939  }
940  this._openTabset(urlsToOpen, aEvent);
941  },
942 
953  openNodeWithEvent: function PU_openNodeWithEvent(aNode, aEvent) {
954  this.openNodeIn(aNode, whereToOpenLink(aEvent));
955  },
956 
962  openNodeIn: function PU_openNodeIn(aNode, aWhere) {
963  if (aNode && PlacesUtils.nodeIsURI(aNode) &&
964  this.checkURLSecurity(aNode)) {
965  var isBookmark = PlacesUtils.nodeIsBookmark(aNode);
966 
967  if (isBookmark)
968  this.markPageAsFollowedBookmark(aNode.uri);
969  else
970  this.markPageAsTyped(aNode.uri);
971 
972  // Check whether the node is a bookmark which should be opened as
973  // a web panel
974  if (aWhere == "current" && isBookmark) {
975  if (PlacesUtils.annotations
976  .itemHasAnnotation(aNode.itemId, LOAD_IN_SIDEBAR_ANNO)) {
977  var w = getTopWin();
978  if (w) {
979  w.openWebPanel(aNode.title, aNode.uri);
980  return;
981  }
982  }
983  }
984  openUILinkIn(aNode.uri, aWhere);
985  }
986  },
987 
998  guessUrlSchemeForUI: function PUU_guessUrlSchemeForUI(aUrlString) {
999  return aUrlString.substr(0, aUrlString.indexOf(":"));
1000  },
1001 
1005  createMenuItemForNode:
1006  function PUU_createMenuItemForNode(aNode) {
1007  var element;
1008  var type = aNode.type;
1009  if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
1010  element = document.createElement("menuseparator");
1011  else {
1012  if (PlacesUtils.uriTypes.indexOf(type) != -1) {
1013  element = document.createElement("menuitem");
1014  element.className = "menuitem-iconic bookmark-item";
1015  element.setAttribute("scheme", this.guessUrlSchemeForUI(aNode.uri));
1016  }
1017  else if (PlacesUtils.containerTypes.indexOf(type) != -1) {
1018  element = document.createElement("menu");
1019  element.setAttribute("container", "true");
1020 
1021  if (aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
1022  element.setAttribute("query", "true");
1023  if (PlacesUtils.nodeIsTagQuery(aNode))
1024  element.setAttribute("tagContainer", "true");
1025  else if (PlacesUtils.nodeIsDay(aNode))
1026  element.setAttribute("dayContainer", "true");
1027  else if (PlacesUtils.nodeIsHost(aNode))
1028  element.setAttribute("hostContainer", "true");
1029  }
1030  else if (aNode.itemId != -1) {
1031  if (PlacesUtils.nodeIsLivemarkContainer(aNode))
1032  element.setAttribute("livemark", "true");
1033  }
1034 
1035  var popup = document.createElement("menupopup");
1036  popup.setAttribute("placespopup", "true");
1037  popup._resultNode = asContainer(aNode);
1038 #ifdef XP_MACOSX
1039  // Binding on Mac native menus is lazy attached, so onPopupShowing,
1040  // in the capturing phase, fields are not yet initialized.
1041  // In that phase we have to ensure markers are not undefined to build
1042  // the popup correctly.
1043  popup._startMarker = -1;
1044  popup._endMarker = -1;
1045 #else
1046  // no context menu on mac
1047  popup.setAttribute("context", "placesContext");
1048 #endif
1049  element.appendChild(popup);
1050  element.className = "menu-iconic bookmark-item";
1051  }
1052  else
1053  throw "Unexpected node";
1054 
1055  element.setAttribute("label", this.getBestTitle(aNode));
1056 
1057  var icon = aNode.icon;
1058  if (icon)
1059  element.setAttribute("image", icon);
1060  }
1061  element.node = aNode;
1062  element.node._DOMElement = element;
1063 
1064  return element;
1065  },
1066 
1067  cleanPlacesPopup: function PU_cleanPlacesPopup(aPopup) {
1068  // Remove places popup children and update markers to keep track of
1069  // their indices.
1070  var start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0;
1071  var end = aPopup._endMarker != -1 ? aPopup._endMarker :
1072  aPopup.childNodes.length;
1073  var items = [];
1074  var placesNodeFound = false;
1075  for (var i = start; i < end; ++i) {
1076  var item = aPopup.childNodes[i];
1077  if (item.getAttribute("builder") == "end") {
1078  // we need to do this for menus that have static content at the end but
1079  // are initially empty, eg. the history menu, we need to know where to
1080  // start inserting new items.
1081  aPopup._endMarker = i;
1082  break;
1083  }
1084  if (item.node) {
1085  items.push(item);
1086  placesNodeFound = true;
1087  }
1088  else {
1089  // This is static content...
1090  if (!placesNodeFound)
1091  // ...at the start of the popup
1092  // Initialized in menu.xml, in the base binding
1093  aPopup._startMarker++;
1094  else {
1095  // ...after places nodes
1096  aPopup._endMarker = i;
1097  break;
1098  }
1099  }
1100  }
1101 
1102  for (var i = 0; i < items.length; ++i) {
1103  aPopup.removeChild(items[i]);
1104  if (aPopup._endMarker != -1)
1105  aPopup._endMarker--;
1106  }
1107  },
1108 
1109  getBestTitle: function PU_getBestTitle(aNode) {
1110  var title;
1111  if (!aNode.title && PlacesUtils.uriTypes.indexOf(aNode.type) != -1) {
1112  // if node title is empty, try to set the label using host and filename
1113  // PlacesUtils._uri() will throw if aNode.uri is not a valid URI
1114  try {
1115  var uri = PlacesUtils._uri(aNode.uri);
1116  var host = uri.host;
1117  var fileName = uri.QueryInterface(Ci.nsIURL).fileName;
1118  // if fileName is empty, use path to distinguish labels
1119  title = host + (fileName ?
1120  (host ? "/" + this.ellipsis + "/" : "") + fileName :
1121  uri.path);
1122  }
1123  catch (e) {
1124  // Use (no title) for non-standard URIs (data:, javascript:, ...)
1125  title = "";
1126  }
1127  }
1128  else
1129  title = aNode.title;
1130 
1131  return title || this.getString("noTitle");
1132  },
1133 
1134  get leftPaneQueries() {
1135  // build the map
1136  this.leftPaneFolderId;
1137  return this.leftPaneQueries;
1138  },
1139 
1140  // Get the folder id for the organizer left-pane folder.
1141  get leftPaneFolderId() {
1142  var leftPaneRoot = -1;
1143  var allBookmarksId;
1144 
1145  // Shortcuts to services.
1146  var bs = PlacesUtils.bookmarks;
1147  var as = PlacesUtils.annotations;
1148 
1149  // Get all items marked as being the left pane folder. We should only have
1150  // one of them.
1151  var items = as.getItemsWithAnnotation(ORGANIZER_FOLDER_ANNO, {});
1152  if (items.length > 1) {
1153  // Something went wrong, we cannot have more than one left pane folder,
1154  // remove all left pane folders and continue. We will create a new one.
1155  items.forEach(bs.removeItem);
1156  }
1157  else if (items.length == 1 && items[0] != -1) {
1158  leftPaneRoot = items[0];
1159  // Check organizer left pane version.
1160  var version = as.getItemAnnotation(leftPaneRoot, ORGANIZER_FOLDER_ANNO);
1161  if (version != ORGANIZER_LEFTPANE_VERSION) {
1162  // If version is not valid we must rebuild the left pane.
1163  bs.removeItem(leftPaneRoot);
1164  leftPaneRoot = -1;
1165  }
1166  }
1167 
1168  var queriesTitles = {
1169  "PlacesRoot": "",
1170  "History": this.getString("OrganizerQueryHistory"),
1171  // TODO: Bug 489681, Tags needs its own string in places.properties
1172  "Tags": bs.getItemTitle(PlacesUtils.tagsFolderId),
1173  "AllBookmarks": this.getString("OrganizerQueryAllBookmarks"),
1174  "Downloads": this.getString("OrganizerQueryDownloads"),
1175  "BookmarksToolbar": null,
1176  "BookmarksMenu": null,
1177  "UnfiledBookmarks": null
1178  };
1179 
1180  if (leftPaneRoot != -1) {
1181  // A valid left pane folder has been found.
1182  // Build the leftPaneQueries Map. This is used to quickly access them
1183  // associating a mnemonic name to the real item ids.
1184  delete this.leftPaneQueries;
1185  this.leftPaneQueries = {};
1186  var items = as.getItemsWithAnnotation(ORGANIZER_QUERY_ANNO, {});
1187  // While looping through queries we will also check for titles validity.
1188  for (var i = 0; i < items.length; i++) {
1189  var queryName = as.getItemAnnotation(items[i], ORGANIZER_QUERY_ANNO);
1190  this.leftPaneQueries[queryName] = items[i];
1191  // Titles could have been corrupted or the user could have changed his
1192  // locale. Check title is correctly set and eventually fix it.
1193  if (bs.getItemTitle(items[i]) != queriesTitles[queryName])
1194  bs.setItemTitle(items[i], queriesTitles[queryName]);
1195  }
1196  delete this.leftPaneFolderId;
1197  return this.leftPaneFolderId = leftPaneRoot;
1198  }
1199 
1200  var self = this;
1201  var callback = {
1202  // Helper to create an organizer special query.
1203  create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) {
1204  let itemId = bs.insertBookmark(aParentId,
1205  PlacesUtils._uri(aQueryUrl),
1206  bs.DEFAULT_INDEX,
1207  queriesTitles[aQueryName]);
1208  // Mark as special organizer query.
1209  as.setItemAnnotation(itemId, ORGANIZER_QUERY_ANNO, aQueryName,
1210  0, as.EXPIRE_NEVER);
1211  // We should never backup this, since it changes between profiles.
1212  as.setItemAnnotation(itemId, EXCLUDE_FROM_BACKUP_ANNO, 1,
1213  0, as.EXPIRE_NEVER);
1214  // Add to the queries map.
1215  self.leftPaneQueries[aQueryName] = itemId;
1216  return itemId;
1217  },
1218 
1219  // Helper to create an organizer special folder.
1220  create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) {
1221  // Left Pane Root Folder.
1222  let folderId = bs.createFolder(aParentId,
1223  queriesTitles[aFolderName],
1224  bs.DEFAULT_INDEX);
1225  // We should never backup this, since it changes between profiles.
1226  as.setItemAnnotation(folderId, EXCLUDE_FROM_BACKUP_ANNO, 1,
1227  0, as.EXPIRE_NEVER);
1228  // Disallow manipulating this folder within the organizer UI.
1229  bs.setFolderReadonly(folderId, true);
1230 
1231  if (aIsRoot) {
1232  // Mark as special left pane root.
1233  as.setItemAnnotation(folderId, ORGANIZER_FOLDER_ANNO,
1235  0, as.EXPIRE_NEVER);
1236  }
1237  else {
1238  // Mark as special organizer folder.
1239  as.setItemAnnotation(folderId, ORGANIZER_QUERY_ANNO, aFolderName,
1240  0, as.EXPIRE_NEVER);
1241  self.leftPaneQueries[aFolderName] = folderId;
1242  }
1243  return folderId;
1244  },
1245 
1246  runBatched: function CB_runBatched(aUserData) {
1247  delete self.leftPaneQueries;
1248  self.leftPaneQueries = { };
1249 
1250  // Left Pane Root Folder.
1251  leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true);
1252 
1253  // History Query.
1254  this.create_query("History", leftPaneRoot,
1255  "place:type=" +
1256  Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY +
1257  "&sort=" +
1258  Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
1259 
1260  // XXX: Downloads.
1261 
1262  // Tags Query.
1263  this.create_query("Tags", leftPaneRoot,
1264  "place:type=" +
1265  Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
1266  "&sort=" +
1267  Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
1268 
1269  // All Bookmarks Folder.
1270  allBookmarksId = this.create_folder("AllBookmarks", leftPaneRoot, false);
1271 
1272  // All Bookmarks->Bookmarks Toolbar Query.
1273  this.create_query("BookmarksToolbar", allBookmarksId,
1274  "place:folder=TOOLBAR");
1275 
1276  // All Bookmarks->Bookmarks Menu Query.
1277  this.create_query("BookmarksMenu", allBookmarksId,
1278  "place:folder=BOOKMARKS_MENU");
1279 
1280  // All Bookmarks->Unfiled Bookmarks Query.
1281  this.create_query("UnfiledBookmarks", allBookmarksId,
1282  "place:folder=UNFILED_BOOKMARKS");
1283  }
1284  };
1285  bs.runInBatchMode(callback, null);
1286 
1287  delete this.leftPaneFolderId;
1288  return this.leftPaneFolderId = leftPaneRoot;
1289  },
1290 
1294  get allBookmarksFolderId() {
1295  // ensure the left-pane root is initialized;
1296  this.leftPaneFolderId;
1297  delete this.allBookmarksFolderId;
1298  return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"];
1299  },
1300 
1308  getLeftPaneQueryNameFromId: function PU_getLeftPaneQueryNameFromId(aItemId) {
1309  var queryName = "";
1310  // If the let pane hasn't been built, use the annotation service
1311  // directly, to avoid building the left pane too early.
1312  if (this.__lookupGetter__("leftPaneFolderId")) {
1313  try {
1314  queryName = PlacesUtils.annotations.
1315  getItemAnnotation(aItemId, ORGANIZER_QUERY_ANNO);
1316  }
1317  catch (ex) {
1318  // doesn't have the annotation
1319  queryName = "";
1320  }
1321  }
1322  else {
1323  // If the left pane has already been built, use the name->id map
1324  // cached in PlacesUIUtils.
1325  for (let [name, id] in Iterator(this.leftPaneQueries)) {
1326  if (aItemId == id)
1327  queryName = name;
1328  }
1329  }
1330  return queryName;
1331  },
1332 
1338  ensureLivemarkStatusMenuItem:
1339  function PU_ensureLivemarkStatusMenuItem(aPopup) {
1340  var itemId = aPopup._resultNode.itemId;
1341 
1342  var lmStatus = null;
1343  if (PlacesUtils.annotations
1344  .itemHasAnnotation(itemId, "livemark/loadfailed"))
1345  lmStatus = "bookmarksLivemarkFailed";
1346  else if (PlacesUtils.annotations
1347  .itemHasAnnotation(itemId, "livemark/loading"))
1348  lmStatus = "bookmarksLivemarkLoading";
1349 
1350  if (lmStatus && !aPopup._lmStatusMenuItem) {
1351  // Create the status menuitem and cache it in the popup object.
1352  aPopup._lmStatusMenuItem = document.createElement("menuitem");
1353  aPopup._lmStatusMenuItem.setAttribute("lmStatus", lmStatus);
1354  aPopup._lmStatusMenuItem.setAttribute("label", this.getString(lmStatus));
1355  aPopup._lmStatusMenuItem.setAttribute("disabled", true);
1356  aPopup.insertBefore(aPopup._lmStatusMenuItem,
1357  aPopup.childNodes.item(aPopup._startMarker + 1));
1358  aPopup._startMarker++;
1359  }
1360  else if (lmStatus &&
1361  aPopup._lmStatusMenuItem.getAttribute("lmStatus") != lmStatus) {
1362  // Status has changed, update the cached status menuitem.
1363  aPopup._lmStatusMenuItem.setAttribute("label",
1364  this.getString(lmStatus));
1365  }
1366  else if (!lmStatus && aPopup._lmStatusMenuItem){
1367  // No status, remove the cached menuitem.
1368  aPopup.removeChild(aPopup._lmStatusMenuItem);
1369  aPopup._lmStatusMenuItem = null;
1370  aPopup._startMarker--;
1371  }
1372  }
1373 };
function start(ch)
var PlacesUIUtils
Definition: utils.js:85
const LMANNO_SITEURI
Definition: utils.js:62
const ORGANIZER_QUERY_ANNO
Definition: utils.js:64
const ORGANIZER_LEFTPANE_VERSION
Definition: utils.js:65
var TAB_DROP_TYPE
function LOG(str)
Definition: utils.js:43
var Cr
Definition: utils.js:49
function openUILinkIn(url, where, allowThirdPartyFixup, postData, referrerUrl)
onPageChanged aValue
Definition: FeedWriter.js:1395
var pref
Definition: openLocation.js:44
function doc() browser.contentDocument
var Ci
Definition: utils.js:47
function getTopWin()
const LMANNO_FEEDURI
Definition: utils.js:61
sidebarFactory createInstance
Definition: nsSidebar.js:351
function asQuery(aNode)
Definition: utils.js:83
version(170)
while((node=formNodes.iterateNext()))
function asFullVisit(aNode)
Definition: utils.js:81
const LOAD_IN_SIDEBAR_ANNO
Definition: utils.js:58
getService(Ci.sbIFaceplateManager)
let window
function QI_node(aNode, aIID)
Definition: utils.js:77
const DESCRIPTION_ANNO
Definition: utils.js:59
function getBrowserURL()
grep callback
return null
Definition: FeedWriter.js:1143
const ORGANIZER_FOLDER_ANNO
Definition: utils.js:63
function whereToOpenLink(e, ignoreButton, ignoreAlt)
let node
const NEWLINE
Definition: utils.js:74
function NS_ASSERT(cond, msg)
Definition: httpd.js:70
var uri
Definition: FeedWriter.js:1135
countRef value
Definition: FeedWriter.js:1423
let promptService
var Cc
Definition: utils.js:48
const EXCLUDE_FROM_BACKUP_ANNO
Definition: utils.js:66
const GUID_ANNO
Definition: utils.js:60
function asVisit(aNode)
Definition: utils.js:80
observe data
Definition: FeedWriter.js:1329
_getSelectedPageStyle s i
function asContainer(aNode)
Definition: utils.js:82
__defineGetter__("PlacesUtils", function(){delete this.PlacesUtils var tmpScope={};Components.utils.import("resource://gre/modules/utils.js", tmpScope);return this.PlacesUtils=tmpScope.PlacesUtils;})
_updateTextAndScrollDataForFrame aData