зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1428924 - Policy: Allow creation of bookmarks in the Bookmarks toolbar, Menu, or a folder inside them. r=mak
MozReview-Commit-ID: 2k2Cl10AV9C --HG-- extra : rebase_source : 803c7f88d64159e3ba1627a53ee8a3a24f758b84
This commit is contained in:
Родитель
25f5f137e8
Коммит
ca99682d13
|
@ -10,6 +10,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gXulStore",
|
|||
"@mozilla.org/xul/xulstore;1",
|
||||
"nsIXULStore");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
|
||||
});
|
||||
|
||||
const PREF_LOGLEVEL = "browser.policies.loglevel";
|
||||
const PREF_MENU_ALREADY_DISPLAYED = "browser.policies.menuBarWasDisplayed";
|
||||
const BROWSER_DOCUMENT_URL = "chrome://browser/content/browser.xul";
|
||||
|
@ -69,6 +73,12 @@ this.Policies = {
|
|||
}
|
||||
},
|
||||
|
||||
"Bookmarks": {
|
||||
onBeforeUIStartup(manager, param) {
|
||||
BookmarksPolicies.processBookmarks(param);
|
||||
}
|
||||
},
|
||||
|
||||
"Cookies": {
|
||||
onBeforeUIStartup(manager, param) {
|
||||
addAllowDenyPermissions("cookie", param.Allow, param.Block);
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* A Bookmark object received through the policy engine will be an
|
||||
* object with the following properties:
|
||||
*
|
||||
* - URL (nsIURI)
|
||||
* (required) The URL for this bookmark
|
||||
*
|
||||
* - Title (string)
|
||||
* (required) The title for this bookmark
|
||||
*
|
||||
* - Placement (string)
|
||||
* (optional) Either "toolbar" or "menu". If missing or invalid,
|
||||
* "toolbar" will be used
|
||||
*
|
||||
* - Folder (string)
|
||||
* (optional) The name of the folder to put this bookmark into.
|
||||
* If present, a folder with this name will be created in the
|
||||
* chosen placement above, and the bookmark will be created there.
|
||||
* If missing, the bookmark will be created directly into the
|
||||
* chosen placement.
|
||||
*
|
||||
* - Favicon (nsIURI)
|
||||
* (optional) An http:, https: or data: URL with the favicon.
|
||||
* If possible, we recommend against using this property, in order
|
||||
* to keep the json file small.
|
||||
* If a favicon is not provided through the policy, it will be loaded
|
||||
* naturally after the user first visits the bookmark.
|
||||
*
|
||||
*
|
||||
* Note: The Policy Engine automatically converts the strings given to
|
||||
* the URL and favicon properties into a nsIURI object.
|
||||
*
|
||||
* The schema for this object is defined in policies-schema.json.
|
||||
*/
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
const PREF_LOGLEVEL = "browser.policies.loglevel";
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
|
||||
return new ConsoleAPI({
|
||||
prefix: "BookmarksPolicies.jsm",
|
||||
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
|
||||
// messages during development. See LOG_LEVELS in Console.jsm for details.
|
||||
maxLogLevel: "error",
|
||||
maxLogLevelPref: PREF_LOGLEVEL,
|
||||
});
|
||||
});
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "BookmarksPolicies" ];
|
||||
|
||||
this.BookmarksPolicies = {
|
||||
// These prefixes must only contain characters
|
||||
// allowed by PlacesUtils.isValidGuid
|
||||
BOOKMARK_GUID_PREFIX: "PolB-",
|
||||
FOLDER_GUID_PREFIX: "PolF-",
|
||||
|
||||
/*
|
||||
* Process the bookmarks specified by the policy engine.
|
||||
*
|
||||
* @param param
|
||||
* This will be an array of bookmarks objects, as
|
||||
* described on the top of this file.
|
||||
*/
|
||||
processBookmarks(param) {
|
||||
calculateLists(param).then(async function addRemoveBookmarks(results) {
|
||||
for (let bookmark of results.add.values()) {
|
||||
await insertBookmark(bookmark).catch(log.error);
|
||||
}
|
||||
for (let bookmark of results.remove.values()) {
|
||||
await PlacesUtils.bookmarks.remove(bookmark).catch(log.error);
|
||||
}
|
||||
for (let bookmark of results.emptyFolders.values()) {
|
||||
await PlacesUtils.bookmarks.remove(bookmark).catch(log.error);
|
||||
}
|
||||
|
||||
gFoldersMapPromise.then(map => map.clear());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This function calculates the differences between the existing bookmarks
|
||||
* that are managed by the policy engine (which are known through a guid
|
||||
* prefix) and the specified bookmarks in the policy file.
|
||||
* They can differ if the policy file has changed.
|
||||
*
|
||||
* @param specifiedBookmarks
|
||||
* This will be an array of bookmarks objects, as
|
||||
* described on the top of this file.
|
||||
*/
|
||||
async function calculateLists(specifiedBookmarks) {
|
||||
// --------- STEP 1 ---------
|
||||
// Build two Maps (one with the existing bookmarks, another with
|
||||
// the specified bookmarks), to make iteration quicker.
|
||||
|
||||
// LIST A
|
||||
// MAP of url (string) -> bookmarks objects from the Policy Engine
|
||||
let specifiedBookmarksMap = new Map();
|
||||
for (let bookmark of specifiedBookmarks) {
|
||||
specifiedBookmarksMap.set(bookmark.URL.spec, bookmark);
|
||||
}
|
||||
|
||||
// LIST B
|
||||
// MAP of url (string) -> bookmarks objects from Places
|
||||
let existingBookmarksMap = new Map();
|
||||
await PlacesUtils.bookmarks.fetch(
|
||||
{ guidPrefix: BookmarksPolicies.BOOKMARK_GUID_PREFIX },
|
||||
(bookmark) => existingBookmarksMap.set(bookmark.url.href, bookmark)
|
||||
);
|
||||
|
||||
// --------- STEP 2 ---------
|
||||
//
|
||||
// /=====/====\=====\
|
||||
// / / \ \
|
||||
// | | | |
|
||||
// | A | {} | B |
|
||||
// | | | |
|
||||
// \ \ / /
|
||||
// \=====\====/=====/
|
||||
//
|
||||
// Find the intersection of the two lists. Items in the intersection
|
||||
// are removed from the original lists.
|
||||
//
|
||||
// The items remaining in list A are new bookmarks to be added.
|
||||
// The items remaining in list B are old bookmarks to be removed.
|
||||
//
|
||||
// There's nothing to do with items in the intersection, so there's no
|
||||
// need to keep track of them.
|
||||
//
|
||||
// BONUS: It's necessary to keep track of the folder names that were
|
||||
// seen, to make sure we remove the ones that were left empty.
|
||||
|
||||
let foldersSeen = new Set();
|
||||
|
||||
for (let [url, item] of specifiedBookmarksMap) {
|
||||
foldersSeen.add(item.Folder);
|
||||
|
||||
if (existingBookmarksMap.has(url)) {
|
||||
log.debug(`Bookmark intersection: ${url}`);
|
||||
// If this specified bookmark exists in the existing bookmarks list,
|
||||
// we can remove it from both lists as it's in the intersection.
|
||||
specifiedBookmarksMap.delete(url);
|
||||
existingBookmarksMap.delete(url);
|
||||
}
|
||||
}
|
||||
|
||||
for (let url of specifiedBookmarksMap.keys()) {
|
||||
log.debug(`Bookmark to add: ${url}`);
|
||||
}
|
||||
|
||||
for (let url of existingBookmarksMap.keys()) {
|
||||
log.debug(`Bookmark to remove: ${url}`);
|
||||
}
|
||||
|
||||
// SET of folders to be deleted (bookmarks object from Places)
|
||||
let foldersToRemove = new Set();
|
||||
|
||||
// If no bookmarks will be deleted, then no folder will
|
||||
// need to be deleted either, so this next section can be skipped.
|
||||
if (existingBookmarksMap.size > 0) {
|
||||
await PlacesUtils.bookmarks.fetch(
|
||||
{ guidPrefix: BookmarksPolicies.FOLDER_GUID_PREFIX },
|
||||
(folder) => {
|
||||
if (!foldersSeen.has(folder.title)) {
|
||||
log.debug(`Folder to remove: ${folder.title}`);
|
||||
foldersToRemove.add(folder);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
add: specifiedBookmarksMap,
|
||||
remove: existingBookmarksMap,
|
||||
emptyFolders: foldersToRemove
|
||||
};
|
||||
}
|
||||
|
||||
async function insertBookmark(bookmark) {
|
||||
let parentGuid = await getParentGuid(bookmark.Placement,
|
||||
bookmark.Folder);
|
||||
|
||||
await PlacesUtils.bookmarks.insert({
|
||||
url: bookmark.URL,
|
||||
title: bookmark.Title,
|
||||
guid: generateGuidWithPrefix(BookmarksPolicies.BOOKMARK_GUID_PREFIX),
|
||||
parentGuid,
|
||||
});
|
||||
|
||||
if (bookmark.Favicon) {
|
||||
await setFaviconForBookmark(bookmark).catch(
|
||||
() => log.error(`Error setting favicon for ${bookmark.Title}`));
|
||||
}
|
||||
}
|
||||
|
||||
async function setFaviconForBookmark(bookmark) {
|
||||
let faviconURI;
|
||||
let nullPrincipal = Services.scriptSecurityManager.createNullPrincipal({});
|
||||
|
||||
switch (bookmark.Favicon.scheme) {
|
||||
case "data":
|
||||
// data urls must first call replaceFaviconDataFromDataURL, using a
|
||||
// fake URL. Later, it's needed to call setAndFetchFaviconForPage
|
||||
// with the same URL.
|
||||
faviconURI = Services.io.newURI("fake-favicon-uri:" + bookmark.URL.spec);
|
||||
|
||||
PlacesUtils.favicons.replaceFaviconDataFromDataURL(
|
||||
faviconURI,
|
||||
bookmark.Favicon.spec,
|
||||
0, /* max expiration length */
|
||||
nullPrincipal
|
||||
);
|
||||
break;
|
||||
|
||||
case "http":
|
||||
case "https":
|
||||
faviconURI = bookmark.Favicon;
|
||||
break;
|
||||
|
||||
default:
|
||||
log.error(`Bad URL given for favicon on bookmark "${bookmark.Title}"`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
PlacesUtils.favicons.setAndFetchFaviconForPage(
|
||||
bookmark.URL,
|
||||
faviconURI,
|
||||
false, /* forceReload */
|
||||
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
|
||||
resolve,
|
||||
nullPrincipal
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function generateGuidWithPrefix(prefix) {
|
||||
// Generates a random GUID and replace its beginning with the given
|
||||
// prefix. We do this instead of just prepending the prefix to keep
|
||||
// the correct character length.
|
||||
return prefix + PlacesUtils.history.makeGuid().substring(prefix.length);
|
||||
}
|
||||
|
||||
// Cache of folder names to guids to be used by the getParentGuid
|
||||
// function. The name consists in the parentGuid (which should always
|
||||
// be the menuGuid or the toolbarGuid) + the folder title. This is to
|
||||
// support having the same folder name in both the toolbar and menu.
|
||||
XPCOMUtils.defineLazyGetter(this, "gFoldersMapPromise", () => {
|
||||
return new Promise(resolve => {
|
||||
let foldersMap = new Map();
|
||||
return PlacesUtils.bookmarks.fetch(
|
||||
{
|
||||
guidPrefix: BookmarksPolicies.FOLDER_GUID_PREFIX
|
||||
},
|
||||
(result) => {
|
||||
foldersMap.set(`${result.parentGuid}|${result.title}`, result.guid);
|
||||
}
|
||||
).then(() => resolve(foldersMap));
|
||||
});
|
||||
});
|
||||
|
||||
async function getParentGuid(placement, folderTitle) {
|
||||
// Defaults to toolbar if no placement was given.
|
||||
let parentGuid = (placement == "menu") ?
|
||||
PlacesUtils.bookmarks.menuGuid :
|
||||
PlacesUtils.bookmarks.toolbarGuid;
|
||||
|
||||
if (!folderTitle) {
|
||||
// If no folderTitle is given, this bookmark is to be placed directly
|
||||
// into the toolbar or menu.
|
||||
return parentGuid;
|
||||
}
|
||||
|
||||
let foldersMap = await gFoldersMapPromise;
|
||||
let folderName = `${parentGuid}|${folderTitle}`;
|
||||
|
||||
if (foldersMap.has(folderName)) {
|
||||
return foldersMap.get(folderName);
|
||||
}
|
||||
|
||||
let guid = generateGuidWithPrefix(BookmarksPolicies.FOLDER_GUID_PREFIX);
|
||||
await PlacesUtils.bookmarks.insert({
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: folderTitle,
|
||||
guid,
|
||||
parentGuid
|
||||
});
|
||||
|
||||
foldersMap.set(folderName, guid);
|
||||
return guid;
|
||||
}
|
|
@ -6,3 +6,7 @@
|
|||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Firefox", "Enterprise Policies")
|
||||
|
||||
EXTRA_JS_MODULES.policies += [
|
||||
'BookmarksPolicies.jsm',
|
||||
]
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"policies": {
|
||||
"DisplayBookmarksToolbar": true,
|
||||
|
||||
"Bookmarks": [
|
||||
{
|
||||
"Title": "Bookmark 1",
|
||||
"URL": "https://bookmark1.example.com"
|
||||
},
|
||||
{
|
||||
"Title": "Bookmark 2",
|
||||
"URL": "https://bookmark2.example.com",
|
||||
"Favicon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=",
|
||||
"Folder": "Folder 1"
|
||||
},
|
||||
{
|
||||
"Title": "Bookmark 3",
|
||||
"URL": "https://bookmark3.example.com",
|
||||
"Favicon": "https://www.mozilla.org/favicon.ico",
|
||||
"Placement": "menu"
|
||||
},
|
||||
{
|
||||
"Title": "Bookmark 4",
|
||||
"URL": "https://bookmark4.example.com",
|
||||
"Favicon": "https://www.mozilla.org/favicon.ico",
|
||||
"Folder": "Folder 1"
|
||||
},
|
||||
{
|
||||
"Title": "Bookmark 5",
|
||||
"URL": "https://bookmark5.example.com",
|
||||
"Favicon": "https://www.mozilla.org/favicon.ico",
|
||||
"Placement": "menu",
|
||||
"Folder": "Folder 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -42,6 +42,39 @@
|
|||
"enum": [true]
|
||||
},
|
||||
|
||||
"Bookmarks": {
|
||||
"description": "Allows the creation of bookmarks in the Bookmarks bar, Bookmarks menu, or a specified folder inside them.",
|
||||
"first_available": "60.0",
|
||||
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Title": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
"URL": {
|
||||
"type": "URL"
|
||||
},
|
||||
|
||||
"Favicon": {
|
||||
"type": "URL"
|
||||
},
|
||||
|
||||
"Placement": {
|
||||
"type": "string",
|
||||
"enum": ["toolbar", "menu"]
|
||||
},
|
||||
|
||||
"Folder": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["title", "URL"]
|
||||
}
|
||||
},
|
||||
|
||||
"Cookies": {
|
||||
"description": "Allow or deny websites to set cookies.",
|
||||
"first_available": "60.0",
|
||||
|
|
|
@ -18,6 +18,7 @@ support-files =
|
|||
[browser_policy_block_about_profiles.js]
|
||||
[browser_policy_block_about_support.js]
|
||||
[browser_policy_block_set_desktop_background.js]
|
||||
[browser_policy_bookmarks.js]
|
||||
[browser_policy_default_browser_check.js]
|
||||
[browser_policy_disable_formhistory.js]
|
||||
[browser_policy_disable_fxscreenshots.js]
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
const FAVICON_DATA = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=";
|
||||
|
||||
const { BookmarksPolicies } = ChromeUtils.import("resource:///modules/policies/BookmarksPolicies.jsm", {});
|
||||
|
||||
let CURRENT_POLICY;
|
||||
|
||||
const BASE_POLICY = {
|
||||
"policies": {
|
||||
"display_bookmarks_toolbar": true,
|
||||
"Bookmarks": [
|
||||
{
|
||||
"Title": "Bookmark 1",
|
||||
"URL": "https://bookmark1.example.com/",
|
||||
"Favicon": FAVICON_DATA
|
||||
},
|
||||
{
|
||||
"Title": "Bookmark 2",
|
||||
"URL": "https://bookmark2.example.com/",
|
||||
"Folder": "Folder 1"
|
||||
},
|
||||
{
|
||||
"Title": "Bookmark 3",
|
||||
"URL": "https://bookmark3.example.com/",
|
||||
"Placement": "menu"
|
||||
},
|
||||
{
|
||||
"Title": "Bookmark 4",
|
||||
"URL": "https://bookmark4.example.com/",
|
||||
"Folder": "Folder 1"
|
||||
},
|
||||
{
|
||||
"Title": "Bookmark 5",
|
||||
"URL": "https://bookmark5.example.com/",
|
||||
"Placement": "menu",
|
||||
"Folder": "Folder 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* =================================
|
||||
* = HELPER FUNCTIONS FOR THE TEST =
|
||||
* =================================
|
||||
*/
|
||||
function deepClone(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
function findBookmarkInPolicy(bookmark) {
|
||||
// Find the entry in the given policy that corresponds
|
||||
// to this bookmark object from Places.
|
||||
for (let entry of CURRENT_POLICY.policies.Bookmarks) {
|
||||
if (entry.Title == bookmark.title) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function promiseAllChangesMade({itemsToAdd, itemsToRemove}) {
|
||||
return new Promise(resolve => {
|
||||
let bmObserver = {
|
||||
onItemAdded() {
|
||||
itemsToAdd--;
|
||||
if (itemsToAdd == 0 && itemsToRemove == 0) {
|
||||
PlacesUtils.bookmarks.removeObserver(bmObserver);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
|
||||
onItemRemoved() {
|
||||
itemsToRemove--;
|
||||
if (itemsToAdd == 0 && itemsToRemove == 0) {
|
||||
PlacesUtils.bookmarks.removeObserver(bmObserver);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
|
||||
onBeginUpdateBatch() {},
|
||||
onEndUpdateBatch() {},
|
||||
onItemChanged() {},
|
||||
onItemVisited() {},
|
||||
onItemMoved() {},
|
||||
};
|
||||
PlacesUtils.bookmarks.addObserver(bmObserver);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* ==================
|
||||
* = CHECK FUNCTION =
|
||||
* ==================
|
||||
*
|
||||
* Performs all the checks comparing what was given in
|
||||
* the policy JSON with what was retrieved from Places.
|
||||
*/
|
||||
async function check({expectedNumberOfFolders}) {
|
||||
let bookmarks = [], folders = [];
|
||||
|
||||
await PlacesUtils.bookmarks.fetch({ guidPrefix: BookmarksPolicies.BOOKMARK_GUID_PREFIX }, (r) => {
|
||||
bookmarks.push(r);
|
||||
});
|
||||
await PlacesUtils.bookmarks.fetch({ guidPrefix: BookmarksPolicies.FOLDER_GUID_PREFIX }, (r) => {
|
||||
folders.push(r);
|
||||
});
|
||||
|
||||
let foldersToGuids = new Map();
|
||||
|
||||
for (let folder of folders) {
|
||||
is(folder.type, PlacesUtils.bookmarks.TYPE_FOLDER, "Correct type for folder");
|
||||
foldersToGuids.set(folder.title, folder.guid);
|
||||
}
|
||||
|
||||
// For simplification and accuracy purposes, the number of expected
|
||||
// folders is manually specified in the test.
|
||||
is(folders.length, expectedNumberOfFolders, "Correct number of folders expected");
|
||||
is(foldersToGuids.size, expectedNumberOfFolders, "There aren't two different folders with the same name");
|
||||
|
||||
is(CURRENT_POLICY.policies.Bookmarks.length, bookmarks.length, "The correct number of bookmarks exist");
|
||||
|
||||
for (let bookmark of bookmarks) {
|
||||
is(bookmark.type, PlacesUtils.bookmarks.TYPE_BOOKMARK, "Correct type for bookmark");
|
||||
|
||||
let entry = findBookmarkInPolicy(bookmark);
|
||||
|
||||
is(bookmark.title, entry.Title, "Title matches");
|
||||
is(bookmark.url.href, entry.URL, "URL matches");
|
||||
|
||||
let expectedPlacementGuid;
|
||||
if (entry.Folder) {
|
||||
expectedPlacementGuid = foldersToGuids.get(entry.Folder);
|
||||
} else {
|
||||
expectedPlacementGuid = entry.Placement == "menu" ?
|
||||
PlacesUtils.bookmarks.menuGuid :
|
||||
PlacesUtils.bookmarks.toolbarGuid;
|
||||
}
|
||||
|
||||
is(bookmark.parentGuid, expectedPlacementGuid, "Correctly placed");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ================
|
||||
* = ACTUAL TESTS =
|
||||
* ================
|
||||
*
|
||||
* Note: the order of these tests is important, as we want to test not
|
||||
* only the end result of each configuration, but also the diff algorithm
|
||||
* that will add or remove bookmarks depending on how the policy changed.
|
||||
*/
|
||||
|
||||
add_task(async function test_initial_bookmarks() {
|
||||
// Make a copy of the policy because we will be adding/removing entries from it
|
||||
CURRENT_POLICY = deepClone(BASE_POLICY);
|
||||
|
||||
setupPolicyEngineWithJson(CURRENT_POLICY);
|
||||
|
||||
await promiseAllChangesMade({
|
||||
itemsToAdd: 7, // 5 bookmarks + 2 folders
|
||||
itemsToRemove: 0,
|
||||
});
|
||||
|
||||
await check({ expectedNumberOfFolders: 2 });
|
||||
});
|
||||
|
||||
add_task(async function checkFavicon() {
|
||||
let bookmark1url = CURRENT_POLICY.policies.Bookmarks[0].URL;
|
||||
|
||||
let result = await new Promise(resolve => {
|
||||
PlacesUtils.favicons.getFaviconDataForPage(
|
||||
Services.io.newURI(bookmark1url),
|
||||
(uri, _, data) => resolve({uri, data})
|
||||
);
|
||||
});
|
||||
|
||||
is(result.uri.spec, "fake-favicon-uri:" + bookmark1url, "Favicon URI is correct");
|
||||
// data is an array of octets, which will be a bit hard to compare against
|
||||
// FAVICON_DATA, which is base64 encoded. Checking the expected length should
|
||||
// be good indication that this is working properly.
|
||||
is(result.data.length, 464, "Favicon data has the correct length");
|
||||
|
||||
let faviconsExpiredNotification = TestUtils.topicObserved("places-favicons-expired");
|
||||
PlacesUtils.favicons.expireAllFavicons();
|
||||
await faviconsExpiredNotification;
|
||||
});
|
||||
|
||||
add_task(async function test_remove_Bookmark_2() {
|
||||
// Continuing from the previous policy:
|
||||
//
|
||||
// Remove the 2nd bookmark. It is inside "Folder 1", but that
|
||||
// folder won't be empty, so it must not be removed.
|
||||
CURRENT_POLICY.policies.Bookmarks.splice(2, 1);
|
||||
|
||||
setupPolicyEngineWithJson(CURRENT_POLICY);
|
||||
|
||||
await promiseAllChangesMade({
|
||||
itemsToAdd: 0,
|
||||
itemsToRemove: 1, // 1 bookmark
|
||||
});
|
||||
|
||||
await check({ expectedNumberOfFolders: 2 });
|
||||
});
|
||||
|
||||
add_task(async function test_remove_Bookmark_5() {
|
||||
// Continuing from the previous policy:
|
||||
//
|
||||
// Remove the last bookmark in the policy,
|
||||
// which means the "Folder 2" should also disappear
|
||||
CURRENT_POLICY.policies.Bookmarks.splice(-1, 1);
|
||||
|
||||
setupPolicyEngineWithJson(CURRENT_POLICY);
|
||||
|
||||
await promiseAllChangesMade({
|
||||
itemsToAdd: 0,
|
||||
itemsToRemove: 2, // 1 bookmark and 1 folder
|
||||
});
|
||||
|
||||
await check({ expectedNumberOfFolders: 1 });
|
||||
});
|
||||
|
||||
add_task(async function test_revert_to_original_policy() {
|
||||
CURRENT_POLICY = deepClone(BASE_POLICY);
|
||||
|
||||
// Reverts to the original policy, which means that:
|
||||
// - "Bookmark 2"
|
||||
// - "Bookmark 5"
|
||||
// - "Folder 2"
|
||||
// should be recreated
|
||||
setupPolicyEngineWithJson(CURRENT_POLICY);
|
||||
|
||||
await promiseAllChangesMade({
|
||||
itemsToAdd: 3, // 2 bookmarks and 1 folder
|
||||
itemsToRemove: 0,
|
||||
});
|
||||
|
||||
await check({ expectedNumberOfFolders: 2 });
|
||||
});
|
||||
|
||||
// Leave this one at the end, so that it cleans up any
|
||||
// bookmarks created during this test.
|
||||
add_task(async function test_empty_all_bookmarks() {
|
||||
CURRENT_POLICY = { policies: { Bookmarks: [] } };
|
||||
|
||||
setupPolicyEngineWithJson(CURRENT_POLICY);
|
||||
|
||||
await promiseAllChangesMade({
|
||||
itemsToAdd: 0,
|
||||
itemsToRemove: 7, // 5 bookmarks and 2 folders
|
||||
});
|
||||
|
||||
check({ expectedNumberOfFolders: 0 });
|
||||
});
|
Загрузка…
Ссылка в новой задаче