зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
47e94b5d6e
|
@ -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"):
|
with Files("LightweightThemeChild.jsm"):
|
||||||
BUG_COMPONENT = ("WebExtensions", "Themes")
|
BUG_COMPONENT = ("WebExtensions", "Themes")
|
||||||
|
|
||||||
with Files("LightWeightThemeInstallChild.jsm"):
|
|
||||||
BUG_COMPONENT = ("Firefox", "Theme")
|
|
||||||
|
|
||||||
with Files("PageInfoChild.jsm"):
|
with Files("PageInfoChild.jsm"):
|
||||||
BUG_COMPONENT = ("Firefox", "Page Info Window")
|
BUG_COMPONENT = ("Firefox", "Page Info Window")
|
||||||
|
|
||||||
|
@ -35,7 +32,6 @@ FINAL_TARGET_FILES.actors += [
|
||||||
'DOMFullscreenChild.jsm',
|
'DOMFullscreenChild.jsm',
|
||||||
'FormValidationChild.jsm',
|
'FormValidationChild.jsm',
|
||||||
'LightweightThemeChild.jsm',
|
'LightweightThemeChild.jsm',
|
||||||
'LightWeightThemeInstallChild.jsm',
|
|
||||||
'LinkHandlerChild.jsm',
|
'LinkHandlerChild.jsm',
|
||||||
'NetErrorChild.jsm',
|
'NetErrorChild.jsm',
|
||||||
'OfflineAppsChild.jsm',
|
'OfflineAppsChild.jsm',
|
||||||
|
|
|
@ -184,7 +184,6 @@ pref("extensions.webextensions.themes.icons.buttons", "back,forward,reload,stop,
|
||||||
|
|
||||||
pref("lightweightThemes.update.enabled", true);
|
pref("lightweightThemes.update.enabled", true);
|
||||||
pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes");
|
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)
|
#if defined(MOZ_WIDEVINE_EME)
|
||||||
pref("browser.eme.ui.enabled", true);
|
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
|
* 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/. */
|
* 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.
|
* Enables compacttheme.css when needed.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,13 +6,21 @@
|
||||||
/* eslint-env mozilla/browser-window */
|
/* 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.
|
* Trigger a WebRender capture of the current state into a local folder.
|
||||||
*/
|
*/
|
||||||
capture() {
|
webrenderCapture() {
|
||||||
window.windowUtils.wrCapture();
|
window.windowUtils.wrCapture();
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -96,7 +96,8 @@
|
||||||
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
|
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
|
||||||
|
|
||||||
#ifdef NIGHTLY_BUILD
|
#ifdef NIGHTLY_BUILD
|
||||||
<command id="wrCaptureCmd" oncommand="gWebRender.capture();"/>
|
<command id="wrCaptureCmd" oncommand="gGfxUtils.webrenderCapture();"/>
|
||||||
|
<command id="windowRecordingCmd" oncommand="gGfxUtils.toggleWindowRecording();"/>
|
||||||
#endif
|
#endif
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
<command id="minimizeWindow"
|
<command id="minimizeWindow"
|
||||||
|
@ -317,6 +318,13 @@
|
||||||
key="#" modifiers="control"
|
key="#" modifiers="control"
|
||||||
#endif
|
#endif
|
||||||
command="wrCaptureCmd"/>
|
command="wrCaptureCmd"/>
|
||||||
|
<key id="key_windowRecordingCmd"
|
||||||
|
#ifdef XP_MACOSX
|
||||||
|
key="4" modifiers="control,shift"
|
||||||
|
#else
|
||||||
|
key="$" modifiers="control"
|
||||||
|
#endif
|
||||||
|
command="windowRecordingCmd"/>
|
||||||
#endif
|
#endif
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
<key id="key_minimizeWindow"
|
<key id="key_minimizeWindow"
|
||||||
|
|
|
@ -31,7 +31,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||||
FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
|
FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
|
||||||
HomePage: "resource:///modules/HomePage.jsm",
|
HomePage: "resource:///modules/HomePage.jsm",
|
||||||
LightweightThemeConsumer: "resource://gre/modules/LightweightThemeConsumer.jsm",
|
LightweightThemeConsumer: "resource://gre/modules/LightweightThemeConsumer.jsm",
|
||||||
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
|
|
||||||
Log: "resource://gre/modules/Log.jsm",
|
Log: "resource://gre/modules/Log.jsm",
|
||||||
LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
|
LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
|
||||||
MigrationUtils: "resource:///modules/MigrationUtils.jsm",
|
MigrationUtils: "resource:///modules/MigrationUtils.jsm",
|
||||||
|
@ -102,8 +101,7 @@ XPCOMUtils.defineLazyScriptGetter(this, "gViewSourceUtils",
|
||||||
"chrome://global/content/viewSourceUtils.js");
|
"chrome://global/content/viewSourceUtils.js");
|
||||||
XPCOMUtils.defineLazyScriptGetter(this, "gTabsPanel",
|
XPCOMUtils.defineLazyScriptGetter(this, "gTabsPanel",
|
||||||
"chrome://browser/content/browser-allTabsMenu.js");
|
"chrome://browser/content/browser-allTabsMenu.js");
|
||||||
XPCOMUtils.defineLazyScriptGetter(this, ["LightWeightThemeWebInstaller",
|
XPCOMUtils.defineLazyScriptGetter(this, ["gExtensionsNotifications",
|
||||||
"gExtensionsNotifications",
|
|
||||||
"gXPInstallObserver"],
|
"gXPInstallObserver"],
|
||||||
"chrome://browser/content/browser-addons.js");
|
"chrome://browser/content/browser-addons.js");
|
||||||
XPCOMUtils.defineLazyScriptGetter(this, "ctrlTab",
|
XPCOMUtils.defineLazyScriptGetter(this, "ctrlTab",
|
||||||
|
@ -141,8 +139,8 @@ XPCOMUtils.defineLazyScriptGetter(this, "gEditItemOverlay",
|
||||||
XPCOMUtils.defineLazyScriptGetter(this, "SearchOneOffs",
|
XPCOMUtils.defineLazyScriptGetter(this, "SearchOneOffs",
|
||||||
"chrome://browser/content/search/search-one-offs.js");
|
"chrome://browser/content/search/search-one-offs.js");
|
||||||
if (AppConstants.NIGHTLY_BUILD) {
|
if (AppConstants.NIGHTLY_BUILD) {
|
||||||
XPCOMUtils.defineLazyScriptGetter(this, "gWebRender",
|
XPCOMUtils.defineLazyScriptGetter(this, "gGfxUtils",
|
||||||
"chrome://browser/content/browser-webrender.js");
|
"chrome://browser/content/browser-graphics-utils.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
XPCOMUtils.defineLazyScriptGetter(this, "pktUI", "chrome://pocket/content/main.js");
|
XPCOMUtils.defineLazyScriptGetter(this, "pktUI", "chrome://pocket/content/main.js");
|
||||||
|
@ -1654,8 +1652,6 @@ var gBrowserInit = {
|
||||||
placesContext.addEventListener("popuphiding", updateEditUIVisibility);
|
placesContext.addEventListener("popuphiding", updateEditUIVisibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
LightWeightThemeWebInstaller.init();
|
|
||||||
|
|
||||||
FullScreen.init();
|
FullScreen.init();
|
||||||
PointerLock.init();
|
PointerLock.init();
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,20 @@
|
||||||
* themes are applied.
|
* themes are applied.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const DEFAULT_THEME = "default-theme@mozilla.org";
|
||||||
const PREF_LWTHEME_USED_THEMES = "lightweightThemes.usedThemes";
|
const PREF_LWTHEME_USED_THEMES = "lightweightThemes.usedThemes";
|
||||||
const COMPACT_LIGHT_ID = "firefox-compact-light@mozilla.org";
|
const COMPACT_LIGHT_ID = "firefox-compact-light@mozilla.org";
|
||||||
const COMPACT_DARK_ID = "firefox-compact-dark@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(() => {
|
registerCleanupFunction(() => {
|
||||||
// Set preferences back to their original values
|
// Set preferences back to their original values
|
||||||
LightweightThemeManager.currentTheme = null;
|
return selectTheme(null);
|
||||||
Services.prefs.clearUserPref(PREF_LWTHEME_USED_THEMES);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
|
@ -22,70 +27,22 @@ function tick() {
|
||||||
|
|
||||||
add_task(async function startTests() {
|
add_task(async function startTests() {
|
||||||
info("Setting the current theme to null");
|
info("Setting the current theme to null");
|
||||||
LightweightThemeManager.currentTheme = null;
|
await selectTheme(null);
|
||||||
await tick();
|
await tick();
|
||||||
ok(!CompactTheme.isStyleSheetEnabled, "There is no compact style sheet when no lw theme is applied.");
|
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.");
|
info("Applying the dark compact theme.");
|
||||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme(COMPACT_DARK_ID);
|
await selectTheme(COMPACT_DARK_ID);
|
||||||
await tick();
|
await tick();
|
||||||
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet has been added when the compact lightweight theme is applied");
|
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet has been added when the compact lightweight theme is applied");
|
||||||
|
|
||||||
info("Applying the light compact theme.");
|
info("Applying the light compact theme.");
|
||||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme(COMPACT_LIGHT_ID);
|
await selectTheme(COMPACT_LIGHT_ID);
|
||||||
await tick();
|
await tick();
|
||||||
ok(CompactTheme.isStyleSheetEnabled, "The compact stylesheet has been added when the compact lightweight theme is applied");
|
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.");
|
info("Unapplying all themes.");
|
||||||
LightweightThemeManager.currentTheme = null;
|
await selectTheme(null);
|
||||||
await tick();
|
await tick();
|
||||||
ok(!CompactTheme.isStyleSheetEnabled, "There is no compact style sheet when no lw theme is applied.");
|
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
|
* 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/. */
|
* 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(() => {
|
registerCleanupFunction(() => {
|
||||||
LightweightThemeManager.currentTheme = null;
|
return selectTheme(null);
|
||||||
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function withoutLWT() {
|
add_task(async function withoutLWT() {
|
||||||
|
@ -16,7 +23,7 @@ add_task(async function withoutLWT() {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function withLWT() {
|
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();
|
let win = await BrowserTestUtils.openNewBrowserWindow();
|
||||||
ok(!win.gBrowser.tabContainer.hasAttribute("overflow"), "tab container not overflowing");
|
ok(!win.gBrowser.tabContainer.hasAttribute("overflow"), "tab container not overflowing");
|
||||||
ok(win.gBrowser.tabContainer.arrowScrollbox.hasAttribute("notoverflowing"), "arrow scrollbox 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-tabsintitlebar.js (content/browser-tabsintitlebar.js)
|
||||||
content/browser/browser-toolbarKeyNav.js (content/browser-toolbarKeyNav.js)
|
content/browser/browser-toolbarKeyNav.js (content/browser-toolbarKeyNav.js)
|
||||||
content/browser/browser-thumbnails.js (content/browser-thumbnails.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/tab-content.js (content/tab-content.js)
|
||||||
content/browser/content.js (content/content.js)
|
content/browser/content.js (content/content.js)
|
||||||
content/browser/defaultthemes/1.header.jpg (content/defaultthemes/1.header.jpg)
|
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: {
|
LightweightTheme: {
|
||||||
child: {
|
child: {
|
||||||
module: "resource:///actors/LightweightThemeChild.jsm",
|
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");
|
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||||
|
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
||||||
|
AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
|
||||||
SearchWidgetTracker: "resource:///modules/SearchWidgetTracker.jsm",
|
SearchWidgetTracker: "resource:///modules/SearchWidgetTracker.jsm",
|
||||||
CustomizableWidgets: "resource:///modules/CustomizableWidgets.jsm",
|
CustomizableWidgets: "resource:///modules/CustomizableWidgets.jsm",
|
||||||
DeferredTask: "resource://gre/modules/DeferredTask.jsm",
|
DeferredTask: "resource://gre/modules/DeferredTask.jsm",
|
||||||
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
|
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
|
||||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||||
ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
|
ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
|
||||||
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
|
XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
|
||||||
|
@ -27,6 +28,8 @@ XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "gELS",
|
XPCOMUtils.defineLazyServiceGetter(this, "gELS",
|
||||||
"@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
|
"@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
|
||||||
|
|
||||||
|
const kDefaultThemeID = "default-theme@mozilla.org";
|
||||||
|
|
||||||
const kSpecialWidgetPfx = "customizableui-special-";
|
const kSpecialWidgetPfx = "customizableui-special-";
|
||||||
|
|
||||||
const kPrefCustomizationState = "browser.uiCustomization.state";
|
const kPrefCustomizationState = "browser.uiCustomization.state";
|
||||||
|
@ -40,6 +43,9 @@ const kPrefAutoHideDownloadsButton = "browser.download.autohideButton";
|
||||||
|
|
||||||
const kExpectedWindowURL = AppConstants.BROWSER_CHROME_URL;
|
const kExpectedWindowURL = AppConstants.BROWSER_CHROME_URL;
|
||||||
|
|
||||||
|
var gDefaultTheme;
|
||||||
|
var gSelectedTheme;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The keys are the handlers that are fired when the event type (the value)
|
* 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
|
* is fired on the subview. A widget that provides a subview has the option
|
||||||
|
@ -178,6 +184,11 @@ var CustomizableUIInternal = {
|
||||||
initialize() {
|
initialize() {
|
||||||
log.debug("Initializing");
|
log.debug("Initializing");
|
||||||
|
|
||||||
|
Services.obs.addObserver(this, "xpi-database-loaded");
|
||||||
|
if (AddonManagerPrivate.isDBLoaded()) {
|
||||||
|
this.observe(null, "xpi-database-loaded");
|
||||||
|
}
|
||||||
|
|
||||||
this.addListener(this);
|
this.addListener(this);
|
||||||
this._defineBuiltInWidgets();
|
this._defineBuiltInWidgets();
|
||||||
this.loadSavedState();
|
this.loadSavedState();
|
||||||
|
@ -245,6 +256,22 @@ var CustomizableUIInternal = {
|
||||||
SearchWidgetTracker.init();
|
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() {
|
get _builtinAreas() {
|
||||||
return new Set([
|
return new Set([
|
||||||
...this._builtinToolbars,
|
...this._builtinToolbars,
|
||||||
|
@ -2652,7 +2679,7 @@ var CustomizableUIInternal = {
|
||||||
gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
|
gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState);
|
||||||
gUIStateBeforeReset.uiDensity = Services.prefs.getIntPref(kPrefUIDensity);
|
gUIStateBeforeReset.uiDensity = Services.prefs.getIntPref(kPrefUIDensity);
|
||||||
gUIStateBeforeReset.autoTouchMode = Services.prefs.getBoolPref(kPrefAutoTouchMode);
|
gUIStateBeforeReset.autoTouchMode = Services.prefs.getBoolPref(kPrefAutoTouchMode);
|
||||||
gUIStateBeforeReset.currentTheme = LightweightThemeManager.currentTheme;
|
gUIStateBeforeReset.currentTheme = gSelectedTheme;
|
||||||
gUIStateBeforeReset.autoHideDownloadsButton = Services.prefs.getBoolPref(kPrefAutoHideDownloadsButton);
|
gUIStateBeforeReset.autoHideDownloadsButton = Services.prefs.getBoolPref(kPrefAutoHideDownloadsButton);
|
||||||
gUIStateBeforeReset.newElementCount = gNewElementCount;
|
gUIStateBeforeReset.newElementCount = gNewElementCount;
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
|
@ -2663,7 +2690,7 @@ var CustomizableUIInternal = {
|
||||||
Services.prefs.clearUserPref(kPrefUIDensity);
|
Services.prefs.clearUserPref(kPrefUIDensity);
|
||||||
Services.prefs.clearUserPref(kPrefAutoTouchMode);
|
Services.prefs.clearUserPref(kPrefAutoTouchMode);
|
||||||
Services.prefs.clearUserPref(kPrefAutoHideDownloadsButton);
|
Services.prefs.clearUserPref(kPrefAutoHideDownloadsButton);
|
||||||
LightweightThemeManager.currentTheme = null;
|
gDefaultTheme.enable();
|
||||||
gNewElementCount = 0;
|
gNewElementCount = 0;
|
||||||
log.debug("State reset");
|
log.debug("State reset");
|
||||||
|
|
||||||
|
@ -2725,7 +2752,7 @@ var CustomizableUIInternal = {
|
||||||
Services.prefs.setIntPref(kPrefUIDensity, uiDensity);
|
Services.prefs.setIntPref(kPrefUIDensity, uiDensity);
|
||||||
Services.prefs.setBoolPref(kPrefAutoTouchMode, autoTouchMode);
|
Services.prefs.setBoolPref(kPrefAutoTouchMode, autoTouchMode);
|
||||||
Services.prefs.setBoolPref(kPrefAutoHideDownloadsButton, autoHideDownloadsButton);
|
Services.prefs.setBoolPref(kPrefAutoHideDownloadsButton, autoHideDownloadsButton);
|
||||||
LightweightThemeManager.currentTheme = currentTheme;
|
currentTheme.enable();
|
||||||
this.loadSavedState();
|
this.loadSavedState();
|
||||||
// If the user just customizes toolbar/titlebar visibility, gSavedState will be null
|
// If the user just customizes toolbar/titlebar visibility, gSavedState will be null
|
||||||
// and we don't need to do anything else here:
|
// and we don't need to do anything else here:
|
||||||
|
@ -2942,9 +2969,9 @@ var CustomizableUIInternal = {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LightweightThemeManager.currentTheme &&
|
// This should just be `gDefaultTheme.isActive`, but bugs...
|
||||||
LightweightThemeManager.currentTheme.id != "default-theme@mozilla.org") {
|
if (gDefaultTheme && gDefaultTheme.id != gSelectedTheme.id) {
|
||||||
log.debug(LightweightThemeManager.currentTheme + " theme is non-default");
|
log.debug(gSelectedTheme.id + " theme is non-default");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,14 @@ const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.j
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGlobalGetters(this, ["CSS"]);
|
XPCOMUtils.defineLazyGlobalGetters(this, ["CSS"]);
|
||||||
|
|
||||||
|
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||||
|
"resource://gre/modules/AddonManager.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "AMTelemetry",
|
ChromeUtils.defineModuleGetter(this, "AMTelemetry",
|
||||||
"resource://gre/modules/AddonManager.jsm");
|
"resource://gre/modules/AddonManager.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "DragPositionManager",
|
ChromeUtils.defineModuleGetter(this, "DragPositionManager",
|
||||||
"resource:///modules/DragPositionManager.jsm");
|
"resource:///modules/DragPositionManager.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
||||||
"resource://gre/modules/BrowserUtils.jsm");
|
"resource://gre/modules/BrowserUtils.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
|
||||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
ChromeUtils.defineModuleGetter(this, "SessionStore",
|
ChromeUtils.defineModuleGetter(this, "SessionStore",
|
||||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||||
XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
|
XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
|
||||||
|
@ -164,11 +164,11 @@ CustomizeMode.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateLWThemeButtonIcon() {
|
async _updateThemeButtonIcon() {
|
||||||
let lwthemeButton = this.$("customization-lwtheme-button");
|
let lwthemeButton = this.$("customization-lwtheme-button");
|
||||||
let lwthemeIcon = this.document.getAnonymousElementByAttribute(lwthemeButton,
|
let lwthemeIcon = this.document.getAnonymousElementByAttribute(lwthemeButton,
|
||||||
"class", "button-icon");
|
"class", "button-icon");
|
||||||
let theme = LightweightThemeManager.currentTheme;
|
let theme = (await AddonManager.getAddonsByTypes(["theme"])).find(addon => addon.isActive);
|
||||||
lwthemeIcon.style.backgroundImage = theme ? "url(" + theme.iconURL + ")" : "";
|
lwthemeIcon.style.backgroundImage = theme ? "url(" + theme.iconURL + ")" : "";
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -344,8 +344,8 @@ CustomizeMode.prototype = {
|
||||||
}, 0);
|
}, 0);
|
||||||
this._updateEmptyPaletteNotice();
|
this._updateEmptyPaletteNotice();
|
||||||
|
|
||||||
this._updateLWThemeButtonIcon();
|
this._updateThemeButtonIcon();
|
||||||
Services.obs.addObserver(this, "lightweight-theme-changed");
|
AddonManager.addAddonListener(this);
|
||||||
|
|
||||||
this._setupDownloadAutoHideToggle();
|
this._setupDownloadAutoHideToggle();
|
||||||
|
|
||||||
|
@ -388,7 +388,7 @@ CustomizeMode.prototype = {
|
||||||
|
|
||||||
this._teardownDownloadAutoHideToggle();
|
this._teardownDownloadAutoHideToggle();
|
||||||
|
|
||||||
Services.obs.removeObserver(this, "lightweight-theme-changed");
|
AddonManager.removeAddonListener(this);
|
||||||
CustomizableUI.removeListener(this);
|
CustomizableUI.removeListener(this);
|
||||||
|
|
||||||
this.document.removeEventListener("keypress", this);
|
this.document.removeEventListener("keypress", this);
|
||||||
|
@ -1310,23 +1310,13 @@ CustomizeMode.prototype = {
|
||||||
this._onUIChange();
|
this._onUIChange();
|
||||||
},
|
},
|
||||||
|
|
||||||
onLWThemesMenuShowing(aEvent) {
|
async onThemesMenuShowing(aEvent) {
|
||||||
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
|
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
|
||||||
const LIGHT_THEME_ID = "firefox-compact-light@mozilla.org";
|
const LIGHT_THEME_ID = "firefox-compact-light@mozilla.org";
|
||||||
const DARK_THEME_ID = "firefox-compact-dark@mozilla.org";
|
const DARK_THEME_ID = "firefox-compact-dark@mozilla.org";
|
||||||
const MAX_THEME_COUNT = 6;
|
const MAX_THEME_COUNT = 6;
|
||||||
|
|
||||||
this._clearLWThemesMenu(aEvent.target);
|
this._clearThemesMenu(aEvent.target);
|
||||||
|
|
||||||
function previewTheme(aPreviewThemeEvent) {
|
|
||||||
LightweightThemeManager.previewTheme(
|
|
||||||
aPreviewThemeEvent.target.theme.id != DEFAULT_THEME_ID ?
|
|
||||||
aPreviewThemeEvent.target.theme : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetPreview() {
|
|
||||||
LightweightThemeManager.resetPreview();
|
|
||||||
}
|
|
||||||
|
|
||||||
let onThemeSelected = panel => {
|
let onThemeSelected = panel => {
|
||||||
// This causes us to call _onUIChange when the LWT actually changes,
|
// This causes us to call _onUIChange when the LWT actually changes,
|
||||||
|
@ -1346,50 +1336,36 @@ CustomizeMode.prototype = {
|
||||||
tbb.setAttribute("tooltiptext", aTheme.description);
|
tbb.setAttribute("tooltiptext", aTheme.description);
|
||||||
tbb.setAttribute("tabindex", "0");
|
tbb.setAttribute("tabindex", "0");
|
||||||
tbb.classList.add("customization-lwtheme-menu-theme");
|
tbb.classList.add("customization-lwtheme-menu-theme");
|
||||||
let isActive = activeThemeID == aTheme.id;
|
let isActive = aTheme.isActive;
|
||||||
tbb.setAttribute("aria-checked", isActive);
|
tbb.setAttribute("aria-checked", isActive);
|
||||||
tbb.setAttribute("role", "menuitemradio");
|
tbb.setAttribute("role", "menuitemradio");
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
tbb.setAttribute("active", "true");
|
tbb.setAttribute("active", "true");
|
||||||
}
|
}
|
||||||
tbb.addEventListener("focus", previewTheme);
|
|
||||||
tbb.addEventListener("mouseover", previewTheme);
|
|
||||||
tbb.addEventListener("blur", resetPreview);
|
|
||||||
|
|
||||||
return tbb;
|
return tbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
let themes = [];
|
let themes = await AddonManager.getAddonsByTypes(["theme"]);
|
||||||
let lwts = LightweightThemeManager.usedThemes;
|
let currentTheme = themes.find(theme => theme.isActive);
|
||||||
let currentLwt = LightweightThemeManager.currentTheme;
|
|
||||||
|
|
||||||
let activeThemeID = currentLwt ? currentLwt.id : DEFAULT_THEME_ID;
|
|
||||||
|
|
||||||
// Move the current theme (if any) and the light/dark themes to the start:
|
// 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];
|
let importantThemes = new Set([DEFAULT_THEME_ID, LIGHT_THEME_ID, DARK_THEME_ID]);
|
||||||
if (currentLwt && !importantThemes.includes(currentLwt.id)) {
|
if (currentTheme) {
|
||||||
importantThemes.push(currentLwt.id);
|
importantThemes.add(currentTheme.id);
|
||||||
}
|
}
|
||||||
for (let importantTheme of importantThemes) {
|
|
||||||
let themeIndex = lwts.findIndex(theme => theme.id == importantTheme);
|
themes.sort((a, b) => importantThemes.has(b) - importantThemes.has(a));
|
||||||
if (themeIndex > -1) {
|
|
||||||
themes.push(...lwts.splice(themeIndex, 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
themes = themes.concat(lwts);
|
|
||||||
if (themes.length > MAX_THEME_COUNT)
|
if (themes.length > MAX_THEME_COUNT)
|
||||||
themes.length = MAX_THEME_COUNT;
|
themes.length = MAX_THEME_COUNT;
|
||||||
|
|
||||||
let footer = doc.getElementById("customization-lwtheme-menu-footer");
|
let footer = doc.getElementById("customization-lwtheme-menu-footer");
|
||||||
let panel = footer.parentNode;
|
let panel = footer.parentNode;
|
||||||
let recommendedLabel = doc.getElementById("customization-lwtheme-menu-recommended");
|
|
||||||
for (let theme of themes) {
|
for (let theme of themes) {
|
||||||
let button = buildToolbarButton(theme);
|
let button = buildToolbarButton(theme);
|
||||||
button.addEventListener("command", () => {
|
button.addEventListener("command", async () => {
|
||||||
if ("userDisabled" in button.theme)
|
await button.theme.enable();
|
||||||
button.theme.userDisabled = false;
|
|
||||||
else
|
|
||||||
LightweightThemeManager.currentTheme = button.theme;
|
|
||||||
onThemeSelected(panel);
|
onThemeSelected(panel);
|
||||||
AMTelemetry.recordActionEvent({
|
AMTelemetry.recordActionEvent({
|
||||||
object: "customize",
|
object: "customize",
|
||||||
|
@ -1397,69 +1373,16 @@ CustomizeMode.prototype = {
|
||||||
extra: {type: "theme", addonId: theme.id},
|
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);
|
panel.insertBefore(button, footer);
|
||||||
}
|
}
|
||||||
let hideRecommendedLabel = (footer.previousElementSibling == recommendedLabel);
|
|
||||||
recommendedLabel.hidden = hideRecommendedLabel;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_clearLWThemesMenu(panel) {
|
_clearThemesMenu(panel) {
|
||||||
let footer = this.$("customization-lwtheme-menu-footer");
|
let footer = this.$("customization-lwtheme-menu-footer");
|
||||||
let recommendedLabel = this.$("customization-lwtheme-menu-recommended");
|
let element = footer;
|
||||||
for (let element of [footer, recommendedLabel]) {
|
while (element.previousElementSibling &&
|
||||||
while (element.previousElementSibling &&
|
element.previousElementSibling.localName == "toolbarbutton") {
|
||||||
element.previousElementSibling.localName == "toolbarbutton") {
|
element.previousElementSibling.remove();
|
||||||
element.previousElementSibling.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for bug 1059934
|
// Workaround for bug 1059934
|
||||||
|
@ -1582,16 +1505,21 @@ CustomizeMode.prototype = {
|
||||||
this._updateDragSpaceCheckbox();
|
this._updateDragSpaceCheckbox();
|
||||||
}
|
}
|
||||||
break;
|
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() {
|
_canDrawInTitlebar() {
|
||||||
return this.window.TabsInTitlebar.systemSupported;
|
return this.window.TabsInTitlebar.systemSupported;
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,12 +41,11 @@
|
||||||
</button>
|
</button>
|
||||||
<button id="customization-lwtheme-button" label="&customizeMode.lwthemes;" class="customizationmode-button" type="menu">
|
<button id="customization-lwtheme-button" label="&customizeMode.lwthemes;" class="customizationmode-button" type="menu">
|
||||||
<panel type="arrow" id="customization-lwtheme-menu"
|
<panel type="arrow" id="customization-lwtheme-menu"
|
||||||
onpopupshowing="gCustomizeMode.onLWThemesMenuShowing(event);"
|
onpopupshowing="gCustomizeMode.onThemesMenuShowing(event);"
|
||||||
position="topcenter bottomleft"
|
position="topcenter bottomleft"
|
||||||
flip="none"
|
flip="none"
|
||||||
role="menu">
|
role="menu">
|
||||||
<label id="customization-lwtheme-menu-header" value="&customizeMode.lwthemes.myThemes;"/>
|
<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">
|
<hbox id="customization-lwtheme-menu-footer">
|
||||||
<toolbarbutton class="customization-lwtheme-menu-footeritem"
|
<toolbarbutton class="customization-lwtheme-menu-footeritem"
|
||||||
label="&customizeMode.lwthemes.menuManage;"
|
label="&customizeMode.lwthemes.menuManage;"
|
||||||
|
|
|
@ -7,11 +7,9 @@
|
||||||
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
|
const DEFAULT_THEME_ID = "default-theme@mozilla.org";
|
||||||
const LIGHT_THEME_ID = "firefox-compact-light@mozilla.org";
|
const LIGHT_THEME_ID = "firefox-compact-light@mozilla.org";
|
||||||
const DARK_THEME_ID = "firefox-compact-dark@mozilla.org";
|
const DARK_THEME_ID = "firefox-compact-dark@mozilla.org";
|
||||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
|
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
|
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
|
||||||
Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
|
|
||||||
|
|
||||||
await startCustomizing();
|
await startCustomizing();
|
||||||
// Check restore defaults button is disabled.
|
// Check restore defaults button is disabled.
|
||||||
|
@ -51,9 +49,9 @@ add_task(async function() {
|
||||||
await popupShownPromise;
|
await popupShownPromise;
|
||||||
|
|
||||||
let header = document.getElementById("customization-lwtheme-menu-header");
|
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");
|
"There should only be three themes (default, light, dark) in the 'My Themes' section by default");
|
||||||
is(header.nextElementSibling.theme.id, DEFAULT_THEME_ID,
|
is(header.nextElementSibling.theme.id, DEFAULT_THEME_ID,
|
||||||
"The first theme should be the default theme");
|
"The first theme should be the default theme");
|
||||||
|
@ -67,8 +65,11 @@ add_task(async function() {
|
||||||
info("Clicked on light theme");
|
info("Clicked on light theme");
|
||||||
await themeChangedPromise;
|
await themeChangedPromise;
|
||||||
|
|
||||||
|
let button = document.getElementById("customization-reset-button");
|
||||||
|
await TestUtils.waitForCondition(() => !button.disabled);
|
||||||
|
|
||||||
// Check restore defaults button is enabled.
|
// Check restore defaults button is enabled.
|
||||||
ok(!document.getElementById("customization-reset-button").disabled,
|
ok(!button.disabled,
|
||||||
"Reset button should not be disabled anymore");
|
"Reset button should not be disabled anymore");
|
||||||
ok((/light/i).test(themesButtonIcon.style.backgroundImage),
|
ok((/light/i).test(themesButtonIcon.style.backgroundImage),
|
||||||
`Button should show light theme thumbnail - was: "${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");
|
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;
|
let firstLWThemeId = firstLWTheme.theme.id;
|
||||||
themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
||||||
firstLWTheme.doCommand();
|
firstLWTheme.doCommand();
|
||||||
info("Clicked on first theme");
|
info("Clicked on first theme");
|
||||||
await themeChangedPromise;
|
await themeChangedPromise;
|
||||||
|
|
||||||
|
await new Promise(executeSoon);
|
||||||
|
|
||||||
popupShownPromise = popupShown(popup);
|
popupShownPromise = popupShown(popup);
|
||||||
EventUtils.synthesizeMouseAtCenter(themesButton, {});
|
EventUtils.synthesizeMouseAtCenter(themesButton, {});
|
||||||
info("Clicked on themes button");
|
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");
|
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 themeCount = 0;
|
||||||
let iterNode = header;
|
let iterNode = header;
|
||||||
while (iterNode.nextElementSibling && iterNode.nextElementSibling.theme) {
|
while (iterNode.nextElementSibling && iterNode.nextElementSibling.theme) {
|
||||||
themeCount++;
|
themeCount++;
|
||||||
iterNode = iterNode.nextElementSibling;
|
iterNode = iterNode.nextElementSibling;
|
||||||
}
|
}
|
||||||
is(themeCount, 4,
|
is(themeCount, 3,
|
||||||
"There should be four themes in the 'My Themes' section");
|
"There should be four themes in the 'My Themes' section");
|
||||||
|
|
||||||
let defaultTheme = header.nextElementSibling;
|
let defaultTheme = header.nextElementSibling;
|
||||||
|
@ -128,20 +127,14 @@ add_task(async function() {
|
||||||
info("Clicked on themes button a fourth time");
|
info("Clicked on themes button a fourth time");
|
||||||
await popupShownPromise;
|
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
|
// check that "Restore Defaults" button resets theme
|
||||||
await gCustomizeMode.reset();
|
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();
|
await endCustomizing();
|
||||||
Services.prefs.setCharPref("lightweightThemes.usedThemes", "[]");
|
Services.prefs.setCharPref("lightweightThemes.usedThemes", "[]");
|
||||||
Services.prefs.setCharPref("lightweightThemes.recommendedThemes", "[]");
|
|
||||||
info("Removed all recommended themes");
|
|
||||||
await startCustomizing();
|
await startCustomizing();
|
||||||
popupShownPromise = popupShown(popup);
|
popupShownPromise = popupShown(popup);
|
||||||
EventUtils.synthesizeMouseAtCenter(themesButton, {});
|
EventUtils.synthesizeMouseAtCenter(themesButton, {});
|
||||||
|
@ -160,18 +153,10 @@ add_task(async function() {
|
||||||
themeNode = themeNode.nextElementSibling;
|
themeNode = themeNode.nextElementSibling;
|
||||||
is(themeNode.theme.id, DARK_THEME_ID, "The third theme should be the Dark theme");
|
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");
|
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() {
|
add_task(async function asyncCleanup() {
|
||||||
await endCustomizing();
|
await endCustomizing();
|
||||||
|
|
||||||
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
|
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
|
||||||
Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,25 +23,28 @@ add_task(async function() {
|
||||||
info("Clicked on themes button");
|
info("Clicked on themes button");
|
||||||
await popupShownPromise;
|
await popupShownPromise;
|
||||||
|
|
||||||
let recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
|
let header = document.getElementById("customization-lwtheme-menu-header");
|
||||||
let firstLWTheme = recommendedHeader.nextElementSibling;
|
let firstLWTheme = header.nextElementSibling.nextElementSibling;
|
||||||
let firstLWThemeId = firstLWTheme.theme.id;
|
let firstLWThemeId = firstLWTheme.theme.id;
|
||||||
let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
|
||||||
firstLWTheme.doCommand();
|
firstLWTheme.doCommand();
|
||||||
info("Clicked on first theme");
|
info("Clicked on first theme");
|
||||||
await themeChangedPromise;
|
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();
|
await gCustomizeMode.reset();
|
||||||
|
|
||||||
ok(CustomizableUI.inDefaultState, "In default state after reset");
|
ok(CustomizableUI.inDefaultState, "In default state after reset");
|
||||||
is(undoResetButton.hidden, false, "The undo button is visible 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();
|
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");
|
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(undoResetButton.hidden, true, "The undo button is hidden after clicking on the undo button");
|
||||||
is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
|
is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
|
||||||
|
|
|
@ -16,33 +16,6 @@ add_task(async function testCustomize() {
|
||||||
|
|
||||||
await startCustomizing();
|
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.
|
// Find the footer buttons to test.
|
||||||
let footerRow = document.getElementById("customization-lwtheme-menu-footer");
|
let footerRow = document.getElementById("customization-lwtheme-menu-footer");
|
||||||
let [manageButton, getMoreButton] = footerRow.childNodes;
|
let [manageButton, getMoreButton] = footerRow.childNodes;
|
||||||
|
@ -77,8 +50,6 @@ add_task(async function testCustomize() {
|
||||||
|
|
||||||
// Events are now [method, object, value, extra] as expected.
|
// Events are now [method, object, value, extra] as expected.
|
||||||
Assert.deepEqual(relatedEvents, [
|
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", "manageThemes"],
|
||||||
["link", "customize", "getThemes"],
|
["link", "customize", "getThemes"],
|
||||||
], "The events are recorded correctly");
|
], "The events are recorded correctly");
|
||||||
|
|
|
@ -30,14 +30,6 @@ if (typeof Mozilla == "undefined") {
|
||||||
Mozilla.UITour = {};
|
Mozilla.UITour = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var themeIntervalId = null;
|
|
||||||
function _stopCyclingThemes() {
|
|
||||||
if (themeIntervalId) {
|
|
||||||
clearInterval(themeIntervalId);
|
|
||||||
themeIntervalId = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _sendEvent(action, data) {
|
function _sendEvent(action, data) {
|
||||||
var event = new CustomEvent("mozUITour", {
|
var event = new CustomEvent("mozUITour", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
|
@ -306,88 +298,6 @@ if (typeof Mozilla == "undefined") {
|
||||||
_sendEvent("hideInfo");
|
_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
|
* @typedef {String} Mozilla.UITour.MenuName
|
||||||
* Valid values:<ul>
|
* Valid values:<ul>
|
||||||
|
|
|
@ -17,8 +17,6 @@ ChromeUtils.defineModuleGetter(this, "CustomizableUI",
|
||||||
"resource:///modules/CustomizableUI.jsm");
|
"resource:///modules/CustomizableUI.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "FxAccounts",
|
ChromeUtils.defineModuleGetter(this, "FxAccounts",
|
||||||
"resource://gre/modules/FxAccounts.jsm");
|
"resource://gre/modules/FxAccounts.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
|
||||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
ChromeUtils.defineModuleGetter(this, "PageActions",
|
ChromeUtils.defineModuleGetter(this, "PageActions",
|
||||||
"resource:///modules/PageActions.jsm");
|
"resource:///modules/PageActions.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
||||||
|
@ -395,16 +393,6 @@ var UITour = {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "previewTheme": {
|
|
||||||
this.previewTheme(data.theme);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "resetTheme": {
|
|
||||||
this.resetTheme();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "showMenu": {
|
case "showMenu": {
|
||||||
this.noautohideMenus.add(data.name);
|
this.noautohideMenus.add(data.name);
|
||||||
this.showMenu(window, data.name, () => {
|
this.showMenu(window, data.name, () => {
|
||||||
|
@ -780,7 +768,6 @@ var UITour = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.noautohideMenus.clear();
|
this.noautohideMenus.clear();
|
||||||
this.resetTheme();
|
|
||||||
|
|
||||||
// If there are no more tour tabs left in the window, teardown the tour for the whole window.
|
// If there are no more tour tabs left in the window, teardown the tour for the whole window.
|
||||||
if (!openTourBrowsers || openTourBrowsers.size == 0) {
|
if (!openTourBrowsers || openTourBrowsers.size == 0) {
|
||||||
|
@ -982,17 +969,6 @@ var UITour = {
|
||||||
return promise;
|
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
|
* 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
|
* 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.toolbars2 "Toolbars">
|
||||||
<!ENTITY customizeMode.lwthemes "Themes">
|
<!ENTITY customizeMode.lwthemes "Themes">
|
||||||
<!ENTITY customizeMode.lwthemes.myThemes "My Themes">
|
<!ENTITY customizeMode.lwthemes.myThemes "My Themes">
|
||||||
<!ENTITY customizeMode.lwthemes.recommended "Recommended">
|
|
||||||
<!ENTITY customizeMode.lwthemes.menuManage "Manage">
|
<!ENTITY customizeMode.lwthemes.menuManage "Manage">
|
||||||
<!ENTITY customizeMode.lwthemes.menuManage.accessKey "M">
|
<!ENTITY customizeMode.lwthemes.menuManage.accessKey "M">
|
||||||
<!ENTITY customizeMode.lwthemes.menuGetMore "Get More Themes">
|
<!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/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)
|
||||||
locale/browser/browser.properties (%chrome/browser/browser.properties)
|
locale/browser/browser.properties (%chrome/browser/browser.properties)
|
||||||
locale/browser/customizableui/customizableWidgets.properties (%chrome/browser/customizableui/customizableWidgets.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/uiDensity.properties (%chrome/browser/uiDensity.properties)
|
||||||
locale/browser/pocket.properties (%chrome/browser/pocket.properties)
|
locale/browser/pocket.properties (%chrome/browser/pocket.properties)
|
||||||
locale/browser/search.properties (%chrome/browser/search.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;
|
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
|
NS_IMETHODIMP
|
||||||
nsDOMWindowUtils::SyncFlushCompositor() {
|
nsDOMWindowUtils::SyncFlushCompositor() {
|
||||||
if (nsIWidget* widget = GetWidget()) {
|
if (nsIWidget* widget = GetWidget()) {
|
||||||
|
@ -4099,6 +4110,18 @@ nsDOMWindowUtils::WrCapture() {
|
||||||
return NS_OK;
|
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
|
NS_IMETHODIMP
|
||||||
nsDOMWindowUtils::SetSystemFont(const nsACString& aFontName) {
|
nsDOMWindowUtils::SetSystemFont(const nsACString& aFontName) {
|
||||||
nsIWidget* widget = GetWidget();
|
nsIWidget* widget = GetWidget();
|
||||||
|
|
|
@ -82,6 +82,7 @@ class nsDOMWindowUtils final : public nsIDOMWindowUtils,
|
||||||
mozilla::dom::Document* GetDocument();
|
mozilla::dom::Document* GetDocument();
|
||||||
mozilla::layers::LayerTransactionChild* GetLayerTransaction();
|
mozilla::layers::LayerTransactionChild* GetLayerTransaction();
|
||||||
mozilla::layers::WebRenderBridgeChild* GetWebRenderBridge();
|
mozilla::layers::WebRenderBridgeChild* GetWebRenderBridge();
|
||||||
|
mozilla::layers::CompositorBridgeChild* GetCompositorBridge();
|
||||||
|
|
||||||
// Until callers are annotated.
|
// Until callers are annotated.
|
||||||
MOZ_CAN_RUN_SCRIPT
|
MOZ_CAN_RUN_SCRIPT
|
||||||
|
|
|
@ -15694,8 +15694,18 @@ QuotaClient::~QuotaClient() {
|
||||||
nsresult QuotaClient::AsyncDeleteFile(FileManager* aFileManager,
|
nsresult QuotaClient::AsyncDeleteFile(FileManager* aFileManager,
|
||||||
int64_t aFileId) {
|
int64_t aFileId) {
|
||||||
AssertIsOnBackgroundThread();
|
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());
|
MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
|
||||||
|
|
||||||
nsresult rv = mDeleteTimer->InitWithNamedFuncCallback(
|
nsresult rv = mDeleteTimer->InitWithNamedFuncCallback(
|
||||||
|
|
|
@ -1932,6 +1932,11 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||||
*/
|
*/
|
||||||
void wrCapture();
|
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
|
* Returns whether the document we're associated to has recorded a given CSS
|
||||||
* property via the use counter mechanism.
|
* property via the use counter mechanism.
|
||||||
|
|
|
@ -56,7 +56,6 @@ DIRS += [
|
||||||
'grid',
|
'grid',
|
||||||
'html',
|
'html',
|
||||||
'jsurl',
|
'jsurl',
|
||||||
'asmjscache',
|
|
||||||
'mathml',
|
'mathml',
|
||||||
'media',
|
'media',
|
||||||
'midi',
|
'midi',
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include "mozilla/CondVar.h"
|
#include "mozilla/CondVar.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
#include "mozilla/dom/PContent.h"
|
#include "mozilla/dom/PContent.h"
|
||||||
#include "mozilla/dom/asmjscache/AsmJSCache.h"
|
|
||||||
#include "mozilla/dom/cache/QuotaClient.h"
|
#include "mozilla/dom/cache/QuotaClient.h"
|
||||||
#include "mozilla/dom/indexedDB/ActorsParent.h"
|
#include "mozilla/dom/indexedDB/ActorsParent.h"
|
||||||
#include "mozilla/dom/localstorage/ActorsParent.h"
|
#include "mozilla/dom/localstorage/ActorsParent.h"
|
||||||
|
@ -1375,7 +1374,7 @@ void ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
nsString gBaseDirPath;
|
StaticAutoPtr<nsString> gBaseDirPath;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool gQuotaManagerInitialized = false;
|
bool gQuotaManagerInitialized = false;
|
||||||
|
@ -2499,6 +2498,14 @@ QuotaManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
if (!strcmp(aTopic, kProfileDoChangeTopic)) {
|
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;
|
nsCOMPtr<nsIFile> baseDir;
|
||||||
rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
|
rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
|
||||||
getter_AddRefs(baseDir));
|
getter_AddRefs(baseDir));
|
||||||
|
@ -2510,7 +2517,7 @@ QuotaManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = baseDir->GetPath(gBaseDirPath);
|
rv = baseDir->GetPath(*gBaseDirPath);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
return 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 (!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
|
// mPendingProfileChange is our re-entrancy guard (the nested event loop
|
||||||
// below may cause re-entrancy).
|
// below may cause re-entrancy).
|
||||||
if (mPendingProfileChange) {
|
if (mPendingProfileChange) {
|
||||||
|
@ -2542,7 +2554,7 @@ QuotaManager::Observer::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
|
|
||||||
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return mShutdownComplete; }));
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return mShutdownComplete; }));
|
||||||
|
|
||||||
gBaseDirPath.Truncate();
|
gBaseDirPath = nullptr;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -2905,16 +2917,19 @@ void QuotaManager::GetOrCreate(nsIRunnable* aCallback,
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
if (IsShuttingDown()) {
|
if (IsShuttingDown()) {
|
||||||
MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!");
|
MOZ_ASSERT(false, "Calling QuotaManager::GetOrCreate() after shutdown!");
|
||||||
return;
|
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);
|
MOZ_ASSERT_IF(gCreateFailed, !gInstance);
|
||||||
} else {
|
} else {
|
||||||
RefPtr<QuotaManager> manager = new QuotaManager();
|
RefPtr<QuotaManager> manager = new QuotaManager();
|
||||||
|
|
||||||
nsresult rv = manager->Init(gBaseDirPath);
|
nsresult rv = manager->Init(*gBaseDirPath);
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
gCreateFailed = true;
|
gCreateFailed = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -3286,9 +3301,8 @@ nsresult QuotaManager::Init(const nsAString& aBasePath) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(Client::IDB == 0 && Client::ASMJS == 1 &&
|
static_assert(Client::IDB == 0 && Client::DOMCACHE == 1 && Client::SDB == 2 &&
|
||||||
Client::DOMCACHE == 2 && Client::SDB == 3 &&
|
Client::LS == 3 && Client::TYPE_MAX == 4,
|
||||||
Client::LS == 4 && Client::TYPE_MAX == 5,
|
|
||||||
"Fix the registration!");
|
"Fix the registration!");
|
||||||
|
|
||||||
MOZ_ASSERT(mClients.Capacity() == Client::TYPE_MAX,
|
MOZ_ASSERT(mClients.Capacity() == Client::TYPE_MAX,
|
||||||
|
@ -3296,7 +3310,6 @@ nsresult QuotaManager::Init(const nsAString& aBasePath) {
|
||||||
|
|
||||||
// Register clients.
|
// Register clients.
|
||||||
mClients.AppendElement(indexedDB::CreateQuotaClient());
|
mClients.AppendElement(indexedDB::CreateQuotaClient());
|
||||||
mClients.AppendElement(asmjscache::CreateClient());
|
|
||||||
mClients.AppendElement(cache::CreateQuotaClient());
|
mClients.AppendElement(cache::CreateQuotaClient());
|
||||||
mClients.AppendElement(simpledb::CreateQuotaClient());
|
mClients.AppendElement(simpledb::CreateQuotaClient());
|
||||||
if (NextGenLocalStorageEnabled()) {
|
if (NextGenLocalStorageEnabled()) {
|
||||||
|
|
|
@ -43,7 +43,6 @@ class Client {
|
||||||
enum Type {
|
enum Type {
|
||||||
IDB = 0,
|
IDB = 0,
|
||||||
// APPCACHE,
|
// APPCACHE,
|
||||||
ASMJS,
|
|
||||||
DOMCACHE,
|
DOMCACHE,
|
||||||
SDB,
|
SDB,
|
||||||
LS,
|
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, (
|
MOZ_DEFINE_ENUM_CLASS_WITH_BASE(CompositionPayloadType, uint8_t, (
|
||||||
eKeyPress,
|
eKeyPress,
|
||||||
eAPZScroll,
|
eAPZScroll,
|
||||||
eAPZPinchZoom
|
eAPZPinchZoom,
|
||||||
|
eContentPaint
|
||||||
));
|
));
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <stdint.h> // for uint64_t, uint32_t
|
#include <stdint.h> // for uint64_t, uint32_t
|
||||||
|
|
||||||
|
#include "mozilla/layers/APZTypes.h"
|
||||||
#include "mozilla/layers/LayersTypes.h" // for TouchBehaviorFlags
|
#include "mozilla/layers/LayersTypes.h" // for TouchBehaviorFlags
|
||||||
#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid, etc
|
#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid, etc
|
||||||
#include "mozilla/layers/ZoomConstraints.h" // for ZoomConstraints
|
#include "mozilla/layers/ZoomConstraints.h" // for ZoomConstraints
|
||||||
|
|
|
@ -371,6 +371,9 @@ void ClientLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
|
||||||
mWidget->PrepareWindowEffects();
|
mWidget->PrepareWindowEffects();
|
||||||
}
|
}
|
||||||
EndTransactionInternal(aCallback, aCallbackData, aFlags);
|
EndTransactionInternal(aCallback, aCallbackData, aFlags);
|
||||||
|
if (XRE_IsContentProcess()) {
|
||||||
|
RegisterPayload({CompositionPayloadType::eContentPaint, TimeStamp::Now()});
|
||||||
|
}
|
||||||
ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
|
ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
|
||||||
|
|
||||||
if (mRepeatTransaction) {
|
if (mRepeatTransaction) {
|
||||||
|
|
|
@ -53,6 +53,7 @@ namespace layers {
|
||||||
class CanvasLayerComposite;
|
class CanvasLayerComposite;
|
||||||
class ColorLayerComposite;
|
class ColorLayerComposite;
|
||||||
class Compositor;
|
class Compositor;
|
||||||
|
class CompositionRecorder;
|
||||||
class ContainerLayerComposite;
|
class ContainerLayerComposite;
|
||||||
class Diagnostics;
|
class Diagnostics;
|
||||||
struct EffectChain;
|
struct EffectChain;
|
||||||
|
@ -200,6 +201,10 @@ class HostLayerManager : public LayerManager {
|
||||||
mCompositorBridgeID = aID;
|
mCompositorBridgeID = aID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetCompositionRecorder(CompositionRecorder* aRecorder) {
|
||||||
|
mCompositionRecorder = aRecorder;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool mDebugOverlayWantsNextFrame;
|
bool mDebugOverlayWantsNextFrame;
|
||||||
nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
|
nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
|
||||||
|
@ -213,6 +218,7 @@ class HostLayerManager : public LayerManager {
|
||||||
bool mWindowOverlayChanged;
|
bool mWindowOverlayChanged;
|
||||||
TimeDuration mLastPaintTime;
|
TimeDuration mLastPaintTime;
|
||||||
TimeStamp mRenderStartTime;
|
TimeStamp mRenderStartTime;
|
||||||
|
CompositionRecorder* mCompositionRecorder = nullptr;
|
||||||
|
|
||||||
// Render time for the current composition.
|
// Render time for the current composition.
|
||||||
TimeStamp mCompositionTime;
|
TimeStamp mCompositionTime;
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "mozilla/layers/APZUpdater.h" // for APZUpdater
|
#include "mozilla/layers/APZUpdater.h" // for APZUpdater
|
||||||
#include "mozilla/layers/AsyncCompositionManager.h"
|
#include "mozilla/layers/AsyncCompositionManager.h"
|
||||||
#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
|
#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
|
||||||
|
#include "mozilla/layers/CompositionRecorder.h" // for CompositionRecorder
|
||||||
#include "mozilla/layers/Compositor.h" // for Compositor
|
#include "mozilla/layers/Compositor.h" // for Compositor
|
||||||
#include "mozilla/layers/CompositorManagerParent.h" // for CompositorManagerParent
|
#include "mozilla/layers/CompositorManagerParent.h" // for CompositorManagerParent
|
||||||
#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
|
#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
|
||||||
|
@ -2599,5 +2600,19 @@ int32_t RecordContentFrameTime(
|
||||||
return 0;
|
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 layers
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -72,6 +72,7 @@ class APZSampler;
|
||||||
class APZUpdater;
|
class APZUpdater;
|
||||||
class AsyncCompositionManager;
|
class AsyncCompositionManager;
|
||||||
class AsyncImagePipelineManager;
|
class AsyncImagePipelineManager;
|
||||||
|
class CompositionRecorder;
|
||||||
class Compositor;
|
class Compositor;
|
||||||
class CompositorAnimationStorage;
|
class CompositorAnimationStorage;
|
||||||
class CompositorBridgeParent;
|
class CompositorBridgeParent;
|
||||||
|
@ -238,6 +239,9 @@ class CompositorBridgeParentBase : public PCompositorBridgeParent,
|
||||||
const nsIntRegion& region) = 0;
|
const nsIntRegion& region) = 0;
|
||||||
virtual mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() = 0;
|
virtual mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() = 0;
|
||||||
virtual mozilla::ipc::IPCResult RecvAllPluginsCaptured() = 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(
|
virtual mozilla::ipc::IPCResult RecvInitialize(
|
||||||
const LayersId& rootLayerTreeId) = 0;
|
const LayersId& rootLayerTreeId) = 0;
|
||||||
virtual mozilla::ipc::IPCResult RecvGetFrameUniformity(
|
virtual mozilla::ipc::IPCResult RecvGetFrameUniformity(
|
||||||
|
@ -343,6 +347,9 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase,
|
||||||
};
|
};
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvAllPluginsCaptured() override;
|
mozilla::ipc::IPCResult RecvAllPluginsCaptured() override;
|
||||||
|
mozilla::ipc::IPCResult RecvBeginRecording(
|
||||||
|
const TimeStamp& aRecordingStart) override;
|
||||||
|
mozilla::ipc::IPCResult RecvEndRecording() override;
|
||||||
|
|
||||||
virtual void NotifyMemoryPressure() override;
|
virtual void NotifyMemoryPressure() override;
|
||||||
virtual void AccumulateMemoryReport(wr::MemoryReport*) 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.
|
// mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy.
|
||||||
RefPtr<CompositorBridgeParent> mSelfRef;
|
RefPtr<CompositorBridgeParent> mSelfRef;
|
||||||
RefPtr<CompositorAnimationStorage> mAnimationStorage;
|
RefPtr<CompositorAnimationStorage> mAnimationStorage;
|
||||||
|
UniquePtr<CompositionRecorder> mCompositionRecorder;
|
||||||
|
|
||||||
TimeDuration mPaintTime;
|
TimeDuration mPaintTime;
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,12 @@ class ContentCompositorBridgeParent final : public CompositorBridgeParentBase {
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvAllPluginsCaptured() override { return IPC_OK(); }
|
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(
|
mozilla::ipc::IPCResult RecvGetFrameUniformity(
|
||||||
FrameUniformityData* aOutData) override {
|
FrameUniformityData* aOutData) override {
|
||||||
// Don't support calculating frame uniformity on the child process and
|
// Don't support calculating frame uniformity on the child process and
|
||||||
|
|
|
@ -264,6 +264,9 @@ parent:
|
||||||
sync CheckContentOnlyTDR(uint32_t sequenceNum)
|
sync CheckContentOnlyTDR(uint32_t sequenceNum)
|
||||||
returns (bool isContentOnlyTDR);
|
returns (bool isContentOnlyTDR);
|
||||||
|
|
||||||
|
async BeginRecording(TimeStamp aRecordingStart);
|
||||||
|
async EndRecording();
|
||||||
|
|
||||||
child:
|
child:
|
||||||
// Send back Compositor Frame Metrics from APZCs so tiled layers can
|
// Send back Compositor Frame Metrics from APZCs so tiled layers can
|
||||||
// update progressively.
|
// update progressively.
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "FrameBuilder.h"
|
#include "FrameBuilder.h"
|
||||||
#include "LayersLogging.h"
|
#include "LayersLogging.h"
|
||||||
#include "UtilityMLGPU.h"
|
#include "UtilityMLGPU.h"
|
||||||
|
#include "CompositionRecorder.h"
|
||||||
#include "mozilla/layers/Diagnostics.h"
|
#include "mozilla/layers/Diagnostics.h"
|
||||||
#include "mozilla/layers/TextRenderer.h"
|
#include "mozilla/layers/TextRenderer.h"
|
||||||
|
|
||||||
|
@ -42,6 +43,49 @@ static const int kDebugOverlayY = 5;
|
||||||
static const int kDebugOverlayMaxWidth = 600;
|
static const int kDebugOverlayMaxWidth = 600;
|
||||||
static const int kDebugOverlayMaxHeight = 96;
|
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)
|
LayerManagerMLGPU::LayerManagerMLGPU(widget::CompositorWidget* aWidget)
|
||||||
: mWidget(aWidget),
|
: mWidget(aWidget),
|
||||||
mDrawDiagnostics(false),
|
mDrawDiagnostics(false),
|
||||||
|
@ -356,6 +400,22 @@ void LayerManagerMLGPU::RenderLayers() {
|
||||||
|
|
||||||
mProfilerScreenshotGrabber.MaybeGrabScreenshot(
|
mProfilerScreenshotGrabber.MaybeGrabScreenshot(
|
||||||
mDevice, builder.GetWidgetRT()->GetTexture());
|
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;
|
mCurrentFrame = nullptr;
|
||||||
|
|
||||||
if (mDrawDiagnostics) {
|
if (mDrawDiagnostics) {
|
||||||
|
|
|
@ -161,6 +161,7 @@ EXPORTS.mozilla.layers += [
|
||||||
'composite/TextRenderer.h',
|
'composite/TextRenderer.h',
|
||||||
'composite/TextureHost.h',
|
'composite/TextureHost.h',
|
||||||
'composite/TiledContentHost.h',
|
'composite/TiledContentHost.h',
|
||||||
|
'CompositionRecorder.h',
|
||||||
'Compositor.h',
|
'Compositor.h',
|
||||||
'CompositorTypes.h',
|
'CompositorTypes.h',
|
||||||
'CopyableCanvasRenderer.h',
|
'CopyableCanvasRenderer.h',
|
||||||
|
@ -409,6 +410,7 @@ UNIFIED_SOURCES += [
|
||||||
'composite/TextRenderer.cpp',
|
'composite/TextRenderer.cpp',
|
||||||
'composite/TextureHost.cpp',
|
'composite/TextureHost.cpp',
|
||||||
'composite/TiledContentHost.cpp',
|
'composite/TiledContentHost.cpp',
|
||||||
|
'CompositionRecorder.cpp',
|
||||||
'Compositor.cpp',
|
'Compositor.cpp',
|
||||||
'CopyableCanvasRenderer.cpp',
|
'CopyableCanvasRenderer.cpp',
|
||||||
'Effects.cpp',
|
'Effects.cpp',
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
#ifndef MOZILLA_GFX_GLMANAGER_H
|
#ifndef MOZILLA_GFX_GLMANAGER_H
|
||||||
#define MOZILLA_GFX_GLMANAGER_H
|
#define MOZILLA_GFX_GLMANAGER_H
|
||||||
|
|
||||||
|
#include "GLDefs.h"
|
||||||
#include "mozilla/gfx/Types.h" // for SurfaceFormat
|
#include "mozilla/gfx/Types.h" // for SurfaceFormat
|
||||||
|
#include "mozilla/gfx/2D.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace gl {
|
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.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
|
||||||
DECL_GFX_PREF(Live, "layers.single-tile.enabled", LayersSingleTileEnabled, bool, true);
|
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(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
|
// 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,
|
// 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/PFileSystemRequestChild.h"
|
||||||
#include "mozilla/dom/EndpointForReportChild.h"
|
#include "mozilla/dom/EndpointForReportChild.h"
|
||||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||||
#include "mozilla/dom/asmjscache/AsmJSCache.h"
|
|
||||||
#include "mozilla/dom/cache/ActorUtils.h"
|
#include "mozilla/dom/cache/ActorUtils.h"
|
||||||
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
|
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
|
||||||
#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
|
#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
|
||||||
|
@ -89,7 +88,6 @@ using mozilla::dom::PServiceWorkerChild;
|
||||||
using mozilla::dom::PServiceWorkerContainerChild;
|
using mozilla::dom::PServiceWorkerContainerChild;
|
||||||
using mozilla::dom::PServiceWorkerRegistrationChild;
|
using mozilla::dom::PServiceWorkerRegistrationChild;
|
||||||
using mozilla::dom::StorageDBChild;
|
using mozilla::dom::StorageDBChild;
|
||||||
using mozilla::dom::asmjscache::PAsmJSCacheEntryChild;
|
|
||||||
using mozilla::dom::cache::PCacheChild;
|
using mozilla::dom::cache::PCacheChild;
|
||||||
using mozilla::dom::cache::PCacheStorageChild;
|
using mozilla::dom::cache::PCacheStorageChild;
|
||||||
using mozilla::dom::cache::PCacheStreamControlChild;
|
using mozilla::dom::cache::PCacheStreamControlChild;
|
||||||
|
@ -572,21 +570,6 @@ bool BackgroundChildImpl::DeallocPParentToChildStreamChild(
|
||||||
return true;
|
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() {
|
BackgroundChildImpl::PQuotaChild* BackgroundChildImpl::AllocPQuotaChild() {
|
||||||
MOZ_CRASH("PQuotaChild actor should be manually constructed!");
|
MOZ_CRASH("PQuotaChild actor should be manually constructed!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,14 +213,6 @@ class BackgroundChildImpl : public PBackgroundChild {
|
||||||
virtual bool DeallocPParentToChildStreamChild(
|
virtual bool DeallocPParentToChildStreamChild(
|
||||||
PParentToChildStreamChild* aActor) override;
|
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 PQuotaChild* AllocPQuotaChild() override;
|
||||||
|
|
||||||
virtual bool DeallocPQuotaChild(PQuotaChild* aActor) override;
|
virtual bool DeallocPQuotaChild(PQuotaChild* aActor) override;
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
#include "mozilla/dom/ServiceWorkerManagerParent.h"
|
#include "mozilla/dom/ServiceWorkerManagerParent.h"
|
||||||
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
||||||
#include "mozilla/dom/StorageActivityService.h"
|
#include "mozilla/dom/StorageActivityService.h"
|
||||||
#include "mozilla/dom/asmjscache/AsmJSCache.h"
|
|
||||||
#include "mozilla/dom/cache/ActorUtils.h"
|
#include "mozilla/dom/cache/ActorUtils.h"
|
||||||
#include "mozilla/dom/indexedDB/ActorsParent.h"
|
#include "mozilla/dom/indexedDB/ActorsParent.h"
|
||||||
#include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
|
#include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
|
||||||
|
@ -88,7 +87,6 @@ using mozilla::dom::PServiceWorkerParent;
|
||||||
using mozilla::dom::PServiceWorkerRegistrationParent;
|
using mozilla::dom::PServiceWorkerRegistrationParent;
|
||||||
using mozilla::dom::UDPSocketParent;
|
using mozilla::dom::UDPSocketParent;
|
||||||
using mozilla::dom::WebAuthnTransactionParent;
|
using mozilla::dom::WebAuthnTransactionParent;
|
||||||
using mozilla::dom::asmjscache::PAsmJSCacheEntryParent;
|
|
||||||
using mozilla::dom::cache::PCacheParent;
|
using mozilla::dom::cache::PCacheParent;
|
||||||
using mozilla::dom::cache::PCacheStorageParent;
|
using mozilla::dom::cache::PCacheStorageParent;
|
||||||
using mozilla::dom::cache::PCacheStreamControlParent;
|
using mozilla::dom::cache::PCacheStreamControlParent;
|
||||||
|
@ -949,26 +947,6 @@ mozilla::ipc::IPCResult BackgroundParentImpl::RecvMessagePortForceClose(
|
||||||
return IPC_OK();
|
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() {
|
BackgroundParentImpl::PQuotaParent* BackgroundParentImpl::AllocPQuotaParent() {
|
||||||
AssertIsInMainOrSocketProcess();
|
AssertIsInMainOrSocketProcess();
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
|
@ -263,14 +263,6 @@ class BackgroundParentImpl : public PBackgroundParent {
|
||||||
const nsID& aUUID, const nsID& aDestinationUUID,
|
const nsID& aUUID, const nsID& aDestinationUUID,
|
||||||
const uint32_t& aSequenceID) override;
|
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 PQuotaParent* AllocPQuotaParent() override;
|
||||||
|
|
||||||
virtual bool DeallocPQuotaParent(PQuotaParent* aActor) 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
include protocol PAsmJSCacheEntry;
|
|
||||||
include protocol PBackgroundIDBFactory;
|
include protocol PBackgroundIDBFactory;
|
||||||
include protocol PBackgroundIndexedDBUtils;
|
include protocol PBackgroundIndexedDBUtils;
|
||||||
include protocol PBackgroundSDBConnection;
|
include protocol PBackgroundSDBConnection;
|
||||||
|
@ -63,18 +62,11 @@ include "mozilla/layers/LayersMessageUtils.h";
|
||||||
using mozilla::dom::cache::Namespace
|
using mozilla::dom::cache::Namespace
|
||||||
from "mozilla/dom/cache/Types.h";
|
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 mozilla {
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
sync protocol PBackground
|
sync protocol PBackground
|
||||||
{
|
{
|
||||||
manages PAsmJSCacheEntry;
|
|
||||||
manages PBackgroundIDBFactory;
|
manages PBackgroundIDBFactory;
|
||||||
manages PBackgroundIndexedDBUtils;
|
manages PBackgroundIndexedDBUtils;
|
||||||
manages PBackgroundSDBConnection;
|
manages PBackgroundSDBConnection;
|
||||||
|
@ -185,10 +177,6 @@ parent:
|
||||||
|
|
||||||
async MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
|
async MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
|
||||||
|
|
||||||
async PAsmJSCacheEntry(OpenMode openMode,
|
|
||||||
WriteParams write,
|
|
||||||
PrincipalInfo principalInfo);
|
|
||||||
|
|
||||||
async PQuota();
|
async PQuota();
|
||||||
|
|
||||||
async ShutdownQuotaManager();
|
async ShutdownQuotaManager();
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
* - writes to fields in private data
|
* - writes to fields in private data
|
||||||
* - writes to non-markable fields like JSObject::private that point to
|
* - writes to non-markable fields like JSObject::private that point to
|
||||||
* markable data
|
* 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
|
* 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.
|
* 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,
|
* reference them. The solution is to maintain information about these pointers,
|
||||||
* and mark their targets when we start a minor collection.
|
* 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
|
* The pointers can be thought of as edges in an object graph, and the set of
|
||||||
* from the tenured generation into the nursery is know as the remembered set.
|
* edges from the tenured generation into the nursery is known as the remembered
|
||||||
* Post barriers are used to track this remembered set.
|
* set. Post barriers are used to track this remembered set.
|
||||||
*
|
*
|
||||||
* Whenever a slot which could contain such a pointer is written, we check
|
* 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
|
* whether the pointed-to thing is in the nursery (if storeBuffer() returns a
|
||||||
|
|
|
@ -63,6 +63,8 @@ LOCAL_INCLUDES += [
|
||||||
'celt',
|
'celt',
|
||||||
'include',
|
'include',
|
||||||
'silk',
|
'silk',
|
||||||
|
'silk/fixed',
|
||||||
|
'silk/float',
|
||||||
'src',
|
'src',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -76,15 +78,9 @@ UNIFIED_SOURCES += opus_sources
|
||||||
SOURCES += opus_nonunified_sources
|
SOURCES += opus_nonunified_sources
|
||||||
|
|
||||||
if CONFIG['MOZ_SAMPLE_TYPE_FLOAT32']:
|
if CONFIG['MOZ_SAMPLE_TYPE_FLOAT32']:
|
||||||
LOCAL_INCLUDES += [
|
|
||||||
'silk/float',
|
|
||||||
]
|
|
||||||
UNIFIED_SOURCES += silk_sources_float
|
UNIFIED_SOURCES += silk_sources_float
|
||||||
UNIFIED_SOURCES += opus_sources_float
|
UNIFIED_SOURCES += opus_sources_float
|
||||||
else:
|
else:
|
||||||
LOCAL_INCLUDES += [
|
|
||||||
'silk/fixed',
|
|
||||||
]
|
|
||||||
UNIFIED_SOURCES += silk_sources_fixed
|
UNIFIED_SOURCES += silk_sources_fixed
|
||||||
|
|
||||||
if CONFIG['CPU_ARCH'] in ('x86', 'x86_64'):
|
if CONFIG['CPU_ARCH'] in ('x86', 'x86_64'):
|
||||||
|
|
|
@ -532,7 +532,7 @@ var BrowserApp = {
|
||||||
Telemetry.addData("FENNEC_TRACKING_PROTECTION_STATE", parseInt(BrowserApp.getTrackingProtectionState()));
|
Telemetry.addData("FENNEC_TRACKING_PROTECTION_STATE", parseInt(BrowserApp.getTrackingProtectionState()));
|
||||||
});
|
});
|
||||||
|
|
||||||
InitLater(() => LightWeightThemeWebInstaller.init());
|
InitLater(() => LightWeightThemeStuff.init());
|
||||||
InitLater(() => CastingApps.init(), window, "CastingApps");
|
InitLater(() => CastingApps.init(), window, "CastingApps");
|
||||||
|
|
||||||
// Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
|
// 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() {
|
init: function sh_init() {
|
||||||
let temp = {};
|
let {LightweightThemeConsumer} =
|
||||||
ChromeUtils.import("resource://gre/modules/LightweightThemeConsumer.jsm", temp);
|
ChromeUtils.import("resource://gre/modules/LightweightThemeConsumer.jsm");
|
||||||
let theme = new temp.LightweightThemeConsumer(document);
|
new LightweightThemeConsumer(document);
|
||||||
BrowserApp.deck.addEventListener("InstallBrowserTheme", this, false, true);
|
|
||||||
BrowserApp.deck.addEventListener("PreviewBrowserTheme", this, false, true);
|
|
||||||
BrowserApp.deck.addEventListener("ResetBrowserThemePreview", this, false, true);
|
|
||||||
|
|
||||||
if (ParentalControls.parentalControlsEnabled &&
|
if (ParentalControls.parentalControlsEnabled &&
|
||||||
!this._manager.currentTheme &&
|
!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 () {
|
get _manager () {
|
||||||
let temp = {};
|
let {LightweightThemeManager} =
|
||||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
|
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||||
delete this._manager;
|
delete this._manager;
|
||||||
return this._manager = temp.LightweightThemeManager;
|
return this._manager = LightweightThemeManager;
|
||||||
},
|
},
|
||||||
|
|
||||||
_installParentalControlsTheme: function() {
|
_installParentalControlsTheme: function() {
|
||||||
|
@ -3336,81 +3306,6 @@ var LightWeightThemeWebInstaller = {
|
||||||
mgr.addBuiltInTheme(parentalControlsTheme);
|
mgr.addBuiltInTheme(parentalControlsTheme);
|
||||||
mgr.themeChanged(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 = {
|
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 {SCORE_INCREMENT_XLARGE} = ChromeUtils.import("resource://services-sync/constants.js");
|
||||||
const {CommonUtils} = ChromeUtils.import("resource://services-common/utils.js");
|
const {CommonUtils} = ChromeUtils.import("resource://services-common/utils.js");
|
||||||
|
|
||||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
|
||||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "PREFS_GUID",
|
XPCOMUtils.defineLazyGetter(this, "PREFS_GUID",
|
||||||
() => CommonUtils.encodeBase64URL(Services.appinfo.ID));
|
() => CommonUtils.encodeBase64URL(Services.appinfo.ID));
|
||||||
|
|
||||||
|
@ -127,19 +124,7 @@ PrefStore.prototype = {
|
||||||
return values;
|
return values;
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateLightWeightTheme(themeID) {
|
|
||||||
let themeObject = null;
|
|
||||||
if (themeID) {
|
|
||||||
themeObject = LightweightThemeManager.getUsedTheme(themeID);
|
|
||||||
}
|
|
||||||
LightweightThemeManager.currentTheme = themeObject;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setAllPrefs(values) {
|
_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
|
// 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).
|
// _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));
|
let prefs = Object.keys(values).sort(a => -a.indexOf(PREF_SYNC_PREFS_PREFIX));
|
||||||
|
@ -154,31 +139,17 @@ PrefStore.prototype = {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (pref) {
|
if (value == null) {
|
||||||
// Some special prefs we don't want to set directly.
|
// Pref has gone missing. The best we can do is reset it.
|
||||||
case selectedThemeIDPref:
|
this._prefs.reset(pref);
|
||||||
selectedThemeIDAfter = value;
|
} else {
|
||||||
break;
|
try {
|
||||||
|
this._prefs.set(pref, value);
|
||||||
// default is to just set the pref
|
} catch (ex) {
|
||||||
default:
|
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() {
|
async getAllIDs() {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
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 {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
|
||||||
const {PrefRec} = ChromeUtils.import("resource://services-sync/engines/prefs.js");
|
const {PrefRec} = ChromeUtils.import("resource://services-sync/engines/prefs.js");
|
||||||
const {Service} = ChromeUtils.import("resource://services-sync/service.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(prefs.get("testing.unsynced.url"), "https://www.example.com/2");
|
||||||
Assert.equal(Svc.Prefs.get("prefs.sync.testing.somepref"), true);
|
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.");
|
_("Only the current app's preferences are applied.");
|
||||||
record = new PrefRec("prefs", "some-fake-app");
|
record = new PrefRec("prefs", "some-fake-app");
|
||||||
record.value = {
|
record.value = {
|
||||||
|
@ -134,38 +107,6 @@ add_task(async function run_test() {
|
||||||
};
|
};
|
||||||
await store.update(record);
|
await store.update(record);
|
||||||
Assert.equal(prefs.get("testing.int"), 42);
|
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 {
|
} finally {
|
||||||
prefs.resetBranch("");
|
prefs.resetBranch("");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
/* vim: set sts=2 sw=2 et tw=80: */
|
/* vim: set sts=2 sw=2 et tw=80: */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
|
|
||||||
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
|
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||||
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
|
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
|
||||||
|
|
||||||
|
@ -56,7 +54,7 @@ add_task(async function test_management_themes() {
|
||||||
let addons = await browser.management.getAll();
|
let addons = await browser.management.getAll();
|
||||||
let themes = addons.filter(addon => addon.type === "theme");
|
let themes = addons.filter(addon => addon.type === "theme");
|
||||||
// We get the 3 built-in themes plus the lwt and our addon.
|
// 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.
|
// We should also get our test extension.
|
||||||
let testExtension = addons.find(addon => { return addon.id === TEST_ID; });
|
let testExtension = addons.find(addon => { return addon.id === TEST_ID; });
|
||||||
browser.test.assertTrue(!!testExtension,
|
browser.test.assertTrue(!!testExtension,
|
||||||
|
@ -109,27 +107,8 @@ add_task(async function test_management_themes() {
|
||||||
});
|
});
|
||||||
await extension.startup();
|
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();
|
await theme.startup();
|
||||||
is(await extension.awaitMessage("onInstalled"), "Simple theme test", "webextension theme installed");
|
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");
|
extension.sendMessage("test");
|
||||||
is(await extension.awaitMessage("onEnabled"), "Default", "default enabled");
|
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");
|
}, "Waiting for footer to become visible");
|
||||||
|
|
||||||
EventUtils.synthesizeMouseAtCenter(footer, {});
|
EventUtils.synthesizeMouseAtCenter(footer, {});
|
||||||
await TestUtils.waitForCondition(() => {
|
let window = await waitForPasswordManagerDialog();
|
||||||
return Services.wm.getMostRecentWindow("Toolkit:PasswordManager") !== null;
|
|
||||||
}, "Waiting for the password manager dialog to open");
|
|
||||||
info("Login dialog was opened");
|
info("Login dialog was opened");
|
||||||
|
|
||||||
let window = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
|
||||||
await TestUtils.waitForCondition(() => {
|
await TestUtils.waitForCondition(() => {
|
||||||
return window.document.getElementById("filter").value == "example.com";
|
return window.document.getElementById("filter").value == "example.com";
|
||||||
}, "Waiting for the search string to filter logins");
|
}, "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_ArrowDown");
|
||||||
await EventUtils.synthesizeKey("KEY_Enter");
|
await EventUtils.synthesizeKey("KEY_Enter");
|
||||||
|
|
||||||
await TestUtils.waitForCondition(() => {
|
let window = await waitForPasswordManagerDialog();
|
||||||
return Services.wm.getMostRecentWindow("Toolkit:PasswordManager") !== null;
|
|
||||||
}, "Waiting for the password manager dialog to open");
|
|
||||||
info("Login dialog was opened");
|
info("Login dialog was opened");
|
||||||
|
|
||||||
let window = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
|
||||||
await TestUtils.waitForCondition(() => {
|
await TestUtils.waitForCondition(() => {
|
||||||
return window.document.getElementById("filter").value == "example.com";
|
return window.document.getElementById("filter").value == "example.com";
|
||||||
}, "Waiting for the search string to filter logins");
|
}, "Waiting for the search string to filter logins");
|
||||||
|
|
|
@ -8,10 +8,7 @@ registerCleanupFunction(resetPrefs);
|
||||||
|
|
||||||
add_task(async function test_noFilter() {
|
add_task(async function test_noFilter() {
|
||||||
LoginHelper.openPasswordManager(window);
|
LoginHelper.openPasswordManager(window);
|
||||||
await TestUtils.waitForCondition(() => {
|
let win = await waitForPasswordManagerDialog();
|
||||||
return Services.wm.getMostRecentWindow("Toolkit:PasswordManager") !== null;
|
|
||||||
}, "Waiting for the password manager dialog to open");
|
|
||||||
let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
|
||||||
ok(win, "Login dialog was opened");
|
ok(win, "Login dialog was opened");
|
||||||
await BrowserTestUtils.closeWindow(win);
|
await BrowserTestUtils.closeWindow(win);
|
||||||
await TestUtils.waitForCondition(() => {
|
await TestUtils.waitForCondition(() => {
|
||||||
|
@ -23,10 +20,7 @@ add_task(async function test_filter() {
|
||||||
// Greek IDN for example.test
|
// Greek IDN for example.test
|
||||||
let domain = "παράδειγμα.δοκιμή";
|
let domain = "παράδειγμα.δοκιμή";
|
||||||
LoginHelper.openPasswordManager(window, domain);
|
LoginHelper.openPasswordManager(window, domain);
|
||||||
await TestUtils.waitForCondition(() => {
|
let win = await waitForPasswordManagerDialog();
|
||||||
return Services.wm.getMostRecentWindow("Toolkit:PasswordManager") !== null;
|
|
||||||
}, "Waiting for the password manager dialog to open");
|
|
||||||
let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
|
|
||||||
await TestUtils.waitForCondition(() => {
|
await TestUtils.waitForCondition(() => {
|
||||||
return win.document.getElementById("filter").value == domain;
|
return win.document.getElementById("filter").value == domain;
|
||||||
}, "Waiting for the search string to filter logins");
|
}, "Waiting for the search string to filter logins");
|
||||||
|
|
|
@ -154,3 +154,13 @@ async function checkDoorhangerUsernamePassword(username, password) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// End popup notification (doorhanger) functions //
|
// 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");
|
"resource:///modules/AttributionCode.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "ctypes",
|
ChromeUtils.defineModuleGetter(this, "ctypes",
|
||||||
"resource://gre/modules/ctypes.jsm");
|
"resource://gre/modules/ctypes.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
|
||||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
ChromeUtils.defineModuleGetter(this, "ProfileAge",
|
ChromeUtils.defineModuleGetter(this, "ProfileAge",
|
||||||
"resource://gre/modules/ProfileAge.jsm");
|
"resource://gre/modules/ProfileAge.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
|
ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
|
||||||
|
@ -699,18 +697,12 @@ EnvironmentAddonBuilder.prototype = {
|
||||||
*/
|
*/
|
||||||
async _updateAddons(atStartup) {
|
async _updateAddons(atStartup) {
|
||||||
this._environment._log.trace("_updateAddons");
|
this._environment._log.trace("_updateAddons");
|
||||||
let personaId = null;
|
|
||||||
let theme = LightweightThemeManager.currentTheme;
|
|
||||||
if (theme) {
|
|
||||||
personaId = theme.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
let addons = {
|
let addons = {
|
||||||
activeAddons: await this._getActiveAddons(),
|
activeAddons: await this._getActiveAddons(),
|
||||||
theme: await this._getActiveTheme(),
|
theme: await this._getActiveTheme(),
|
||||||
activePlugins: this._getActivePlugins(atStartup),
|
activePlugins: this._getActivePlugins(atStartup),
|
||||||
activeGMPlugins: await this._getActiveGMPlugins(atStartup),
|
activeGMPlugins: await this._getActiveGMPlugins(atStartup),
|
||||||
persona: personaId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
|
|
|
@ -275,7 +275,6 @@ Structure:
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
},
|
},
|
||||||
persona: <string>, // id of the current persona
|
|
||||||
},
|
},
|
||||||
experiments: {
|
experiments: {
|
||||||
"<experiment id>": { branch: "<branch>" },
|
"<experiment id>": { branch: "<branch>" },
|
||||||
|
@ -465,6 +464,10 @@ For each experiment we collect the ``id`` and the ``branch`` the client is enrol
|
||||||
Version History
|
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:
|
- Firefox 61:
|
||||||
|
|
||||||
- Removed empty ``addons.activeExperiment`` (`bug 1452935 <https://bugzilla.mozilla.org/show_bug.cgi?id=1452935>`_).
|
- 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",
|
ChromeUtils.defineModuleGetter(this, "AttributionCode",
|
||||||
"resource:///modules/AttributionCode.jsm");
|
"resource:///modules/AttributionCode.jsm");
|
||||||
|
|
||||||
// Lazy load |LightweightThemeManager|.
|
|
||||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
|
||||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
|
|
||||||
ChromeUtils.defineModuleGetter(this, "ExtensionTestUtils",
|
ChromeUtils.defineModuleGetter(this, "ExtensionTestUtils",
|
||||||
"resource://testing-common/ExtensionXPCShellUtils.jsm");
|
"resource://testing-common/ExtensionXPCShellUtils.jsm");
|
||||||
|
|
||||||
|
@ -72,12 +68,6 @@ const PLUGIN2_NAME = "Quicktime";
|
||||||
const PLUGIN2_DESC = "A mock Quicktime plugin";
|
const PLUGIN2_DESC = "A mock Quicktime plugin";
|
||||||
const PLUGIN2_VERSION = "2.3";
|
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";
|
const PLUGIN_UPDATED_TOPIC = "plugins-list-updated";
|
||||||
|
|
||||||
// system add-ons are enabled at startup, so record date when the test starts
|
// system add-ons are enabled at startup, so record date when the test starts
|
||||||
|
@ -274,20 +264,6 @@ function createMockAddonProvider(aName) {
|
||||||
return mockProvider;
|
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() {
|
function spoofGfxAdapter() {
|
||||||
try {
|
try {
|
||||||
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
|
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
|
||||||
|
@ -816,7 +792,6 @@ function checkActiveGMPlugin(data) {
|
||||||
function checkAddonsSection(data, expectBrokenAddons, partialAddonsRecords) {
|
function checkAddonsSection(data, expectBrokenAddons, partialAddonsRecords) {
|
||||||
const EXPECTED_FIELDS = [
|
const EXPECTED_FIELDS = [
|
||||||
"activeAddons", "theme", "activePlugins", "activeGMPlugins",
|
"activeAddons", "theme", "activePlugins", "activeGMPlugins",
|
||||||
"persona",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Assert.ok("addons" in data, "There must be an addons section in Environment.");
|
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) {
|
for (let gmPlugin in activeGMPlugins) {
|
||||||
checkActiveGMPlugin(activeGMPlugins[gmPlugin]);
|
checkActiveGMPlugin(activeGMPlugins[gmPlugin]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check persona
|
|
||||||
Assert.ok(checkNullOrString(data.addons.persona));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkExperimentsSection(data) {
|
function checkExperimentsSection(data) {
|
||||||
|
@ -902,10 +874,6 @@ add_task(async function setup() {
|
||||||
system_addon.lastModifiedTime = SYSTEM_ADDON_INSTALL_DATE;
|
system_addon.lastModifiedTime = SYSTEM_ADDON_INSTALL_DATE;
|
||||||
loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
|
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 test runs in a fresh profile so starting the AddonManager causes
|
||||||
// the addons database to be created (as does setting new theme).
|
// the addons database to be created (as does setting new theme).
|
||||||
// For test_addonsStartup below, we want to test a "warm" startup where
|
// 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();
|
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.
|
// Check plugin data.
|
||||||
Assert.equal(data.addons.activePlugins.length, 1, "We must have only one active plugin.");
|
Assert.equal(data.addons.activePlugins.length, 1, "We must have only one active plugin.");
|
||||||
let targetPlugin = data.addons.activePlugins[0];
|
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 == PLUGIN_MIME_TYPE2));
|
||||||
Assert.ok(!targetPlugin.mimeTypes.find(m => m == "Not There."));
|
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.
|
// Uninstall the addon.
|
||||||
await addon.startupPromise;
|
await addon.startupPromise;
|
||||||
await addon.uninstall();
|
await addon.uninstall();
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
const {CommonUtils} = ChromeUtils.import("resource://services-common/utils.js");
|
const {CommonUtils} = ChromeUtils.import("resource://services-common/utils.js");
|
||||||
const {ClientID} = ChromeUtils.import("resource://gre/modules/ClientID.jsm");
|
const {ClientID} = ChromeUtils.import("resource://gre/modules/ClientID.jsm");
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.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/XPCOMUtils.jsm", this);
|
||||||
ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", this);
|
ChromeUtils.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||||
ChromeUtils.import("resource://gre/modules/TelemetrySession.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
|
// Simulates quickly switching between different list views to verify that only
|
||||||
// the last selected is displayed
|
// the last selected is displayed
|
||||||
|
|
||||||
var tempScope = {};
|
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope);
|
|
||||||
var LightweightThemeManager = tempScope.LightweightThemeManager;
|
PromiseTestUtils.whitelistRejectionsGlobally(/this\._errorLink/);
|
||||||
|
|
||||||
var gManagerWindow;
|
var gManagerWindow;
|
||||||
var gCategoryUtilities;
|
var gCategoryUtilities;
|
||||||
|
@ -15,13 +15,6 @@ var gCategoryUtilities;
|
||||||
async function test() {
|
async function test() {
|
||||||
waitForExplicitFinish();
|
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);
|
let aWindow = await open_manager(null);
|
||||||
gManagerWindow = aWindow;
|
gManagerWindow = aWindow;
|
||||||
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
|
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
|
||||||
|
@ -30,7 +23,6 @@ async function test() {
|
||||||
|
|
||||||
async function end_test() {
|
async function end_test() {
|
||||||
await close_manager(gManagerWindow);
|
await close_manager(gManagerWindow);
|
||||||
LightweightThemeManager.forgetUsedTheme("test");
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,25 +5,9 @@
|
||||||
// Bug 591465 - Context menu of add-ons miss context related state change entries
|
// 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 gManagerWindow;
|
||||||
var gProvider;
|
var gProvider;
|
||||||
var gContextMenu;
|
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() {
|
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() {
|
add_test(async function() {
|
||||||
gManagerWindow.loadView("addons://detail/addon1@tests.mozilla.org");
|
gManagerWindow.loadView("addons://detail/addon1@tests.mozilla.org");
|
||||||
await wait_for_view_load(gManagerWindow);
|
await wait_for_view_load(gManagerWindow);
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
|
|
||||||
// Tests the list view
|
// Tests the list view
|
||||||
|
|
||||||
var tempScope = {};
|
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||||
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope);
|
|
||||||
var LightweightThemeManager = tempScope.LightweightThemeManager;
|
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
|
||||||
|
PromiseTestUtils.whitelistRejectionsGlobally(/this\._errorLink/);
|
||||||
|
|
||||||
var gProvider;
|
var gProvider;
|
||||||
var gManagerWindow;
|
var gManagerWindow;
|
||||||
|
@ -20,18 +21,6 @@ var infoURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsi
|
||||||
|
|
||||||
const EXPECTED_ADDONS = 11;
|
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() {
|
add_task(async function() {
|
||||||
gProvider = new MockProvider();
|
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
|
// Check that onPropertyChanges for appDisabled updates the UI
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
info("Checking that onPropertyChanges for appDisabled updates the UI");
|
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.
|
// doesn't support lightweight themes.
|
||||||
Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
|
Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
|
||||||
|
|
||||||
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
|
|
||||||
var gInstallDate;
|
|
||||||
|
|
||||||
const updateFile = "test_update.json";
|
const updateFile = "test_update.json";
|
||||||
|
|
||||||
const profileDir = gProfD.clone();
|
const profileDir = gProfD.clone();
|
||||||
|
@ -771,153 +767,6 @@ add_task(async function test_no_auto_update() {
|
||||||
await a8.uninstall();
|
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
|
// Test that the update check returns nothing for addons in locked install
|
||||||
// locations.
|
// locations.
|
||||||
add_task(async function run_test_locked_install() {
|
add_task(async function run_test_locked_install() {
|
||||||
|
|
|
@ -8,13 +8,12 @@
|
||||||
* Coverage may overlap with other tests in this folder.
|
* Coverage may overlap with other tests in this folder.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
|
|
||||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
|
||||||
const THEME_IDS = [
|
const THEME_IDS = [
|
||||||
"theme3@tests.mozilla.org",
|
"theme3@tests.mozilla.org",
|
||||||
"theme2@personas.mozilla.org",
|
"theme2@personas.mozilla.org", // Unused. Legacy. Evil.
|
||||||
"default-theme@mozilla.org",
|
"default-theme@mozilla.org",
|
||||||
];
|
];
|
||||||
|
const REAL_THEME_IDS = [THEME_IDS[0], THEME_IDS[2]];
|
||||||
const DEFAULT_THEME = THEME_IDS[2];
|
const DEFAULT_THEME = THEME_IDS[2];
|
||||||
|
|
||||||
const profileDir = gProfD.clone();
|
const profileDir = gProfD.clone();
|
||||||
|
@ -41,38 +40,20 @@ add_task(async function setup_to_default_browserish_state() {
|
||||||
|
|
||||||
await promiseStartupManager();
|
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);
|
let [ t1, t2, d ] = await promiseAddonsByIDs(THEME_IDS);
|
||||||
Assert.ok(t1, "Theme addon should exist");
|
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");
|
Assert.ok(d, "Theme addon should exist");
|
||||||
|
|
||||||
await t1.disable();
|
await t1.disable();
|
||||||
await t2.disable();
|
|
||||||
await new Promise(executeSoon);
|
await new Promise(executeSoon);
|
||||||
Assert.ok(!t1.isActive, "Theme should be disabled");
|
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");
|
Assert.ok(d.isActive, "Default theme should be active");
|
||||||
|
|
||||||
await promiseRestartManager();
|
await promiseRestartManager();
|
||||||
|
|
||||||
[ t1, t2, d ] = await promiseAddonsByIDs(THEME_IDS);
|
[ t1, t2, d ] = await promiseAddonsByIDs(THEME_IDS);
|
||||||
Assert.ok(!t1.isActive, "Theme should still be disabled");
|
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");
|
Assert.ok(d.isActive, "Default theme should still be active");
|
||||||
|
|
||||||
gActiveTheme = d.id;
|
gActiveTheme = d.id;
|
||||||
|
@ -117,7 +98,7 @@ async function setDisabledStateAndCheck(which, disabled = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let isDisabled;
|
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;
|
isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
|
||||||
Assert.equal(theme.userDisabled, isDisabled,
|
Assert.equal(theme.userDisabled, isDisabled,
|
||||||
`Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
|
`Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
|
||||||
|
@ -130,7 +111,7 @@ async function setDisabledStateAndCheck(which, disabled = false) {
|
||||||
await promiseRestartManager();
|
await promiseRestartManager();
|
||||||
|
|
||||||
// All should still be good after a restart of the Addon Manager.
|
// 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;
|
isDisabled = (theme.id in expectedStates) ? expectedStates[theme.id] : true;
|
||||||
Assert.equal(theme.userDisabled, isDisabled,
|
Assert.equal(theme.userDisabled, isDisabled,
|
||||||
`Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
|
`Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled`);
|
||||||
|
@ -154,29 +135,6 @@ add_task(async function test_WebExtension_themes() {
|
||||||
|
|
||||||
// Enable it again.
|
// Enable it again.
|
||||||
await setDisabledStateAndCheck(THEME_IDS[0]);
|
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() {
|
add_task(async function test_default_theme() {
|
||||||
|
|
|
@ -14,7 +14,6 @@ support-files =
|
||||||
skip-if = os == "android"
|
skip-if = os == "android"
|
||||||
[test_AddonRepository_langpacks.js]
|
[test_AddonRepository_langpacks.js]
|
||||||
[test_AddonRepository_paging.js]
|
[test_AddonRepository_paging.js]
|
||||||
[test_LightweightThemeManager.js]
|
|
||||||
[test_ProductAddonChecker.js]
|
[test_ProductAddonChecker.js]
|
||||||
[test_XPIStates.js]
|
[test_XPIStates.js]
|
||||||
[test_XPIcancel.js]
|
[test_XPIcancel.js]
|
||||||
|
@ -229,7 +228,6 @@ head = head_addons.js head_system_addons.js
|
||||||
[test_temporary.js]
|
[test_temporary.js]
|
||||||
skip-if = os == "win" # Bug 1469904
|
skip-if = os == "win" # Bug 1469904
|
||||||
tags = webextensions
|
tags = webextensions
|
||||||
[test_theme_update.js]
|
|
||||||
[test_trash_directory.js]
|
[test_trash_directory.js]
|
||||||
skip-if = os != "win"
|
skip-if = os != "win"
|
||||||
[test_types.js]
|
[test_types.js]
|
||||||
|
|
|
@ -46,7 +46,7 @@ interface nsIProfileLock : nsISupports
|
||||||
* @note THIS INTERFACE SHOULD BE IMPLEMENTED BY THE TOOLKIT CODE ONLY! DON'T
|
* @note THIS INTERFACE SHOULD BE IMPLEMENTED BY THE TOOLKIT CODE ONLY! DON'T
|
||||||
* EVEN THINK ABOUT IMPLEMENTING THIS IN JAVASCRIPT!
|
* 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
|
interface nsIToolkitProfile : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -52,20 +52,67 @@ using namespace mozilla;
|
||||||
#define DEV_EDITION_NAME "dev-edition-default"
|
#define DEV_EDITION_NAME "dev-edition-default"
|
||||||
#define DEFAULT_NAME "default"
|
#define DEFAULT_NAME "default"
|
||||||
#define COMPAT_FILE NS_LITERAL_STRING("compatibility.ini")
|
#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,
|
nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||||
nsIFile* aLocalDir, nsToolkitProfile* aPrev)
|
nsIFile* aLocalDir, bool aFromDB)
|
||||||
: mPrev(aPrev),
|
: mName(aName),
|
||||||
mName(aName),
|
|
||||||
mRootDir(aRootDir),
|
mRootDir(aRootDir),
|
||||||
mLocalDir(aLocalDir),
|
mLocalDir(aLocalDir),
|
||||||
mLock(nullptr) {
|
mLock(nullptr),
|
||||||
|
mIndex(0),
|
||||||
|
mSection("Profile") {
|
||||||
NS_ASSERTION(aRootDir, "No file!");
|
NS_ASSERTION(aRootDir, "No file!");
|
||||||
|
|
||||||
if (aPrev) {
|
RefPtr<nsToolkitProfile> prev =
|
||||||
aPrev->mNext = this;
|
nsToolkitProfileService::gService->mProfiles.getLast();
|
||||||
} else {
|
if (prev) {
|
||||||
nsToolkitProfileService::gService->mFirst = this;
|
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;
|
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
|
// Setting the name to the dev-edition default profile name will cause this
|
||||||
// profile to become the dev-edition default.
|
// profile to become the dev-edition default.
|
||||||
if (aName.EqualsLiteral(DEV_EDITION_NAME) &&
|
if (aName.EqualsLiteral(DEV_EDITION_NAME) &&
|
||||||
|
@ -122,8 +173,9 @@ nsresult nsToolkitProfile::RemoveInternal(bool aRemoveFiles,
|
||||||
|
|
||||||
if (mLock) return NS_ERROR_FILE_IS_LOCKED;
|
if (mLock) return NS_ERROR_FILE_IS_LOCKED;
|
||||||
|
|
||||||
if (!mPrev && !mNext && nsToolkitProfileService::gService->mFirst != this)
|
if (!isInList()) {
|
||||||
return NS_ERROR_NOT_INITIALIZED;
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
if (aRemoveFiles) {
|
if (aRemoveFiles) {
|
||||||
// Check if another instance is using this profile.
|
// Check if another instance is using this profile.
|
||||||
|
@ -160,15 +212,29 @@ nsresult nsToolkitProfile::RemoveInternal(bool aRemoveFiles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPrev)
|
nsINIParser* db = &nsToolkitProfileService::gService->mProfileDB;
|
||||||
mPrev->mNext = mNext;
|
db->DeleteSection(mSection.get());
|
||||||
else
|
|
||||||
nsToolkitProfileService::gService->mFirst = mNext;
|
|
||||||
|
|
||||||
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;
|
if (last != getNext()) {
|
||||||
mNext = nullptr;
|
last->remove();
|
||||||
|
setNext(last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove();
|
||||||
|
|
||||||
if (nsToolkitProfileService::gService->mNormalDefault == this) {
|
if (nsToolkitProfileService::gService->mNormalDefault == this) {
|
||||||
nsToolkitProfileService::gService->mNormalDefault = nullptr;
|
nsToolkitProfileService::gService->mNormalDefault = nullptr;
|
||||||
|
@ -313,7 +379,10 @@ nsToolkitProfileService::nsToolkitProfileService()
|
||||||
gService = this;
|
gService = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsToolkitProfileService::~nsToolkitProfileService() { gService = nullptr; }
|
nsToolkitProfileService::~nsToolkitProfileService() {
|
||||||
|
gService = nullptr;
|
||||||
|
mProfiles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void nsToolkitProfileService::CompleteStartup() {
|
void nsToolkitProfileService::CompleteStartup() {
|
||||||
if (!mStartupProfileSelected) {
|
if (!mStartupProfileSelected) {
|
||||||
|
@ -335,7 +404,7 @@ void nsToolkitProfileService::CompleteStartup() {
|
||||||
NS_ENSURE_SUCCESS_VOID(rv);
|
NS_ENSURE_SUCCESS_VOID(rv);
|
||||||
|
|
||||||
if (isDefaultApp) {
|
if (isDefaultApp) {
|
||||||
mInstallData.SetString(mInstallHash.get(), "Locked", "1");
|
mProfileDB.SetString(mInstallSection.get(), "Locked", "1");
|
||||||
Flush();
|
Flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -440,7 +509,7 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
||||||
const nsCString& install = installs[i];
|
const nsCString& install = installs[i];
|
||||||
|
|
||||||
nsCString path;
|
nsCString path;
|
||||||
rv = mInstallData.GetString(install.get(), "Default", path);
|
rv = mProfileDB.GetString(install.get(), "Default", path);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -452,7 +521,7 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
||||||
|
|
||||||
// Is this profile locked to this other install?
|
// Is this profile locked to this other install?
|
||||||
nsCString isLocked;
|
nsCString isLocked;
|
||||||
rv = mInstallData.GetString(install.get(), "Locked", isLocked);
|
rv = mProfileDB.GetString(install.get(), "Locked", isLocked);
|
||||||
if (NS_SUCCEEDED(rv) && isLocked.Equals("1")) {
|
if (NS_SUCCEEDED(rv) && isLocked.Equals("1")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -465,7 +534,7 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
||||||
for (uint32_t i = 0; i < inUseInstalls.Length(); i++) {
|
for (uint32_t i = 0; i < inUseInstalls.Length(); i++) {
|
||||||
// Removing the default setting entirely will make the install go through
|
// Removing the default setting entirely will make the install go through
|
||||||
// the first run process again at startup and create itself a new profile.
|
// 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.
|
// 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
|
// 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
|
// other installs will steal it, but this was auto-selected so we want to
|
||||||
// unlock it so that other installs can potentially take it.
|
// unlock it so that other installs can potentially take it.
|
||||||
mInstallData.DeleteString(mInstallHash.get(), "Locked");
|
mProfileDB.DeleteString(mInstallSection.get(), "Locked");
|
||||||
|
|
||||||
// Persist the changes.
|
// Persist the changes.
|
||||||
Flush();
|
Flush();
|
||||||
|
@ -486,6 +555,33 @@ bool nsToolkitProfileService::MaybeMakeDefaultDedicatedProfile(
|
||||||
return true;
|
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() {
|
nsresult nsToolkitProfileService::Init() {
|
||||||
NS_ASSERTION(gDirServiceProvider, "No dirserviceprovider!");
|
NS_ASSERTION(gDirServiceProvider, "No dirserviceprovider!");
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
@ -496,54 +592,76 @@ nsresult nsToolkitProfileService::Init() {
|
||||||
rv = nsXREDirProvider::GetUserLocalDataDirectory(getter_AddRefs(mTempData));
|
rv = nsXREDirProvider::GetUserLocalDataDirectory(getter_AddRefs(mTempData));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCString installProfilePath;
|
rv = mAppData->Clone(getter_AddRefs(mProfileDBFile));
|
||||||
|
|
||||||
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));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
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);
|
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;
|
bool exists;
|
||||||
rv = mListFile->IsFile(&exists);
|
rv = mProfileDBFile->IsFile(&exists);
|
||||||
if (NS_SUCCEEDED(rv) && 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
|
// Init does not fail on parsing errors, only on OOM/really unexpected
|
||||||
// conditions.
|
// conditions.
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return 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;
|
nsCString installProfilePath;
|
||||||
rv = parser.GetString("General", "StartWithLastProfile", buffer);
|
|
||||||
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("0")) mStartWithLast = false;
|
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;
|
nsToolkitProfile* currentProfile = nullptr;
|
||||||
|
|
||||||
|
@ -575,14 +693,14 @@ nsresult nsToolkitProfileService::Init() {
|
||||||
nsAutoCString profileID("Profile");
|
nsAutoCString profileID("Profile");
|
||||||
profileID.AppendInt(c);
|
profileID.AppendInt(c);
|
||||||
|
|
||||||
rv = parser.GetString(profileID.get(), "IsRelative", buffer);
|
rv = mProfileDB.GetString(profileID.get(), "IsRelative", buffer);
|
||||||
if (NS_FAILED(rv)) break;
|
if (NS_FAILED(rv)) break;
|
||||||
|
|
||||||
bool isRelative = buffer.EqualsLiteral("1");
|
bool isRelative = buffer.EqualsLiteral("1");
|
||||||
|
|
||||||
nsAutoCString filePath;
|
nsAutoCString filePath;
|
||||||
|
|
||||||
rv = parser.GetString(profileID.get(), "Path", filePath);
|
rv = mProfileDB.GetString(profileID.get(), "Path", filePath);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
NS_ERROR("Malformed profiles.ini: Path= not found");
|
NS_ERROR("Malformed profiles.ini: Path= not found");
|
||||||
continue;
|
continue;
|
||||||
|
@ -590,7 +708,7 @@ nsresult nsToolkitProfileService::Init() {
|
||||||
|
|
||||||
nsAutoCString name;
|
nsAutoCString name;
|
||||||
|
|
||||||
rv = parser.GetString(profileID.get(), "Name", name);
|
rv = mProfileDB.GetString(profileID.get(), "Name", name);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
NS_ERROR("Malformed profiles.ini: Name= not found");
|
NS_ERROR("Malformed profiles.ini: Name= not found");
|
||||||
continue;
|
continue;
|
||||||
|
@ -618,11 +736,10 @@ nsresult nsToolkitProfileService::Init() {
|
||||||
localDir = rootDir;
|
localDir = rootDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProfile =
|
currentProfile = new nsToolkitProfile(name, rootDir, localDir, true);
|
||||||
new nsToolkitProfile(name, rootDir, localDir, currentProfile);
|
|
||||||
NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY);
|
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")) {
|
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1")) {
|
||||||
mNormalDefault = currentProfile;
|
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 there is only one non-dev-edition profile then mark it as the default.
|
||||||
if (!mNormalDefault && nonDevEditionProfiles == 1) {
|
if (!mNormalDefault && nonDevEditionProfiles == 1) {
|
||||||
mNormalDefault = autoSelectProfile;
|
SetNormalDefault(autoSelectProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mUseDedicatedProfile) {
|
if (!mUseDedicatedProfile) {
|
||||||
|
@ -664,6 +781,9 @@ nsresult nsToolkitProfileService::Init() {
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsToolkitProfileService::SetStartWithLastProfile(bool aValue) {
|
nsToolkitProfileService::SetStartWithLastProfile(bool aValue) {
|
||||||
if (mStartWithLast != aValue) {
|
if (mStartWithLast != aValue) {
|
||||||
|
nsresult rv = mProfileDB.SetString("General", "StartWithLastProfile",
|
||||||
|
mStartWithLast ? "1" : "0");
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
mStartWithLast = aValue;
|
mStartWithLast = aValue;
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -677,7 +797,7 @@ nsToolkitProfileService::GetStartWithLastProfile(bool* aResult) {
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsToolkitProfileService::GetProfiles(nsISimpleEnumerator** aResult) {
|
nsToolkitProfileService::GetProfiles(nsISimpleEnumerator** aResult) {
|
||||||
*aResult = new ProfileEnumerator(this->mFirst);
|
*aResult = new ProfileEnumerator(mProfiles.getFirst());
|
||||||
if (!*aResult) return NS_ERROR_OUT_OF_MEMORY;
|
if (!*aResult) return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
NS_ADDREF(*aResult);
|
NS_ADDREF(*aResult);
|
||||||
|
@ -696,7 +816,7 @@ nsToolkitProfileService::ProfileEnumerator::GetNext(nsISupports** aResult) {
|
||||||
|
|
||||||
NS_ADDREF(*aResult = mCurrent);
|
NS_ADDREF(*aResult = mCurrent);
|
||||||
|
|
||||||
mCurrent = mCurrent->mNext;
|
mCurrent = mCurrent->getNext();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,6 +842,26 @@ nsToolkitProfileService::GetDefaultProfile(nsIToolkitProfile** aResult) {
|
||||||
return NS_OK;
|
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
|
NS_IMETHODIMP
|
||||||
nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) {
|
nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) {
|
||||||
if (mUseDedicatedProfile) {
|
if (mUseDedicatedProfile) {
|
||||||
|
@ -730,20 +870,20 @@ nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) {
|
||||||
// Setting this to the empty string means no profile will be found on
|
// Setting this to the empty string means no profile will be found on
|
||||||
// startup but we'll recognise that this install has been used
|
// startup but we'll recognise that this install has been used
|
||||||
// previously.
|
// previously.
|
||||||
mInstallData.SetString(mInstallHash.get(), "Default", "");
|
mProfileDB.SetString(mInstallSection.get(), "Default", "");
|
||||||
} else {
|
} else {
|
||||||
nsCString profilePath;
|
nsCString profilePath;
|
||||||
nsresult rv = GetProfileDescriptor(aProfile, profilePath, nullptr);
|
nsresult rv = GetProfileDescriptor(aProfile, profilePath, nullptr);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
mInstallData.SetString(mInstallHash.get(), "Default",
|
mProfileDB.SetString(mInstallSection.get(), "Default",
|
||||||
profilePath.get());
|
profilePath.get());
|
||||||
}
|
}
|
||||||
mDedicatedProfile = aProfile;
|
mDedicatedProfile = aProfile;
|
||||||
|
|
||||||
// Some kind of choice has happened here, lock this profile to this
|
// Some kind of choice has happened here, lock this profile to this
|
||||||
// install.
|
// install.
|
||||||
mInstallData.SetString(mInstallHash.get(), "Locked", "1");
|
mProfileDB.SetString(mInstallSection.get(), "Locked", "1");
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -753,7 +893,8 @@ nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
mNormalDefault = aProfile;
|
SetNormalDefault(aProfile);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -813,7 +954,7 @@ nsresult nsToolkitProfileService::CreateDefaultProfile(
|
||||||
} else if (mUseDevEditionProfile) {
|
} else if (mUseDevEditionProfile) {
|
||||||
mDevEditionDefault = mCurrent;
|
mDevEditionDefault = mCurrent;
|
||||||
} else {
|
} else {
|
||||||
mNormalDefault = mCurrent;
|
SetNormalDefault(mCurrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -842,9 +983,9 @@ nsToolkitProfileService::SelectStartupProfile(
|
||||||
argv[argc] = nullptr;
|
argv[argc] = nullptr;
|
||||||
|
|
||||||
bool wasDefault;
|
bool wasDefault;
|
||||||
nsresult rv = SelectStartupProfile(&argc, argv.get(), aIsResetting, aRootDir,
|
nsresult rv =
|
||||||
aLocalDir, aProfile, aDidCreate,
|
SelectStartupProfile(&argc, argv.get(), aIsResetting, aRootDir, aLocalDir,
|
||||||
&wasDefault);
|
aProfile, aDidCreate, &wasDefault);
|
||||||
|
|
||||||
// Since we were called outside of the normal startup path complete any
|
// Since we were called outside of the normal startup path complete any
|
||||||
// startup tasks.
|
// startup tasks.
|
||||||
|
@ -1163,10 +1304,12 @@ nsresult nsToolkitProfileService::SelectStartupProfile(
|
||||||
// older versions of Firefox use then we must create a default profile
|
// older versions of Firefox use then we must create a default profile
|
||||||
// for older versions of Firefox to avoid the existing profile being
|
// for older versions of Firefox to avoid the existing profile being
|
||||||
// auto-selected.
|
// auto-selected.
|
||||||
if ((mUseDedicatedProfile || mUseDevEditionProfile) && mFirst &&
|
if ((mUseDedicatedProfile || mUseDevEditionProfile) &&
|
||||||
!mFirst->mNext) {
|
mProfiles.getFirst() == mProfiles.getLast()) {
|
||||||
|
nsCOMPtr<nsIToolkitProfile> newProfile;
|
||||||
CreateProfile(nullptr, NS_LITERAL_CSTRING(DEFAULT_NAME),
|
CreateProfile(nullptr, NS_LITERAL_CSTRING(DEFAULT_NAME),
|
||||||
getter_AddRefs(mNormalDefault));
|
getter_AddRefs(newProfile));
|
||||||
|
SetNormalDefault(newProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
Flush();
|
Flush();
|
||||||
|
@ -1249,14 +1392,14 @@ nsresult nsToolkitProfileService::ApplyResetProfile(
|
||||||
// If the old profile would have been the default for old installs then mark
|
// If the old profile would have been the default for old installs then mark
|
||||||
// the new profile as such.
|
// the new profile as such.
|
||||||
if (mNormalDefault == aOldProfile) {
|
if (mNormalDefault == aOldProfile) {
|
||||||
mNormalDefault = mCurrent;
|
SetNormalDefault(mCurrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mUseDedicatedProfile && mDedicatedProfile == aOldProfile) {
|
if (mUseDedicatedProfile && mDedicatedProfile == aOldProfile) {
|
||||||
bool wasLocked = false;
|
bool wasLocked = false;
|
||||||
nsCString val;
|
nsCString val;
|
||||||
if (NS_SUCCEEDED(
|
if (NS_SUCCEEDED(
|
||||||
mInstallData.GetString(mInstallHash.get(), "Locked", val))) {
|
mProfileDB.GetString(mInstallSection.get(), "Locked", val))) {
|
||||||
wasLocked = val.Equals("1");
|
wasLocked = val.Equals("1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1264,7 +1407,7 @@ nsresult nsToolkitProfileService::ApplyResetProfile(
|
||||||
|
|
||||||
// Make the locked state match if necessary.
|
// Make the locked state match if necessary.
|
||||||
if (!wasLocked) {
|
if (!wasLocked) {
|
||||||
mInstallData.DeleteString(mInstallHash.get(), "Locked");
|
mProfileDB.DeleteString(mInstallSection.get(), "Locked");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1286,13 +1429,11 @@ nsresult nsToolkitProfileService::ApplyResetProfile(
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsToolkitProfileService::GetProfileByName(const nsACString& aName,
|
nsToolkitProfileService::GetProfileByName(const nsACString& aName,
|
||||||
nsIToolkitProfile** aResult) {
|
nsIToolkitProfile** aResult) {
|
||||||
nsToolkitProfile* curP = mFirst;
|
for (RefPtr<nsToolkitProfile> profile : mProfiles) {
|
||||||
while (curP) {
|
if (profile->mName.Equals(aName)) {
|
||||||
if (curP->mName.Equals(aName)) {
|
NS_ADDREF(*aResult = profile);
|
||||||
NS_ADDREF(*aResult = curP);
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
curP = curP->mNext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
@ -1305,18 +1446,16 @@ nsToolkitProfileService::GetProfileByName(const nsACString& aName,
|
||||||
void nsToolkitProfileService::GetProfileByDir(nsIFile* aRootDir,
|
void nsToolkitProfileService::GetProfileByDir(nsIFile* aRootDir,
|
||||||
nsIFile* aLocalDir,
|
nsIFile* aLocalDir,
|
||||||
nsIToolkitProfile** aResult) {
|
nsIToolkitProfile** aResult) {
|
||||||
nsToolkitProfile* curP = mFirst;
|
for (RefPtr<nsToolkitProfile> profile : mProfiles) {
|
||||||
while (curP) {
|
|
||||||
bool equal;
|
bool equal;
|
||||||
nsresult rv = curP->mRootDir->Equals(aRootDir, &equal);
|
nsresult rv = profile->mRootDir->Equals(aRootDir, &equal);
|
||||||
if (NS_SUCCEEDED(rv) && equal) {
|
if (NS_SUCCEEDED(rv) && equal) {
|
||||||
rv = curP->mLocalDir->Equals(aLocalDir, &equal);
|
rv = profile->mLocalDir->Equals(aLocalDir, &equal);
|
||||||
if (NS_SUCCEEDED(rv) && equal) {
|
if (NS_SUCCEEDED(rv) && equal) {
|
||||||
NS_ADDREF(*aResult = curP);
|
NS_ADDREF(*aResult = profile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
curP = curP->mNext;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1449,15 +1588,8 @@ nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
|
||||||
rv = CreateTimesInternal(rootDir);
|
rv = CreateTimesInternal(rootDir);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsToolkitProfile* last = mFirst.get();
|
|
||||||
if (last) {
|
|
||||||
while (last->mNext) {
|
|
||||||
last = last->mNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIToolkitProfile> profile =
|
nsCOMPtr<nsIToolkitProfile> profile =
|
||||||
new nsToolkitProfile(aName, rootDir, localDir, last);
|
new nsToolkitProfile(aName, rootDir, localDir, false);
|
||||||
if (!profile) return NS_ERROR_OUT_OF_MEMORY;
|
if (!profile) return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
if (aName.Equals(DEV_EDITION_NAME)) {
|
if (aName.Equals(DEV_EDITION_NAME)) {
|
||||||
|
@ -1496,6 +1628,11 @@ struct FindInstallsClosure {
|
||||||
static bool FindInstalls(const char* aSection, void* aClosure) {
|
static bool FindInstalls(const char* aSection, void* aClosure) {
|
||||||
FindInstallsClosure* closure = static_cast<FindInstallsClosure*>(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);
|
nsCString install(aSection);
|
||||||
closure->installs->AppendElement(install);
|
closure->installs->AppendElement(install);
|
||||||
|
|
||||||
|
@ -1504,9 +1641,9 @@ static bool FindInstalls(const char* aSection, void* aClosure) {
|
||||||
|
|
||||||
nsTArray<nsCString> nsToolkitProfileService::GetKnownInstalls() {
|
nsTArray<nsCString> nsToolkitProfileService::GetKnownInstalls() {
|
||||||
nsTArray<nsCString> result;
|
nsTArray<nsCString> result;
|
||||||
FindInstallsClosure closure = {&mInstallData, &result};
|
FindInstallsClosure closure = {&mProfileDB, &result};
|
||||||
|
|
||||||
mInstallData.GetSections(&FindInstalls, &closure);
|
mProfileDB.GetSections(&FindInstalls, &closure);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1545,10 +1682,9 @@ nsresult nsToolkitProfileService::CreateTimesInternal(nsIFile* aProfileDir) {
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsToolkitProfileService::GetProfileCount(uint32_t* aResult) {
|
nsToolkitProfileService::GetProfileCount(uint32_t* aResult) {
|
||||||
*aResult = 0;
|
*aResult = 0;
|
||||||
nsToolkitProfile* profile = mFirst;
|
for (nsToolkitProfile* profile : mProfiles) {
|
||||||
while (profile) {
|
Unused << profile;
|
||||||
(*aResult)++;
|
(*aResult)++;
|
||||||
profile = profile->mNext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -1558,71 +1694,59 @@ NS_IMETHODIMP
|
||||||
nsToolkitProfileService::Flush() {
|
nsToolkitProfileService::Flush() {
|
||||||
nsresult rv;
|
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) {
|
if (mUseDedicatedProfile) {
|
||||||
rv = mInstallData.WriteToFile(mInstallFile);
|
// Export the installs to the backup.
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
nsTArray<nsCString> installs = GetKnownInstalls();
|
||||||
}
|
|
||||||
|
|
||||||
// Errors during writing might cause unhappy semi-written files.
|
if (!installs.IsEmpty()) {
|
||||||
// To avoid this, write the entire thing to a buffer, then write
|
nsCString data;
|
||||||
// that buffer to disk.
|
nsCString buffer;
|
||||||
|
|
||||||
uint32_t pCount = 0;
|
for (uint32_t i = 0; i < installs.Length(); i++) {
|
||||||
nsToolkitProfile* cur;
|
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;
|
for (uint32_t j = 0; j < strings.Length(); j++) {
|
||||||
const int bufsize = 100 + MAXPATHLEN * pCount;
|
data.AppendPrintf("%s=%s\n", strings[j]->key.get(),
|
||||||
auto buffer = MakeUnique<char[]>(bufsize);
|
strings[j]->value.get());
|
||||||
|
}
|
||||||
|
|
||||||
char* pos = buffer.get();
|
data.Append("\n");
|
||||||
char* end = pos + bufsize;
|
}
|
||||||
|
|
||||||
pos += snprintf(pos, end - pos,
|
FILE* writeFile;
|
||||||
"[General]\n"
|
rv = mInstallDBFile->OpenANSIFileDesc("w", &writeFile);
|
||||||
"StartWithLastProfile=%s\n\n",
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
mStartWithLast ? "1" : "0");
|
|
||||||
|
|
||||||
nsAutoCString path;
|
uint32_t length = data.Length();
|
||||||
cur = mFirst;
|
if (fwrite(data.get(), sizeof(char), length, writeFile) != length) {
|
||||||
pCount = 0;
|
fclose(writeFile);
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
while (cur) {
|
fclose(writeFile);
|
||||||
bool isRelative;
|
} else {
|
||||||
nsresult rv = GetProfileDescriptor(cur, path, &isRelative);
|
rv = mInstallDBFile->Remove(false);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
|
||||||
|
rv != NS_ERROR_FILE_NOT_FOUND) {
|
||||||
pos +=
|
return rv;
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += snprintf(pos, end - pos, "\n");
|
|
||||||
|
|
||||||
cur = cur->mNext;
|
|
||||||
++pCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* writeFile;
|
rv = mProfileDB.WriteToFile(mProfileDBFile);
|
||||||
rv = mListFile->OpenANSIFileDesc("w", &writeFile);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
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;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,20 +15,20 @@
|
||||||
#include "nsProfileLock.h"
|
#include "nsProfileLock.h"
|
||||||
#include "nsINIParser.h"
|
#include "nsINIParser.h"
|
||||||
|
|
||||||
class nsToolkitProfile final : public nsIToolkitProfile {
|
class nsToolkitProfile final
|
||||||
|
: public nsIToolkitProfile,
|
||||||
|
public mozilla::LinkedListElement<RefPtr<nsToolkitProfile>> {
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_NSITOOLKITPROFILE
|
NS_DECL_NSITOOLKITPROFILE
|
||||||
|
|
||||||
friend class nsToolkitProfileService;
|
friend class nsToolkitProfileService;
|
||||||
RefPtr<nsToolkitProfile> mNext;
|
|
||||||
nsToolkitProfile* mPrev;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~nsToolkitProfile() = default;
|
~nsToolkitProfile() = default;
|
||||||
|
|
||||||
nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
|
||||||
nsIFile* aLocalDir, nsToolkitProfile* aPrev);
|
nsIFile* aLocalDir, bool aFromDB);
|
||||||
|
|
||||||
nsresult RemoveInternal(bool aRemoveFiles, bool aInBackground);
|
nsresult RemoveInternal(bool aRemoveFiles, bool aInBackground);
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ class nsToolkitProfile final : public nsIToolkitProfile {
|
||||||
nsCOMPtr<nsIFile> mRootDir;
|
nsCOMPtr<nsIFile> mRootDir;
|
||||||
nsCOMPtr<nsIFile> mLocalDir;
|
nsCOMPtr<nsIFile> mLocalDir;
|
||||||
nsIProfileLock* mLock;
|
nsIProfileLock* mLock;
|
||||||
|
uint32_t mIndex;
|
||||||
|
nsCString mSection;
|
||||||
};
|
};
|
||||||
|
|
||||||
class nsToolkitProfileLock final : public nsIProfileLock {
|
class nsToolkitProfileLock final : public nsIProfileLock {
|
||||||
|
@ -103,6 +105,7 @@ class nsToolkitProfileService final : public nsIToolkitProfileService {
|
||||||
bool MaybeMakeDefaultDedicatedProfile(nsIToolkitProfile* aProfile);
|
bool MaybeMakeDefaultDedicatedProfile(nsIToolkitProfile* aProfile);
|
||||||
bool IsSnapEnvironment();
|
bool IsSnapEnvironment();
|
||||||
nsresult CreateDefaultProfile(nsIToolkitProfile** aResult);
|
nsresult CreateDefaultProfile(nsIToolkitProfile** aResult);
|
||||||
|
void SetNormalDefault(nsIToolkitProfile* aProfile);
|
||||||
|
|
||||||
// Returns the known install hashes from the installs database. Modifying the
|
// Returns the known install hashes from the installs database. Modifying the
|
||||||
// installs database is safe while iterating the returned array.
|
// installs database is safe while iterating the returned array.
|
||||||
|
@ -110,8 +113,8 @@ class nsToolkitProfileService final : public nsIToolkitProfileService {
|
||||||
|
|
||||||
// Tracks whether SelectStartupProfile has been called.
|
// Tracks whether SelectStartupProfile has been called.
|
||||||
bool mStartupProfileSelected;
|
bool mStartupProfileSelected;
|
||||||
// The first profile in a linked list of profiles loaded from profiles.ini.
|
// The profiles loaded from profiles.ini.
|
||||||
RefPtr<nsToolkitProfile> mFirst;
|
mozilla::LinkedList<RefPtr<nsToolkitProfile>> mProfiles;
|
||||||
// The profile selected for use at startup, if it exists in profiles.ini.
|
// The profile selected for use at startup, if it exists in profiles.ini.
|
||||||
nsCOMPtr<nsIToolkitProfile> mCurrent;
|
nsCOMPtr<nsIToolkitProfile> mCurrent;
|
||||||
// The profile selected for this install in installs.ini.
|
// 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.
|
// The directory that holds the cache files for profiles.
|
||||||
nsCOMPtr<nsIFile> mTempData;
|
nsCOMPtr<nsIFile> mTempData;
|
||||||
// The location of profiles.ini.
|
// The location of profiles.ini.
|
||||||
nsCOMPtr<nsIFile> mListFile;
|
nsCOMPtr<nsIFile> mProfileDBFile;
|
||||||
// The location of installs.ini.
|
// The location of installs.ini.
|
||||||
nsCOMPtr<nsIFile> mInstallFile;
|
nsCOMPtr<nsIFile> mInstallDBFile;
|
||||||
// The data loaded from installs.ini.
|
// The data loaded from profiles.ini.
|
||||||
nsINIParser mInstallData;
|
nsINIParser mProfileDB;
|
||||||
// The install hash for the currently running install.
|
// The section in the profiles db for the current install.
|
||||||
nsCString mInstallHash;
|
nsCString mInstallSection;
|
||||||
// Whether to start with the selected profile by default.
|
// Whether to start with the selected profile by default.
|
||||||
bool mStartWithLast;
|
bool mStartWithLast;
|
||||||
// True if during startup it appeared that this is the first run.
|
// True if during startup it appeared that this is the first run.
|
||||||
|
|
|
@ -169,7 +169,7 @@ function writeProfilesIni(profileData) {
|
||||||
getService(Ci.nsIINIParserFactory);
|
getService(Ci.nsIINIParserFactory);
|
||||||
let ini = factory.createINIParser().QueryInterface(Ci.nsIINIParserWriter);
|
let ini = factory.createINIParser().QueryInterface(Ci.nsIINIParserWriter);
|
||||||
|
|
||||||
const { options = {}, profiles = [] } = profileData;
|
const { options = {}, profiles = [], installs = null } = profileData;
|
||||||
|
|
||||||
let { startWithLastProfile = true } = options;
|
let { startWithLastProfile = true } = options;
|
||||||
ini.setString("General", "StartWithLastProfile", startWithLastProfile ? "1" : "0");
|
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);
|
ini.writeFile(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,6 +220,7 @@ function readProfilesIni() {
|
||||||
startWithLastProfile: true,
|
startWithLastProfile: true,
|
||||||
},
|
},
|
||||||
profiles: [],
|
profiles: [],
|
||||||
|
installs: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!target.exists()) {
|
if (!target.exists()) {
|
||||||
|
@ -216,29 +232,52 @@ function readProfilesIni() {
|
||||||
let ini = factory.createINIParser(target);
|
let ini = factory.createINIParser(target);
|
||||||
|
|
||||||
profileData.options.startWithLastProfile = safeGet(ini, "General", "StartWithLastProfile") == "1";
|
profileData.options.startWithLastProfile = safeGet(ini, "General", "StartWithLastProfile") == "1";
|
||||||
|
if (safeGet(ini, "General", "Version") == "2") {
|
||||||
|
profileData.installs = {};
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; true; i++) {
|
let sections = ini.getSections();
|
||||||
let section = `Profile${i}`;
|
while (sections.hasMore()) {
|
||||||
|
let section = sections.getNext();
|
||||||
|
|
||||||
let isRelative = safeGet(ini, section, "IsRelative");
|
if (section == "General") {
|
||||||
if (isRelative === null) {
|
continue;
|
||||||
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("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));
|
profileData.profiles.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
@ -255,6 +294,14 @@ function writeInstallsIni(installData) {
|
||||||
let target = gDataHome.clone();
|
let target = gDataHome.clone();
|
||||||
target.append("installs.ini");
|
target.append("installs.ini");
|
||||||
|
|
||||||
|
if (!installData) {
|
||||||
|
try {
|
||||||
|
target.remove(false);
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { installs = {} } = installData;
|
const { installs = {} } = installData;
|
||||||
|
|
||||||
let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
|
let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
|
||||||
|
@ -283,6 +330,7 @@ function readInstallsIni() {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!target.exists()) {
|
if (!target.exists()) {
|
||||||
|
dump("Missing installs.ini\n");
|
||||||
return installData;
|
return installData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,19 +344,37 @@ function readInstallsIni() {
|
||||||
if (hash != "General") {
|
if (hash != "General") {
|
||||||
installData.installs[hash] = {
|
installData.installs[hash] = {
|
||||||
default: safeGet(ini, hash, "Default"),
|
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;
|
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
|
* 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.
|
* 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 service = getProfileService();
|
||||||
|
|
||||||
let serviceProfiles = Array.from(service.profiles);
|
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));
|
profileData.profiles.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
let hash = xreDirProvider.getInstallHash();
|
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 dedicatedProfile = null;
|
||||||
let snapProfile = null;
|
let snapProfile = null;
|
||||||
|
|
||||||
|
@ -352,6 +419,10 @@ function checkProfileService(profileData = readProfilesIni(), installData = read
|
||||||
} else {
|
} else {
|
||||||
Assert.equal(service.defaultProfile, dedicatedProfile, "Should have seen the right profile selected.");
|
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,
|
path: defaultProfile.leafName,
|
||||||
default: true,
|
default: true,
|
||||||
}],
|
}],
|
||||||
});
|
|
||||||
|
|
||||||
let hash = xreDirProvider.getInstallHash();
|
|
||||||
writeProfilesIni({
|
|
||||||
installs: {
|
installs: {
|
||||||
other: {
|
other: {
|
||||||
default: defaultProfile.leafName,
|
default: defaultProfile.leafName,
|
||||||
|
@ -29,16 +25,16 @@ add_task(async () => {
|
||||||
let { profile: selectedProfile, didCreate } = selectStartupProfile();
|
let { profile: selectedProfile, didCreate } = selectStartupProfile();
|
||||||
|
|
||||||
let profileData = readProfilesIni();
|
let profileData = readProfilesIni();
|
||||||
let installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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(profileData.profiles.length, 2, "Should have the right number of profiles.");
|
||||||
|
|
||||||
Assert.equal(Object.keys(installData.installs).length, 1, "Should be two known installs.");
|
let hash = xreDirProvider.getInstallHash();
|
||||||
Assert.notEqual(installData.installs[hash].default, defaultProfile.leafName, "Should not have marked the original default profile as the default for this install.");
|
Assert.equal(Object.keys(profileData.installs).length, 2, "Should be two known installs.");
|
||||||
Assert.ok(installData.installs[hash].locked, "Should have locked as we created this profile for this install.");
|
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(didCreate, "Should have created a new profile.");
|
||||||
Assert.ok(!selectedProfile.rootDir.equals(defaultProfile), "Should be using a different directory.");
|
Assert.ok(!selectedProfile.rootDir.equals(defaultProfile), "Should be using a different directory.");
|
||||||
|
|
|
@ -18,7 +18,6 @@ add_task(async () => {
|
||||||
service.flush();
|
service.flush();
|
||||||
|
|
||||||
let profileData = readProfilesIni();
|
let profileData = readProfilesIni();
|
||||||
let installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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.");
|
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||||
|
|
||||||
// The new profile hasn't been marked as the default yet!
|
// 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.defaultProfile = newProfile;
|
||||||
service.flush();
|
service.flush();
|
||||||
|
|
||||||
profileData = readProfilesIni();
|
profileData = readProfilesIni();
|
||||||
installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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.");
|
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||||
|
|
||||||
let hash = xreDirProvider.getInstallHash();
|
let hash = xreDirProvider.getInstallHash();
|
||||||
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.");
|
||||||
Assert.equal(installData.installs[hash].default, profileData.profiles[0].path, "Should have marked the new profile as the default for this 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");
|
let otherProfile = service.createProfile(null, "another");
|
||||||
service.defaultProfile = otherProfile;
|
service.defaultProfile = otherProfile;
|
||||||
|
@ -57,7 +55,6 @@ add_task(async () => {
|
||||||
service.flush();
|
service.flush();
|
||||||
|
|
||||||
profileData = readProfilesIni();
|
profileData = readProfilesIni();
|
||||||
installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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(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.equal(profile.name, "dedicated", "Should have the right name.");
|
||||||
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
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(Object.keys(profileData.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(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);
|
newProfile.remove(true);
|
||||||
service.flush();
|
service.flush();
|
||||||
|
|
||||||
profileData = readProfilesIni();
|
profileData = readProfilesIni();
|
||||||
installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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.equal(profile.name, "another", "Should have the right name.");
|
||||||
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
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(Object.keys(profileData.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(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);
|
otherProfile.remove(true);
|
||||||
service.flush();
|
service.flush();
|
||||||
|
|
||||||
profileData = readProfilesIni();
|
profileData = readProfilesIni();
|
||||||
installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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
|
// We leave a reference to the missing profile to stop us trying to steal the
|
||||||
// old-style default profile on next startup.
|
// 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");
|
checkStartupReason("firstrun-created-default");
|
||||||
|
|
||||||
let profileData = readProfilesIni();
|
let profileData = readProfilesIni();
|
||||||
let installData = readInstallsIni();
|
checkProfileService(profileData);
|
||||||
checkProfileService(profileData, installData);
|
|
||||||
|
|
||||||
Assert.ok(didCreate, "Should have created a new profile.");
|
Assert.ok(didCreate, "Should have created a new profile.");
|
||||||
Assert.equal(profile, service.defaultProfile, "Should now be the default 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.");
|
Assert.ok(!profile.default, "Should not be marked as the old-style default.");
|
||||||
|
|
||||||
let hash = xreDirProvider.getInstallHash();
|
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 hash = xreDirProvider.getInstallHash();
|
||||||
let profileData = readProfilesIni();
|
let profileData = readProfilesIni();
|
||||||
let installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
|
||||||
Assert.ok(profile.default, "Should be marked as the old-style default.");
|
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.");
|
||||||
Assert.equal(installData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
|
Assert.equal(profileData.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.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(!didCreate, "Should not have created a new profile.");
|
||||||
Assert.ok(selectedProfile.rootDir.equals(defaultProfile), "Should be using the right directory.");
|
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 hash = xreDirProvider.getInstallHash();
|
||||||
let profileData = readProfilesIni();
|
let profileData = readProfilesIni();
|
||||||
let installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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.equal(profile.path, mydefaultProfile.leafName, "Should be the original default profile.");
|
||||||
Assert.ok(profile.default, "Should be marked as the old-style default.");
|
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) {
|
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 {
|
} 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.");
|
Assert.ok(!didCreate, "Should not have created a new profile.");
|
||||||
if (AppConstants.MOZ_DEV_EDITION) {
|
if (AppConstants.MOZ_DEV_EDITION) {
|
||||||
|
|
|
@ -16,9 +16,6 @@ add_task(async () => {
|
||||||
path: defaultProfile.leafName,
|
path: defaultProfile.leafName,
|
||||||
default: true,
|
default: true,
|
||||||
}],
|
}],
|
||||||
});
|
|
||||||
|
|
||||||
writeInstallsIni({
|
|
||||||
installs: {
|
installs: {
|
||||||
[hash]: {
|
[hash]: {
|
||||||
default: "foobar",
|
default: "foobar",
|
||||||
|
@ -30,7 +27,6 @@ add_task(async () => {
|
||||||
testStartsProfileManager();
|
testStartsProfileManager();
|
||||||
|
|
||||||
let profileData = readProfilesIni();
|
let profileData = readProfilesIni();
|
||||||
let installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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.");
|
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...
|
// 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,
|
path: defaultProfile.leafName,
|
||||||
default: true,
|
default: true,
|
||||||
}],
|
}],
|
||||||
};
|
|
||||||
writeProfilesIni(profilesIni);
|
|
||||||
|
|
||||||
let installsIni = {
|
|
||||||
installs: {
|
installs: {
|
||||||
[hash]: {
|
[hash]: {
|
||||||
default: defaultProfile.leafName,
|
default: defaultProfile.leafName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
writeInstallsIni(installsIni);
|
writeProfilesIni(profilesIni);
|
||||||
|
|
||||||
let service = getProfileService();
|
let service = getProfileService();
|
||||||
checkProfileService(profilesIni, installsIni);
|
checkProfileService(profilesIni);
|
||||||
|
|
||||||
let { profile, didCreate } = selectStartupProfile();
|
let { profile, didCreate } = selectStartupProfile();
|
||||||
Assert.ok(!didCreate, "Should have not created a new profile.");
|
Assert.ok(!didCreate, "Should have not created a new profile.");
|
||||||
Assert.equal(profile.name, "default", "Should have selected the default profile.");
|
Assert.equal(profile.name, "default", "Should have selected the default profile.");
|
||||||
Assert.equal(profile, service.defaultProfile, "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
|
// 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
|
// 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.
|
// These are the modifications that should have been made.
|
||||||
profilesIni.profiles.pop();
|
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();
|
service.flush();
|
||||||
|
|
||||||
|
@ -56,6 +53,6 @@ add_task(async () => {
|
||||||
|
|
||||||
// checkProfileService doesn't differentiate between a blank default profile
|
// checkProfileService doesn't differentiate between a blank default profile
|
||||||
// for the install and a missing install.
|
// for the install and a missing install.
|
||||||
let installs = readInstallsIni();
|
profilesIni = readProfilesIni();
|
||||||
Assert.equal(installs.installs[hash].default, "", "Should be a blank default profile.");
|
Assert.equal(profilesIni.installs[hash].default, "", "Should be a blank default profile.");
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,8 +16,6 @@ add_task(async () => {
|
||||||
name: "Profile3",
|
name: "Profile3",
|
||||||
path: "Path3",
|
path: "Path3",
|
||||||
}],
|
}],
|
||||||
};
|
|
||||||
let installData = {
|
|
||||||
installs: {
|
installs: {
|
||||||
[hash]: {
|
[hash]: {
|
||||||
default: "Path2",
|
default: "Path2",
|
||||||
|
@ -43,7 +41,6 @@ add_task(async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
writeProfilesIni(profileData);
|
writeProfilesIni(profileData);
|
||||||
writeInstallsIni(installData);
|
|
||||||
|
|
||||||
let { profile, didCreate } = selectStartupProfile();
|
let { profile, didCreate } = selectStartupProfile();
|
||||||
checkStartupReason("default");
|
checkStartupReason("default");
|
||||||
|
|
|
@ -22,7 +22,6 @@ add_task(async () => {
|
||||||
|
|
||||||
let hash = xreDirProvider.getInstallHash();
|
let hash = xreDirProvider.getInstallHash();
|
||||||
let profileData = readProfilesIni();
|
let profileData = readProfilesIni();
|
||||||
let installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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.equal(profile.path, defaultProfile.leafName, "Should be the original default profile.");
|
||||||
Assert.ok(profile.default, "Should be marked as the old-style default.");
|
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.");
|
||||||
Assert.equal(installData.installs[hash].default, defaultProfile.leafName, "Should have marked the original default profile as the default for this install.");
|
Assert.equal(profileData.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.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.");
|
Assert.ok(!didCreate, "Should not have created a new profile.");
|
||||||
let service = getProfileService();
|
let service = getProfileService();
|
||||||
|
|
|
@ -23,7 +23,6 @@ add_task(async () => {
|
||||||
let service = getProfileService();
|
let service = getProfileService();
|
||||||
|
|
||||||
let profileData = readProfilesIni();
|
let profileData = readProfilesIni();
|
||||||
let installData = readInstallsIni();
|
|
||||||
|
|
||||||
Assert.ok(profileData.options.startWithLastProfile, "Should be set to start with the last profile.");
|
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.");
|
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.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.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();
|
let { profile: selectedProfile, didCreate } = selectStartupProfile();
|
||||||
checkStartupReason("firstrun-skipped-default");
|
checkStartupReason("firstrun-skipped-default");
|
||||||
|
|
|
@ -26,10 +26,7 @@ add_task(async () => {
|
||||||
name: "Profile3",
|
name: "Profile3",
|
||||||
path: "Path3",
|
path: "Path3",
|
||||||
}],
|
}],
|
||||||
};
|
// Another install is using the profile and it is locked.
|
||||||
|
|
||||||
// Another install is using the profile and it is locked.
|
|
||||||
let installData = {
|
|
||||||
installs: {
|
installs: {
|
||||||
otherinstall: {
|
otherinstall: {
|
||||||
default: root.leafName,
|
default: root.leafName,
|
||||||
|
@ -39,8 +36,7 @@ add_task(async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
writeProfilesIni(profileData);
|
writeProfilesIni(profileData);
|
||||||
writeInstallsIni(installData);
|
checkProfileService(profileData);
|
||||||
checkProfileService(profileData, installData);
|
|
||||||
|
|
||||||
let env = Cc["@mozilla.org/process/environment;1"].
|
let env = Cc["@mozilla.org/process/environment;1"].
|
||||||
getService(Ci.nsIEnvironment);
|
getService(Ci.nsIEnvironment);
|
||||||
|
@ -73,10 +69,9 @@ add_task(async () => {
|
||||||
Assert.ok(!profileData.profiles[1].default, "Should not be the old default profile.");
|
Assert.ok(!profileData.profiles[1].default, "Should not be the old default profile.");
|
||||||
|
|
||||||
let hash = xreDirProvider.getInstallHash();
|
let hash = xreDirProvider.getInstallHash();
|
||||||
installData = readInstallsIni();
|
Assert.equal(Object.keys(profileData.installs).length, 2, "Should be one known install.");
|
||||||
Assert.equal(Object.keys(installData.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.notEqual(installData.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.ok(installData.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.equal(installData.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.");
|
||||||
Assert.ok(installData.installs[hash].locked, "Should still be locked to the other install.");
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,10 +26,7 @@ add_task(async () => {
|
||||||
name: "Profile3",
|
name: "Profile3",
|
||||||
path: "Path3",
|
path: "Path3",
|
||||||
}],
|
}],
|
||||||
};
|
// Another install is using the profile but it isn't locked.
|
||||||
|
|
||||||
// Another install is using the profile but it isn't locked.
|
|
||||||
let installData = {
|
|
||||||
installs: {
|
installs: {
|
||||||
otherinstall: {
|
otherinstall: {
|
||||||
default: root.leafName,
|
default: root.leafName,
|
||||||
|
@ -38,8 +35,7 @@ add_task(async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
writeProfilesIni(profileData);
|
writeProfilesIni(profileData);
|
||||||
writeInstallsIni(installData);
|
checkProfileService(profileData);
|
||||||
checkProfileService(profileData, installData);
|
|
||||||
|
|
||||||
let env = Cc["@mozilla.org/process/environment;1"].
|
let env = Cc["@mozilla.org/process/environment;1"].
|
||||||
getService(Ci.nsIEnvironment);
|
getService(Ci.nsIEnvironment);
|
||||||
|
@ -64,9 +60,8 @@ add_task(async () => {
|
||||||
Assert.ok(profileData.profiles[0].default, "Should still be the old default profile.");
|
Assert.ok(profileData.profiles[0].default, "Should still be the old default profile.");
|
||||||
|
|
||||||
let hash = xreDirProvider.getInstallHash();
|
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.
|
// 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(Object.keys(profileData.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.equal(profileData.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.ok(!profileData.installs[hash].locked, "Should not have locked as we're not the default app.");
|
||||||
});
|
});
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче