зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
226e6c59d9
|
@ -1,51 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
var EXPORTED_SYMBOLS = ["LightWeightThemeInstallChild"];
|
||||
|
||||
const {ActorChild} = ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
|
||||
|
||||
class LightWeightThemeInstallChild extends ActorChild {
|
||||
handleEvent(event) {
|
||||
let {mm} = this;
|
||||
switch (event.type) {
|
||||
case "InstallBrowserTheme": {
|
||||
mm.sendAsyncMessage("LightWeightThemeWebInstaller:Install", {
|
||||
baseURI: event.target.baseURI,
|
||||
principal: event.target.nodePrincipal,
|
||||
themeData: event.target.getAttribute("data-browsertheme"),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "PreviewBrowserTheme": {
|
||||
mm.sendAsyncMessage("LightWeightThemeWebInstaller:Preview", {
|
||||
baseURI: event.target.baseURI,
|
||||
principal: event.target.nodePrincipal,
|
||||
themeData: event.target.getAttribute("data-browsertheme"),
|
||||
});
|
||||
this._previewWindow = event.target.ownerGlobal;
|
||||
this._previewWindow.addEventListener("pagehide", this, true);
|
||||
break;
|
||||
}
|
||||
case "pagehide": {
|
||||
mm.sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview");
|
||||
this._resetPreviewWindow();
|
||||
break;
|
||||
}
|
||||
case "ResetBrowserThemePreview": {
|
||||
if (this._previewWindow) {
|
||||
mm.sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview",
|
||||
{principal: event.target.nodePrincipal});
|
||||
this._resetPreviewWindow();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_resetPreviewWindow() {
|
||||
this._previewWindow.removeEventListener("pagehide", this, true);
|
||||
this._previewWindow = null;
|
||||
}
|
||||
}
|
|
@ -10,9 +10,6 @@ with Files("**"):
|
|||
with Files("LightweightThemeChild.jsm"):
|
||||
BUG_COMPONENT = ("WebExtensions", "Themes")
|
||||
|
||||
with Files("LightWeightThemeInstallChild.jsm"):
|
||||
BUG_COMPONENT = ("Firefox", "Theme")
|
||||
|
||||
with Files("PageInfoChild.jsm"):
|
||||
BUG_COMPONENT = ("Firefox", "Page Info Window")
|
||||
|
||||
|
@ -35,7 +32,6 @@ FINAL_TARGET_FILES.actors += [
|
|||
'DOMFullscreenChild.jsm',
|
||||
'FormValidationChild.jsm',
|
||||
'LightweightThemeChild.jsm',
|
||||
'LightWeightThemeInstallChild.jsm',
|
||||
'LinkHandlerChild.jsm',
|
||||
'NetErrorChild.jsm',
|
||||
'OfflineAppsChild.jsm',
|
||||
|
|
|
@ -184,7 +184,6 @@ pref("extensions.webextensions.themes.icons.buttons", "back,forward,reload,stop,
|
|||
|
||||
pref("lightweightThemes.update.enabled", true);
|
||||
pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes");
|
||||
pref("lightweightThemes.recommendedThemes", "[{\"id\":\"recommended-1\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/a-web-browser-renaissance/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.header.jpg\",\"textcolor\":\"#000000\",\"accentcolor\":\"#834d29\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.preview.jpg\",\"author\":\"Sean.Martell\",\"version\":\"0\"},{\"id\":\"recommended-2\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/space-fantasy/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.header.jpg\",\"textcolor\":\"#ffffff\",\"accentcolor\":\"#d9d9d9\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.preview.jpg\",\"author\":\"fx5800p\",\"version\":\"1.0\"},{\"id\":\"recommended-4\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/pastel-gradient/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.header.png\",\"textcolor\":\"#000000\",\"accentcolor\":\"#000000\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.preview.png\",\"author\":\"darrinhenein\",\"version\":\"1.0\"}]");
|
||||
|
||||
#if defined(MOZ_WIDEVINE_EME)
|
||||
pref("browser.eme.ui.enabled", true);
|
||||
|
|
|
@ -671,156 +671,3 @@ var gExtensionsNotifications = {
|
|||
}
|
||||
},
|
||||
};
|
||||
|
||||
var LightWeightThemeWebInstaller = {
|
||||
init() {
|
||||
let mm = window.messageManager;
|
||||
mm.addMessageListener("LightWeightThemeWebInstaller:Install", this);
|
||||
mm.addMessageListener("LightWeightThemeWebInstaller:Preview", this);
|
||||
mm.addMessageListener("LightWeightThemeWebInstaller:ResetPreview", this);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "_apiTesting", "extensions.webapi.testing", false);
|
||||
},
|
||||
|
||||
receiveMessage(message) {
|
||||
// ignore requests from background tabs
|
||||
if (message.target != gBrowser.selectedBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = message.data;
|
||||
|
||||
switch (message.name) {
|
||||
case "LightWeightThemeWebInstaller:Install": {
|
||||
this._installRequest(data.themeData, data.principal, data.baseURI);
|
||||
break;
|
||||
}
|
||||
case "LightWeightThemeWebInstaller:Preview": {
|
||||
this._preview(data.themeData, data.principal, data.baseURI);
|
||||
break;
|
||||
}
|
||||
case "LightWeightThemeWebInstaller:ResetPreview": {
|
||||
this._resetPreview(data && data.principal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "TabSelect": {
|
||||
this._resetPreview();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get _manager() {
|
||||
let temp = {};
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
|
||||
delete this._manager;
|
||||
return this._manager = temp.LightweightThemeManager;
|
||||
},
|
||||
|
||||
_installRequest(dataString, principal, baseURI) {
|
||||
// Don't allow installing off null principals.
|
||||
if (!principal.URI) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = this._manager.parseTheme(dataString, baseURI);
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A notification bar with the option to undo is normally shown after a
|
||||
// theme is installed. But the discovery pane served from the url(s)
|
||||
// below has its own toggle switch for quick undos, so don't show the
|
||||
// notification in that case.
|
||||
let notify = this._shouldShowUndoPrompt(principal);
|
||||
if (this._isAllowed(principal)) {
|
||||
this._install(data, notify);
|
||||
return;
|
||||
}
|
||||
|
||||
let strings = {
|
||||
header: gNavigatorBundle.getFormattedString("webextPerms.header", ["<>"]),
|
||||
addonName: data.name,
|
||||
text: gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message2",
|
||||
[principal.URI.host]),
|
||||
acceptText: gNavigatorBundle.getString("lwthemeInstallRequest.allowButton2"),
|
||||
acceptKey: gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey2"),
|
||||
cancelText: gNavigatorBundle.getString("webextPerms.cancel.label"),
|
||||
cancelKey: gNavigatorBundle.getString("webextPerms.cancel.accessKey"),
|
||||
msgs: [],
|
||||
};
|
||||
ExtensionsUI.showPermissionsPrompt(gBrowser.selectedBrowser, strings, null,
|
||||
"installWeb").then(answer => {
|
||||
if (answer) {
|
||||
LightWeightThemeWebInstaller._install(data, notify);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_install(newLWTheme, notify) {
|
||||
let listener = {
|
||||
onEnabled(aAddon) {
|
||||
if (notify) {
|
||||
ExtensionsUI.showInstallNotification(gBrowser.selectedBrowser, newLWTheme);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
AddonManager.addAddonListener(listener);
|
||||
this._manager.currentTheme = newLWTheme;
|
||||
AddonManager.removeAddonListener(listener);
|
||||
},
|
||||
|
||||
_preview(dataString, principal, baseURI) {
|
||||
if (!this._isAllowed(principal))
|
||||
return;
|
||||
|
||||
let data = this._manager.parseTheme(dataString, baseURI);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
this._resetPreview();
|
||||
gBrowser.tabContainer.addEventListener("TabSelect", this);
|
||||
this._manager.previewTheme(data);
|
||||
},
|
||||
|
||||
_resetPreview(principal) {
|
||||
if (!this._isAllowed(principal))
|
||||
return;
|
||||
gBrowser.tabContainer.removeEventListener("TabSelect", this);
|
||||
this._manager.resetPreview();
|
||||
},
|
||||
|
||||
_isAllowed(principal) {
|
||||
if (!principal || !principal.URI || !principal.URI.schemeIs("https")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let pm = Services.perms;
|
||||
return pm.testPermission(principal.URI, "install") == pm.ALLOW_ACTION;
|
||||
},
|
||||
|
||||
_shouldShowUndoPrompt(principal) {
|
||||
if (!principal || !principal.URI) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let prePath = principal.URI.prePath;
|
||||
if (prePath == "https://discovery.addons.mozilla.org") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._apiTesting && (prePath == "https://discovery.addons.allizom.org" ||
|
||||
prePath == "https://discovery.addons-dev.allizom.org")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
* 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/. */
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
/**
|
||||
* Enables compacttheme.css when needed.
|
||||
*/
|
||||
|
|
|
@ -6,13 +6,21 @@
|
|||
/* eslint-env mozilla/browser-window */
|
||||
|
||||
/**
|
||||
* Global browser interface with the WebRender backend.
|
||||
* Global browser interface with graphics utilities.
|
||||
*/
|
||||
var gWebRender = {
|
||||
var gGfxUtils = {
|
||||
_isRecording: false,
|
||||
/**
|
||||
* Toggle composition recording for the current window.
|
||||
*/
|
||||
toggleWindowRecording() {
|
||||
window.windowUtils.setCompositionRecording(!this._isRecording);
|
||||
this._isRecording = !this._isRecording;
|
||||
},
|
||||
/**
|
||||
* Trigger a WebRender capture of the current state into a local folder.
|
||||
*/
|
||||
capture() {
|
||||
webrenderCapture() {
|
||||
window.windowUtils.wrCapture();
|
||||
},
|
||||
};
|
|
@ -96,7 +96,8 @@
|
|||
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
<command id="wrCaptureCmd" oncommand="gWebRender.capture();"/>
|
||||
<command id="wrCaptureCmd" oncommand="gGfxUtils.webrenderCapture();"/>
|
||||
<command id="windowRecordingCmd" oncommand="gGfxUtils.toggleWindowRecording();"/>
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
<command id="minimizeWindow"
|
||||
|
@ -317,6 +318,13 @@
|
|||
key="#" modifiers="control"
|
||||
#endif
|
||||
command="wrCaptureCmd"/>
|
||||
<key id="key_windowRecordingCmd"
|
||||
#ifdef XP_MACOSX
|
||||
key="4" modifiers="control,shift"
|
||||
#else
|
||||
key="$" modifiers="control"
|
||||
#endif
|
||||
command="windowRecordingCmd"/>
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
<key id="key_minimizeWindow"
|
||||
|
|
|
@ -31,7 +31,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
|
||||
HomePage: "resource:///modules/HomePage.jsm",
|
||||
LightweightThemeConsumer: "resource://gre/modules/LightweightThemeConsumer.jsm",
|
||||
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
|
||||
Log: "resource://gre/modules/Log.jsm",
|
||||
LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
|
||||
MigrationUtils: "resource:///modules/MigrationUtils.jsm",
|
||||
|
@ -102,8 +101,7 @@ XPCOMUtils.defineLazyScriptGetter(this, "gViewSourceUtils",
|
|||
"chrome://global/content/viewSourceUtils.js");
|
||||
XPCOMUtils.defineLazyScriptGetter(this, "gTabsPanel",
|
||||
"chrome://browser/content/browser-allTabsMenu.js");
|
||||
XPCOMUtils.defineLazyScriptGetter(this, ["LightWeightThemeWebInstaller",
|
||||
"gExtensionsNotifications",
|
||||
XPCOMUtils.defineLazyScriptGetter(this, ["gExtensionsNotifications",
|
||||
"gXPInstallObserver"],
|
||||
"chrome://browser/content/browser-addons.js");
|
||||
XPCOMUtils.defineLazyScriptGetter(this, "ctrlTab",
|
||||
|
@ -141,8 +139,8 @@ XPCOMUtils.defineLazyScriptGetter(this, "gEditItemOverlay",
|
|||
XPCOMUtils.defineLazyScriptGetter(this, "SearchOneOffs",
|
||||
"chrome://browser/content/search/search-one-offs.js");
|
||||
if (AppConstants.NIGHTLY_BUILD) {
|
||||
XPCOMUtils.defineLazyScriptGetter(this, "gWebRender",
|
||||
"chrome://browser/content/browser-webrender.js");
|
||||
XPCOMUtils.defineLazyScriptGetter(this, "gGfxUtils",
|
||||
"chrome://browser/content/browser-graphics-utils.js");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyScriptGetter(this, "pktUI", "chrome://pocket/content/main.js");
|
||||
|
@ -1654,8 +1652,6 @@ var gBrowserInit = {
|
|||
placesContext.addEventListener("popuphiding", updateEditUIVisibility);
|
||||
}
|
||||
|
||||
LightWeightThemeWebInstaller.init();
|
||||
|
||||
FullScreen.init();
|
||||
PointerLock.init();
|
||||
|
||||
|
|
|
@ -5,15 +5,20 @@
|
|||
* themes are applied.
|
||||
*/
|
||||
|
||||
const DEFAULT_THEME = "default-theme@mozilla.org";
|
||||
const PREF_LWTHEME_USED_THEMES = "lightweightThemes.usedThemes";
|
||||
const COMPACT_LIGHT_ID = "firefox-compact-light@mozilla.org";
|
||||
const COMPACT_DARK_ID = "firefox-compact-dark@mozilla.org";
|
||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
async function selectTheme(id) {
|
||||
let theme = await AddonManager.getAddonByID(id || DEFAULT_THEME);
|
||||
await theme.enable();
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
// Set preferences back to their original values
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
Services.prefs.clearUserPref(PREF_LWTHEME_USED_THEMES);
|
||||
return selectTheme(null);
|
||||
});
|
||||
|
||||
function tick() {
|
||||
|
@ -22,70 +27,22 @@ function tick() {
|
|||
|
||||
add_task(async function startTests() {
|
||||
info("Setting the current theme to null");
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
await selectTheme(null);
|
||||
await tick();
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "There is no compact style sheet when no lw theme is applied.");
|
||||
|
||||
info("Adding a lightweight theme.");
|
||||
LightweightThemeManager.currentTheme = dummyLightweightTheme("preview0");
|
||||
await tick();
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "The compact stylesheet has been removed when a lightweight theme is applied.");
|
||||
|
||||
info("Applying the dark compact theme.");
|
||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme(COMPACT_DARK_ID);
|
||||
await selectTheme(COMPACT_DARK_ID);
|
||||
await tick();
|
||||
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet has been added when the compact lightweight theme is applied");
|
||||
|
||||
info("Applying the light compact theme.");
|
||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme(COMPACT_LIGHT_ID);
|
||||
await selectTheme(COMPACT_LIGHT_ID);
|
||||
await tick();
|
||||
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet has been added when the compact lightweight theme is applied");
|
||||
|
||||
info("Adding a different lightweight theme.");
|
||||
LightweightThemeManager.currentTheme = dummyLightweightTheme("preview1");
|
||||
await tick();
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "The compact stylesheet has been removed when a lightweight theme is applied.");
|
||||
|
||||
info("Unapplying all themes.");
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
await selectTheme(null);
|
||||
await tick();
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "There is no compact style sheet when no lw theme is applied.");
|
||||
});
|
||||
|
||||
function dummyLightweightTheme(id) {
|
||||
return {
|
||||
id,
|
||||
name: id,
|
||||
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/light.icon.svg",
|
||||
textcolor: "red",
|
||||
accentcolor: "blue",
|
||||
};
|
||||
}
|
||||
|
||||
add_task(async function testLightweightThemePreview() {
|
||||
info("Setting compact to current and previewing others");
|
||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme(COMPACT_LIGHT_ID);
|
||||
await tick();
|
||||
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet is enabled.");
|
||||
LightweightThemeManager.previewTheme(dummyLightweightTheme("preview0"));
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "The compact stylesheet is not enabled after a lightweight theme preview.");
|
||||
LightweightThemeManager.resetPreview();
|
||||
LightweightThemeManager.previewTheme(dummyLightweightTheme("preview1"));
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "The compact stylesheet is not enabled after a second lightweight theme preview.");
|
||||
LightweightThemeManager.resetPreview();
|
||||
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet is enabled again after resetting the preview.");
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
await tick();
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "The compact stylesheet is gone after removing the current theme.");
|
||||
|
||||
info("Previewing the compact theme");
|
||||
LightweightThemeManager.previewTheme(LightweightThemeManager.getUsedTheme(COMPACT_LIGHT_ID));
|
||||
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet is enabled.");
|
||||
LightweightThemeManager.previewTheme(LightweightThemeManager.getUsedTheme(COMPACT_DARK_ID));
|
||||
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet is enabled.");
|
||||
|
||||
LightweightThemeManager.previewTheme(dummyLightweightTheme("preview2"));
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "The compact stylesheet is now disabled after previewing a new sheet.");
|
||||
LightweightThemeManager.resetPreview();
|
||||
ok(!CompactTheme.isStyleSheetEnabled, "The compact stylesheet is now disabled after resetting the preview.");
|
||||
});
|
||||
|
|
|
@ -2,10 +2,17 @@
|
|||
* 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/. */
|
||||
|
||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
const DEFAULT_THEME = "default-theme@mozilla.org";
|
||||
|
||||
const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
async function selectTheme(id) {
|
||||
let theme = await AddonManager.getAddonByID(id || DEFAULT_THEME);
|
||||
await theme.enable();
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
|
||||
return selectTheme(null);
|
||||
});
|
||||
|
||||
add_task(async function withoutLWT() {
|
||||
|
@ -16,7 +23,7 @@ add_task(async function withoutLWT() {
|
|||
});
|
||||
|
||||
add_task(async function withLWT() {
|
||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-compact-light@mozilla.org");
|
||||
await selectTheme("firefox-compact-light@mozilla.org");
|
||||
let win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
ok(!win.gBrowser.tabContainer.hasAttribute("overflow"), "tab container not overflowing");
|
||||
ok(win.gBrowser.tabContainer.arrowScrollbox.hasAttribute("notoverflowing"), "arrow scrollbox not overflowing");
|
||||
|
|
|
@ -58,7 +58,7 @@ browser.jar:
|
|||
content/browser/browser-tabsintitlebar.js (content/browser-tabsintitlebar.js)
|
||||
content/browser/browser-toolbarKeyNav.js (content/browser-toolbarKeyNav.js)
|
||||
content/browser/browser-thumbnails.js (content/browser-thumbnails.js)
|
||||
content/browser/browser-webrender.js (content/browser-webrender.js)
|
||||
content/browser/browser-graphics-utils.js (content/browser-graphics-utils.js)
|
||||
content/browser/tab-content.js (content/tab-content.js)
|
||||
content/browser/content.js (content/content.js)
|
||||
content/browser/defaultthemes/1.header.jpg (content/defaultthemes/1.header.jpg)
|
||||
|
|
|
@ -132,17 +132,6 @@ let ACTORS = {
|
|||
},
|
||||
},
|
||||
|
||||
LightWeightThemeInstall: {
|
||||
child: {
|
||||
module: "resource:///actors/LightWeightThemeInstallChild.jsm",
|
||||
events: {
|
||||
"InstallBrowserTheme": {wantUntrusted: true},
|
||||
"PreviewBrowserTheme": {wantUntrusted: true},
|
||||
"ResetBrowserThemePreview": {wantUntrusted: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
LightweightTheme: {
|
||||
child: {
|
||||
module: "resource:///actors/LightweightThemeChild.jsm",
|
||||
|
|
|
@ -10,13 +10,14 @@ const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm")
|
|||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
||||
AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
|
||||
SearchWidgetTracker: "resource:///modules/SearchWidgetTracker.jsm",
|
||||
CustomizableWidgets: "resource:///modules/CustomizableWidgets.jsm",
|
||||
DeferredTask: "resource://gre/modules/DeferredTask.jsm",
|
||||
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
|
||||
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
|
||||
|
@ -27,6 +28,8 @@ XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
|
|||
XPCOMUtils.defineLazyServiceGetter(this, "gELS",
|
||||
"@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
|
||||
|
||||
const kDefaultThemeID = "default-theme@mozilla.org";
|
||||
|
||||
const kSpecialWidgetPfx = "customizableui-special-";
|
||||
|
||||
const kPrefCustomizationState = "browser.uiCustomization.state";
|
||||
|
@ -40,6 +43,9 @@ const kPrefAutoHideDownloadsButton = "browser.download.autohideButton";
|
|||
|
||||
const kExpectedWindowURL = AppConstants.BROWSER_CHROME_URL;
|
||||
|
||||
var gDefaultTheme;
|
||||
var gSelectedTheme;
|
||||
|
||||
/**
|
||||
* The keys are the handlers that are fired when the event type (the value)
|
||||
* is fired on the subview. A widget that provides a subview has the option
|
||||
|
@ -178,6 +184,11 @@ var CustomizableUIInternal = {
|
|||
initialize() {
|
||||
log.debug("Initializing");
|
||||
|
||||
Services.obs.addObserver(this, "xpi-database-loaded");
|
||||
if (AddonManagerPrivate.isDBLoaded()) {
|
||||
this.observe(null, "xpi-database-loaded");
|
||||
}
|
||||
|
||||
this.addListener(this);
|
||||
this._defineBuiltInWidgets();
|
||||
this.loadSavedState();
|
||||
|
@ -245,6 +256,22 @@ var CustomizableUIInternal = {
|
|||
SearchWidgetTracker.init();
|
||||
},
|
||||
|
||||
async observe(subject, topic, data) {
|
||||
if (topic == "xpi-database-loaded") {
|
||||
AddonManager.addAddonListener(this);
|
||||
|
||||
let addons = await AddonManager.getAddonsByTypes(["theme"]);
|
||||
gDefaultTheme = addons.find(addon => addon.id == kDefaultThemeID);
|
||||
gSelectedTheme = addons.find(addon => addon.isActive) || gDefaultTheme;
|
||||
}
|
||||
},
|
||||
|
||||
onEnabled(addon) {
|
||||
if (addon.type == "theme") {
|
||||
gSelectedTheme = addon;
|
||||
}
|
||||
},
|
||||
|
||||
get _builtinAreas() {
|
||||
return new Set([
|
||||
...this._builtinToolbars,
|
||||
|
@ -2652,7 +2679,7 @@ var CustomizableUIInternal = {
|
|||
gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
|
||||
gUIStateBeforeReset.uiDensity = Services.prefs.getIntPref(kPrefUIDensity);
|
||||
gUIStateBeforeReset.autoTouchMode = Services.prefs.getBoolPref(kPrefAutoTouchMode);
|
||||
gUIStateBeforeReset.currentTheme = LightweightThemeManager.currentTheme;
|
||||
gUIStateBeforeReset.currentTheme = gSelectedTheme;
|
||||
gUIStateBeforeReset.autoHideDownloadsButton = Services.prefs.getBoolPref(kPrefAutoHideDownloadsButton);
|
||||
gUIStateBeforeReset.newElementCount = gNewElementCount;
|
||||
} catch (e) { }
|
||||
|
@ -2663,7 +2690,7 @@ var CustomizableUIInternal = {
|
|||
Services.prefs.clearUserPref(kPrefUIDensity);
|
||||
Services.prefs.clearUserPref(kPrefAutoTouchMode);
|
||||
Services.prefs.clearUserPref(kPrefAutoHideDownloadsButton);
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
gDefaultTheme.enable();
|
||||
gNewElementCount = 0;
|
||||
log.debug("State reset");
|
||||
|
||||
|
@ -2725,7 +2752,7 @@ var CustomizableUIInternal = {
|
|||
Services.prefs.setIntPref(kPrefUIDensity, uiDensity);
|
||||
Services.prefs.setBoolPref(kPrefAutoTouchMode, autoTouchMode);
|
||||
Services.prefs.setBoolPref(kPrefAutoHideDownloadsButton, autoHideDownloadsButton);
|
||||
LightweightThemeManager.currentTheme = currentTheme;
|
||||
currentTheme.enable();
|
||||
this.loadSavedState();
|
||||
// If the user just customizes toolbar/titlebar visibility, gSavedState will be null
|
||||
// and we don't need to do anything else here:
|
||||
|
@ -2942,9 +2969,9 @@ var CustomizableUIInternal = {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (LightweightThemeManager.currentTheme &&
|
||||
LightweightThemeManager.currentTheme.id != "default-theme@mozilla.org") {
|
||||
log.debug(LightweightThemeManager.currentTheme + " theme is non-default");
|
||||
// This should just be `gDefaultTheme.isActive`, but bugs...
|
||||
if (gDefaultTheme && gDefaultTheme.id != gSelectedTheme.id) {
|
||||
log.debug(gSelectedTheme.id + " theme is non-default");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,14 +28,14 @@ const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.j
|
|||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["CSS"]);
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "AMTelemetry",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "DragPositionManager",
|
||||
"resource:///modules/DragPositionManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
|
||||
|
@ -164,11 +164,11 @@ CustomizeMode.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_updateLWThemeButtonIcon() {
|
||||
async _updateThemeButtonIcon() {
|
||||
let lwthemeButton = this.$("customization-lwtheme-button");
|
||||
let lwthemeIcon = this.document.getAnonymousElementByAttribute(lwthemeButton,
|
||||
"class", "button-icon");
|
||||
let theme = LightweightThemeManager.currentTheme;
|
||||
let theme = (await AddonManager.getAddonsByTypes(["theme"])).find(addon => addon.isActive);
|
||||
lwthemeIcon.style.backgroundImage = theme ? "url(" + theme.iconURL + ")" : "";
|
||||
},
|
||||
|
||||
|
@ -344,8 +344,8 @@ CustomizeMode.prototype = {
|
|||
}, 0);
|
||||
this._updateEmptyPaletteNotice();
|
||||
|
||||
this._updateLWThemeButtonIcon();
|
||||
Services.obs.addObserver(this, "lightweight-theme-changed");
|
||||
this._updateThemeButtonIcon();
|
||||
AddonManager.addAddonListener(this);
|
||||
|
||||
this._setupDownloadAutoHideToggle();
|
||||
|
||||
|
@ -388,7 +388,7 @@ CustomizeMode.prototype = {
|
|||
|
||||
this._teardownDownloadAutoHideToggle();
|
||||
|
||||
Services.obs.removeObserver(this, "lightweight-theme-changed");
|
||||
AddonManager.removeAddonListener(this);
|
||||
CustomizableUI.removeListener(this);
|
||||
|
||||
this.document.removeEventListener("keypress", this);
|
||||
|
@ -1310,23 +1310,13 @@ CustomizeMode.prototype = {
|
|||
this._onUIChange();
|
||||
},
|
||||
|
||||
onLWThemesMenuShowing(aEvent) {
|
||||
async onThemesMenuShowing(aEvent) {
|
||||
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
|
||||
const LIGHT_THEME_ID = "firefox-compact-light@mozilla.org";
|
||||
const DARK_THEME_ID = "firefox-compact-dark@mozilla.org";
|
||||
const MAX_THEME_COUNT = 6;
|
||||
|
||||
this._clearLWThemesMenu(aEvent.target);
|
||||
|
||||
function previewTheme(aPreviewThemeEvent) {
|
||||
LightweightThemeManager.previewTheme(
|
||||
aPreviewThemeEvent.target.theme.id != DEFAULT_THEME_ID ?
|
||||
aPreviewThemeEvent.target.theme : null);
|
||||
}
|
||||
|
||||
function resetPreview() {
|
||||
LightweightThemeManager.resetPreview();
|
||||
}
|
||||
this._clearThemesMenu(aEvent.target);
|
||||
|
||||
let onThemeSelected = panel => {
|
||||
// This causes us to call _onUIChange when the LWT actually changes,
|
||||
|
@ -1346,50 +1336,36 @@ CustomizeMode.prototype = {
|
|||
tbb.setAttribute("tooltiptext", aTheme.description);
|
||||
tbb.setAttribute("tabindex", "0");
|
||||
tbb.classList.add("customization-lwtheme-menu-theme");
|
||||
let isActive = activeThemeID == aTheme.id;
|
||||
let isActive = aTheme.isActive;
|
||||
tbb.setAttribute("aria-checked", isActive);
|
||||
tbb.setAttribute("role", "menuitemradio");
|
||||
if (isActive) {
|
||||
tbb.setAttribute("active", "true");
|
||||
}
|
||||
tbb.addEventListener("focus", previewTheme);
|
||||
tbb.addEventListener("mouseover", previewTheme);
|
||||
tbb.addEventListener("blur", resetPreview);
|
||||
|
||||
return tbb;
|
||||
}
|
||||
|
||||
let themes = [];
|
||||
let lwts = LightweightThemeManager.usedThemes;
|
||||
let currentLwt = LightweightThemeManager.currentTheme;
|
||||
|
||||
let activeThemeID = currentLwt ? currentLwt.id : DEFAULT_THEME_ID;
|
||||
let themes = await AddonManager.getAddonsByTypes(["theme"]);
|
||||
let currentTheme = themes.find(theme => theme.isActive);
|
||||
|
||||
// Move the current theme (if any) and the light/dark themes to the start:
|
||||
let importantThemes = [DEFAULT_THEME_ID, LIGHT_THEME_ID, DARK_THEME_ID];
|
||||
if (currentLwt && !importantThemes.includes(currentLwt.id)) {
|
||||
importantThemes.push(currentLwt.id);
|
||||
let importantThemes = new Set([DEFAULT_THEME_ID, LIGHT_THEME_ID, DARK_THEME_ID]);
|
||||
if (currentTheme) {
|
||||
importantThemes.add(currentTheme.id);
|
||||
}
|
||||
for (let importantTheme of importantThemes) {
|
||||
let themeIndex = lwts.findIndex(theme => theme.id == importantTheme);
|
||||
if (themeIndex > -1) {
|
||||
themes.push(...lwts.splice(themeIndex, 1));
|
||||
}
|
||||
}
|
||||
themes = themes.concat(lwts);
|
||||
|
||||
themes.sort((a, b) => importantThemes.has(b) - importantThemes.has(a));
|
||||
|
||||
if (themes.length > MAX_THEME_COUNT)
|
||||
themes.length = MAX_THEME_COUNT;
|
||||
|
||||
let footer = doc.getElementById("customization-lwtheme-menu-footer");
|
||||
let panel = footer.parentNode;
|
||||
let recommendedLabel = doc.getElementById("customization-lwtheme-menu-recommended");
|
||||
for (let theme of themes) {
|
||||
let button = buildToolbarButton(theme);
|
||||
button.addEventListener("command", () => {
|
||||
if ("userDisabled" in button.theme)
|
||||
button.theme.userDisabled = false;
|
||||
else
|
||||
LightweightThemeManager.currentTheme = button.theme;
|
||||
button.addEventListener("command", async () => {
|
||||
await button.theme.enable();
|
||||
onThemeSelected(panel);
|
||||
AMTelemetry.recordActionEvent({
|
||||
object: "customize",
|
||||
|
@ -1397,69 +1373,16 @@ CustomizeMode.prototype = {
|
|||
extra: {type: "theme", addonId: theme.id},
|
||||
});
|
||||
});
|
||||
panel.insertBefore(button, recommendedLabel);
|
||||
}
|
||||
|
||||
function panelMouseOut(e) {
|
||||
// mouseout events bubble, so we get mouseout events for the buttons
|
||||
// in the panel. Here, we only care when the mouse actually leaves
|
||||
// the panel. For some reason event.target might not be the panel
|
||||
// even when the mouse *is* leaving the panel, so we check
|
||||
// explicitOriginalTarget instead.
|
||||
if (e.explicitOriginalTarget == panel) {
|
||||
resetPreview();
|
||||
}
|
||||
}
|
||||
|
||||
panel.addEventListener("mouseout", panelMouseOut);
|
||||
panel.addEventListener("popuphidden", () => {
|
||||
panel.removeEventListener("mouseout", panelMouseOut);
|
||||
resetPreview();
|
||||
}, {once: true});
|
||||
|
||||
let lwthemePrefs = Services.prefs.getBranch("lightweightThemes.");
|
||||
let recommendedThemes = lwthemePrefs.getStringPref("recommendedThemes");
|
||||
recommendedThemes = JSON.parse(recommendedThemes);
|
||||
let sb = Services.strings.createBundle("chrome://browser/locale/lightweightThemes.properties");
|
||||
for (let theme of recommendedThemes) {
|
||||
try {
|
||||
theme.name = sb.GetStringFromName("lightweightThemes." + theme.id + ".name");
|
||||
theme.description = sb.GetStringFromName("lightweightThemes." + theme.id + ".description");
|
||||
} catch (ex) {
|
||||
// If finding strings for this failed, just don't build it. This can
|
||||
// happen for users with 'older' recommended themes lists, some of which
|
||||
// have since been removed from Firefox.
|
||||
continue;
|
||||
}
|
||||
let button = buildToolbarButton(theme);
|
||||
button.addEventListener("command", () => {
|
||||
LightweightThemeManager.setLocalTheme(button.theme);
|
||||
recommendedThemes = recommendedThemes.filter((aTheme) => { return aTheme.id != button.theme.id; });
|
||||
lwthemePrefs.setStringPref("recommendedThemes",
|
||||
JSON.stringify(recommendedThemes));
|
||||
onThemeSelected(panel);
|
||||
let addonId = `${button.theme.id}@personas.mozilla.org`;
|
||||
AMTelemetry.recordActionEvent({
|
||||
object: "customize",
|
||||
action: "enable",
|
||||
value: "recommended",
|
||||
extra: {type: "theme", addonId},
|
||||
});
|
||||
});
|
||||
panel.insertBefore(button, footer);
|
||||
}
|
||||
let hideRecommendedLabel = (footer.previousElementSibling == recommendedLabel);
|
||||
recommendedLabel.hidden = hideRecommendedLabel;
|
||||
},
|
||||
|
||||
_clearLWThemesMenu(panel) {
|
||||
_clearThemesMenu(panel) {
|
||||
let footer = this.$("customization-lwtheme-menu-footer");
|
||||
let recommendedLabel = this.$("customization-lwtheme-menu-recommended");
|
||||
for (let element of [footer, recommendedLabel]) {
|
||||
while (element.previousElementSibling &&
|
||||
element.previousElementSibling.localName == "toolbarbutton") {
|
||||
element.previousElementSibling.remove();
|
||||
}
|
||||
let element = footer;
|
||||
while (element.previousElementSibling &&
|
||||
element.previousElementSibling.localName == "toolbarbutton") {
|
||||
element.previousElementSibling.remove();
|
||||
}
|
||||
|
||||
// Workaround for bug 1059934
|
||||
|
@ -1582,16 +1505,21 @@ CustomizeMode.prototype = {
|
|||
this._updateDragSpaceCheckbox();
|
||||
}
|
||||
break;
|
||||
case "lightweight-theme-changed":
|
||||
this._updateLWThemeButtonIcon();
|
||||
if (this._nextThemeChangeUserTriggered) {
|
||||
this._onUIChange();
|
||||
}
|
||||
this._nextThemeChangeUserTriggered = false;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
async onEnabled(addon) {
|
||||
if (addon.type != "theme") {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._updateThemeButtonIcon();
|
||||
if (this._nextThemeChangeUserTriggered) {
|
||||
this._onUIChange();
|
||||
}
|
||||
this._nextThemeChangeUserTriggered = false;
|
||||
},
|
||||
|
||||
_canDrawInTitlebar() {
|
||||
return this.window.TabsInTitlebar.systemSupported;
|
||||
},
|
||||
|
|
|
@ -41,12 +41,11 @@
|
|||
</button>
|
||||
<button id="customization-lwtheme-button" label="&customizeMode.lwthemes;" class="customizationmode-button" type="menu">
|
||||
<panel type="arrow" id="customization-lwtheme-menu"
|
||||
onpopupshowing="gCustomizeMode.onLWThemesMenuShowing(event);"
|
||||
onpopupshowing="gCustomizeMode.onThemesMenuShowing(event);"
|
||||
position="topcenter bottomleft"
|
||||
flip="none"
|
||||
role="menu">
|
||||
<label id="customization-lwtheme-menu-header" value="&customizeMode.lwthemes.myThemes;"/>
|
||||
<label id="customization-lwtheme-menu-recommended" value="&customizeMode.lwthemes.recommended;"/>
|
||||
<hbox id="customization-lwtheme-menu-footer">
|
||||
<toolbarbutton class="customization-lwtheme-menu-footeritem"
|
||||
label="&customizeMode.lwthemes.menuManage;"
|
||||
|
|
|
@ -7,11 +7,9 @@
|
|||
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
|
||||
const LIGHT_THEME_ID = "firefox-compact-light@mozilla.org";
|
||||
const DARK_THEME_ID = "firefox-compact-dark@mozilla.org";
|
||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
add_task(async function() {
|
||||
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
|
||||
Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
|
||||
|
||||
await startCustomizing();
|
||||
// Check restore defaults button is disabled.
|
||||
|
@ -51,9 +49,9 @@ add_task(async function() {
|
|||
await popupShownPromise;
|
||||
|
||||
let header = document.getElementById("customization-lwtheme-menu-header");
|
||||
let recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
|
||||
let footer = document.getElementById("customization-lwtheme-menu-footer");
|
||||
|
||||
is(header.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling, recommendedHeader,
|
||||
is(header.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling, footer,
|
||||
"There should only be three themes (default, light, dark) in the 'My Themes' section by default");
|
||||
is(header.nextElementSibling.theme.id, DEFAULT_THEME_ID,
|
||||
"The first theme should be the default theme");
|
||||
|
@ -67,8 +65,11 @@ add_task(async function() {
|
|||
info("Clicked on light theme");
|
||||
await themeChangedPromise;
|
||||
|
||||
let button = document.getElementById("customization-reset-button");
|
||||
await TestUtils.waitForCondition(() => !button.disabled);
|
||||
|
||||
// Check restore defaults button is enabled.
|
||||
ok(!document.getElementById("customization-reset-button").disabled,
|
||||
ok(!button.disabled,
|
||||
"Reset button should not be disabled anymore");
|
||||
ok((/light/i).test(themesButtonIcon.style.backgroundImage),
|
||||
`Button should show light theme thumbnail - was: "${themesButtonIcon.style.backgroundImage}"`);
|
||||
|
@ -84,13 +85,15 @@ add_task(async function() {
|
|||
is(activeThemes[0].theme.id, LIGHT_THEME_ID, "Light theme should be selected");
|
||||
}
|
||||
|
||||
let firstLWTheme = recommendedHeader.nextElementSibling;
|
||||
let firstLWTheme = footer.previousElementSibling;
|
||||
let firstLWThemeId = firstLWTheme.theme.id;
|
||||
themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
||||
firstLWTheme.doCommand();
|
||||
info("Clicked on first theme");
|
||||
await themeChangedPromise;
|
||||
|
||||
await new Promise(executeSoon);
|
||||
|
||||
popupShownPromise = popupShown(popup);
|
||||
EventUtils.synthesizeMouseAtCenter(themesButton, {});
|
||||
info("Clicked on themes button");
|
||||
|
@ -103,17 +106,13 @@ add_task(async function() {
|
|||
}
|
||||
|
||||
is(header.nextElementSibling.theme.id, DEFAULT_THEME_ID, "The first theme should be the Default theme");
|
||||
let installedThemeId = header.nextElementSibling.nextElementSibling.nextElementSibling.nextElementSibling.theme.id;
|
||||
ok(installedThemeId.startsWith(firstLWThemeId),
|
||||
"The second theme in the 'My Themes' section should be the newly installed theme: " +
|
||||
"Installed theme id: " + installedThemeId + "; First theme ID: " + firstLWThemeId);
|
||||
let themeCount = 0;
|
||||
let iterNode = header;
|
||||
while (iterNode.nextElementSibling && iterNode.nextElementSibling.theme) {
|
||||
themeCount++;
|
||||
iterNode = iterNode.nextElementSibling;
|
||||
}
|
||||
is(themeCount, 4,
|
||||
is(themeCount, 3,
|
||||
"There should be four themes in the 'My Themes' section");
|
||||
|
||||
let defaultTheme = header.nextElementSibling;
|
||||
|
@ -128,20 +127,14 @@ add_task(async function() {
|
|||
info("Clicked on themes button a fourth time");
|
||||
await popupShownPromise;
|
||||
|
||||
firstLWTheme = recommendedHeader.nextElementSibling;
|
||||
themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
||||
firstLWTheme.doCommand();
|
||||
info("Clicked on first theme again");
|
||||
await themeChangedPromise;
|
||||
|
||||
// check that "Restore Defaults" button resets theme
|
||||
await gCustomizeMode.reset();
|
||||
is(LightweightThemeManager.currentTheme.id, DEFAULT_THEME_ID, "Current theme reset to default");
|
||||
|
||||
defaultTheme = await AddonManager.getAddonByID(DEFAULT_THEME_ID);
|
||||
is(defaultTheme.isActive, true, "Current theme reset to default");
|
||||
|
||||
await endCustomizing();
|
||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", "[]");
|
||||
Services.prefs.setCharPref("lightweightThemes.recommendedThemes", "[]");
|
||||
info("Removed all recommended themes");
|
||||
await startCustomizing();
|
||||
popupShownPromise = popupShown(popup);
|
||||
EventUtils.synthesizeMouseAtCenter(themesButton, {});
|
||||
|
@ -160,18 +153,10 @@ add_task(async function() {
|
|||
themeNode = themeNode.nextElementSibling;
|
||||
is(themeNode.theme.id, DARK_THEME_ID, "The third theme should be the Dark theme");
|
||||
is(themeNode.hidden, false, "The dark theme should never be hidden");
|
||||
|
||||
recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
|
||||
is(themeNode.nextElementSibling, recommendedHeader,
|
||||
"There should only be three themes (default, light, dark) in the 'My Themes' section now");
|
||||
let footer = document.getElementById("customization-lwtheme-menu-footer");
|
||||
is(recommendedHeader.nextElementSibling.id, footer.id, "There should be no recommended themes in the menu");
|
||||
is(recommendedHeader.hidden, true, "The recommendedHeader should be hidden since there are no recommended themes");
|
||||
});
|
||||
|
||||
add_task(async function asyncCleanup() {
|
||||
await endCustomizing();
|
||||
|
||||
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
|
||||
Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
|
||||
});
|
||||
|
|
|
@ -23,25 +23,28 @@ add_task(async function() {
|
|||
info("Clicked on themes button");
|
||||
await popupShownPromise;
|
||||
|
||||
let recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
|
||||
let firstLWTheme = recommendedHeader.nextElementSibling;
|
||||
let header = document.getElementById("customization-lwtheme-menu-header");
|
||||
let firstLWTheme = header.nextElementSibling.nextElementSibling;
|
||||
let firstLWThemeId = firstLWTheme.theme.id;
|
||||
let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
||||
firstLWTheme.doCommand();
|
||||
info("Clicked on first theme");
|
||||
await themeChangedPromise;
|
||||
|
||||
is(LightweightThemeManager.currentTheme.id, firstLWThemeId, "Theme changed to first option");
|
||||
let theme = await AddonManager.getAddonByID(firstLWThemeId);
|
||||
is(theme.isActive, true, "Theme changed to first option");
|
||||
|
||||
await gCustomizeMode.reset();
|
||||
|
||||
ok(CustomizableUI.inDefaultState, "In default state after reset");
|
||||
is(undoResetButton.hidden, false, "The undo button is visible after reset");
|
||||
is(LightweightThemeManager.currentTheme.id, "default-theme@mozilla.org", "Theme reset to default");
|
||||
theme = await AddonManager.getAddonByID("default-theme@mozilla.org");
|
||||
is(theme.isActive, true, "Theme reset to default");
|
||||
|
||||
await gCustomizeMode.undoReset();
|
||||
|
||||
is(LightweightThemeManager.currentTheme.id, firstLWThemeId, "Theme has been reset from default to original choice");
|
||||
theme = await AddonManager.getAddonByID(firstLWThemeId);
|
||||
is(theme.isActive, true, "Theme has been reset from default to original choice");
|
||||
ok(!CustomizableUI.inDefaultState, "Not in default state after undo-reset");
|
||||
is(undoResetButton.hidden, true, "The undo button is hidden after clicking on the undo button");
|
||||
is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
|
||||
|
|
|
@ -16,33 +16,6 @@ add_task(async function testCustomize() {
|
|||
|
||||
await startCustomizing();
|
||||
|
||||
// Open the panel to populate the recommended themes.
|
||||
let themePanel = document.getElementById("customization-lwtheme-menu");
|
||||
themePanel.openPopup();
|
||||
await BrowserTestUtils.waitForPopupEvent(themePanel, "shown");
|
||||
|
||||
// Install a recommended theme.
|
||||
let recommendedLabel = document.getElementById("customization-lwtheme-menu-recommended");
|
||||
let themeButton = recommendedLabel.nextElementSibling;
|
||||
let themeId = `${themeButton.theme.id}@personas.mozilla.org`;
|
||||
let themeChanged = TestUtils.topicObserved("lightweight-theme-changed");
|
||||
themeButton.click();
|
||||
|
||||
// Wait for the theme to change and the popup to close.
|
||||
await themeChanged;
|
||||
await BrowserTestUtils.waitForPopupEvent(themePanel, "hidden");
|
||||
|
||||
// Switch back to the default theme.
|
||||
let installedThemes = document.querySelectorAll(".customization-lwtheme-menu-theme");
|
||||
let defaultId = "default-theme@mozilla.org";
|
||||
let defaultThemeIndex = Array.from(installedThemes).findIndex(btn => btn.theme.id == defaultId);
|
||||
let defaultThemeButton = installedThemes[defaultThemeIndex];
|
||||
themeChanged = TestUtils.topicObserved("lightweight-theme-changed");
|
||||
defaultThemeButton.click();
|
||||
|
||||
// Wait for the theme to change back to default.
|
||||
await themeChanged;
|
||||
|
||||
// Find the footer buttons to test.
|
||||
let footerRow = document.getElementById("customization-lwtheme-menu-footer");
|
||||
let [manageButton, getMoreButton] = footerRow.childNodes;
|
||||
|
@ -77,8 +50,6 @@ add_task(async function testCustomize() {
|
|||
|
||||
// Events are now [method, object, value, extra] as expected.
|
||||
Assert.deepEqual(relatedEvents, [
|
||||
["action", "customize", "recommended", {action: "enable", addonId: themeId, type: "theme"}],
|
||||
["action", "customize", null, {action: "enable", addonId: defaultId, type: "theme"}],
|
||||
["link", "customize", "manageThemes"],
|
||||
["link", "customize", "getThemes"],
|
||||
], "The events are recorded correctly");
|
||||
|
|
|
@ -30,14 +30,6 @@ if (typeof Mozilla == "undefined") {
|
|||
Mozilla.UITour = {};
|
||||
}
|
||||
|
||||
var themeIntervalId = null;
|
||||
function _stopCyclingThemes() {
|
||||
if (themeIntervalId) {
|
||||
clearInterval(themeIntervalId);
|
||||
themeIntervalId = null;
|
||||
}
|
||||
}
|
||||
|
||||
function _sendEvent(action, data) {
|
||||
var event = new CustomEvent("mozUITour", {
|
||||
bubbles: true,
|
||||
|
@ -306,88 +298,6 @@ if (typeof Mozilla == "undefined") {
|
|||
_sendEvent("hideInfo");
|
||||
};
|
||||
|
||||
/**
|
||||
* Preview a lightweight-theme applied to the browser UI.
|
||||
*
|
||||
* @see Mozilla.UITour.cycleThemes
|
||||
* @see Mozilla.UITour.resetTheme
|
||||
*
|
||||
* @param {Object} theme - Theme object format expected by `LightweightThemeManager.previewTheme`
|
||||
* @example
|
||||
* var theme = {
|
||||
* …
|
||||
* "iconURL": "https://addons.mozilla.org/_files/…/preview_small.jpg",
|
||||
* "headerURL": "https://addons.mozilla.org/_files/….jpg",
|
||||
* "name": "My cool theme",
|
||||
* "author": "Mozilla",
|
||||
* "footer": "https://addons.mozilla.org/_files/….jpg",
|
||||
* "previewURL": "https://addons.mozilla.org/_files/…/preview.jpg",
|
||||
* "updateURL": "https://versioncheck.addons.mozilla.org/…",
|
||||
* "accentcolor": "#000000",
|
||||
* "header": "https://addons.mozilla.org/_files/….jpg",
|
||||
* "version": "1.0",
|
||||
* "detailURL": "https://addons.mozilla.org/firefox/addon/…",
|
||||
* "textcolor": "#ffffff",
|
||||
* "id": "18066",
|
||||
* "description": "My awesome theme.",
|
||||
* …
|
||||
* };
|
||||
*
|
||||
* Mozilla.UITour.previewTheme(theme);
|
||||
*/
|
||||
Mozilla.UITour.previewTheme = function(theme) {
|
||||
_stopCyclingThemes();
|
||||
|
||||
_sendEvent("previewTheme", {
|
||||
theme: JSON.stringify(theme),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop previewing and cycling themes, returning to the user's previous theme.
|
||||
* @see Mozilla.UITour.cycleThemes
|
||||
* @see Mozilla.UITour.previewTheme
|
||||
*/
|
||||
Mozilla.UITour.resetTheme = function() {
|
||||
_stopCyclingThemes();
|
||||
|
||||
_sendEvent("resetTheme");
|
||||
};
|
||||
|
||||
/**
|
||||
* Cycle between an array of themes using the given delay.
|
||||
*
|
||||
* @see Mozilla.UITour.previewTheme
|
||||
* @see Mozilla.UITour.resetTheme
|
||||
*
|
||||
* @param {Object[]} themes - Array of themes
|
||||
* @param {Number} [delay=Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY]
|
||||
* - Time in milliseconds between rotating themes
|
||||
* @param {Function} callback - Function called at each rotation
|
||||
*/
|
||||
Mozilla.UITour.cycleThemes = function(themes, delay, callback) {
|
||||
_stopCyclingThemes();
|
||||
|
||||
if (!delay) {
|
||||
delay = Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY;
|
||||
}
|
||||
|
||||
function nextTheme() {
|
||||
var theme = themes.shift();
|
||||
themes.push(theme);
|
||||
|
||||
_sendEvent("previewTheme", {
|
||||
theme: JSON.stringify(theme),
|
||||
state: true,
|
||||
});
|
||||
|
||||
callback(theme);
|
||||
}
|
||||
|
||||
themeIntervalId = setInterval(nextTheme, delay);
|
||||
nextTheme();
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {String} Mozilla.UITour.MenuName
|
||||
* Valid values:<ul>
|
||||
|
|
|
@ -17,8 +17,6 @@ ChromeUtils.defineModuleGetter(this, "CustomizableUI",
|
|||
"resource:///modules/CustomizableUI.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "FxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PageActions",
|
||||
"resource:///modules/PageActions.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
||||
|
@ -395,16 +393,6 @@ var UITour = {
|
|||
break;
|
||||
}
|
||||
|
||||
case "previewTheme": {
|
||||
this.previewTheme(data.theme);
|
||||
break;
|
||||
}
|
||||
|
||||
case "resetTheme": {
|
||||
this.resetTheme();
|
||||
break;
|
||||
}
|
||||
|
||||
case "showMenu": {
|
||||
this.noautohideMenus.add(data.name);
|
||||
this.showMenu(window, data.name, () => {
|
||||
|
@ -780,7 +768,6 @@ var UITour = {
|
|||
}
|
||||
|
||||
this.noautohideMenus.clear();
|
||||
this.resetTheme();
|
||||
|
||||
// If there are no more tour tabs left in the window, teardown the tour for the whole window.
|
||||
if (!openTourBrowsers || openTourBrowsers.size == 0) {
|
||||
|
@ -982,17 +969,6 @@ var UITour = {
|
|||
return promise;
|
||||
},
|
||||
|
||||
previewTheme(aTheme) {
|
||||
let origin = Services.prefs.getCharPref("browser.uitour.themeOrigin");
|
||||
let data = LightweightThemeManager.parseTheme(aTheme, origin);
|
||||
if (data)
|
||||
LightweightThemeManager.previewTheme(data);
|
||||
},
|
||||
|
||||
resetTheme() {
|
||||
LightweightThemeManager.resetPreview();
|
||||
},
|
||||
|
||||
/**
|
||||
* The node to which a highlight or notification(-popup) is anchored is sometimes
|
||||
* obscured because it may be inside an overflow menu. This function should figure
|
||||
|
|
|
@ -973,7 +973,6 @@ you can use these alternative items. Otherwise, their values should be empty. -
|
|||
<!ENTITY customizeMode.toolbars2 "Toolbars">
|
||||
<!ENTITY customizeMode.lwthemes "Themes">
|
||||
<!ENTITY customizeMode.lwthemes.myThemes "My Themes">
|
||||
<!ENTITY customizeMode.lwthemes.recommended "Recommended">
|
||||
<!ENTITY customizeMode.lwthemes.menuManage "Manage">
|
||||
<!ENTITY customizeMode.lwthemes.menuManage.accessKey "M">
|
||||
<!ENTITY customizeMode.lwthemes.menuGetMore "Get More Themes">
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
# 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/.
|
||||
|
||||
lightweightThemes.recommended-1.name=A Web Browser Renaissance
|
||||
lightweightThemes.recommended-1.description=A Web Browser Renaissance is (C) Sean.Martell. Available under CC-BY-SA. No warranty.
|
||||
|
||||
lightweightThemes.recommended-2.name=Space Fantasy
|
||||
lightweightThemes.recommended-2.description=Space Fantasy is (C) fx5800p. Available under CC-BY-SA. No warranty.
|
||||
|
||||
lightweightThemes.recommended-4.name=Pastel Gradient
|
||||
lightweightThemes.recommended-4.description=Pastel Gradient is (C) darrinhenein. Available under CC-BY. No warranty.
|
|
@ -20,7 +20,6 @@
|
|||
locale/browser/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)
|
||||
locale/browser/browser.properties (%chrome/browser/browser.properties)
|
||||
locale/browser/customizableui/customizableWidgets.properties (%chrome/browser/customizableui/customizableWidgets.properties)
|
||||
locale/browser/lightweightThemes.properties (%chrome/browser/lightweightThemes.properties)
|
||||
locale/browser/uiDensity.properties (%chrome/browser/uiDensity.properties)
|
||||
locale/browser/pocket.properties (%chrome/browser/pocket.properties)
|
||||
locale/browser/search.properties (%chrome/browser/search.properties)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,175 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_asmjscache_asmjscache_h
|
||||
#define mozilla_dom_asmjscache_asmjscache_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Vector.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace JS {
|
||||
|
||||
enum AsmJSCacheResult {
|
||||
AsmJSCache_Success,
|
||||
AsmJSCache_MIN = AsmJSCache_Success,
|
||||
AsmJSCache_ModuleTooSmall,
|
||||
AsmJSCache_SynchronousScript,
|
||||
AsmJSCache_QuotaExceeded,
|
||||
AsmJSCache_StorageInitFailure,
|
||||
AsmJSCache_Disabled_Internal,
|
||||
AsmJSCache_Disabled_ShellFlags,
|
||||
AsmJSCache_Disabled_JitInspector,
|
||||
AsmJSCache_InternalError,
|
||||
AsmJSCache_Disabled_PrivateBrowsing,
|
||||
AsmJSCache_LIMIT
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
|
||||
class PrincipalInfo;
|
||||
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
namespace quota {
|
||||
class Client;
|
||||
} // namespace quota
|
||||
|
||||
namespace asmjscache {
|
||||
|
||||
class PAsmJSCacheEntryChild;
|
||||
class PAsmJSCacheEntryParent;
|
||||
|
||||
enum OpenMode { eOpenForRead, eOpenForWrite, NUM_OPEN_MODES };
|
||||
|
||||
// Each origin stores a fixed size (kNumEntries) LRU cache of compiled asm.js
|
||||
// modules. Each compiled asm.js module is stored in a separate file with one
|
||||
// extra metadata file that stores the LRU cache and enough information for a
|
||||
// client to pick which cached module's file to open.
|
||||
struct Metadata {
|
||||
static const unsigned kNumEntries = 16;
|
||||
static const unsigned kLastEntry = kNumEntries - 1;
|
||||
|
||||
struct Entry {
|
||||
uint32_t mFastHash;
|
||||
uint32_t mNumChars;
|
||||
uint32_t mFullHash;
|
||||
unsigned mModuleIndex;
|
||||
|
||||
void clear() {
|
||||
mFastHash = -1;
|
||||
mNumChars = -1;
|
||||
mFullHash = -1;
|
||||
}
|
||||
};
|
||||
|
||||
Entry mEntries[kNumEntries];
|
||||
};
|
||||
|
||||
// Parameters specific to opening a cache entry for writing
|
||||
struct WriteParams {
|
||||
int64_t mSize;
|
||||
int64_t mFastHash;
|
||||
int64_t mNumChars;
|
||||
int64_t mFullHash;
|
||||
|
||||
WriteParams() : mSize(0), mFastHash(0), mNumChars(0), mFullHash(0) {}
|
||||
};
|
||||
|
||||
// Parameters specific to opening a cache entry for reading
|
||||
struct ReadParams {
|
||||
const char16_t* mBegin;
|
||||
const char16_t* mLimit;
|
||||
|
||||
ReadParams() : mBegin(nullptr), mLimit(nullptr) {}
|
||||
};
|
||||
|
||||
// Implementation of AsmJSCacheOps, installed for the main JSRuntime by
|
||||
// nsJSEnvironment.cpp and DOM Worker JSRuntimes in RuntimeService.cpp.
|
||||
//
|
||||
// The Open* functions cannot be called directly from AsmJSCacheOps: they take
|
||||
// an nsIPrincipal as the first argument instead of a Handle<JSObject*>. The
|
||||
// caller must map the object to an nsIPrincipal.
|
||||
//
|
||||
// These methods may be called off the main thread and guarantee not to
|
||||
// access the given aPrincipal except on the main thread. In exchange, the
|
||||
// caller must ensure the given principal is alive from when OpenEntryForX is
|
||||
// called to when CloseEntryForX returns.
|
||||
|
||||
bool OpenEntryForRead(nsIPrincipal* aPrincipal, const char16_t* aBegin,
|
||||
const char16_t* aLimit, size_t* aSize,
|
||||
const uint8_t** aMemory, intptr_t* aHandle);
|
||||
void CloseEntryForRead(size_t aSize, const uint8_t* aMemory, intptr_t aHandle);
|
||||
JS::AsmJSCacheResult OpenEntryForWrite(nsIPrincipal* aPrincipal,
|
||||
const char16_t* aBegin,
|
||||
const char16_t* aEnd, size_t aSize,
|
||||
uint8_t** aMemory, intptr_t* aHandle);
|
||||
void CloseEntryForWrite(size_t aSize, uint8_t* aMemory, intptr_t aHandle);
|
||||
|
||||
// Called from QuotaManager.cpp:
|
||||
|
||||
quota::Client* CreateClient();
|
||||
|
||||
// Called from ipc/ContentParent.cpp:
|
||||
|
||||
PAsmJSCacheEntryParent* AllocEntryParent(
|
||||
OpenMode aOpenMode, WriteParams aWriteParams,
|
||||
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
|
||||
|
||||
void DeallocEntryParent(PAsmJSCacheEntryParent* aActor);
|
||||
|
||||
// Called from ipc/ContentChild.cpp:
|
||||
|
||||
void DeallocEntryChild(PAsmJSCacheEntryChild* aActor);
|
||||
|
||||
} // namespace asmjscache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::asmjscache::OpenMode>
|
||||
: public ContiguousEnumSerializer<
|
||||
mozilla::dom::asmjscache::OpenMode,
|
||||
mozilla::dom::asmjscache::eOpenForRead,
|
||||
mozilla::dom::asmjscache::NUM_OPEN_MODES> {};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::asmjscache::Metadata> {
|
||||
typedef mozilla::dom::asmjscache::Metadata paramType;
|
||||
static void Write(Message* aMsg, const paramType& aParam);
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter,
|
||||
paramType* aResult);
|
||||
static void Log(const paramType& aParam, std::wstring* aLog);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::asmjscache::WriteParams> {
|
||||
typedef mozilla::dom::asmjscache::WriteParams paramType;
|
||||
static void Write(Message* aMsg, const paramType& aParam);
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter,
|
||||
paramType* aResult);
|
||||
static void Log(const paramType& aParam, std::wstring* aLog);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<JS::AsmJSCacheResult>
|
||||
: public ContiguousEnumSerializer<JS::AsmJSCacheResult, JS::AsmJSCache_MIN,
|
||||
JS::AsmJSCache_LIMIT> {};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_dom_asmjscache_asmjscache_h
|
|
@ -1,53 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
include protocol PBackground;
|
||||
|
||||
using mozilla::dom::asmjscache::Metadata from "mozilla/dom/asmjscache/AsmJSCache.h";
|
||||
using JS::AsmJSCacheResult from "mozilla/dom/asmjscache/AsmJSCache.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace asmjscache {
|
||||
|
||||
union OpenMetadataForReadResponse
|
||||
{
|
||||
AsmJSCacheResult;
|
||||
uint32_t;
|
||||
};
|
||||
|
||||
protocol PAsmJSCacheEntry
|
||||
{
|
||||
manager PBackground;
|
||||
|
||||
// When the cache is opened to read, the parent process sends over the
|
||||
// origin's Metadata so the child process can select the cache entry to open
|
||||
// (based on hash) and notify the parent (via SelectCacheFileToRead).
|
||||
child:
|
||||
async OnOpenMetadataForRead(Metadata metadata);
|
||||
parent:
|
||||
async SelectCacheFileToRead(OpenMetadataForReadResponse response);
|
||||
|
||||
child:
|
||||
// Once the cache file has been opened, the child is notified and sent an
|
||||
// open file descriptor.
|
||||
async OnOpenCacheFile(int64_t fileSize, FileDescriptor fileDesc);
|
||||
|
||||
parent:
|
||||
// When the child process is done with the cache entry, the parent process
|
||||
// is notified (via Close).
|
||||
async Close();
|
||||
|
||||
child:
|
||||
// When there's an error during the opening phase, the child process is
|
||||
// notified (via __delete__) and sent an error result.
|
||||
// When the parent process receives the Close message, it closes the cache
|
||||
// entry on the parent side and the child is notified (via __delete__).
|
||||
// The protocol is destroyed in both cases.
|
||||
async __delete__(AsmJSCacheResult result);
|
||||
};
|
||||
|
||||
} // namespace asmjscache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -1,26 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "JavaScript Engine")
|
||||
|
||||
EXPORTS.mozilla.dom.asmjscache += [
|
||||
'AsmJSCache.h'
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'AsmJSCache.cpp'
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PAsmJSCacheEntry.ipdl'
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
|
|
@ -1,7 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"plugin:mozilla/mochitest-test",
|
||||
],
|
||||
};
|
|
@ -1,78 +0,0 @@
|
|||
/* globals jsFuns:false, complete:false */
|
||||
function f1() { "use asm"; function g() {} return g; }
|
||||
if (this.jsFuns) {
|
||||
ok(jsFuns.isAsmJSModule(f1), "f1 is an asm.js module");
|
||||
ok(jsFuns.isAsmJSFunction(f1()), "f1.g is an asm.js function");
|
||||
}
|
||||
|
||||
function f2(stdlib, foreign, buffer) {
|
||||
"use asm";
|
||||
var i32 = new stdlib.Int32Array(buffer);
|
||||
function main(n) {
|
||||
n = n|0;
|
||||
var i = 0, sum = 0;
|
||||
for (; (i|0) < (n|0); i=(i + 1)|0)
|
||||
sum = (sum + (i32[(i << 2) >> 2]|0))|0;
|
||||
return sum|0;
|
||||
}
|
||||
return main;
|
||||
}
|
||||
if (this.jsFuns)
|
||||
ok(jsFuns.isAsmJSModule(f2), "f2 is an asm.js module");
|
||||
var i32 = new Int32Array(16384); // Smallest allowed buffer size is 64KBy
|
||||
for (let i = 0; i < i32.length; i++)
|
||||
i32[i] = i;
|
||||
var f2Main = f2(this, null, i32.buffer);
|
||||
if (this.jsFuns)
|
||||
ok(jsFuns.isAsmJSFunction(f2Main), "f2.main is an asm.js function");
|
||||
if (f2Main(4) !== 6)
|
||||
throw new Error("f2Main(4)");
|
||||
if (f2Main(100) !== 4950)
|
||||
throw new Error("f2.main(100)");
|
||||
var sum = (((i32.length - 1) * i32.length) / 2);
|
||||
if (f2Main(i32.length) !== sum)
|
||||
throw new Error(`f2.main(${i32.length})`);
|
||||
if (f2Main(i32.length + 100) !== sum)
|
||||
throw new Error(`f2.main(${i32.length})`);
|
||||
|
||||
/* eslint-disable no-unused-vars,no-shadow */
|
||||
function f3(stdlib, foreign, buffer) {
|
||||
"use asm";
|
||||
var doneFunc = foreign.done;
|
||||
var i32 = new stdlib.Int32Array(buffer);
|
||||
function main() {
|
||||
var i = 0;
|
||||
var total = 0;
|
||||
while (1) {
|
||||
for (i = 0; (i|0) < 1000; i=(i + 1)|0)
|
||||
total = (total + i)|0;
|
||||
if (doneFunc(total|0)|0)
|
||||
break;
|
||||
}
|
||||
return total|0;
|
||||
}
|
||||
return main;
|
||||
}
|
||||
/* eslint-enable no-unused-vars,no-shadow */
|
||||
|
||||
var begin;
|
||||
var lastSum;
|
||||
function done(sumInner) {
|
||||
if (sumInner !== ((lastSum + 499500)|0))
|
||||
throw new Error(`bad sum: ${sumInner}, ${lastSum}, ${((lastSum + 499500)|0)}`);
|
||||
lastSum = sumInner;
|
||||
return (Date.now() - begin) > 3000;
|
||||
}
|
||||
var f3Main = f3(this, {done}, i32.buffer);
|
||||
if (this.jsFuns)
|
||||
ok(jsFuns.isAsmJSFunction(f3Main), "f3.main is an asm.js function");
|
||||
|
||||
begin = Date.now();
|
||||
lastSum = 0;
|
||||
if (f3Main() !== lastSum)
|
||||
throw new Error("f3.main()");
|
||||
|
||||
if (!this.jsFuns)
|
||||
postMessage("ok");
|
||||
else
|
||||
complete();
|
|
@ -1,14 +0,0 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_slow.js
|
||||
|
||||
[test_cachingBasic.html]
|
||||
disabled = bug 1520931
|
||||
[test_workers.html]
|
||||
disabled = bug 1520931
|
||||
[test_cachingMulti.html]
|
||||
disabled = bug 1520931
|
||||
[test_slow.html]
|
||||
# bug 929498
|
||||
skip-if = os == 'android'
|
||||
disabled = bug 1520931
|
|
@ -1,70 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=929236
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>asm.js browser tests</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=929236">asm.js browser tests</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
|
||||
<script>
|
||||
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
|
||||
|
||||
var code = "function f() { 'use asm';\n";
|
||||
for (var i = 0; i < 5000; i++)
|
||||
code += "function g" + i + "() { return " + i + "}\n";
|
||||
code += "return g42 }\n";
|
||||
code += "ok(jsFuns.isAsmJSModule(f), 'f is an asm.js module')\n";
|
||||
code += "var g42 = f();\n";
|
||||
code += "ok(jsFuns.isAsmJSFunction(g42), 'g42 is an asm.js function')\n";
|
||||
code += "ok(g42() === 42, 'g42 returns the correct result')\n";
|
||||
code += "finishedEvalAsync(f);";
|
||||
ok(code.length > 100000, "code is long enough to definitely hit the cache");
|
||||
|
||||
function evalAsync(codeInner) {
|
||||
var blob = new Blob([codeInner], {type: "application/javascript"});
|
||||
var script = document.createElement("script");
|
||||
script.src = URL.createObjectURL(blob);
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
var state = 0;
|
||||
function finishedEvalAsync(module) {
|
||||
switch (state) {
|
||||
case 0:
|
||||
state++;
|
||||
evalAsync(code);
|
||||
break;
|
||||
case 1:
|
||||
ok(jsFuns.isAsmJSModule(module), "module");
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
default:
|
||||
throw new Error("huh?");
|
||||
}
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
// generate a big ol asm.js module and compile it async so that we can hit
|
||||
// the asm.js cache.
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
evalAsync(code);
|
||||
}
|
||||
|
||||
if (!jsFuns.isAsmJSCompilationAvailable()) {
|
||||
ok(true, "isAsmJSCompilationAvailable is false, skipping this test!");
|
||||
} else {
|
||||
runTest();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,81 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=944821
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>asm.js browser tests</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=944821">asm.js browser tests</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
|
||||
<script>
|
||||
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
|
||||
|
||||
// generate four slightly different big asm.js modules and compile them async
|
||||
// so that we can hit the asm.js cache.
|
||||
|
||||
var code = "function f() { 'use asm';\n";
|
||||
for (let i = 0; i < 5000; i++)
|
||||
code += "function g" + i + "() { return " + i + "}\n";
|
||||
ok(code.length > 100000, "code is long enough to definitely hit the cache");
|
||||
|
||||
const N = 4;
|
||||
|
||||
var codes = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
var code2 = code;
|
||||
code2 += "return g" + i + ";\n";
|
||||
code2 += "}\n";
|
||||
code2 += "ok(jsFuns.isAsmJSModule(f), 'f is an asm.js module')\n";
|
||||
code2 += "var gX = f();\n";
|
||||
code2 += "ok(jsFuns.isAsmJSFunction(gX), 'gX is an asm.js function')\n";
|
||||
code2 += "ok(gX() === " + i + ", 'gX returns the correct result')\n";
|
||||
code2 += "finishedEvalAsync();\n";
|
||||
codes.push(code2);
|
||||
}
|
||||
|
||||
function evalAsync(codeInner) {
|
||||
var blob = new Blob([codeInner], {type: "application/javascript"});
|
||||
var script = document.createElement("script");
|
||||
script.src = URL.createObjectURL(blob);
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
var finishedCount = 0;
|
||||
function finishedEvalAsync() {
|
||||
finishedCount++;
|
||||
|
||||
if (finishedCount < 1 || finishedCount > 2 * N) {
|
||||
throw new Error("Huh?!");
|
||||
} else if (finishedCount == N) {
|
||||
for (let i = 0; i < N; i++)
|
||||
evalAsync(codes[i]);
|
||||
} else if (finishedCount == 2 * N) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
for (let i = 0; i < N; i++)
|
||||
evalAsync(codes[i]);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
}
|
||||
|
||||
if (!jsFuns.isAsmJSCompilationAvailable()) {
|
||||
ok(true, "isAsmJSCompilationAvailable is false, skipping this test!");
|
||||
} else {
|
||||
runTest();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=854209
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>asm.js browser tests</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=854209">asm.js browser tests</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
|
||||
<script>
|
||||
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
|
||||
|
||||
var completed = 0;
|
||||
function complete() {
|
||||
if (++completed == 2)
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (!jsFuns.isAsmJSCompilationAvailable()) {
|
||||
ok(true, "isAsmJSCompilationAvailable is false, skipping this test!");
|
||||
} else {
|
||||
var script = document.createElement("script");
|
||||
script.src = "http://mochi.test:8888/tests/dom/asmjscache/test/file_slow.js";
|
||||
document.body.appendChild(script);
|
||||
|
||||
var w = new Worker("http://mochi.test:8888/tests/dom/asmjscache/test/file_slow.js");
|
||||
w.onmessage = function(e) {
|
||||
ok(e.data === "ok", "Worker asm.js tests");
|
||||
complete();
|
||||
};
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,74 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=941830
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>asm.js browser tests</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=941830">asm.js browser tests</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
|
||||
<script>
|
||||
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
|
||||
|
||||
function runTest() {
|
||||
var asmjsCode = "function f() { 'use asm';";
|
||||
for (var i = 0; i < 5000; i++)
|
||||
asmjsCode += "function g" + i + "() { return " + i + "}";
|
||||
asmjsCode += "return g42 }";
|
||||
ok(asmjsCode.length > 100000, "code is long enough to definitely hit the cache");
|
||||
|
||||
var workerCode = asmjsCode;
|
||||
workerCode += "if (f()() !== 42) postMessage('fail'); else postMessage('ok');";
|
||||
workerCode = 'var code = "' + workerCode + '"; eval(code); eval(code)';
|
||||
var workerBlob = new Blob([workerCode], {type: "application/javascript"});
|
||||
|
||||
var mainCode = asmjsCode;
|
||||
mainCode += "ok(jsFuns.isAsmJSModule(f), 'f is a module')\n";
|
||||
mainCode += "var g42 = f();\n";
|
||||
mainCode += "ok(jsFuns.isAsmJSFunction(g42), 'g42 is an asm.js function');\n";
|
||||
mainCode += "ok(g42() === 42, 'g42 returns the correct result');\n";
|
||||
mainCode += "SimpleTest.finish();\n";
|
||||
var mainBlob = new Blob([mainCode], {type: "application/javascript"});
|
||||
|
||||
var w = new Worker(URL.createObjectURL(workerBlob));
|
||||
|
||||
var received = 0;
|
||||
w.onmessage = function(e) {
|
||||
switch (received) {
|
||||
case 0:
|
||||
ok(e.data === "ok", "Received first message");
|
||||
received = 1;
|
||||
break;
|
||||
case 1:
|
||||
ok(e.data === "ok", "Received second message");
|
||||
received = 2;
|
||||
|
||||
var script = document.createElement("script");
|
||||
script.src = URL.createObjectURL(mainBlob);
|
||||
document.body.appendChild(script);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Huh?");
|
||||
}
|
||||
};
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
}
|
||||
|
||||
if (!jsFuns.isAsmJSCompilationAvailable()) {
|
||||
ok(true, "isAsmJSCompilationAvailable is false, skipping this test!");
|
||||
} else {
|
||||
runTest();
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -261,6 +261,17 @@ WebRenderBridgeChild* nsDOMWindowUtils::GetWebRenderBridge() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
CompositorBridgeChild* nsDOMWindowUtils::GetCompositorBridge() {
|
||||
if (nsIWidget* widget = GetWidget()) {
|
||||
if (LayerManager* lm = widget->GetLayerManager()) {
|
||||
if (CompositorBridgeChild* cbc = lm->GetCompositorBridgeChild()) {
|
||||
return cbc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SyncFlushCompositor() {
|
||||
if (nsIWidget* widget = GetWidget()) {
|
||||
|
@ -4099,6 +4110,18 @@ nsDOMWindowUtils::WrCapture() {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SetCompositionRecording(bool aValue) {
|
||||
if (CompositorBridgeChild* cbc = GetCompositorBridge()) {
|
||||
if (aValue) {
|
||||
cbc->SendBeginRecording(TimeStamp::Now());
|
||||
} else {
|
||||
cbc->SendEndRecording();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SetSystemFont(const nsACString& aFontName) {
|
||||
nsIWidget* widget = GetWidget();
|
||||
|
|
|
@ -82,6 +82,7 @@ class nsDOMWindowUtils final : public nsIDOMWindowUtils,
|
|||
mozilla::dom::Document* GetDocument();
|
||||
mozilla::layers::LayerTransactionChild* GetLayerTransaction();
|
||||
mozilla::layers::WebRenderBridgeChild* GetWebRenderBridge();
|
||||
mozilla::layers::CompositorBridgeChild* GetCompositorBridge();
|
||||
|
||||
// Until callers are annotated.
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
|
|
|
@ -15694,8 +15694,18 @@ QuotaClient::~QuotaClient() {
|
|||
nsresult QuotaClient::AsyncDeleteFile(FileManager* aFileManager,
|
||||
int64_t aFileId) {
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(mDeleteTimer);
|
||||
|
||||
if (mShutdownRequested) {
|
||||
// Whoops! We want to delete an IndexedDB disk-backed File but it's too late
|
||||
// to actually delete the file! This means we're going to "leak" the file
|
||||
// and leave it around when we shouldn't! (The file will stay around until
|
||||
// next storage initialization is triggered when the app is started again).
|
||||
// Fixing this is tracked by bug 1539377.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mDeleteTimer);
|
||||
MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
|
||||
|
||||
nsresult rv = mDeleteTimer->InitWithNamedFuncCallback(
|
||||
|
|
|
@ -1932,6 +1932,11 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
*/
|
||||
void wrCapture();
|
||||
|
||||
/**
|
||||
* Toggle recording of composition on and off.
|
||||
*/
|
||||
void setCompositionRecording(in boolean aValue);
|
||||
|
||||
/**
|
||||
* Returns whether the document we're associated to has recorded a given CSS
|
||||
* property via the use counter mechanism.
|
||||
|
|
|
@ -56,7 +56,6 @@ DIRS += [
|
|||
'grid',
|
||||
'html',
|
||||
'jsurl',
|
||||
'asmjscache',
|
||||
'mathml',
|
||||
'media',
|
||||
'midi',
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/dom/PContent.h"
|
||||
#include "mozilla/dom/asmjscache/AsmJSCache.h"
|
||||
#include "mozilla/dom/cache/QuotaClient.h"
|
||||
#include "mozilla/dom/indexedDB/ActorsParent.h"
|
||||
#include "mozilla/dom/localstorage/ActorsParent.h"
|
||||
|
@ -1375,7 +1374,7 @@ void ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) {
|
|||
|
||||
namespace {
|
||||
|
||||
nsString gBaseDirPath;
|
||||
StaticAutoPtr<nsString> gBaseDirPath;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool gQuotaManagerInitialized = false;
|
||||
|
@ -2499,6 +2498,14 @@ QuotaManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
nsresult rv;
|
||||
|
||||
if (!strcmp(aTopic, kProfileDoChangeTopic)) {
|
||||
if (NS_WARN_IF(gBaseDirPath)) {
|
||||
NS_WARNING("profile-before-change-qm must precede repeated "
|
||||
"profile-do-change!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
gBaseDirPath = new nsString();
|
||||
|
||||
nsCOMPtr<nsIFile> baseDir;
|
||||
rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
|
||||
getter_AddRefs(baseDir));
|
||||
|
@ -2510,7 +2517,7 @@ QuotaManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
return rv;
|
||||
}
|
||||
|
||||
rv = baseDir->GetPath(gBaseDirPath);
|
||||
rv = baseDir->GetPath(*gBaseDirPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -2519,6 +2526,11 @@ QuotaManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
}
|
||||
|
||||
if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID)) {
|
||||
if (NS_WARN_IF(!gBaseDirPath)) {
|
||||
NS_WARNING("profile-do-change must precede profile-before-change-qm!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// mPendingProfileChange is our re-entrancy guard (the nested event loop
|
||||
// below may cause re-entrancy).
|
||||
if (mPendingProfileChange) {
|
||||
|
@ -2542,7 +2554,7 @@ QuotaManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
|
||||
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return mShutdownComplete; }));
|
||||
|
||||
gBaseDirPath.Truncate();
|
||||
gBaseDirPath = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2905,16 +2917,19 @@ void QuotaManager::GetOrCreate(nsIRunnable* aCallback,
|
|||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (IsShuttingDown()) {
|
||||
MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!");
|
||||
MOZ_ASSERT(false, "Calling QuotaManager::GetOrCreate() after shutdown!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (gInstance || gCreateFailed) {
|
||||
if (NS_WARN_IF(!gBaseDirPath)) {
|
||||
NS_WARNING("profile-do-change must precede QuotaManager::GetOrCreate()");
|
||||
MOZ_ASSERT(!gInstance);
|
||||
} else if (gInstance || gCreateFailed) {
|
||||
MOZ_ASSERT_IF(gCreateFailed, !gInstance);
|
||||
} else {
|
||||
RefPtr<QuotaManager> manager = new QuotaManager();
|
||||
|
||||
nsresult rv = manager->Init(gBaseDirPath);
|
||||
nsresult rv = manager->Init(*gBaseDirPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
gCreateFailed = true;
|
||||
} else {
|
||||
|
@ -3286,9 +3301,8 @@ nsresult QuotaManager::Init(const nsAString& aBasePath) {
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
static_assert(Client::IDB == 0 && Client::ASMJS == 1 &&
|
||||
Client::DOMCACHE == 2 && Client::SDB == 3 &&
|
||||
Client::LS == 4 && Client::TYPE_MAX == 5,
|
||||
static_assert(Client::IDB == 0 && Client::DOMCACHE == 1 && Client::SDB == 2 &&
|
||||
Client::LS == 3 && Client::TYPE_MAX == 4,
|
||||
"Fix the registration!");
|
||||
|
||||
MOZ_ASSERT(mClients.Capacity() == Client::TYPE_MAX,
|
||||
|
@ -3296,7 +3310,6 @@ nsresult QuotaManager::Init(const nsAString& aBasePath) {
|
|||
|
||||
// Register clients.
|
||||
mClients.AppendElement(indexedDB::CreateQuotaClient());
|
||||
mClients.AppendElement(asmjscache::CreateClient());
|
||||
mClients.AppendElement(cache::CreateQuotaClient());
|
||||
mClients.AppendElement(simpledb::CreateQuotaClient());
|
||||
if (NextGenLocalStorageEnabled()) {
|
||||
|
|
|
@ -43,7 +43,6 @@ class Client {
|
|||
enum Type {
|
||||
IDB = 0,
|
||||
// APPCACHE,
|
||||
ASMJS,
|
||||
DOMCACHE,
|
||||
SDB,
|
||||
LS,
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "CompositionRecorder.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "gfxPrefs.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include "stdio.h"
|
||||
#ifdef XP_WIN
|
||||
# include "direct.h"
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include "sys/stat.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
CompositionRecorder::CompositionRecorder(TimeStamp aRecordingStart)
|
||||
: mRecordingStart(aRecordingStart) {}
|
||||
|
||||
CompositionRecorder::~CompositionRecorder() {}
|
||||
|
||||
void CompositionRecorder::RecordFrame(RecordedFrame* aFrame) {
|
||||
mCollectedFrames.AppendElement(aFrame);
|
||||
}
|
||||
|
||||
void CompositionRecorder::WriteCollectedFrames() {
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::tm tm = *std::localtime(&t);
|
||||
std::stringstream str;
|
||||
str << gfxPrefs::LayersWindowRecordingPath() << "windowrecording-"
|
||||
<< std::put_time(&tm, "%Y%m%d%H%M%S");
|
||||
|
||||
#ifdef XP_WIN
|
||||
_mkdir(str.str().c_str());
|
||||
#else
|
||||
mkdir(str.str().c_str(), 0777);
|
||||
#endif
|
||||
|
||||
uint32_t i = 1;
|
||||
for (RefPtr<RecordedFrame>& frame : mCollectedFrames) {
|
||||
RefPtr<DataSourceSurface> surf = frame->GetSourceSurface();
|
||||
std::stringstream filename;
|
||||
filename << str.str() << "/frame-" << i << "-"
|
||||
<< uint32_t(
|
||||
(frame->GetTimeStamp() - mRecordingStart).ToMilliseconds())
|
||||
<< ".png";
|
||||
gfxUtils::WriteAsPNG(surf, filename.str().c_str());
|
||||
i++;
|
||||
}
|
||||
mCollectedFrames.Clear();
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_layers_CompositionRecorder_h
|
||||
#define mozilla_layers_CompositionRecorder_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gfx {
|
||||
class DataSourceSurface;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
|
||||
class RecordedFrame {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecordedFrame)
|
||||
|
||||
// The resulting DataSourceSurface must not be kept alive beyond the lifetime
|
||||
// of the RecordedFrame object, since it may refer to data owned by the frame.
|
||||
virtual already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() = 0;
|
||||
TimeStamp GetTimeStamp() { return mTimeStamp; }
|
||||
|
||||
protected:
|
||||
virtual ~RecordedFrame() {}
|
||||
RecordedFrame(const TimeStamp& aTimeStamp) : mTimeStamp(aTimeStamp) {}
|
||||
|
||||
private:
|
||||
TimeStamp mTimeStamp;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class CompositionRecorder final {
|
||||
public:
|
||||
explicit CompositionRecorder(TimeStamp aRecordingStart);
|
||||
~CompositionRecorder();
|
||||
|
||||
void RecordFrame(RecordedFrame* aFrame);
|
||||
|
||||
void WriteCollectedFrames();
|
||||
|
||||
private:
|
||||
nsTArray<RefPtr<RecordedFrame>> mCollectedFrames;
|
||||
TimeStamp mRecordingStart;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_ProfilerScreenshots_h
|
|
@ -415,7 +415,8 @@ MOZ_DEFINE_ENUM_CLASS_WITH_BASE(ScrollDirection, uint32_t, (
|
|||
MOZ_DEFINE_ENUM_CLASS_WITH_BASE(CompositionPayloadType, uint8_t, (
|
||||
eKeyPress,
|
||||
eAPZScroll,
|
||||
eAPZPinchZoom
|
||||
eAPZPinchZoom,
|
||||
eContentPaint
|
||||
));
|
||||
// clang-format on
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <stdint.h> // for uint64_t, uint32_t
|
||||
|
||||
#include "mozilla/layers/APZTypes.h"
|
||||
#include "mozilla/layers/LayersTypes.h" // for TouchBehaviorFlags
|
||||
#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid, etc
|
||||
#include "mozilla/layers/ZoomConstraints.h" // for ZoomConstraints
|
||||
|
|
|
@ -371,6 +371,9 @@ void ClientLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
|
|||
mWidget->PrepareWindowEffects();
|
||||
}
|
||||
EndTransactionInternal(aCallback, aCallbackData, aFlags);
|
||||
if (XRE_IsContentProcess()) {
|
||||
RegisterPayload({CompositionPayloadType::eContentPaint, TimeStamp::Now()});
|
||||
}
|
||||
ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
|
||||
|
||||
if (mRepeatTransaction) {
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace layers {
|
|||
class CanvasLayerComposite;
|
||||
class ColorLayerComposite;
|
||||
class Compositor;
|
||||
class CompositionRecorder;
|
||||
class ContainerLayerComposite;
|
||||
class Diagnostics;
|
||||
struct EffectChain;
|
||||
|
@ -200,6 +201,10 @@ class HostLayerManager : public LayerManager {
|
|||
mCompositorBridgeID = aID;
|
||||
}
|
||||
|
||||
void SetCompositionRecorder(CompositionRecorder* aRecorder) {
|
||||
mCompositionRecorder = aRecorder;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool mDebugOverlayWantsNextFrame;
|
||||
nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
|
||||
|
@ -213,6 +218,7 @@ class HostLayerManager : public LayerManager {
|
|||
bool mWindowOverlayChanged;
|
||||
TimeDuration mLastPaintTime;
|
||||
TimeStamp mRenderStartTime;
|
||||
CompositionRecorder* mCompositionRecorder = nullptr;
|
||||
|
||||
// Render time for the current composition.
|
||||
TimeStamp mCompositionTime;
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "mozilla/layers/APZUpdater.h" // for APZUpdater
|
||||
#include "mozilla/layers/AsyncCompositionManager.h"
|
||||
#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
|
||||
#include "mozilla/layers/CompositionRecorder.h" // for CompositionRecorder
|
||||
#include "mozilla/layers/Compositor.h" // for Compositor
|
||||
#include "mozilla/layers/CompositorManagerParent.h" // for CompositorManagerParent
|
||||
#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
|
||||
|
@ -2599,5 +2600,19 @@ int32_t RecordContentFrameTime(
|
|||
return 0;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult CompositorBridgeParent::RecvBeginRecording(
|
||||
const TimeStamp& aRecordingStart) {
|
||||
mCompositionRecorder.reset(new CompositionRecorder(aRecordingStart));
|
||||
mLayerManager->SetCompositionRecorder(mCompositionRecorder.get());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult CompositorBridgeParent::RecvEndRecording() {
|
||||
mLayerManager->SetCompositionRecorder(nullptr);
|
||||
mCompositionRecorder->WriteCollectedFrames();
|
||||
mCompositionRecorder.reset(nullptr);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -72,6 +72,7 @@ class APZSampler;
|
|||
class APZUpdater;
|
||||
class AsyncCompositionManager;
|
||||
class AsyncImagePipelineManager;
|
||||
class CompositionRecorder;
|
||||
class Compositor;
|
||||
class CompositorAnimationStorage;
|
||||
class CompositorBridgeParent;
|
||||
|
@ -238,6 +239,9 @@ class CompositorBridgeParentBase : public PCompositorBridgeParent,
|
|||
const nsIntRegion& region) = 0;
|
||||
virtual mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() = 0;
|
||||
virtual mozilla::ipc::IPCResult RecvAllPluginsCaptured() = 0;
|
||||
virtual mozilla::ipc::IPCResult RecvBeginRecording(
|
||||
const TimeStamp& aRecordingStart) = 0;
|
||||
virtual mozilla::ipc::IPCResult RecvEndRecording() = 0;
|
||||
virtual mozilla::ipc::IPCResult RecvInitialize(
|
||||
const LayersId& rootLayerTreeId) = 0;
|
||||
virtual mozilla::ipc::IPCResult RecvGetFrameUniformity(
|
||||
|
@ -343,6 +347,9 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
|
|||
};
|
||||
|
||||
mozilla::ipc::IPCResult RecvAllPluginsCaptured() override;
|
||||
mozilla::ipc::IPCResult RecvBeginRecording(
|
||||
const TimeStamp& aRecordingStart) override;
|
||||
mozilla::ipc::IPCResult RecvEndRecording() override;
|
||||
|
||||
virtual void NotifyMemoryPressure() override;
|
||||
virtual void AccumulateMemoryReport(wr::MemoryReport*) override;
|
||||
|
@ -762,6 +769,7 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
|
|||
// mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy.
|
||||
RefPtr<CompositorBridgeParent> mSelfRef;
|
||||
RefPtr<CompositorAnimationStorage> mAnimationStorage;
|
||||
UniquePtr<CompositionRecorder> mCompositionRecorder;
|
||||
|
||||
TimeDuration mPaintTime;
|
||||
|
||||
|
|
|
@ -84,6 +84,12 @@ class ContentCompositorBridgeParent final : public CompositorBridgeParentBase {
|
|||
|
||||
mozilla::ipc::IPCResult RecvAllPluginsCaptured() override { return IPC_OK(); }
|
||||
|
||||
mozilla::ipc::IPCResult RecvBeginRecording(
|
||||
const TimeStamp& aRecordingStart) override {
|
||||
return IPC_OK();
|
||||
}
|
||||
mozilla::ipc::IPCResult RecvEndRecording() override { return IPC_OK(); }
|
||||
|
||||
mozilla::ipc::IPCResult RecvGetFrameUniformity(
|
||||
FrameUniformityData* aOutData) override {
|
||||
// Don't support calculating frame uniformity on the child process and
|
||||
|
|
|
@ -264,6 +264,9 @@ parent:
|
|||
sync CheckContentOnlyTDR(uint32_t sequenceNum)
|
||||
returns (bool isContentOnlyTDR);
|
||||
|
||||
async BeginRecording(TimeStamp aRecordingStart);
|
||||
async EndRecording();
|
||||
|
||||
child:
|
||||
// Send back Compositor Frame Metrics from APZCs so tiled layers can
|
||||
// update progressively.
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "FrameBuilder.h"
|
||||
#include "LayersLogging.h"
|
||||
#include "UtilityMLGPU.h"
|
||||
#include "CompositionRecorder.h"
|
||||
#include "mozilla/layers/Diagnostics.h"
|
||||
#include "mozilla/layers/TextRenderer.h"
|
||||
|
||||
|
@ -42,6 +43,49 @@ static const int kDebugOverlayY = 5;
|
|||
static const int kDebugOverlayMaxWidth = 600;
|
||||
static const int kDebugOverlayMaxHeight = 96;
|
||||
|
||||
class RecordedFrameMLGPU : public RecordedFrame {
|
||||
public:
|
||||
RecordedFrameMLGPU(MLGDevice* aDevice, MLGTexture* aTexture,
|
||||
const TimeStamp& aTimestamp)
|
||||
: RecordedFrame(aTimestamp), mDevice(aDevice) {
|
||||
mSoftTexture =
|
||||
aDevice->CreateTexture(aTexture->GetSize(), SurfaceFormat::B8G8R8A8,
|
||||
MLGUsage::Staging, MLGTextureFlags::None);
|
||||
|
||||
aDevice->CopyTexture(mSoftTexture, IntPoint(), aTexture,
|
||||
IntRect(IntPoint(), aTexture->GetSize()));
|
||||
}
|
||||
|
||||
~RecordedFrameMLGPU() {
|
||||
if (mIsMapped) {
|
||||
mDevice->Unmap(mSoftTexture);
|
||||
}
|
||||
}
|
||||
|
||||
virtual already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override {
|
||||
if (mDataSurf) {
|
||||
return RefPtr<DataSourceSurface>(mDataSurf).forget();
|
||||
}
|
||||
MLGMappedResource map;
|
||||
if (!mDevice->Map(mSoftTexture, MLGMapType::READ, &map)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mIsMapped = true;
|
||||
mDataSurf = Factory::CreateWrappingDataSourceSurface(
|
||||
map.mData, map.mStride, mSoftTexture->GetSize(),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
return RefPtr<DataSourceSurface>(mDataSurf).forget();
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<MLGDevice> mDevice;
|
||||
// Software texture in VRAM.
|
||||
RefPtr<MLGTexture> mSoftTexture;
|
||||
RefPtr<DataSourceSurface> mDataSurf;
|
||||
bool mIsMapped = false;
|
||||
};
|
||||
|
||||
LayerManagerMLGPU::LayerManagerMLGPU(widget::CompositorWidget* aWidget)
|
||||
: mWidget(aWidget),
|
||||
mDrawDiagnostics(false),
|
||||
|
@ -356,6 +400,22 @@ void LayerManagerMLGPU::RenderLayers() {
|
|||
|
||||
mProfilerScreenshotGrabber.MaybeGrabScreenshot(
|
||||
mDevice, builder.GetWidgetRT()->GetTexture());
|
||||
|
||||
if (mCompositionRecorder) {
|
||||
bool hasContentPaint = false;
|
||||
for (CompositionPayload& payload : mPayload) {
|
||||
if (payload.mType == CompositionPayloadType::eContentPaint) {
|
||||
hasContentPaint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasContentPaint) {
|
||||
RefPtr<RecordedFrame> frame = new RecordedFrameMLGPU(
|
||||
mDevice, builder.GetWidgetRT()->GetTexture(), TimeStamp::Now());
|
||||
mCompositionRecorder->RecordFrame(frame);
|
||||
}
|
||||
}
|
||||
mCurrentFrame = nullptr;
|
||||
|
||||
if (mDrawDiagnostics) {
|
||||
|
|
|
@ -161,6 +161,7 @@ EXPORTS.mozilla.layers += [
|
|||
'composite/TextRenderer.h',
|
||||
'composite/TextureHost.h',
|
||||
'composite/TiledContentHost.h',
|
||||
'CompositionRecorder.h',
|
||||
'Compositor.h',
|
||||
'CompositorTypes.h',
|
||||
'CopyableCanvasRenderer.h',
|
||||
|
@ -409,6 +410,7 @@ UNIFIED_SOURCES += [
|
|||
'composite/TextRenderer.cpp',
|
||||
'composite/TextureHost.cpp',
|
||||
'composite/TiledContentHost.cpp',
|
||||
'CompositionRecorder.cpp',
|
||||
'Compositor.cpp',
|
||||
'CopyableCanvasRenderer.cpp',
|
||||
'Effects.cpp',
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#ifndef MOZILLA_GFX_GLMANAGER_H
|
||||
#define MOZILLA_GFX_GLMANAGER_H
|
||||
|
||||
#include "GLDefs.h"
|
||||
#include "mozilla/gfx/Types.h" // for SurfaceFormat
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
|
|
@ -669,6 +669,7 @@ class gfxPrefs final {
|
|||
DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.single-tile.enabled", LayersSingleTileEnabled, bool, true);
|
||||
DECL_GFX_PREF(Live, "layers.force-synchronous-resize", LayersForceSynchronousResize, bool, true);
|
||||
DECL_GFX_PREF(Once, "layers.windowrecording.path", LayersWindowRecordingPath, std::string, std::string());
|
||||
|
||||
// We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
|
||||
// screen size does not align nicely to the default tile size. Although layers can be any size,
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "mozilla/dom/PFileSystemRequestChild.h"
|
||||
#include "mozilla/dom/EndpointForReportChild.h"
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "mozilla/dom/asmjscache/AsmJSCache.h"
|
||||
#include "mozilla/dom/cache/ActorUtils.h"
|
||||
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
|
||||
#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
|
||||
|
@ -89,7 +88,6 @@ using mozilla::dom::PServiceWorkerChild;
|
|||
using mozilla::dom::PServiceWorkerContainerChild;
|
||||
using mozilla::dom::PServiceWorkerRegistrationChild;
|
||||
using mozilla::dom::StorageDBChild;
|
||||
using mozilla::dom::asmjscache::PAsmJSCacheEntryChild;
|
||||
using mozilla::dom::cache::PCacheChild;
|
||||
using mozilla::dom::cache::PCacheStorageChild;
|
||||
using mozilla::dom::cache::PCacheStreamControlChild;
|
||||
|
@ -572,21 +570,6 @@ bool BackgroundChildImpl::DeallocPParentToChildStreamChild(
|
|||
return true;
|
||||
}
|
||||
|
||||
PAsmJSCacheEntryChild* BackgroundChildImpl::AllocPAsmJSCacheEntryChild(
|
||||
const dom::asmjscache::OpenMode& aOpenMode,
|
||||
const dom::asmjscache::WriteParams& aWriteParams,
|
||||
const PrincipalInfo& aPrincipalInfo) {
|
||||
MOZ_CRASH("PAsmJSCacheEntryChild actors should be manually constructed!");
|
||||
}
|
||||
|
||||
bool BackgroundChildImpl::DeallocPAsmJSCacheEntryChild(
|
||||
PAsmJSCacheEntryChild* aActor) {
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
dom::asmjscache::DeallocEntryChild(aActor);
|
||||
return true;
|
||||
}
|
||||
|
||||
BackgroundChildImpl::PQuotaChild* BackgroundChildImpl::AllocPQuotaChild() {
|
||||
MOZ_CRASH("PQuotaChild actor should be manually constructed!");
|
||||
}
|
||||
|
|
|
@ -213,14 +213,6 @@ class BackgroundChildImpl : public PBackgroundChild {
|
|||
virtual bool DeallocPParentToChildStreamChild(
|
||||
PParentToChildStreamChild* aActor) override;
|
||||
|
||||
virtual PAsmJSCacheEntryChild* AllocPAsmJSCacheEntryChild(
|
||||
const dom::asmjscache::OpenMode& aOpenMode,
|
||||
const dom::asmjscache::WriteParams& aWriteParams,
|
||||
const PrincipalInfo& aPrincipalInfo) override;
|
||||
|
||||
virtual bool DeallocPAsmJSCacheEntryChild(
|
||||
PAsmJSCacheEntryChild* aActor) override;
|
||||
|
||||
virtual PQuotaChild* AllocPQuotaChild() override;
|
||||
|
||||
virtual bool DeallocPQuotaChild(PQuotaChild* aActor) override;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include "mozilla/dom/ServiceWorkerManagerParent.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/dom/StorageActivityService.h"
|
||||
#include "mozilla/dom/asmjscache/AsmJSCache.h"
|
||||
#include "mozilla/dom/cache/ActorUtils.h"
|
||||
#include "mozilla/dom/indexedDB/ActorsParent.h"
|
||||
#include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
|
||||
|
@ -88,7 +87,6 @@ using mozilla::dom::PServiceWorkerParent;
|
|||
using mozilla::dom::PServiceWorkerRegistrationParent;
|
||||
using mozilla::dom::UDPSocketParent;
|
||||
using mozilla::dom::WebAuthnTransactionParent;
|
||||
using mozilla::dom::asmjscache::PAsmJSCacheEntryParent;
|
||||
using mozilla::dom::cache::PCacheParent;
|
||||
using mozilla::dom::cache::PCacheStorageParent;
|
||||
using mozilla::dom::cache::PCacheStreamControlParent;
|
||||
|
@ -949,26 +947,6 @@ mozilla::ipc::IPCResult BackgroundParentImpl::RecvMessagePortForceClose(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
PAsmJSCacheEntryParent* BackgroundParentImpl::AllocPAsmJSCacheEntryParent(
|
||||
const dom::asmjscache::OpenMode& aOpenMode,
|
||||
const dom::asmjscache::WriteParams& aWriteParams,
|
||||
const PrincipalInfo& aPrincipalInfo) {
|
||||
AssertIsInMainOrSocketProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
return dom::asmjscache::AllocEntryParent(aOpenMode, aWriteParams,
|
||||
aPrincipalInfo);
|
||||
}
|
||||
|
||||
bool BackgroundParentImpl::DeallocPAsmJSCacheEntryParent(
|
||||
PAsmJSCacheEntryParent* aActor) {
|
||||
AssertIsInMainOrSocketProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
dom::asmjscache::DeallocEntryParent(aActor);
|
||||
return true;
|
||||
}
|
||||
|
||||
BackgroundParentImpl::PQuotaParent* BackgroundParentImpl::AllocPQuotaParent() {
|
||||
AssertIsInMainOrSocketProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
|
|
@ -263,14 +263,6 @@ class BackgroundParentImpl : public PBackgroundParent {
|
|||
const nsID& aUUID, const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID) override;
|
||||
|
||||
virtual PAsmJSCacheEntryParent* AllocPAsmJSCacheEntryParent(
|
||||
const dom::asmjscache::OpenMode& aOpenMode,
|
||||
const dom::asmjscache::WriteParams& aWriteParams,
|
||||
const PrincipalInfo& aPrincipalInfo) override;
|
||||
|
||||
virtual bool DeallocPAsmJSCacheEntryParent(
|
||||
PAsmJSCacheEntryParent* aActor) override;
|
||||
|
||||
virtual PQuotaParent* AllocPQuotaParent() override;
|
||||
|
||||
virtual bool DeallocPQuotaParent(PQuotaParent* aActor) override;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* 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/. */
|
||||
|
||||
include protocol PAsmJSCacheEntry;
|
||||
include protocol PBackgroundIDBFactory;
|
||||
include protocol PBackgroundIndexedDBUtils;
|
||||
include protocol PBackgroundSDBConnection;
|
||||
|
@ -63,18 +62,11 @@ include "mozilla/layers/LayersMessageUtils.h";
|
|||
using mozilla::dom::cache::Namespace
|
||||
from "mozilla/dom/cache/Types.h";
|
||||
|
||||
using mozilla::dom::asmjscache::OpenMode
|
||||
from "mozilla/dom/asmjscache/AsmJSCache.h";
|
||||
|
||||
using mozilla::dom::asmjscache::WriteParams
|
||||
from "mozilla/dom/asmjscache/AsmJSCache.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
sync protocol PBackground
|
||||
{
|
||||
manages PAsmJSCacheEntry;
|
||||
manages PBackgroundIDBFactory;
|
||||
manages PBackgroundIndexedDBUtils;
|
||||
manages PBackgroundSDBConnection;
|
||||
|
@ -185,10 +177,6 @@ parent:
|
|||
|
||||
async MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
|
||||
|
||||
async PAsmJSCacheEntry(OpenMode openMode,
|
||||
WriteParams write,
|
||||
PrincipalInfo principalInfo);
|
||||
|
||||
async PQuota();
|
||||
|
||||
async ShutdownQuotaManager();
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
* - writes to fields in private data
|
||||
* - writes to non-markable fields like JSObject::private that point to
|
||||
* markable data
|
||||
* The last category is the trickiest. Even though the private pointers does not
|
||||
* The last category is the trickiest. Even though the private pointer does not
|
||||
* point to a GC thing, changing the private pointer may change the set of
|
||||
* objects that are traced by the GC. Therefore it needs a write barrier.
|
||||
*
|
||||
|
@ -119,9 +119,9 @@
|
|||
* reference them. The solution is to maintain information about these pointers,
|
||||
* and mark their targets when we start a minor collection.
|
||||
*
|
||||
* The pointers can be thought of as edges in object graph, and the set of edges
|
||||
* from the tenured generation into the nursery is know as the remembered set.
|
||||
* Post barriers are used to track this remembered set.
|
||||
* The pointers can be thought of as edges in an object graph, and the set of
|
||||
* edges from the tenured generation into the nursery is known as the remembered
|
||||
* set. Post barriers are used to track this remembered set.
|
||||
*
|
||||
* Whenever a slot which could contain such a pointer is written, we check
|
||||
* whether the pointed-to thing is in the nursery (if storeBuffer() returns a
|
||||
|
|
|
@ -63,6 +63,8 @@ LOCAL_INCLUDES += [
|
|||
'celt',
|
||||
'include',
|
||||
'silk',
|
||||
'silk/fixed',
|
||||
'silk/float',
|
||||
'src',
|
||||
]
|
||||
|
||||
|
@ -76,15 +78,9 @@ UNIFIED_SOURCES += opus_sources
|
|||
SOURCES += opus_nonunified_sources
|
||||
|
||||
if CONFIG['MOZ_SAMPLE_TYPE_FLOAT32']:
|
||||
LOCAL_INCLUDES += [
|
||||
'silk/float',
|
||||
]
|
||||
UNIFIED_SOURCES += silk_sources_float
|
||||
UNIFIED_SOURCES += opus_sources_float
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'silk/fixed',
|
||||
]
|
||||
UNIFIED_SOURCES += silk_sources_fixed
|
||||
|
||||
if CONFIG['CPU_ARCH'] in ('x86', 'x86_64'):
|
||||
|
|
|
@ -532,7 +532,7 @@ var BrowserApp = {
|
|||
Telemetry.addData("FENNEC_TRACKING_PROTECTION_STATE", parseInt(BrowserApp.getTrackingProtectionState()));
|
||||
});
|
||||
|
||||
InitLater(() => LightWeightThemeWebInstaller.init());
|
||||
InitLater(() => LightWeightThemeStuff.init());
|
||||
InitLater(() => CastingApps.init(), window, "CastingApps");
|
||||
|
||||
// Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
|
||||
|
@ -3274,14 +3274,11 @@ ChromeUtils.defineModuleGetter(this, "PageActions",
|
|||
});
|
||||
});
|
||||
|
||||
var LightWeightThemeWebInstaller = {
|
||||
var LightWeightThemeStuff = {
|
||||
init: function sh_init() {
|
||||
let temp = {};
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeConsumer.jsm", temp);
|
||||
let theme = new temp.LightweightThemeConsumer(document);
|
||||
BrowserApp.deck.addEventListener("InstallBrowserTheme", this, false, true);
|
||||
BrowserApp.deck.addEventListener("PreviewBrowserTheme", this, false, true);
|
||||
BrowserApp.deck.addEventListener("ResetBrowserThemePreview", this, false, true);
|
||||
let {LightweightThemeConsumer} =
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeConsumer.jsm");
|
||||
new LightweightThemeConsumer(document);
|
||||
|
||||
if (ParentalControls.parentalControlsEnabled &&
|
||||
!this._manager.currentTheme &&
|
||||
|
@ -3291,38 +3288,11 @@ var LightWeightThemeWebInstaller = {
|
|||
}
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
switch (event.type) {
|
||||
case "InstallBrowserTheme":
|
||||
case "PreviewBrowserTheme":
|
||||
case "ResetBrowserThemePreview":
|
||||
// ignore requests from background tabs
|
||||
if (event.target.ownerGlobal.top != content)
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case "InstallBrowserTheme":
|
||||
this._installRequest(event);
|
||||
break;
|
||||
case "PreviewBrowserTheme":
|
||||
this._preview(event);
|
||||
break;
|
||||
case "ResetBrowserThemePreview":
|
||||
this._resetPreview(event);
|
||||
break;
|
||||
case "pagehide":
|
||||
case "TabSelect":
|
||||
this._resetPreview();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
get _manager () {
|
||||
let temp = {};
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
|
||||
let {LightweightThemeManager} =
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
delete this._manager;
|
||||
return this._manager = temp.LightweightThemeManager;
|
||||
return this._manager = LightweightThemeManager;
|
||||
},
|
||||
|
||||
_installParentalControlsTheme: function() {
|
||||
|
@ -3336,81 +3306,6 @@ var LightWeightThemeWebInstaller = {
|
|||
mgr.addBuiltInTheme(parentalControlsTheme);
|
||||
mgr.themeChanged(parentalControlsTheme);
|
||||
},
|
||||
|
||||
_installRequest: function (event) {
|
||||
let node = event.target;
|
||||
let data = this._getThemeFromNode(node);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (this._isAllowed(node)) {
|
||||
this._install(data);
|
||||
return;
|
||||
}
|
||||
|
||||
let allowButtonText = Strings.browser.GetStringFromName("lwthemeInstallRequest.allowButton");
|
||||
let IDNService = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
|
||||
let hostname = IDNService.convertToDisplayIDN(node.ownerDocument.location.hostname, {});
|
||||
let message = Strings.browser.formatStringFromName("lwthemeInstallRequest.message", [hostname], 1);
|
||||
let buttons = [{
|
||||
label: allowButtonText,
|
||||
callback: function () {
|
||||
LightWeightThemeWebInstaller._install(data);
|
||||
},
|
||||
positive: true
|
||||
}];
|
||||
|
||||
NativeWindow.doorhanger.show(message, "Personas", buttons, BrowserApp.selectedTab.id);
|
||||
},
|
||||
|
||||
_install: function (newLWTheme) {
|
||||
this._manager.currentTheme = newLWTheme;
|
||||
},
|
||||
|
||||
_previewWindow: null,
|
||||
_preview: function (event) {
|
||||
if (!this._isAllowed(event.target))
|
||||
return;
|
||||
let data = this._getThemeFromNode(event.target);
|
||||
if (!data)
|
||||
return;
|
||||
this._resetPreview();
|
||||
|
||||
this._previewWindow = event.target.ownerGlobal;
|
||||
this._previewWindow.addEventListener("pagehide", this, true);
|
||||
BrowserApp.deck.addEventListener("TabSelect", this);
|
||||
this._manager.previewTheme(data);
|
||||
},
|
||||
|
||||
_resetPreview: function (event) {
|
||||
if (!this._previewWindow ||
|
||||
event && !this._isAllowed(event.target))
|
||||
return;
|
||||
|
||||
this._previewWindow.removeEventListener("pagehide", this, true);
|
||||
this._previewWindow = null;
|
||||
BrowserApp.deck.removeEventListener("TabSelect", this);
|
||||
|
||||
this._manager.resetPreview();
|
||||
},
|
||||
|
||||
_isAllowed: function (node) {
|
||||
// Make sure the whitelist has been imported to permissions
|
||||
PermissionsUtils.importFromPrefs("xpinstall.", "install");
|
||||
|
||||
let pm = Services.perms;
|
||||
|
||||
let uri = node.ownerDocument.documentURIObject;
|
||||
if (!uri.schemeIs("https")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
|
||||
},
|
||||
|
||||
_getThemeFromNode: function (node) {
|
||||
return this._manager.parseTheme(node.getAttribute("data-browsertheme"), node.baseURI);
|
||||
}
|
||||
};
|
||||
|
||||
var DesktopUserAgent = {
|
||||
|
|
|
@ -15,9 +15,6 @@ const {Svc, Utils} = ChromeUtils.import("resource://services-sync/util.js");
|
|||
const {SCORE_INCREMENT_XLARGE} = ChromeUtils.import("resource://services-sync/constants.js");
|
||||
const {CommonUtils} = ChromeUtils.import("resource://services-common/utils.js");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PREFS_GUID",
|
||||
() => CommonUtils.encodeBase64URL(Services.appinfo.ID));
|
||||
|
||||
|
@ -127,19 +124,7 @@ PrefStore.prototype = {
|
|||
return values;
|
||||
},
|
||||
|
||||
_updateLightWeightTheme(themeID) {
|
||||
let themeObject = null;
|
||||
if (themeID) {
|
||||
themeObject = LightweightThemeManager.getUsedTheme(themeID);
|
||||
}
|
||||
LightweightThemeManager.currentTheme = themeObject;
|
||||
},
|
||||
|
||||
_setAllPrefs(values) {
|
||||
let selectedThemeIDPref = "lightweightThemes.selectedThemeID";
|
||||
let selectedThemeIDBefore = this._prefs.get(selectedThemeIDPref, null);
|
||||
let selectedThemeIDAfter = selectedThemeIDBefore;
|
||||
|
||||
// Update 'services.sync.prefs.sync.foo.pref' before 'foo.pref', otherwise
|
||||
// _isSynced returns false when 'foo.pref' doesn't exist (e.g., on a new device).
|
||||
let prefs = Object.keys(values).sort(a => -a.indexOf(PREF_SYNC_PREFS_PREFIX));
|
||||
|
@ -154,31 +139,17 @@ PrefStore.prototype = {
|
|||
continue;
|
||||
}
|
||||
|
||||
switch (pref) {
|
||||
// Some special prefs we don't want to set directly.
|
||||
case selectedThemeIDPref:
|
||||
selectedThemeIDAfter = value;
|
||||
break;
|
||||
|
||||
// default is to just set the pref
|
||||
default:
|
||||
if (value == null) {
|
||||
// Pref has gone missing. The best we can do is reset it.
|
||||
this._prefs.reset(pref);
|
||||
} else {
|
||||
try {
|
||||
this._prefs.set(pref, value);
|
||||
} catch (ex) {
|
||||
this._log.trace(`Failed to set pref: ${pref}`, ex);
|
||||
}
|
||||
}
|
||||
if (value == null) {
|
||||
// Pref has gone missing. The best we can do is reset it.
|
||||
this._prefs.reset(pref);
|
||||
} else {
|
||||
try {
|
||||
this._prefs.set(pref, value);
|
||||
} catch (ex) {
|
||||
this._log.trace(`Failed to set pref: ${pref}`, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the lightweight theme manager if the selected theme has changed.
|
||||
if (selectedThemeIDBefore != selectedThemeIDAfter) {
|
||||
this._updateLightWeightTheme(selectedThemeIDAfter);
|
||||
}
|
||||
},
|
||||
|
||||
async getAllIDs() {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
|
||||
const {PrefRec} = ChromeUtils.import("resource://services-sync/engines/prefs.js");
|
||||
const {Service} = ChromeUtils.import("resource://services-sync/service.js");
|
||||
|
@ -101,32 +100,6 @@ add_task(async function run_test() {
|
|||
Assert.equal(prefs.get("testing.unsynced.url"), "https://www.example.com/2");
|
||||
Assert.equal(Svc.Prefs.get("prefs.sync.testing.somepref"), true);
|
||||
|
||||
_("Enable persona");
|
||||
// Ensure we don't go to the network to fetch personas and end up leaking
|
||||
// stuff.
|
||||
Services.io.offline = true;
|
||||
Assert.equal(LightweightThemeManager.currentTheme.id, "default-theme@mozilla.org");
|
||||
|
||||
let persona1 = makePersona();
|
||||
let persona2 = makePersona();
|
||||
let usedThemes = JSON.stringify([persona1, persona2]);
|
||||
record.value = {
|
||||
"lightweightThemes.selectedThemeID": persona1.id,
|
||||
"lightweightThemes.usedThemes": usedThemes,
|
||||
};
|
||||
await store.update(record);
|
||||
Assert.equal(prefs.get("lightweightThemes.selectedThemeID"), persona1.id);
|
||||
Assert.ok(Utils.deepEquals(LightweightThemeManager.currentTheme,
|
||||
persona1));
|
||||
|
||||
_("Disable persona");
|
||||
record.value = {
|
||||
"lightweightThemes.selectedThemeID": null,
|
||||
"lightweightThemes.usedThemes": usedThemes,
|
||||
};
|
||||
await store.update(record);
|
||||
Assert.equal(LightweightThemeManager.currentTheme.id, "default-theme@mozilla.org");
|
||||
|
||||
_("Only the current app's preferences are applied.");
|
||||
record = new PrefRec("prefs", "some-fake-app");
|
||||
record.value = {
|
||||
|
@ -134,38 +107,6 @@ add_task(async function run_test() {
|
|||
};
|
||||
await store.update(record);
|
||||
Assert.equal(prefs.get("testing.int"), 42);
|
||||
|
||||
_("The light-weight theme preference is handled correctly.");
|
||||
let lastThemeID = undefined;
|
||||
let orig_updateLightWeightTheme = store._updateLightWeightTheme;
|
||||
store._updateLightWeightTheme = function(themeID) {
|
||||
lastThemeID = themeID;
|
||||
};
|
||||
try {
|
||||
record = new PrefRec("prefs", PREFS_GUID);
|
||||
record.value = {
|
||||
"testing.int": 42,
|
||||
};
|
||||
await store.update(record);
|
||||
Assert.ok(lastThemeID === undefined,
|
||||
"should not have tried to change the theme with an unrelated pref.");
|
||||
Services.prefs.setCharPref("lightweightThemes.selectedThemeID", "foo");
|
||||
record.value = {
|
||||
"lightweightThemes.selectedThemeID": "foo",
|
||||
};
|
||||
await store.update(record);
|
||||
Assert.ok(lastThemeID === undefined,
|
||||
"should not have tried to change the theme when the incoming pref matches current value.");
|
||||
|
||||
record.value = {
|
||||
"lightweightThemes.selectedThemeID": "bar",
|
||||
};
|
||||
await store.update(record);
|
||||
Assert.equal(lastThemeID, "bar",
|
||||
"should have tried to change the theme when the incoming pref was different.");
|
||||
} finally {
|
||||
store._updateLightWeightTheme = orig_updateLightWeightTheme;
|
||||
}
|
||||
} finally {
|
||||
prefs.resetBranch("");
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
|
||||
|
||||
|
@ -56,7 +54,7 @@ add_task(async function test_management_themes() {
|
|||
let addons = await browser.management.getAll();
|
||||
let themes = addons.filter(addon => addon.type === "theme");
|
||||
// We get the 3 built-in themes plus the lwt and our addon.
|
||||
browser.test.assertEq(5, themes.length, "got expected addons");
|
||||
browser.test.assertEq(4, themes.length, "got expected addons");
|
||||
// We should also get our test extension.
|
||||
let testExtension = addons.find(addon => { return addon.id === TEST_ID; });
|
||||
browser.test.assertTrue(!!testExtension,
|
||||
|
@ -109,27 +107,8 @@ add_task(async function test_management_themes() {
|
|||
});
|
||||
await extension.startup();
|
||||
|
||||
// Test LWT
|
||||
LightweightThemeManager.currentTheme = {
|
||||
id: "lwt@personas.mozilla.org",
|
||||
version: "1",
|
||||
name: "Bling",
|
||||
description: "SO MUCH BLING!",
|
||||
author: "Pixel Pusher",
|
||||
homepageURL: "http://mochi.test:8888/data/index.html",
|
||||
headerURL: "http://mochi.test:8888/data/header.png",
|
||||
previewURL: "http://mochi.test:8888/data/preview.png",
|
||||
iconURL: "http://mochi.test:8888/data/icon.png",
|
||||
textcolor: Math.random().toString(),
|
||||
accentcolor: Math.random().toString(),
|
||||
};
|
||||
is(await extension.awaitMessage("onInstalled"), "Bling", "LWT installed");
|
||||
is(await extension.awaitMessage("onEnabled"), "Bling", "LWT enabled");
|
||||
|
||||
await theme.startup();
|
||||
is(await extension.awaitMessage("onInstalled"), "Simple theme test", "webextension theme installed");
|
||||
is(await extension.awaitMessage("onDisabled"), "Bling", "LWT disabled");
|
||||
// no enabled event when installed.
|
||||
|
||||
extension.sendMessage("test");
|
||||
is(await extension.awaitMessage("onEnabled"), "Default", "default enabled");
|
||||
|
|
|
@ -71,12 +71,9 @@ add_task(async function test_autocomplete_footer_onclick() {
|
|||
}, "Waiting for footer to become visible");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(footer, {});
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return Services.wm.getMostRecentWindow("Toolkit:PasswordManager") !== null;
|
||||
}, "Waiting for the password manager dialog to open");
|
||||
let window = await waitForPasswordManagerDialog();
|
||||
info("Login dialog was opened");
|
||||
|
||||
let window = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return window.document.getElementById("filter").value == "example.com";
|
||||
}, "Waiting for the search string to filter logins");
|
||||
|
@ -109,12 +106,9 @@ add_task(async function test_autocomplete_footer_keydown() {
|
|||
await EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
await EventUtils.synthesizeKey("KEY_Enter");
|
||||
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return Services.wm.getMostRecentWindow("Toolkit:PasswordManager") !== null;
|
||||
}, "Waiting for the password manager dialog to open");
|
||||
let window = await waitForPasswordManagerDialog();
|
||||
info("Login dialog was opened");
|
||||
|
||||
let window = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return window.document.getElementById("filter").value == "example.com";
|
||||
}, "Waiting for the search string to filter logins");
|
||||
|
|
|
@ -8,10 +8,7 @@ registerCleanupFunction(resetPrefs);
|
|||
|
||||
add_task(async function test_noFilter() {
|
||||
LoginHelper.openPasswordManager(window);
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return Services.wm.getMostRecentWindow("Toolkit:PasswordManager") !== null;
|
||||
}, "Waiting for the password manager dialog to open");
|
||||
let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
||||
let win = await waitForPasswordManagerDialog();
|
||||
ok(win, "Login dialog was opened");
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
await TestUtils.waitForCondition(() => {
|
||||
|
@ -23,10 +20,7 @@ add_task(async function test_filter() {
|
|||
// Greek IDN for example.test
|
||||
let domain = "παράδειγμα.δοκιμή";
|
||||
LoginHelper.openPasswordManager(window, domain);
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return Services.wm.getMostRecentWindow("Toolkit:PasswordManager") !== null;
|
||||
}, "Waiting for the password manager dialog to open");
|
||||
let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
||||
let win = await waitForPasswordManagerDialog();
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return win.document.getElementById("filter").value == domain;
|
||||
}, "Waiting for the search string to filter logins");
|
||||
|
|
|
@ -154,3 +154,13 @@ async function checkDoorhangerUsernamePassword(username, password) {
|
|||
}
|
||||
|
||||
// End popup notification (doorhanger) functions //
|
||||
|
||||
async function waitForPasswordManagerDialog() {
|
||||
let win;
|
||||
await TestUtils.waitForCondition(() => {
|
||||
win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
||||
return win && win.document.getElementById("filter");
|
||||
}, "Waiting for the password manager dialog to open");
|
||||
|
||||
return win;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,6 @@ ChromeUtils.defineModuleGetter(this, "AttributionCode",
|
|||
"resource:///modules/AttributionCode.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ctypes",
|
||||
"resource://gre/modules/ctypes.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ProfileAge",
|
||||
"resource://gre/modules/ProfileAge.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
|
||||
|
@ -699,18 +697,12 @@ EnvironmentAddonBuilder.prototype = {
|
|||
*/
|
||||
async _updateAddons(atStartup) {
|
||||
this._environment._log.trace("_updateAddons");
|
||||
let personaId = null;
|
||||
let theme = LightweightThemeManager.currentTheme;
|
||||
if (theme) {
|
||||
personaId = theme.id;
|
||||
}
|
||||
|
||||
let addons = {
|
||||
activeAddons: await this._getActiveAddons(),
|
||||
theme: await this._getActiveTheme(),
|
||||
activePlugins: this._getActivePlugins(atStartup),
|
||||
activeGMPlugins: await this._getActiveGMPlugins(atStartup),
|
||||
persona: personaId,
|
||||
};
|
||||
|
||||
let result = {
|
||||
|
|
|
@ -275,7 +275,6 @@ Structure:
|
|||
},
|
||||
...
|
||||
},
|
||||
persona: <string>, // id of the current persona
|
||||
},
|
||||
experiments: {
|
||||
"<experiment id>": { branch: "<branch>" },
|
||||
|
@ -465,6 +464,10 @@ For each experiment we collect the ``id`` and the ``branch`` the client is enrol
|
|||
Version History
|
||||
---------------
|
||||
|
||||
- Firefox 67:
|
||||
|
||||
- Removed ``persona``. The ``addons.activeAddons`` list should be used instead. (`bug 1525511 https://bugzilla.mozilla.org/show_bug.cgi?id=1525511>`_)
|
||||
|
||||
- Firefox 61:
|
||||
|
||||
- Removed empty ``addons.activeExperiment`` (`bug 1452935 <https://bugzilla.mozilla.org/show_bug.cgi?id=1452935>`_).
|
||||
|
|
|
@ -17,10 +17,6 @@ const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
|||
ChromeUtils.defineModuleGetter(this, "AttributionCode",
|
||||
"resource:///modules/AttributionCode.jsm");
|
||||
|
||||
// Lazy load |LightweightThemeManager|.
|
||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionTestUtils",
|
||||
"resource://testing-common/ExtensionXPCShellUtils.jsm");
|
||||
|
||||
|
@ -72,12 +68,6 @@ const PLUGIN2_NAME = "Quicktime";
|
|||
const PLUGIN2_DESC = "A mock Quicktime plugin";
|
||||
const PLUGIN2_VERSION = "2.3";
|
||||
|
||||
const PERSONA_ID = "3785";
|
||||
// Defined by LightweightThemeManager, it is appended to the PERSONA_ID.
|
||||
const PERSONA_ID_SUFFIX = "@personas.mozilla.org";
|
||||
const PERSONA_NAME = "Test Theme";
|
||||
const PERSONA_DESCRIPTION = "A nice theme/persona description.";
|
||||
|
||||
const PLUGIN_UPDATED_TOPIC = "plugins-list-updated";
|
||||
|
||||
// system add-ons are enabled at startup, so record date when the test starts
|
||||
|
@ -274,20 +264,6 @@ function createMockAddonProvider(aName) {
|
|||
return mockProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to spoof the Persona Id.
|
||||
*/
|
||||
function spoofTheme(aId, aName, aDesc) {
|
||||
return {
|
||||
id: aId,
|
||||
name: aName,
|
||||
description: aDesc,
|
||||
headerURL: "http://lwttest.invalid/a.png",
|
||||
textcolor: Math.random().toString(),
|
||||
accentcolor: Math.random().toString(),
|
||||
};
|
||||
}
|
||||
|
||||
function spoofGfxAdapter() {
|
||||
try {
|
||||
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
|
||||
|
@ -816,7 +792,6 @@ function checkActiveGMPlugin(data) {
|
|||
function checkAddonsSection(data, expectBrokenAddons, partialAddonsRecords) {
|
||||
const EXPECTED_FIELDS = [
|
||||
"activeAddons", "theme", "activePlugins", "activeGMPlugins",
|
||||
"persona",
|
||||
];
|
||||
|
||||
Assert.ok("addons" in data, "There must be an addons section in Environment.");
|
||||
|
@ -848,9 +823,6 @@ function checkAddonsSection(data, expectBrokenAddons, partialAddonsRecords) {
|
|||
for (let gmPlugin in activeGMPlugins) {
|
||||
checkActiveGMPlugin(activeGMPlugins[gmPlugin]);
|
||||
}
|
||||
|
||||
// Check persona
|
||||
Assert.ok(checkNullOrString(data.addons.persona));
|
||||
}
|
||||
|
||||
function checkExperimentsSection(data) {
|
||||
|
@ -902,10 +874,6 @@ add_task(async function setup() {
|
|||
system_addon.lastModifiedTime = SYSTEM_ADDON_INSTALL_DATE;
|
||||
loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
|
||||
|
||||
// Spoof the persona ID.
|
||||
LightweightThemeManager.currentTheme =
|
||||
spoofTheme(PERSONA_ID, PERSONA_NAME, PERSONA_DESCRIPTION);
|
||||
|
||||
// The test runs in a fresh profile so starting the AddonManager causes
|
||||
// the addons database to be created (as does setting new theme).
|
||||
// For test_addonsStartup below, we want to test a "warm" startup where
|
||||
|
@ -1376,12 +1344,6 @@ add_task(async function test_addonsAndPlugins() {
|
|||
|
||||
await webextension.unload();
|
||||
|
||||
// Check theme data.
|
||||
let theme = data.addons.theme;
|
||||
Assert.equal(theme.id, (PERSONA_ID + PERSONA_ID_SUFFIX));
|
||||
Assert.equal(theme.name, PERSONA_NAME);
|
||||
Assert.equal(theme.description, PERSONA_DESCRIPTION);
|
||||
|
||||
// Check plugin data.
|
||||
Assert.equal(data.addons.activePlugins.length, 1, "We must have only one active plugin.");
|
||||
let targetPlugin = data.addons.activePlugins[0];
|
||||
|
@ -1394,8 +1356,6 @@ add_task(async function test_addonsAndPlugins() {
|
|||
Assert.ok(targetPlugin.mimeTypes.find(m => m == PLUGIN_MIME_TYPE2));
|
||||
Assert.ok(!targetPlugin.mimeTypes.find(m => m == "Not There."));
|
||||
|
||||
Assert.equal(data.addons.persona, PERSONA_ID, "The correct Persona Id must be reported.");
|
||||
|
||||
// Uninstall the addon.
|
||||
await addon.startupPromise;
|
||||
await addon.uninstall();
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
const {CommonUtils} = ChromeUtils.import("resource://services-common/utils.js");
|
||||
const {ClientID} = ChromeUtils.import("resource://gre/modules/ClientID.jsm");
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||
ChromeUtils.import("resource://gre/modules/TelemetrySession.jsm", this);
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
// Simulates quickly switching between different list views to verify that only
|
||||
// the last selected is displayed
|
||||
|
||||
var tempScope = {};
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope);
|
||||
var LightweightThemeManager = tempScope.LightweightThemeManager;
|
||||
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||
|
||||
PromiseTestUtils.whitelistRejectionsGlobally(/this\._errorLink/);
|
||||
|
||||
var gManagerWindow;
|
||||
var gCategoryUtilities;
|
||||
|
@ -15,13 +15,6 @@ var gCategoryUtilities;
|
|||
async function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Add a lightweight theme so at least one theme exists
|
||||
LightweightThemeManager.currentTheme = {
|
||||
id: "test",
|
||||
name: "Test lightweight theme",
|
||||
headerURL: "http://example.com/header.png",
|
||||
};
|
||||
|
||||
let aWindow = await open_manager(null);
|
||||
gManagerWindow = aWindow;
|
||||
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
|
||||
|
@ -30,7 +23,6 @@ async function test() {
|
|||
|
||||
async function end_test() {
|
||||
await close_manager(gManagerWindow);
|
||||
LightweightThemeManager.forgetUsedTheme("test");
|
||||
finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,25 +5,9 @@
|
|||
// Bug 591465 - Context menu of add-ons miss context related state change entries
|
||||
|
||||
|
||||
var tempScope = {};
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope);
|
||||
var LightweightThemeManager = tempScope.LightweightThemeManager;
|
||||
|
||||
|
||||
var gManagerWindow;
|
||||
var gProvider;
|
||||
var gContextMenu;
|
||||
var gLWTheme = {
|
||||
id: "4",
|
||||
version: "1",
|
||||
name: "Bling",
|
||||
description: "SO MUCH BLING!",
|
||||
author: "Pixel Pusher",
|
||||
homepageURL: "http://mochi.test:8888/data/index.html",
|
||||
headerURL: "http://mochi.test:8888/data/header.png",
|
||||
previewURL: "http://mochi.test:8888/data/preview.png",
|
||||
iconURL: "http://mochi.test:8888/data/icon.png",
|
||||
};
|
||||
|
||||
|
||||
add_task(async function setup() {
|
||||
|
@ -222,85 +206,6 @@ add_test(function() {
|
|||
});
|
||||
|
||||
|
||||
add_test(function() {
|
||||
LightweightThemeManager.currentTheme = gLWTheme;
|
||||
|
||||
var el = get_addon_element(gManagerWindow, "4@personas.mozilla.org");
|
||||
|
||||
gContextMenu.addEventListener("popupshown", function() {
|
||||
check_contextmenu(true, true, false, false, false);
|
||||
|
||||
gContextMenu.hidePopup();
|
||||
run_next_test();
|
||||
}, {once: true});
|
||||
|
||||
info("Opening context menu on enabled LW theme item");
|
||||
el.parentNode.ensureElementIsVisible(el);
|
||||
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
|
||||
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
|
||||
});
|
||||
|
||||
|
||||
add_test(function() {
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
|
||||
var el = get_addon_element(gManagerWindow, "4@personas.mozilla.org");
|
||||
|
||||
gContextMenu.addEventListener("popupshown", function() {
|
||||
check_contextmenu(true, false, false, false, false);
|
||||
|
||||
gContextMenu.hidePopup();
|
||||
run_next_test();
|
||||
}, {once: true});
|
||||
|
||||
info("Opening context menu on disabled LW theme item");
|
||||
el.parentNode.ensureElementIsVisible(el);
|
||||
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
|
||||
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
|
||||
});
|
||||
|
||||
|
||||
add_test(async function() {
|
||||
LightweightThemeManager.currentTheme = gLWTheme;
|
||||
|
||||
gManagerWindow.loadView("addons://detail/4@personas.mozilla.org");
|
||||
await wait_for_view_load(gManagerWindow);
|
||||
gContextMenu.addEventListener("popupshown", function() {
|
||||
check_contextmenu(true, true, false, true, false);
|
||||
|
||||
gContextMenu.hidePopup();
|
||||
run_next_test();
|
||||
}, {once: true});
|
||||
|
||||
info("Opening context menu on enabled LW theme, in detail view");
|
||||
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
|
||||
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
|
||||
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
|
||||
});
|
||||
|
||||
|
||||
add_test(async function() {
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
|
||||
gManagerWindow.loadView("addons://detail/4@personas.mozilla.org");
|
||||
await wait_for_view_load(gManagerWindow);
|
||||
gContextMenu.addEventListener("popupshown", async function() {
|
||||
check_contextmenu(true, false, false, true, false);
|
||||
|
||||
gContextMenu.hidePopup();
|
||||
|
||||
let aAddon = await AddonManager.getAddonByID("4@personas.mozilla.org");
|
||||
aAddon.uninstall();
|
||||
run_next_test();
|
||||
}, {once: true});
|
||||
|
||||
info("Opening context menu on disabled LW theme, in detail view");
|
||||
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
|
||||
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
|
||||
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
|
||||
});
|
||||
|
||||
|
||||
add_test(async function() {
|
||||
gManagerWindow.loadView("addons://detail/addon1@tests.mozilla.org");
|
||||
await wait_for_view_load(gManagerWindow);
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
|
||||
// Tests the list view
|
||||
|
||||
var tempScope = {};
|
||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope);
|
||||
var LightweightThemeManager = tempScope.LightweightThemeManager;
|
||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||
|
||||
PromiseTestUtils.whitelistRejectionsGlobally(/this\._errorLink/);
|
||||
|
||||
var gProvider;
|
||||
var gManagerWindow;
|
||||
|
@ -20,18 +21,6 @@ var infoURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsi
|
|||
|
||||
const EXPECTED_ADDONS = 11;
|
||||
|
||||
var gLWTheme = {
|
||||
id: "4",
|
||||
version: "1",
|
||||
name: "Bling",
|
||||
description: "SO MUCH BLING!",
|
||||
author: "Pixel Pusher",
|
||||
homepageURL: "http://mochi.test:8888/data/index.html",
|
||||
headerURL: "http://mochi.test:8888/data/header.png",
|
||||
previewURL: "http://mochi.test:8888/data/preview.png",
|
||||
iconURL: "http://mochi.test:8888/data/icon.png",
|
||||
};
|
||||
|
||||
add_task(async function() {
|
||||
gProvider = new MockProvider();
|
||||
|
||||
|
@ -452,38 +441,6 @@ add_task(async function() {
|
|||
});
|
||||
|
||||
|
||||
function tick() {
|
||||
return new Promise(SimpleTest.executeSoon);
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
info("Enabling lightweight theme");
|
||||
LightweightThemeManager.currentTheme = gLWTheme;
|
||||
await tick();
|
||||
|
||||
gManagerWindow.loadView("addons://list/theme");
|
||||
await new Promise(resolve => wait_for_view_load(gManagerWindow, resolve));
|
||||
|
||||
var addon = get_addon_element(gManagerWindow, "4@personas.mozilla.org");
|
||||
|
||||
is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
|
||||
is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
|
||||
is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
|
||||
is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
|
||||
|
||||
info("Disabling lightweight theme");
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
await tick();
|
||||
|
||||
is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
|
||||
is_element_visible(get_node(addon, "enable-btn"), "Enable button should be hidden");
|
||||
is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be visible");
|
||||
is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
|
||||
|
||||
let [aAddon] = await promiseAddonsByIDs(["4@personas.mozilla.org"]);
|
||||
aAddon.uninstall();
|
||||
});
|
||||
|
||||
// Check that onPropertyChanges for appDisabled updates the UI
|
||||
add_task(async function() {
|
||||
info("Checking that onPropertyChanges for appDisabled updates the UI");
|
||||
|
|
|
@ -1,615 +0,0 @@
|
|||
const MANDATORY = ["id", "name"];
|
||||
const OPTIONAL = ["headerURL", "textcolor", "accentcolor",
|
||||
"iconURL", "previewURL", "author", "description",
|
||||
"homepageURL", "updateURL", "version"];
|
||||
|
||||
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
|
||||
|
||||
var LightweightThemeManager;
|
||||
|
||||
function dummy(id) {
|
||||
return {
|
||||
id: id || Math.random().toString(),
|
||||
name: Math.random().toString(),
|
||||
headerURL: "http://lwttest.invalid/a.png",
|
||||
textcolor: Math.random().toString(),
|
||||
accentcolor: Math.random().toString(),
|
||||
};
|
||||
}
|
||||
|
||||
function tick() {
|
||||
return new Promise(executeSoon);
|
||||
}
|
||||
|
||||
function setTheme(theme) {
|
||||
LightweightThemeManager.currentTheme = theme;
|
||||
return tick();
|
||||
}
|
||||
|
||||
function hasPermission(aAddon, aPerm) {
|
||||
var perm = AddonManager["PERM_CAN_" + aPerm.toUpperCase()];
|
||||
return !!(aAddon.permissions & perm);
|
||||
}
|
||||
|
||||
add_task(async function run_test() {
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
|
||||
await promiseStartupManager();
|
||||
|
||||
Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 8);
|
||||
|
||||
({LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm"));
|
||||
let ltm = LightweightThemeManager;
|
||||
|
||||
Assert.equal(typeof ltm, "object");
|
||||
Assert.equal(typeof ltm.usedThemes, "object");
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
ltm.previewTheme(dummy("preview0"));
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
ltm.previewTheme(dummy("preview1"));
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
ltm.resetPreview();
|
||||
|
||||
await setTheme(dummy("x0"));
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.id, "x0");
|
||||
Assert.equal(ltm.usedThemes[0].id, "x0");
|
||||
Assert.equal(ltm.getUsedTheme("x0").id, "x0");
|
||||
|
||||
ltm.previewTheme(dummy("preview0"));
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.id, "x0");
|
||||
|
||||
ltm.resetPreview();
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.id, "x0");
|
||||
|
||||
await setTheme(dummy("x1"));
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
Assert.equal(ltm.currentTheme.id, "x1");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x0");
|
||||
|
||||
await setTheme(dummy("x2"));
|
||||
Assert.equal(ltm.usedThemes.length, 4);
|
||||
Assert.equal(ltm.currentTheme.id, "x2");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x1");
|
||||
Assert.equal(ltm.usedThemes[2].id, "x0");
|
||||
|
||||
ltm.currentTheme = dummy("x3");
|
||||
ltm.currentTheme = dummy("x4");
|
||||
ltm.currentTheme = dummy("x5");
|
||||
ltm.currentTheme = dummy("x6");
|
||||
ltm.currentTheme = dummy("x7");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 9);
|
||||
Assert.equal(ltm.currentTheme.id, "x7");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x6");
|
||||
Assert.equal(ltm.usedThemes[7].id, "x0");
|
||||
|
||||
await setTheme(dummy("x8"));
|
||||
Assert.equal(ltm.usedThemes.length, 9);
|
||||
Assert.equal(ltm.currentTheme.id, "x8");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x7");
|
||||
Assert.equal(ltm.usedThemes[7].id, "x1");
|
||||
Assert.equal(ltm.getUsedTheme("x0"), null);
|
||||
|
||||
ltm.forgetUsedTheme("nonexistent");
|
||||
Assert.equal(ltm.usedThemes.length, 9);
|
||||
Assert.notEqual(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
ltm.forgetUsedTheme("x8");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 8);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
Assert.equal(ltm.usedThemes[0].id, "x7");
|
||||
Assert.equal(ltm.usedThemes[6].id, "x1");
|
||||
|
||||
ltm.forgetUsedTheme("x7");
|
||||
ltm.forgetUsedTheme("x6");
|
||||
ltm.forgetUsedTheme("x5");
|
||||
ltm.forgetUsedTheme("x4");
|
||||
ltm.forgetUsedTheme("x3");
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
Assert.equal(ltm.usedThemes[0].id, "x2");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x1");
|
||||
|
||||
await setTheme(dummy("x1"));
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
Assert.equal(ltm.currentTheme.id, "x1");
|
||||
Assert.equal(ltm.usedThemes[0].id, "x1");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x2");
|
||||
|
||||
await setTheme(dummy("x2"));
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
Assert.equal(ltm.currentTheme.id, "x2");
|
||||
Assert.equal(ltm.usedThemes[0].id, "x2");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x1");
|
||||
|
||||
await setTheme(ltm.getUsedTheme("x1"));
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
Assert.equal(ltm.currentTheme.id, "x1");
|
||||
Assert.equal(ltm.usedThemes[0].id, "x1");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x2");
|
||||
|
||||
ltm.forgetUsedTheme("x1");
|
||||
ltm.forgetUsedTheme("x2");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
// Use chinese name to test utf-8, for bug #541943
|
||||
var chineseTheme = dummy("chinese0");
|
||||
chineseTheme.name = "笢恅0";
|
||||
chineseTheme.description = "笢恅1";
|
||||
await setTheme(chineseTheme);
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.name, "笢恅0");
|
||||
Assert.equal(ltm.currentTheme.description, "笢恅1");
|
||||
Assert.equal(ltm.usedThemes[0].name, "笢恅0");
|
||||
Assert.equal(ltm.usedThemes[0].description, "笢恅1");
|
||||
Assert.equal(ltm.getUsedTheme("chinese0").name, "笢恅0");
|
||||
Assert.equal(ltm.getUsedTheme("chinese0").description, "笢恅1");
|
||||
|
||||
// This name used to break the usedTheme JSON causing all LWTs to be lost
|
||||
var chineseTheme1 = dummy("chinese1");
|
||||
chineseTheme1.name = "眵昜湮桵蔗坌~郔乾";
|
||||
chineseTheme1.description = "眵昜湮桵蔗坌~郔乾";
|
||||
await setTheme(chineseTheme1);
|
||||
Assert.notEqual(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
Assert.equal(ltm.currentTheme.name, "眵昜湮桵蔗坌~郔乾");
|
||||
Assert.equal(ltm.currentTheme.description, "眵昜湮桵蔗坌~郔乾");
|
||||
Assert.equal(ltm.usedThemes[1].name, "笢恅0");
|
||||
Assert.equal(ltm.usedThemes[1].description, "笢恅1");
|
||||
Assert.equal(ltm.usedThemes[0].name, "眵昜湮桵蔗坌~郔乾");
|
||||
Assert.equal(ltm.usedThemes[0].description, "眵昜湮桵蔗坌~郔乾");
|
||||
|
||||
ltm.forgetUsedTheme("chinese0");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.notEqual(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
ltm.forgetUsedTheme("chinese1");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
Assert.equal(ltm.parseTheme("invalid json"), null);
|
||||
Assert.equal(ltm.parseTheme('"json string"'), null);
|
||||
|
||||
function roundtrip(data, secure) {
|
||||
return ltm.parseTheme(JSON.stringify(data),
|
||||
"http" + (secure ? "s" : "") + "://lwttest.invalid/");
|
||||
}
|
||||
|
||||
var data = dummy();
|
||||
Assert.notEqual(roundtrip(data), null);
|
||||
data.id = null;
|
||||
Assert.equal(roundtrip(data), null);
|
||||
data.id = 1;
|
||||
Assert.equal(roundtrip(data), null);
|
||||
data.id = 1.5;
|
||||
Assert.equal(roundtrip(data), null);
|
||||
data.id = true;
|
||||
Assert.equal(roundtrip(data), null);
|
||||
data.id = {};
|
||||
Assert.equal(roundtrip(data), null);
|
||||
data.id = [];
|
||||
Assert.equal(roundtrip(data), null);
|
||||
|
||||
// Check whether parseTheme handles international characters right
|
||||
var chineseTheme2 = dummy();
|
||||
chineseTheme2.name = "眵昜湮桵蔗坌~郔乾";
|
||||
chineseTheme2.description = "眵昜湮桵蔗坌~郔乾";
|
||||
Assert.notEqual(roundtrip(chineseTheme2), null);
|
||||
Assert.equal(roundtrip(chineseTheme2).name, "眵昜湮桵蔗坌~郔乾");
|
||||
Assert.equal(roundtrip(chineseTheme2).description, "眵昜湮桵蔗坌~郔乾");
|
||||
|
||||
data = dummy();
|
||||
data.unknownProperty = "Foo";
|
||||
Assert.equal(typeof roundtrip(data).unknownProperty, "undefined");
|
||||
|
||||
data = dummy();
|
||||
data.unknownURL = "http://lwttest.invalid/";
|
||||
Assert.equal(typeof roundtrip(data).unknownURL, "undefined");
|
||||
|
||||
function roundtripSet(props, modify, test, secure) {
|
||||
props.forEach(function(prop) {
|
||||
var theme = dummy();
|
||||
modify(theme, prop);
|
||||
test(roundtrip(theme, secure), prop, theme);
|
||||
});
|
||||
}
|
||||
|
||||
roundtripSet(MANDATORY, function(theme, prop) {
|
||||
delete theme[prop];
|
||||
}, function(after) {
|
||||
Assert.equal(after, null);
|
||||
});
|
||||
|
||||
roundtripSet(OPTIONAL, function(theme, prop) {
|
||||
delete theme[prop];
|
||||
}, function(after) {
|
||||
Assert.notEqual(after, null);
|
||||
});
|
||||
|
||||
roundtripSet(MANDATORY, function(theme, prop) {
|
||||
theme[prop] = "";
|
||||
}, function(after) {
|
||||
Assert.equal(after, null);
|
||||
});
|
||||
|
||||
roundtripSet(OPTIONAL, function(theme, prop) {
|
||||
theme[prop] = "";
|
||||
}, function(after, prop) {
|
||||
Assert.equal(typeof after[prop], "undefined");
|
||||
});
|
||||
|
||||
roundtripSet(MANDATORY, function(theme, prop) {
|
||||
theme[prop] = " ";
|
||||
}, function(after) {
|
||||
Assert.equal(after, null);
|
||||
});
|
||||
|
||||
roundtripSet(OPTIONAL, function(theme, prop) {
|
||||
theme[prop] = " ";
|
||||
}, function(after, prop) {
|
||||
Assert.notEqual(after, null);
|
||||
Assert.equal(typeof after[prop], "undefined");
|
||||
});
|
||||
|
||||
function non_urls(props) {
|
||||
return props.filter(prop => !/URL$/.test(prop));
|
||||
}
|
||||
|
||||
function urls(props) {
|
||||
return props.filter(prop => /URL$/.test(prop));
|
||||
}
|
||||
|
||||
roundtripSet(non_urls(MANDATORY.concat(OPTIONAL)), function(theme, prop) {
|
||||
theme[prop] = prop;
|
||||
}, function(after, prop, before) {
|
||||
Assert.equal(after[prop], before[prop]);
|
||||
});
|
||||
|
||||
roundtripSet(non_urls(MANDATORY.concat(OPTIONAL)), function(theme, prop) {
|
||||
theme[prop] = " " + prop + " ";
|
||||
}, function(after, prop, before) {
|
||||
Assert.equal(after[prop], before[prop].trim());
|
||||
});
|
||||
|
||||
roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function(theme, prop) {
|
||||
theme[prop] = Math.random().toString();
|
||||
}, function(after, prop, before) {
|
||||
if (prop == "updateURL")
|
||||
Assert.equal(typeof after[prop], "undefined");
|
||||
else
|
||||
Assert.equal(after[prop], "http://lwttest.invalid/" + before[prop]);
|
||||
});
|
||||
|
||||
roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function(theme, prop) {
|
||||
theme[prop] = Math.random().toString();
|
||||
}, function(after, prop, before) {
|
||||
Assert.equal(after[prop], "https://lwttest.invalid/" + before[prop]);
|
||||
}, true);
|
||||
|
||||
roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function(theme, prop) {
|
||||
theme[prop] = "https://sub.lwttest.invalid/" + Math.random().toString();
|
||||
}, function(after, prop, before) {
|
||||
Assert.equal(after[prop], before[prop]);
|
||||
});
|
||||
|
||||
roundtripSet(urls(MANDATORY), function(theme, prop) {
|
||||
theme[prop] = "ftp://lwttest.invalid/" + Math.random().toString();
|
||||
}, function(after) {
|
||||
Assert.equal(after, null);
|
||||
});
|
||||
|
||||
roundtripSet(urls(OPTIONAL), function(theme, prop) {
|
||||
theme[prop] = "ftp://lwttest.invalid/" + Math.random().toString();
|
||||
}, function(after, prop) {
|
||||
Assert.equal(typeof after[prop], "undefined");
|
||||
});
|
||||
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
data = dummy();
|
||||
delete data.name;
|
||||
try {
|
||||
ltm.currentTheme = data;
|
||||
do_throw("Should have rejected a theme with no name");
|
||||
} catch (e) {
|
||||
// Expected exception
|
||||
}
|
||||
|
||||
// Sanitize themes with a bad headerURL
|
||||
data = dummy();
|
||||
data.headerURL = "foo";
|
||||
await setTheme(data);
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.headerURL, undefined);
|
||||
ltm.forgetUsedTheme(ltm.currentTheme.id);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
|
||||
// Sanitize themes with a non-http(s) headerURL
|
||||
data = dummy();
|
||||
data.headerURL = "ftp://lwtest.invalid/test.png";
|
||||
await setTheme(data);
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.headerURL, undefined);
|
||||
ltm.forgetUsedTheme(ltm.currentTheme.id);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
|
||||
// Sanitize themes with a non-http(s) headerURL
|
||||
data = dummy();
|
||||
data.headerURL = "file:///test.png";
|
||||
await setTheme(data);
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.headerURL, undefined);
|
||||
ltm.forgetUsedTheme(ltm.currentTheme.id);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
|
||||
data = dummy();
|
||||
data.updateURL = "file:///test.json";
|
||||
ltm.setLocalTheme(data);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.updateURL, undefined);
|
||||
ltm.forgetUsedTheme(ltm.currentTheme.id);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
|
||||
data = dummy();
|
||||
data.headerURL = "file:///test.png";
|
||||
ltm.setLocalTheme(data);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.headerURL, "file:///test.png");
|
||||
ltm.forgetUsedTheme(ltm.currentTheme.id);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
|
||||
data = dummy();
|
||||
data.headerURL = "ftp://lwtest.invalid/test.png";
|
||||
ltm.setLocalTheme(data);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.currentTheme.updateURL, undefined);
|
||||
ltm.forgetUsedTheme(ltm.currentTheme.id);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
|
||||
data = dummy();
|
||||
delete data.id;
|
||||
try {
|
||||
ltm.currentTheme = data;
|
||||
do_throw("Should have rejected a theme with no ID");
|
||||
} catch (e) {
|
||||
// Expected exception
|
||||
}
|
||||
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
// Force the theme into the prefs anyway
|
||||
let themes = [data];
|
||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
|
||||
// This should silently drop the bad theme.
|
||||
await setTheme(dummy());
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
ltm.forgetUsedTheme(ltm.currentTheme.id);
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
// Add one broken and some working.
|
||||
themes = [data, dummy("x1"), dummy("x2")];
|
||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
|
||||
Assert.equal(ltm.usedThemes.length, 4);
|
||||
|
||||
// Switching to an existing theme should drop the bad theme.
|
||||
await setTheme(ltm.getUsedTheme("x1"));
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
ltm.forgetUsedTheme("x1");
|
||||
ltm.forgetUsedTheme("x2");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
|
||||
Assert.equal(ltm.usedThemes.length, 4);
|
||||
|
||||
// Forgetting an existing theme should drop the bad theme.
|
||||
ltm.forgetUsedTheme("x1");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
ltm.forgetUsedTheme("x2");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
// Test whether a JSON set with setCharPref can be retrieved with usedThemes
|
||||
ltm.currentTheme = dummy("x0");
|
||||
ltm.currentTheme = dummy("x1");
|
||||
await tick();
|
||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(ltm.usedThemes));
|
||||
Assert.equal(ltm.usedThemes.length, 4);
|
||||
Assert.equal(ltm.currentTheme.id, "x1");
|
||||
Assert.equal(ltm.usedThemes[1].id, "x0");
|
||||
Assert.equal(ltm.usedThemes[0].id, "x1");
|
||||
|
||||
ltm.forgetUsedTheme("x0");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.notEqual(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
ltm.forgetUsedTheme("x1");
|
||||
await tick();
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
Assert.equal(ltm.currentTheme.id, DEFAULT_THEME_ID);
|
||||
|
||||
Services.prefs.clearUserPref("lightweightThemes.maxUsedThemes");
|
||||
|
||||
ltm.currentTheme = dummy("x1");
|
||||
ltm.currentTheme = dummy("x2");
|
||||
ltm.currentTheme = dummy("x3");
|
||||
ltm.currentTheme = dummy("x4");
|
||||
ltm.currentTheme = dummy("x5");
|
||||
ltm.currentTheme = dummy("x6");
|
||||
ltm.currentTheme = dummy("x7");
|
||||
ltm.currentTheme = dummy("x8");
|
||||
ltm.currentTheme = dummy("x9");
|
||||
ltm.currentTheme = dummy("x10");
|
||||
ltm.currentTheme = dummy("x11");
|
||||
ltm.currentTheme = dummy("x12");
|
||||
ltm.currentTheme = dummy("x13");
|
||||
ltm.currentTheme = dummy("x14");
|
||||
ltm.currentTheme = dummy("x15");
|
||||
ltm.currentTheme = dummy("x16");
|
||||
ltm.currentTheme = dummy("x17");
|
||||
ltm.currentTheme = dummy("x18");
|
||||
ltm.currentTheme = dummy("x19");
|
||||
ltm.currentTheme = dummy("x20");
|
||||
ltm.currentTheme = dummy("x21");
|
||||
ltm.currentTheme = dummy("x22");
|
||||
ltm.currentTheme = dummy("x23");
|
||||
ltm.currentTheme = dummy("x24");
|
||||
ltm.currentTheme = dummy("x25");
|
||||
ltm.currentTheme = dummy("x26");
|
||||
ltm.currentTheme = dummy("x27");
|
||||
ltm.currentTheme = dummy("x28");
|
||||
ltm.currentTheme = dummy("x29");
|
||||
ltm.currentTheme = dummy("x30");
|
||||
await tick();
|
||||
|
||||
Assert.equal(ltm.usedThemes.length, 31);
|
||||
|
||||
ltm.currentTheme = dummy("x31");
|
||||
await tick();
|
||||
|
||||
Assert.equal(ltm.usedThemes.length, 31);
|
||||
Assert.equal(ltm.getUsedTheme("x1"), null);
|
||||
|
||||
Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 15);
|
||||
|
||||
Assert.equal(ltm.usedThemes.length, 16);
|
||||
|
||||
Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 32);
|
||||
|
||||
ltm.currentTheme = dummy("x1");
|
||||
ltm.currentTheme = dummy("x2");
|
||||
ltm.currentTheme = dummy("x3");
|
||||
ltm.currentTheme = dummy("x4");
|
||||
ltm.currentTheme = dummy("x5");
|
||||
ltm.currentTheme = dummy("x6");
|
||||
ltm.currentTheme = dummy("x7");
|
||||
ltm.currentTheme = dummy("x8");
|
||||
ltm.currentTheme = dummy("x9");
|
||||
ltm.currentTheme = dummy("x10");
|
||||
ltm.currentTheme = dummy("x11");
|
||||
ltm.currentTheme = dummy("x12");
|
||||
ltm.currentTheme = dummy("x13");
|
||||
ltm.currentTheme = dummy("x14");
|
||||
ltm.currentTheme = dummy("x15");
|
||||
ltm.currentTheme = dummy("x16");
|
||||
|
||||
ltm.currentTheme = dummy("x32");
|
||||
await tick();
|
||||
|
||||
Assert.equal(ltm.usedThemes.length, 33);
|
||||
|
||||
ltm.currentTheme = dummy("x33");
|
||||
await tick();
|
||||
|
||||
Assert.equal(ltm.usedThemes.length, 33);
|
||||
|
||||
Services.prefs.clearUserPref("lightweightThemes.maxUsedThemes");
|
||||
|
||||
Assert.equal(ltm.usedThemes.length, 31);
|
||||
|
||||
let usedThemes = ltm.usedThemes;
|
||||
for (let theme of usedThemes) {
|
||||
ltm.forgetUsedTheme(theme.id);
|
||||
}
|
||||
|
||||
// Check builtInTheme functionality for Bug 1094821
|
||||
Assert.equal(ltm._builtInThemes.toString(), "[object Map]");
|
||||
Assert.equal([...ltm._builtInThemes.entries()].length, 1);
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
|
||||
ltm.addBuiltInTheme(dummy("builtInTheme0"));
|
||||
Assert.equal([...ltm._builtInThemes].length, 2);
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
Assert.equal(ltm.usedThemes[1].id, "builtInTheme0");
|
||||
|
||||
ltm.addBuiltInTheme(dummy("builtInTheme1"));
|
||||
Assert.equal([...ltm._builtInThemes].length, 3);
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
Assert.equal(ltm.usedThemes[2].id, "builtInTheme1");
|
||||
|
||||
// Clear all and then re-add
|
||||
ltm.clearBuiltInThemes();
|
||||
Assert.equal([...ltm._builtInThemes].length, 0);
|
||||
Assert.equal(ltm.usedThemes.length, 0);
|
||||
|
||||
ltm.addBuiltInTheme(dummy("builtInTheme0"));
|
||||
ltm.addBuiltInTheme(dummy("builtInTheme1"));
|
||||
Assert.equal([...ltm._builtInThemes].length, 2);
|
||||
Assert.equal(ltm.usedThemes.length, 2);
|
||||
|
||||
let builtInThemeAddon = await AddonManager.getAddonByID("builtInTheme0@personas.mozilla.org");
|
||||
Assert.equal(hasPermission(builtInThemeAddon, "uninstall"), false);
|
||||
Assert.equal(hasPermission(builtInThemeAddon, "disable"), false);
|
||||
Assert.equal(hasPermission(builtInThemeAddon, "enable"), true);
|
||||
|
||||
await setTheme(dummy("x0"));
|
||||
Assert.equal([...ltm._builtInThemes].length, 2);
|
||||
Assert.equal(ltm.usedThemes.length, 3);
|
||||
Assert.equal(ltm.usedThemes[0].id, "x0");
|
||||
Assert.equal(ltm.currentTheme.id, "x0");
|
||||
Assert.equal(ltm.usedThemes[1].id, "builtInTheme0");
|
||||
Assert.equal(ltm.usedThemes[2].id, "builtInTheme1");
|
||||
|
||||
Assert.throws(() => { ltm.addBuiltInTheme(dummy("builtInTheme0")); },
|
||||
/Error: Trying to add invalid builtIn theme/,
|
||||
"Exception is thrown adding a duplicate theme");
|
||||
Assert.throws(() => { ltm.addBuiltInTheme("not a theme object"); },
|
||||
/Error: Trying to add invalid builtIn theme/,
|
||||
"Exception is thrown adding an invalid theme");
|
||||
|
||||
let x0Addon = await AddonManager.getAddonByID("x0@personas.mozilla.org");
|
||||
Assert.equal(hasPermission(x0Addon, "uninstall"), true);
|
||||
Assert.equal(hasPermission(x0Addon, "disable"), true);
|
||||
Assert.equal(hasPermission(x0Addon, "enable"), false);
|
||||
|
||||
ltm.forgetUsedTheme("x0");
|
||||
await tick();
|
||||
Assert.equal(ltm.currentTheme, null);
|
||||
|
||||
// Removing the currently applied app specific theme should unapply it
|
||||
await setTheme(ltm.getUsedTheme("builtInTheme0"));
|
||||
Assert.equal(ltm.currentTheme.id, "builtInTheme0");
|
||||
Assert.ok(ltm.forgetBuiltInTheme("builtInTheme0"));
|
||||
Assert.equal(ltm.currentTheme, null);
|
||||
|
||||
Assert.equal([...ltm._builtInThemes].length, 1);
|
||||
Assert.equal(ltm.usedThemes.length, 1);
|
||||
|
||||
Assert.ok(ltm.forgetBuiltInTheme("builtInTheme1"));
|
||||
Assert.ok(!ltm.forgetBuiltInTheme("not-an-existing-theme-id"));
|
||||
|
||||
Assert.equal([...ltm._builtInThemes].length, 0);
|
||||
Assert.equal(ltm.usedThemes.length, 0);
|
||||
Assert.equal(ltm.currentTheme, null);
|
||||
});
|
|
@ -1,182 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
|
||||
|
||||
add_task(async function test_theme_updates() {
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
|
||||
|
||||
const server = createHttpServer({hosts: ["example.com"]});
|
||||
|
||||
let updates = {};
|
||||
server.registerPrefixHandler("/", (request, response) => {
|
||||
let update = updates[request.path];
|
||||
if (update) {
|
||||
response.setHeader("content-type", "application/json");
|
||||
response.write(JSON.stringify(update));
|
||||
} else {
|
||||
response.setStatusLine("1.1", 404, "Not Found");
|
||||
}
|
||||
});
|
||||
|
||||
// Themes 1 and 2 will be updated to xpi themes, theme 3 will
|
||||
// (attempt to) be updated to a regular webextension.
|
||||
let theme1XPI = createTempWebExtensionFile({
|
||||
manifest: {
|
||||
name: "Theme 1",
|
||||
version: "2.0",
|
||||
theme: {
|
||||
images: { headerURL: "webextension.png" },
|
||||
},
|
||||
},
|
||||
});
|
||||
server.registerFile("/theme1.xpi", theme1XPI);
|
||||
|
||||
let theme2XPI = createTempWebExtensionFile({
|
||||
manifest: {
|
||||
name: "Theme 2",
|
||||
version: "2.0",
|
||||
theme: {
|
||||
images: { headerURL: "webextension.png" },
|
||||
},
|
||||
},
|
||||
});
|
||||
server.registerFile("/theme2.xpi", theme2XPI);
|
||||
|
||||
let theme3XPI = createTempWebExtensionFile({
|
||||
manifest: {
|
||||
name: "Theme 3",
|
||||
version: "2.0",
|
||||
},
|
||||
});
|
||||
server.registerFile("/theme3.xpi", theme3XPI);
|
||||
|
||||
await promiseStartupManager();
|
||||
|
||||
function makeTheme(n, {image = "orig.png", version = "1.0"} = {}) {
|
||||
return {
|
||||
id: `theme${n}`,
|
||||
name: `Theme ${n}`,
|
||||
version,
|
||||
updateURL: `http://example.com/${n}`,
|
||||
headerURL: `http://example.com/${image}`,
|
||||
textcolor: "#000000",
|
||||
accentcolor: "#ffffff",
|
||||
};
|
||||
}
|
||||
|
||||
// "Install" some LWTs. Leave theme 2 as current
|
||||
LightweightThemeManager.currentTheme = makeTheme(1);
|
||||
LightweightThemeManager.currentTheme = makeTheme(3);
|
||||
LightweightThemeManager.currentTheme = makeTheme(2);
|
||||
|
||||
function filterThemes(themes) {
|
||||
return themes.filter(t => t.id != "default-theme@mozilla.org");
|
||||
}
|
||||
|
||||
equal(filterThemes(LightweightThemeManager.usedThemes).length, 3,
|
||||
"Have 3 lightweight themes");
|
||||
|
||||
// Update URLs are all 404, nothing should change
|
||||
await AddonManagerPrivate.backgroundUpdateCheck();
|
||||
|
||||
equal(filterThemes(LightweightThemeManager.usedThemes).length, 3,
|
||||
"Have 3 lightweight themes");
|
||||
|
||||
// Make updates to all LWTs available, only the active on should be updated
|
||||
updates["/1"] = makeTheme(1, {image: "new.png", version: "1.1"});
|
||||
updates["/2"] = makeTheme(2, {image: "new.png", version: "1.1"});
|
||||
updates["/3"] = makeTheme(3, {image: "new.png", version: "1.1"});
|
||||
await AddonManagerPrivate.backgroundUpdateCheck();
|
||||
|
||||
equal(LightweightThemeManager.currentTheme.id, "theme2",
|
||||
"Theme 2 is currently selected");
|
||||
|
||||
let lwThemes = filterThemes(LightweightThemeManager.usedThemes);
|
||||
let theme1 = lwThemes.find(t => t.id == "theme1");
|
||||
notEqual(theme1, undefined, "Found theme1");
|
||||
ok(theme1.headerURL.endsWith("orig.png"), "LWT 1 was not updated");
|
||||
|
||||
let theme2 = lwThemes.find(t => t.id == "theme2");
|
||||
notEqual(theme2, undefined, "Found theme2");
|
||||
dump(`theme2 ${JSON.stringify(theme2)}\n`);
|
||||
ok(theme2.headerURL.endsWith("new.png"), "LWT 2 was updated");
|
||||
|
||||
let theme3 = lwThemes.find(t => t.id == "theme3");
|
||||
notEqual(theme3, undefined, "Found theme3");
|
||||
ok(theme3.headerURL.endsWith("orig.png"), "LWT 3 was not updated");
|
||||
|
||||
// Update an inactive LWT to an XPI packaged theme
|
||||
updates["/1"] = {
|
||||
converted_theme: {
|
||||
url: "http://example.com/theme1.xpi",
|
||||
hash: do_get_file_hash(theme1XPI),
|
||||
},
|
||||
};
|
||||
|
||||
await AddonManagerPrivate.backgroundUpdateCheck();
|
||||
|
||||
let themes = filterThemes(await AddonManager.getAddonsByTypes(["theme"]));
|
||||
equal(themes.length, 3, "Still have a total of 3 themes");
|
||||
equal(filterThemes(LightweightThemeManager.usedThemes).length, 2,
|
||||
"Have 2 lightweight themes");
|
||||
theme1 = themes.find(t => t.name == "Theme 1");
|
||||
notEqual(theme1, undefined, "Found theme1");
|
||||
ok(!theme1.id.endsWith("@personas.mozilla.org"),
|
||||
"Theme 1 has been updated to an XPI packaged theme");
|
||||
equal(theme1.userDisabled, true, "Theme 1 is not active");
|
||||
|
||||
Assert.deepEqual(theme1.installTelemetryInfo, {
|
||||
source: "lwt-converted-theme",
|
||||
}, "Got the expected source on the converted webextension theme");
|
||||
|
||||
// Update the current LWT to an XPI
|
||||
updates["/2"] = {
|
||||
converted_theme: {
|
||||
url: "http://example.com/theme2.xpi",
|
||||
hash: do_get_file_hash(theme2XPI),
|
||||
},
|
||||
};
|
||||
|
||||
await AddonManagerPrivate.backgroundUpdateCheck();
|
||||
themes = filterThemes(await AddonManager.getAddonsByTypes(["theme"]));
|
||||
equal(themes.length, 3, "Still have a total of 3 themes");
|
||||
equal(filterThemes(LightweightThemeManager.usedThemes).length, 1,
|
||||
"Have 1 lightweight theme");
|
||||
theme2 = themes.find(t => t.name == "Theme 2");
|
||||
notEqual(theme2, undefined, "Found theme2");
|
||||
ok(!theme2.id.endsWith("@personas.mozilla.org"),
|
||||
"Theme 2 has been updated to an XPI packaged theme");
|
||||
equal(theme2.userDisabled, false, "Theme 2 is active");
|
||||
|
||||
Assert.deepEqual(theme2.installTelemetryInfo, {
|
||||
source: "lwt-converted-theme",
|
||||
}, "Got the expected source on the converted webextension theme");
|
||||
|
||||
// Serve an update with a bad hash, the LWT should remain in place.
|
||||
updates["/3"] = {
|
||||
converted_theme: {
|
||||
url: "http://example.com/theme3.xpi",
|
||||
hash: "sha256:abcd",
|
||||
},
|
||||
};
|
||||
|
||||
await AddonManagerPrivate.backgroundUpdateCheck();
|
||||
equal(filterThemes(LightweightThemeManager.usedThemes).length, 1,
|
||||
"Still have 1 lightweight theme");
|
||||
|
||||
// Try updating to a regular (non-theme) webextension, the
|
||||
// LWT should remain in place.
|
||||
updates["/3"] = {
|
||||
converted_theme: {
|
||||
url: "http://example.com/theme3.xpi",
|
||||
hash: do_get_file_hash(theme3XPI),
|
||||
},
|
||||
};
|
||||
await AddonManagerPrivate.backgroundUpdateCheck();
|
||||
equal(filterThemes(LightweightThemeManager.usedThemes).length, 1,
|
||||
"Still have 1 lightweight theme");
|
||||
|
||||
await promiseShutdownManager();
|
||||
});
|
|
@ -11,10 +11,6 @@ Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
|
|||
// doesn't support lightweight themes.
|
||||
Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
|
||||
|
||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
|
||||
var gInstallDate;
|
||||
|
||||
const updateFile = "test_update.json";
|
||||
|
||||
const profileDir = gProfD.clone();
|
||||
|
@ -771,153 +767,6 @@ add_task(async function test_no_auto_update() {
|
|||
await a8.uninstall();
|
||||
});
|
||||
|
||||
// Test that background update checks work for lightweight themes
|
||||
add_task(async function test_lwt_update() {
|
||||
LightweightThemeManager.currentTheme = {
|
||||
id: "1",
|
||||
version: "1",
|
||||
name: "Test LW Theme",
|
||||
description: "A test theme",
|
||||
author: "Mozilla",
|
||||
homepageURL: "http://example.com/data/index.html",
|
||||
headerURL: "http://example.com/data/header.png",
|
||||
previewURL: "http://example.com/data/preview.png",
|
||||
iconURL: "http://example.com/data/icon.png",
|
||||
updateURL: "http://example.com/data/lwtheme.js",
|
||||
};
|
||||
|
||||
// XXX The lightweight theme manager strips non-https updateURLs so hack it
|
||||
// back in.
|
||||
let themes = JSON.parse(Services.prefs.getCharPref("lightweightThemes.usedThemes"));
|
||||
equal(themes.length, 1);
|
||||
themes[0].updateURL = "http://example.com/data/lwtheme.js";
|
||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
|
||||
|
||||
testserver.registerPathHandler("/data/lwtheme.js", function(request, response) {
|
||||
// Server will specify an expiry in one year.
|
||||
let expiry = new Date();
|
||||
expiry.setFullYear(expiry.getFullYear() + 1);
|
||||
response.setHeader("Expires", expiry.toUTCString(), false);
|
||||
response.write(JSON.stringify({
|
||||
id: "1",
|
||||
version: "2",
|
||||
name: "Updated Theme",
|
||||
description: "A test theme",
|
||||
author: "Mozilla",
|
||||
homepageURL: "http://example.com/data/index2.html",
|
||||
headerURL: "http://example.com/data/header.png",
|
||||
previewURL: "http://example.com/data/preview.png",
|
||||
iconURL: "http://example.com/data/icon2.png",
|
||||
updateURL: "http://example.com/data/lwtheme.js",
|
||||
}));
|
||||
});
|
||||
|
||||
let p1 = await AddonManager.getAddonByID("1@personas.mozilla.org");
|
||||
notEqual(p1, null);
|
||||
equal(p1.version, "1");
|
||||
equal(p1.name, "Test LW Theme");
|
||||
ok(p1.isActive);
|
||||
equal(p1.installDate.getTime(), p1.updateDate.getTime());
|
||||
|
||||
// 5 seconds leeway seems like a lot, but tests can run slow and really if
|
||||
// this is within 5 seconds it is fine. If it is going to be wrong then it
|
||||
// is likely to be hours out at least
|
||||
ok((Date.now() - p1.installDate.getTime()) < 5000);
|
||||
|
||||
gInstallDate = p1.installDate.getTime();
|
||||
|
||||
await new Promise(resolve => {
|
||||
prepare_test({
|
||||
"1@personas.mozilla.org": [
|
||||
["onInstalling", false],
|
||||
"onInstalled",
|
||||
],
|
||||
}, [
|
||||
"onExternalInstall",
|
||||
], resolve);
|
||||
|
||||
AddonManagerInternal.backgroundUpdateCheck();
|
||||
});
|
||||
|
||||
p1 = await AddonManager.getAddonByID("1@personas.mozilla.org");
|
||||
notEqual(p1, null);
|
||||
equal(p1.version, "2");
|
||||
equal(p1.name, "Updated Theme");
|
||||
equal(p1.installDate.getTime(), gInstallDate);
|
||||
ok(p1.installDate.getTime() < p1.updateDate.getTime());
|
||||
|
||||
// 5 seconds leeway seems like a lot, but tests can run slow and really if
|
||||
// this is within 5 seconds it is fine. If it is going to be wrong then it
|
||||
// is likely to be hours out at least
|
||||
ok((Date.now() - p1.updateDate.getTime()) < 5000);
|
||||
|
||||
gInstallDate = p1.installDate.getTime();
|
||||
});
|
||||
|
||||
// Test that background update checks for lightweight themes do not use the cache
|
||||
// The update body from test 7 shouldn't be used since the cache should be bypassed.
|
||||
add_task(async function() {
|
||||
// XXX The lightweight theme manager strips non-https updateURLs so hack it
|
||||
// back in.
|
||||
let themes = JSON.parse(Services.prefs.getCharPref("lightweightThemes.usedThemes"));
|
||||
equal(themes.length, 1);
|
||||
themes[0].updateURL = "http://example.com/data/lwtheme.js";
|
||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
|
||||
|
||||
testserver.registerPathHandler("/data/lwtheme.js", function(request, response) {
|
||||
response.write(JSON.stringify({
|
||||
id: "1",
|
||||
version: "3",
|
||||
name: "Updated Theme v.3",
|
||||
description: "A test theme v.3",
|
||||
author: "John Smith",
|
||||
homepageURL: "http://example.com/data/index3.html?v=3",
|
||||
headerURL: "http://example.com/data/header.png?v=3",
|
||||
previewURL: "http://example.com/data/preview.png?v=3",
|
||||
iconURL: "http://example.com/data/icon2.png?v=3",
|
||||
updateURL: "https://example.com/data/lwtheme.js?v=3",
|
||||
}));
|
||||
});
|
||||
|
||||
let p1 = await AddonManager.getAddonByID("1@personas.mozilla.org");
|
||||
notEqual(p1, null);
|
||||
equal(p1.version, "2");
|
||||
equal(p1.name, "Updated Theme");
|
||||
ok(p1.isActive);
|
||||
equal(p1.installDate.getTime(), gInstallDate);
|
||||
ok(p1.installDate.getTime() < p1.updateDate.getTime());
|
||||
|
||||
await new Promise(resolve => {
|
||||
prepare_test({
|
||||
"1@personas.mozilla.org": [
|
||||
["onInstalling", false],
|
||||
"onInstalled",
|
||||
],
|
||||
}, [
|
||||
"onExternalInstall",
|
||||
], resolve);
|
||||
|
||||
AddonManagerInternal.backgroundUpdateCheck();
|
||||
});
|
||||
|
||||
p1 = await AddonManager.getAddonByID("1@personas.mozilla.org");
|
||||
let currentTheme = LightweightThemeManager.currentTheme;
|
||||
notEqual(p1, null);
|
||||
equal(p1.version, "3");
|
||||
equal(p1.name, "Updated Theme v.3");
|
||||
equal(p1.description, "A test theme v.3");
|
||||
info(JSON.stringify(p1));
|
||||
equal(p1.creator.name, "John Smith");
|
||||
equal(p1.homepageURL, "http://example.com/data/index3.html?v=3");
|
||||
equal(p1.screenshots[0].url, "http://example.com/data/preview.png?v=3");
|
||||
equal(p1.iconURL, "http://example.com/data/icon2.png?v=3");
|
||||
equal(currentTheme.headerURL, "http://example.com/data/header.png?v=3");
|
||||
equal(currentTheme.updateURL, "https://example.com/data/lwtheme.js?v=3");
|
||||
|
||||
equal(p1.installDate.getTime(), gInstallDate);
|
||||
ok(p1.installDate.getTime() < p1.updateDate.getTime());
|
||||
});
|
||||
|
||||
// Test that the update check returns nothing for addons in locked install
|
||||
// locations.
|
||||
add_task(async function run_test_locked_install() {
|
||||
|
|
|
@ -8,13 +8,12 @@
|
|||
* Coverage may overlap with other tests in this folder.
|
||||
*/
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
const THEME_IDS = [
|
||||
"theme3@tests.mozilla.org",
|
||||
"theme2@personas.mozilla.org",
|
||||
"theme2@personas.mozilla.org", // Unused. Legacy. Evil.
|
||||
"default-theme@mozilla.org",
|
||||
];
|
||||
const REAL_THEME_IDS = [THEME_IDS[0], THEME_IDS[2]];
|
||||
const DEFAULT_THEME = THEME_IDS[2];
|
||||
|
||||
const profileDir = gProfD.clone();
|
||||
|
@ -41,38 +40,20 @@ add_task(async function setup_to_default_browserish_state() {
|
|||
|
||||
await promiseStartupManager();
|
||||
|
||||
// We can add an LWT only after the Addon Manager was started.
|
||||
LightweightThemeManager.currentTheme = {
|
||||
id: THEME_IDS[1].substr(0, THEME_IDS[1].indexOf("@")),
|
||||
version: "1",
|
||||
name: "Bling",
|
||||
description: "SO MUCH BLING!",
|
||||
author: "Pixel Pusher",
|
||||
homepageURL: "http://localhost:8888/data/index.html",
|
||||
headerURL: "http://localhost:8888/data/header.png",
|
||||
previewURL: "http://localhost:8888/data/preview.png",
|
||||
iconURL: "http://localhost:8888/data/icon.png",
|
||||
textcolor: Math.random().toString(),
|
||||
accentcolor: Math.random().toString(),
|
||||
};
|
||||
|
||||
let [ t1, t2, d ] = await promiseAddonsByIDs(THEME_IDS);
|
||||
Assert.ok(t1, "Theme addon should exist");
|
||||
Assert.ok(t2, "Theme addon should exist");
|
||||
Assert.equal(t2, null, "Theme addon is not a thing anymore");
|
||||
Assert.ok(d, "Theme addon should exist");
|
||||
|
||||
await t1.disable();
|
||||
await t2.disable();
|
||||
await new Promise(executeSoon);
|
||||
Assert.ok(!t1.isActive, "Theme should be disabled");
|
||||
Assert.ok(!t2.isActive, "Theme should be disabled");
|
||||
Assert.ok(d.isActive, "Default theme should be active");
|
||||
|
||||
await promiseRestartManager();
|
||||
|
||||
[ t1, t2, d ] = await promiseAddonsByIDs(THEME_IDS);
|
||||
Assert.ok(!t1.isActive, "Theme should still be disabled");
|
||||
Assert.ok(!t2.isActive, "Theme should still be disabled");
|
||||
Assert.ok(d.isActive, "Default theme should still be active");
|
||||
|
||||
gActiveTheme = d.id;
|
||||
|
@ -117,7 +98,7 @@ async function setDisabledStateAndCheck(which, disabled = false) {
|
|||
}
|
||||
|
||||
let isDisabled;
|
||||
for (theme of await promiseAddonsByIDs(THEME_IDS)) {
|
||||
for (theme of await promiseAddonsByIDs(REAL_THEME_IDS)) {
|
||||
isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
|
||||
Assert.equal(theme.userDisabled, isDisabled,
|
||||
`Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
|
||||
|
@ -130,7 +111,7 @@ async function setDisabledStateAndCheck(which, disabled = false) {
|
|||
await promiseRestartManager();
|
||||
|
||||
// All should still be good after a restart of the Addon Manager.
|
||||
for (theme of await promiseAddonsByIDs(THEME_IDS)) {
|
||||
for (theme of await promiseAddonsByIDs(REAL_THEME_IDS)) {
|
||||
isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
|
||||
Assert.equal(theme.userDisabled, isDisabled,
|
||||
`Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
|
||||
|
@ -154,29 +135,6 @@ add_task(async function test_WebExtension_themes() {
|
|||
|
||||
// Enable it again.
|
||||
await setDisabledStateAndCheck(THEME_IDS[0]);
|
||||
|
||||
// Enabling an LWT should disable the active theme.
|
||||
await setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
|
||||
// Switching back should disable the LWT.
|
||||
await setDisabledStateAndCheck(THEME_IDS[0]);
|
||||
});
|
||||
|
||||
add_task(async function test_LWTs() {
|
||||
// Start with enabling an LWT.
|
||||
await setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
|
||||
// Disabling LWT should revert to the default theme.
|
||||
await setDisabledStateAndCheck(THEME_IDS[1], true);
|
||||
|
||||
// Enable it again.
|
||||
await setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
|
||||
// Enabling a WebExtension theme should disable the active theme.
|
||||
await setDisabledStateAndCheck(THEME_IDS[0]);
|
||||
|
||||
// Switching back should disable the LWT.
|
||||
await setDisabledStateAndCheck(THEME_IDS[1]);
|
||||
});
|
||||
|
||||
add_task(async function test_default_theme() {
|
||||
|
|
|
@ -14,7 +14,6 @@ support-files =
|
|||
skip-if = os == "android"
|
||||
[test_AddonRepository_langpacks.js]
|
||||
[test_AddonRepository_paging.js]
|
||||
[test_LightweightThemeManager.js]
|
||||
[test_ProductAddonChecker.js]
|
||||
[test_XPIStates.js]
|
||||
[test_XPIcancel.js]
|
||||
|
@ -229,7 +228,6 @@ head = head_addons.js head_system_addons.js
|
|||
[test_temporary.js]
|
||||
skip-if = os == "win" # Bug 1469904
|
||||
tags = webextensions
|
||||
[test_theme_update.js]
|
||||
[test_trash_directory.js]
|
||||
skip-if = os != "win"
|
||||
[test_types.js]
|
||||
|
|
|
@ -46,7 +46,7 @@ interface nsIProfileLock : nsISupports
|
|||
* @note THIS INTERFACE SHOULD BE IMPLEMENTED BY THE TOOLKIT CODE ONLY! DON'T
|
||||
* EVEN THINK ABOUT IMPLEMENTING THIS IN JAVASCRIPT!
|
||||
*/
|
||||
[scriptable, uuid(7422b090-4a86-4407-972e-75468a625388)]
|
||||
[scriptable, builtinclass, uuid(7422b090-4a86-4407-972e-75468a625388)]
|
||||
interface nsIToolkitProfile : nsISupports
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -52,20 +52,67 @@ using namespace mozilla;
|
|||
#define DEV_EDITION_NAME "dev-edition-default"
|
||||
#define DEFAULT_NAME "default"
|
||||
#define COMPAT_FILE NS_LITERAL_STRING("compatibility.ini")
|
||||
#define PROFILE_DB_VERSION "2"
|
||||
#define INSTALL_PREFIX "Install"
|
||||
#define INSTALL_PREFIX_LENGTH 7
|
||||
|
||||
struct KeyValue {
|
||||
KeyValue(const char* aKey, const char* aValue) : key(aKey), value(aValue) {}
|
||||
|
||||
nsCString key;
|
||||
nsCString value;
|
||||
};
|
||||
|
||||
static bool GetStrings(const char* aString, const char* aValue,
|
||||
void* aClosure) {
|
||||
nsTArray<UniquePtr<KeyValue>>* array =
|
||||
static_cast<nsTArray<UniquePtr<KeyValue>>*>(aClosure);
|
||||
array->AppendElement(MakeUnique<KeyValue>(aString, aValue));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the strings inside a section of an ini file.
|
||||
*/
|
||||
nsTArray<UniquePtr<KeyValue>> GetSectionStrings(nsINIParser* aParser,
|
||||
const char* aSection) {
|
||||
nsTArray<UniquePtr<KeyValue>> result;
|
||||
aParser->GetStrings(aSection, &GetStrings, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
nsIFile* aLocalDir, nsToolkitProfile* aPrev)
|
||||
: mPrev(aPrev),
|
||||
mName(aName),
|
||||
nsIFile* aLocalDir, bool aFromDB)
|
||||
: mName(aName),
|
||||
mRootDir(aRootDir),
|
||||
mLocalDir(aLocalDir),
|
||||
mLock(nullptr) {
|
||||
mLock(nullptr),
|
||||
mIndex(0),
|
||||
mSection("Profile") {
|
||||
NS_ASSERTION(aRootDir, "No file!");
|
||||
|
||||
if (aPrev) {
|
||||
aPrev->mNext = this;
|
||||
} else {
|
||||
nsToolkitProfileService::gService->mFirst = this;
|
||||
RefPtr<nsToolkitProfile> prev =
|
||||
nsToolkitProfileService::gService->mProfiles.getLast();
|
||||
if (prev) {
|
||||
mIndex = prev->mIndex + 1;
|
||||
}
|
||||
mSection.AppendInt(mIndex);
|
||||
|
||||
nsToolkitProfileService::gService->mProfiles.insertBack(this);
|
||||
|
||||
// If this profile isn't in the database already add it.
|
||||
if (!aFromDB) {
|
||||
nsINIParser* db = &nsToolkitProfileService::gService->mProfileDB;
|
||||
db->SetString(mSection.get(), "Name", mName.get());
|
||||
|
||||
bool isRelative = false;
|
||||
nsCString descriptor;
|
||||
nsToolkitProfileService::gService->GetProfileDescriptor(this, descriptor,
|
||||
&isRelative);
|
||||
|
||||
db->SetString(mSection.get(), "IsRelative", isRelative ? "1" : "0");
|
||||
db->SetString(mSection.get(), "Path", descriptor.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,6 +153,10 @@ nsToolkitProfile::SetName(const nsACString& aName) {
|
|||
|
||||
mName = aName;
|
||||
|
||||
nsresult rv = nsToolkitProfileService::gService->mProfileDB.SetString(
|
||||
mSection.get(), "Name", mName.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Setting the name to the dev-edition default profile name will cause this
|
||||
// profile to become the dev-edition default.
|
||||
if (aName.EqualsLiteral(DEV_EDITION_NAME) &&
|
||||
|
@ -122,8 +173,9 @@ nsresult nsToolkitProfile::RemoveInternal(bool aRemoveFiles,
|
|||
|
||||
if (mLock) return NS_ERROR_FILE_IS_LOCKED;
|
||||
|
||||
if (!mPrev && !mNext && nsToolkitProfileService::gService->mFirst != this)
|
||||
if (!isInList()) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (aRemoveFiles) {
|
||||
// Check if another instance is using this profile.
|
||||
|
@ -160,15 +212,29 @@ nsresult nsToolkitProfile::RemoveInternal(bool aRemoveFiles,
|
|||
}
|
||||
}
|
||||
|
||||
if (mPrev)
|
||||
mPrev->mNext = mNext;
|
||||
else
|
||||
nsToolkitProfileService::gService->mFirst = mNext;
|
||||
nsINIParser* db = &nsToolkitProfileService::gService->mProfileDB;
|
||||
db->DeleteSection(mSection.get());
|
||||
|
||||
if (mNext) mNext->mPrev = mPrev;
|
||||
// We make some assumptions that the profile's index in the database is based
|
||||
// on its position in the linked list. Removing a profile means we have to fix
|
||||
// the index of later profiles in the list. The easiest way to do that is just
|
||||
// to move the last profile into the profile's position and just update its
|
||||
// index.
|
||||
RefPtr<nsToolkitProfile> last =
|
||||
nsToolkitProfileService::gService->mProfiles.getLast();
|
||||
if (last != this) {
|
||||
// Update the section in the db.
|
||||
last->mIndex = mIndex;
|
||||
db->RenameSection(last->mSection.get(), mSection.get());
|
||||
last->mSection = mSection;
|
||||
|
||||
mPrev = nullptr;
|
||||
mNext = nullptr;
|
||||
if (last != getNext()) {
|
||||
last->remove();
|
||||
setNext(last);
|
||||
}
|
||||
}
|
||||
|
||||
remove();
|
||||
|
||||
if (nsToolkitProfileService::gService->mNormalDefault == this) {
|
||||
nsToolkitProfileService::gService->mNormalDefault = nullptr;
|
||||
|
@ -313,7 +379,10 @@ nsToolkitProfileService::nsToolkitProfileService()
|
|||
gService = this;
|
||||
}
|
||||
|
||||
nsToolkitProfileService::~nsToolkitProfileService() { gService = nullptr; }
|
||||
nsToolkitProfileService::~nsToolkitProfileService() {
|
||||
gService = nullptr;
|
||||
mProfiles.clear();
|
||||
}
|
||||
|
||||
void nsToolkitProfileService::CompleteStartup() {
|
||||
if (!mStartupProfileSelected) {
|
||||
|
@ -335,7 +404,7 @@ void nsToolkitProfileService::CompleteStartup() {
|
|||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
if (isDefaultApp) {
|
||||
mInstallData.SetString(mInstallHash.get(), "Locked", "1");
|
||||
mProfileDB.SetString(mInstallSection.get(), "Locked", "1");
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
@ -440,7 +509,7 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
|||
const nsCString& install = installs[i];
|
||||
|
||||
nsCString path;
|
||||
rv = mInstallData.GetString(install.get(), "Default", path);
|
||||
rv = mProfileDB.GetString(install.get(), "Default", path);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -452,7 +521,7 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
|||
|
||||
// Is this profile locked to this other install?
|
||||
nsCString isLocked;
|
||||
rv = mInstallData.GetString(install.get(), "Locked", isLocked);
|
||||
rv = mProfileDB.GetString(install.get(), "Locked", isLocked);
|
||||
if (NS_SUCCEEDED(rv) && isLocked.Equals("1")) {
|
||||
return false;
|
||||
}
|
||||
|
@ -465,7 +534,7 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
|||
for (uint32_t i = 0; i < inUseInstalls.Length(); i++) {
|
||||
// Removing the default setting entirely will make the install go through
|
||||
// the first run process again at startup and create itself a new profile.
|
||||
mInstallData.DeleteString(inUseInstalls[i].get(), "Default");
|
||||
mProfileDB.DeleteString(inUseInstalls[i].get(), "Default");
|
||||
}
|
||||
|
||||
// Set this as the default profile for this install.
|
||||
|
@ -474,7 +543,7 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
|||
// SetDefaultProfile will have locked this profile to this install so no
|
||||
// other installs will steal it, but this was auto-selected so we want to
|
||||
// unlock it so that other installs can potentially take it.
|
||||
mInstallData.DeleteString(mInstallHash.get(), "Locked");
|
||||
mProfileDB.DeleteString(mInstallSection.get(), "Locked");
|
||||
|
||||
// Persist the changes.
|
||||
Flush();
|
||||
|
@ -486,6 +555,33 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
|||
return true;
|
||||
}
|
||||
|
||||
struct ImportInstallsClosure {
|
||||
nsINIParser* backupData;
|
||||
nsINIParser* profileDB;
|
||||
};
|
||||
|
||||
static bool ImportInstalls(const char* aSection, void* aClosure) {
|
||||
ImportInstallsClosure* closure =
|
||||
static_cast<ImportInstallsClosure*>(aClosure);
|
||||
|
||||
nsTArray<UniquePtr<KeyValue>> strings =
|
||||
GetSectionStrings(closure->backupData, aSection);
|
||||
if (strings.IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCString newSection(INSTALL_PREFIX);
|
||||
newSection.Append(aSection);
|
||||
nsCString buffer;
|
||||
|
||||
for (uint32_t i = 0; i < strings.Length(); i++) {
|
||||
closure->profileDB->SetString(newSection.get(), strings[i]->key.get(),
|
||||
strings[i]->value.get());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult nsToolkitProfileService::Init() {
|
||||
NS_ASSERTION(gDirServiceProvider, "No dirserviceprovider!");
|
||||
nsresult rv;
|
||||
|
@ -496,54 +592,76 @@ nsresult nsToolkitProfileService::Init() {
|
|||
rv = nsXREDirProvider::GetUserLocalDataDirectory(getter_AddRefs(mTempData));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString installProfilePath;
|
||||
|
||||
if (mUseDedicatedProfile) {
|
||||
// Load the dedicated profiles database.
|
||||
rv = mAppData->Clone(getter_AddRefs(mInstallFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mInstallFile->AppendNative(NS_LITERAL_CSTRING("installs.ini"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsString installHash;
|
||||
rv = gDirServiceProvider->GetInstallHash(installHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
CopyUTF16toUTF8(installHash, mInstallHash);
|
||||
|
||||
rv = mInstallData.Init(mInstallFile);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Try to find the descriptor for the default profile for this install.
|
||||
rv = mInstallData.GetString(mInstallHash.get(), "Default",
|
||||
installProfilePath);
|
||||
// Not having a value means this install doesn't appear in installs.ini so
|
||||
// this is the first run for this install.
|
||||
mIsFirstRun = NS_FAILED(rv);
|
||||
}
|
||||
}
|
||||
|
||||
rv = mAppData->Clone(getter_AddRefs(mListFile));
|
||||
rv = mAppData->Clone(getter_AddRefs(mProfileDBFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mListFile->AppendNative(NS_LITERAL_CSTRING("profiles.ini"));
|
||||
rv = mProfileDBFile->AppendNative(NS_LITERAL_CSTRING("profiles.ini"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsINIParser parser;
|
||||
rv = mAppData->Clone(getter_AddRefs(mInstallDBFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mInstallDBFile->AppendNative(NS_LITERAL_CSTRING("installs.ini"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString buffer;
|
||||
|
||||
bool exists;
|
||||
rv = mListFile->IsFile(&exists);
|
||||
rv = mProfileDBFile->IsFile(&exists);
|
||||
if (NS_SUCCEEDED(rv) && exists) {
|
||||
rv = parser.Init(mListFile);
|
||||
rv = mProfileDB.Init(mProfileDBFile);
|
||||
// Init does not fail on parsing errors, only on OOM/really unexpected
|
||||
// conditions.
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mProfileDB.GetString("General", "StartWithLastProfile", buffer);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mStartWithLast = !buffer.EqualsLiteral("0");
|
||||
}
|
||||
|
||||
rv = mProfileDB.GetString("General", "Version", buffer);
|
||||
if (NS_FAILED(rv)) {
|
||||
// This is a profiles.ini written by an older version. We must restore
|
||||
// any install data from the backup.
|
||||
nsINIParser installDB;
|
||||
|
||||
rv = mInstallDBFile->IsFile(&exists);
|
||||
if (NS_SUCCEEDED(rv) && exists &&
|
||||
NS_SUCCEEDED(installDB.Init(mInstallDBFile))) {
|
||||
// There is install data to import.
|
||||
ImportInstallsClosure closure = {&installDB, &mProfileDB};
|
||||
installDB.GetSections(&ImportInstalls, &closure);
|
||||
}
|
||||
|
||||
rv = mProfileDB.SetString("General", "Version", PROFILE_DB_VERSION);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
} else {
|
||||
rv = mProfileDB.SetString("General", "StartWithLastProfile",
|
||||
mStartWithLast ? "1" : "0");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mProfileDB.SetString("General", "Version", PROFILE_DB_VERSION);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsAutoCString buffer;
|
||||
rv = parser.GetString("General", "StartWithLastProfile", buffer);
|
||||
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("0")) mStartWithLast = false;
|
||||
nsCString installProfilePath;
|
||||
|
||||
if (mUseDedicatedProfile) {
|
||||
nsString installHash;
|
||||
rv = gDirServiceProvider->GetInstallHash(installHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
CopyUTF16toUTF8(installHash, mInstallSection);
|
||||
mInstallSection.Insert(INSTALL_PREFIX, 0);
|
||||
|
||||
// Try to find the descriptor for the default profile for this install.
|
||||
rv = mProfileDB.GetString(mInstallSection.get(), "Default",
|
||||
installProfilePath);
|
||||
// Not having a value means this install doesn't appear in installs.ini so
|
||||
// this is the first run for this install.
|
||||
mIsFirstRun = NS_FAILED(rv);
|
||||
}
|
||||
|
||||
nsToolkitProfile* currentProfile = nullptr;
|
||||
|
||||
|
@ -575,14 +693,14 @@ nsresult nsToolkitProfileService::Init() {
|
|||
nsAutoCString profileID("Profile");
|
||||
profileID.AppendInt(c);
|
||||
|
||||
rv = parser.GetString(profileID.get(), "IsRelative", buffer);
|
||||
rv = mProfileDB.GetString(profileID.get(), "IsRelative", buffer);
|
||||
if (NS_FAILED(rv)) break;
|
||||
|
||||
bool isRelative = buffer.EqualsLiteral("1");
|
||||
|
||||
nsAutoCString filePath;
|
||||
|
||||
rv = parser.GetString(profileID.get(), "Path", filePath);
|
||||
rv = mProfileDB.GetString(profileID.get(), "Path", filePath);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Malformed profiles.ini: Path= not found");
|
||||
continue;
|
||||
|
@ -590,7 +708,7 @@ nsresult nsToolkitProfileService::Init() {
|
|||
|
||||
nsAutoCString name;
|
||||
|
||||
rv = parser.GetString(profileID.get(), "Name", name);
|
||||
rv = mProfileDB.GetString(profileID.get(), "Name", name);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Malformed profiles.ini: Name= not found");
|
||||
continue;
|
||||
|
@ -618,11 +736,10 @@ nsresult nsToolkitProfileService::Init() {
|
|||
localDir = rootDir;
|
||||
}
|
||||
|
||||
currentProfile =
|
||||
new nsToolkitProfile(name, rootDir, localDir, currentProfile);
|
||||
currentProfile = new nsToolkitProfile(name, rootDir, localDir, true);
|
||||
NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = parser.GetString(profileID.get(), "Default", buffer);
|
||||
rv = mProfileDB.GetString(profileID.get(), "Default", buffer);
|
||||
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1")) {
|
||||
mNormalDefault = currentProfile;
|
||||
}
|
||||
|
@ -644,7 +761,7 @@ nsresult nsToolkitProfileService::Init() {
|
|||
|
||||
// If there is only one non-dev-edition profile then mark it as the default.
|
||||
if (!mNormalDefault && nonDevEditionProfiles == 1) {
|
||||
mNormalDefault = autoSelectProfile;
|
||||
SetNormalDefault(autoSelectProfile);
|
||||
}
|
||||
|
||||
if (!mUseDedicatedProfile) {
|
||||
|
@ -664,6 +781,9 @@ nsresult nsToolkitProfileService::Init() {
|
|||
NS_IMETHODIMP
|
||||
nsToolkitProfileService::SetStartWithLastProfile(bool aValue) {
|
||||
if (mStartWithLast != aValue) {
|
||||
nsresult rv = mProfileDB.SetString("General", "StartWithLastProfile",
|
||||
mStartWithLast ? "1" : "0");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mStartWithLast = aValue;
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -677,7 +797,7 @@ nsToolkitProfileService::GetStartWithLastProfile(bool* aResult) {
|
|||
|
||||
NS_IMETHODIMP
|
||||
nsToolkitProfileService::GetProfiles(nsISimpleEnumerator** aResult) {
|
||||
*aResult = new ProfileEnumerator(this->mFirst);
|
||||
*aResult = new ProfileEnumerator(mProfiles.getFirst());
|
||||
if (!*aResult) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
NS_ADDREF(*aResult);
|
||||
|
@ -696,7 +816,7 @@ nsToolkitProfileService::ProfileEnumerator::GetNext(nsISupports** aResult) {
|
|||
|
||||
NS_ADDREF(*aResult = mCurrent);
|
||||
|
||||
mCurrent = mCurrent->mNext;
|
||||
mCurrent = mCurrent->getNext();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -722,6 +842,26 @@ nsToolkitProfileService::GetDefaultProfile(nsIToolkitProfile** aResult) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsToolkitProfileService::SetNormalDefault(nsIToolkitProfile* aProfile) {
|
||||
if (mNormalDefault == aProfile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNormalDefault) {
|
||||
nsToolkitProfile* profile =
|
||||
static_cast<nsToolkitProfile*>(mNormalDefault.get());
|
||||
mProfileDB.DeleteString(profile->mSection.get(), "Default");
|
||||
}
|
||||
|
||||
mNormalDefault = aProfile;
|
||||
|
||||
if (mNormalDefault) {
|
||||
nsToolkitProfile* profile =
|
||||
static_cast<nsToolkitProfile*>(mNormalDefault.get());
|
||||
mProfileDB.SetString(profile->mSection.get(), "Default", "1");
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) {
|
||||
if (mUseDedicatedProfile) {
|
||||
|
@ -730,20 +870,20 @@ nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) {
|
|||
// Setting this to the empty string means no profile will be found on
|
||||
// startup but we'll recognise that this install has been used
|
||||
// previously.
|
||||
mInstallData.SetString(mInstallHash.get(), "Default", "");
|
||||
mProfileDB.SetString(mInstallSection.get(), "Default", "");
|
||||
} else {
|
||||
nsCString profilePath;
|
||||
nsresult rv = GetProfileDescriptor(aProfile, profilePath, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mInstallData.SetString(mInstallHash.get(), "Default",
|
||||
profilePath.get());
|
||||
mProfileDB.SetString(mInstallSection.get(), "Default",
|
||||
profilePath.get());
|
||||
}
|
||||
mDedicatedProfile = aProfile;
|
||||
|
||||
// Some kind of choice has happened here, lock this profile to this
|
||||
// install.
|
||||
mInstallData.SetString(mInstallHash.get(), "Locked", "1");
|
||||
mProfileDB.SetString(mInstallSection.get(), "Locked", "1");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -753,7 +893,8 @@ nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) {
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mNormalDefault = aProfile;
|
||||
SetNormalDefault(aProfile);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -813,7 +954,7 @@ nsresult nsToolkitProfileService::CreateDefaultProfile(
|
|||
} else if (mUseDevEditionProfile) {
|
||||
mDevEditionDefault = mCurrent;
|
||||
} else {
|
||||
mNormalDefault = mCurrent;
|
||||
SetNormalDefault(mCurrent);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -842,9 +983,9 @@ nsToolkitProfileService::SelectStartupProfile(
|
|||
argv[argc] = nullptr;
|
||||
|
||||
bool wasDefault;
|
||||
nsresult rv = SelectStartupProfile(&argc, argv.get(), aIsResetting, aRootDir,
|
||||
aLocalDir, aProfile, aDidCreate,
|
||||
&wasDefault);
|
||||
nsresult rv =
|
||||
SelectStartupProfile(&argc, argv.get(), aIsResetting, aRootDir, aLocalDir,
|
||||
aProfile, aDidCreate, &wasDefault);
|
||||
|
||||
// Since we were called outside of the normal startup path complete any
|
||||
// startup tasks.
|
||||
|
@ -1163,10 +1304,12 @@ nsresult nsToolkitProfileService::SelectStartupProfile(
|
|||
// older versions of Firefox use then we must create a default profile
|
||||
// for older versions of Firefox to avoid the existing profile being
|
||||
// auto-selected.
|
||||
if ((mUseDedicatedProfile || mUseDevEditionProfile) && mFirst &&
|
||||
!mFirst->mNext) {
|
||||
if ((mUseDedicatedProfile || mUseDevEditionProfile) &&
|
||||
mProfiles.getFirst() == mProfiles.getLast()) {
|
||||
nsCOMPtr<nsIToolkitProfile> newProfile;
|
||||
CreateProfile(nullptr, NS_LITERAL_CSTRING(DEFAULT_NAME),
|
||||
getter_AddRefs(mNormalDefault));
|
||||
getter_AddRefs(newProfile));
|
||||
SetNormalDefault(newProfile);
|
||||
}
|
||||
|
||||
Flush();
|
||||
|
@ -1249,14 +1392,14 @@ nsresult nsToolkitProfileService::ApplyResetProfile(
|
|||
// If the old profile would have been the default for old installs then mark
|
||||
// the new profile as such.
|
||||
if (mNormalDefault == aOldProfile) {
|
||||
mNormalDefault = mCurrent;
|
||||
SetNormalDefault(mCurrent);
|
||||
}
|
||||
|
||||
if (mUseDedicatedProfile && mDedicatedProfile == aOldProfile) {
|
||||
bool wasLocked = false;
|
||||
nsCString val;
|
||||
if (NS_SUCCEEDED(
|
||||
mInstallData.GetString(mInstallHash.get(), "Locked", val))) {
|
||||
mProfileDB.GetString(mInstallSection.get(), "Locked", val))) {
|
||||
wasLocked = val.Equals("1");
|
||||
}
|
||||
|
||||
|
@ -1264,7 +1407,7 @@ nsresult nsToolkitProfileService::ApplyResetProfile(
|
|||
|
||||
// Make the locked state match if necessary.
|
||||
if (!wasLocked) {
|
||||
mInstallData.DeleteString(mInstallHash.get(), "Locked");
|
||||
mProfileDB.DeleteString(mInstallSection.get(), "Locked");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1286,13 +1429,11 @@ nsresult nsToolkitProfileService::ApplyResetProfile(
|
|||
NS_IMETHODIMP
|
||||
nsToolkitProfileService::GetProfileByName(const nsACString& aName,
|
||||
nsIToolkitProfile** aResult) {
|
||||
nsToolkitProfile* curP = mFirst;
|
||||
while (curP) {
|
||||
if (curP->mName.Equals(aName)) {
|
||||
NS_ADDREF(*aResult = curP);
|
||||
for (RefPtr<nsToolkitProfile> profile : mProfiles) {
|
||||
if (profile->mName.Equals(aName)) {
|
||||
NS_ADDREF(*aResult = profile);
|
||||
return NS_OK;
|
||||
}
|
||||
curP = curP->mNext;
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -1305,18 +1446,16 @@ nsToolkitProfileService::GetProfileByName(const nsACString& aName,
|
|||
void nsToolkitProfileService::GetProfileByDir(nsIFile* aRootDir,
|
||||
nsIFile* aLocalDir,
|
||||
nsIToolkitProfile** aResult) {
|
||||
nsToolkitProfile* curP = mFirst;
|
||||
while (curP) {
|
||||
for (RefPtr<nsToolkitProfile> profile : mProfiles) {
|
||||
bool equal;
|
||||
nsresult rv = curP->mRootDir->Equals(aRootDir, &equal);
|
||||
nsresult rv = profile->mRootDir->Equals(aRootDir, &equal);
|
||||
if (NS_SUCCEEDED(rv) && equal) {
|
||||
rv = curP->mLocalDir->Equals(aLocalDir, &equal);
|
||||
rv = profile->mLocalDir->Equals(aLocalDir, &equal);
|
||||
if (NS_SUCCEEDED(rv) && equal) {
|
||||
NS_ADDREF(*aResult = curP);
|
||||
NS_ADDREF(*aResult = profile);
|
||||
return;
|
||||
}
|
||||
}
|
||||
curP = curP->mNext;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1449,15 +1588,8 @@ nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
|
|||
rv = CreateTimesInternal(rootDir);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsToolkitProfile* last = mFirst.get();
|
||||
if (last) {
|
||||
while (last->mNext) {
|
||||
last = last->mNext;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIToolkitProfile> profile =
|
||||
new nsToolkitProfile(aName, rootDir, localDir, last);
|
||||
new nsToolkitProfile(aName, rootDir, localDir, false);
|
||||
if (!profile) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (aName.Equals(DEV_EDITION_NAME)) {
|
||||
|
@ -1496,6 +1628,11 @@ struct FindInstallsClosure {
|
|||
static bool FindInstalls(const char* aSection, void* aClosure) {
|
||||
FindInstallsClosure* closure = static_cast<FindInstallsClosure*>(aClosure);
|
||||
|
||||
// Check if the section starts with "Install"
|
||||
if (strncmp(aSection, INSTALL_PREFIX, INSTALL_PREFIX_LENGTH) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCString install(aSection);
|
||||
closure->installs->AppendElement(install);
|
||||
|
||||
|
@ -1504,9 +1641,9 @@ static bool FindInstalls(const char* aSection, void* aClosure) {
|
|||
|
||||
nsTArray<nsCString> nsToolkitProfileService::GetKnownInstalls() {
|
||||
nsTArray<nsCString> result;
|
||||
FindInstallsClosure closure = {&mInstallData, &result};
|
||||
FindInstallsClosure closure = {&mProfileDB, &result};
|
||||
|
||||
mInstallData.GetSections(&FindInstalls, &closure);
|
||||
mProfileDB.GetSections(&FindInstalls, &closure);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1545,10 +1682,9 @@ nsresult nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir) {
|
|||
NS_IMETHODIMP
|
||||
nsToolkitProfileService::GetProfileCount(uint32_t* aResult) {
|
||||
*aResult = 0;
|
||||
nsToolkitProfile* profile = mFirst;
|
||||
while (profile) {
|
||||
for (nsToolkitProfile* profile : mProfiles) {
|
||||
Unused << profile;
|
||||
(*aResult)++;
|
||||
profile = profile->mNext;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -1558,71 +1694,59 @@ NS_IMETHODIMP
|
|||
nsToolkitProfileService::Flush() {
|
||||
nsresult rv;
|
||||
|
||||
// If we aren't using dedicated profiles then nothing about the list of
|
||||
// installs can have changed, so no need to update the backup.
|
||||
if (mUseDedicatedProfile) {
|
||||
rv = mInstallData.WriteToFile(mInstallFile);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
// Export the installs to the backup.
|
||||
nsTArray<nsCString> installs = GetKnownInstalls();
|
||||
|
||||
// Errors during writing might cause unhappy semi-written files.
|
||||
// To avoid this, write the entire thing to a buffer, then write
|
||||
// that buffer to disk.
|
||||
if (!installs.IsEmpty()) {
|
||||
nsCString data;
|
||||
nsCString buffer;
|
||||
|
||||
uint32_t pCount = 0;
|
||||
nsToolkitProfile* cur;
|
||||
for (uint32_t i = 0; i < installs.Length(); i++) {
|
||||
nsTArray<UniquePtr<KeyValue>> strings =
|
||||
GetSectionStrings(&mProfileDB, installs[i].get());
|
||||
if (strings.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (cur = mFirst; cur != nullptr; cur = cur->mNext) ++pCount;
|
||||
// Strip "Install" from the start.
|
||||
const nsDependentCSubstring& install =
|
||||
Substring(installs[i], INSTALL_PREFIX_LENGTH);
|
||||
data.AppendPrintf("[%s]\n", PromiseFlatCString(install).get());
|
||||
|
||||
uint32_t length;
|
||||
const int bufsize = 100 + MAXPATHLEN * pCount;
|
||||
auto buffer = MakeUnique<char[]>(bufsize);
|
||||
for (uint32_t j = 0; j < strings.Length(); j++) {
|
||||
data.AppendPrintf("%s=%s\n", strings[j]->key.get(),
|
||||
strings[j]->value.get());
|
||||
}
|
||||
|
||||
char* pos = buffer.get();
|
||||
char* end = pos + bufsize;
|
||||
data.Append("\n");
|
||||
}
|
||||
|
||||
pos += snprintf(pos, end - pos,
|
||||
"[General]\n"
|
||||
"StartWithLastProfile=%s\n\n",
|
||||
mStartWithLast ? "1" : "0");
|
||||
FILE* writeFile;
|
||||
rv = mInstallDBFile->OpenANSIFileDesc("w", &writeFile);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString path;
|
||||
cur = mFirst;
|
||||
pCount = 0;
|
||||
uint32_t length = data.Length();
|
||||
if (fwrite(data.get(), sizeof(char), length, writeFile) != length) {
|
||||
fclose(writeFile);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
while (cur) {
|
||||
bool isRelative;
|
||||
nsresult rv = GetProfileDescriptor(cur, path, &isRelative);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
pos +=
|
||||
snprintf(pos, end - pos,
|
||||
"[Profile%u]\n"
|
||||
"Name=%s\n"
|
||||
"IsRelative=%s\n"
|
||||
"Path=%s\n",
|
||||
pCount, cur->mName.get(), isRelative ? "1" : "0", path.get());
|
||||
|
||||
if (cur == mNormalDefault) {
|
||||
pos += snprintf(pos, end - pos, "Default=1\n");
|
||||
fclose(writeFile);
|
||||
} else {
|
||||
rv = mInstallDBFile->Remove(false);
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
|
||||
rv != NS_ERROR_FILE_NOT_FOUND) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
pos += snprintf(pos, end - pos, "\n");
|
||||
|
||||
cur = cur->mNext;
|
||||
++pCount;
|
||||
}
|
||||
|
||||
FILE* writeFile;
|
||||
rv = mListFile->OpenANSIFileDesc("w", &writeFile);
|
||||
rv = mProfileDB.WriteToFile(mProfileDBFile);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
length = pos - buffer.get();
|
||||
|
||||
if (fwrite(buffer.get(), sizeof(char), length, writeFile) != length) {
|
||||
fclose(writeFile);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
fclose(writeFile);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,20 +15,20 @@
|
|||
#include "nsProfileLock.h"
|
||||
#include "nsINIParser.h"
|
||||
|
||||
class nsToolkitProfile final : public nsIToolkitProfile {
|
||||
class nsToolkitProfile final
|
||||
: public nsIToolkitProfile,
|
||||
public mozilla::LinkedListElement<RefPtr<nsToolkitProfile>> {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITOOLKITPROFILE
|
||||
|
||||
friend class nsToolkitProfileService;
|
||||
RefPtr<nsToolkitProfile> mNext;
|
||||
nsToolkitProfile* mPrev;
|
||||
|
||||
private:
|
||||
~nsToolkitProfile() = default;
|
||||
|
||||
nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||
nsIFile* aLocalDir, nsToolkitProfile* aPrev);
|
||||
nsIFile* aLocalDir, bool aFromDB);
|
||||
|
||||
nsresult RemoveInternal(bool aRemoveFiles, bool aInBackground);
|
||||
|
||||
|
@ -38,6 +38,8 @@ class nsToolkitProfile final : public nsIToolkitProfile {
|
|||
nsCOMPtr<nsIFile> mRootDir;
|
||||
nsCOMPtr<nsIFile> mLocalDir;
|
||||
nsIProfileLock* mLock;
|
||||
uint32_t mIndex;
|
||||
nsCString mSection;
|
||||
};
|
||||
|
||||
class nsToolkitProfileLock final : public nsIProfileLock {
|
||||
|
@ -103,6 +105,7 @@ class nsToolkitProfileService final : public nsIToolkitProfileService {
|
|||
bool MaybeMakeDefaultDedicatedProfile(nsIToolkitProfile* aProfile);
|
||||
bool IsSnapEnvironment();
|
||||
nsresult CreateDefaultProfile(nsIToolkitProfile** aResult);
|
||||
void SetNormalDefault(nsIToolkitProfile* aProfile);
|
||||
|
||||
// Returns the known install hashes from the installs database. Modifying the
|
||||
// installs database is safe while iterating the returned array.
|
||||
|
@ -110,8 +113,8 @@ class nsToolkitProfileService final : public nsIToolkitProfileService {
|
|||
|
||||
// Tracks whether SelectStartupProfile has been called.
|
||||
bool mStartupProfileSelected;
|
||||
// The first profile in a linked list of profiles loaded from profiles.ini.
|
||||
RefPtr<nsToolkitProfile> mFirst;
|
||||
// The profiles loaded from profiles.ini.
|
||||
mozilla::LinkedList<RefPtr<nsToolkitProfile>> mProfiles;
|
||||
// The profile selected for use at startup, if it exists in profiles.ini.
|
||||
nsCOMPtr<nsIToolkitProfile> mCurrent;
|
||||
// The profile selected for this install in installs.ini.
|
||||
|
@ -126,13 +129,13 @@ class nsToolkitProfileService final : public nsIToolkitProfileService {
|
|||
// The directory that holds the cache files for profiles.
|
||||
nsCOMPtr<nsIFile> mTempData;
|
||||
// The location of profiles.ini.
|
||||
nsCOMPtr<nsIFile> mListFile;
|
||||
nsCOMPtr<nsIFile> mProfileDBFile;
|
||||
// The location of installs.ini.
|
||||
nsCOMPtr<nsIFile> mInstallFile;
|
||||
// The data loaded from installs.ini.
|
||||
nsINIParser mInstallData;
|
||||
// The install hash for the currently running install.
|
||||
nsCString mInstallHash;
|
||||
nsCOMPtr<nsIFile> mInstallDBFile;
|
||||
// The data loaded from profiles.ini.
|
||||
nsINIParser mProfileDB;
|
||||
// The section in the profiles db for the current install.
|
||||
nsCString mInstallSection;
|
||||
// Whether to start with the selected profile by default.
|
||||
bool mStartWithLast;
|
||||
// True if during startup it appeared that this is the first run.
|
||||
|
|
|
@ -169,7 +169,7 @@ function writeProfilesIni(profileData) {
|
|||
getService(Ci.nsIINIParserFactory);
|
||||
let ini = factory.createINIParser().QueryInterface(Ci.nsIINIParserWriter);
|
||||
|
||||
const { options = {}, profiles = [] } = profileData;
|
||||
const { options = {}, profiles = [], installs = null } = profileData;
|
||||
|
||||
let { startWithLastProfile = true } = options;
|
||||
ini.setString("General", "StartWithLastProfile", startWithLastProfile ? "1" : "0");
|
||||
|
@ -187,6 +187,21 @@ function writeProfilesIni(profileData) {
|
|||
}
|
||||
}
|
||||
|
||||
if (installs) {
|
||||
ini.setString("General", "Version", "2");
|
||||
|
||||
for (let hash of Object.keys(installs)) {
|
||||
ini.setString(`Install${hash}`, "Default", installs[hash].default);
|
||||
if ("locked" in installs[hash]) {
|
||||
ini.setString(`Install${hash}`, "Locked", installs[hash].locked ? "1" : "0");
|
||||
}
|
||||
}
|
||||
|
||||
writeInstallsIni({ installs });
|
||||
} else {
|
||||
writeInstallsIni(null);
|
||||
}
|
||||
|
||||
ini.writeFile(target);
|
||||
}
|
||||
|
||||
|
@ -205,6 +220,7 @@ function readProfilesIni() {
|
|||
startWithLastProfile: true,
|
||||
},
|
||||
profiles: [],
|
||||
installs: null,
|
||||
};
|
||||
|
||||
if (!target.exists()) {
|
||||
|
@ -216,29 +232,52 @@ function readProfilesIni() {
|
|||
let ini = factory.createINIParser(target);
|
||||
|
||||
profileData.options.startWithLastProfile = safeGet(ini, "General", "StartWithLastProfile") == "1";
|
||||
if (safeGet(ini, "General", "Version") == "2") {
|
||||
profileData.installs = {};
|
||||
}
|
||||
|
||||
for (let i = 0; true; i++) {
|
||||
let section = `Profile${i}`;
|
||||
let sections = ini.getSections();
|
||||
while (sections.hasMore()) {
|
||||
let section = sections.getNext();
|
||||
|
||||
let isRelative = safeGet(ini, section, "IsRelative");
|
||||
if (isRelative === null) {
|
||||
break;
|
||||
}
|
||||
Assert.equal(isRelative, "1", "Paths should always be relative in these tests.");
|
||||
|
||||
let profile = {
|
||||
name: safeGet(ini, section, "Name"),
|
||||
path: safeGet(ini, section, "Path"),
|
||||
};
|
||||
|
||||
try {
|
||||
profile.default = ini.getString(section, "Default") == "1";
|
||||
Assert.ok(profile.default, "The Default value is only written when true.");
|
||||
} catch (e) {
|
||||
profile.default = false;
|
||||
if (section == "General") {
|
||||
continue;
|
||||
}
|
||||
|
||||
profileData.profiles.push(profile);
|
||||
if (section.startsWith("Profile")) {
|
||||
let isRelative = safeGet(ini, section, "IsRelative");
|
||||
if (isRelative === null) {
|
||||
break;
|
||||
}
|
||||
Assert.equal(isRelative, "1", "Paths should always be relative in these tests.");
|
||||
|
||||
let profile = {
|
||||
name: safeGet(ini, section, "Name"),
|
||||
path: safeGet(ini, section, "Path"),
|
||||
};
|
||||
|
||||
try {
|
||||
profile.default = ini.getString(section, "Default") == "1";
|
||||
Assert.ok(profile.default, "The Default value is only written when true.");
|
||||
} catch (e) {
|
||||
profile.default = false;
|
||||
}
|
||||
|
||||
profileData.profiles.push(profile);
|
||||
}
|
||||
|
||||
if (section.startsWith("Install")) {
|
||||
Assert.ok(profileData.installs, "Should only see an install section if the ini version was correct.");
|
||||
|
||||
profileData.installs[section.substring(7)] = {
|
||||
default: safeGet(ini, section, "Default"),
|
||||
};
|
||||
|
||||
let locked = safeGet(ini, section, "Locked");
|
||||
if (locked !== null) {
|
||||
profileData.installs[section.substring(7)].locked = locked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profileData.profiles.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
@ -255,6 +294,14 @@ function writeInstallsIni(installData) {
|
|||
let target = gDataHome.clone();
|
||||
target.append("installs.ini");
|
||||
|
||||
if (!installData) {
|
||||
try {
|
||||
target.remove(false);
|
||||
} catch (e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { installs = {} } = installData;
|
||||
|
||||
let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
|
||||
|
@ -283,6 +330,7 @@ function readInstallsIni() {
|
|||
};
|
||||
|
||||
if (!target.exists()) {
|
||||
dump("Missing installs.ini\n");
|
||||
return installData;
|
||||
}
|
||||
|
||||
|
@ -296,19 +344,37 @@ function readInstallsIni() {
|
|||
if (hash != "General") {
|
||||
installData.installs[hash] = {
|
||||
default: safeGet(ini, hash, "Default"),
|
||||
locked: safeGet(ini, hash, "Locked") == 1,
|
||||
};
|
||||
|
||||
let locked = safeGet(ini, hash, "Locked");
|
||||
if (locked !== null) {
|
||||
installData.installs[hash].locked = locked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return installData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the backup data in installs.ini matches the install data in
|
||||
* profiles.ini.
|
||||
*/
|
||||
function checkBackup(profileData = readProfilesIni(), installData = readInstallsIni()) {
|
||||
if (!profileData.installs) {
|
||||
// If the profiles db isn't of the right version we wouldn't expect the
|
||||
// backup to be accurate.
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.deepEqual(profileData.installs, installData.installs, "Backup installs.ini should match installs in profiles.ini");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the profile service seems to have the right data in it compared
|
||||
* to profile and install data structured as in the above functions.
|
||||
*/
|
||||
function checkProfileService(profileData = readProfilesIni(), installData = readInstallsIni()) {
|
||||
function checkProfileService(profileData = readProfilesIni(), verifyBackup = true) {
|
||||
let service = getProfileService();
|
||||
|
||||
let serviceProfiles = Array.from(service.profiles);
|
||||
|
@ -320,7 +386,8 @@ function checkProfileService(profileData = readProfilesIni(), installData = read
|
|||
profileData.profiles.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
let defaultPath = hash in installData.installs ? installData.installs[hash].default : null;
|
||||
let defaultPath = (profileData.installs && hash in profileData.installs) ?
|
||||
profileData.installs[hash].default : null;
|
||||
let dedicatedProfile = null;
|
||||
let snapProfile = null;
|
||||
|
||||
|
@ -352,6 +419,10 @@ function checkProfileService(profileData = readProfilesIni(), installData = read
|
|||
} else {
|
||||
Assert.equal(service.defaultProfile, dedicatedProfile, "Should have seen the right profile selected.");
|
||||
}
|
||||
|
||||
if (verifyBackup) {
|
||||
checkBackup(profileData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Tests that when the profiles DB is missing the install data we reload it.
|
||||
*/
|
||||
|
||||
add_task(async () => {
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
|
||||
let profileData = {
|
||||
options: {
|
||||
startWithLastProfile: true,
|
||||
},
|
||||
profiles: [{
|
||||
name: "Profile1",
|
||||
path: "Path1",
|
||||
}, {
|
||||
name: "Profile2",
|
||||
path: "Path2",
|
||||
}],
|
||||
};
|
||||
|
||||
let installs = {
|
||||
[hash]: {
|
||||
default: "Path2",
|
||||
},
|
||||
};
|
||||
|
||||
writeProfilesIni(profileData);
|
||||
writeInstallsIni({ installs });
|
||||
|
||||
let { profile, didCreate } = selectStartupProfile();
|
||||
checkStartupReason("default");
|
||||
|
||||
let service = getProfileService();
|
||||
// Should have added the backup data to the service, check that is true.
|
||||
profileData.installs = installs;
|
||||
checkProfileService(profileData);
|
||||
|
||||
Assert.ok(!didCreate, "Should not have created a new profile.");
|
||||
Assert.equal(profile.name, "Profile2", "Should have selected the right profile");
|
||||
Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
|
||||
});
|
|
@ -14,10 +14,6 @@ add_task(async () => {
|
|||
path: defaultProfile.leafName,
|
||||
default: true,
|
||||
}],
|
||||
});
|
||||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
writeProfilesIni({
|
||||
installs: {
|
||||
other: {
|
||||
default: defaultProfile.leafName,
|
||||
|
@ -29,16 +25,16 @@ add_task(async () => {
|
|||
let { profile: selectedProfile, didCreate } = selectStartupProfile();
|
||||
|
||||
let profileData = readProfilesIni();
|
||||
let installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 2, "Should have the right number of profiles.");
|
||||
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be two known installs.");
|
||||
Assert.notEqual(installData.installs[hash].default, defaultProfile.leafName, "Should not have marked the original default profile as the default for this install.");
|
||||
Assert.ok(installData.installs[hash].locked, "Should have locked as we created this profile for this install.");
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
Assert.equal(Object.keys(profileData.installs).length, 2, "Should be two known installs.");
|
||||
Assert.notEqual(profileData.installs[hash].default, defaultProfile.leafName, "Should not have marked the original default profile as the default for this install.");
|
||||
Assert.ok(profileData.installs[hash].locked, "Should have locked as we created this profile for this install.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
Assert.ok(didCreate, "Should have created a new profile.");
|
||||
Assert.ok(!selectedProfile.rootDir.equals(defaultProfile), "Should be using a different directory.");
|
||||
|
|
|
@ -18,7 +18,6 @@ add_task(async () => {
|
|||
service.flush();
|
||||
|
||||
let profileData = readProfilesIni();
|
||||
let installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
|
||||
|
@ -28,15 +27,14 @@ add_task(async () => {
|
|||
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||
|
||||
// The new profile hasn't been marked as the default yet!
|
||||
Assert.equal(Object.keys(installData.installs).length, 0, "Should be no defaults for installs yet.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 0, "Should be no defaults for installs yet.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
service.defaultProfile = newProfile;
|
||||
service.flush();
|
||||
|
||||
profileData = readProfilesIni();
|
||||
installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
|
||||
|
@ -46,10 +44,10 @@ add_task(async () => {
|
|||
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(installData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(profileData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
let otherProfile = service.createProfile(null, "another");
|
||||
service.defaultProfile = otherProfile;
|
||||
|
@ -57,7 +55,6 @@ add_task(async () => {
|
|||
service.flush();
|
||||
|
||||
profileData = readProfilesIni();
|
||||
installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 2, "Should have the right number of profiles.");
|
||||
|
@ -70,16 +67,15 @@ add_task(async () => {
|
|||
Assert.equal(profile.name, "dedicated", "Should have the right name.");
|
||||
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(installData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(profileData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
newProfile.remove(true);
|
||||
service.flush();
|
||||
|
||||
profileData = readProfilesIni();
|
||||
installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
|
||||
|
@ -88,23 +84,22 @@ add_task(async () => {
|
|||
Assert.equal(profile.name, "another", "Should have the right name.");
|
||||
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(installData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(profileData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this install.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
otherProfile.remove(true);
|
||||
service.flush();
|
||||
|
||||
profileData = readProfilesIni();
|
||||
installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 0, "Should have the right number of profiles.");
|
||||
|
||||
// We leave a reference to the missing profile to stop us trying to steal the
|
||||
// old-style default profile on next startup.
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Should be only one known install.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
});
|
||||
|
|
|
@ -9,8 +9,7 @@ add_task(async () => {
|
|||
checkStartupReason("firstrun-created-default");
|
||||
|
||||
let profileData = readProfilesIni();
|
||||
let installData = readInstallsIni();
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
Assert.ok(didCreate, "Should have created a new profile.");
|
||||
Assert.equal(profile, service.defaultProfile, "Should now be the default profile.");
|
||||
|
@ -29,5 +28,5 @@ add_task(async () => {
|
|||
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
Assert.ok(installData.installs[hash].locked, "Should have locked the profile");
|
||||
Assert.ok(profileData.installs[hash].locked, "Should have locked the profile");
|
||||
});
|
||||
|
|
|
@ -21,7 +21,6 @@ add_task(async () => {
|
|||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
let profileData = readProfilesIni();
|
||||
let installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
|
||||
|
@ -31,11 +30,11 @@ add_task(async () => {
|
|||
Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
|
||||
Assert.ok(profile.default, "Should be marked as the old-style default.");
|
||||
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(installData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.ok(installData.installs[hash].locked, "Should have locked as we're the default app.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(profileData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.ok(profileData.installs[hash].locked, "Should have locked as we're the default app.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
Assert.ok(!didCreate, "Should not have created a new profile.");
|
||||
Assert.ok(selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* When profiles.ini is missing there isn't any point in restoring from any
|
||||
* installs.ini, the profiles it refers to are gone anyway.
|
||||
*/
|
||||
|
||||
add_task(async () => {
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
|
||||
let installs = {
|
||||
[hash]: {
|
||||
default: "Path2",
|
||||
},
|
||||
otherhash: {
|
||||
default: "foo",
|
||||
},
|
||||
anotherhash: {
|
||||
default: "bar",
|
||||
},
|
||||
};
|
||||
|
||||
writeInstallsIni({ installs });
|
||||
|
||||
let { profile, didCreate } = selectStartupProfile();
|
||||
checkStartupReason("firstrun-created-default");
|
||||
|
||||
let service = getProfileService();
|
||||
Assert.ok(didCreate, "Should have created a new profile.");
|
||||
Assert.equal(profile.name, DEDICATED_NAME, "Should have created the right profile");
|
||||
Assert.ok(!service.createdAlternateProfile, "Should not have created an alternate profile.");
|
||||
|
||||
let profilesData = readProfilesIni();
|
||||
Assert.equal(Object.keys(profilesData.installs).length, 1, "Should be only one known install");
|
||||
Assert.ok(hash in profilesData.installs, "Should be the expected install.");
|
||||
Assert.notEqual(profilesData.installs[hash].default, "Path2", "Didn't import the previous data.");
|
||||
|
||||
checkProfileService(profilesData);
|
||||
});
|
|
@ -31,7 +31,6 @@ add_task(async () => {
|
|||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
let profileData = readProfilesIni();
|
||||
let installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 3, "Should have the right number of profiles.");
|
||||
|
@ -51,16 +50,16 @@ add_task(async () => {
|
|||
Assert.equal(profile.path, mydefaultProfile.leafName, "Should be the original default profile.");
|
||||
Assert.ok(profile.default, "Should be marked as the old-style default.");
|
||||
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Should be only one known install.");
|
||||
if (AppConstants.MOZ_DEV_EDITION) {
|
||||
Assert.equal(installData.installs[hash].default, devDefaultProfile.leafName, "Should have marked the original dev default profile as the default for this install.");
|
||||
Assert.equal(profileData.installs[hash].default, devDefaultProfile.leafName, "Should have marked the original dev default profile as the default for this install.");
|
||||
} else {
|
||||
Assert.equal(installData.installs[hash].default, mydefaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.equal(profileData.installs[hash].default, mydefaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
}
|
||||
|
||||
Assert.ok(!installData.installs[hash].locked, "Should not be locked as we're not the default app.");
|
||||
Assert.ok(!profileData.installs[hash].locked, "Should not be locked as we're not the default app.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
Assert.ok(!didCreate, "Should not have created a new profile.");
|
||||
if (AppConstants.MOZ_DEV_EDITION) {
|
||||
|
|
|
@ -16,9 +16,6 @@ add_task(async () => {
|
|||
path: defaultProfile.leafName,
|
||||
default: true,
|
||||
}],
|
||||
});
|
||||
|
||||
writeInstallsIni({
|
||||
installs: {
|
||||
[hash]: {
|
||||
default: "foobar",
|
||||
|
@ -30,7 +27,6 @@ add_task(async () => {
|
|||
testStartsProfileManager();
|
||||
|
||||
let profileData = readProfilesIni();
|
||||
let installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
|
||||
|
@ -42,7 +38,7 @@ add_task(async () => {
|
|||
Assert.ok(profile.default, "Should be marked as the old-style default.");
|
||||
|
||||
// We keep the data here so we don't steal on the next reboot...
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Still list the broken reference.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Still list the broken reference.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Tests adding and removing functions correctly.
|
||||
*/
|
||||
|
||||
function compareLists(service, knownProfiles) {
|
||||
Assert.equal(service.profileCount, knownProfiles.length, "profileCount should be correct.");
|
||||
let serviceProfiles = Array.from(service.profiles);
|
||||
Assert.equal(serviceProfiles.length, knownProfiles.length, "Enumerator length should be correct.");
|
||||
|
||||
for (let i = 0; i < knownProfiles.length; i++) {
|
||||
// Cannot use strictEqual here, it attempts to print out a string
|
||||
// representation of the profile objects and on some platforms that recurses
|
||||
// infinitely.
|
||||
Assert.ok(serviceProfiles[i] === knownProfiles[i], `Should have the right profile in position ${i}.`);
|
||||
}
|
||||
}
|
||||
|
||||
function removeProfile(profiles, position) {
|
||||
dump(`Removing profile in position ${position}.`);
|
||||
Assert.greaterOrEqual(position, 0, "Should be removing a valid position.");
|
||||
Assert.less(position, profiles.length, "Should be removing a valid position.");
|
||||
|
||||
let last = profiles.pop();
|
||||
|
||||
if (profiles.length == position) {
|
||||
// We were asked to remove the last profile.
|
||||
last.remove(false);
|
||||
return;
|
||||
}
|
||||
|
||||
profiles[position].remove(false);
|
||||
profiles[position] = last;
|
||||
}
|
||||
|
||||
add_task(async () => {
|
||||
let service = getProfileService();
|
||||
let profiles = [];
|
||||
compareLists(service, profiles);
|
||||
|
||||
profiles.push(service.createProfile(null, "profile1"));
|
||||
profiles.push(service.createProfile(null, "profile2"));
|
||||
profiles.push(service.createProfile(null, "profile3"));
|
||||
profiles.push(service.createProfile(null, "profile4"));
|
||||
profiles.push(service.createProfile(null, "profile5"));
|
||||
profiles.push(service.createProfile(null, "profile6"));
|
||||
profiles.push(service.createProfile(null, "profile7"));
|
||||
profiles.push(service.createProfile(null, "profile8"));
|
||||
profiles.push(service.createProfile(null, "profile9"));
|
||||
compareLists(service, profiles);
|
||||
|
||||
// Test removing the first profile.
|
||||
removeProfile(profiles, 0);
|
||||
compareLists(service, profiles);
|
||||
|
||||
// And the last profile.
|
||||
removeProfile(profiles, profiles.length - 1);
|
||||
compareLists(service, profiles);
|
||||
|
||||
// Last but one...
|
||||
removeProfile(profiles, profiles.length - 2);
|
||||
compareLists(service, profiles);
|
||||
|
||||
// Second one...
|
||||
removeProfile(profiles, 1);
|
||||
compareLists(service, profiles);
|
||||
|
||||
// Something in the middle.
|
||||
removeProfile(profiles, 2);
|
||||
compareLists(service, profiles);
|
||||
|
||||
let expectedNames = [
|
||||
"profile9",
|
||||
"profile7",
|
||||
"profile5",
|
||||
"profile4",
|
||||
];
|
||||
|
||||
let serviceProfiles = Array.from(service.profiles);
|
||||
for (let i = 0; i < expectedNames.length; i++) {
|
||||
Assert.equal(serviceProfiles[i].name, expectedNames[i]);
|
||||
}
|
||||
|
||||
removeProfile(profiles, 0);
|
||||
removeProfile(profiles, 0);
|
||||
removeProfile(profiles, 0);
|
||||
removeProfile(profiles, 0);
|
||||
|
||||
Assert.equal(Array.from(service.profiles).length, 0, "All profiles gone.");
|
||||
Assert.equal(service.profileCount, 0, "All profiles gone.");
|
||||
});
|
|
@ -13,27 +13,23 @@ add_task(async () => {
|
|||
path: defaultProfile.leafName,
|
||||
default: true,
|
||||
}],
|
||||
};
|
||||
writeProfilesIni(profilesIni);
|
||||
|
||||
let installsIni = {
|
||||
installs: {
|
||||
[hash]: {
|
||||
default: defaultProfile.leafName,
|
||||
},
|
||||
},
|
||||
};
|
||||
writeInstallsIni(installsIni);
|
||||
writeProfilesIni(profilesIni);
|
||||
|
||||
let service = getProfileService();
|
||||
checkProfileService(profilesIni, installsIni);
|
||||
checkProfileService(profilesIni);
|
||||
|
||||
let { profile, didCreate } = selectStartupProfile();
|
||||
Assert.ok(!didCreate, "Should have not created a new profile.");
|
||||
Assert.equal(profile.name, "default", "Should have selected the default profile.");
|
||||
Assert.equal(profile, service.defaultProfile, "Should have selected the default profile.");
|
||||
|
||||
checkProfileService(profilesIni, installsIni);
|
||||
checkProfileService(profilesIni);
|
||||
|
||||
// In an actual run of Firefox we wouldn't be able to delete the profile in
|
||||
// use because it would be locked. But we don't actually lock the profile in
|
||||
|
@ -45,9 +41,10 @@ add_task(async () => {
|
|||
|
||||
// These are the modifications that should have been made.
|
||||
profilesIni.profiles.pop();
|
||||
installsIni.installs[hash].default = "";
|
||||
profilesIni.installs[hash].default = "";
|
||||
|
||||
checkProfileService(profilesIni, installsIni);
|
||||
// The data isn't flushed to disk so don't check the backup here.
|
||||
checkProfileService(profilesIni, false);
|
||||
|
||||
service.flush();
|
||||
|
||||
|
@ -56,6 +53,6 @@ add_task(async () => {
|
|||
|
||||
// checkProfileService doesn't differentiate between a blank default profile
|
||||
// for the install and a missing install.
|
||||
let installs = readInstallsIni();
|
||||
Assert.equal(installs.installs[hash].default, "", "Should be a blank default profile.");
|
||||
profilesIni = readProfilesIni();
|
||||
Assert.equal(profilesIni.installs[hash].default, "", "Should be a blank default profile.");
|
||||
});
|
||||
|
|
|
@ -16,8 +16,6 @@ add_task(async () => {
|
|||
name: "Profile3",
|
||||
path: "Path3",
|
||||
}],
|
||||
};
|
||||
let installData = {
|
||||
installs: {
|
||||
[hash]: {
|
||||
default: "Path2",
|
||||
|
@ -43,7 +41,6 @@ add_task(async () => {
|
|||
}
|
||||
|
||||
writeProfilesIni(profileData);
|
||||
writeInstallsIni(installData);
|
||||
|
||||
let { profile, didCreate } = selectStartupProfile();
|
||||
checkStartupReason("default");
|
||||
|
|
|
@ -22,7 +22,6 @@ add_task(async () => {
|
|||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
let profileData = readProfilesIni();
|
||||
let installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
|
||||
|
@ -32,11 +31,11 @@ add_task(async () => {
|
|||
Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
|
||||
Assert.ok(profile.default, "Should be marked as the old-style default.");
|
||||
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(installData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.ok(!installData.installs[hash].locked, "Should not have locked as we're not the default app.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Should be only one known install.");
|
||||
Assert.equal(profileData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.ok(!profileData.installs[hash].locked, "Should not have locked as we're not the default app.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
Assert.ok(!didCreate, "Should not have created a new profile.");
|
||||
let service = getProfileService();
|
||||
|
|
|
@ -23,7 +23,6 @@ add_task(async () => {
|
|||
let service = getProfileService();
|
||||
|
||||
let profileData = readProfilesIni();
|
||||
let installData = readInstallsIni();
|
||||
|
||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
||||
Assert.equal(profileData.profiles.length, 1, "Should have the right number of profiles.");
|
||||
|
@ -33,9 +32,9 @@ add_task(async () => {
|
|||
Assert.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
|
||||
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||
|
||||
Assert.equal(Object.keys(installData.installs).length, 0, "Should be no defaults for installs yet.");
|
||||
Assert.ok(!profileData.installs, "Should be no defaults for installs yet.");
|
||||
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
let { profile: selectedProfile, didCreate } = selectStartupProfile();
|
||||
checkStartupReason("firstrun-skipped-default");
|
||||
|
|
|
@ -26,10 +26,7 @@ add_task(async () => {
|
|||
name: "Profile3",
|
||||
path: "Path3",
|
||||
}],
|
||||
};
|
||||
|
||||
// Another install is using the profile and it is locked.
|
||||
let installData = {
|
||||
// Another install is using the profile and it is locked.
|
||||
installs: {
|
||||
otherinstall: {
|
||||
default: root.leafName,
|
||||
|
@ -39,8 +36,7 @@ add_task(async () => {
|
|||
};
|
||||
|
||||
writeProfilesIni(profileData);
|
||||
writeInstallsIni(installData);
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
let env = Cc["@mozilla.org/process/environment;1"].
|
||||
getService(Ci.nsIEnvironment);
|
||||
|
@ -73,10 +69,9 @@ add_task(async () => {
|
|||
Assert.ok(!profileData.profiles[1].default, "Should not be the old default profile.");
|
||||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
installData = readInstallsIni();
|
||||
Assert.equal(Object.keys(installData.installs).length, 2, "Should be one known install.");
|
||||
Assert.notEqual(installData.installs[hash].default, root.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.ok(installData.installs[hash].locked, "Should have locked as we created the profile for this install.");
|
||||
Assert.equal(installData.installs.otherinstall.default, root.leafName, "Should have left the other profile as the default for the other install.");
|
||||
Assert.ok(installData.installs[hash].locked, "Should still be locked to the other install.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 2, "Should be one known install.");
|
||||
Assert.notEqual(profileData.installs[hash].default, root.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.ok(profileData.installs[hash].locked, "Should have locked as we created the profile for this install.");
|
||||
Assert.equal(profileData.installs.otherinstall.default, root.leafName, "Should have left the other profile as the default for the other install.");
|
||||
Assert.ok(profileData.installs[hash].locked, "Should still be locked to the other install.");
|
||||
});
|
||||
|
|
|
@ -26,10 +26,7 @@ add_task(async () => {
|
|||
name: "Profile3",
|
||||
path: "Path3",
|
||||
}],
|
||||
};
|
||||
|
||||
// Another install is using the profile but it isn't locked.
|
||||
let installData = {
|
||||
// Another install is using the profile but it isn't locked.
|
||||
installs: {
|
||||
otherinstall: {
|
||||
default: root.leafName,
|
||||
|
@ -38,8 +35,7 @@ add_task(async () => {
|
|||
};
|
||||
|
||||
writeProfilesIni(profileData);
|
||||
writeInstallsIni(installData);
|
||||
checkProfileService(profileData, installData);
|
||||
checkProfileService(profileData);
|
||||
|
||||
let env = Cc["@mozilla.org/process/environment;1"].
|
||||
getService(Ci.nsIEnvironment);
|
||||
|
@ -64,9 +60,8 @@ add_task(async () => {
|
|||
Assert.ok(profileData.profiles[0].default, "Should still be the old default profile.");
|
||||
|
||||
let hash = xreDirProvider.getInstallHash();
|
||||
installData = readInstallsIni();
|
||||
// The info about the other install will have been removed so it goes through first run on next startup.
|
||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be one known install.");
|
||||
Assert.equal(installData.installs[hash].default, root.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.ok(!installData.installs[hash].locked, "Should not have locked as we're not the default app.");
|
||||
Assert.equal(Object.keys(profileData.installs).length, 1, "Should be one known install.");
|
||||
Assert.equal(profileData.installs[hash].default, root.leafName, "Should have marked the original default profile as the default for this install.");
|
||||
Assert.ok(!profileData.installs[hash].locked, "Should not have locked as we're not the default app.");
|
||||
});
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче