Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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 { defer, all, resolve } = require('../../core/promise'); |
michael@0 | 16 | const { safeMerge, omit } = require('../../util/object'); |
michael@0 | 17 | const historyService = Cc['@mozilla.org/browser/nav-history-service;1'] |
michael@0 | 18 | .getService(Ci.nsINavHistoryService); |
michael@0 | 19 | const bookmarksService = Cc['@mozilla.org/browser/nav-bookmarks-service;1'] |
michael@0 | 20 | .getService(Ci.nsINavBookmarksService); |
michael@0 | 21 | const { request, response } = require('../../addon/host'); |
michael@0 | 22 | const { newURI } = require('../../url/utils'); |
michael@0 | 23 | const { send } = require('../../addon/events'); |
michael@0 | 24 | const { on, emit } = require('../../event/core'); |
michael@0 | 25 | const { filter } = require('../../event/utils'); |
michael@0 | 26 | |
michael@0 | 27 | const ROOT_FOLDERS = [ |
michael@0 | 28 | bookmarksService.unfiledBookmarksFolder, bookmarksService.toolbarFolder, |
michael@0 | 29 | bookmarksService.bookmarksMenuFolder |
michael@0 | 30 | ]; |
michael@0 | 31 | |
michael@0 | 32 | const EVENT_MAP = { |
michael@0 | 33 | 'sdk-places-query': queryReceiver |
michael@0 | 34 | }; |
michael@0 | 35 | |
michael@0 | 36 | // Properties that need to be manually |
michael@0 | 37 | // copied into a nsINavHistoryQuery object |
michael@0 | 38 | const MANUAL_QUERY_PROPERTIES = [ |
michael@0 | 39 | 'uri', 'folder', 'tags', 'url', 'folder' |
michael@0 | 40 | ]; |
michael@0 | 41 | |
michael@0 | 42 | const PLACES_PROPERTIES = [ |
michael@0 | 43 | 'uri', 'title', 'accessCount', 'time' |
michael@0 | 44 | ]; |
michael@0 | 45 | |
michael@0 | 46 | function execute (queries, options) { |
michael@0 | 47 | let deferred = defer(); |
michael@0 | 48 | let root = historyService |
michael@0 | 49 | .executeQueries(queries, queries.length, options).root; |
michael@0 | 50 | |
michael@0 | 51 | let items = collect([], root); |
michael@0 | 52 | deferred.resolve(items); |
michael@0 | 53 | return deferred.promise; |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | function collect (acc, node) { |
michael@0 | 57 | node.containerOpen = true; |
michael@0 | 58 | for (let i = 0; i < node.childCount; i++) { |
michael@0 | 59 | let child = node.getChild(i); |
michael@0 | 60 | acc.push(child); |
michael@0 | 61 | if (child.type === child.RESULT_TYPE_FOLDER) { |
michael@0 | 62 | let container = child.QueryInterface(Ci.nsINavHistoryContainerResultNode); |
michael@0 | 63 | collect(acc, container); |
michael@0 | 64 | } |
michael@0 | 65 | } |
michael@0 | 66 | node.containerOpen = false; |
michael@0 | 67 | return acc; |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | function query (queries, options) { |
michael@0 | 71 | queries = queries || []; |
michael@0 | 72 | options = options || {}; |
michael@0 | 73 | let deferred = defer(); |
michael@0 | 74 | let optionsObj, queryObjs; |
michael@0 | 75 | |
michael@0 | 76 | try { |
michael@0 | 77 | optionsObj = historyService.getNewQueryOptions(); |
michael@0 | 78 | queryObjs = [].concat(queries).map(createQuery); |
michael@0 | 79 | if (!queryObjs.length) { |
michael@0 | 80 | queryObjs = [historyService.getNewQuery()]; |
michael@0 | 81 | } |
michael@0 | 82 | safeMerge(optionsObj, options); |
michael@0 | 83 | } catch (e) { |
michael@0 | 84 | deferred.reject(e); |
michael@0 | 85 | return deferred.promise; |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | /* |
michael@0 | 89 | * Currently `places:` queries are not supported |
michael@0 | 90 | */ |
michael@0 | 91 | optionsObj.excludeQueries = true; |
michael@0 | 92 | |
michael@0 | 93 | execute(queryObjs, optionsObj).then(function (results) { |
michael@0 | 94 | if (optionsObj.queryType === 0) { |
michael@0 | 95 | return results.map(normalize); |
michael@0 | 96 | } else if (optionsObj.queryType === 1) { |
michael@0 | 97 | // Formats query results into more standard |
michael@0 | 98 | // data structures for returning |
michael@0 | 99 | return all(results.map(({itemId}) => |
michael@0 | 100 | send('sdk-places-bookmarks-get', { id: itemId }))); |
michael@0 | 101 | } |
michael@0 | 102 | }).then(deferred.resolve, deferred.reject); |
michael@0 | 103 | |
michael@0 | 104 | return deferred.promise; |
michael@0 | 105 | } |
michael@0 | 106 | exports.query = query; |
michael@0 | 107 | |
michael@0 | 108 | function createQuery (query) { |
michael@0 | 109 | query = query || {}; |
michael@0 | 110 | let queryObj = historyService.getNewQuery(); |
michael@0 | 111 | |
michael@0 | 112 | safeMerge(queryObj, omit(query, MANUAL_QUERY_PROPERTIES)); |
michael@0 | 113 | |
michael@0 | 114 | if (query.tags && Array.isArray(query.tags)) |
michael@0 | 115 | queryObj.tags = query.tags; |
michael@0 | 116 | if (query.uri || query.url) |
michael@0 | 117 | queryObj.uri = newURI(query.uri || query.url); |
michael@0 | 118 | if (query.folder) |
michael@0 | 119 | queryObj.setFolders([query.folder], 1); |
michael@0 | 120 | return queryObj; |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | function queryReceiver (message) { |
michael@0 | 124 | let queries = message.data.queries || message.data.query; |
michael@0 | 125 | let options = message.data.options; |
michael@0 | 126 | let resData = { |
michael@0 | 127 | id: message.id, |
michael@0 | 128 | event: message.event |
michael@0 | 129 | }; |
michael@0 | 130 | |
michael@0 | 131 | query(queries, options).then(results => { |
michael@0 | 132 | resData.data = results; |
michael@0 | 133 | respond(resData); |
michael@0 | 134 | }, reason => { |
michael@0 | 135 | resData.error = reason; |
michael@0 | 136 | respond(resData); |
michael@0 | 137 | }); |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | /* |
michael@0 | 141 | * Converts a nsINavHistoryResultNode into a plain object |
michael@0 | 142 | * |
michael@0 | 143 | * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryResultNode |
michael@0 | 144 | */ |
michael@0 | 145 | function normalize (historyObj) { |
michael@0 | 146 | return PLACES_PROPERTIES.reduce((obj, prop) => { |
michael@0 | 147 | if (prop === 'uri') |
michael@0 | 148 | obj.url = historyObj.uri; |
michael@0 | 149 | else if (prop === 'time') { |
michael@0 | 150 | // Cast from microseconds to milliseconds |
michael@0 | 151 | obj.time = Math.floor(historyObj.time / 1000) |
michael@0 | 152 | } else if (prop === 'accessCount') |
michael@0 | 153 | obj.visitCount = historyObj[prop]; |
michael@0 | 154 | else |
michael@0 | 155 | obj[prop] = historyObj[prop]; |
michael@0 | 156 | return obj; |
michael@0 | 157 | }, {}); |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | /* |
michael@0 | 161 | * Hook into host |
michael@0 | 162 | */ |
michael@0 | 163 | |
michael@0 | 164 | let reqStream = filter(request, function (data) /sdk-places-query/.test(data.event)); |
michael@0 | 165 | on(reqStream, 'data', function (e) { |
michael@0 | 166 | if (EVENT_MAP[e.event]) EVENT_MAP[e.event](e); |
michael@0 | 167 | }); |
michael@0 | 168 | |
michael@0 | 169 | function respond (data) { |
michael@0 | 170 | emit(response, 'data', data); |
michael@0 | 171 | } |