addon-sdk/source/lib/sdk/places/utils.js

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

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

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

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 'use strict';
michael@0 6
michael@0 7 module.metadata = {
michael@0 8 "stability": "experimental",
michael@0 9 "engines": {
michael@0 10 "Firefox": "*"
michael@0 11 }
michael@0 12 };
michael@0 13
michael@0 14 const { Cc, Ci } = require('chrome');
michael@0 15 const { Class } = require('../core/heritage');
michael@0 16 const { method } = require('../lang/functional');
michael@0 17 const { defer, promised, all } = require('../core/promise');
michael@0 18 const { send } = require('../addon/events');
michael@0 19 const { EventTarget } = require('../event/target');
michael@0 20 const { merge } = require('../util/object');
michael@0 21 const bmsrv = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
michael@0 22 getService(Ci.nsINavBookmarksService);
michael@0 23
michael@0 24 /*
michael@0 25 * TreeNodes are used to construct dependency trees
michael@0 26 * for BookmarkItems
michael@0 27 */
michael@0 28 let TreeNode = Class({
michael@0 29 initialize: function (value) {
michael@0 30 this.value = value;
michael@0 31 this.children = [];
michael@0 32 },
michael@0 33 add: function (values) {
michael@0 34 [].concat(values).forEach(value => {
michael@0 35 this.children.push(value instanceof TreeNode ? value : TreeNode(value));
michael@0 36 });
michael@0 37 },
michael@0 38 get length () {
michael@0 39 let count = 0;
michael@0 40 this.walk(() => count++);
michael@0 41 // Do not count the current node
michael@0 42 return --count;
michael@0 43 },
michael@0 44 get: method(get),
michael@0 45 walk: method(walk),
michael@0 46 toString: function () '[object TreeNode]'
michael@0 47 });
michael@0 48 exports.TreeNode = TreeNode;
michael@0 49
michael@0 50 /*
michael@0 51 * Descends down from `node` applying `fn` to each in order.
michael@0 52 * `fn` can return values or promises -- if promise returned,
michael@0 53 * children are not processed until resolved. `fn` is passed
michael@0 54 * one argument, the current node, `curr`.
michael@0 55 */
michael@0 56 function walk (curr, fn) {
michael@0 57 return promised(fn)(curr).then(val => {
michael@0 58 return all(curr.children.map(child => walk(child, fn)));
michael@0 59 });
michael@0 60 }
michael@0 61
michael@0 62 /*
michael@0 63 * Descends from the TreeNode `node`, returning
michael@0 64 * the node with value `value` if found or `null`
michael@0 65 * otherwise
michael@0 66 */
michael@0 67 function get (node, value) {
michael@0 68 if (node.value === value) return node;
michael@0 69 for (let child of node.children) {
michael@0 70 let found = get(child, value);
michael@0 71 if (found) return found;
michael@0 72 }
michael@0 73 return null;
michael@0 74 }
michael@0 75
michael@0 76 /*
michael@0 77 * Constructs a tree of bookmark nodes
michael@0 78 * returning the root (value: null);
michael@0 79 */
michael@0 80
michael@0 81 function constructTree (items) {
michael@0 82 let root = TreeNode(null);
michael@0 83 items.forEach(treeify.bind(null, root));
michael@0 84
michael@0 85 function treeify (root, item) {
michael@0 86 // If node already exists, skip
michael@0 87 let node = root.get(item);
michael@0 88 if (node) return node;
michael@0 89 node = TreeNode(item);
michael@0 90
michael@0 91 let parentNode = item.group ? treeify(root, item.group) : root;
michael@0 92 parentNode.add(node);
michael@0 93
michael@0 94 return node;
michael@0 95 }
michael@0 96
michael@0 97 return root;
michael@0 98 }
michael@0 99 exports.constructTree = constructTree;
michael@0 100
michael@0 101 /*
michael@0 102 * Shortcut for converting an id, or an object with an id, into
michael@0 103 * an object with corresponding bookmark data
michael@0 104 */
michael@0 105 function fetchItem (item)
michael@0 106 send('sdk-places-bookmarks-get', { id: item.id || item })
michael@0 107 exports.fetchItem = fetchItem;
michael@0 108
michael@0 109 /*
michael@0 110 * Takes an ID or an object with ID and checks it against
michael@0 111 * the root bookmark folders
michael@0 112 */
michael@0 113 function isRootGroup (id) {
michael@0 114 id = id && id.id;
michael@0 115 return ~[bmsrv.bookmarksMenuFolder, bmsrv.toolbarFolder,
michael@0 116 bmsrv.unfiledBookmarksFolder
michael@0 117 ].indexOf(id);
michael@0 118 }
michael@0 119 exports.isRootGroup = isRootGroup;
michael@0 120
michael@0 121 /*
michael@0 122 * Merges appropriate options into query based off of url
michael@0 123 * 4 scenarios:
michael@0 124 *
michael@0 125 * 'moz.com' // domain: moz.com, domainIsHost: true
michael@0 126 * --> 'http://moz.com', 'http://moz.com/thunderbird'
michael@0 127 * '*.moz.com' // domain: moz.com, domainIsHost: false
michael@0 128 * --> 'http://moz.com', 'http://moz.com/index', 'http://ff.moz.com/test'
michael@0 129 * 'http://moz.com' // url: http://moz.com/, urlIsPrefix: false
michael@0 130 * --> 'http://moz.com/'
michael@0 131 * 'http://moz.com/*' // url: http://moz.com/, urlIsPrefix: true
michael@0 132 * --> 'http://moz.com/', 'http://moz.com/thunderbird'
michael@0 133 */
michael@0 134
michael@0 135 function urlQueryParser (query, url) {
michael@0 136 if (!url) return;
michael@0 137 if (/^https?:\/\//.test(url)) {
michael@0 138 query.uri = url.charAt(url.length - 1) === '/' ? url : url + '/';
michael@0 139 if (/\*$/.test(url)) {
michael@0 140 query.uri = url.replace(/\*$/, '');
michael@0 141 query.uriIsPrefix = true;
michael@0 142 }
michael@0 143 } else {
michael@0 144 if (/^\*/.test(url)) {
michael@0 145 query.domain = url.replace(/^\*\./, '');
michael@0 146 query.domainIsHost = false;
michael@0 147 } else {
michael@0 148 query.domain = url;
michael@0 149 query.domainIsHost = true;
michael@0 150 }
michael@0 151 }
michael@0 152 }
michael@0 153 exports.urlQueryParser = urlQueryParser;
michael@0 154
michael@0 155 /*
michael@0 156 * Takes an EventEmitter and returns a promise that
michael@0 157 * aggregates results and handles a bulk resolve and reject
michael@0 158 */
michael@0 159
michael@0 160 function promisedEmitter (emitter) {
michael@0 161 let { promise, resolve, reject } = defer();
michael@0 162 let errors = [];
michael@0 163 emitter.on('error', error => errors.push(error));
michael@0 164 emitter.on('end', (items) => {
michael@0 165 if (errors.length) reject(errors[0]);
michael@0 166 else resolve(items);
michael@0 167 });
michael@0 168 return promise;
michael@0 169 }
michael@0 170 exports.promisedEmitter = promisedEmitter;
michael@0 171
michael@0 172
michael@0 173 // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryQueryOptions
michael@0 174 function createQuery (type, query) {
michael@0 175 query = query || {};
michael@0 176 let qObj = {
michael@0 177 searchTerms: query.query
michael@0 178 };
michael@0 179
michael@0 180 urlQueryParser(qObj, query.url);
michael@0 181
michael@0 182 // 0 === history
michael@0 183 if (type === 0) {
michael@0 184 // PRTime used by query is in microseconds, not milliseconds
michael@0 185 qObj.beginTime = (query.from || 0) * 1000;
michael@0 186 qObj.endTime = (query.to || new Date()) * 1000;
michael@0 187
michael@0 188 // Set reference time to Epoch
michael@0 189 qObj.beginTimeReference = 0;
michael@0 190 qObj.endTimeReference = 0;
michael@0 191 }
michael@0 192 // 1 === bookmarks
michael@0 193 else if (type === 1) {
michael@0 194 qObj.tags = query.tags;
michael@0 195 qObj.folder = query.group && query.group.id;
michael@0 196 }
michael@0 197 // 2 === unified (not implemented on platform)
michael@0 198 else if (type === 2) {
michael@0 199
michael@0 200 }
michael@0 201
michael@0 202 return qObj;
michael@0 203 }
michael@0 204 exports.createQuery = createQuery;
michael@0 205
michael@0 206 // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryQueryOptions
michael@0 207
michael@0 208 const SORT_MAP = {
michael@0 209 title: 1,
michael@0 210 date: 3, // sort by visit date
michael@0 211 url: 5,
michael@0 212 visitCount: 7,
michael@0 213 // keywords currently unsupported
michael@0 214 // keyword: 9,
michael@0 215 dateAdded: 11, // bookmarks only
michael@0 216 lastModified: 13 // bookmarks only
michael@0 217 };
michael@0 218
michael@0 219 function createQueryOptions (type, options) {
michael@0 220 options = options || {};
michael@0 221 let oObj = {};
michael@0 222 oObj.sortingMode = SORT_MAP[options.sort] || 0;
michael@0 223 if (options.descending && options.sort)
michael@0 224 oObj.sortingMode++;
michael@0 225
michael@0 226 // Resolve to default sort if ineligible based on query type
michael@0 227 if (type === 0 && // history
michael@0 228 (options.sort === 'dateAdded' || options.sort === 'lastModified'))
michael@0 229 oObj.sortingMode = 0;
michael@0 230
michael@0 231 oObj.maxResults = typeof options.count === 'number' ? options.count : 0;
michael@0 232
michael@0 233 oObj.queryType = type;
michael@0 234
michael@0 235 return oObj;
michael@0 236 }
michael@0 237 exports.createQueryOptions = createQueryOptions;
michael@0 238
michael@0 239
michael@0 240 function mapBookmarkItemType (type) {
michael@0 241 if (typeof type === 'number') {
michael@0 242 if (bmsrv.TYPE_BOOKMARK === type) return 'bookmark';
michael@0 243 if (bmsrv.TYPE_FOLDER === type) return 'group';
michael@0 244 if (bmsrv.TYPE_SEPARATOR === type) return 'separator';
michael@0 245 } else {
michael@0 246 if ('bookmark' === type) return bmsrv.TYPE_BOOKMARK;
michael@0 247 if ('group' === type) return bmsrv.TYPE_FOLDER;
michael@0 248 if ('separator' === type) return bmsrv.TYPE_SEPARATOR;
michael@0 249 }
michael@0 250 }
michael@0 251 exports.mapBookmarkItemType = mapBookmarkItemType;

mercurial