summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--application/palemoon/base/content/browser-thumbnails.js4
-rw-r--r--application/palemoon/base/content/browser.css9
-rw-r--r--application/palemoon/base/content/browser.js6
-rw-r--r--application/palemoon/base/content/browser.xul6
-rw-r--r--application/palemoon/base/content/popup-notifications.inc4
-rw-r--r--application/palemoon/base/content/tabbrowser.xml15
-rw-r--r--application/palemoon/components/about/AboutRedirector.cpp87
-rw-r--r--application/palemoon/components/build/nsModule.cpp1
-rw-r--r--application/palemoon/components/downloads/DownloadsCommon.jsm16
-rw-r--r--application/palemoon/components/nsBrowserGlue.js80
-rw-r--r--application/palemoon/locales/en-US/chrome/browser/browser.dtd2
-rw-r--r--application/palemoon/locales/jar.mn2
-rw-r--r--application/palemoon/modules/WindowsPreviewPerTab.jsm9
-rw-r--r--application/palemoon/modules/moz.build6
-rw-r--r--application/palemoon/themes/linux/browser.css10
-rw-r--r--application/palemoon/themes/linux/jar.mn2
-rw-r--r--application/palemoon/themes/osx/browser.css7
-rw-r--r--application/palemoon/themes/osx/jar.mn2
-rw-r--r--application/palemoon/themes/shared/browser.inc5
-rw-r--r--application/palemoon/themes/windows/browser.css7
-rw-r--r--application/palemoon/themes/windows/jar.mn2
-rw-r--r--browser/LICENSE10
-rw-r--r--browser/app/profile/firefox.js12
-rw-r--r--browser/base/content/abouthome/aboutHome.css59
-rw-r--r--browser/base/content/abouthome/aboutHome.js280
-rw-r--r--browser/base/content/abouthome/aboutHome.xhtml11
-rwxr-xr-xbrowser/base/content/browser.js44
-rw-r--r--browser/base/content/tab-content.js5
-rw-r--r--browser/base/content/utilityOverlay.js2
-rw-r--r--browser/branding/unofficial/VisualElements_150.pngbin37693 -> 16524 bytes
-rw-r--r--browser/branding/unofficial/VisualElements_70.pngbin11763 -> 6827 bytes
-rw-r--r--browser/branding/unofficial/branding.nsi18
-rw-r--r--browser/branding/unofficial/configure.sh2
-rw-r--r--browser/branding/unofficial/content/about-background.pngbin88500 -> 63599 bytes
-rw-r--r--browser/branding/unofficial/content/about-logo.pngbin30469 -> 16835 bytes
-rw-r--r--browser/branding/unofficial/content/about-logo@2x.pngbin81662 -> 41517 bytes
-rw-r--r--browser/branding/unofficial/content/about-wordmark.svg101
-rw-r--r--browser/branding/unofficial/content/about.pngbin54712 -> 22324 bytes
-rw-r--r--browser/branding/unofficial/content/icon48.pngbin3442 -> 2909 bytes
-rw-r--r--browser/branding/unofficial/content/icon64.pngbin5096 -> 3850 bytes
-rw-r--r--browser/branding/unofficial/default16.pngbin901 -> 793 bytes
-rw-r--r--browser/branding/unofficial/default32.pngbin2037 -> 1748 bytes
-rw-r--r--browser/branding/unofficial/default48.pngbin3441 -> 2909 bytes
-rw-r--r--browser/branding/unofficial/firefox.icnsbin648208 -> 69916 bytes
-rw-r--r--browser/branding/unofficial/firefox.icobin74588 -> 45953 bytes
-rw-r--r--browser/branding/unofficial/locales/en-US/brand.dtd9
-rw-r--r--browser/branding/unofficial/locales/en-US/brand.properties9
-rw-r--r--browser/branding/unofficial/mozicon128.pngbin13817 -> 9246 bytes
-rw-r--r--browser/branding/unofficial/serpent.VisualElementsManifest.xml (renamed from browser/branding/unofficial/basilisk.VisualElementsManifest.xml)2
-rw-r--r--browser/components/about/AboutRedirector.cpp124
-rw-r--r--browser/components/build/nsModule.cpp1
-rw-r--r--browser/components/nsBrowserContentHandler.js2
-rw-r--r--browser/experiments/.eslintrc.js11
-rw-r--r--browser/experiments/Experiments.jsm2354
-rw-r--r--browser/experiments/Experiments.manifest6
-rw-r--r--browser/experiments/ExperimentsService.js118
-rw-r--r--browser/experiments/Makefile.in16
-rw-r--r--browser/experiments/docs/index.rst13
-rw-r--r--browser/experiments/docs/manifest.rst429
-rw-r--r--browser/experiments/moz.build18
-rw-r--r--browser/experiments/test/addons/experiment-1/install.rdf16
-rw-r--r--browser/experiments/test/addons/experiment-1a/install.rdf16
-rw-r--r--browser/experiments/test/addons/experiment-2/install.rdf16
-rw-r--r--browser/experiments/test/addons/experiment-racybranch/bootstrap.js35
-rw-r--r--browser/experiments/test/addons/experiment-racybranch/install.rdf16
-rw-r--r--browser/experiments/test/xpcshell/.eslintrc.js15
-rw-r--r--browser/experiments/test/xpcshell/experiments_1.manifest19
-rw-r--r--browser/experiments/test/xpcshell/head.js199
-rw-r--r--browser/experiments/test/xpcshell/test_activate.js151
-rw-r--r--browser/experiments/test/xpcshell/test_api.js1647
-rw-r--r--browser/experiments/test/xpcshell/test_cache.js399
-rw-r--r--browser/experiments/test/xpcshell/test_cacherace.js102
-rw-r--r--browser/experiments/test/xpcshell/test_conditions.js325
-rw-r--r--browser/experiments/test/xpcshell/test_disableExperiments.js180
-rw-r--r--browser/experiments/test/xpcshell/test_fetch.js68
-rw-r--r--browser/experiments/test/xpcshell/test_nethang_bug1012924.js47
-rw-r--r--browser/experiments/test/xpcshell/test_previous_provider.js179
-rw-r--r--browser/experiments/test/xpcshell/test_telemetry.js294
-rw-r--r--browser/experiments/test/xpcshell/test_telemetry_disabled.js21
-rw-r--r--browser/experiments/test/xpcshell/test_upgrade.js52
-rw-r--r--browser/experiments/test/xpcshell/xpcshell.ini31
-rw-r--r--browser/modules/AboutHome.jsm25
-rw-r--r--browser/moz.build1
-rw-r--r--build/directive4.py1
-rw-r--r--devtools/client/commandline/test/browser_cmd_csscoverage_startstop.js1
-rw-r--r--devtools/client/framework/browser-menus.js66
-rw-r--r--devtools/client/framework/devtools-browser.js2
-rw-r--r--devtools/client/framework/test/browser_keybindings_01.js3
-rw-r--r--devtools/client/menus.js4
-rw-r--r--devtools/client/scratchpad/test/head.js1
-rw-r--r--devtools/client/webconsole/new-console-output/test/mochitest/head.js1
-rw-r--r--docshell/base/nsAboutRedirector.cpp29
-rw-r--r--docshell/build/nsDocShellModule.cpp3
-rw-r--r--js/src/builtin/ModuleObject.cpp2
-rw-r--r--js/src/builtin/ReflectParse.cpp2
-rw-r--r--old-configure.in1
-rw-r--r--toolkit/components/protobuf/moz.build5
-rw-r--r--toolkit/components/telemetry/TelemetryEnvironment.jsm30
-rw-r--r--toolkit/content/aboutSupport.xhtml33
-rw-r--r--toolkit/content/jar.mn4
-rw-r--r--toolkit/content/memoriam.xhtml76
-rw-r--r--toolkit/content/mozilla.css36
-rw-r--r--toolkit/content/mozilla.xhtml35
-rw-r--r--toolkit/modules/Troubleshoot.jsm19
-rw-r--r--toolkit/mozapps/extensions/content/extensions.js81
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_experiments.js645
-rw-r--r--toolkit/mozapps/webextensions/content/extensions.js84
-rw-r--r--toolkit/mozapps/webextensions/internal/XPIProvider.jsm10
-rw-r--r--toolkit/mozapps/webextensions/test/browser/browser_experiments.js654
109 files changed, 607 insertions, 9013 deletions
diff --git a/application/palemoon/base/content/browser-thumbnails.js b/application/palemoon/base/content/browser-thumbnails.js
index d6fe25231..b06dfd503 100644
--- a/application/palemoon/base/content/browser-thumbnails.js
+++ b/application/palemoon/base/content/browser-thumbnails.js
@@ -95,7 +95,7 @@ let gBrowserThumbnails = {
// Tycho: aCallback([browser.currentURI.spec for (browser of gBrowser.browsers)]);
let result = [];
for (let browser of gBrowser.browsers) {
- result.push(browser.currentURL.spec);
+ result.push(browser.currentURI.spec);
}
aCallback(result);
},
@@ -142,7 +142,7 @@ let gBrowserThumbnails = {
// FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
// that currently regresses Talos SVG tests.
- if (doc instanceof SVGDocument || doc instanceof XMLDocument)
+ if (doc instanceof XMLDocument)
return false;
// There's no point in taking screenshot of loading pages.
diff --git a/application/palemoon/base/content/browser.css b/application/palemoon/base/content/browser.css
index 2c8ba3e69..e6a84421b 100644
--- a/application/palemoon/base/content/browser.css
+++ b/application/palemoon/base/content/browser.css
@@ -115,8 +115,17 @@ toolbar[printpreview="true"] {
#titlebar {
-moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
+ -moz-window-dragging: drag;
}
+%ifdef XP_WIN
+#main-window[tabsontop="true"] #TabsToolbar,
+#main-window[tabsontop="true"] #toolbar-menubar,
+#main-window[tabsontop="true"] #navigator-toolbox > toolbar:-moz-lwtheme {
+ -moz-window-dragging: drag;
+}
+%endif
+
#titlebar-spacer {
pointer-events: none;
}
diff --git a/application/palemoon/base/content/browser.js b/application/palemoon/base/content/browser.js
index ffb133963..328cc4975 100644
--- a/application/palemoon/base/content/browser.js
+++ b/application/palemoon/base/content/browser.js
@@ -153,7 +153,11 @@ let gInitialPages = [
#include browser-plugins.js
#include browser-tabPreviews.js
#include browser-thumbnails.js
+
+#ifdef MOZ_WEBRTC
#include browser-webrtcUI.js
+#endif
+
#include browser-gestureSupport.js
#ifdef MOZ_SERVICES_SYNC
@@ -1020,7 +1024,9 @@ var gBrowserInit = {
OfflineApps.init();
IndexedDBPromptHelper.init();
AddonManager.addAddonListener(AddonsMgrListener);
+#ifdef MOZ_WEBRTC
WebrtcIndicator.init();
+#endif
// Ensure login manager is up and running.
Services.logins;
diff --git a/application/palemoon/base/content/browser.xul b/application/palemoon/base/content/browser.xul
index 3044ce675..30b1838ea 100644
--- a/application/palemoon/base/content/browser.xul
+++ b/application/palemoon/base/content/browser.xul
@@ -440,8 +440,10 @@
<image id="alert-plugins-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="blocked-plugins-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="mixed-content-blocked-notification-icon" class="notification-anchor-icon" role="button"/>
+#ifdef MOZ_WEBRTC
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
+#endif
<image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="servicesInstall-notification-icon" class="notification-anchor-icon" role="button"/>
</box>
@@ -513,7 +515,7 @@
flex="100" persist="width" removable="true">
<searchbar id="searchbar" flex="1"/>
</toolbaritem>
-
+#ifdef MOZ_WEBRTC
<toolbarbutton id="webrtc-status-button"
class="toolbarbutton-1 chromeclass-toolbar-additional"
type="menu"
@@ -525,7 +527,7 @@
onpopuphiding="WebrtcIndicator.clearPopup(this);"
oncommand="WebrtcIndicator.menuCommand(event.target);"/>
</toolbarbutton>
-
+#endif
<toolbarbutton id="bookmarks-menu-button"
class="toolbarbutton-1 chromeclass-toolbar-additional"
persist="class"
diff --git a/application/palemoon/base/content/popup-notifications.inc b/application/palemoon/base/content/popup-notifications.inc
index 04f4cb5b7..3112de597 100644
--- a/application/palemoon/base/content/popup-notifications.inc
+++ b/application/palemoon/base/content/popup-notifications.inc
@@ -54,7 +54,7 @@
</hbox>
</panel>
-
+#ifdef MOZ_WEBRTC
<popupnotification id="webRTC-shareDevices-notification" hidden="true">
<popupnotificationcontent id="webRTC-selectCamera" orient="vertical">
<separator class="thin"/>
@@ -75,7 +75,7 @@
</menulist>
</popupnotificationcontent>
</popupnotification>
-
+#endif
<popupnotification id="servicesInstall-notification" hidden="true">
<popupnotificationcontent orient="vertical" align="start">
<!-- XXX bug 974146, tests are looking for this, can't remove yet. -->
diff --git a/application/palemoon/base/content/tabbrowser.xml b/application/palemoon/base/content/tabbrowser.xml
index 3e30c2ba1..ef0af5afb 100644
--- a/application/palemoon/base/content/tabbrowser.xml
+++ b/application/palemoon/base/content/tabbrowser.xml
@@ -707,7 +707,8 @@
let autocomplete = this.mTabBrowser._placesAutocomplete;
if (this.mBrowser.registeredOpenURI) {
- autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
+ autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI,
+ this.mBrowser.getAttribute("usercontextid") || 0);
delete this.mBrowser.registeredOpenURI;
}
// Tabs in private windows aren't registered as "Open" so
@@ -715,7 +716,8 @@
if (!isBlankPageURL(aLocation.spec) &&
(!PrivateBrowsingUtils.isWindowPrivate(window) ||
PrivateBrowsingUtils.permanentPrivateBrowsing)) {
- autocomplete.registerOpenPage(aLocation);
+ autocomplete.registerOpenPage(aLocation,
+ this.mBrowser.getAttribute("usercontextid") || 0);
this.mBrowser.registeredOpenURI = aLocation;
}
}
@@ -1865,7 +1867,8 @@
this.mTabListeners[aTab._tPos].destroy();
if (browser.registeredOpenURI && !aTabWillBeMoved) {
- this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
+ this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI,
+ browser.getAttribute("usercontextid") || 0);
delete browser.registeredOpenURI;
}
@@ -2209,7 +2212,8 @@
<![CDATA[
// If the current URI is registered as open remove it from the list.
if (aOurBrowser.registeredOpenURI) {
- this._placesAutocomplete.unregisterOpenPage(aOurBrowser.registeredOpenURI);
+ this._placesAutocomplete.unregisterOpenPage(aOurBrowser.registeredOpenURI,
+ aOurBrowser.getAttribute("usercontextid") || 0);
delete aOurBrowser.registeredOpenURI;
}
@@ -3066,7 +3070,8 @@
for (var i = 0; i < this.mTabListeners.length; ++i) {
let browser = this.getBrowserAtIndex(i);
if (browser.registeredOpenURI) {
- this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
+ this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI,
+ browser.getAttribute("usercontextid") || 0);
delete browser.registeredOpenURI;
}
browser.webProgress.removeProgressListener(this.mTabFilters[i]);
diff --git a/application/palemoon/components/about/AboutRedirector.cpp b/application/palemoon/components/about/AboutRedirector.cpp
index b5dd4abfb..d52b873b9 100644
--- a/application/palemoon/components/about/AboutRedirector.cpp
+++ b/application/palemoon/components/about/AboutRedirector.cpp
@@ -30,17 +30,47 @@ struct RedirEntry {
URI_SAFE_FOR_UNTRUSTED_CONTENT.
*/
static RedirEntry kRedirMap[] = {
- { "certerror", "chrome://browser/content/certerror/aboutCertError.xhtml",
+ {
+ "certerror", "chrome://browser/content/certerror/aboutCertError.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "feeds", "chrome://browser/content/feeds/subscribe.xhtml",
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "feeds", "chrome://browser/content/feeds/subscribe.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "privatebrowsing", "chrome://browser/content/aboutPrivateBrowsing.xhtml",
- nsIAboutModule::ALLOW_SCRIPT },
- { "rights",
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::MAKE_LINKABLE |
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "newtab", "chrome://browser/content/newtab/newTab.xul",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "palemoon", "chrome://global/content/memoriam.xhtml",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "privatebrowsing", "chrome://browser/content/aboutPrivateBrowsing.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "rights",
#ifdef MOZ_OFFICIAL_BRANDING
"chrome://global/content/aboutRights.xhtml",
#else
@@ -48,34 +78,27 @@ static RedirEntry kRedirMap[] = {
#endif
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::MAKE_LINKABLE |
- nsIAboutModule::ALLOW_SCRIPT },
- { "palemoon", "chrome://global/content/palemoon.xhtml",
- nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "logopage", "chrome://global/content/logopage.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "robots", "chrome://browser/content/aboutRobots.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "robots", "chrome://browser/content/aboutRobots.xhtml",
- nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::ALLOW_SCRIPT },
- { "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
- nsIAboutModule::ALLOW_SCRIPT },
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
#ifdef MOZ_SERVICES_SYNC
- { "sync-progress", "chrome://browser/content/sync/progress.xhtml",
- nsIAboutModule::ALLOW_SCRIPT },
- { "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
- nsIAboutModule::ALLOW_SCRIPT },
+ {
+ "sync-progress", "chrome://browser/content/sync/progress.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
#endif
- { "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
- nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::MAKE_LINKABLE |
- nsIAboutModule::ALLOW_SCRIPT },
- { "newtab", "chrome://browser/content/newtab/newTab.xul",
- nsIAboutModule::ALLOW_SCRIPT },
- { "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
- nsIAboutModule::ALLOW_SCRIPT },
- { "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
- nsIAboutModule::ALLOW_SCRIPT },
};
static const int kRedirTotal = ArrayLength(kRedirMap);
diff --git a/application/palemoon/components/build/nsModule.cpp b/application/palemoon/components/build/nsModule.cpp
index d5b79b455..fad87d831 100644
--- a/application/palemoon/components/build/nsModule.cpp
+++ b/application/palemoon/components/build/nsModule.cpp
@@ -91,7 +91,6 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "palemoon", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
- { NS_ABOUT_MODULE_CONTRACTID_PREFIX "logopage", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "robots", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
#ifdef MOZ_SERVICES_SYNC
diff --git a/application/palemoon/components/downloads/DownloadsCommon.jsm b/application/palemoon/components/downloads/DownloadsCommon.jsm
index b90baaf9c..0921f8400 100644
--- a/application/palemoon/components/downloads/DownloadsCommon.jsm
+++ b/application/palemoon/components/downloads/DownloadsCommon.jsm
@@ -867,9 +867,19 @@ DownloadsDataCtor.prototype = {
// Sort backwards by start time, ensuring that the most recent
// downloads are added first regardless of their state.
- let loadedItemsArray = [dataItem
- for each (dataItem in this.dataItems)
- if (dataItem)];
+ // Tycho:
+ //let loadedItemsArray = [dataItem
+ // for each (dataItem in this.dataItems)
+ // if (dataItem)];
+
+ let loadedItemsArray = [];
+
+ for each (let dataItem in this.dataItems) {
+ if (dataItem) {
+ loadedItemsArray.push(dataItem);
+ }
+ }
+
loadedItemsArray.sort(function(a, b) b.startTime - a.startTime);
loadedItemsArray.forEach(
function (dataItem) aView.onDataItemAdded(dataItem, false)
diff --git a/application/palemoon/components/nsBrowserGlue.js b/application/palemoon/components/nsBrowserGlue.js
index 5b7fdde33..aafdced9f 100644
--- a/application/palemoon/components/nsBrowserGlue.js
+++ b/application/palemoon/components/nsBrowserGlue.js
@@ -13,59 +13,29 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
- "resource://gre/modules/AddonManager.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
- "resource://gre/modules/NetUtil.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "UserAgentOverrides",
- "resource://gre/modules/UserAgentOverrides.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
- "resource://gre/modules/FileUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
- "resource://gre/modules/BookmarkHTMLUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "BookmarkJSONUtils",
- "resource://gre/modules/BookmarkJSONUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
- "resource://gre/modules/PageThumbs.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
- "resource://gre/modules/NewTabUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader",
- "resource:///modules/BrowserNewTabPreloader.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "webrtcUI",
- "resource:///modules/webrtcUI.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
- "resource:///modules/RecentWindow.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Task",
- "resource://gre/modules/Task.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
- "resource://gre/modules/PlacesBackups.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "OS",
- "resource://gre/modules/osfile.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
- "resource://gre/modules/LoginManagerParent.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
- "resource:///modules/FormValidationHandler.jsm");
+// Define Lazy Module Gitters
+[
+ ["AddonManager", "resource://gre/modules/AddonManager.jsm"],
+ ["NetUtil", "resource://gre/modules/NetUtil.jsm"],
+ ["UserAgentOverrides", "resource://gre/modules/UserAgentOverrides.jsm"],
+ ["FileUtils", "resource://gre/modules/FileUtils.jsm"],
+ ["PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"],
+ ["BookmarkHTMLUtils", "resource://gre/modules/BookmarkHTMLUtils.jsm"],
+ ["BookmarkJSONUtils", "resource://gre/modules/BookmarkJSONUtils.jsm"],
+ ["PageThumbs", "resource://gre/modules/PageThumbs.jsm"],
+ ["NewTabUtils", "resource://gre/modules/NewTabUtils.jsm"],
+ ["BrowserNewTabPreloader", "resource:///modules/BrowserNewTabPreloader.jsm"],
+#ifdef MOZ_WEBRTC
+ ["webrtcUI", "resource:///modules/webrtcUI.jsm"],
+#endif
+ ["PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"],
+ ["RecentWindow", "resource:///modules/RecentWindow.jsm"],
+ ["Task", "resource://gre/modules/Task.jsm"],
+ ["PlacesBackups", "resource://gre/modules/PlacesBackups.jsm"],
+ ["OS", "resource://gre/modules/osfile.jsm"],
+ ["LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"],
+ ["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"],
+].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
@@ -427,7 +397,9 @@ BrowserGlue.prototype = {
PageThumbs.init();
NewTabUtils.init();
BrowserNewTabPreloader.init();
+#ifdef MOZ_WEBRTC
webrtcUI.init();
+#endif
FormValidationHandler.init();
LoginManagerParent.init();
@@ -534,7 +506,9 @@ BrowserGlue.prototype = {
_onProfileShutdown: function BG__onProfileShutdown() {
BrowserNewTabPreloader.uninit();
UserAgentOverrides.uninit();
+#ifdef MOZ_WEBRTC
webrtcUI.uninit();
+#endif
FormValidationHandler.uninit();
this._dispose();
},
diff --git a/application/palemoon/locales/en-US/chrome/browser/browser.dtd b/application/palemoon/locales/en-US/chrome/browser/browser.dtd
index 0225f422b..65cc34fa0 100644
--- a/application/palemoon/locales/en-US/chrome/browser/browser.dtd
+++ b/application/palemoon/locales/en-US/chrome/browser/browser.dtd
@@ -642,8 +642,10 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
<!ENTITY getUserMedia.selectMicrophone.accesskey "M">
+#ifdef MOZ_WEBRTC
<!ENTITY webrtcIndicatorButton.label "Camera / Microphone Access">
<!ENTITY webrtcIndicatorButton.tooltip "Display sites you are currently sharing your camera or microphone with">
+#endif
<!ENTITY mixedContentBlocked.moreinfo "Most websites will still work properly even when this content is blocked.">
diff --git a/application/palemoon/locales/jar.mn b/application/palemoon/locales/jar.mn
index 451a86ab7..6512c683e 100644
--- a/application/palemoon/locales/jar.mn
+++ b/application/palemoon/locales/jar.mn
@@ -16,7 +16,7 @@
locale/browser/syncProgress.dtd (%chrome/browser/syncProgress.dtd)
locale/browser/aboutSyncTabs.dtd (%chrome/browser/aboutSyncTabs.dtd)
#endif
- locale/browser/browser.dtd (%chrome/browser/browser.dtd)
+* locale/browser/browser.dtd (%chrome/browser/browser.dtd)
locale/browser/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)
locale/browser/charsetOverlay.dtd (%chrome/browser/charsetOverlay.dtd)
locale/browser/browser.properties (%chrome/browser/browser.properties)
diff --git a/application/palemoon/modules/WindowsPreviewPerTab.jsm b/application/palemoon/modules/WindowsPreviewPerTab.jsm
index 41b38f0cf..e186bcad2 100644
--- a/application/palemoon/modules/WindowsPreviewPerTab.jsm
+++ b/application/palemoon/modules/WindowsPreviewPerTab.jsm
@@ -510,7 +510,14 @@ TabWindow.prototype = {
// Previews are internally stored using a map, so we need to iterate over
// the tabbrowser's array of tabs to retrieve previews in the same order.
- let inorder = [previews.get(t) for (t of tabs) if (previews.has(t))];
+ // Tycho: let inorder = [previews.get(t) for (t of tabs) if (previews.has(t))];
+ let inorder = [];
+
+ for (let t of tabs) {
+ if (previews.has(t)) {
+ inorder.push(previews.get(t));
+ }
+ }
// Since the internal taskbar array has not yet been updated, we must force
// the sorting order of our local array on it. To do so, we must walk
diff --git a/application/palemoon/modules/moz.build b/application/palemoon/modules/moz.build
index b3459bcd5..7620b9862 100644
--- a/application/palemoon/modules/moz.build
+++ b/application/palemoon/modules/moz.build
@@ -19,10 +19,12 @@ EXTRA_JS_MODULES += [
'PageMenu.jsm',
'PopupNotifications.jsm',
'QuotaManager.jsm',
- 'SharedFrame.jsm',
- 'webrtcUI.jsm'
+ 'SharedFrame.jsm'
]
+if CONFIG['MOZ_WEBRTC']:
+ EXTRA_JS_MODULES += ['webrtcUI.jsm']
+
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
EXTRA_JS_MODULES += [
'Windows8WindowFrameColor.jsm',
diff --git a/application/palemoon/themes/linux/browser.css b/application/palemoon/themes/linux/browser.css
index 07e9dae9c..33c242909 100644
--- a/application/palemoon/themes/linux/browser.css
+++ b/application/palemoon/themes/linux/browser.css
@@ -728,9 +728,11 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
opacity: .4;
}
+%ifdef MOZ_WEBRTC
#webrtc-status-button {
-moz-image-region: rect(0px 192px 24px 168px);
}
+%endif
/* 16px primary toolbar buttons */
toolbar[iconsize="small"] .toolbarbutton-1:not([type="menu-button"]) {
@@ -812,7 +814,9 @@ toolbar[iconsize="small"] #downloads-button {
-moz-image-region: rect(0px 16px 16px 0px);
}
+%ifdef MOZ_WEBRTC
toolbar[iconsize="small"] #webrtc-status-button /* temporary placeholder (bug 824825) */,
+%endif
toolbar[iconsize="small"] #history-button,
toolbar[iconsize="small"] #history-menu-button {
-moz-image-region: rect(0px 32px 16px 16px);
@@ -884,9 +888,11 @@ toolbar[iconsize="small"] #feed-button {
-moz-image-region: rect(0px 112px 16px 96px);
}
+%ifdef MOZ_WEBRTC
toolbar[iconsize="small"] #webrtc-status-button {
-moz-image-region: rect(0px 128px 16px 112px);
}
+%endif
/* Fullscreen window controls */
#window-controls {
@@ -1226,10 +1232,12 @@ toolbar[iconsize="small"] #webrtc-status-button {
list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
}
+%ifdef MOZ_WEBRTC
.popup-notification-icon[popupid="webRTC-sharingDevices"],
.popup-notification-icon[popupid="webRTC-shareDevices"] {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
}
+%endif
.popup-notification-icon[popupid="pointerLock"] {
list-style-image: url(chrome://browser/skin/pointerLock-64.png);
@@ -1352,6 +1360,7 @@ toolbar[iconsize="small"] #webrtc-status-button {
list-style-image: url(chrome://browser/skin/mixed-content-blocked-16.png);
}
+%ifdef MOZ_WEBRTC
.webRTC-shareDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16.png);
@@ -1361,6 +1370,7 @@ toolbar[iconsize="small"] #webrtc-status-button {
#webRTC-sharingDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-sharingDevice-16.png);
}
+%endif
.web-notifications-notification-icon,
#web-notifications-notification-icon {
diff --git a/application/palemoon/themes/linux/jar.mn b/application/palemoon/themes/linux/jar.mn
index a756edbc3..44d837778 100644
--- a/application/palemoon/themes/linux/jar.mn
+++ b/application/palemoon/themes/linux/jar.mn
@@ -53,9 +53,11 @@ browser.jar:
skin/classic/browser/Toolbar.png
skin/classic/browser/Toolbar-small.png
skin/classic/browser/urlbar-arrow.png
+#ifdef MOZ_WEBRTC
skin/classic/browser/webRTC-shareDevice-16.png
skin/classic/browser/webRTC-shareDevice-64.png
skin/classic/browser/webRTC-sharingDevice-16.png
+#endif
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
skin/classic/browser/downloads/download-glow.png (downloads/download-glow.png)
skin/classic/browser/downloads/download-glow-small.png (downloads/download-glow-small.png)
diff --git a/application/palemoon/themes/osx/browser.css b/application/palemoon/themes/osx/browser.css
index 58348a408..882a0c39c 100644
--- a/application/palemoon/themes/osx/browser.css
+++ b/application/palemoon/themes/osx/browser.css
@@ -801,10 +801,11 @@ toolbar[brighttext] #bookmarks-menu-button.bookmark-item {
-moz-image-region: rect(0, 342px, 18px, 324px);
}
+%ifdef MOZ_WEBRTC
#webrtc-status-button {
-moz-image-region: rect(0, 360px, 18px, 342px);
}
-
+%endif
/* ::::: fullscreen window controls ::::: */
@@ -1955,11 +1956,13 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
}
+%ifdef MOZ_WEBRTC
.popup-notification-icon[popupid="webRTC-sharingDevices"],
.popup-notification-icon[popupid="webRTC-shareDevices"] {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
}
+%endif
.popup-notification-icon[popupid="pointerLock"] {
list-style-image: url(chrome://browser/skin/pointerLock-64.png);
}
@@ -2079,6 +2082,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
list-style-image: url(chrome://browser/skin/mixed-content-blocked-16.png);
}
+%ifdef MOZ_WEBRTC
.webRTC-shareDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16.png);
@@ -2088,6 +2092,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
#webRTC-sharingDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-sharingDevice-16.png);
}
+%endif
.web-notifications-notification-icon,
#web-notifications-notification-icon {
diff --git a/application/palemoon/themes/osx/jar.mn b/application/palemoon/themes/osx/jar.mn
index 00575bac8..8742f1b87 100644
--- a/application/palemoon/themes/osx/jar.mn
+++ b/application/palemoon/themes/osx/jar.mn
@@ -72,9 +72,11 @@ browser.jar:
skin/classic/browser/notification-pluginNormal.png (../shared/plugins/notification-pluginNormal.png)
skin/classic/browser/notification-pluginAlert.png (../shared/plugins/notification-pluginAlert.png)
skin/classic/browser/notification-pluginBlocked.png (../shared/plugins/notification-pluginBlocked.png)
+#ifdef MOZ_WEBRTC
skin/classic/browser/webRTC-shareDevice-16.png
skin/classic/browser/webRTC-shareDevice-64.png
skin/classic/browser/webRTC-sharingDevice-16.png
+#endif
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
skin/classic/browser/downloads/download-glow.png (downloads/download-glow.png)
skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
diff --git a/application/palemoon/themes/shared/browser.inc b/application/palemoon/themes/shared/browser.inc
index cd17903ce..18e69ad43 100644
--- a/application/palemoon/themes/shared/browser.inc
+++ b/application/palemoon/themes/shared/browser.inc
@@ -1,3 +1,8 @@
%filter substitution
+%ifndef MOZ_WEBRTC
+%define primaryToolbarButtons #back-button, #forward-button, #reload-button, #stop-button, #home-button, #print-button, #downloads-button, #downloads-indicator, #history-button, #history-menu-button, #bookmarks-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-in-button, #sync-button, #feed-button, #alltabs-button
+%else
%define primaryToolbarButtons #back-button, #forward-button, #reload-button, #stop-button, #home-button, #print-button, #downloads-button, #downloads-indicator, #history-button, #history-menu-button, #bookmarks-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-in-button, #sync-button, #feed-button, #alltabs-button, #webrtc-status-button
+%endif
+
diff --git a/application/palemoon/themes/windows/browser.css b/application/palemoon/themes/windows/browser.css
index 7c837764d..75e144a39 100644
--- a/application/palemoon/themes/windows/browser.css
+++ b/application/palemoon/themes/windows/browser.css
@@ -1192,10 +1192,11 @@ toolbar[brighttext] #bookmarks-menu-button.bookmark-item {
-moz-image-region: rect(0, 342px, 18px, 324px);
}
+%ifdef MOZ_WEBRTC
#webrtc-status-button {
-moz-image-region: rect(0, 360px, 18px, 342px);
}
-
+%endif
/* ::::: fullscreen window controls ::::: */
@@ -2443,10 +2444,12 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
}
+%ifdef MOZ_WEBRTC
.popup-notification-icon[popupid="webRTC-sharingDevices"],
.popup-notification-icon[popupid="webRTC-shareDevices"] {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
}
+%endif
.popup-notification-icon[popupid="pointerLock"] {
list-style-image: url(chrome://browser/skin/pointerLock-64.png);
@@ -2567,6 +2570,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
list-style-image: url(chrome://browser/skin/mixed-content-blocked-16.png);
}
+%ifdef MOZ_WEBRTC
.webRTC-shareDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16.png);
@@ -2576,6 +2580,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
#webRTC-sharingDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-sharingDevice-16.png);
}
+%endif
.web-notifications-notification-icon,
#web-notifications-notification-icon {
diff --git a/application/palemoon/themes/windows/jar.mn b/application/palemoon/themes/windows/jar.mn
index 1c1f139de..f708d39c0 100644
--- a/application/palemoon/themes/windows/jar.mn
+++ b/application/palemoon/themes/windows/jar.mn
@@ -74,9 +74,11 @@ browser.jar:
skin/classic/browser/notification-pluginNormal.png (../shared/plugins/notification-pluginNormal.png)
skin/classic/browser/notification-pluginAlert.png (../shared/plugins/notification-pluginAlert.png)
skin/classic/browser/notification-pluginBlocked.png (../shared/plugins/notification-pluginBlocked.png)
+#ifdef MOZ_WEBRTC
skin/classic/browser/webRTC-shareDevice-16.png
skin/classic/browser/webRTC-shareDevice-64.png
skin/classic/browser/webRTC-sharingDevice-16.png
+#endif
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
skin/classic/browser/downloads/download-glow.png (downloads/download-glow.png)
skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
diff --git a/browser/LICENSE b/browser/LICENSE
index 99d9d6bcd..f1b2067a7 100644
--- a/browser/LICENSE
+++ b/browser/LICENSE
@@ -2,6 +2,10 @@ Please see the file ../toolkit/content/license.html for the copyright
licensing conditions attached to this codebase, including copies of the
licenses concerned.
-You are not granted rights or licenses to the trademarks of the
-Mozilla Foundation or any party, including without limitation the
-Firefox name or logo.
+You are not granted rights or licenses to the trademarks of Moonchild
+Productions or any other party, including without limitation the
+Basilisk name or logo.
+
+The Serpent logo in branding/unofficial is derived from "Sea Serpent"
+by Lorc, licensed under the Creative Commons license CC-BY 3.0
+
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index d6de538d7..0ef9d4ab5 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -229,11 +229,6 @@ pref("browser.slowStartup.notificationDisabled", false);
pref("browser.slowStartup.timeThreshold", 40000);
pref("browser.slowStartup.maxSamples", 5);
-// This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
-// this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
-// repackager of this code using an alternate snippet url, please keep your users safe
-pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
-
pref("browser.enable_automatic_image_resizing", true);
pref("browser.casting.enabled", false);
pref("browser.chrome.site_icons", true);
@@ -1397,13 +1392,6 @@ pref("browser.translation.engine", "bing");
// Determines if Telemetry pings can be archived locally.
pref("toolkit.telemetry.archive.enabled", true);
-// Telemetry experiments settings.
-pref("experiments.enabled", true);
-pref("experiments.manifest.fetchIntervalSeconds", 86400);
-pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
-// Whether experiments are supported by the current application profile.
-pref("experiments.supported", true);
-
// Enable GMP support in the addon manager.
pref("media.gmp-provider.enabled", true);
diff --git a/browser/base/content/abouthome/aboutHome.css b/browser/base/content/abouthome/aboutHome.css
index c0b02e257..bc3f9882c 100644
--- a/browser/base/content/abouthome/aboutHome.css
+++ b/browser/base/content/abouthome/aboutHome.css
@@ -49,8 +49,7 @@ a {
background-repeat: no-repeat;
}
-#searchIconAndTextContainer,
-#snippets {
+#searchIconAndTextContainer {
width: 470px;
}
@@ -168,48 +167,6 @@ a {
transition-duration: 0ms;
}
-#defaultSnippet1,
-#defaultSnippet2,
-#rightsSnippet {
- display: block;
- min-height: 38px;
- background: 0 center no-repeat;
- padding: 6px 0;
- padding-inline-start: 49px;
-}
-
-#rightsSnippet[hidden] {
- display: none;
-}
-
-#defaultSnippet1:dir(rtl),
-#defaultSnippet2:dir(rtl),
-#rightsSnippet:dir(rtl) {
- background-position: right 0 center;
-}
-
-#defaultSnippet1 {
- background-image: url("chrome://browser/content/abouthome/snippet1.png");
-}
-
-#defaultSnippet2 {
- background-image: url("chrome://browser/content/abouthome/snippet2.png");
-}
-
-#snippets {
- display: inline-block;
- text-align: start;
- margin: 12px 0;
- color: #3c3c3c;
- font-size: 75%;
- /* 12px is the computed font size, 15px the computed line height of the snippets
- with Segoe UI on a default Windows 7 setup. The 15/12 multiplier approximately
- converts em from units of font-size to units of line-height. The goal is to
- preset the height of a three-line snippet to avoid visual moving/flickering as
- the snippets load. */
- min-height: calc(15/12 * 3em);
-}
-
#launcher {
display: -moz-box;
-moz-box-align: center;
@@ -385,20 +342,6 @@ body[narrow] #restorePreviousSession::before {
background-image: url("chrome://branding/content/about-logo@2x.png");
}
- #defaultSnippet1,
- #defaultSnippet2,
- #rightsSnippet {
- background-size: 40px;
- }
-
- #defaultSnippet1 {
- background-image: url("chrome://browser/content/abouthome/snippet1@2x.png");
- }
-
- #defaultSnippet2 {
- background-image: url("chrome://browser/content/abouthome/snippet2@2x.png");
- }
-
.launchButton::before,
#aboutMozilla::before {
transform: scale(.5);
diff --git a/browser/base/content/abouthome/aboutHome.js b/browser/base/content/abouthome/aboutHome.js
index 50f3e01cd..0cbcc835a 100644
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -6,23 +6,10 @@
/* import-globals-from ../contentSearchUI.js */
-// The process of adding a new default snippet involves:
-// * add a new entity to aboutHome.dtd
-// * add a <span/> for it in aboutHome.xhtml
-// * add an entry here in the proper ordering (based on spans)
-// The <a/> part of the snippet will be linked to the corresponding url.
-const DEFAULT_SNIPPETS_URLS = [
- "https://www.mozilla.org/firefox/features/?utm_source=snippet&utm_medium=snippet&utm_campaign=default+feature+snippet"
-, "https://addons.mozilla.org/firefox/?utm_source=snippet&utm_medium=snippet&utm_campaign=addons"
-];
-
-const SNIPPETS_UPDATE_INTERVAL_MS = 14400000; // 4 hours.
-
// IndexedDB storage constants.
const DATABASE_NAME = "abouthome";
const DATABASE_VERSION = 1;
const DATABASE_STORAGE = "persistent";
-const SNIPPETS_OBJECTSTORE_NAME = "snippets";
var searchText;
// This global tracks if the page has been set up before, to prevent double inits
@@ -33,13 +20,6 @@ var gObserver = new MutationObserver(function (mutations) {
if (mutation.attributeName == "session") {
fitToWidth();
}
- if (mutation.attributeName == "snippetsVersion") {
- if (!gInitialized) {
- ensureSnippetsMapThen(loadSnippets);
- gInitialized = true;
- }
- return;
- }
}
});
@@ -90,126 +70,6 @@ window.addEventListener("keypress", ev => {
searchText.value += ev.key;
});
-// This object has the same interface as Map and is used to store and retrieve
-// the snippets data. It is lazily initialized by ensureSnippetsMapThen(), so
-// be sure its callback returned before trying to use it.
-var gSnippetsMap;
-var gSnippetsMapCallbacks = [];
-
-/**
- * Ensure the snippets map is properly initialized.
- *
- * @param aCallback
- * Invoked once the map has been initialized, gets the map as argument.
- * @note Snippets should never directly manage the underlying storage, since
- * it may change inadvertently.
- */
-function ensureSnippetsMapThen(aCallback)
-{
- if (gSnippetsMap) {
- aCallback(gSnippetsMap);
- return;
- }
-
- // Handle multiple requests during the async initialization.
- gSnippetsMapCallbacks.push(aCallback);
- if (gSnippetsMapCallbacks.length > 1) {
- // We are already updating, the callbacks will be invoked when done.
- return;
- }
-
- let invokeCallbacks = function () {
- if (!gSnippetsMap) {
- gSnippetsMap = Object.freeze(new Map());
- }
-
- for (let callback of gSnippetsMapCallbacks) {
- callback(gSnippetsMap);
- }
- gSnippetsMapCallbacks.length = 0;
- }
-
- let openRequest = indexedDB.open(DATABASE_NAME, {version: DATABASE_VERSION,
- storage: DATABASE_STORAGE});
-
- openRequest.onerror = function (event) {
- // Try to delete the old database so that we can start this process over
- // next time.
- indexedDB.deleteDatabase(DATABASE_NAME);
- invokeCallbacks();
- };
-
- openRequest.onupgradeneeded = function (event) {
- let db = event.target.result;
- if (!db.objectStoreNames.contains(SNIPPETS_OBJECTSTORE_NAME)) {
- db.createObjectStore(SNIPPETS_OBJECTSTORE_NAME);
- }
- }
-
- openRequest.onsuccess = function (event) {
- let db = event.target.result;
-
- db.onerror = function (event) {
- invokeCallbacks();
- }
-
- db.onversionchange = function (event) {
- event.target.close();
- invokeCallbacks();
- }
-
- let cache = new Map();
- let cursorRequest;
- try {
- cursorRequest = db.transaction(SNIPPETS_OBJECTSTORE_NAME)
- .objectStore(SNIPPETS_OBJECTSTORE_NAME).openCursor();
- } catch (ex) {
- console.error(ex);
- invokeCallbacks();
- return;
- }
-
- cursorRequest.onerror = function (event) {
- invokeCallbacks();
- }
-
- cursorRequest.onsuccess = function(event) {
- let cursor = event.target.result;
-
- // Populate the cache from the persistent storage.
- if (cursor) {
- cache.set(cursor.key, cursor.value);
- cursor.continue();
- return;
- }
-
- // The cache has been filled up, create the snippets map.
- gSnippetsMap = Object.freeze({
- get: (aKey) => cache.get(aKey),
- set: function (aKey, aValue) {
- db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
- .objectStore(SNIPPETS_OBJECTSTORE_NAME).put(aValue, aKey);
- return cache.set(aKey, aValue);
- },
- has: (aKey) => cache.has(aKey),
- delete: function (aKey) {
- db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
- .objectStore(SNIPPETS_OBJECTSTORE_NAME).delete(aKey);
- return cache.delete(aKey);
- },
- clear: function () {
- db.transaction(SNIPPETS_OBJECTSTORE_NAME, "readwrite")
- .objectStore(SNIPPETS_OBJECTSTORE_NAME).clear();
- return cache.clear();
- },
- get size() { return cache.size; },
- });
-
- setTimeout(invokeCallbacks, 0);
- }
- }
-}
-
function onSearchSubmit(aEvent)
{
gContentSearchController.search(aEvent);
@@ -246,146 +106,6 @@ function setupSearch()
*/
function loadCompleted()
{
- var event = new CustomEvent("AboutHomeLoadSnippetsCompleted", {bubbles:true});
- document.dispatchEvent(event);
-}
-
-/**
- * Update the local snippets from the remote storage, then show them through
- * showSnippets.
- */
-function loadSnippets()
-{
- if (!gSnippetsMap)
- throw new Error("Snippets map has not properly been initialized");
-
- // Allow tests to modify the snippets map before using it.
- var event = new CustomEvent("AboutHomeLoadSnippets", {bubbles:true});
- document.dispatchEvent(event);
-
- // Check cached snippets version.
- let cachedVersion = gSnippetsMap.get("snippets-cached-version") || 0;
- let currentVersion = document.documentElement.getAttribute("snippetsVersion");
- if (cachedVersion < currentVersion) {
- // The cached snippets are old and unsupported, restart from scratch.
- gSnippetsMap.clear();
- }
-
- // Check last snippets update.
- let lastUpdate = gSnippetsMap.get("snippets-last-update");
- let updateURL = document.documentElement.getAttribute("snippetsURL");
- let shouldUpdate = !lastUpdate ||
- Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
- if (updateURL && shouldUpdate) {
- // Try to update from network.
- let xhr = new XMLHttpRequest();
- xhr.timeout = 5000;
- // Even if fetching should fail we don't want to spam the server, thus
- // set the last update time regardless its results. Will retry tomorrow.
- gSnippetsMap.set("snippets-last-update", Date.now());
- xhr.onloadend = function (event) {
- if (xhr.status == 200) {
- gSnippetsMap.set("snippets", xhr.responseText);
- gSnippetsMap.set("snippets-cached-version", currentVersion);
- }
- showSnippets();
- loadCompleted();
- };
- try {
- xhr.open("GET", updateURL, true);
- xhr.send(null);
- } catch (ex) {
- showSnippets();
- loadCompleted();
- return;
- }
- } else {
- showSnippets();
- loadCompleted();
- }
-}
-
-/**
- * Shows locally cached remote snippets, or default ones when not available.
- *
- * @note: snippets should never invoke showSnippets(), or they may cause
- * a "too much recursion" exception.
- */
-var _snippetsShown = false;
-function showSnippets()
-{
- let snippetsElt = document.getElementById("snippets");
-
- // Show about:rights notification, if needed.
- let showRights = document.documentElement.getAttribute("showKnowYourRights");
- if (showRights) {
- let rightsElt = document.getElementById("rightsSnippet");
- let anchor = rightsElt.getElementsByTagName("a")[0];
- anchor.href = "about:rights";
- snippetsElt.appendChild(rightsElt);
- rightsElt.removeAttribute("hidden");
- return;
- }
-
- if (!gSnippetsMap)
- throw new Error("Snippets map has not properly been initialized");
- if (_snippetsShown) {
- // There's something wrong with the remote snippets, just in case fall back
- // to the default snippets.
- showDefaultSnippets();
- throw new Error("showSnippets should never be invoked multiple times");
- }
- _snippetsShown = true;
-
- let snippets = gSnippetsMap.get("snippets");
- // If there are remotely fetched snippets, try to to show them.
- if (snippets) {
- // Injecting snippets can throw if they're invalid XML.
- try {
- snippetsElt.innerHTML = snippets;
- // Scripts injected by innerHTML are inactive, so we have to relocate them
- // through DOM manipulation to activate their contents.
- Array.forEach(snippetsElt.getElementsByTagName("script"), function(elt) {
- let relocatedScript = document.createElement("script");
- relocatedScript.type = "text/javascript;version=1.8";
- relocatedScript.text = elt.text;
- elt.parentNode.replaceChild(relocatedScript, elt);
- });
- return;
- } catch (ex) {
- // Bad content, continue to show default snippets.
- }
- }
-
- showDefaultSnippets();
-}
-
-/**
- * Clear snippets element contents and show default snippets.
- */
-function showDefaultSnippets()
-{
- // Clear eventual contents...
- let snippetsElt = document.getElementById("snippets");
- snippetsElt.innerHTML = "";
-
- // ...then show default snippets.
- let defaultSnippetsElt = document.getElementById("defaultSnippets");
- let entries = defaultSnippetsElt.querySelectorAll("span");
- // Choose a random snippet. Assume there is always at least one.
- let randIndex = Math.floor(Math.random() * entries.length);
- let entry = entries[randIndex];
- // Inject url in the eventual link.
- if (DEFAULT_SNIPPETS_URLS[randIndex]) {
- let links = entry.getElementsByTagName("a");
- // Default snippets can have only one link, otherwise something is messed
- // up in the translation.
- if (links.length == 1) {
- links[0].href = DEFAULT_SNIPPETS_URLS[randIndex];
- }
- }
- // Move the default snippet to the snippets element.
- snippetsElt.appendChild(entry);
}
function fitToWidth() {
diff --git a/browser/base/content/abouthome/aboutHome.xhtml b/browser/base/content/abouthome/aboutHome.xhtml
index c288e732e..22bf2e7e8 100644
--- a/browser/base/content/abouthome/aboutHome.xhtml
+++ b/browser/base/content/abouthome/aboutHome.xhtml
@@ -46,15 +46,6 @@
<input id="searchSubmit" type="button" onclick="onSearchSubmit(event)"
title="&contentSearchSubmit.tooltip;"/>
</div>
-
- <div id="snippetContainer">
- <div id="defaultSnippets" hidden="true">
- <span id="defaultSnippet1">&abouthome.defaultSnippet1.v1;</span>
- <span id="defaultSnippet2">&abouthome.defaultSnippet2.v1;</span>
- </div>
- <span id="rightsSnippet" hidden="true">&abouthome.rightsSnippet;</span>
- <div id="snippets"/>
- </div>
</div>
<div class="spacer"/>
@@ -73,7 +64,5 @@
<button class="launchButton" id="restorePreviousSession">&historyRestoreLastSession.label;</button>
</div>
- <a id="aboutMozilla" href="https://www.mozilla.org/about/?utm_source=about-home&amp;utm_medium=Referral"
- aria-label="&abouthome.aboutMozilla.label;"/>
</body>
</html>
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index d41e94ae6..53cc3c735 100755
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -53,8 +53,6 @@ Cu.import("resource://gre/modules/NotificationDB.jsm");
["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"],
["Weave", "resource://services-sync/main.js"],
["fxAccounts", "resource://gre/modules/FxAccounts.jsm"],
- ["gDevTools", "resource://devtools/client/framework/gDevTools.jsm"],
- ["gDevToolsBrowser", "resource://devtools/client/framework/gDevTools.jsm"],
["webrtcUI", "resource:///modules/webrtcUI.jsm", ]
].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
@@ -81,12 +79,6 @@ if (AppConstants.MOZ_CRASHREPORTER) {
}
-XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function() {
- let tmp = {};
- Cu.import("resource://devtools/client/framework/ToolboxProcess.jsm", tmp);
- return tmp.BrowserToolboxProcess;
-});
-
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
return Services.strings.createBundle('chrome://browser/locale/browser.properties');
});
@@ -214,7 +206,8 @@ var gInitialPages = [
"about:home",
"about:privatebrowsing",
"about:welcomeback",
- "about:sessionrestore"
+ "about:sessionrestore",
+ "about:logopage"
];
function* browserWindows() {
@@ -7893,15 +7886,6 @@ var TabContextMenu = {
}
};
-Object.defineProperty(this, "HUDService", {
- get: function HUDService_getter() {
- let devtools = Cu.import("resource://devtools/shared/Loader.jsm", {}).devtools;
- return devtools.require("devtools/client/webconsole/hudservice").HUDService;
- },
- configurable: true,
- enumerable: true
-});
-
// Prompt user to restart the browser in safe mode
function safeModeRestart() {
if (Services.appinfo.inSafeMode) {
@@ -7959,30 +7943,6 @@ function duplicateTabIn(aTab, where, delta) {
}
}
-var Scratchpad = {
- openScratchpad: function SP_openScratchpad() {
- return this.ScratchpadManager.openScratchpad();
- }
-};
-
-XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
- let tmp = {};
- Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", tmp);
- return tmp.ScratchpadManager;
-});
-
-var ResponsiveUI = {
- toggle: function RUI_toggle() {
- this.ResponsiveUIManager.toggle(window, gBrowser.selectedTab);
- }
-};
-
-XPCOMUtils.defineLazyGetter(ResponsiveUI, "ResponsiveUIManager", function() {
- let tmp = {};
- Cu.import("resource://devtools/client/responsivedesign/responsivedesign.jsm", tmp);
- return tmp.ResponsiveUIManager;
-});
-
var MousePosTracker = {
_listeners: new Set(),
_x: 0,
diff --git a/browser/base/content/tab-content.js b/browser/base/content/tab-content.js
index 05f8e00ab..7e803796a 100644
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -147,13 +147,10 @@ var AboutHomeListener = {
if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isContentWindowPrivate(content))
doc.getElementById("launcher").setAttribute("session", "true");
- // Inject search engine and snippets URL.
+ // Inject search engine URL.
let docElt = doc.documentElement;
- // Set snippetsVersion last, which triggers to show the snippets when it's set.
- docElt.setAttribute("snippetsURL", aData.snippetsURL);
if (aData.showKnowYourRights)
docElt.setAttribute("showKnowYourRights", "true");
- docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
},
onPageLoad: function() {
diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js
index 833369f4d..0b703b6f8 100644
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -35,7 +35,7 @@ var gBidiUI = false;
* Determines whether the given url is considered a special URL for new tabs.
*/
function isBlankPageURL(aURL) {
- return aURL == "about:blank" || aURL == BROWSER_NEW_TAB_URL;
+ return aURL == "about:blank" || aURL == "about:newtab" || aURL == "about:logopage";
}
function getBrowserURL()
diff --git a/browser/branding/unofficial/VisualElements_150.png b/browser/branding/unofficial/VisualElements_150.png
index 461961e8d..eb74e4adc 100644
--- a/browser/branding/unofficial/VisualElements_150.png
+++ b/browser/branding/unofficial/VisualElements_150.png
Binary files differ
diff --git a/browser/branding/unofficial/VisualElements_70.png b/browser/branding/unofficial/VisualElements_70.png
index aad81f40d..571532a9b 100644
--- a/browser/branding/unofficial/VisualElements_70.png
+++ b/browser/branding/unofficial/VisualElements_70.png
Binary files differ
diff --git a/browser/branding/unofficial/branding.nsi b/browser/branding/unofficial/branding.nsi
index 34214453f..77f08a4cb 100644
--- a/browser/branding/unofficial/branding.nsi
+++ b/browser/branding/unofficial/branding.nsi
@@ -8,19 +8,19 @@
# BrandFullNameInternal is used for some registry and file system values
# instead of BrandFullName and typically should not be modified.
-!define BrandFullNameInternal "Mozilla Developer Preview"
-!define CompanyName "mozilla.org"
-!define URLInfoAbout "https://www.mozilla.org"
-!define HelpLink "https://support.mozilla.org"
+!define BrandFullNameInternal "Serpent"
+!define CompanyName "Moonchild Productions"
+!define URLInfoAbout "http://www.basilisk-browser.org"
+!define HelpLink "https://forum.palemoon.org"
-!define URLStubDownload "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
-!define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=release&installer_lang=${AB_CD}"
-!define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
+!define URLStubDownload ""
+!define URLManualDownload ""
+!define URLSystemRequirements ""
!define Channel "unofficial"
# The installer's certificate name and issuer expected by the stub installer
-!define CertNameDownload "Mozilla Corporation"
-!define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
+!define CertNameDownload ""
+!define CertIssuerDownload ""
# Dialog units are used so the UI displays correctly with the system's DPI
# settings.
diff --git a/browser/branding/unofficial/configure.sh b/browser/branding/unofficial/configure.sh
index edd3bd3e8..ea4e37e45 100644
--- a/browser/branding/unofficial/configure.sh
+++ b/browser/branding/unofficial/configure.sh
@@ -2,4 +2,4 @@
# 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/.
-MOZ_APP_DISPLAYNAME=Nightly
+MOZ_APP_DISPLAYNAME=Serpent
diff --git a/browser/branding/unofficial/content/about-background.png b/browser/branding/unofficial/content/about-background.png
index 70eb8dafd..e36211b08 100644
--- a/browser/branding/unofficial/content/about-background.png
+++ b/browser/branding/unofficial/content/about-background.png
Binary files differ
diff --git a/browser/branding/unofficial/content/about-logo.png b/browser/branding/unofficial/content/about-logo.png
index 4c7214ba3..c5a838178 100644
--- a/browser/branding/unofficial/content/about-logo.png
+++ b/browser/branding/unofficial/content/about-logo.png
Binary files differ
diff --git a/browser/branding/unofficial/content/about-logo@2x.png b/browser/branding/unofficial/content/about-logo@2x.png
index 3526eda54..48c31564c 100644
--- a/browser/branding/unofficial/content/about-logo@2x.png
+++ b/browser/branding/unofficial/content/about-logo@2x.png
Binary files differ
diff --git a/browser/branding/unofficial/content/about-wordmark.svg b/browser/branding/unofficial/content/about-wordmark.svg
index 60b278d03..ce7a5c07b 100644
--- a/browser/branding/unofficial/content/about-wordmark.svg
+++ b/browser/branding/unofficial/content/about-wordmark.svg
@@ -1,22 +1,87 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="132px" height="48px" viewBox="0 0 132 48">
- <path fill="#fff" d="M60.6,14.3l-2.4-2.4C57,12.7,56,13,54.7,13c-3,0-3.8-1.4-7.6-1.4c-5.4,0-9.2,3.4-9.2,8.4
- c0,3.3,2.2,6.1,5.6,7.2c-3.4,1-4.5,2.2-4.5,4.3c0,2.2,1.8,3.6,4.7,3.6h3.8c2.5,0,3.9,0.2,4.9,0.9c0.9,0.6,1.4,1.6,1.4,3
- c0,3.1-2.2,4.4-6,4.4c-2,0-3.8-0.5-5.1-1.2c-0.9-0.6-1.5-1.6-1.5-2.9c0-0.8,0.3-1.7,0.7-2.2l-4.1,0.4c-0.3,1-0.5,1.7-0.5,2.6
- c0,3.5,3,6.4,10.8,6.4c6.1,0,9.9-2.5,9.9-7.9c0-2.1-0.8-3.9-2.7-5.3c-1.5-1.1-3.1-1.4-6-1.4h-4c-1.3,0-2-0.5-2-1.2
- c0-0.8,1.1-1.7,4.5-2.9c1.8,0,3.4-0.3,4.7-1.1c2.3-1.4,3.7-4.1,3.7-6.8c0-1.6-0.5-3-1.5-4.3c0.4,0.2,1.1,0.3,1.7,0.3
- C57.9,15.8,59,15.4,60.6,14.3z M47.1,24.8c-3.1,0-4.8-1.7-4.8-4.8c0-3.5,1.6-5.1,4.7-5.1c3.3,0,4.6,1.5,4.6,4.9
- C51.6,23.1,50.1,24.8,47.1,24.8z M30.7,1.3c-1.7,0-3,1.4-3,3.1s1.4,3,3,3c1.7,0,3.1-1.3,3.1-3C33.7,2.7,32.4,1.3,30.7,1.3z
- M107.7,34.5c-1.1,0-1.4-0.6-1.4-2.5V6.5c0-3.8-0.6-5.9-0.6-5.9l-3.9,0.8c0,0,0.6,1.9,0.6,5.1v26.4c0,1.8,0.4,2.8,1.2,3.5
- c0.7,0.7,1.7,1,2.9,1c1,0,1.5-0.1,2.5-0.5l-0.8-2.5C108.2,34.4,107.8,34.5,107.7,34.5z M74.7,11.6c-3.2,0-6.1,1.8-8.3,3.9
- c0,0,0.2-1.8,0.2-3.4V6.3c0-3.8-0.7-5.9-0.7-5.9l-3.9,0.7c0,0,0.7,1.9,0.7,5.1V37h3.9V19.3c2.1-2.7,4.9-4.2,7.2-4.2
- c1.3,0,2.3,0.4,2.9,1c0.7,0.7,0.9,1.8,0.9,3.7V37h3.8V19.1c0-1.8-0.1-2.6-0.4-3.6C80.4,13.2,77.7,11.6,74.7,11.6z M127.4,12.1
- l-4.9,16.4c-0.6,2-1.6,5.2-1.6,5.2s-0.7-3.9-1.5-6.2l-5.1-16.2l-3.9,1.3l5.4,15.6c0.8,2.5,2.2,7.4,2.5,9l1.6-0.3
- c-1.3,5.1-2.5,6.7-5.7,7.6l1.2,2.7c4.4-1,6.4-4.3,8-9.3l8.6-25.8H127.4z M96.9,15l1.2-2.9h-6.2c0-3.3,0.5-7.2,0.5-7.2l-4.1,0.9
- c0,0-0.4,3.9-0.4,6.3h-3.2V15h3.2v17.1c0,2.5,0.7,4.1,2.4,5c0.9,0.4,1.9,0.7,3.3,0.7c1.8,0,3.1-0.4,4.4-1l-0.6-2.5
- c-0.7,0.3-1.3,0.5-2.4,0.5c-2.4,0-3.2-0.9-3.2-3.7V15H96.9z M28.6,37h4.1V11.5l-4.1,0.6V37z M18.9,21.3c0,5,0.4,10.5,0.4,10.5
- s-1.4-3.8-3.2-7.2L4.8,2.7H0V37h4.2L4,17.1c0-4.5-0.4-9.3-0.4-9.3s1.7,4.1,3.9,8.2l11,21h4.3V2.7h-4L18.9,21.3z"/>
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="132px"
+ height="48px"
+ viewBox="0 0 132 48"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="about-wordmark.svg">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1215"
+ inkscape:window-height="778"
+ id="namedview6"
+ showgrid="false"
+ inkscape:zoom="2.4090909"
+ inkscape:cx="91.154186"
+ inkscape:cy="24"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <g
+ transform="scale(0.83939803,1.1913299)"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:42.56122971px;line-height:125%;font-family:'Levenim MT';-inkscape-font-specification:'Levenim MT';letter-spacing:0px;word-spacing:0px;fill:#a8e6db;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="text4138">
+ <path
+ d="m 18.521413,24.115665 q 0,3.470569 -2.618513,5.881264 -2.535386,2.348349 -6.0475185,2.348349 -5.6942271,0 -9.22714164,-6.463156 L 3.2259712,24.323483 q 2.7432042,5.04999 6.3384644,5.04999 3.4082234,0 4.9460804,-2.639295 0.706583,-1.205347 0.706583,-2.556167 0,-1.537857 -1.039093,-3.013369 Q 12.827186,19.23193 9.1487985,16.488726 5.3872836,13.703958 4.1819363,12.082974 2.560952,9.9008792 2.560952,7.4070572 q 0,-4.2394975 3.6576056,-6.2345551 1.7248936,-0.93518331 3.761515,-0.93518331 4.3641884,0 8.0217944,5.00842591 L 15.508045,7.1368931 Q 14.115661,5.3080903 13.138914,4.5391618 11.684184,3.3961601 9.917727,3.3961601 q -1.7664573,0 -2.9510228,1.0598743 -1.246911,1.0806563 -1.246911,2.8263317 0,2.0574032 1.9327121,3.9693339 0.56111,0.540328 3.9485517,3.054932 3.117277,2.306785 4.655134,4.15637 2.265222,2.763986 2.265222,5.652663 z"
+ style=""
+ id="path4143" />
+ <path
+ d="m 46.743165,20.499623 -20.324649,0 q 0.08313,3.803079 2.369131,6.338464 2.410694,2.660077 6.151427,2.660077 3.616042,0 6.130646,-2.202876 1.143002,-0.997529 2.47304,-3.221187 l 2.452259,1.288475 q -2.286004,4.468098 -6.338465,6.026737 -1.974275,0.768928 -4.468097,0.768928 -5.091554,0 -8.437432,-3.36666 -3.325096,-3.366659 -3.325096,-8.458213 0,-4.301843 2.639295,-7.668503 3.345878,-4.2810608 8.956978,-4.2810608 5.777354,0 9.227141,4.3849708 2.452259,3.117277 2.493822,7.730848 z M 43.54276,17.985019 q -1.080656,-4.301843 -4.634352,-5.964391 -1.828803,-0.852056 -3.844643,-0.852056 -3.325096,0 -5.715008,2.140531 -1.745676,1.558639 -2.639295,4.675916 l 16.833298,0 z"
+ style=""
+ id="path4145" />
+ <path
+ d="m 62.516592,9.1527326 -1.517075,2.4522584 q -0.914401,-0.394855 -1.537857,-0.394855 -3.491351,0 -4.862953,4.904517 -0.540328,1.932712 -0.540328,7.813975 l 0,7.647721 -2.971805,0 0,-22.610653 2.971805,0 0,3.304314 q 2.639295,-3.8862058 5.881264,-3.8862058 1.205347,0 2.576949,0.7689284 z"
+ style=""
+ id="path4147" />
+ <path
+ d="m 88.577029,20.208677 q 0,4.94608 -3.325096,8.416649 -3.366659,3.532915 -8.271176,3.532915 -5.382499,0 -9.060887,-4.468098 l 0,12.157383 -2.888677,0 0,-30.88183 2.888677,0 0,4.15637 q 3.449787,-4.7382618 8.977759,-4.7382618 4.883735,0 8.271177,3.4705688 3.408223,3.470569 3.408223,8.354304 z m -2.930241,0.103909 q 0,-2.410694 -1.205347,-4.572007 -1.205347,-2.161312 -3.283532,-3.345878 -2.057403,-1.205347 -4.48888,-1.205347 -3.865424,0 -6.421592,2.660077 -2.535385,2.660077 -2.535385,6.546283 0,5.403281 4.468097,7.897103 2.140531,1.205347 4.530444,1.205347 2.410694,0 4.488879,-1.246911 2.01584,-1.226129 3.221187,-3.387441 1.226129,-2.161313 1.226129,-4.551226 z"
+ style=""
+ id="path4149" />
+ <path
+ d="m 116.23767,20.499623 -20.324647,0 q 0.08313,3.803079 2.36913,6.338464 2.410697,2.660077 6.151427,2.660077 3.61604,0 6.13065,-2.202876 1.143,-0.997529 2.47304,-3.221187 l 2.45226,1.288475 q -2.28601,4.468098 -6.33847,6.026737 -1.97427,0.768928 -4.4681,0.768928 -5.09155,0 -8.437428,-3.36666 -3.325096,-3.366659 -3.325096,-8.458213 0,-4.301843 2.639295,-7.668503 3.345878,-4.2810608 8.956979,-4.2810608 5.77735,0 9.22714,4.3849708 2.45226,3.117277 2.49382,7.730848 z m -3.2004,-2.514604 q -1.08066,-4.301843 -4.63436,-5.964391 -1.8288,-0.852056 -3.84464,-0.852056 -3.32509,0 -5.715007,2.140531 -1.745675,1.558639 -2.639295,4.675916 l 16.833302,0 z"
+ style=""
+ id="path4151" />
+ <path
+ d="m 141.4045,31.576349 -2.88868,0 0,-10.78578 q 0,-3.823861 -0.33251,-5.216245 -1.08066,-4.509661 -5.69423,-4.509661 -2.63929,0 -4.73826,1.745675 -2.07819,1.724894 -2.7432,4.322625 -0.41564,1.641766 -0.41564,6.151428 l 0,8.291958 -2.90946,0 0,-22.610653 2.90946,0 0,4.052461 q 3.47057,-4.6343528 8.47899,-4.6343528 2.47304,0 4.44732,1.2676928 1.99506,1.246911 2.93024,3.470569 0.95597,2.202876 0.95597,6.816447 l 0,11.637836 z"
+ style=""
+ id="path4153" />
+ <path
+ d="m 157.69747,11.459518 -4.61357,0 0,20.116831 -2.93024,0 0,-20.116831 -3.96934,0 0,-2.493822 3.96934,0 0,-8.3958676 2.93024,0 0,8.3958676 4.61357,0 0,2.493822 z"
+ style=""
+ id="path4155" />
+ </g>
</svg>
diff --git a/browser/branding/unofficial/content/about.png b/browser/branding/unofficial/content/about.png
index 231449344..e323c8df5 100644
--- a/browser/branding/unofficial/content/about.png
+++ b/browser/branding/unofficial/content/about.png
Binary files differ
diff --git a/browser/branding/unofficial/content/icon48.png b/browser/branding/unofficial/content/icon48.png
index 5fc7861e5..16e022a64 100644
--- a/browser/branding/unofficial/content/icon48.png
+++ b/browser/branding/unofficial/content/icon48.png
Binary files differ
diff --git a/browser/branding/unofficial/content/icon64.png b/browser/branding/unofficial/content/icon64.png
index 83f7016bc..9860917e3 100644
--- a/browser/branding/unofficial/content/icon64.png
+++ b/browser/branding/unofficial/content/icon64.png
Binary files differ
diff --git a/browser/branding/unofficial/default16.png b/browser/branding/unofficial/default16.png
index d285a90b4..67ef39df8 100644
--- a/browser/branding/unofficial/default16.png
+++ b/browser/branding/unofficial/default16.png
Binary files differ
diff --git a/browser/branding/unofficial/default32.png b/browser/branding/unofficial/default32.png
index 95adf2497..2f709e6b2 100644
--- a/browser/branding/unofficial/default32.png
+++ b/browser/branding/unofficial/default32.png
Binary files differ
diff --git a/browser/branding/unofficial/default48.png b/browser/branding/unofficial/default48.png
index d38185f54..02a1e14c1 100644
--- a/browser/branding/unofficial/default48.png
+++ b/browser/branding/unofficial/default48.png
Binary files differ
diff --git a/browser/branding/unofficial/firefox.icns b/browser/branding/unofficial/firefox.icns
index 0c6941acf..2c613634b 100644
--- a/browser/branding/unofficial/firefox.icns
+++ b/browser/branding/unofficial/firefox.icns
Binary files differ
diff --git a/browser/branding/unofficial/firefox.ico b/browser/branding/unofficial/firefox.ico
index 5217a6c0b..0c7acb61b 100644
--- a/browser/branding/unofficial/firefox.ico
+++ b/browser/branding/unofficial/firefox.ico
Binary files differ
diff --git a/browser/branding/unofficial/locales/en-US/brand.dtd b/browser/branding/unofficial/locales/en-US/brand.dtd
index cf4596ae0..17c243606 100644
--- a/browser/branding/unofficial/locales/en-US/brand.dtd
+++ b/browser/branding/unofficial/locales/en-US/brand.dtd
@@ -2,8 +2,9 @@
- 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/. -->
-<!ENTITY brandShorterName "Nightly">
-<!ENTITY brandShortName "Nightly">
-<!ENTITY brandFullName "Nightly">
-<!ENTITY vendorShortName "Mozilla">
+<!ENTITY brandShorterName "Serpent">
+<!ENTITY brandShortName "Serpent">
+<!ENTITY brandFullName "Serpent">
+<!ENTITY vendorShortName "Moonchild">
+<!ENTITY vendorFullName "Moonchild Productions">
<!ENTITY trademarkInfo.part1 " ">
diff --git a/browser/branding/unofficial/locales/en-US/brand.properties b/browser/branding/unofficial/locales/en-US/brand.properties
index 8cd2c2ec9..80349f0e3 100644
--- a/browser/branding/unofficial/locales/en-US/brand.properties
+++ b/browser/branding/unofficial/locales/en-US/brand.properties
@@ -2,9 +2,10 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-brandShorterName=Nightly
-brandShortName=Nightly
-brandFullName=Nightly
-vendorShortName=Mozilla
+brandShorterName=Serpent
+brandShortName=Serpent
+brandFullName=Serpent
+vendorShortName=Moonchild
+vendorFullName=Moonchild Productions
syncBrandShortName=Sync
diff --git a/browser/branding/unofficial/mozicon128.png b/browser/branding/unofficial/mozicon128.png
index 471cf4645..739b61084 100644
--- a/browser/branding/unofficial/mozicon128.png
+++ b/browser/branding/unofficial/mozicon128.png
Binary files differ
diff --git a/browser/branding/unofficial/basilisk.VisualElementsManifest.xml b/browser/branding/unofficial/serpent.VisualElementsManifest.xml
index 7654e0ab7..5046ee7da 100644
--- a/browser/branding/unofficial/basilisk.VisualElementsManifest.xml
+++ b/browser/branding/unofficial/serpent.VisualElementsManifest.xml
@@ -4,5 +4,5 @@
Square150x150Logo='browser\VisualElements\VisualElements_150.png'
Square70x70Logo='browser\VisualElements\VisualElements_70.png'
ForegroundText='light'
- BackgroundColor='#14171a'/>
+ BackgroundColor='#304D7E'/>
</Application>
diff --git a/browser/components/about/AboutRedirector.cpp b/browser/components/about/AboutRedirector.cpp
index a09932d95..717ae9c48 100644
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -35,75 +35,117 @@ struct RedirEntry {
URI_SAFE_FOR_UNTRUSTED_CONTENT.
*/
static RedirEntry kRedirMap[] = {
- { "blocked", "chrome://browser/content/blockedSite.xhtml",
+ {
+ "basilisk", "chrome://global/content/memoriam.xhtml",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "blocked", "chrome://browser/content/blockedSite.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "certerror", "chrome://browser/content/aboutNetError.xhtml",
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "certerror", "chrome://browser/content/aboutNetError.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "socialerror", "chrome://browser/content/aboutSocialError.xhtml",
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "socialerror", "chrome://browser/content/aboutSocialError.xhtml",
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "providerdirectory", "chrome://browser/content/aboutProviderDirectory.xhtml",
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "providerdirectory", "chrome://browser/content/aboutProviderDirectory.xhtml",
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "feeds", "chrome://browser/content/feeds/subscribe.xhtml",
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "feeds", "chrome://browser/content/feeds/subscribe.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "privatebrowsing", "chrome://browser/content/aboutPrivateBrowsing.xhtml",
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "privatebrowsing", "chrome://browser/content/aboutPrivateBrowsing.xhtml",
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
- nsIAboutModule::ALLOW_SCRIPT },
- { "rights",
- "chrome://global/content/aboutRights.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "rights", "chrome://global/content/aboutRights.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::MAKE_LINKABLE |
- nsIAboutModule::ALLOW_SCRIPT },
- { "robots", "chrome://browser/content/aboutRobots.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "robots", "chrome://browser/content/aboutRobots.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::ALLOW_SCRIPT },
- { "searchreset", "chrome://browser/content/search/searchReset.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "searchreset", "chrome://browser/content/search/searchReset.xhtml",
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
- { "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
- nsIAboutModule::ALLOW_SCRIPT },
- { "welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml",
- nsIAboutModule::ALLOW_SCRIPT },
- { "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
- nsIAboutModule::ALLOW_SCRIPT },
- // Linkable because of indexeddb use (bug 1228118)
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+ {
+ "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
{ "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
nsIAboutModule::ALLOW_SCRIPT |
+ // Linkable because of indexeddb use (bug 1228118)
nsIAboutModule::MAKE_LINKABLE |
nsIAboutModule::ENABLE_INDEXED_DB },
- // the newtab's actual URL will be determined when the channel is created
- { "newtab", "about:blank",
- nsIAboutModule::ALLOW_SCRIPT },
- { "preferences", "chrome://browser/content/preferences/in-content/preferences.xul",
- nsIAboutModule::ALLOW_SCRIPT },
- { "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
- nsIAboutModule::ALLOW_SCRIPT },
+ {
+ // the newtab's actual URL will be determined when the channel is created
+ "newtab", "about:blank",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "preferences", "chrome://browser/content/preferences/in-content/preferences.xul",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
#ifdef MOZ_SERVICES_HEALTHREPORT
- { "healthreport", "chrome://browser/content/abouthealthreport/abouthealth.xhtml",
- nsIAboutModule::ALLOW_SCRIPT },
+ {
+ "healthreport", "chrome://browser/content/abouthealthreport/abouthealth.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
#endif
- { "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
- nsIAboutModule::ALLOW_SCRIPT },
- { "reader", "chrome://global/content/reader/aboutReader.html",
+ {
+ "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
+ nsIAboutModule::ALLOW_SCRIPT
+ },
+ {
+ "reader", "chrome://global/content/reader/aboutReader.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT },
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
};
static const int kRedirTotal = ArrayLength(kRedirMap);
diff --git a/browser/components/build/nsModule.cpp b/browser/components/build/nsModule.cpp
index f85d8812c..1fad0ce68 100644
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -85,6 +85,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
{ NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
#endif
{ NS_FEEDSNIFFER_CONTRACTID, &kNS_FEEDSNIFFER_CID },
+ { NS_ABOUT_MODULE_CONTRACTID_PREFIX "basilisk", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "blocked", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "certerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "socialerror", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
diff --git a/browser/components/nsBrowserContentHandler.js b/browser/components/nsBrowserContentHandler.js
index b366c3f81..74144fc1b 100644
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -558,7 +558,7 @@ nsBrowserContentHandler.prototype = {
if (overridePage && startPage && !willRestoreSession && !skipStartPage)
return overridePage + "|" + startPage;
- return overridePage || startPage || "about:blank";
+ return overridePage || startPage || "about:blank" || "about:logopage";
},
get startPage() {
diff --git a/browser/experiments/.eslintrc.js b/browser/experiments/.eslintrc.js
deleted file mode 100644
index 1f6b11d67..000000000
--- a/browser/experiments/.eslintrc.js
+++ /dev/null
@@ -1,11 +0,0 @@
-"use strict";
-
-module.exports = {
- "rules": {
- "no-unused-vars": ["error", {
- "vars": "all",
- "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$",
- "args": "none"
- }]
- }
-};
diff --git a/browser/experiments/Experiments.jsm b/browser/experiments/Experiments.jsm
deleted file mode 100644
index e9a63f19f..000000000
--- a/browser/experiments/Experiments.jsm
+++ /dev/null
@@ -1,2354 +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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = [
- "Experiments",
-];
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/AsyncShutdown.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
- "resource://gre/modules/UpdateUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
- "resource://gre/modules/AddonManager.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
- "resource://gre/modules/AddonManager.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
- "resource://gre/modules/TelemetryEnvironment.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
- "resource://gre/modules/TelemetryLog.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "TelemetryUtils",
- "resource://gre/modules/TelemetryUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
- "resource://services-common/utils.js");
-
-XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
- "@mozilla.org/xre/app-info;1",
- "nsICrashReporter");
-
-const FILE_CACHE = "experiments.json";
-const EXPERIMENTS_CHANGED_TOPIC = "experiments-changed";
-const MANIFEST_VERSION = 1;
-const CACHE_VERSION = 1;
-
-const KEEP_HISTORY_N_DAYS = 180;
-
-const PREF_BRANCH = "experiments.";
-const PREF_ENABLED = "enabled"; // experiments.enabled
-const PREF_ACTIVE_EXPERIMENT = "activeExperiment"; // whether we have an active experiment
-const PREF_LOGGING = "logging";
-const PREF_LOGGING_LEVEL = PREF_LOGGING + ".level"; // experiments.logging.level
-const PREF_LOGGING_DUMP = PREF_LOGGING + ".dump"; // experiments.logging.dump
-const PREF_MANIFEST_URI = "manifest.uri"; // experiments.logging.manifest.uri
-const PREF_FORCE_SAMPLE = "force-sample-value"; // experiments.force-sample-value
-
-const PREF_BRANCH_TELEMETRY = "toolkit.telemetry.";
-const PREF_TELEMETRY_ENABLED = "enabled";
-
-const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
-const STRING_TYPE_NAME = "type.%ID%.name";
-
-const CACHE_WRITE_RETRY_DELAY_SEC = 60 * 3;
-const MANIFEST_FETCH_TIMEOUT_MSEC = 60 * 3 * 1000; // 3 minutes
-
-const TELEMETRY_LOG = {
- // log(key, [kind, experimentId, details])
- ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
- ACTIVATION: {
- // Successfully activated.
- ACTIVATED: "ACTIVATED",
- // Failed to install the add-on.
- INSTALL_FAILURE: "INSTALL_FAILURE",
- // Experiment does not meet activation requirements. Details will
- // be provided.
- REJECTED: "REJECTED",
- },
-
- // log(key, [kind, experimentId, optionalDetails...])
- TERMINATION_KEY: "EXPERIMENT_TERMINATION",
- TERMINATION: {
- // The Experiments service was disabled.
- SERVICE_DISABLED: "SERVICE_DISABLED",
- // Add-on uninstalled.
- ADDON_UNINSTALLED: "ADDON_UNINSTALLED",
- // The experiment disabled itself.
- FROM_API: "FROM_API",
- // The experiment expired (e.g. by exceeding the end date).
- EXPIRED: "EXPIRED",
- // Disabled after re-evaluating conditions. If this is specified,
- // details will be provided.
- RECHECK: "RECHECK",
- },
-};
-XPCOMUtils.defineConstant(this, "TELEMETRY_LOG", TELEMETRY_LOG);
-
-const gPrefs = new Preferences(PREF_BRANCH);
-const gPrefsTelemetry = new Preferences(PREF_BRANCH_TELEMETRY);
-var gExperimentsEnabled = false;
-var gAddonProvider = null;
-var gExperiments = null;
-var gLogAppenderDump = null;
-var gPolicyCounter = 0;
-var gExperimentsCounter = 0;
-var gExperimentEntryCounter = 0;
-var gPreviousProviderCounter = 0;
-
-// Tracks active AddonInstall we know about so we can deny external
-// installs.
-var gActiveInstallURLs = new Set();
-
-// Tracks add-on IDs that are being uninstalled by us. This allows us
-// to differentiate between expected uninstalled and user-driven uninstalls.
-var gActiveUninstallAddonIDs = new Set();
-
-var gLogger;
-var gLogDumping = false;
-
-function configureLogging() {
- if (!gLogger) {
- gLogger = Log.repository.getLogger("Browser.Experiments");
- gLogger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
- }
- gLogger.level = gPrefs.get(PREF_LOGGING_LEVEL, Log.Level.Warn);
-
- let logDumping = gPrefs.get(PREF_LOGGING_DUMP, false);
- if (logDumping != gLogDumping) {
- if (logDumping) {
- gLogAppenderDump = new Log.DumpAppender(new Log.BasicFormatter());
- gLogger.addAppender(gLogAppenderDump);
- } else {
- gLogger.removeAppender(gLogAppenderDump);
- gLogAppenderDump = null;
- }
- gLogDumping = logDumping;
- }
-}
-
-// Loads a JSON file using OS.file. file is a string representing the path
-// of the file to be read, options contains additional options to pass to
-// OS.File.read.
-// Returns a Promise resolved with the json payload or rejected with
-// OS.File.Error or JSON.parse() errors.
-function loadJSONAsync(file, options) {
- return Task.spawn(function*() {
- let rawData = yield OS.File.read(file, options);
- // Read json file into a string
- let data;
- try {
- // Obtain a converter to read from a UTF-8 encoded input stream.
- let converter = new TextDecoder();
- data = JSON.parse(converter.decode(rawData));
- } catch (ex) {
- gLogger.error("Experiments: Could not parse JSON: " + file + " " + ex);
- throw ex;
- }
- return data;
- });
-}
-
-// Returns a promise that is resolved with the AddonInstall for that URL.
-function addonInstallForURL(url, hash) {
- let deferred = Promise.defer();
- AddonManager.getInstallForURL(url, install => deferred.resolve(install),
- "application/x-xpinstall", hash);
- return deferred.promise;
-}
-
-// Returns a promise that is resolved with an Array<Addon> of the installed
-// experiment addons.
-function installedExperimentAddons() {
- let deferred = Promise.defer();
- AddonManager.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons.filter(a => !a.appDisabled));
- });
- return deferred.promise;
-}
-
-// Takes an Array<Addon> and returns a promise that is resolved when the
-// addons are uninstalled.
-function uninstallAddons(addons) {
- let ids = new Set(addons.map(addon => addon.id));
- let deferred = Promise.defer();
-
- let listener = {};
- listener.onUninstalled = addon => {
- if (!ids.has(addon.id)) {
- return;
- }
-
- ids.delete(addon.id);
- if (ids.size == 0) {
- AddonManager.removeAddonListener(listener);
- deferred.resolve();
- }
- };
-
- AddonManager.addAddonListener(listener);
-
- for (let addon of addons) {
- // Disabling the add-on before uninstalling is necessary to cause tests to
- // pass. This might be indicative of a bug in XPIProvider.
- // TODO follow up in bug 992396.
- addon.userDisabled = true;
- addon.uninstall();
- }
-
- return deferred.promise;
-}
-
-/**
- * The experiments module.
- */
-
-var Experiments = {
- /**
- * Provides access to the global `Experiments.Experiments` instance.
- */
- instance: function () {
- if (!gExperiments) {
- gExperiments = new Experiments.Experiments();
- }
-
- return gExperiments;
- },
-};
-
-/*
- * The policy object allows us to inject fake enviroment data from the
- * outside by monkey-patching.
- */
-
-Experiments.Policy = function () {
- this._log = Log.repository.getLoggerWithMessagePrefix(
- "Browser.Experiments.Policy",
- "Policy #" + gPolicyCounter++ + "::");
-
- // Set to true to ignore hash verification on downloaded XPIs. This should
- // not be used outside of testing.
- this.ignoreHashes = false;
-};
-
-Experiments.Policy.prototype = {
- now: function () {
- return new Date();
- },
-
- random: function () {
- let pref = gPrefs.get(PREF_FORCE_SAMPLE);
- if (pref !== undefined) {
- let val = Number.parseFloat(pref);
- this._log.debug("random sample forced: " + val);
- if (isNaN(val) || val < 0) {
- return 0;
- }
- if (val > 1) {
- return 1;
- }
- return val;
- }
- return Math.random();
- },
-
- futureDate: function (offset) {
- return new Date(this.now().getTime() + offset);
- },
-
- oneshotTimer: function (callback, timeout, thisObj, name) {
- return CommonUtils.namedTimer(callback, timeout, thisObj, name);
- },
-
- updatechannel: function () {
- return UpdateUtils.UpdateChannel;
- },
-
- locale: function () {
- let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
- return chrome.getSelectedLocale("global");
- },
-
- /**
- * For testing a race condition, one of the tests delays the callback of
- * writing the cache by replacing this policy function.
- */
- delayCacheWrite: function(promise) {
- return promise;
- },
-};
-
-function AlreadyShutdownError(message="already shut down") {
- Error.call(this, message);
- let error = new Error();
- this.name = "AlreadyShutdownError";
- this.message = message;
- this.stack = error.stack;
-}
-AlreadyShutdownError.prototype = Object.create(Error.prototype);
-AlreadyShutdownError.prototype.constructor = AlreadyShutdownError;
-
-function CacheWriteError(message="Error writing cache file") {
- Error.call(this, message);
- let error = new Error();
- this.name = "CacheWriteError";
- this.message = message;
- this.stack = error.stack;
-}
-CacheWriteError.prototype = Object.create(Error.prototype);
-CacheWriteError.prototype.constructor = CacheWriteError;
-
-/**
- * Manages the experiments and provides an interface to control them.
- */
-
-Experiments.Experiments = function (policy=new Experiments.Policy()) {
- let log = Log.repository.getLoggerWithMessagePrefix(
- "Browser.Experiments.Experiments",
- "Experiments #" + gExperimentsCounter++ + "::");
-
- // At the time of this writing, Experiments.jsm has severe
- // crashes. For forensics purposes, keep the last few log
- // messages in memory and upload them in case of crash.
- this._forensicsLogs = [];
- this._forensicsLogs.length = 30;
- this._log = Object.create(log);
- this._log.log = (level, string, params) => {
- this._addToForensicsLog("Experiments", string);
- log.log(level, string, params);
- };
-
- this._log.trace("constructor");
-
- // Capture the latest error, for forensics purposes.
- this._latestError = null;
-
-
- this._policy = policy;
-
- // This is a Map of (string -> ExperimentEntry), keyed with the experiment id.
- // It holds both the current experiments and history.
- // Map() preserves insertion order, which means we preserve the manifest order.
- // This is null until we've successfully completed loading the cache from
- // disk the first time.
- this._experiments = null;
- this._refresh = false;
- this._terminateReason = null; // or TELEMETRY_LOG.TERMINATION....
- this._dirty = false;
-
- // Loading the cache happens once asynchronously on startup
- this._loadTask = null;
-
- // The _main task handles all other actions:
- // * refreshing the manifest off the network (if _refresh)
- // * disabling/enabling experiments
- // * saving the cache (if _dirty)
- this._mainTask = null;
-
- // Timer for re-evaluating experiment status.
- this._timer = null;
-
- this._shutdown = false;
- this._networkRequest = null;
-
- // We need to tell when we first evaluated the experiments to fire an
- // experiments-changed notification when we only loaded completed experiments.
- this._firstEvaluate = true;
-
- this.init();
-};
-
-Experiments.Experiments.prototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback, Ci.nsIObserver]),
-
- /**
- * `true` if the experiments manager is currently setup (has been fully initialized
- * and not uninitialized yet).
- */
- get isReady() {
- return !this._shutdown;
- },
-
- init: function () {
- this._shutdown = false;
- configureLogging();
-
- gExperimentsEnabled = gPrefs.get(PREF_ENABLED, false) && TelemetryUtils.isTelemetryEnabled;
- this._log.trace("enabled=" + gExperimentsEnabled + ", " + this.enabled);
-
- gPrefs.observe(PREF_LOGGING, configureLogging);
- gPrefs.observe(PREF_MANIFEST_URI, this.updateManifest, this);
- gPrefs.observe(PREF_ENABLED, this._toggleExperimentsEnabled, this);
-
- gPrefsTelemetry.observe(PREF_TELEMETRY_ENABLED, this._telemetryStatusChanged, this);
-
- AddonManager.shutdown.addBlocker("Experiments.jsm shutdown",
- this.uninit.bind(this),
- this._getState.bind(this)
- );
-
- this._registerWithAddonManager();
-
- this._loadTask = this._loadFromCache();
-
- return this._loadTask.then(
- () => {
- this._log.trace("_loadTask finished ok");
- this._loadTask = null;
- return this._run();
- },
- (e) => {
- this._log.error("_loadFromCache caught error: " + e);
- this._latestError = e;
- throw e;
- }
- );
- },
-
- /**
- * Uninitialize this instance.
- *
- * This function is susceptible to race conditions. If it is called multiple
- * times before the previous uninit() has completed or if it is called while
- * an init() operation is being performed, the object may get in bad state
- * and/or deadlock could occur.
- *
- * @return Promise<>
- * The promise is fulfilled when all pending tasks are finished.
- */
- uninit: Task.async(function* () {
- this._log.trace("uninit: started");
- yield this._loadTask;
- this._log.trace("uninit: finished with _loadTask");
-
- if (!this._shutdown) {
- this._log.trace("uninit: no previous shutdown");
- this._unregisterWithAddonManager();
-
- gPrefs.ignore(PREF_LOGGING, configureLogging);
- gPrefs.ignore(PREF_MANIFEST_URI, this.updateManifest, this);
- gPrefs.ignore(PREF_ENABLED, this._toggleExperimentsEnabled, this);
-
- gPrefsTelemetry.ignore(PREF_TELEMETRY_ENABLED, this._telemetryStatusChanged, this);
-
- if (this._timer) {
- this._timer.clear();
- }
- }
-
- this._shutdown = true;
- if (this._mainTask) {
- if (this._networkRequest) {
- try {
- this._log.trace("Aborting pending network request: " + this._networkRequest);
- this._networkRequest.abort();
- } catch (e) {
- // pass
- }
- }
- try {
- this._log.trace("uninit: waiting on _mainTask");
- yield this._mainTask;
- } catch (e) {
- // We error out of tasks after shutdown via this exception.
- this._log.trace(`uninit: caught error - ${e}`);
- if (!(e instanceof AlreadyShutdownError)) {
- this._latestError = e;
- throw e;
- }
- }
- }
-
- this._log.info("Completed uninitialization.");
- }),
-
- // Return state information, for debugging purposes.
- _getState: function() {
- let activeExperiment = this._getActiveExperiment();
- let state = {
- isShutdown: this._shutdown,
- isEnabled: gExperimentsEnabled,
- isRefresh: this._refresh,
- isDirty: this._dirty,
- isFirstEvaluate: this._firstEvaluate,
- hasLoadTask: !!this._loadTask,
- hasMainTask: !!this._mainTask,
- hasTimer: !!this._hasTimer,
- hasAddonProvider: !!gAddonProvider,
- latestLogs: this._forensicsLogs,
- experiments: this._experiments ? [...this._experiments.keys()] : null,
- terminateReason: this._terminateReason,
- activeExperiment: activeExperiment ? activeExperiment.id : null,
- };
- if (this._latestError) {
- if (typeof this._latestError == "object") {
- state.latestError = {
- message: this._latestError.message,
- stack: this._latestError.stack
- };
- } else {
- state.latestError = "" + this._latestError;
- }
- }
- return state;
- },
-
- _addToForensicsLog: function (what, string) {
- this._forensicsLogs.shift();
- let timeInSec = Math.floor(Services.telemetry.msSinceProcessStart() / 1000);
- this._forensicsLogs.push(`${timeInSec}: ${what} - ${string}`);
- },
-
- _registerWithAddonManager: function (previousExperimentsProvider) {
- this._log.trace("Registering instance with Addon Manager.");
-
- AddonManager.addAddonListener(this);
- AddonManager.addInstallListener(this);
-
- if (!gAddonProvider) {
- // The properties of this AddonType should be kept in sync with the
- // experiment AddonType registered in XPIProvider.
- this._log.trace("Registering previous experiment add-on provider.");
- gAddonProvider = previousExperimentsProvider || new Experiments.PreviousExperimentProvider(this);
- AddonManagerPrivate.registerProvider(gAddonProvider, [
- new AddonManagerPrivate.AddonType("experiment",
- URI_EXTENSION_STRINGS,
- STRING_TYPE_NAME,
- AddonManager.VIEW_TYPE_LIST,
- 11000,
- AddonManager.TYPE_UI_HIDE_EMPTY),
- ]);
- }
-
- },
-
- _unregisterWithAddonManager: function () {
- this._log.trace("Unregistering instance with Addon Manager.");
-
- this._log.trace("Removing install listener from add-on manager.");
- AddonManager.removeInstallListener(this);
- this._log.trace("Removing addon listener from add-on manager.");
- AddonManager.removeAddonListener(this);
- this._log.trace("Finished unregistering with addon manager.");
-
- if (gAddonProvider) {
- this._log.trace("Unregistering previous experiment add-on provider.");
- AddonManagerPrivate.unregisterProvider(gAddonProvider);
- gAddonProvider = null;
- }
- },
-
- /*
- * Change the PreviousExperimentsProvider that this instance uses.
- * For testing only.
- */
- _setPreviousExperimentsProvider: function (provider) {
- this._unregisterWithAddonManager();
- this._registerWithAddonManager(provider);
- },
-
- /**
- * Throws an exception if we've already shut down.
- */
- _checkForShutdown: function() {
- if (this._shutdown) {
- throw new AlreadyShutdownError("uninit() already called");
- }
- },
-
- /**
- * Whether the experiments feature is enabled.
- */
- get enabled() {
- return gExperimentsEnabled;
- },
-
- /**
- * Toggle whether the experiments feature is enabled or not.
- */
- set enabled(enabled) {
- this._log.trace("set enabled(" + enabled + ")");
- gPrefs.set(PREF_ENABLED, enabled);
- },
-
- _toggleExperimentsEnabled: Task.async(function* (enabled) {
- this._log.trace("_toggleExperimentsEnabled(" + enabled + ")");
- let wasEnabled = gExperimentsEnabled;
- gExperimentsEnabled = enabled && TelemetryUtils.isTelemetryEnabled;
-
- if (wasEnabled == gExperimentsEnabled) {
- return;
- }
-
- if (gExperimentsEnabled) {
- yield this.updateManifest();
- } else {
- yield this.disableExperiment(TELEMETRY_LOG.TERMINATION.SERVICE_DISABLED);
- if (this._timer) {
- this._timer.clear();
- }
- }
- }),
-
- _telemetryStatusChanged: function () {
- this._toggleExperimentsEnabled(gExperimentsEnabled);
- },
-
- /**
- * Returns a promise that is resolved with an array of `ExperimentInfo` objects,
- * which provide info on the currently and recently active experiments.
- * The array is in chronological order.
- *
- * The experiment info is of the form:
- * {
- * id: <string>,
- * name: <string>,
- * description: <string>,
- * active: <boolean>,
- * endDate: <integer>, // epoch ms
- * detailURL: <string>,
- * ... // possibly extended later
- * }
- *
- * @return Promise<Array<ExperimentInfo>> Array of experiment info objects.
- */
- getExperiments: function () {
- return Task.spawn(function*() {
- yield this._loadTask;
- let list = [];
-
- for (let [id, experiment] of this._experiments) {
- if (!experiment.startDate) {
- // We only collect experiments that are or were active.
- continue;
- }
-
- list.push({
- id: id,
- name: experiment._name,
- description: experiment._description,
- active: experiment.enabled,
- endDate: experiment.endDate.getTime(),
- detailURL: experiment._homepageURL,
- branch: experiment.branch,
- });
- }
-
- // Sort chronologically, descending.
- list.sort((a, b) => b.endDate - a.endDate);
- return list;
- }.bind(this));
- },
-
- /**
- * Returns the ExperimentInfo for the active experiment, or null
- * if there is none.
- */
- getActiveExperiment: function () {
- let experiment = this._getActiveExperiment();
- if (!experiment) {
- return null;
- }
-
- let info = {
- id: experiment.id,
- name: experiment._name,
- description: experiment._description,
- active: experiment.enabled,
- endDate: experiment.endDate.getTime(),
- detailURL: experiment._homepageURL,
- };
-
- return info;
- },
-
- /**
- * Experiment "branch" support. If an experiment has multiple branches, it
- * can record the branch with the experiment system and it will
- * automatically be included in data reporting (FHR/telemetry payloads).
- */
-
- /**
- * Set the experiment branch for the specified experiment ID.
- * @returns Promise<>
- */
- setExperimentBranch: Task.async(function*(id, branchstr) {
- yield this._loadTask;
- let e = this._experiments.get(id);
- if (!e) {
- throw new Error("Experiment not found");
- }
- e.branch = String(branchstr);
- this._log.trace("setExperimentBranch(" + id + ", " + e.branch + ") _dirty=" + this._dirty);
- this._dirty = true;
- Services.obs.notifyObservers(null, EXPERIMENTS_CHANGED_TOPIC, null);
- yield this._run();
- }),
- /**
- * Get the branch of the specified experiment. If the experiment is unknown,
- * throws an error.
- *
- * @param id The ID of the experiment. Pass null for the currently running
- * experiment.
- * @returns Promise<string|null>
- * @throws Error if the specified experiment ID is unknown, or if there is no
- * current experiment.
- */
- getExperimentBranch: Task.async(function*(id=null) {
- yield this._loadTask;
- let e;
- if (id) {
- e = this._experiments.get(id);
- if (!e) {
- throw new Error("Experiment not found");
- }
- } else {
- e = this._getActiveExperiment();
- if (e === null) {
- throw new Error("No active experiment");
- }
- }
- return e.branch;
- }),
-
- /**
- * Determine whether another date has the same UTC day as now().
- */
- _dateIsTodayUTC: function (d) {
- let now = this._policy.now();
-
- return stripDateToMidnight(now).getTime() == stripDateToMidnight(d).getTime();
- },
-
- /**
- * Obtain the entry of the most recent active experiment that was active
- * today.
- *
- * If no experiment was active today, this resolves to nothing.
- *
- * Assumption: Only a single experiment can be active at a time.
- *
- * @return Promise<object>
- */
- lastActiveToday: function () {
- return Task.spawn(function* getMostRecentActiveExperimentTask() {
- let experiments = yield this.getExperiments();
-
- // Assumption: Ordered chronologically, descending, with active always
- // first.
- for (let experiment of experiments) {
- if (experiment.active) {
- return experiment;
- }
-
- if (experiment.endDate && this._dateIsTodayUTC(experiment.endDate)) {
- return experiment;
- }
- }
- return null;
- }.bind(this));
- },
-
- _run: function() {
- this._log.trace("_run");
- this._checkForShutdown();
- if (!this._mainTask) {
- this._mainTask = Task.spawn(function*() {
- try {
- yield this._main();
- } catch (e) {
- // In the CacheWriteError case we want to reschedule
- if (!(e instanceof CacheWriteError)) {
- this._log.error("_main caught error: " + e);
- return;
- }
- } finally {
- this._mainTask = null;
- }
- this._log.trace("_main finished, scheduling next run");
- try {
- yield this._scheduleNextRun();
- } catch (ex) {
- // We error out of tasks after shutdown via this exception.
- if (!(ex instanceof AlreadyShutdownError)) {
- throw ex;
- }
- }
- }.bind(this));
- }
- return this._mainTask;
- },
-
- _main: function*() {
- do {
- this._log.trace("_main iteration");
- yield this._loadTask;
- if (!gExperimentsEnabled) {
- this._refresh = false;
- }
-
- if (this._refresh) {
- yield this._loadManifest();
- }
- yield this._evaluateExperiments();
- if (this._dirty) {
- yield this._saveToCache();
- }
- // If somebody called .updateManifest() or disableExperiment()
- // while we were running, go again right now.
- }
- while (this._refresh || this._terminateReason || this._dirty);
- },
-
- _loadManifest: function*() {
- this._log.trace("_loadManifest");
- let uri = Services.urlFormatter.formatURLPref(PREF_BRANCH + PREF_MANIFEST_URI);
-
- this._checkForShutdown();
-
- this._refresh = false;
- try {
- let responseText = yield this._httpGetRequest(uri);
- this._log.trace("_loadManifest() - responseText=\"" + responseText + "\"");
-
- if (this._shutdown) {
- return;
- }
-
- let data = JSON.parse(responseText);
- this._updateExperiments(data);
- } catch (e) {
- this._log.error("_loadManifest - failure to fetch/parse manifest (continuing anyway): " + e);
- }
- },
-
- /**
- * Fetch an updated list of experiments and trigger experiment updates.
- * Do only use when experiments are enabled.
- *
- * @return Promise<>
- * The promise is resolved when the manifest and experiment list is updated.
- */
- updateManifest: function () {
- this._log.trace("updateManifest()");
-
- if (!gExperimentsEnabled) {
- return Promise.reject(new Error("experiments are disabled"));
- }
-
- if (this._shutdown) {
- return Promise.reject(Error("uninit() alrady called"));
- }
-
- this._refresh = true;
- return this._run();
- },
-
- notify: function (timer) {
- this._log.trace("notify()");
- this._checkForShutdown();
- return this._run();
- },
-
- // START OF ADD-ON LISTENERS
-
- onUninstalled: function (addon) {
- this._log.trace("onUninstalled() - addon id: " + addon.id);
- if (gActiveUninstallAddonIDs.has(addon.id)) {
- this._log.trace("matches pending uninstall");
- return;
- }
- let activeExperiment = this._getActiveExperiment();
- if (!activeExperiment || activeExperiment._addonId != addon.id) {
- return;
- }
-
- this.disableExperiment(TELEMETRY_LOG.TERMINATION.ADDON_UNINSTALLED);
- },
-
- /**
- * @returns {Boolean} returns false when we cancel the install.
- */
- onInstallStarted: function (install) {
- if (install.addon.type != "experiment") {
- return true;
- }
-
- this._log.trace("onInstallStarted() - " + install.addon.id);
- if (install.addon.appDisabled) {
- // This is a PreviousExperiment
- return true;
- }
-
- // We want to be in control of all experiment add-ons: reject installs
- // for add-ons that we don't know about.
-
- // We have a race condition of sorts to worry about here. We have 2
- // onInstallStarted listeners. This one (the global one) and the one
- // created as part of ExperimentEntry._installAddon. Because of the order
- // they are registered in, this one likely executes first. Unfortunately,
- // this means that the add-on ID is not yet set on the ExperimentEntry.
- // So, we can't just look at this._trackedAddonIds because the new experiment
- // will have its add-on ID set to null. We work around this by storing a
- // identifying field - the source URL of the install - in a module-level
- // variable (so multiple Experiments instances doesn't cancel each other
- // out).
-
- if (this._trackedAddonIds.has(install.addon.id)) {
- this._log.info("onInstallStarted allowing install because add-on ID " +
- "tracked by us.");
- return true;
- }
-
- if (gActiveInstallURLs.has(install.sourceURI.spec)) {
- this._log.info("onInstallStarted allowing install because install " +
- "tracked by us.");
- return true;
- }
-
- this._log.warn("onInstallStarted cancelling install of unknown " +
- "experiment add-on: " + install.addon.id);
- return false;
- },
-
- // END OF ADD-ON LISTENERS.
-
- _getExperimentByAddonId: function (addonId) {
- for (let [, entry] of this._experiments) {
- if (entry._addonId === addonId) {
- return entry;
- }
- }
-
- return null;
- },
-
- /*
- * Helper function to make HTTP GET requests. Returns a promise that is resolved with
- * the responseText when the request is complete.
- */
- _httpGetRequest: function (url) {
- this._log.trace("httpGetRequest(" + url + ")");
- let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
-
- this._networkRequest = xhr;
- let deferred = Promise.defer();
-
- let log = this._log;
- let errorhandler = (evt) => {
- log.error("httpGetRequest::onError() - Error making request to " + url + ": " + evt.type);
- deferred.reject(new Error("Experiments - XHR error for " + url + " - " + evt.type));
- this._networkRequest = null;
- };
- xhr.onerror = errorhandler;
- xhr.ontimeout = errorhandler;
- xhr.onabort = errorhandler;
-
- xhr.onload = (event) => {
- if (xhr.status !== 200 && xhr.state !== 0) {
- log.error("httpGetRequest::onLoad() - Request to " + url + " returned status " + xhr.status);
- deferred.reject(new Error("Experiments - XHR status for " + url + " is " + xhr.status));
- this._networkRequest = null;
- return;
- }
-
- deferred.resolve(xhr.responseText);
- this._networkRequest = null;
- };
-
- try {
- xhr.open("GET", url);
-
- if (xhr.channel instanceof Ci.nsISupportsPriority) {
- xhr.channel.priority = Ci.nsISupportsPriority.PRIORITY_LOWEST;
- }
-
- xhr.timeout = MANIFEST_FETCH_TIMEOUT_MSEC;
- xhr.send(null);
- } catch (e) {
- this._log.error("httpGetRequest() - Error opening request to " + url + ": " + e);
- return Promise.reject(new Error("Experiments - Error opening XHR for " + url));
- }
- return deferred.promise;
- },
-
- /*
- * Path of the cache file we use in the profile.
- */
- get _cacheFilePath() {
- return OS.Path.join(OS.Constants.Path.profileDir, FILE_CACHE);
- },
-
- /*
- * Part of the main task to save the cache to disk, called from _main.
- */
- _saveToCache: function* () {
- this._log.trace("_saveToCache");
- let path = this._cacheFilePath;
- this._dirty = false;
- try {
- let textData = JSON.stringify({
- version: CACHE_VERSION,
- data: [...this._experiments.values()].map(e => e.toJSON()),
- });
-
- let encoder = new TextEncoder();
- let data = encoder.encode(textData);
- let options = { tmpPath: path + ".tmp", compression: "lz4" };
- yield this._policy.delayCacheWrite(OS.File.writeAtomic(path, data, options));
- } catch (e) {
- // We failed to write the cache, it's still dirty.
- this._dirty = true;
- this._log.error("_saveToCache failed and caught error: " + e);
- throw new CacheWriteError();
- }
-
- this._log.debug("_saveToCache saved to " + path);
- },
-
- /*
- * Task function, load the cached experiments manifest file from disk.
- */
- _loadFromCache: Task.async(function* () {
- this._log.trace("_loadFromCache");
- let path = this._cacheFilePath;
- try {
- let result = yield loadJSONAsync(path, { compression: "lz4" });
- this._populateFromCache(result);
- } catch (e) {
- if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
- // No cached manifest yet.
- this._experiments = new Map();
- } else {
- throw e;
- }
- }
- }),
-
- _populateFromCache: function (data) {
- this._log.trace("populateFromCache() - data: " + JSON.stringify(data));
-
- // If the user has a newer cache version than we can understand, we fail
- // hard; no experiments should be active in this older client.
- if (CACHE_VERSION !== data.version) {
- throw new Error("Experiments::_populateFromCache() - invalid cache version");
- }
-
- let experiments = new Map();
- for (let item of data.data) {
- let entry = new Experiments.ExperimentEntry(this._policy);
- if (!entry.initFromCacheData(item)) {
- continue;
- }
-
- // Discard old experiments if they ended more than 180 days ago.
- if (entry.shouldDiscard()) {
- // We discarded an experiment, the cache needs to be updated.
- this._dirty = true;
- continue;
- }
-
- experiments.set(entry.id, entry);
- }
-
- this._experiments = experiments;
- },
-
- /*
- * Update the experiment entries from the experiments
- * array in the manifest
- */
- _updateExperiments: function (manifestObject) {
- this._log.trace("_updateExperiments() - experiments: " + JSON.stringify(manifestObject));
-
- if (manifestObject.version !== MANIFEST_VERSION) {
- this._log.warning("updateExperiments() - unsupported version " + manifestObject.version);
- }
-
- let experiments = new Map(); // The new experiments map
-
- // Collect new and updated experiments.
- for (let data of manifestObject.experiments) {
- let entry = this._experiments.get(data.id);
-
- if (entry) {
- if (!entry.updateFromManifestData(data)) {
- this._log.error("updateExperiments() - Invalid manifest data for " + data.id);
- continue;
- }
- } else {
- entry = new Experiments.ExperimentEntry(this._policy);
- if (!entry.initFromManifestData(data)) {
- continue;
- }
- }
-
- if (entry.shouldDiscard()) {
- continue;
- }
-
- experiments.set(entry.id, entry);
- }
-
- // Make sure we keep experiments that are or were running.
- // We remove them after KEEP_HISTORY_N_DAYS.
- for (let [id, entry] of this._experiments) {
- if (experiments.has(id)) {
- continue;
- }
-
- if (!entry.startDate || entry.shouldDiscard()) {
- this._log.trace("updateExperiments() - discarding entry for " + id);
- continue;
- }
-
- experiments.set(id, entry);
- }
-
- this._experiments = experiments;
- this._dirty = true;
- },
-
- getActiveExperimentID: function() {
- if (!this._experiments) {
- return null;
- }
- let e = this._getActiveExperiment();
- if (!e) {
- return null;
- }
- return e.id;
- },
-
- getActiveExperimentBranch: function() {
- if (!this._experiments) {
- return null;
- }
- let e = this._getActiveExperiment();
- if (!e) {
- return null;
- }
- return e.branch;
- },
-
- _getActiveExperiment: function () {
- let enabled = [...this._experiments.values()].filter(experiment => experiment._enabled);
-
- if (enabled.length == 1) {
- return enabled[0];
- }
-
- if (enabled.length > 1) {
- this._log.error("getActiveExperimentId() - should not have more than 1 active experiment");
- throw new Error("have more than 1 active experiment");
- }
-
- return null;
- },
-
- /**
- * Disables all active experiments.
- *
- * @return Promise<> Promise that will get resolved once the task is done or failed.
- */
- disableExperiment: function (reason) {
- if (!reason) {
- throw new Error("Must specify a termination reason.");
- }
-
- this._log.trace("disableExperiment()");
- this._terminateReason = reason;
- return this._run();
- },
-
- /**
- * The Set of add-on IDs that we know about from manifests.
- */
- get _trackedAddonIds() {
- if (!this._experiments) {
- return new Set();
- }
-
- return new Set([...this._experiments.values()].map(e => e._addonId));
- },
-
- /*
- * Task function to check applicability of experiments, disable the active
- * experiment if needed and activate the first applicable candidate.
- */
- _evaluateExperiments: function*() {
- this._log.trace("_evaluateExperiments");
-
- this._checkForShutdown();
-
- // The first thing we do is reconcile our state against what's in the
- // Addon Manager. It's possible that the Addon Manager knows of experiment
- // add-ons that we don't. This could happen if an experiment gets installed
- // when we're not listening or if there is a bug in our synchronization
- // code.
- //
- // We have a few options of what to do with unknown experiment add-ons
- // coming from the Addon Manager. Ideally, we'd convert these to
- // ExperimentEntry instances and stuff them inside this._experiments.
- // However, since ExperimentEntry contain lots of metadata from the
- // manifest and trying to make up data could be error prone, it's safer
- // to not try. Furthermore, if an experiment really did come from us, we
- // should have some record of it. In the end, we decide to discard all
- // knowledge for these unknown experiment add-ons.
- let installedExperiments = yield installedExperimentAddons();
- let expectedAddonIds = this._trackedAddonIds;
- let unknownAddons = installedExperiments.filter(a => !expectedAddonIds.has(a.id));
- if (unknownAddons.length) {
- this._log.warn("_evaluateExperiments() - unknown add-ons in AddonManager: " +
- unknownAddons.map(a => a.id).join(", "));
-
- yield uninstallAddons(unknownAddons);
- }
-
- let activeExperiment = this._getActiveExperiment();
- let activeChanged = false;
-
- if (!activeExperiment) {
- // Avoid this pref staying out of sync if there were e.g. crashes.
- gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
- }
-
- // Ensure the active experiment is in the proper state. This may install,
- // uninstall, upgrade, or enable the experiment add-on. What exactly is
- // abstracted away from us by design.
- if (activeExperiment) {
- let changes;
- let shouldStopResult = yield activeExperiment.shouldStop();
- if (shouldStopResult.shouldStop) {
- let expireReasons = ["endTime", "maxActiveSeconds"];
- let kind, reason;
-
- if (expireReasons.indexOf(shouldStopResult.reason[0]) != -1) {
- kind = TELEMETRY_LOG.TERMINATION.EXPIRED;
- reason = null;
- } else {
- kind = TELEMETRY_LOG.TERMINATION.RECHECK;
- reason = shouldStopResult.reason;
- }
- changes = yield activeExperiment.stop(kind, reason);
- }
- else if (this._terminateReason) {
- changes = yield activeExperiment.stop(this._terminateReason);
- }
- else {
- changes = yield activeExperiment.reconcileAddonState();
- }
-
- if (changes) {
- this._dirty = true;
- activeChanged = true;
- }
-
- if (!activeExperiment._enabled) {
- activeExperiment = null;
- activeChanged = true;
- }
- }
-
- this._terminateReason = null;
-
- if (!activeExperiment && gExperimentsEnabled) {
- for (let [id, experiment] of this._experiments) {
- let applicable;
- let reason = null;
- try {
- applicable = yield experiment.isApplicable();
- }
- catch (e) {
- applicable = false;
- reason = e;
- }
-
- if (!applicable && reason && reason[0] != "was-active") {
- // Report this from here to avoid over-reporting.
- let data = [TELEMETRY_LOG.ACTIVATION.REJECTED, id];
- data = data.concat(reason);
- const key = TELEMETRY_LOG.ACTIVATION_KEY;
- TelemetryLog.log(key, data);
- this._log.trace("evaluateExperiments() - added " + key + " to TelemetryLog: " + JSON.stringify(data));
- }
-
- if (!applicable) {
- continue;
- }
-
- this._log.debug("evaluateExperiments() - activating experiment " + id);
- try {
- yield experiment.start();
- activeChanged = true;
- activeExperiment = experiment;
- this._dirty = true;
- break;
- } catch (e) {
- // On failure, clean up the best we can and try the next experiment.
- this._log.error("evaluateExperiments() - Unable to start experiment: " + e.message);
- experiment._enabled = false;
- yield experiment.reconcileAddonState();
- }
- }
- }
-
- gPrefs.set(PREF_ACTIVE_EXPERIMENT, activeExperiment != null);
-
- if (activeChanged || this._firstEvaluate) {
- Services.obs.notifyObservers(null, EXPERIMENTS_CHANGED_TOPIC, null);
- this._firstEvaluate = false;
- }
-
- if ("@mozilla.org/toolkit/crash-reporter;1" in Cc && activeExperiment) {
- try {
- gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id);
- gCrashReporter.annotateCrashReport("ActiveExperimentBranch", activeExperiment.branch);
- } catch (e) {
- // It's ok if crash reporting is disabled.
- }
- }
- },
-
- /*
- * Schedule the soonest re-check of experiment applicability that is needed.
- */
- _scheduleNextRun: function () {
- this._checkForShutdown();
-
- if (this._timer) {
- this._timer.clear();
- }
-
- if (!gExperimentsEnabled || this._experiments.length == 0) {
- return;
- }
-
- let time = null;
- let now = this._policy.now().getTime();
- if (this._dirty) {
- // If we failed to write the cache, we should try again periodically
- time = now + 1000 * CACHE_WRITE_RETRY_DELAY_SEC;
- }
-
- for (let [, experiment] of this._experiments) {
- let scheduleTime = experiment.getScheduleTime();
- if (scheduleTime > now) {
- if (time !== null) {
- time = Math.min(time, scheduleTime);
- } else {
- time = scheduleTime;
- }
- }
- }
-
- if (time === null) {
- // No schedule time found.
- return;
- }
-
- this._log.trace("scheduleExperimentEvaluation() - scheduling for "+time+", now: "+now);
- this._policy.oneshotTimer(this.notify, time - now, this, "_timer");
- },
-};
-
-
-/*
- * Represents a single experiment.
- */
-
-Experiments.ExperimentEntry = function (policy) {
- this._policy = policy || new Experiments.Policy();
- let log = Log.repository.getLoggerWithMessagePrefix(
- "Browser.Experiments.Experiments",
- "ExperimentEntry #" + gExperimentEntryCounter++ + "::");
- this._log = Object.create(log);
- this._log.log = (level, string, params) => {
- if (gExperiments) {
- gExperiments._addToForensicsLog("ExperimentEntry", string);
- }
- log.log(level, string, params);
- };
-
- // Is the experiment supposed to be running.
- this._enabled = false;
- // When this experiment was started, if ever.
- this._startDate = null;
- // When this experiment was ended, if ever.
- this._endDate = null;
- // The condition data from the manifest.
- this._manifestData = null;
- // For an active experiment, signifies whether we need to update the xpi.
- this._needsUpdate = false;
- // A random sample value for comparison against the manifest conditions.
- this._randomValue = null;
- // When this entry was last changed for respecting history retention duration.
- this._lastChangedDate = null;
- // Has this experiment failed to activate before?
- this._failedStart = false;
- // The experiment branch
- this._branch = null;
-
- // We grab these from the addon after download.
- this._name = null;
- this._description = null;
- this._homepageURL = null;
- this._addonId = null;
-};
-
-Experiments.ExperimentEntry.prototype = {
- MANIFEST_REQUIRED_FIELDS: new Set([
- "id",
- "xpiURL",
- "xpiHash",
- "startTime",
- "endTime",
- "maxActiveSeconds",
- "appName",
- "channel",
- ]),
-
- MANIFEST_OPTIONAL_FIELDS: new Set([
- "maxStartTime",
- "minVersion",
- "maxVersion",
- "version",
- "minBuildID",
- "maxBuildID",
- "buildIDs",
- "os",
- "locale",
- "sample",
- "disabled",
- "frozen",
- "jsfilter",
- ]),
-
- SERIALIZE_KEYS: new Set([
- "_enabled",
- "_manifestData",
- "_needsUpdate",
- "_randomValue",
- "_failedStart",
- "_name",
- "_description",
- "_homepageURL",
- "_addonId",
- "_startDate",
- "_endDate",
- "_branch",
- ]),
-
- DATE_KEYS: new Set([
- "_startDate",
- "_endDate",
- ]),
-
- UPGRADE_KEYS: new Map([
- ["_branch", null],
- ]),
-
- ADDON_CHANGE_NONE: 0,
- ADDON_CHANGE_INSTALL: 1,
- ADDON_CHANGE_UNINSTALL: 2,
- ADDON_CHANGE_ENABLE: 4,
-
- /*
- * Initialize entry from the manifest.
- * @param data The experiment data from the manifest.
- * @return boolean Whether initialization succeeded.
- */
- initFromManifestData: function (data) {
- if (!this._isManifestDataValid(data)) {
- return false;
- }
-
- this._manifestData = data;
-
- this._randomValue = this._policy.random();
- this._lastChangedDate = this._policy.now();
-
- return true;
- },
-
- get enabled() {
- return this._enabled;
- },
-
- get id() {
- return this._manifestData.id;
- },
-
- get branch() {
- return this._branch;
- },
-
- set branch(v) {
- this._branch = v;
- },
-
- get startDate() {
- return this._startDate;
- },
-
- get endDate() {
- if (!this._startDate) {
- return null;
- }
-
- let endTime = 0;
-
- if (!this._enabled) {
- return this._endDate;
- }
-
- let maxActiveMs = 1000 * this._manifestData.maxActiveSeconds;
- endTime = Math.min(1000 * this._manifestData.endTime,
- this._startDate.getTime() + maxActiveMs);
-
- return new Date(endTime);
- },
-
- get needsUpdate() {
- return this._needsUpdate;
- },
-
- /*
- * Initialize entry from the cache.
- * @param data The entry data from the cache.
- * @return boolean Whether initialization succeeded.
- */
- initFromCacheData: function (data) {
- for (let [key, dval] of this.UPGRADE_KEYS) {
- if (!(key in data)) {
- data[key] = dval;
- }
- }
-
- for (let key of this.SERIALIZE_KEYS) {
- if (!(key in data) && !this.DATE_KEYS.has(key)) {
- this._log.error("initFromCacheData() - missing required key " + key);
- return false;
- }
- }
-
- if (!this._isManifestDataValid(data._manifestData)) {
- return false;
- }
-
- // Dates are restored separately from epoch ms, everything else is just
- // copied in.
-
- this.SERIALIZE_KEYS.forEach(key => {
- if (!this.DATE_KEYS.has(key)) {
- this[key] = data[key];
- }
- });
-
- this.DATE_KEYS.forEach(key => {
- if (key in data) {
- let date = new Date();
- date.setTime(data[key]);
- this[key] = date;
- }
- });
-
- // In order for the experiment's data expiration mechanism to work, use the experiment's
- // |_endData| as the |_lastChangedDate| (if available).
- this._lastChangedDate = this._endDate ? this._endDate : this._policy.now();
-
- return true;
- },
-
- /*
- * Returns a JSON representation of this object.
- */
- toJSON: function () {
- let obj = {};
-
- // Dates are serialized separately as epoch ms.
-
- this.SERIALIZE_KEYS.forEach(key => {
- if (!this.DATE_KEYS.has(key)) {
- obj[key] = this[key];
- }
- });
-
- this.DATE_KEYS.forEach(key => {
- if (this[key]) {
- obj[key] = this[key].getTime();
- }
- });
-
- return obj;
- },
-
- /*
- * Update from the experiment data from the manifest.
- * @param data The experiment data from the manifest.
- * @return boolean Whether updating succeeded.
- */
- updateFromManifestData: function (data) {
- let old = this._manifestData;
-
- if (!this._isManifestDataValid(data)) {
- return false;
- }
-
- if (this._enabled) {
- if (old.xpiHash !== data.xpiHash) {
- // A changed hash means we need to update active experiments.
- this._needsUpdate = true;
- }
- } else if (this._failedStart &&
- (old.xpiHash !== data.xpiHash) ||
- (old.xpiURL !== data.xpiURL)) {
- // Retry installation of previously invalid experiments
- // if hash or url changed.
- this._failedStart = false;
- }
-
- this._manifestData = data;
- this._lastChangedDate = this._policy.now();
-
- return true;
- },
-
- /*
- * Is this experiment applicable?
- * @return Promise<> Resolved if the experiment is applicable.
- * If it is not applicable it is rejected with
- * a Promise<string> which contains the reason.
- */
- isApplicable: function () {
- let versionCmp = Cc["@mozilla.org/xpcom/version-comparator;1"]
- .getService(Ci.nsIVersionComparator);
- let app = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
- let runtime = Cc["@mozilla.org/xre/app-info;1"]
- .getService(Ci.nsIXULRuntime);
-
- let locale = this._policy.locale();
- let channel = this._policy.updatechannel();
- let data = this._manifestData;
-
- let now = this._policy.now() / 1000; // The manifest times are in seconds.
- let maxActive = data.maxActiveSeconds || 0;
- let startSec = (this.startDate || 0) / 1000;
-
- this._log.trace("isApplicable() - now=" + now
- + ", randomValue=" + this._randomValue);
-
- // Not applicable if it already ran.
-
- if (!this.enabled && this._endDate) {
- return Promise.reject(["was-active"]);
- }
-
- // Define and run the condition checks.
-
- let simpleChecks = [
- { name: "failedStart",
- condition: () => !this._failedStart },
- { name: "disabled",
- condition: () => !data.disabled },
- { name: "frozen",
- condition: () => !data.frozen || this._enabled },
- { name: "startTime",
- condition: () => now >= data.startTime },
- { name: "endTime",
- condition: () => now < data.endTime },
- { name: "maxStartTime",
- condition: () => this._startDate || !data.maxStartTime || now <= data.maxStartTime },
- { name: "maxActiveSeconds",
- condition: () => !this._startDate || now <= (startSec + maxActive) },
- { name: "appName",
- condition: () => !data.appName || data.appName.indexOf(app.name) != -1 },
- { name: "minBuildID",
- condition: () => !data.minBuildID || app.platformBuildID >= data.minBuildID },
- { name: "maxBuildID",
- condition: () => !data.maxBuildID || app.platformBuildID <= data.maxBuildID },
- { name: "buildIDs",
- condition: () => !data.buildIDs || data.buildIDs.indexOf(app.platformBuildID) != -1 },
- { name: "os",
- condition: () => !data.os || data.os.indexOf(runtime.OS) != -1 },
- { name: "channel",
- condition: () => !data.channel || data.channel.indexOf(channel) != -1 },
- { name: "locale",
- condition: () => !data.locale || data.locale.indexOf(locale) != -1 },
- { name: "sample",
- condition: () => data.sample === undefined || this._randomValue <= data.sample },
- { name: "version",
- condition: () => !data.version || data.version.indexOf(app.version) != -1 },
- { name: "minVersion",
- condition: () => !data.minVersion || versionCmp.compare(app.version, data.minVersion) >= 0 },
- { name: "maxVersion",
- condition: () => !data.maxVersion || versionCmp.compare(app.version, data.maxVersion) <= 0 },
- ];
-
- for (let check of simpleChecks) {
- let result = check.condition();
- if (!result) {
- this._log.debug("isApplicable() - id="
- + data.id + " - test '" + check.name + "' failed");
- return Promise.reject([check.name]);
- }
- }
-
- if (data.jsfilter) {
- return this._runFilterFunction(data.jsfilter);
- }
-
- return Promise.resolve(true);
- },
-
- /*
- * Run the jsfilter function from the manifest in a sandbox and return the
- * result (forced to boolean).
- */
- _runFilterFunction: Task.async(function* (jsfilter) {
- this._log.trace("runFilterFunction() - filter: " + jsfilter);
-
- let ssm = Services.scriptSecurityManager;
- const nullPrincipal = ssm.createNullPrincipal({});
- let options = {
- sandboxName: "telemetry experiments jsfilter sandbox",
- wantComponents: false,
- };
-
- let sandbox = Cu.Sandbox(nullPrincipal, options);
- try {
- Cu.evalInSandbox(jsfilter, sandbox);
- } catch (e) {
- this._log.error("runFilterFunction() - failed to eval jsfilter: " + e.message);
- throw ["jsfilter-evalfailed"];
- }
-
- let currentEnvironment = yield TelemetryEnvironment.onInitialized();
-
- Object.defineProperty(sandbox, "_e",
- { get: () => Cu.cloneInto(currentEnvironment, sandbox) });
-
- let result = false;
- try {
- result = !!Cu.evalInSandbox("filter({get telemetryEnvironment() { return _e; } })", sandbox);
- }
- catch (e) {
- this._log.debug("runFilterFunction() - filter function failed: "
- + e.message + ", " + e.stack);
- throw ["jsfilter-threw", e.message];
- }
- finally {
- Cu.nukeSandbox(sandbox);
- }
-
- if (!result) {
- throw ["jsfilter-false"];
- }
-
- return true;
- }),
-
- /*
- * Start running the experiment.
- *
- * @return Promise<> Resolved when the operation is complete.
- */
- start: Task.async(function* () {
- this._log.trace("start() for " + this.id);
-
- this._enabled = true;
- return yield this.reconcileAddonState();
- }),
-
- // Async install of the addon for this experiment, part of the start task above.
- _installAddon: Task.async(function* () {
- let deferred = Promise.defer();
-
- let hash = this._policy.ignoreHashes ? null : this._manifestData.xpiHash;
-
- let install = yield addonInstallForURL(this._manifestData.xpiURL, hash);
- gActiveInstallURLs.add(install.sourceURI.spec);
-
- let failureHandler = (install, handler) => {
- let message = "AddonInstall " + handler + " for " + this.id + ", state=" +
- (install.state || "?") + ", error=" + install.error;
- this._log.error("_installAddon() - " + message);
- this._failedStart = true;
- gActiveInstallURLs.delete(install.sourceURI.spec);
-
- TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY,
- [TELEMETRY_LOG.ACTIVATION.INSTALL_FAILURE, this.id]);
-
- deferred.reject(new Error(message));
- };
-
- let listener = {
- _expectedID: null,
-
- onDownloadEnded: install => {
- this._log.trace("_installAddon() - onDownloadEnded for " + this.id);
-
- if (install.existingAddon) {
- this._log.warn("_installAddon() - onDownloadEnded, addon already installed");
- }
-
- if (install.addon.type !== "experiment") {
- this._log.error("_installAddon() - onDownloadEnded, wrong addon type");
- install.cancel();
- }
- },
-
- onInstallStarted: install => {
- this._log.trace("_installAddon() - onInstallStarted for " + this.id);
-
- if (install.existingAddon) {
- this._log.warn("_installAddon() - onInstallStarted, addon already installed");
- }
-
- if (install.addon.type !== "experiment") {
- this._log.error("_installAddon() - onInstallStarted, wrong addon type");
- return false;
- }
- return undefined;
- },
-
- onInstallEnded: install => {
- this._log.trace("_installAddon() - install ended for " + this.id);
- gActiveInstallURLs.delete(install.sourceURI.spec);
-
- this._lastChangedDate = this._policy.now();
- this._startDate = this._policy.now();
- this._enabled = true;
-
- TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY,
- [TELEMETRY_LOG.ACTIVATION.ACTIVATED, this.id]);
-
- let addon = install.addon;
- this._name = addon.name;
- this._addonId = addon.id;
- this._description = addon.description || "";
- this._homepageURL = addon.homepageURL || "";
-
- // Experiment add-ons default to userDisabled=true. Enable if needed.
- if (addon.userDisabled) {
- this._log.trace("Add-on is disabled. Enabling.");
- listener._expectedID = addon.id;
- AddonManager.addAddonListener(listener);
- addon.userDisabled = false;
- } else {
- this._log.trace("Add-on is enabled. start() completed.");
- deferred.resolve();
- }
- },
-
- onEnabled: addon => {
- this._log.info("onEnabled() for " + addon.id);
-
- if (addon.id != listener._expectedID) {
- return;
- }
-
- AddonManager.removeAddonListener(listener);
- deferred.resolve();
- },
- };
-
- ["onDownloadCancelled", "onDownloadFailed", "onInstallCancelled", "onInstallFailed"]
- .forEach(what => {
- listener[what] = install => failureHandler(install, what)
- });
-
- install.addListener(listener);
- install.install();
-
- return yield deferred.promise;
- }),
-
- /**
- * Stop running the experiment if it is active.
- *
- * @param terminationKind (optional)
- * The termination kind, e.g. ADDON_UNINSTALLED or EXPIRED.
- * @param terminationReason (optional)
- * The termination reason details for termination kind RECHECK.
- * @return Promise<> Resolved when the operation is complete.
- */
- stop: Task.async(function* (terminationKind, terminationReason) {
- this._log.trace("stop() - id=" + this.id + ", terminationKind=" + terminationKind);
- if (!this._enabled) {
- throw new Error("Must not call stop() on an inactive experiment.");
- }
-
- this._enabled = false;
- let now = this._policy.now();
- this._lastChangedDate = now;
- this._endDate = now;
-
- let changes = yield this.reconcileAddonState();
- this._logTermination(terminationKind, terminationReason);
-
- if (terminationKind == TELEMETRY_LOG.TERMINATION.ADDON_UNINSTALLED) {
- changes |= this.ADDON_CHANGE_UNINSTALL;
- }
-
- return changes;
- }),
-
- /**
- * Reconcile the state of the add-on against what it's supposed to be.
- *
- * If we are active, ensure the add-on is enabled and up to date.
- *
- * If we are inactive, ensure the add-on is not installed.
- */
- reconcileAddonState: Task.async(function* () {
- this._log.trace("reconcileAddonState()");
-
- if (!this._enabled) {
- if (!this._addonId) {
- this._log.trace("reconcileAddonState() - Experiment is not enabled and " +
- "has no add-on. Doing nothing.");
- return this.ADDON_CHANGE_NONE;
- }
-
- let addon = yield this._getAddon();
- if (!addon) {
- this._log.trace("reconcileAddonState() - Inactive experiment has no " +
- "add-on. Doing nothing.");
- return this.ADDON_CHANGE_NONE;
- }
-
- this._log.info("reconcileAddonState() - Uninstalling add-on for inactive " +
- "experiment: " + addon.id);
- gActiveUninstallAddonIDs.add(addon.id);
- yield uninstallAddons([addon]);
- gActiveUninstallAddonIDs.delete(addon.id);
- return this.ADDON_CHANGE_UNINSTALL;
- }
-
- // If we get here, we're supposed to be active.
-
- let changes = 0;
-
- // That requires an add-on.
- let currentAddon = yield this._getAddon();
-
- // If we have an add-on but it isn't up to date, uninstall it
- // (to prepare for reinstall).
- if (currentAddon && this._needsUpdate) {
- this._log.info("reconcileAddonState() - Uninstalling add-on because update " +
- "needed: " + currentAddon.id);
- gActiveUninstallAddonIDs.add(currentAddon.id);
- yield uninstallAddons([currentAddon]);
- gActiveUninstallAddonIDs.delete(currentAddon.id);
- changes |= this.ADDON_CHANGE_UNINSTALL;
- }
-
- if (!currentAddon || this._needsUpdate) {
- this._log.info("reconcileAddonState() - Installing add-on.");
- yield this._installAddon();
- changes |= this.ADDON_CHANGE_INSTALL;
- }
-
- let addon = yield this._getAddon();
- if (!addon) {
- throw new Error("Could not obtain add-on for experiment that should be " +
- "enabled.");
- }
-
- // If we have the add-on and it is enabled, we are done.
- if (!addon.userDisabled) {
- return changes;
- }
-
- // Check permissions to see if we can enable the addon.
- if (!(addon.permissions & AddonManager.PERM_CAN_ENABLE)) {
- throw new Error("Don't have permission to enable addon " + addon.id + ", perm=" + addon.permission);
- }
-
- // Experiment addons should not require a restart.
- if (addon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE) {
- throw new Error("Experiment addon requires a restart: " + addon.id);
- }
-
- let deferred = Promise.defer();
-
- // Else we need to enable it.
- let listener = {
- onEnabled: enabledAddon => {
- if (enabledAddon.id != addon.id) {
- return;
- }
-
- AddonManager.removeAddonListener(listener);
- deferred.resolve();
- },
- };
-
- for (let handler of ["onDisabled", "onOperationCancelled", "onUninstalled"]) {
- listener[handler] = (evtAddon) => {
- if (evtAddon.id != addon.id) {
- return;
- }
-
- AddonManager.removeAddonListener(listener);
- deferred.reject("Failed to enable addon " + addon.id + " due to: " + handler);
- };
- }
-
- this._log.info("reconcileAddonState() - Activating add-on: " + addon.id);
- AddonManager.addAddonListener(listener);
- addon.userDisabled = false;
- yield deferred.promise;
- changes |= this.ADDON_CHANGE_ENABLE;
-
- this._log.info("reconcileAddonState() - Add-on has been enabled: " + addon.id);
- return changes;
- }),
-
- /**
- * Obtain the underlying Addon from the Addon Manager.
- *
- * @return Promise<Addon|null>
- */
- _getAddon: function () {
- if (!this._addonId) {
- return Promise.resolve(null);
- }
-
- let deferred = Promise.defer();
-
- AddonManager.getAddonByID(this._addonId, (addon) => {
- if (addon && addon.appDisabled) {
- // Don't return PreviousExperiments.
- addon = null;
- }
-
- deferred.resolve(addon);
- });
-
- return deferred.promise;
- },
-
- _logTermination: function (terminationKind, terminationReason) {
- if (terminationKind === undefined) {
- return;
- }
-
- if (!(terminationKind in TELEMETRY_LOG.TERMINATION)) {
- this._log.warn("stop() - unknown terminationKind " + terminationKind);
- return;
- }
-
- let data = [terminationKind, this.id];
- if (terminationReason) {
- data = data.concat(terminationReason);
- }
-
- TelemetryLog.log(TELEMETRY_LOG.TERMINATION_KEY, data);
- },
-
- /**
- * Determine whether an active experiment should be stopped.
- */
- shouldStop: function () {
- if (!this._enabled) {
- throw new Error("shouldStop must not be called on disabled experiments.");
- }
-
- let deferred = Promise.defer();
- this.isApplicable().then(
- () => deferred.resolve({shouldStop: false}),
- reason => deferred.resolve({shouldStop: true, reason: reason})
- );
-
- return deferred.promise;
- },
-
- /*
- * Should this be discarded from the cache due to age?
- */
- shouldDiscard: function () {
- let limit = this._policy.now();
- limit.setDate(limit.getDate() - KEEP_HISTORY_N_DAYS);
- return (this._lastChangedDate < limit);
- },
-
- /*
- * Get next date (in epoch-ms) to schedule a re-evaluation for this.
- * Returns 0 if it doesn't need one.
- */
- getScheduleTime: function () {
- if (this._enabled) {
- let startTime = this._startDate.getTime();
- let maxActiveTime = startTime + 1000 * this._manifestData.maxActiveSeconds;
- return Math.min(1000 * this._manifestData.endTime, maxActiveTime);
- }
-
- if (this._endDate) {
- return this._endDate.getTime();
- }
-
- return 1000 * this._manifestData.startTime;
- },
-
- /*
- * Perform sanity checks on the experiment data.
- */
- _isManifestDataValid: function (data) {
- this._log.trace("isManifestDataValid() - data: " + JSON.stringify(data));
-
- for (let key of this.MANIFEST_REQUIRED_FIELDS) {
- if (!(key in data)) {
- this._log.error("isManifestDataValid() - missing required key: " + key);
- return false;
- }
- }
-
- for (let key in data) {
- if (!this.MANIFEST_OPTIONAL_FIELDS.has(key) &&
- !this.MANIFEST_REQUIRED_FIELDS.has(key)) {
- this._log.error("isManifestDataValid() - unknown key: " + key);
- return false;
- }
- }
-
- return true;
- },
-};
-
-/**
- * Strip a Date down to its UTC midnight.
- *
- * This will return a cloned Date object. The original is unchanged.
- */
-var stripDateToMidnight = function (d) {
- let m = new Date(d);
- m.setUTCHours(0, 0, 0, 0);
-
- return m;
-};
-
-/**
- * An Add-ons Manager provider that knows about old experiments.
- *
- * This provider exposes read-only add-ons corresponding to previously-active
- * experiments. The existence of this provider (and the add-ons it knows about)
- * facilitates the display of old experiments in the Add-ons Manager UI with
- * very little custom code in that component.
- */
-this.Experiments.PreviousExperimentProvider = function (experiments) {
- this._experiments = experiments;
- this._experimentList = [];
- this._log = Log.repository.getLoggerWithMessagePrefix(
- "Browser.Experiments.Experiments",
- "PreviousExperimentProvider #" + gPreviousProviderCounter++ + "::");
-}
-
-this.Experiments.PreviousExperimentProvider.prototype = Object.freeze({
- name: "PreviousExperimentProvider",
-
- startup: function () {
- this._log.trace("startup()");
- Services.obs.addObserver(this, EXPERIMENTS_CHANGED_TOPIC, false);
- },
-
- shutdown: function () {
- this._log.trace("shutdown()");
- try {
- Services.obs.removeObserver(this, EXPERIMENTS_CHANGED_TOPIC);
- } catch (e) {
- // Prevent crash in mochitest-browser3 on Mulet
- }
- },
-
- observe: function (subject, topic, data) {
- switch (topic) {
- case EXPERIMENTS_CHANGED_TOPIC:
- this._updateExperimentList();
- break;
- }
- },
-
- getAddonByID: function (id, cb) {
- for (let experiment of this._experimentList) {
- if (experiment.id == id) {
- cb(new PreviousExperimentAddon(experiment));
- return;
- }
- }
-
- cb(null);
- },
-
- getAddonsByTypes: function (types, cb) {
- if (types && types.length > 0 && types.indexOf("experiment") == -1) {
- cb([]);
- return;
- }
-
- cb(this._experimentList.map(e => new PreviousExperimentAddon(e)));
- },
-
- _updateExperimentList: function () {
- return this._experiments.getExperiments().then((experiments) => {
- let list = experiments.filter(e => !e.active);
-
- let newMap = new Map(list.map(e => [e.id, e]));
- let oldMap = new Map(this._experimentList.map(e => [e.id, e]));
-
- let added = [...newMap.keys()].filter(id => !oldMap.has(id));
- let removed = [...oldMap.keys()].filter(id => !newMap.has(id));
-
- for (let id of added) {
- this._log.trace("updateExperimentList() - adding " + id);
- let wrapper = new PreviousExperimentAddon(newMap.get(id));
- AddonManagerPrivate.callInstallListeners("onExternalInstall", null, wrapper, null, false);
- AddonManagerPrivate.callAddonListeners("onInstalling", wrapper, false);
- }
-
- for (let id of removed) {
- this._log.trace("updateExperimentList() - removing " + id);
- let wrapper = new PreviousExperimentAddon(oldMap.get(id));
- AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
- }
-
- this._experimentList = list;
-
- for (let id of added) {
- let wrapper = new PreviousExperimentAddon(newMap.get(id));
- AddonManagerPrivate.callAddonListeners("onInstalled", wrapper);
- }
-
- for (let id of removed) {
- let wrapper = new PreviousExperimentAddon(oldMap.get(id));
- AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
- }
-
- return this._experimentList;
- });
- },
-});
-
-/**
- * An add-on that represents a previously-installed experiment.
- */
-function PreviousExperimentAddon(experiment) {
- this._id = experiment.id;
- this._name = experiment.name;
- this._endDate = experiment.endDate;
- this._description = experiment.description;
-}
-
-PreviousExperimentAddon.prototype = Object.freeze({
- // BEGIN REQUIRED ADDON PROPERTIES
-
- get appDisabled() {
- return true;
- },
-
- get blocklistState() {
- Ci.nsIBlocklistService.STATE_NOT_BLOCKED
- },
-
- get creator() {
- return new AddonManagerPrivate.AddonAuthor("");
- },
-
- get foreignInstall() {
- return false;
- },
-
- get id() {
- return this._id;
- },
-
- get isActive() {
- return false;
- },
-
- get isCompatible() {
- return true;
- },
-
- get isPlatformCompatible() {
- return true;
- },
-
- get name() {
- return this._name;
- },
-
- get pendingOperations() {
- return AddonManager.PENDING_NONE;
- },
-
- get permissions() {
- return 0;
- },
-
- get providesUpdatesSecurely() {
- return true;
- },
-
- get scope() {
- return AddonManager.SCOPE_PROFILE;
- },
-
- get type() {
- return "experiment";
- },
-
- get userDisabled() {
- return true;
- },
-
- get version() {
- return null;
- },
-
- // END REQUIRED PROPERTIES
-
- // BEGIN OPTIONAL PROPERTIES
-
- get description() {
- return this._description;
- },
-
- get updateDate() {
- return new Date(this._endDate);
- },
-
- // END OPTIONAL PROPERTIES
-
- // BEGIN REQUIRED METHODS
-
- isCompatibleWith: function (appVersion, platformVersion) {
- return true;
- },
-
- findUpdates: function (listener, reason, appVersion, platformVersion) {
- AddonManagerPrivate.callNoUpdateListeners(this, listener, reason,
- appVersion, platformVersion);
- },
-
- // END REQUIRED METHODS
-
- /**
- * The end-date of the experiment, required for the Addon Manager UI.
- */
-
- get endDate() {
- return this._endDate;
- },
-
-});
diff --git a/browser/experiments/Experiments.manifest b/browser/experiments/Experiments.manifest
deleted file mode 100644
index 4a6a05a60..000000000
--- a/browser/experiments/Experiments.manifest
+++ /dev/null
@@ -1,6 +0,0 @@
-component {f7800463-3b97-47f9-9341-b7617e6d8d49} ExperimentsService.js
-contract @mozilla.org/browser/experiments-service;1 {f7800463-3b97-47f9-9341-b7617e6d8d49}
-category update-timer ExperimentsService @mozilla.org/browser/experiments-service;1,getService,experiments-update-timer,experiments.manifest.fetchIntervalSeconds,86400
-category profile-after-change ExperimentsService @mozilla.org/browser/experiments-service;1
-
-
diff --git a/browser/experiments/ExperimentsService.js b/browser/experiments/ExperimentsService.js
deleted file mode 100644
index 53e811251..000000000
--- a/browser/experiments/ExperimentsService.js
+++ /dev/null
@@ -1,118 +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/. */
-
-"use strict";
-
-const {interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Preferences.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
- "resource:///modules/experiments/Experiments.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OS",
- "resource://gre/modules/osfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
- "resource://services-common/utils.js");
-
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_ACTIVE_EXPERIMENT = "experiments.activeExperiment"; // whether we have an active experiment
-const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
-const PREF_TELEMETRY_UNIFIED = "toolkit.telemetry.unified";
-const DELAY_INIT_MS = 30 * 1000;
-
-// Whether the FHR/Telemetry unification features are enabled.
-// Changing this pref requires a restart.
-const IS_UNIFIED_TELEMETRY = Preferences.get(PREF_TELEMETRY_UNIFIED, false);
-
-XPCOMUtils.defineLazyGetter(
- this, "gPrefs", () => {
- return new Preferences();
- });
-
-XPCOMUtils.defineLazyGetter(
- this, "gExperimentsEnabled", () => {
- // We can enable experiments if either unified Telemetry or FHR is on, and the user
- // has opted into Telemetry.
- return gPrefs.get(PREF_EXPERIMENTS_ENABLED, false) &&
- IS_UNIFIED_TELEMETRY && gPrefs.get(PREF_TELEMETRY_ENABLED, false);
- });
-
-XPCOMUtils.defineLazyGetter(
- this, "gActiveExperiment", () => {
- return gPrefs.get(PREF_ACTIVE_EXPERIMENT);
- });
-
-function ExperimentsService() {
- this._initialized = false;
- this._delayedInitTimer = null;
-}
-
-ExperimentsService.prototype = {
- classID: Components.ID("{f7800463-3b97-47f9-9341-b7617e6d8d49}"),
- QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback, Ci.nsIObserver]),
-
- notify: function (timer) {
- if (!gExperimentsEnabled) {
- return;
- }
- if (OS.Constants.Path.profileDir === undefined) {
- throw Error("Update timer fired before profile was initialized?");
- }
- let instance = Experiments.instance();
- if (instance.isReady) {
- instance.updateManifest();
- }
- },
-
- _delayedInit: function () {
- if (!this._initialized) {
- this._initialized = true;
- Experiments.instance(); // for side effects
- }
- },
-
- observe: function (subject, topic, data) {
- switch (topic) {
- case "profile-after-change":
- if (gExperimentsEnabled) {
- Services.obs.addObserver(this, "quit-application", false);
- Services.obs.addObserver(this, "sessionstore-state-finalized", false);
- Services.obs.addObserver(this, "EM-loaded", false);
-
- if (gActiveExperiment) {
- this._initialized = true;
- Experiments.instance(); // for side effects
- }
- }
- break;
- case "sessionstore-state-finalized":
- if (!this._initialized) {
- CommonUtils.namedTimer(this._delayedInit, DELAY_INIT_MS, this, "_delayedInitTimer");
- }
- break;
- case "EM-loaded":
- if (!this._initialized) {
- Experiments.instance(); // for side effects
- this._initialized = true;
-
- if (this._delayedInitTimer) {
- this._delayedInitTimer.clear();
- }
- }
- break;
- case "quit-application":
- Services.obs.removeObserver(this, "quit-application");
- Services.obs.removeObserver(this, "sessionstore-state-finalized");
- Services.obs.removeObserver(this, "EM-loaded");
- if (this._delayedInitTimer) {
- this._delayedInitTimer.clear();
- }
- break;
- }
- },
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ExperimentsService]);
diff --git a/browser/experiments/Makefile.in b/browser/experiments/Makefile.in
deleted file mode 100644
index 5558582a6..000000000
--- a/browser/experiments/Makefile.in
+++ /dev/null
@@ -1,16 +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 $(topsrcdir)/config/rules.mk
-
-# This is so hacky. Waiting on bug 988938.
-addondir = $(srcdir)/test/addons
-testdir = $(topobjdir)/_tests/xpcshell/browser/experiments/test/xpcshell
-
-misc:: $(call mkdir_deps,$(testdir))
- $(EXIT_ON_ERROR) \
- for dir in $(addondir)/*; do \
- base=`basename $$dir`; \
- (cd $$dir && zip -qr $(testdir)/$$base.xpi *); \
- done
diff --git a/browser/experiments/docs/index.rst b/browser/experiments/docs/index.rst
deleted file mode 100644
index 11e5d4faa..000000000
--- a/browser/experiments/docs/index.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-=====================
-Telemetry Experiments
-=====================
-
-Telemetry Experiments is a feature of Firefox that allows the installation
-of add-ons called experiments to a subset of the Firefox population for
-the purposes of experimenting with changes and collecting data on specific
-aspects of application usage.
-
-.. toctree::
- :maxdepth: 1
-
- manifest
diff --git a/browser/experiments/docs/manifest.rst b/browser/experiments/docs/manifest.rst
deleted file mode 100644
index d4fad5243..000000000
--- a/browser/experiments/docs/manifest.rst
+++ /dev/null
@@ -1,429 +0,0 @@
-.. _experiments_manifests:
-
-=====================
-Experiments Manifests
-=====================
-
-*Experiments Manifests* are documents that describe the set of active
-experiments a client may run.
-
-*Experiments Manifests* are fetched periodically by clients. When
-fetched, clients look at the experiments within the manifest and
-determine which experiments are applicable. If an experiment is
-applicable, the client may download and start the experiment.
-
-Manifest Format
-===============
-
-Manifests are JSON documents where the main element is an object.
-
-The *schema* of the object is versioned and defined by the presence
-of a top-level ``version`` property, whose integer value is the
-schema version used by that manifest. Each version is documented
-in the sections below.
-
-Version 1
----------
-
-Version 1 is the original manifest format.
-
-The following properties may exist in the root object:
-
-experiments
- An array of objects describing candidate experiments. The format of
- these objects is documented below.
-
- An array is used to create an explicit priority of experiments.
- Experiments listed at the beginning of the array take priority over
- experiments that follow.
-
-Experiments Objects
-^^^^^^^^^^^^^^^^^^^
-
-Each object in the ``experiments`` array may contain the following
-properties:
-
-id
- (required) String identifier of this experiment. The identifier should
- be treated as opaque by clients. It is used to uniquely identify an
- experiment for all of time.
-
-xpiURL
- (required) String URL of the XPI that implements this experiment.
-
- If the experiment is activated, the client will download and install this
- XPI.
-
-xpiHash
- (required) String hash of the XPI that implements this experiment.
-
- The value is composed of a hash identifier followed by a colon
- followed by the hash value. e.g.
- `sha1:f677428b9172e22e9911039aef03f3736e7f78a7`. `sha1` and `sha256`
- are the two supported hashing mechanisms. The hash value is the hex
- encoding of the binary hash.
-
- When the client downloads the XPI for the experiment, it should compare
- the hash of that XPI against this value. If the hashes don't match,
- the client should not install the XPI.
-
- Clients may also use this hash as a means of determining when an
- experiment's XPI has changed and should be refreshed.
-
-startTime
- Integer seconds since UNIX epoch that this experiment should
- start. Clients should not start an experiment if *now()* is less than
- this value.
-
-maxStartTime
- (optional) Integer seconds since UNIX epoch after which this experiment
- should no longer start.
-
- Some experiments may wish to impose hard deadlines after which no new
- clients should activate the experiment. This property may be used to
- facilitate that.
-
-endTime
- Integer seconds since UNIX epoch after which this experiment
- should no longer run. Clients should cease an experiment when the current
- time is beyond this value.
-
-maxActiveSeconds
- Integer seconds defining the max wall time this experiment should be
- active for.
-
- The client should deactivate the experiment this many seconds after
- initial activation.
-
- This value only involves wall time, not browser activity or session time.
-
-appName
- Array of application names this experiment should run on.
-
- An application name comes from ``nsIXULAppInfo.name``. It is a value
- like ``Firefox``, ``Fennec``, or `B2G`.
-
- The client should compare its application name against the members of
- this array. If a match is found, the experiment is applicable.
-
-minVersion
- (optional) String version number of the minimum application version this
- experiment should run on.
-
- A version number is something like ``27.0.0`` or ``28``.
-
- The client should compare its version number to this value. If the client's
- version is greater or equal to this version (using a version-aware comparison
- function), the experiment is applicable.
-
- If this is not specified, there is no lower bound to versions this
- experiment should run on.
-
-maxVersion
- (optional) String version number of the maximum application version this
- experiment should run on.
-
- This is similar to ``minVersion`` except it sets the upper bound for
- application versions.
-
- If the client's version is less than or equal to this version, the
- experiment is applicable.
-
- If this is not specified, there is no upper bound to versions this
- experiment should run on.
-
-version
- (optional) Array of application versions this experiment should run on.
-
- This is similar to ``minVersion`` and ``maxVersion`` except only a
- whitelisted set of specific versions are allowed.
-
- The client should compare its version to members of this array. If a match
- is found, the experiment is applicable.
-
-minBuildID
- (optional) String minimum Build ID this experiment should run on.
-
- Build IDs are values like ``201402261424``.
-
- The client should perform a string comparison of its Build ID against this
- value. If its value is greater than or equal to this value, the experiment
- is applicable.
-
-maxBuildID
- (optional) String maximum Build ID this experiment should run on.
-
- This is similar to ``minBuildID`` except it sets the upper bound
- for Build IDs.
-
- The client should perform a string comparison of its Build ID against
- this value. If its value is less than or equal to this value, the
- experiment is applicable.
-
-buildIDs
- (optional) Array of Build IDs this experiment should run on.
-
- This is similar to ``minBuildID`` and ``maxBuildID`` except only a
- whitelisted set of Build IDs are considered.
-
- The client should compare its Build ID to members of this array. If a
- match is found, the experiment is applicable.
-
-os
- (optional) Array of operating system identifiers this experiment should
- run on.
-
- Values for this array come from ``nsIXULRuntime.OS``.
-
- The client will compare its operating system identifier to members
- of this array. If a match is found, the experiment is applicable to the
- client.
-
-channel
- (optional) Array of release channel identifiers this experiment should run
- on.
-
- The client will compare its channel to members of this array. If a match
- is found, the experiment is applicable.
-
- If this property is not defined, the client should assume the experiment
- is to run on all channels.
-
-locale
- (optional) Array of locale identifiers this experiment should run on.
-
- A locale identifier is a string like ``en-US`` or ``zh-CN`` and is
- obtained by looking at
- ``nsIXULChromeRegistry.getSelectedLocale("global")``.
-
- The client should compare its locale identifier to members of this array.
- If a match is found, the experiment is applicable.
-
- If this property is not defined, the client should assume the experiment
- is to run on all locales.
-
-sample
- (optional) Decimal number indicating the sampling rate for this experiment.
-
- This will contain a value between ``0.0`` and ``1.0``. The client should
- generate a random decimal between ``0.0`` and ``1.0``. If the randomly
- generated number is less than or equal to the value of this field, the
- experiment is applicable.
-
-disabled
- (optional) Boolean value indicating whether an experiment is disabled.
-
- Normally, experiments are deactivated after a certain time has passed or
- after the experiment itself determines it no longer needs to run (perhaps
- it collected sufficient data already).
-
- This property serves as a backup mechanism to remotely disable an
- experiment before it was scheduled to be disabled. It can be used to
- kill experiments that are found to be doing wrong or bad things or that
- aren't useful.
-
- If this property is not defined or is false, the client should assume
- the experiment is active and a candidate for activation.
-
-frozen
- (optional) Boolean value indicating this experiment is frozen and no
- longer accepting new enrollments.
-
- If a client sees a true value in this field, it should not attempt to
- activate an experiment.
-
-jsfilter
- (optional) JavaScript code that will be evaluated to determine experiment
- applicability.
-
- This property contains the string representation of JavaScript code that
- will be evaluated in a sandboxed environment using JavaScript's
- ``eval()``.
-
- The string is expected to contain the definition of a JavaScript function
- ``filter(context)``. This function receives as its argument an object
- holding application state. See the section below for the definition of
- this object.
-
- The purpose of this property is to allow experiments to define complex
- rules and logic for evaluating experiment applicability in a manner
- that is privacy conscious and doesn't require the transmission of
- excessive data.
-
- The return value of this filter indicates whether the experiment is
- applicable. Functions should return true if the experiment is
- applicable.
-
- If an experiment is not applicable, they should throw an Error whose
- message contains the reason the experiment is not applicable. This
- message may be logged and sent to remote servers, so it should not
- contain private or otherwise sensitive data that wouldn't normally
- be submitted.
-
- If a falsey (or undefined) value is returned, the client should
- assume the experiment is not applicable.
-
- If this property is not defined, the client does not consider a custom
- JavaScript filter function when determining whether an experiment is
- applicable.
-
-JavaScript Filter Context Objects
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The object passed to a ``jsfilter`` ``filter()`` function contains the
-following properties:
-
-healthReportSubmissionEnabled
- This property contains a boolean indicating whether Firefox Health
- Report has its data submission flag enabled (whether Firefox Health
- Report is sending data to remote servers).
-
-healthReportPayload
- This property contains the current Firefox Health Report payload.
-
- The payload format is documented at :ref:`healthreport_dataformat`.
-
-telemetryPayload
- This property contains the current Telemetry payload.
-
-The evaluation sandbox for the JavaScript filters may be destroyed
-immediately after ``filter()`` returns. This function should not assume
-async code will finish.
-
-Experiment Applicability and Client Behavior
-============================================
-
-The point of an experiment manifest is to define which experiments are
-available and where and how to run them. This section explains those
-rules in more detail.
-
-Many of the properties in *Experiment Objects* are related to determining
-whether an experiment should run on a given client. This evaluation is
-performed client side.
-
-1. Multiple conditions in an experiment
----------------------------------------
-
-If multiple conditions are defined for an experiment, the client should
-combine each condition with a logical *AND*: all conditions must be
-satisfied for an experiment to run. If one condition fails, the experiment
-is not applicable.
-
-2. Active experiment disappears from manifest
----------------------------------------------
-
-If a specific experiment disappears from the manifest, the client should
-continue conducting an already-active experiment. Furthermore, the
-client should remember what the expiration events were for an experiment
-and honor them.
-
-The rationale here is that we want to prevent an accidental deletion
-or temporary failure on the server to inadvertantly deactivate
-supposed-to-be-active experiments. We also don't want premature deletion
-of an experiment from the manifest to result in indefinite activation
-periods.
-
-3. Inactive experiment disappears from manifest
------------------------------------------------
-
-If an inactive but scheduled-to-be-active experiment disappears from the
-manifest, the client should not activate the experiment.
-
-If that experiment reappears in the manifest, the client should not
-treat that experiment any differently than any other new experiment. Put
-another way, the fact an inactive experiment disappears and then
-reappears should not be significant.
-
-The rationale here is that server operators should have complete
-control of an inactive experiment up to it's go-live date.
-
-4. Re-evaluating applicability on manifest refresh
---------------------------------------------------
-
-When an experiment manifest is refreshed or updated, the client should
-re-evaluate the applicability of each experiment therein.
-
-The rationale here is that the server may change the parameters of an
-experiment and want clients to pick those up.
-
-5. Activating a previously non-applicable experiment
-----------------------------------------------------
-
-If the conditions of an experiment change or the state of the client
-changes to allow an experiment to transition from previously
-non-applicable to applicable, the experiment should be activated.
-
-For example, if a client is running version 28 and the experiment
-initially requires version 29 or above, the client will not mark the
-experiment as applicable. But if the client upgrades to version 29 or if
-the manifest is updated to require 28 or above, the experiment will
-become applicable.
-
-6. Deactivating a previously active experiment
-----------------------------------------------
-
-If the conditions of an experiment change or the state of the client
-changes and an active experiment is no longer applicable, that
-experiment should be deactivated.
-
-7. Calculation of sampling-based applicability
-----------------------------------------------
-
-For calculating sampling-based applicability, the client will associate
-a random value between ``0.0`` and ``1.0`` for each observed experiment
-ID. This random value will be generated the first time sampling
-applicability is evaluated. This random value will be persisted and used
-in future applicability evaluations for this experiment.
-
-By saving and re-using the value, the client is able to reliably and
-consistently evaluate applicability, even if the sampling threshold
-in the manifest changes.
-
-Clients should retain the randomly-generated sampling value for
-experiments that no longer appear in a manifest for a period of at least
-30 days. The rationale is that if an experiment disappears and reappears
-from a manifest, the client will not have multiple opportunities to
-generate a random value that satisfies the sampling criteria.
-
-8. Incompatible version numbers
--------------------------------
-
-If a client receives a manifest with a version number that it doesn't
-recognize, it should ignore the manifest.
-
-9. Usage of old manifests
--------------------------
-
-If a client experiences an error fetching a manifest (server not
-available) or if the manifest is corrupt, not readable, or compatible,
-the client may use a previously-fetched (cached) manifest.
-
-10. Updating XPIs
------------------
-
-If the URL or hash of an active experiment's XPI changes, the client
-should fetch the new XPI, uninstall the old XPI, and install the new
-XPI.
-
-Examples
-========
-
-Here is an example manifest::
-
- {
- "version": 1,
- "experiments": [
- {
- "id": "da9d7f4f-f3f9-4f81-bacd-6f0626ffa360",
- "xpiURL": "https://experiments.mozilla.org/foo.xpi",
- "xpiHash": "sha1:cb1eb32b89d86d78b7326f416cf404548c5e0099",
- "startTime": 1393000000,
- "endTime": 1394000000,
- "appName": ["Firefox", "Fennec"],
- "minVersion": "28",
- "maxVersion": "30",
- "os": ["windows", "linux", "osx"],
- "jsfilter": "function filter(context) { return context.healthReportEnabled; }"
- }
- ]
- }
diff --git a/browser/experiments/moz.build b/browser/experiments/moz.build
deleted file mode 100644
index a11e4b725..000000000
--- a/browser/experiments/moz.build
+++ /dev/null
@@ -1,18 +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/.
-
-HAS_MISC_RULE = True
-
-EXTRA_COMPONENTS += [
- 'Experiments.manifest',
- 'ExperimentsService.js',
-]
-
-EXTRA_JS_MODULES.experiments += [
- 'Experiments.jsm',
-]
-
-XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
-
-SPHINX_TREES['experiments'] = 'docs'
diff --git a/browser/experiments/test/addons/experiment-1/install.rdf b/browser/experiments/test/addons/experiment-1/install.rdf
deleted file mode 100644
index f9d70054a..000000000
--- a/browser/experiments/test/addons/experiment-1/install.rdf
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>test-experiment-1@tests.mozilla.org</em:id>
- <em:version>1</em:version>
- <em:type>128</em:type>
-
- <!-- Front End MetaData -->
- <em:name>Test experiment 1</em:name>
- <em:description>Yet another experiment that experiments experimentally.</em:description>
-
- </Description>
-</RDF>
diff --git a/browser/experiments/test/addons/experiment-1a/install.rdf b/browser/experiments/test/addons/experiment-1a/install.rdf
deleted file mode 100644
index 7806b11b1..000000000
--- a/browser/experiments/test/addons/experiment-1a/install.rdf
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>test-experiment-1@tests.mozilla.org</em:id>
- <em:version>1.1</em:version>
- <em:type>128</em:type>
-
- <!-- Front End MetaData -->
- <em:name>Test experiment 1.1</em:name>
- <em:description>And yet another experiment that experiments experimentally.</em:description>
-
- </Description>
-</RDF>
diff --git a/browser/experiments/test/addons/experiment-2/install.rdf b/browser/experiments/test/addons/experiment-2/install.rdf
deleted file mode 100644
index 69122c0ef..000000000
--- a/browser/experiments/test/addons/experiment-2/install.rdf
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>test-experiment-2@tests.mozilla.org</em:id>
- <em:version>1</em:version>
- <em:type>128</em:type>
-
- <!-- Front End MetaData -->
- <em:name>Test experiment 2</em:name>
- <em:description>And yet another experiment that experiments experimentally.</em:description>
-
- </Description>
-</RDF>
diff --git a/browser/experiments/test/addons/experiment-racybranch/bootstrap.js b/browser/experiments/test/addons/experiment-racybranch/bootstrap.js
deleted file mode 100644
index e8278f50f..000000000
--- a/browser/experiments/test/addons/experiment-racybranch/bootstrap.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/* exported startup, shutdown, install, uninstall */
-
-var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource:///modules/experiments/Experiments.jsm");
-
-var gStarted = false;
-
-function startup(data, reasonCode) {
- if (gStarted) {
- return;
- }
- gStarted = true;
-
- // delay realstartup to trigger the race condition
- Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager)
- .mainThread.dispatch(realstartup, 0);
-}
-
-function realstartup() {
- let experiments = Experiments.instance();
- let experiment = experiments._getActiveExperiment();
- if (experiment.branch) {
- Cu.reportError("Found pre-existing branch: " + experiment.branch);
- return;
- }
-
- let branch = "racy-set";
- experiments.setExperimentBranch(experiment.id, branch)
- .then(null, Cu.reportError);
-}
-
-function shutdown() { }
-function install() { }
-function uninstall() { }
diff --git a/browser/experiments/test/addons/experiment-racybranch/install.rdf b/browser/experiments/test/addons/experiment-racybranch/install.rdf
deleted file mode 100644
index cebaede56..000000000
--- a/browser/experiments/test/addons/experiment-racybranch/install.rdf
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>test-experiment-racybranch@tests.mozilla.org</em:id>
- <em:version>1</em:version>
- <em:type>128</em:type>
-
- <!-- Front End MetaData -->
- <em:name>Test experiment racybranch</em:name>
- <em:description>An experiment that sets the experiment branch in a potentially racy way.</em:description>
-
- </Description>
-</RDF>
diff --git a/browser/experiments/test/xpcshell/.eslintrc.js b/browser/experiments/test/xpcshell/.eslintrc.js
deleted file mode 100644
index 1f540a05b..000000000
--- a/browser/experiments/test/xpcshell/.eslintrc.js
+++ /dev/null
@@ -1,15 +0,0 @@
-"use strict";
-
-module.exports = {
- "extends": [
- "../../../../testing/xpcshell/xpcshell.eslintrc.js"
- ],
-
- "rules": {
- "no-unused-vars": ["error", {
- "vars": "all",
- "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$",
- "args": "none"
- }]
- }
-};
diff --git a/browser/experiments/test/xpcshell/experiments_1.manifest b/browser/experiments/test/xpcshell/experiments_1.manifest
deleted file mode 100644
index 0401ea328..000000000
--- a/browser/experiments/test/xpcshell/experiments_1.manifest
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "version": 1,
- "experiments": [
- {
- "id": "test-experiment-1@tests.mozilla.org",
- "xpiURL": "https://experiments.mozilla.org/foo.xpi",
- "xpiHash": "sha1:cb1eb32b89d86d78b7326f416cf404548c5e0099",
- "startTime": 1393000000,
- "endTime": 1394000000,
- "appName": ["Firefox", "Fennec"],
- "minVersion": "28",
- "maxVersion": "30",
- "maxActiveSeconds": 60,
- "os": ["windows", "linux", "osx"],
- "channel": ["daily", "weekly", "nightly"],
- "jsfilter": "function filter(context) { return true; }"
- }
- ]
-}
diff --git a/browser/experiments/test/xpcshell/head.js b/browser/experiments/test/xpcshell/head.js
deleted file mode 100644
index ae356ea2d..000000000
--- a/browser/experiments/test/xpcshell/head.js
+++ /dev/null
@@ -1,199 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/* exported PREF_EXPERIMENTS_ENABLED, PREF_LOGGING_LEVEL, PREF_LOGGING_DUMP
- PREF_MANIFEST_URI, PREF_FETCHINTERVAL, EXPERIMENT1_ID,
- EXPERIMENT1_NAME, EXPERIMENT1_XPI_SHA1, EXPERIMENT1A_NAME,
- EXPERIMENT1A_XPI_SHA1, EXPERIMENT2_ID, EXPERIMENT2_XPI_SHA1,
- EXPERIMENT3_ID, EXPERIMENT4_ID, FAKE_EXPERIMENTS_1,
- FAKE_EXPERIMENTS_2, gAppInfo, removeCacheFile, defineNow,
- futureDate, dateToSeconds, loadAddonManager, promiseRestartManager,
- startAddonManagerOnly, getExperimentAddons, replaceExperiments */
-
-var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource://testing-common/AddonManagerTesting.jsm");
-Cu.import("resource://testing-common/AddonTestUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
- "resource://gre/modules/AddonManager.jsm");
-
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_LOGGING_LEVEL = "experiments.logging.level";
-const PREF_LOGGING_DUMP = "experiments.logging.dump";
-const PREF_MANIFEST_URI = "experiments.manifest.uri";
-const PREF_FETCHINTERVAL = "experiments.manifest.fetchIntervalSeconds";
-const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
-
-function getExperimentPath(base) {
- let p = do_get_cwd();
- p.append(base);
- return p.path;
-}
-
-function sha1File(path) {
- let f = Cc["@mozilla.org/file/local;1"]
- .createInstance(Ci.nsILocalFile);
- f.initWithPath(path);
- let hasher = Cc["@mozilla.org/security/hash;1"]
- .createInstance(Ci.nsICryptoHash);
- hasher.init(hasher.SHA1);
-
- let is = Cc["@mozilla.org/network/file-input-stream;1"]
- .createInstance(Ci.nsIFileInputStream);
- is.init(f, -1, 0, 0);
- hasher.updateFromStream(is, Math.pow(2, 32) - 1);
- is.close();
- let bytes = hasher.finish(false);
-
- let rv = "";
- for (let i = 0; i < bytes.length; i++) {
- rv += ("0" + bytes.charCodeAt(i).toString(16)).substr(-2);
- }
- return rv;
-}
-
-const EXPERIMENT1_ID = "test-experiment-1@tests.mozilla.org";
-const EXPERIMENT1_XPI_NAME = "experiment-1.xpi";
-const EXPERIMENT1_NAME = "Test experiment 1";
-const EXPERIMENT1_PATH = getExperimentPath(EXPERIMENT1_XPI_NAME);
-const EXPERIMENT1_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT1_PATH);
-
-
-const EXPERIMENT1A_XPI_NAME = "experiment-1a.xpi";
-const EXPERIMENT1A_NAME = "Test experiment 1.1";
-const EXPERIMENT1A_PATH = getExperimentPath(EXPERIMENT1A_XPI_NAME);
-const EXPERIMENT1A_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT1A_PATH);
-
-const EXPERIMENT2_ID = "test-experiment-2@tests.mozilla.org"
-const EXPERIMENT2_XPI_NAME = "experiment-2.xpi";
-const EXPERIMENT2_PATH = getExperimentPath(EXPERIMENT2_XPI_NAME);
-const EXPERIMENT2_XPI_SHA1 = "sha1:" + sha1File(EXPERIMENT2_PATH);
-
-const EXPERIMENT3_ID = "test-experiment-3@tests.mozilla.org";
-const EXPERIMENT4_ID = "test-experiment-4@tests.mozilla.org";
-
-const FAKE_EXPERIMENTS_1 = [
- {
- id: "id1",
- name: "experiment1",
- description: "experiment 1",
- active: true,
- detailUrl: "https://dummy/experiment1",
- branch: "foo",
- },
-];
-
-const FAKE_EXPERIMENTS_2 = [
- {
- id: "id2",
- name: "experiment2",
- description: "experiment 2",
- active: false,
- endDate: new Date(2014, 2, 11, 2, 4, 35, 42).getTime(),
- detailUrl: "https://dummy/experiment2",
- branch: null,
- },
- {
- id: "id1",
- name: "experiment1",
- description: "experiment 1",
- active: false,
- endDate: new Date(2014, 2, 10, 0, 0, 0, 0).getTime(),
- detailURL: "https://dummy/experiment1",
- branch: null,
- },
-];
-
-var gAppInfo = null;
-
-function removeCacheFile() {
- let path = OS.Path.join(OS.Constants.Path.profileDir, "experiments.json");
- return OS.File.remove(path);
-}
-
-function patchPolicy(policy, data) {
- for (let key of Object.keys(data)) {
- Object.defineProperty(policy, key, {
- value: data[key],
- writable: true,
- });
- }
-}
-
-function defineNow(policy, time) {
- patchPolicy(policy, { now: () => new Date(time) });
-}
-
-function futureDate(date, offset) {
- return new Date(date.getTime() + offset);
-}
-
-function dateToSeconds(date) {
- return date.getTime() / 1000;
-}
-
-var gGlobalScope = this;
-function loadAddonManager() {
- AddonTestUtils.init(gGlobalScope);
- AddonTestUtils.overrideCertDB();
- createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
- return AddonTestUtils.promiseStartupManager();
-}
-
-const {
- promiseRestartManager,
-} = AddonTestUtils;
-
-// Starts the addon manager without creating app info. We can't directly use
-// |loadAddonManager| defined above in test_conditions.js as it would make the test fail.
-function startAddonManagerOnly() {
- let addonManager = Cc["@mozilla.org/addons/integration;1"]
- .getService(Ci.nsIObserver)
- .QueryInterface(Ci.nsITimerCallback);
- addonManager.observe(null, "addons-startup", null);
-}
-
-function getExperimentAddons(previous=false) {
- let deferred = Promise.defer();
-
- AddonManager.getAddonsByTypes(["experiment"], (addons) => {
- if (previous) {
- deferred.resolve(addons);
- } else {
- deferred.resolve(addons.filter(a => !a.appDisabled));
- }
- });
-
- return deferred.promise;
-}
-
-function createAppInfo(ID="xpcshell@tests.mozilla.org", name="XPCShell",
- version="1.0", platformVersion="1.0") {
- AddonTestUtils.createAppInfo(ID, name, version, platformVersion);
- gAppInfo = AddonTestUtils.appInfo;
-}
-
-/**
- * Replace the experiments on an Experiments with a new list.
- *
- * This monkeypatches getExperiments(). It doesn't monkeypatch the internal
- * experiments list. So its utility is not as great as it could be.
- */
-function replaceExperiments(experiment, list) {
- Object.defineProperty(experiment, "getExperiments", {
- writable: true,
- value: () => {
- return Promise.resolve(list);
- },
- });
-}
-
-// Experiments require Telemetry to be enabled, and that's not true for debug
-// builds. Let's just enable it here instead of going through each test.
-Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
diff --git a/browser/experiments/test/xpcshell/test_activate.js b/browser/experiments/test/xpcshell/test_activate.js
deleted file mode 100644
index 60deafbfb..000000000
--- a/browser/experiments/test/xpcshell/test_activate.js
+++ /dev/null
@@ -1,151 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource:///modules/experiments/Experiments.jsm");
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
-
-var gHttpServer = null;
-var gHttpRoot = null;
-var gPolicy = null;
-
-function ManifestEntry(data) {
- this.id = data.id || EXPERIMENT1_ID;
- this.xpiURL = data.xpiURL || gHttpRoot + EXPERIMENT1_XPI_NAME;
- this.xpiHash = data.xpiHash || EXPERIMENT1_XPI_SHA1;
- this.appName = data.appName || ["XPCShell"];
- this.channel = data.appName || ["nightly"];
- this.startTime = data.startTime || new Date(2010, 0, 1, 12).getTime() / 1000;
- this.endTime = data.endTime || new Date(9001, 0, 1, 12).getTime() / 1000;
- this.maxActiveSeconds = data.maxActiveSeconds || 5 * SEC_IN_ONE_DAY;
-}
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function* test_setup() {
- loadAddonManager();
- gPolicy = new Experiments.Policy();
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let port = gHttpServer.identity.primaryPort;
- gHttpRoot = "http://localhost:" + port + "/";
- gHttpServer.registerDirectory("/", do_get_cwd());
- do_register_cleanup(() => gHttpServer.stop(() => {}));
-
- patchPolicy(gPolicy, {
- updatechannel: () => "nightly",
- });
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
-});
-
-function isApplicable(experiment) {
- let deferred = Promise.defer();
- experiment.isApplicable().then(
- result => deferred.resolve({ applicable: true, reason: null }),
- reason => deferred.resolve({ applicable: false, reason: reason })
- );
-
- return deferred.promise;
-}
-
-add_task(function* test_startStop() {
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 30 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 60 * MS_IN_ONE_DAY);
- let manifestData = new ManifestEntry({
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- });
- let experiment = new Experiments.ExperimentEntry(gPolicy);
- experiment.initFromManifestData(manifestData);
-
- // We need to associate it with the singleton so the onInstallStarted
- // Addon Manager listener will know about it.
- Experiments.instance()._experiments = new Map();
- Experiments.instance()._experiments.set(experiment.id, experiment);
-
- let result;
-
- defineNow(gPolicy, baseDate);
- result = yield isApplicable(experiment);
- Assert.equal(result.applicable, false, "Experiment should not be applicable.");
- Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
-
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
-
- defineNow(gPolicy, futureDate(startDate, 5 * MS_IN_ONE_DAY));
- result = yield isApplicable(experiment);
- Assert.equal(result.applicable, true, "Experiment should now be applicable.");
- Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
-
- let changes = yield experiment.start();
- Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
- addons = yield getExperimentAddons();
- Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
- Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
- Assert.equal(addons[0].id, experiment._addonId, "The add-on is the one we expect.");
- Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
- Assert.ok(addons[0].isActive, "The add-on is active.");
-
- changes = yield experiment.stop();
- Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on was uninstalled.");
- addons = yield getExperimentAddons();
- Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
- Assert.equal(addons.length, 0, "Experiment should be uninstalled from the Addon Manager.");
-
- changes = yield experiment.start();
- Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
- addons = yield getExperimentAddons();
- Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
- Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
- Assert.equal(addons[0].id, experiment._addonId, "The add-on is the one we expect.");
- Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
- Assert.ok(addons[0].isActive, "The add-on is active.");
-
- result = yield experiment.shouldStop();
- Assert.equal(result.shouldStop, false, "shouldStop should be false.");
- Assert.equal(experiment.enabled, true, "Experiment should be enabled.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "Experiment still in add-ons manager.");
- Assert.ok(addons[0].isActive, "The add-on is still active.");
-
- defineNow(gPolicy, futureDate(endDate, MS_IN_ONE_DAY));
- result = yield experiment.shouldStop();
- Assert.equal(result.shouldStop, true, "shouldStop should now be true.");
- changes = yield experiment.stop();
- Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on should be uninstalled.");
- Assert.equal(experiment.enabled, false, "Experiment should be disabled.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Experiment add-on is uninstalled.");
-
- // Ensure hash validation works.
- // We set an incorrect hash and expect the install to fail.
- experiment._manifestData.xpiHash = "sha1:41014dcc66b4dcedcd973491a1530a32f0517d8a";
- let errored = false;
- try {
- yield experiment.start();
- } catch (ex) {
- errored = true;
- }
- Assert.ok(experiment._failedStart, "Experiment failed to start.");
- Assert.ok(errored, "start() threw an exception.");
-
- // Make sure "ignore hashes" mode works.
- gPolicy.ignoreHashes = true;
- changes = yield experiment.start();
- Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL);
- yield experiment.stop();
- gPolicy.ignoreHashes = false;
-});
diff --git a/browser/experiments/test/xpcshell/test_api.js b/browser/experiments/test/xpcshell/test_api.js
deleted file mode 100644
index 9f0112570..000000000
--- a/browser/experiments/test/xpcshell/test_api.js
+++ /dev/null
@@ -1,1647 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource://testing-common/AddonManagerTesting.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
- "resource:///modules/experiments/Experiments.jsm");
-
-const MANIFEST_HANDLER = "manifests/handler";
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
-
-var gHttpServer = null;
-var gHttpRoot = null;
-var gDataRoot = null;
-var gPolicy = null;
-var gManifestObject = null;
-var gManifestHandlerURI = null;
-var gTimerScheduleOffset = -1;
-
-function uninstallExperimentAddons() {
- return Task.spawn(function* () {
- let addons = yield getExperimentAddons();
- for (let a of addons) {
- yield AddonManagerTesting.uninstallAddonByID(a.id);
- }
- });
-}
-
-function testCleanup(experimentsInstance) {
- return Task.spawn(function* () {
- yield promiseRestartManager();
- yield uninstallExperimentAddons();
- yield removeCacheFile();
- });
-}
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function* test_setup() {
- loadAddonManager();
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let port = gHttpServer.identity.primaryPort;
- gHttpRoot = "http://localhost:" + port + "/";
- gDataRoot = gHttpRoot + "data/";
- gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
- gHttpServer.registerDirectory("/data/", do_get_cwd());
- gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
- response.setStatusLine(null, 200, "OK");
- response.write(JSON.stringify(gManifestObject));
- response.processAsync();
- response.finish();
- });
- do_register_cleanup(() => gHttpServer.stop(() => {}));
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
- Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
- Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
-
- gPolicy = new Experiments.Policy();
- patchPolicy(gPolicy, {
- updatechannel: () => "nightly",
- oneshotTimer: (callback, timeout, thisObj, name) => gTimerScheduleOffset = timeout,
- });
-});
-
-add_task(function* test_contract() {
- Cc["@mozilla.org/browser/experiments-service;1"].getService();
-});
-
-// Test basic starting and stopping of experiments.
-
-add_task(function* test_getExperiments() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
- let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let startDate2 = futureDate(baseDate, 150 * MS_IN_ONE_DAY);
- let endDate2 = futureDate(baseDate, 200 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT2_ID,
- xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- startTime: dateToSeconds(startDate2),
- endTime: dateToSeconds(endDate2),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate1),
- endTime: dateToSeconds(endDate1),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- // Data to compare the result of Experiments.getExperiments() against.
-
- let experimentListData = [
- {
- id: EXPERIMENT2_ID,
- name: "Test experiment 2",
- description: "And yet another experiment that experiments experimentally.",
- },
- {
- id: EXPERIMENT1_ID,
- name: EXPERIMENT1_NAME,
- description: "Yet another experiment that experiments experimentally.",
- },
- ];
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
- // Use updateManifest() to provide for coverage of that path.
-
- let now = baseDate;
- gTimerScheduleOffset = -1;
- defineNow(gPolicy, now);
-
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- Assert.equal(experiments.getActiveExperimentID(), null,
- "getActiveExperimentID should return null");
-
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are installed.");
-
- try {
- yield experiments.getExperimentBranch();
- Assert.ok(false, "getExperimentBranch should fail with no experiment");
- }
- catch (e) {
- Assert.ok(true, "getExperimentBranch correctly threw");
- }
-
- // Trigger update, clock set for experiment 1 to start.
-
- now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
- gTimerScheduleOffset = -1;
- defineNow(gPolicy, now);
-
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
- "getActiveExperimentID should return the active experiment1");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "An experiment add-on was installed.");
-
- experimentListData[1].active = true;
- experimentListData[1].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
- for (let k of Object.keys(experimentListData[1])) {
- Assert.equal(experimentListData[1][k], list[0][k],
- "Property " + k + " should match reference data.");
- }
-
- let b = yield experiments.getExperimentBranch();
- Assert.strictEqual(b, null, "getExperimentBranch should return null by default");
-
- b = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
- Assert.strictEqual(b, null, "getExperimentsBranch should return null (with id)");
-
- yield experiments.setExperimentBranch(EXPERIMENT1_ID, "foo");
- b = yield experiments.getExperimentBranch();
- Assert.strictEqual(b, "foo", "getExperimentsBranch should return the set value");
-
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- Assert.equal(gTimerScheduleOffset, 10 * MS_IN_ONE_DAY,
- "Experiment re-evaluation should have been scheduled correctly.");
-
- // Trigger update, clock set for experiment 1 to stop.
-
- now = futureDate(endDate1, 1000);
- gTimerScheduleOffset = -1;
- defineNow(gPolicy, now);
-
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- Assert.equal(experiments.getActiveExperimentID(), null,
- "getActiveExperimentID should return null again");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "The experiment add-on should be uninstalled.");
-
- experimentListData[1].active = false;
- experimentListData[1].endDate = now.getTime();
- for (let k of Object.keys(experimentListData[1])) {
- Assert.equal(experimentListData[1][k], list[0][k],
- "Property " + k + " should match reference data.");
- }
-
- Assert.equal(gTimerScheduleOffset, startDate2 - now,
- "Experiment re-evaluation should have been scheduled correctly.");
-
- // Trigger update, clock set for experiment 2 to start.
- // Use notify() to provide for coverage of that path.
-
- now = startDate2;
- gTimerScheduleOffset = -1;
- defineNow(gPolicy, now);
-
- yield experiments.notify();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT2_ID,
- "getActiveExperimentID should return the active experiment2");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "An experiment add-on is installed.");
-
- experimentListData[0].active = true;
- experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
- for (let i=0; i<experimentListData.length; ++i) {
- let entry = experimentListData[i];
- for (let k of Object.keys(entry)) {
- Assert.equal(entry[k], list[i][k],
- "Entry " + i + " - Property '" + k + "' should match reference data.");
- }
- }
-
- Assert.equal(gTimerScheduleOffset, 10 * MS_IN_ONE_DAY,
- "Experiment re-evaluation should have been scheduled correctly.");
-
- // Trigger update, clock set for experiment 2 to stop.
-
- now = futureDate(startDate2, 10 * MS_IN_ONE_DAY + 1000);
- gTimerScheduleOffset = -1;
- defineNow(gPolicy, now);
- yield experiments.notify();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- Assert.equal(experiments.getActiveExperimentID(), null,
- "getActiveExperimentID should return null again2");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "No experiments add-ons are installed.");
-
- experimentListData[0].active = false;
- experimentListData[0].endDate = now.getTime();
- for (let i=0; i<experimentListData.length; ++i) {
- let entry = experimentListData[i];
- for (let k of Object.keys(entry)) {
- Assert.equal(entry[k], list[i][k],
- "Entry " + i + " - Property '" + k + "' should match reference data.");
- }
- }
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-add_task(function* test_getActiveExperimentID() {
- // Check that getActiveExperimentID returns the correct result even
- // after .uninit()
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
- let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate1),
- endTime: dateToSeconds(endDate1),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
- gTimerScheduleOffset = -1;
- defineNow(gPolicy, now);
-
- let experiments = new Experiments.Experiments(gPolicy);
- yield experiments.updateManifest();
-
- Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
- "getActiveExperimentID should return the active experiment1");
-
- yield promiseRestartManager();
- Assert.equal(experiments.getActiveExperimentID(), EXPERIMENT1_ID,
- "getActiveExperimentID should return the active experiment1 after uninit()");
-
- yield testCleanup(experiments);
-});
-
-// Test that we handle the experiments addon already being
-// installed properly.
-// We should just pave over them.
-
-add_task(function* test_addonAlreadyInstalled() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Trigger update, clock set for the experiment to start.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active.");
-
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "1 add-on is installed.");
-
- // Install conflicting addon.
-
- yield AddonManagerTesting.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "1 add-on is installed.");
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should still have 1 entry.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-add_task(function* test_lastActiveToday() {
- let experiments = new Experiments.Experiments(gPolicy);
-
- replaceExperiments(experiments, FAKE_EXPERIMENTS_1);
-
- let e = yield experiments.getExperiments();
- Assert.equal(e.length, 1, "Monkeypatch successful.");
- Assert.equal(e[0].id, "id1", "ID looks sane");
- Assert.ok(e[0].active, "Experiment is active.");
-
- let lastActive = yield experiments.lastActiveToday();
- Assert.equal(e[0], lastActive, "Last active object is expected.");
-
- replaceExperiments(experiments, FAKE_EXPERIMENTS_2);
- e = yield experiments.getExperiments();
- Assert.equal(e.length, 2, "Monkeypatch successful.");
-
- defineNow(gPolicy, e[0].endDate);
-
- lastActive = yield experiments.lastActiveToday();
- Assert.ok(lastActive, "Have a last active experiment");
- Assert.equal(lastActive, e[0], "Last active object is expected.");
-
- yield testCleanup(experiments);
-});
-
-// Test explicitly disabling experiments.
-
-add_task(function* test_disableExperiment() {
- // Dates this test is based on.
-
- let startDate = new Date(2004, 10, 9, 12);
- let endDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- // Data to compare the result of Experiments.getExperiments() against.
-
- let experimentInfo = {
- id: EXPERIMENT1_ID,
- name: EXPERIMENT1_NAME,
- description: "Yet another experiment that experiments experimentally.",
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set for the experiment to start.
-
- let now = futureDate(startDate, 5 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
-
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
-
- experimentInfo.active = true;
- experimentInfo.endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
- for (let k of Object.keys(experimentInfo)) {
- Assert.equal(experimentInfo[k], list[0][k],
- "Property " + k + " should match reference data.");
- }
-
- // Test disabling the experiment.
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.disableExperiment("foo");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
-
- experimentInfo.active = false;
- experimentInfo.endDate = now.getTime();
- for (let k of Object.keys(experimentInfo)) {
- Assert.equal(experimentInfo[k], list[0][k],
- "Property " + k + " should match reference data.");
- }
-
- // Test that updating the list doesn't re-enable it.
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
-
- for (let k of Object.keys(experimentInfo)) {
- Assert.equal(experimentInfo[k], list[0][k],
- "Property " + k + " should match reference data.");
- }
-
- yield testCleanup(experiments);
-});
-
-add_task(function* test_disableExperimentsFeature() {
- // Dates this test is based on.
-
- let startDate = new Date(2004, 10, 9, 12);
- let endDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- // Data to compare the result of Experiments.getExperiments() against.
-
- let experimentInfo = {
- id: EXPERIMENT1_ID,
- name: EXPERIMENT1_NAME,
- description: "Yet another experiment that experiments experimentally.",
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
- Assert.equal(experiments.enabled, true, "Experiments feature should be enabled.");
-
- // Trigger update, clock set for the experiment to start.
-
- let now = futureDate(startDate, 5 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
-
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
-
- experimentInfo.active = true;
- experimentInfo.endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
- for (let k of Object.keys(experimentInfo)) {
- Assert.equal(experimentInfo[k], list[0][k],
- "Property " + k + " should match reference data.");
- }
-
- // Test disabling experiments.
-
- experiments._toggleExperimentsEnabled(false);
- yield experiments.notify();
- Assert.equal(experiments.enabled, false, "Experiments feature should be disabled now.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
-
- experimentInfo.active = false;
- experimentInfo.endDate = now.getTime();
- for (let k of Object.keys(experimentInfo)) {
- Assert.equal(experimentInfo[k], list[0][k],
- "Property " + k + " should match reference data.");
- }
-
- // Test that updating the list doesn't re-enable it.
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- try {
- yield experiments.updateManifest();
- } catch (e) {
- // Exception expected, the feature is disabled.
- }
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
-
- for (let k of Object.keys(experimentInfo)) {
- Assert.equal(experimentInfo[k], list[0][k],
- "Property " + k + " should match reference data.");
- }
-
- yield testCleanup(experiments);
-});
-
-// Test that after a failed experiment install:
-// * the next applicable experiment gets installed
-// * changing the experiments data later triggers re-evaluation
-
-add_task(function* test_installFailure() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- {
- id: EXPERIMENT2_ID,
- xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- // Data to compare the result of Experiments.getExperiments() against.
-
- let experimentListData = [
- {
- id: EXPERIMENT1_ID,
- name: EXPERIMENT1_NAME,
- description: "Yet another experiment that experiments experimentally.",
- },
- {
- id: EXPERIMENT2_ID,
- name: "Test experiment 2",
- description: "And yet another experiment that experiments experimentally.",
- },
- ];
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Trigger update, clock set for experiment 1 & 2 to start,
- // invalid hash for experiment 1.
- // Order in the manifest matters, so we should start experiment 1,
- // fail to install it & start experiment 2 instead.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].xpiHash = "sha1:0000000000000000000000000000000000000000";
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT2_ID, "Experiment 2 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 2 should be active.");
-
- // Trigger update, clock set for experiment 2 to stop.
-
- now = futureDate(now, 20 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- experimentListData[0].active = false;
- experimentListData[0].endDate = now;
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT2_ID, "Experiment 2 should be the sole entry.");
- Assert.equal(list[0].active, false, "Experiment should not be active.");
-
- // Trigger update with a fixed entry for experiment 1,
- // which should get re-evaluated & started now.
-
- now = futureDate(now, 20 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].xpiHash = EXPERIMENT1_XPI_SHA1;
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- experimentListData[0].active = true;
- experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
-
- for (let i=0; i<experimentListData.length; ++i) {
- let entry = experimentListData[i];
- for (let k of Object.keys(entry)) {
- Assert.equal(entry[k], list[i][k],
- "Entry " + i + " - Property '" + k + "' should match reference data.");
- }
- }
-
- yield testCleanup(experiments);
-});
-
-// Test that after an experiment was disabled by user action,
-// the experiment is not activated again if manifest data changes.
-
-add_task(function* test_userDisabledAndUpdated() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Trigger update, clock set for experiment 1 to start.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active.");
- let todayActive = yield experiments.lastActiveToday();
- Assert.ok(todayActive, "Last active for today reports a value.");
- Assert.equal(todayActive.id, list[0].id, "The entry is what we expect.");
-
- // Explicitly disable an experiment.
-
- now = futureDate(now, 20 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.disableExperiment("foo");
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, false, "Experiment should not be active anymore.");
- todayActive = yield experiments.lastActiveToday();
- Assert.ok(todayActive, "Last active for today still returns a value.");
- Assert.equal(todayActive.id, list[0].id, "The ID is still the same.");
-
- // Trigger an update with a faked change for experiment 1.
-
- now = futureDate(now, 20 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- experiments._experiments.get(EXPERIMENT1_ID)._manifestData.xpiHash =
- "sha1:0000000000000000000000000000000000000000";
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, expectedObserverFireCount,
- "Experiments observer should not have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, false, "Experiment should still be inactive.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-// Test that changing the hash for an active experiments triggers an
-// update for it.
-
-add_task(function* test_updateActiveExperiment() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- let todayActive = yield experiments.lastActiveToday();
- Assert.equal(todayActive, null, "No experiment active today.");
-
- // Trigger update, clock set for the experiment to start.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active.");
- Assert.equal(list[0].name, EXPERIMENT1_NAME, "Experiments name should match.");
- todayActive = yield experiments.lastActiveToday();
- Assert.ok(todayActive, "todayActive() returns a value.");
- Assert.equal(todayActive.id, list[0].id, "It returns the active experiment.");
-
- // Trigger an update for the active experiment by changing it's hash (and xpi)
- // in the manifest.
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].xpiHash = EXPERIMENT1A_XPI_SHA1;
- gManifestObject.experiments[0].xpiURL = gDataRoot + EXPERIMENT1A_XPI_NAME;
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should still be active.");
- Assert.equal(list[0].name, EXPERIMENT1A_NAME, "Experiments name should have been updated.");
- todayActive = yield experiments.lastActiveToday();
- Assert.equal(todayActive.id, list[0].id, "last active today is still sane.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-// Tests that setting the disable flag for an active experiment
-// stops it.
-
-add_task(function* test_disableActiveExperiment() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Trigger update, clock set for the experiment to start.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active.");
-
- // Trigger an update with the experiment being disabled.
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].disabled = true;
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, false, "Experiment 1 should be disabled.");
-
- // Check that the experiment stays disabled.
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- delete gManifestObject.experiments[0].disabled;
- yield experiments.updateManifest();
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, false, "Experiment 1 should still be disabled.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-// Test that:
-// * setting the frozen flag for a not-yet-started experiment keeps
-// it from starting
-// * after a removing the frozen flag, the experiment can still start
-
-add_task(function* test_freezePendingExperiment() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Trigger update, clock set for the experiment to start but frozen.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].frozen = true;
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, expectedObserverFireCount,
- "Experiments observer should have not been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should have no entries yet.");
-
- // Trigger an update with the experiment not being frozen anymore.
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- delete gManifestObject.experiments[0].frozen;
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active now.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-// Test that setting the frozen flag for an active experiment doesn't
-// stop it.
-
-add_task(function* test_freezeActiveExperiment() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Trigger update, clock set for the experiment to start.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active.");
- Assert.equal(list[0].name, EXPERIMENT1_NAME, "Experiments name should match.");
-
- // Trigger an update with the experiment being disabled.
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].frozen = true;
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should still be active.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-// Test that removing an active experiment from the manifest doesn't
-// stop it.
-
-add_task(function* test_removeActiveExperiment() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
- let startDate2 = futureDate(baseDate, 20000 * MS_IN_ONE_DAY);
- let endDate2 = futureDate(baseDate, 30000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- {
- id: EXPERIMENT2_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- startTime: dateToSeconds(startDate2),
- endTime: dateToSeconds(endDate2),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Trigger update, clock set for the experiment to start.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active.");
- Assert.equal(list[0].name, EXPERIMENT1_NAME, "Experiments name should match.");
-
- // Trigger an update with experiment 1 missing from the manifest
-
- now = futureDate(now, 1 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].frozen = true;
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should still be active.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-// Test that we correctly handle experiment start & install failures.
-
-add_task(function* test_invalidUrl() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME + ".invalid",
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: 0,
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set for the experiment to start.
-
- let now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gTimerScheduleOffset = null;
-
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- Assert.equal(gTimerScheduleOffset, null, "No new timer should have been scheduled.");
-
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-// Test that we handle it properly when active experiment addons are being
-// uninstalled.
-
-add_task(function* test_unexpectedUninstall() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Trigger update, clock set for the experiment to start.
-
- now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, true, "Experiment 1 should be active.");
-
- // Uninstall the addon through the addon manager instead of stopping it through
- // the experiments API.
-
- yield AddonManagerTesting.uninstallAddonByID(EXPERIMENT1_ID);
- yield experiments._mainTask;
-
- yield experiments.notify();
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.equal(list[0].active, false, "Experiment 1 should not be active anymore.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield testCleanup(experiments);
-});
-
-// If the Addon Manager knows of an experiment that we don't, it should get
-// uninstalled.
-add_task(function* testUnknownExperimentsUninstalled() {
- let experiments = new Experiments.Experiments(gPolicy);
-
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are present.");
-
- // Simulate us not listening.
- experiments._unregisterWithAddonManager();
- yield AddonManagerTesting.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
- experiments._registerWithAddonManager();
-
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "Experiment 1 installed via AddonManager");
-
- // Simulate no known experiments.
- gManifestObject = {
- "version": 1,
- experiments: [],
- };
-
- yield experiments.updateManifest();
- let fromManifest = yield experiments.getExperiments();
- Assert.equal(fromManifest.length, 0, "No experiments known in manifest.");
-
- // And the unknown add-on should be gone.
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Experiment 1 was uninstalled.");
-
- yield testCleanup(experiments);
-});
-
-// If someone else installs an experiment add-on, we detect and stop that.
-add_task(function* testForeignExperimentInstall() {
- let experiments = new Experiments.Experiments(gPolicy);
-
- gManifestObject = {
- "version": 1,
- experiments: [],
- };
-
- yield experiments.init();
-
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Precondition: No experiment add-ons present.");
-
- let failed = false;
- try {
- yield AddonManagerTesting.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
- } catch (ex) {
- failed = true;
- }
- Assert.ok(failed, "Add-on install should not have completed successfully");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Add-on install should have been cancelled.");
-
- yield testCleanup(experiments);
-});
-
-// Experiment add-ons will be disabled after Addon Manager restarts. Ensure
-// we enable them automatically.
-add_task(function* testEnabledAfterRestart() {
- let experiments = new Experiments.Experiments(gPolicy);
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: gPolicy.now().getTime() / 1000 - 60,
- endTime: gPolicy.now().getTime() / 1000 + 60,
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
-
- yield experiments.updateManifest();
- let fromManifest = yield experiments.getExperiments();
- Assert.equal(fromManifest.length, 1, "A single experiment is known.");
-
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "A single experiment add-on is installed.");
- Assert.ok(addons[0].isActive, "That experiment is active.");
-
- dump("Restarting Addon Manager\n");
- yield promiseRestartManager();
- experiments = new Experiments.Experiments(gPolicy);
-
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "The experiment is still there after restart.");
- Assert.ok(addons[0].userDisabled, "But it is disabled.");
- Assert.equal(addons[0].isActive, false, "And not active.");
-
- yield experiments.updateManifest();
- Assert.ok(addons[0].isActive, "It activates when the manifest is evaluated.");
-
- yield testCleanup(experiments);
-});
-
-// If experiment add-ons were ever started, maxStartTime shouldn't be evaluated
-// anymore. Ensure that if maxStartTime is passed but experiment has started
-// already, maxStartTime does not cause deactivation.
-
-add_task(function* testMaxStartTimeEvaluation() {
-
- // Dates the following tests are based on.
-
- let startDate = new Date(2014, 5, 1, 12);
- let now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
- let maxStartDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
- let endDate = futureDate(startDate, 1000 * MS_IN_ONE_DAY);
-
- defineNow(gPolicy, now);
-
- // The manifest data we test with.
- // We set a value for maxStartTime.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate),
- endTime: dateToSeconds(endDate),
- maxActiveSeconds: 1000 * SEC_IN_ONE_DAY,
- maxStartTime: dateToSeconds(maxStartDate),
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
-
- yield experiments.updateManifest();
- let fromManifest = yield experiments.getExperiments();
- Assert.equal(fromManifest.length, 1, "A single experiment is known.");
-
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "A single experiment add-on is installed.");
- Assert.ok(addons[0].isActive, "That experiment is active.");
-
- dump("Setting current time to maxStartTime + 100 days and reloading manifest\n");
- now = futureDate(maxStartDate, 100 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- yield experiments.updateManifest();
-
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "The experiment is still there.");
- Assert.ok(addons[0].isActive, "It is still active.");
-
- yield testCleanup(experiments);
-});
-
-// Test coverage for an add-on uninstall disabling the experiment and that it stays
-// disabled over restarts.
-add_task(function* test_foreignUninstallAndRestart() {
- let experiments = new Experiments.Experiments(gPolicy);
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: gPolicy.now().getTime() / 1000 - 60,
- endTime: gPolicy.now().getTime() / 1000 + 60,
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
-
- yield experiments.updateManifest();
- let experimentList = yield experiments.getExperiments();
- Assert.equal(experimentList.length, 1, "A single experiment is known.");
-
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "A single experiment add-on is installed.");
- Assert.ok(addons[0].isActive, "That experiment is active.");
-
- yield AddonManagerTesting.uninstallAddonByID(EXPERIMENT1_ID);
- yield experiments._mainTask;
-
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Experiment add-on should have been removed.");
-
- experimentList = yield experiments.getExperiments();
- Assert.equal(experimentList.length, 1, "A single experiment is known.");
- Assert.equal(experimentList[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.ok(!experimentList[0].active, "Experiment 1 should not be active anymore.");
-
- // Fake restart behaviour.
- yield promiseRestartManager();
- experiments = new Experiments.Experiments(gPolicy);
- yield experiments.updateManifest();
-
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "No experiment add-ons installed.");
-
- experimentList = yield experiments.getExperiments();
- Assert.equal(experimentList.length, 1, "A single experiment is known.");
- Assert.equal(experimentList[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
- Assert.ok(!experimentList[0].active, "Experiment 1 should not be active.");
-
- yield testCleanup(experiments);
-});
diff --git a/browser/experiments/test/xpcshell/test_cache.js b/browser/experiments/test/xpcshell/test_cache.js
deleted file mode 100644
index 4f2bce881..000000000
--- a/browser/experiments/test/xpcshell/test_cache.js
+++ /dev/null
@@ -1,399 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://testing-common/httpd.js");
-XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
- "resource:///modules/experiments/Experiments.jsm");
-
-const MANIFEST_HANDLER = "manifests/handler";
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
-
-var gHttpServer = null;
-var gHttpRoot = null;
-var gDataRoot = null;
-var gPolicy = null;
-var gManifestObject = null;
-var gManifestHandlerURI = null;
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function* test_setup() {
- loadAddonManager();
- yield removeCacheFile();
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let port = gHttpServer.identity.primaryPort;
- gHttpRoot = "http://localhost:" + port + "/";
- gDataRoot = gHttpRoot + "data/";
- gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
- gHttpServer.registerDirectory("/data/", do_get_cwd());
- gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
- response.setStatusLine(null, 200, "OK");
- response.write(JSON.stringify(gManifestObject));
- response.processAsync();
- response.finish();
- });
- do_register_cleanup(() => gHttpServer.stop(() => {}));
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
- Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
- Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
-
- gPolicy = new Experiments.Policy();
- patchPolicy(gPolicy, {
- updatechannel: () => "nightly",
- oneshotTimer: (callback, timeout, thisObj, name) => {},
- });
-});
-
-function checkExperimentListsEqual(list, list2) {
- Assert.equal(list.length, list2.length, "Lists should have the same length.")
-
- for (let i=0; i<list.length; ++i) {
- for (let k of Object.keys(list[i])) {
- Assert.equal(list[i][k], list2[i][k],
- "Field '" + k + "' should match for list entry " + i + ".");
- }
- }
-}
-
-function checkExperimentSerializations(experimentEntryIterator) {
- for (let experiment of experimentEntryIterator) {
- let experiment2 = new Experiments.ExperimentEntry(gPolicy);
- let jsonStr = JSON.stringify(experiment.toJSON());
- Assert.ok(experiment2.initFromCacheData(JSON.parse(jsonStr)),
- "Should have initialized successfully from JSON serialization.");
- Assert.equal(JSON.stringify(experiment), JSON.stringify(experiment2),
- "Object stringifications should match.");
- }
-}
-
-function validateCache(cachedExperiments, experimentIds) {
- let cachedExperimentIds = new Set(cachedExperiments);
- Assert.equal(cachedExperimentIds.size, experimentIds.length,
- "The number of cached experiments does not match with the provided list");
- for (let id of experimentIds) {
- Assert.ok(cachedExperimentIds.has(id), "The cache must contain the experiment with id " + id);
- }
-}
-
-// Set up an experiments instance and check if it is properly restored from cache.
-
-add_task(function* test_cache() {
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- {
- id: EXPERIMENT2_ID,
- xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- {
- id: EXPERIMENT3_ID,
- xpiURL: "https://inval.id/foo.xpi",
- xpiHash: "sha1:0000000000000000000000000000000000000000",
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- // Setup dates for the experiments.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDates = [];
- let endDates = [];
-
- for (let i=0; i<gManifestObject.experiments.length; ++i) {
- let experiment = gManifestObject.experiments[i];
- startDates.push(futureDate(baseDate, (50 + (150 * i)) * MS_IN_ONE_DAY));
- endDates .push(futureDate(startDates[i], 50 * MS_IN_ONE_DAY));
- experiment.startTime = dateToSeconds(startDates[i]);
- experiment.endTime = dateToSeconds(endDates[i]);
- }
-
- // Data to compare the result of Experiments.getExperiments() against.
-
- let experimentListData = [
- {
- id: EXPERIMENT2_ID,
- name: "Test experiment 2",
- description: "And yet another experiment that experiments experimentally.",
- },
- {
- id: EXPERIMENT1_ID,
- name: EXPERIMENT1_NAME,
- description: "Yet another experiment that experiments experimentally.",
- },
- ];
-
- // Trigger update & re-init, clock set to before any activation.
-
- let now = baseDate;
- defineNow(gPolicy, now);
-
- let experiments = new Experiments.Experiments(gPolicy);
- yield experiments.updateManifest();
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
- checkExperimentSerializations(experiments._experiments.values());
-
- yield promiseRestartManager();
- experiments = new Experiments.Experiments(gPolicy);
-
- yield experiments._run();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
- checkExperimentSerializations(experiments._experiments.values());
-
- // Re-init, clock set for experiment 1 to start.
-
- now = futureDate(startDates[0], 5 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
-
- yield promiseRestartManager();
- experiments = new Experiments.Experiments(gPolicy);
- yield experiments._run();
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
-
- experimentListData[1].active = true;
- experimentListData[1].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
- checkExperimentListsEqual(experimentListData.slice(1), list);
- checkExperimentSerializations(experiments._experiments.values());
-
- let branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
- Assert.strictEqual(branch, null);
-
- yield experiments.setExperimentBranch(EXPERIMENT1_ID, "testbranch");
- branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
- Assert.strictEqual(branch, "testbranch");
-
- // Re-init, clock set for experiment 1 to stop.
-
- now = futureDate(now, 20 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
-
- yield promiseRestartManager();
- experiments = new Experiments.Experiments(gPolicy);
- yield experiments._run();
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
-
- experimentListData[1].active = false;
- experimentListData[1].endDate = now.getTime();
- checkExperimentListsEqual(experimentListData.slice(1), list);
- checkExperimentSerializations(experiments._experiments.values());
-
- branch = yield experiments.getExperimentBranch(EXPERIMENT1_ID);
- Assert.strictEqual(branch, "testbranch");
-
- // Re-init, clock set for experiment 2 to start.
-
- now = futureDate(startDates[1], 20 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
-
- yield promiseRestartManager();
- experiments = new Experiments.Experiments(gPolicy);
- yield experiments._run();
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
-
- experimentListData[0].active = true;
- experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
- checkExperimentListsEqual(experimentListData, list);
- checkExperimentSerializations(experiments._experiments.values());
-
- // Re-init, clock set for experiment 2 to stop.
-
- now = futureDate(now, 20 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
-
- yield promiseRestartManager();
- experiments = new Experiments.Experiments(gPolicy);
- yield experiments._run();
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
-
- experimentListData[0].active = false;
- experimentListData[0].endDate = now.getTime();
- checkExperimentListsEqual(experimentListData, list);
- checkExperimentSerializations(experiments._experiments.values());
-
- // Cleanup.
-
- yield experiments._toggleExperimentsEnabled(false);
- yield promiseRestartManager();
- yield removeCacheFile();
-});
-
-add_task(function* test_expiration() {
- // The manifest data we test with.
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- {
- id: EXPERIMENT2_ID,
- xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- maxActiveSeconds: 50 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- // The 3rd experiment will never run, so it's ok to use experiment's 2 data.
- {
- id: EXPERIMENT3_ID,
- xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- }
- ],
- };
-
- // Data to compare the result of Experiments.getExperiments() against.
- let experimentListData = [
- {
- id: EXPERIMENT2_ID,
- name: "Test experiment 2",
- description: "And yet another experiment that experiments experimentally.",
- },
- {
- id: EXPERIMENT1_ID,
- name: EXPERIMENT1_NAME,
- description: "Yet another experiment that experiments experimentally.",
- },
- ];
-
- // Setup dates for the experiments.
- let baseDate = new Date(2014, 5, 1, 12);
- let startDates = [];
- let endDates = [];
-
- for (let i=0; i<gManifestObject.experiments.length; ++i) {
- let experiment = gManifestObject.experiments[i];
- // Spread out experiments in time so that one experiment can end and expire while
- // the next is still running.
- startDates.push(futureDate(baseDate, (50 + (200 * i)) * MS_IN_ONE_DAY));
- endDates .push(futureDate(startDates[i], 50 * MS_IN_ONE_DAY));
- experiment.startTime = dateToSeconds(startDates[i]);
- experiment.endTime = dateToSeconds(endDates[i]);
- }
-
- let now = null;
- let experiments = null;
-
- let setDateAndRestartExperiments = new Task.async(function* (newDate) {
- now = newDate;
- defineNow(gPolicy, now);
-
- yield promiseRestartManager();
- experiments = new Experiments.Experiments(gPolicy);
- yield experiments._run();
- });
-
- // Trigger update & re-init, clock set to before any activation.
- now = baseDate;
- defineNow(gPolicy, now);
-
- experiments = new Experiments.Experiments(gPolicy);
- yield experiments.updateManifest();
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- // Re-init, clock set for experiment 1 to start...
- yield setDateAndRestartExperiments(startDates[0]);
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "The first experiment should have started.");
-
- // ... init again, and set the clock so that the first experiment ends.
- yield setDateAndRestartExperiments(endDates[0]);
-
- // The experiment just ended, it should still be in the cache, but marked
- // as finished.
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
-
- experimentListData[1].active = false;
- experimentListData[1].endDate = now.getTime();
- checkExperimentListsEqual(experimentListData.slice(1), list);
- validateCache([...experiments._experiments.keys()], [EXPERIMENT1_ID, EXPERIMENT2_ID, EXPERIMENT3_ID]);
-
- // Start the second experiment.
- yield setDateAndRestartExperiments(startDates[1]);
-
- // The experiments cache should contain the finished experiment and the
- // one that's still running.
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
-
- experimentListData[0].active = true;
- experimentListData[0].endDate = now.getTime() + 50 * MS_IN_ONE_DAY;
- checkExperimentListsEqual(experimentListData, list);
-
- // Move the clock in the future, just 31 days after the start date of the second experiment,
- // so that the cache for the first experiment expires and the second experiment is still running.
- yield setDateAndRestartExperiments(futureDate(startDates[1], 31 * MS_IN_ONE_DAY));
- validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID, EXPERIMENT3_ID]);
-
- // Make sure that the expired experiment is not reported anymore.
- let history = yield experiments.getExperiments();
- Assert.equal(history.length, 1, "Experiments older than 180 days must be removed from the cache.");
-
- // Test that we don't write expired experiments in the cache.
- yield setDateAndRestartExperiments(now);
- validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID, EXPERIMENT3_ID]);
-
- // The first experiment should be expired and not in the cache, it ended more than
- // 180 days ago. We should see the one still running in the cache.
- history = yield experiments.getExperiments();
- Assert.equal(history.length, 1, "Expired experiments must not be saved to cache.");
- checkExperimentListsEqual(experimentListData.slice(0, 1), history);
-
- // Test that experiments that are cached locally but never ran are removed from cache
- // when they are removed from the manifest (this is cached data, not really history).
- gManifestObject["experiments"] = gManifestObject["experiments"].slice(1, 1);
- yield experiments.updateManifest();
- validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID]);
-
- // Cleanup.
- yield experiments._toggleExperimentsEnabled(false);
- yield promiseRestartManager();
- yield removeCacheFile();
-});
diff --git a/browser/experiments/test/xpcshell/test_cacherace.js b/browser/experiments/test/xpcshell/test_cacherace.js
deleted file mode 100644
index ff77cfdc4..000000000
--- a/browser/experiments/test/xpcshell/test_cacherace.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource://gre/modules/Timer.jsm");
-
-const MANIFEST_HANDLER = "manifests/handler";
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
-
-var gHttpServer = null;
-var gHttpRoot = null;
-var gDataRoot = null;
-var gPolicy = null;
-var gManifestObject = null;
-var gManifestHandlerURI = null;
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function* test_setup() {
- loadAddonManager();
- yield removeCacheFile();
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let port = gHttpServer.identity.primaryPort;
- gHttpRoot = "http://localhost:" + port + "/";
- gDataRoot = gHttpRoot + "data/";
- gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
- gHttpServer.registerDirectory("/data/", do_get_cwd());
- gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
- response.setStatusLine(null, 200, "OK");
- response.write(JSON.stringify(gManifestObject));
- response.processAsync();
- response.finish();
- });
- do_register_cleanup(() => gHttpServer.stop(() => {}));
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
- Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
- Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
-
- let ExperimentsScope = Cu.import("resource:///modules/experiments/Experiments.jsm");
- let Experiments = ExperimentsScope.Experiments;
-
- gPolicy = new Experiments.Policy();
- patchPolicy(gPolicy, {
- updatechannel: () => "nightly",
- delayCacheWrite: (promise) => {
- return new Promise((resolve, reject) => {
- promise.then(
- (result) => { setTimeout(() => resolve(result), 500); },
- (err) => { reject(err); }
- );
- });
- },
- });
-
- let now = new Date(2014, 5, 1, 12);
- defineNow(gPolicy, now);
-
- let experimentName = "experiment-racybranch.xpi";
- let experimentPath = getExperimentPath(experimentName);
- let experimentHash = "sha1:" + sha1File(experimentPath);
-
- gManifestObject = {
- version: 1,
- experiments: [
- {
- id: "test-experiment-racybranch@tests.mozilla.org",
- xpiURL: gDataRoot + "experiment-racybranch.xpi",
- xpiHash: experimentHash,
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- startTime: dateToSeconds(futureDate(now, -MS_IN_ONE_DAY)),
- endTime: dateToSeconds(futureDate(now, MS_IN_ONE_DAY)),
- },
- ],
- };
-
- do_print("gManifestObject: " + JSON.stringify(gManifestObject));
-
- // In order for the addon manager to work properly, we hack
- // Experiments.instance which is used by the XPIProvider
- let experiments = new Experiments.Experiments(gPolicy);
- Assert.strictEqual(ExperimentsScope.gExperiments, null);
- ExperimentsScope.gExperiments = experiments;
-
- yield experiments.updateManifest();
- let active = experiments._getActiveExperiment();
- Assert.ok(active);
- Assert.equal(active.branch, "racy-set");
- Assert.ok(!experiments._dirty);
-});
diff --git a/browser/experiments/test/xpcshell/test_conditions.js b/browser/experiments/test/xpcshell/test_conditions.js
deleted file mode 100644
index 23c147fdb..000000000
--- a/browser/experiments/test/xpcshell/test_conditions.js
+++ /dev/null
@@ -1,325 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-
-Cu.import("resource:///modules/experiments/Experiments.jsm");
-Cu.import("resource://gre/modules/TelemetryController.jsm", this);
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-
-var gPolicy = null;
-
-function ManifestEntry(data) {
- this.id = EXPERIMENT1_ID;
- this.xpiURL = "http://localhost:1/dummy.xpi";
- this.xpiHash = EXPERIMENT1_XPI_SHA1;
- this.startTime = new Date(2010, 0, 1, 12).getTime() / 1000;
- this.endTime = new Date(9001, 0, 1, 12).getTime() / 1000;
- this.maxActiveSeconds = SEC_IN_ONE_DAY;
- this.appName = ["XPCShell"];
- this.channel = ["nightly"];
-
- data = data || {};
- for (let k of Object.keys(data)) {
- this[k] = data[k];
- }
-
- if (!this.endTime) {
- this.endTime = this.startTime + 5 * SEC_IN_ONE_DAY;
- }
-}
-
-function applicableFromManifestData(data, policy) {
- let manifestData = new ManifestEntry(data);
- let entry = new Experiments.ExperimentEntry(policy);
- entry.initFromManifestData(manifestData);
- return entry.isApplicable();
-}
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function* test_setup() {
- createAppInfo();
- do_get_profile();
- startAddonManagerOnly();
- yield TelemetryController.testSetup();
- gPolicy = new Experiments.Policy();
-
- patchPolicy(gPolicy, {
- updatechannel: () => "nightly",
- locale: () => "en-US",
- random: () => 0.5,
- });
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
-});
-
-function arraysEqual(a, b) {
- if (a.length !== b.length) {
- return false;
- }
-
- for (let i=0; i<a.length; ++i) {
- if (a[i] !== b[i]) {
- return false;
- }
- }
-
- return true;
-}
-
-// This function exists solely to be .toSource()d
-const sanityFilter = function filter(c) {
- if (c.telemetryEnvironment === undefined) {
- throw Error("No .telemetryEnvironment");
- }
- if (c.telemetryEnvironment.build == undefined) {
- throw Error("No .telemetryEnvironment.build");
- }
- return true;
-}
-
-// Utility function to generate build ID for previous/next date.
-function addDate(buildId, diff) {
- let m = /^([0-9]{4})([0-9]{2})([0-9]{2})(.*)$/.exec(buildId);
- if (!m) {
- throw Error("Unsupported build ID: " + buildId);
- }
- let year = Number.parseInt(m[1], 10);
- let month = Number.parseInt(m[2], 10);
- let date = Number.parseInt(m[3], 10);
- let remainingParts = m[4];
-
- let d = new Date();
- d.setUTCFullYear(year, month - 1, date);
- d.setTime(d.getTime() + diff * 24 * 60 * 60 * 1000);
-
- let yearStr = String(d.getUTCFullYear());
- let monthStr = ("0" + String(d.getUTCMonth() + 1)).slice(-2);
- let dateStr = ("0" + String(d.getUTCDate())).slice(-2);
- return yearStr + monthStr + dateStr + remainingParts;
-}
-function prevDate(buildId) {
- return addDate(buildId, -1);
-}
-function nextDate(buildId) {
- return addDate(buildId, 1);
-}
-
-add_task(function* test_simpleFields() {
- let testData = [
- // "expected applicable?", failure reason or null, manifest data
-
- // misc. environment
-
- [false, ["appName"], {appName: []}],
- [false, ["appName"], {appName: ["foo", gAppInfo.name + "-invalid"]}],
- [true, null, {appName: ["not-an-app-name", gAppInfo.name]}],
-
- [false, ["os"], {os: []}],
- [false, ["os"], {os: ["42", "abcdef"]}],
- [true, null, {os: [gAppInfo.OS, "plan9"]}],
-
- [false, ["channel"], {channel: []}],
- [false, ["channel"], {channel: ["foo", gPolicy.updatechannel() + "-invalid"]}],
- [true, null, {channel: ["not-a-channel", gPolicy.updatechannel()]}],
-
- [false, ["locale"], {locale: []}],
- [false, ["locale"], {locale: ["foo", gPolicy.locale + "-invalid"]}],
- [true, null, {locale: ["not-a-locale", gPolicy.locale()]}],
-
- // version
-
- [false, ["version"], {version: []}],
- [false, ["version"], {version: ["-1", gAppInfo.version + "-invalid", "asdf", "0,4", "99.99", "0.1.1.1"]}],
- [true, null, {version: ["99999999.999", "-1", gAppInfo.version]}],
-
- [false, ["minVersion"], {minVersion: "1.0.1"}],
- [true, null, {minVersion: "1.0b1"}],
- [true, null, {minVersion: "1.0"}],
- [true, null, {minVersion: "0.9"}],
-
- [false, ["maxVersion"], {maxVersion: "0.1"}],
- [false, ["maxVersion"], {maxVersion: "0.9.9"}],
- [false, ["maxVersion"], {maxVersion: "1.0b1"}],
- [true, ["maxVersion"], {maxVersion: "1.0"}],
- [true, ["maxVersion"], {maxVersion: "1.7pre"}],
-
- // build id
-
- [false, ["buildIDs"], {buildIDs: []}],
- [false, ["buildIDs"], {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID + "-invalid"]}],
- [true, null, {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID]}],
-
- [true, null, {minBuildID: prevDate(gAppInfo.platformBuildID)}],
- [true, null, {minBuildID: gAppInfo.platformBuildID}],
- [false, ["minBuildID"], {minBuildID: nextDate(gAppInfo.platformBuildID)}],
-
- [false, ["maxBuildID"], {maxBuildID: prevDate(gAppInfo.platformBuildID)}],
- [true, null, {maxBuildID: gAppInfo.platformBuildID}],
- [true, null, {maxBuildID: nextDate(gAppInfo.platformBuildID)}],
-
- // sample
-
- [false, ["sample"], {sample: -1 }],
- [false, ["sample"], {sample: 0.0}],
- [false, ["sample"], {sample: 0.1}],
- [true, null, {sample: 0.5}],
- [true, null, {sample: 0.6}],
- [true, null, {sample: 1.0}],
- [true, null, {sample: 0.5}],
-
- // experiment control
-
- [false, ["disabled"], {disabled: true}],
- [true, null, {disabled: false}],
-
- [false, ["frozen"], {frozen: true}],
- [true, null, {frozen: false}],
-
- [false, null, {frozen: true, disabled: true}],
- [false, null, {frozen: true, disabled: false}],
- [false, null, {frozen: false, disabled: true}],
- [true, null, {frozen: false, disabled: false}],
-
- // jsfilter
-
- [true, null, {jsfilter: "function filter(c) { return true; }"}],
- [false, ["jsfilter-false"], {jsfilter: "function filter(c) { return false; }"}],
- [true, null, {jsfilter: "function filter(c) { return 123; }"}], // truthy
- [false, ["jsfilter-false"], {jsfilter: "function filter(c) { return ''; }"}], // falsy
- [false, ["jsfilter-false"], {jsfilter: "function filter(c) { var a = []; }"}], // undefined
- [false, ["jsfilter-threw", "some error"], {jsfilter: "function filter(c) { throw new Error('some error'); }"}],
- [false, ["jsfilter-evalfailed"], {jsfilter: "123, this won't work"}],
- [true, null, {jsfilter: "var filter = " + sanityFilter.toSource()}],
- ];
-
- for (let i=0; i<testData.length; ++i) {
- let entry = testData[i];
- let applicable;
- let reason = null;
-
- yield applicableFromManifestData(entry[2], gPolicy).then(
- value => applicable = value,
- value => {
- applicable = false;
- reason = value;
- }
- );
-
- Assert.equal(applicable, entry[0],
- "Experiment entry applicability should match for test "
- + i + ": " + JSON.stringify(entry[2]));
-
- let expectedReason = entry[1];
- if (!applicable && expectedReason) {
- Assert.ok(arraysEqual(reason, expectedReason),
- "Experiment rejection reasons should match for test " + i + ". "
- + "Got " + JSON.stringify(reason) + ", expected "
- + JSON.stringify(expectedReason));
- }
- }
-});
-
-add_task(function* test_times() {
- let now = new Date(2014, 5, 6, 12);
- let nowSec = now.getTime() / 1000;
- let testData = [
- // "expected applicable?", rejection reason or null, fake now date, manifest data
-
- // start time
-
- [true, null, now,
- {startTime: nowSec - 5 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [true, null, now,
- {startTime: nowSec,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [false, "startTime", now,
- {startTime: nowSec + 5 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
-
- // end time
-
- [false, "endTime", now,
- {startTime: nowSec - 5 * SEC_IN_ONE_DAY,
- endTime: nowSec - 10 * SEC_IN_ONE_DAY}],
- [false, "endTime", now,
- {startTime: nowSec - 5 * SEC_IN_ONE_DAY,
- endTime: nowSec - 5 * SEC_IN_ONE_DAY}],
-
- // max start time
-
- [false, "maxStartTime", now,
- {maxStartTime: nowSec - 15 * SEC_IN_ONE_DAY,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [false, "maxStartTime", now,
- {maxStartTime: nowSec - 1 * SEC_IN_ONE_DAY,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [false, "maxStartTime", now,
- {maxStartTime: nowSec - 10 * SEC_IN_ONE_DAY,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [true, null, now,
- {maxStartTime: nowSec,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [true, null, now,
- {maxStartTime: nowSec + 1 * SEC_IN_ONE_DAY,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
-
- // max active seconds
-
- [true, null, now,
- {maxActiveSeconds: 5 * SEC_IN_ONE_DAY,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [true, null, now,
- {maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [true, null, now,
- {maxActiveSeconds: 15 * SEC_IN_ONE_DAY,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- [true, null, now,
- {maxActiveSeconds: 20 * SEC_IN_ONE_DAY,
- startTime: nowSec - 10 * SEC_IN_ONE_DAY,
- endTime: nowSec + 10 * SEC_IN_ONE_DAY}],
- ];
-
- for (let i=0; i<testData.length; ++i) {
- let entry = testData[i];
- let applicable;
- let reason = null;
- defineNow(gPolicy, entry[2]);
-
- yield applicableFromManifestData(entry[3], gPolicy).then(
- value => applicable = value,
- value => {
- applicable = false;
- reason = value;
- }
- );
-
- Assert.equal(applicable, entry[0],
- "Experiment entry applicability should match for test "
- + i + ": " + JSON.stringify([entry[2], entry[3]]));
- if (!applicable && entry[1]) {
- Assert.equal(reason, entry[1], "Experiment rejection reason should match for test " + i);
- }
- }
-});
-
-add_task(function* test_shutdown() {
- yield TelemetryController.testShutdown();
-});
diff --git a/browser/experiments/test/xpcshell/test_disableExperiments.js b/browser/experiments/test/xpcshell/test_disableExperiments.js
deleted file mode 100644
index 8441b922d..000000000
--- a/browser/experiments/test/xpcshell/test_disableExperiments.js
+++ /dev/null
@@ -1,180 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource://testing-common/AddonManagerTesting.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
- "resource:///modules/experiments/Experiments.jsm");
-
-const MANIFEST_HANDLER = "manifests/handler";
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
-
-var gHttpServer = null;
-var gHttpRoot = null;
-var gDataRoot = null;
-var gPolicy = null;
-var gManifestObject = null;
-var gManifestHandlerURI = null;
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function* test_setup() {
- loadAddonManager();
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let port = gHttpServer.identity.primaryPort;
- gHttpRoot = "http://localhost:" + port + "/";
- gDataRoot = gHttpRoot + "data/";
- gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
- gHttpServer.registerDirectory("/data/", do_get_cwd());
- gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
- response.setStatusLine(null, 200, "OK");
- response.write(JSON.stringify(gManifestObject));
- response.processAsync();
- response.finish();
- });
- do_register_cleanup(() => gHttpServer.stop(() => {}));
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
- Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
- Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
-
- gPolicy = new Experiments.Policy();
- patchPolicy(gPolicy, {
- updatechannel: () => "nightly",
- oneshotTimer: (callback, timeout, thisObj, name) => {},
- });
-});
-
-// Test disabling the feature stops current and future experiments.
-
-add_task(function* test_disableExperiments() {
- const OBSERVER_TOPIC = "experiments-changed";
- let observerFireCount = 0;
- let expectedObserverFireCount = 0;
- let observer = () => ++observerFireCount;
- Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
- let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let startDate2 = futureDate(baseDate, 150 * MS_IN_ONE_DAY);
- let endDate2 = futureDate(baseDate, 200 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT2_ID,
- xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- startTime: dateToSeconds(startDate2),
- endTime: dateToSeconds(endDate2),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate1),
- endTime: dateToSeconds(endDate1),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
- // Use updateManifest() to provide for coverage of that path.
-
- let now = baseDate;
- defineNow(gPolicy, now);
-
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are installed.");
-
- // Trigger update, clock set for experiment 1 to start.
-
- now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
-
- yield experiments.updateManifest();
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
- Assert.equal(list[0].active, true, "Experiment should be active.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 1, "An experiment add-on was installed.");
-
- // Disable the experiments feature. Check that we stop the running experiment.
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, false);
- yield experiments._mainTask;
-
- Assert.equal(observerFireCount, ++expectedObserverFireCount,
- "Experiments observer should have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
- Assert.equal(list[0].active, false, "Experiment entry should not be active.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "The experiment add-on should be uninstalled.");
-
- // Trigger update, clock set for experiment 2 to start. Verify we don't start it.
-
- now = startDate2;
- defineNow(gPolicy, now);
-
- try {
- yield experiments.updateManifest();
- } catch (e) {
- // This exception is expected, we rethrow everything else
- if (e.message != "experiments are disabled") {
- throw e;
- }
- }
-
- experiments.notify();
- yield experiments._mainTask;
-
- Assert.equal(observerFireCount, expectedObserverFireCount,
- "Experiments observer should not have been called.");
-
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should still have 1 entry.");
- Assert.equal(list[0].active, false, "Experiment entry should not be active.");
- addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "There should still be no experiment add-on installed.");
-
- // Cleanup.
-
- Services.obs.removeObserver(observer, OBSERVER_TOPIC);
- yield promiseRestartManager();
- yield removeCacheFile();
-});
diff --git a/browser/experiments/test/xpcshell/test_fetch.js b/browser/experiments/test/xpcshell/test_fetch.js
deleted file mode 100644
index e8d76fa35..000000000
--- a/browser/experiments/test/xpcshell/test_fetch.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource:///modules/experiments/Experiments.jsm");
-
-var gHttpServer = null;
-var gHttpRoot = null;
-var gPolicy = new Experiments.Policy();
-
-function run_test() {
- loadAddonManager();
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let port = gHttpServer.identity.primaryPort;
- gHttpRoot = "http://localhost:" + port + "/";
- gHttpServer.registerDirectory("/", do_get_cwd());
- do_register_cleanup(() => gHttpServer.stop(() => {}));
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
-
- patchPolicy(gPolicy, {
- updatechannel: () => "nightly",
- });
-
- run_next_test();
-}
-
-add_task(function* test_fetchAndCache() {
- Services.prefs.setCharPref(PREF_MANIFEST_URI, gHttpRoot + "experiments_1.manifest");
- let ex = new Experiments.Experiments(gPolicy);
-
- Assert.equal(ex._experiments, null, "There should be no cached experiments yet.");
- yield ex.updateManifest();
- Assert.notEqual(ex._experiments.size, 0, "There should be cached experiments now.");
-
- yield promiseRestartManager();
-});
-
-add_task(function* test_checkCache() {
- let ex = new Experiments.Experiments(gPolicy);
- yield ex.notify();
- Assert.notEqual(ex._experiments.size, 0, "There should be cached experiments now.");
-
- yield promiseRestartManager();
-});
-
-add_task(function* test_fetchInvalid() {
- yield removeCacheFile();
-
- Services.prefs.setCharPref(PREF_MANIFEST_URI, gHttpRoot + "experiments_1.manifest");
- let ex = new Experiments.Experiments(gPolicy);
- yield ex.updateManifest();
- Assert.notEqual(ex._experiments.size, 0, "There should be experiments");
-
- Services.prefs.setCharPref(PREF_MANIFEST_URI, gHttpRoot + "invalid.manifest");
- yield ex.updateManifest()
- Assert.notEqual(ex._experiments.size, 0, "There should still be experiments: fetch failure shouldn't remove them.");
-
- yield promiseRestartManager();
-});
diff --git a/browser/experiments/test/xpcshell/test_nethang_bug1012924.js b/browser/experiments/test/xpcshell/test_nethang_bug1012924.js
deleted file mode 100644
index 7ef604901..000000000
--- a/browser/experiments/test/xpcshell/test_nethang_bug1012924.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource:///modules/experiments/Experiments.jsm");
-
-const MANIFEST_HANDLER = "manifests/handler";
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function* test_setup() {
- loadAddonManager();
- do_get_profile();
-
- let httpServer = new HttpServer();
- httpServer.start(-1);
- let port = httpServer.identity.primaryPort;
- let httpRoot = "http://localhost:" + port + "/";
- let handlerURI = httpRoot + MANIFEST_HANDLER;
- httpServer.registerPathHandler("/" + MANIFEST_HANDLER,
- (request, response) => {
- response.processAsync();
- response.setStatus(null, 200, "OK");
- response.write("["); // never finish!
- });
-
- do_register_cleanup(() => httpServer.stop(() => {}));
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
- Services.prefs.setCharPref(PREF_MANIFEST_URI, handlerURI);
- Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
-
- let experiments = Experiments.instance();
- experiments.updateManifest().then(
- () => {
- Assert.ok(true, "updateManifest finished successfully");
- },
- (e) => {
- do_throw("updateManifest should not have failed: got error " + e);
- });
- yield experiments.uninit();
-});
diff --git a/browser/experiments/test/xpcshell/test_previous_provider.js b/browser/experiments/test/xpcshell/test_previous_provider.js
deleted file mode 100644
index f7186e159..000000000
--- a/browser/experiments/test/xpcshell/test_previous_provider.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource:///modules/experiments/Experiments.jsm");
-Cu.import("resource://testing-common/httpd.js");
-
-var gDataRoot;
-var gHttpServer;
-var gManifestObject;
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function test_setup() {
- loadAddonManager();
- do_get_profile();
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let httpRoot = "http://localhost:" + gHttpServer.identity.primaryPort + "/";
- gDataRoot = httpRoot + "data/";
- gHttpServer.registerDirectory("/data/", do_get_cwd());
- gHttpServer.registerPathHandler("/manifests/handler", (req, res) => {
- res.setStatusLine(null, 200, "OK");
- res.write(JSON.stringify(gManifestObject));
- res.processAsync();
- res.finish();
- });
- do_register_cleanup(() => gHttpServer.stop(() => {}));
-
- Services.prefs.setBoolPref("experiments.enabled", true);
- Services.prefs.setCharPref("experiments.manifest.uri",
- httpRoot + "manifests/handler");
- Services.prefs.setBoolPref("experiments.logging.dump", true);
- Services.prefs.setCharPref("experiments.logging.level", "Trace");
-});
-
-add_task(function* test_provider_basic() {
- let e = Experiments.instance();
-
- let provider = new Experiments.PreviousExperimentProvider(e);
- e._setPreviousExperimentsProvider(provider);
-
- let deferred = Promise.defer();
- provider.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- let experimentAddons = yield deferred.promise;
- Assert.ok(Array.isArray(experimentAddons), "getAddonsByTypes returns an Array.");
- Assert.equal(experimentAddons.length, 0, "No previous add-ons returned.");
-
- gManifestObject = {
- version: 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: Date.now() / 1000 - 60,
- endTime: Date.now() / 1000 + 60,
- maxActiveSeconds: 60,
- appName: ["XPCShell"],
- channel: [e._policy.updatechannel()],
- },
- ],
- };
-
- yield e.updateManifest();
-
- deferred = Promise.defer();
- provider.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- experimentAddons = yield deferred.promise;
- Assert.equal(experimentAddons.length, 0, "Still no previous experiment.");
-
- let experiments = yield e.getExperiments();
- Assert.equal(experiments.length, 1, "1 experiment present.");
- Assert.ok(experiments[0].active, "It is active.");
-
- // Deactivate it.
- defineNow(e._policy, new Date(gManifestObject.experiments[0].endTime * 1000 + 1000));
- yield e.updateManifest();
-
- experiments = yield e.getExperiments();
- Assert.equal(experiments.length, 1, "1 experiment present.");
- Assert.equal(experiments[0].active, false, "It isn't active.");
-
- deferred = Promise.defer();
- provider.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- experimentAddons = yield deferred.promise;
- Assert.equal(experimentAddons.length, 1, "1 previous add-on known.");
- Assert.equal(experimentAddons[0].id, EXPERIMENT1_ID, "ID matches expected.");
-
- deferred = Promise.defer();
- provider.getAddonByID(EXPERIMENT1_ID, (addon) => {
- deferred.resolve(addon);
- });
- let addon = yield deferred.promise;
- Assert.ok(addon, "We got an add-on from its ID.");
- Assert.equal(addon.id, EXPERIMENT1_ID, "ID matches expected.");
- Assert.ok(addon.appDisabled, "Add-on is a previous experiment.");
- Assert.ok(addon.userDisabled, "Add-on is disabled.");
- Assert.equal(addon.type, "experiment", "Add-on is an experiment.");
- Assert.equal(addon.isActive, false, "Add-on is not active.");
- Assert.equal(addon.permissions, 0, "Add-on has no permissions.");
-
- deferred = Promise.defer();
- AddonManager.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- experimentAddons = yield deferred.promise;
- Assert.equal(experimentAddons.length, 1, "Got 1 experiment from add-on manager.");
- Assert.equal(experimentAddons[0].id, EXPERIMENT1_ID, "ID matches expected.");
- Assert.ok(experimentAddons[0].appDisabled, "It is a previous experiment add-on.");
-});
-
-add_task(function* test_active_and_previous() {
- // Building on the previous test, activate experiment 2.
- let e = Experiments.instance();
- let provider = new Experiments.PreviousExperimentProvider(e);
- e._setPreviousExperimentsProvider(provider);
-
- gManifestObject = {
- version: 1,
- experiments: [
- {
- id: EXPERIMENT2_ID,
- xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- startTime: Date.now() / 1000 - 60,
- endTime: Date.now() / 1000 + 60,
- maxActiveSeconds: 60,
- appName: ["XPCShell"],
- channel: [e._policy.updatechannel()],
- },
- ],
- };
-
- defineNow(e._policy, new Date());
- yield e.updateManifest();
-
- let experiments = yield e.getExperiments();
- Assert.equal(experiments.length, 2, "2 experiments known.");
-
- let deferred = Promise.defer();
- provider.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- let experimentAddons = yield deferred.promise;
- Assert.equal(experimentAddons.length, 1, "1 previous experiment.");
-
- deferred = Promise.defer();
- AddonManager.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- experimentAddons = yield deferred.promise;
- Assert.equal(experimentAddons.length, 2, "2 experiment add-ons known.");
-
- for (let addon of experimentAddons) {
- if (addon.id == EXPERIMENT1_ID) {
- Assert.equal(addon.isActive, false, "Add-on is not active.");
- Assert.ok(addon.appDisabled, "Should be a previous experiment.");
- }
- else if (addon.id == EXPERIMENT2_ID) {
- Assert.ok(addon.isActive, "Add-on is active.");
- Assert.ok(!addon.appDisabled, "Should not be a previous experiment.");
- }
- else {
- throw new Error("Unexpected add-on ID: " + addon.id);
- }
- }
-});
diff --git a/browser/experiments/test/xpcshell/test_telemetry.js b/browser/experiments/test/xpcshell/test_telemetry.js
deleted file mode 100644
index 02bd15d2b..000000000
--- a/browser/experiments/test/xpcshell/test_telemetry.js
+++ /dev/null
@@ -1,294 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource://gre/modules/TelemetryLog.jsm");
-var bsp = Cu.import("resource:///modules/experiments/Experiments.jsm");
-
-
-const MANIFEST_HANDLER = "manifests/handler";
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
-
-
-var gHttpServer = null;
-var gHttpRoot = null;
-var gDataRoot = null;
-var gPolicy = null;
-var gManifestObject = null;
-var gManifestHandlerURI = null;
-
-const TLOG = bsp.TELEMETRY_LOG;
-
-function checkEvent(event, id, data)
-{
- do_print("Checking message " + id);
- Assert.equal(event[0], id, "id should match");
- Assert.ok(event[1] > 0, "timestamp should be greater than 0");
-
- if (data === undefined) {
- Assert.equal(event.length, 2, "event array should have 2 entries");
- } else {
- Assert.equal(event.length, data.length + 2, "event entry count should match expected count");
- for (var i = 0; i < data.length; ++i) {
- Assert.equal(typeof(event[i + 2]), "string", "event entry should be a string");
- Assert.equal(event[i + 2], data[i], "event entry should match expected entry");
- }
- }
-}
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function* test_setup() {
- loadAddonManager();
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let port = gHttpServer.identity.primaryPort;
- gHttpRoot = "http://localhost:" + port + "/";
- gDataRoot = gHttpRoot + "data/";
- gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
- gHttpServer.registerDirectory("/data/", do_get_cwd());
- gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
- response.setStatusLine(null, 200, "OK");
- response.write(JSON.stringify(gManifestObject));
- response.processAsync();
- response.finish();
- });
- do_register_cleanup(() => gHttpServer.stop(() => {}));
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
- Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
- Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
- Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
-
- gPolicy = new Experiments.Policy();
- let dummyTimer = { cancel: () => {}, clear: () => {} };
- patchPolicy(gPolicy, {
- updatechannel: () => "nightly",
- oneshotTimer: (callback, timeout, thisObj, name) => dummyTimer,
- });
-
- yield removeCacheFile();
-});
-
-// Test basic starting and stopping of experiments.
-
-add_task(function* test_telemetryBasics() {
- // Check TelemetryLog instead of TelemetrySession.getPayload().log because
- // TelemetrySession gets Experiments.instance() and side-effects log entries.
-
- let expectedLogLength = 0;
-
- // Dates the following tests are based on.
-
- let baseDate = new Date(2014, 5, 1, 12);
- let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
- let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
- let startDate2 = futureDate(baseDate, 150 * MS_IN_ONE_DAY);
- let endDate2 = futureDate(baseDate, 200 * MS_IN_ONE_DAY);
-
- // The manifest data we test with.
-
- gManifestObject = {
- "version": 1,
- experiments: [
- {
- id: EXPERIMENT1_ID,
- xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
- xpiHash: EXPERIMENT1_XPI_SHA1,
- startTime: dateToSeconds(startDate1),
- endTime: dateToSeconds(endDate1),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- {
- id: EXPERIMENT2_ID,
- xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
- xpiHash: EXPERIMENT2_XPI_SHA1,
- startTime: dateToSeconds(startDate2),
- endTime: dateToSeconds(endDate2),
- maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
- appName: ["XPCShell"],
- channel: ["nightly"],
- },
- ],
- };
-
- let experiments = new Experiments.Experiments(gPolicy);
-
- // Trigger update, clock set to before any activation.
- // Use updateManifest() to provide for coverage of that path.
-
- let now = baseDate;
- defineNow(gPolicy, now);
-
- yield experiments.updateManifest();
- let list = yield experiments.getExperiments();
- Assert.equal(list.length, 0, "Experiment list should be empty.");
-
- expectedLogLength += 2;
- let log = TelemetryLog.entries();
- do_print("Telemetry log: " + JSON.stringify(log));
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-2], TLOG.ACTIVATION_KEY,
- [TLOG.ACTIVATION.REJECTED, EXPERIMENT1_ID, "startTime"]);
- checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
- [TLOG.ACTIVATION.REJECTED, EXPERIMENT2_ID, "startTime"]);
-
- // Trigger update, clock set for experiment 1 to start.
-
- now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
-
- yield experiments.updateManifest();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
-
- expectedLogLength += 1;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries. Got " + log.toSource());
- checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
- [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT1_ID]);
-
- // Trigger update, clock set for experiment 1 to stop.
-
- now = futureDate(endDate1, 1000);
- defineNow(gPolicy, now);
-
- yield experiments.updateManifest();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
-
- expectedLogLength += 2;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-2], TLOG.TERMINATION_KEY,
- [TLOG.TERMINATION.EXPIRED, EXPERIMENT1_ID]);
- checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
- [TLOG.ACTIVATION.REJECTED, EXPERIMENT2_ID, "startTime"]);
-
- // Trigger update, clock set for experiment 2 to start with invalid hash.
-
- now = startDate2;
- defineNow(gPolicy, now);
- gManifestObject.experiments[1].xpiHash = "sha1:0000000000000000000000000000000000000000";
-
- yield experiments.updateManifest();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 1, "Experiment list should have 1 entries.");
-
- expectedLogLength += 1;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
- [TLOG.ACTIVATION.INSTALL_FAILURE, EXPERIMENT2_ID]);
-
- // Trigger update, clock set for experiment 2 to properly start now.
-
- now = futureDate(now, MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[1].xpiHash = EXPERIMENT2_XPI_SHA1;
-
- yield experiments.updateManifest();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
-
- expectedLogLength += 1;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
- [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT2_ID]);
-
- // Fake user uninstall of experiment via add-on manager.
-
- now = futureDate(now, MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
-
- yield experiments.disableExperiment(TLOG.TERMINATION.ADDON_UNINSTALLED);
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
-
- expectedLogLength += 1;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
- [TLOG.TERMINATION.ADDON_UNINSTALLED, EXPERIMENT2_ID]);
-
- // Trigger update with experiment 1a ready to start.
-
- now = futureDate(now, MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].id = EXPERIMENT3_ID;
- gManifestObject.experiments[0].endTime = dateToSeconds(futureDate(now, 50 * MS_IN_ONE_DAY));
-
- yield experiments.updateManifest();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 3, "Experiment list should have 3 entries.");
-
- expectedLogLength += 1;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
- [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT3_ID]);
-
- // Trigger disable of an experiment via the API.
-
- now = futureDate(now, MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
-
- yield experiments.disableExperiment(TLOG.TERMINATION.FROM_API);
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 3, "Experiment list should have 3 entries.");
-
- expectedLogLength += 1;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
- [TLOG.TERMINATION.FROM_API, EXPERIMENT3_ID]);
-
- // Trigger update with experiment 1a ready to start.
-
- now = futureDate(now, MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].id = EXPERIMENT4_ID;
- gManifestObject.experiments[0].endTime = dateToSeconds(futureDate(now, 50 * MS_IN_ONE_DAY));
-
- yield experiments.updateManifest();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 4, "Experiment list should have 4 entries.");
-
- expectedLogLength += 1;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
- [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT4_ID]);
-
- // Trigger experiment termination by something other than expiry via the manifest.
-
- now = futureDate(now, MS_IN_ONE_DAY);
- defineNow(gPolicy, now);
- gManifestObject.experiments[0].os = "Plan9";
-
- yield experiments.updateManifest();
- list = yield experiments.getExperiments();
- Assert.equal(list.length, 4, "Experiment list should have 4 entries.");
-
- expectedLogLength += 1;
- log = TelemetryLog.entries();
- Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
- checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
- [TLOG.TERMINATION.RECHECK, EXPERIMENT4_ID, "os"]);
-
- // Cleanup.
-
- yield promiseRestartManager();
- yield removeCacheFile();
-});
diff --git a/browser/experiments/test/xpcshell/test_telemetry_disabled.js b/browser/experiments/test/xpcshell/test_telemetry_disabled.js
deleted file mode 100644
index 74f85ccfc..000000000
--- a/browser/experiments/test/xpcshell/test_telemetry_disabled.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-Cu.import("resource:///modules/experiments/Experiments.jsm");
-
-add_test(function test_experiments_activation() {
- do_get_profile();
- loadAddonManager();
-
- Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
- Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
-
- let experiments = Experiments.instance();
- Assert.ok(!experiments.enabled, "Experiments must be disabled if Telemetry is disabled.");
-
- // TODO: Test that Experiments are turned back on when bug 1232648 lands.
-
- run_next_test();
-});
diff --git a/browser/experiments/test/xpcshell/test_upgrade.js b/browser/experiments/test/xpcshell/test_upgrade.js
deleted file mode 100644
index f094a406d..000000000
--- a/browser/experiments/test/xpcshell/test_upgrade.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-Cu.import("resource:///modules/experiments/Experiments.jsm");
-
-var cacheData = {
- _enabled: true,
- _manifestData: {
- id: "foobartestid",
- xpiURL: "http://example.com/foo.xpi",
- xpiHash: "sha256:abcde",
- startTime: 0,
- endTime: 2000000000,
- maxActiveSeconds: 40000000,
- appName: "TestApp",
- channel: "test-foo",
- },
- _needsUpdate: false,
- _randomValue: 0.5,
- _failedStart: false,
- _name: "Foo",
- _description: "Foobar",
- _homepageURL: "",
- _addonId: "foo@test",
- _startDate: 0,
- _endDate: 2000000000,
- _branch: null
-};
-
-add_task(function* test_valid() {
- let e = new Experiments.ExperimentEntry();
- Assert.ok(e.initFromCacheData(cacheData));
- Assert.ok(e.enabled);
-});
-
-add_task(function* test_upgrade() {
- let e = new Experiments.ExperimentEntry();
- delete cacheData._branch;
- Assert.ok(e.initFromCacheData(cacheData));
- Assert.ok(e.enabled);
-});
-
-add_task(function* test_missing() {
- let e = new Experiments.ExperimentEntry();
- delete cacheData._name;
- Assert.ok(!e.initFromCacheData(cacheData));
-});
-
-function run_test() {
- run_next_test();
-}
diff --git a/browser/experiments/test/xpcshell/xpcshell.ini b/browser/experiments/test/xpcshell/xpcshell.ini
deleted file mode 100644
index 5921c9c47..000000000
--- a/browser/experiments/test/xpcshell/xpcshell.ini
+++ /dev/null
@@ -1,31 +0,0 @@
-[DEFAULT]
-head = head.js
-tail =
-tags = addons
-firefox-appdir = browser
-skip-if = toolkit == 'android'
-support-files =
- experiments_1.manifest
- experiment-1.xpi
- experiment-1a.xpi
- experiment-2.xpi
- experiment-racybranch.xpi
- !/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js
-generated-files =
- experiment-1.xpi
- experiment-1a.xpi
- experiment-2.xpi
- experiment-racybranch.xpi
-
-[test_activate.js]
-[test_api.js]
-[test_cache.js]
-[test_cacherace.js]
-[test_conditions.js]
-[test_disableExperiments.js]
-[test_fetch.js]
-[test_telemetry.js]
-[test_telemetry_disabled.js]
-[test_previous_provider.js]
-[test_upgrade.js]
-[test_nethang_bug1012924.js]
diff --git a/browser/modules/AboutHome.jsm b/browser/modules/AboutHome.jsm
index 01cbafba9..8c0fc4c15 100644
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -24,17 +24,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
-// Url to fetch snippets, in the urlFormatter service format.
-const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
-
-// Should be bumped up if the snippets content format changes.
-const STARTPAGE_VERSION = 4;
+// Should be bumped up if any data content format changes.
+const STARTPAGE_VERSION = 5;
this.AboutHomeUtils = {
- get snippetsVersion() {
- return STARTPAGE_VERSION;
- },
-
/*
* showKnowYourRights - Determines if the user should be shown the
* about:rights notification. The notification should *not* be shown if
@@ -77,16 +70,6 @@ this.AboutHomeUtils = {
};
/**
- * Returns the URL to fetch snippets from, in the urlFormatter service format.
- */
-XPCOMUtils.defineLazyGetter(AboutHomeUtils, "snippetsURL", function() {
- let updateURL = Services.prefs
- .getCharPref(SNIPPETS_URL_PREF)
- .replace("%STARTPAGE_VERSION%", STARTPAGE_VERSION);
- return Services.urlFormatter.formatURL(updateURL);
-});
-
-/**
* This code provides services to the about:home page. Whenever
* about:home needs to do something chrome-privileged, it sends a
* message that's handled here.
@@ -169,9 +152,7 @@ var AboutHome = {
ss.promiseInitialized.then(function() {
let data = {
showRestoreLastSession: ss.canRestoreLastSession,
- snippetsURL: AboutHomeUtils.snippetsURL,
- showKnowYourRights: AboutHomeUtils.showKnowYourRights,
- snippetsVersion: AboutHomeUtils.snippetsVersion,
+ showKnowYourRights: AboutHomeUtils.showKnowYourRights
};
if (AboutHomeUtils.showKnowYourRights) {
diff --git a/browser/moz.build b/browser/moz.build
index a691aeef2..0985148c0 100644
--- a/browser/moz.build
+++ b/browser/moz.build
@@ -11,7 +11,6 @@ SPHINX_TREES['browser'] = 'docs'
DIRS += [
'base',
'components',
- 'experiments',
'fonts',
'locales',
'modules',
diff --git a/build/directive4.py b/build/directive4.py
index 8f05eeed5..dd8c111cf 100644
--- a/build/directive4.py
+++ b/build/directive4.py
@@ -49,6 +49,7 @@ if ('MOZ_OFFICIAL_BRANDING' in listConfig) or (strBrandingDirectory.endswith("br
# Applies to Pale Moon Only
if 'MC_PALEMOON' in listConfig:
listViolations += [
+ 'MOZ_EME',
'MOZ_WEBRTC'
]
diff --git a/devtools/client/commandline/test/browser_cmd_csscoverage_startstop.js b/devtools/client/commandline/test/browser_cmd_csscoverage_startstop.js
index 2bdb86d86..c48136989 100644
--- a/devtools/client/commandline/test/browser_cmd_csscoverage_startstop.js
+++ b/devtools/client/commandline/test/browser_cmd_csscoverage_startstop.js
@@ -4,6 +4,7 @@
// Tests that the addon commands works as they should
const csscoverage = require("devtools/shared/fronts/csscoverage");
+const {gDevTools} = require("devtools/client/framework/devtools");
const PAGE_1 = TEST_BASE_HTTPS + "browser_cmd_csscoverage_page1.html";
const PAGE_2 = TEST_BASE_HTTPS + "browser_cmd_csscoverage_page2.html";
diff --git a/devtools/client/framework/browser-menus.js b/devtools/client/framework/browser-menus.js
index 3d6c4def6..15d2ec1b6 100644
--- a/devtools/client/framework/browser-menus.js
+++ b/devtools/client/framework/browser-menus.js
@@ -156,6 +156,12 @@ function createToolMenuElements(toolDefinition, doc) {
});
}
+ let appmenuitem = createMenuItem({
+ doc,
+ id: "appmenuitem_" + id,
+ label: toolDefinition.menuLabel || toolDefinition.label
+ });
+
let menuitem = createMenuItem({
doc,
id: "menuitem_" + id,
@@ -170,6 +176,7 @@ function createToolMenuElements(toolDefinition, doc) {
return {
key,
+ appmenuitem,
menuitem
};
}
@@ -186,12 +193,24 @@ function createToolMenuElements(toolDefinition, doc) {
* The tool definition after which the tool menu item is to be added.
*/
function insertToolMenuElements(doc, toolDefinition, prevDef) {
- let { key, menuitem } = createToolMenuElements(toolDefinition, doc);
+ let { key, appmenuitem, menuitem } = createToolMenuElements(toolDefinition, doc);
if (key) {
attachKeybindingsToBrowser(doc, key);
}
+ let amp;
+ if (prevDef) {
+ let menuitem = doc.getElementById("appmenuitem_" + prevDef.id);
+ ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null;
+ } else {
+ ref = doc.getElementById("appmenu_devtools_separator");
+ }
+
+ if (ref) {
+ amp.parentNode.insertBefore(menuitem, ref);
+ }
+
let ref;
if (prevDef) {
let menuitem = doc.getElementById("menuitem_" + prevDef.id);
@@ -220,6 +239,11 @@ function removeToolFromMenu(toolId, doc) {
key.remove();
}
+ let appmenuitem = doc.getElementById("appmenuitem_" + toolId);
+ if (appmenuitem) {
+ appmenuitem.remove();
+ }
+
let menuitem = doc.getElementById("menuitem_" + toolId);
if (menuitem) {
menuitem.remove();
@@ -235,6 +259,7 @@ exports.removeToolFromMenu = removeToolFromMenu;
*/
function addAllToolsToMenu(doc) {
let fragKeys = doc.createDocumentFragment();
+ let fragAppMenuItems = doc.createDocumentFragment();
let fragMenuItems = doc.createDocumentFragment();
for (let toolDefinition of gDevTools.getToolDefinitionArray()) {
@@ -251,11 +276,17 @@ function addAllToolsToMenu(doc) {
if (elements.key) {
fragKeys.appendChild(elements.key);
}
+ fragAppMenuItems.appendChild(elements.appmenuitem);
fragMenuItems.appendChild(elements.menuitem);
}
attachKeybindingsToBrowser(doc, fragKeys);
+ let amps = doc.getElementById("appmenu_devtools_separator");
+ if (amps) {
+ amps.parentNode.insertBefore(fragAppMenuItems, amps);
+ }
+
let mps = doc.getElementById("menu_devtools_separator");
if (mps) {
mps.parentNode.insertBefore(fragMenuItems, mps);
@@ -270,18 +301,28 @@ function addAllToolsToMenu(doc) {
*/
function addTopLevelItems(doc) {
let keys = doc.createDocumentFragment();
+ let appmenuItems = doc.createDocumentFragment();
let menuItems = doc.createDocumentFragment();
let { menuitems } = require("../menus");
for (let item of menuitems) {
if (item.separator) {
+ let appseparator = doc.createElement("menuseparator");
+ appseparator.id = "app" + item.id;
let separator = doc.createElement("menuseparator");
separator.id = item.id;
+ appmenuItems.appendChild(appseparator);
menuItems.appendChild(separator);
} else {
let { id, l10nKey } = item;
// Create a <menuitem>
+ let appmenuitem = createMenuItem({
+ doc,
+ id: "app" + id,
+ label: l10n(l10nKey + ".label"),
+ isCheckbox: item.checkbox
+ });
let menuitem = createMenuItem({
doc,
id,
@@ -289,7 +330,9 @@ function addTopLevelItems(doc) {
accesskey: l10n(l10nKey + ".accesskey"),
isCheckbox: item.checkbox
});
+ appmenuitem.addEventListener("command", item.oncommand);
menuitem.addEventListener("command", item.oncommand);
+ appmenuItems.appendChild(appmenuitem);
menuItems.appendChild(menuitem);
if (item.key && l10nKey) {
@@ -330,6 +373,9 @@ function addTopLevelItems(doc) {
for (let node of keys.children) {
nodes.push(node);
}
+ for (let node of appmenuItems.children) {
+ nodes.push(node);
+ }
for (let node of menuItems.children) {
nodes.push(node);
}
@@ -337,15 +383,27 @@ function addTopLevelItems(doc) {
attachKeybindingsToBrowser(doc, keys);
+ let appmenu = doc.getElementById("appmenu_webDeveloper_popup");
+ if (appmenu) {
+ appmenu.appendChild(appmenuItems);
+
+ // There is still "Page Source" menuitem hardcoded into browser.xul. Instead
+ // of manually inserting everything around it, move it to the expected
+ // position.
+ let appmenu_pageSource = doc.getElementById("appmenu_pageSource");
+ let appmenu_endSeparator = doc.getElementById("appmenu_devToolsEndSeparator");
+ appmenu.insertBefore(appmenu_pageSource, appmenu_endSeparator);
+ }
+
let menu = doc.getElementById("menuWebDeveloperPopup");
menu.appendChild(menuItems);
// There is still "Page Source" menuitem hardcoded into browser.xul. Instead
// of manually inserting everything around it, move it to the expected
// position.
- let pageSource = doc.getElementById("menu_pageSource");
- let endSeparator = doc.getElementById("devToolsEndSeparator");
- menu.insertBefore(pageSource, endSeparator);
+ let menu_pageSource = doc.getElementById("menu_pageSource");
+ let menu_endSeparator = doc.getElementById("menu_devToolsEndSeparator");
+ menu.insertBefore(menu_pageSource, menu_endSeparator);
}
/**
diff --git a/devtools/client/framework/devtools-browser.js b/devtools/client/framework/devtools-browser.js
index b9f4d92ba..fe40cdb32 100644
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -8,7 +8,7 @@
* This is the main module loaded in Firefox desktop that handles browser
* windows and coordinates devtools around each window.
*
- * This module is loaded lazily by devtools-clhandler.js, once the first
+ * This module is loaded lazily by devtools-startup.js, once the first
* browser window is ready (i.e. fired browser-delayed-startup-finished event)
**/
diff --git a/devtools/client/framework/test/browser_keybindings_01.js b/devtools/client/framework/test/browser_keybindings_01.js
index 4e4effb07..134fb127c 100644
--- a/devtools/client/framework/test/browser_keybindings_01.js
+++ b/devtools/client/framework/test/browser_keybindings_01.js
@@ -8,6 +8,9 @@
const TEST_URL = "data:text/html,<html><head><title>Test for the " +
"highlighter keybindings</title></head><body>" +
"<h1>Keybindings!</h1></body></html>"
+
+const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
+
function test()
{
waitForExplicitFinish();
diff --git a/devtools/client/menus.js b/devtools/client/menus.js
index 7e36839da..1d2168967 100644
--- a/devtools/client/menus.js
+++ b/devtools/client/menus.js
@@ -183,9 +183,9 @@ exports.menuitems = [
}
},
{ separator: true,
- id: "devToolsEndSeparator"
+ id: "menu_devToolsEndSeparator"
},
- { id: "getMoreDevtools",
+ { id: "menu_getMoreDevtools",
l10nKey: "getMoreDevtoolsCmd",
oncommand(event) {
let window = event.target.ownerDocument.defaultView;
diff --git a/devtools/client/scratchpad/test/head.js b/devtools/client/scratchpad/test/head.js
index 15619a169..955c037d7 100644
--- a/devtools/client/scratchpad/test/head.js
+++ b/devtools/client/scratchpad/test/head.js
@@ -9,6 +9,7 @@ const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
const {ScratchpadManager} = Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", {});
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const {gDevTools} = require("devtools/client/framework/devtools");
const Services = require("Services");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const flags = require("devtools/shared/flags");
diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/head.js b/devtools/client/webconsole/new-console-output/test/mochitest/head.js
index b71eaec4f..049f3d1ce 100644
--- a/devtools/client/webconsole/new-console-output/test/mochitest/head.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/head.js
@@ -14,6 +14,7 @@ Services.scriptloader.loadSubScript(
var {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
const WEBCONSOLE_STRINGS_URI = "devtools/client/locales/webconsole.properties";
+var {HUDService} = require("devtools/client/webconsole/hudservice");
var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI);
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
diff --git a/docshell/base/nsAboutRedirector.cpp b/docshell/base/nsAboutRedirector.cpp
index e7d362864..845669951 100644
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -42,12 +42,12 @@ static RedirEntry kRedirMap[] = {
{
"buildconfig", "chrome://global/content/buildconfig.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::MAKE_LINKABLE
+ nsIAboutModule::MAKE_LINKABLE
},
{
"checkerboard", "chrome://global/content/aboutCheckerboard.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::ALLOW_SCRIPT
+ nsIAboutModule::ALLOW_SCRIPT
},
{ "config", "chrome://global/content/config.xul", 0 },
#ifdef MOZ_CRASHREPORTER
@@ -67,7 +67,7 @@ static RedirEntry kRedirMap[] = {
{
"license", "chrome://global/content/license.html",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::MAKE_LINKABLE
+ nsIAboutModule::MAKE_LINKABLE
},
{
"logo", "chrome://branding/content/about.png",
@@ -75,6 +75,13 @@ static RedirEntry kRedirMap[] = {
// Linkable for testing reasons.
nsIAboutModule::MAKE_LINKABLE
},
+#ifdef MOZ_PHOENIX
+ {
+ "logopage", "chrome://global/content/logopage.xhtml",
+ nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ },
+#endif
{
"memory", "chrome://global/content/aboutMemory.xhtml",
nsIAboutModule::ALLOW_SCRIPT
@@ -86,9 +93,9 @@ static RedirEntry kRedirMap[] = {
{
"neterror", "chrome://global/content/netError.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
- nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
+ nsIAboutModule::ALLOW_SCRIPT |
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
},
{
"networking", "chrome://global/content/aboutNetworking.xhtml",
@@ -97,7 +104,7 @@ static RedirEntry kRedirMap[] = {
{
"newaddon", "chrome://mozapps/content/extensions/newaddon.xul",
nsIAboutModule::ALLOW_SCRIPT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT
},
{
"performance", "chrome://global/content/aboutPerformance.xhtml",
@@ -124,10 +131,10 @@ static RedirEntry kRedirMap[] = {
{
"srcdoc", "about:blank",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
- nsIAboutModule::HIDE_FROM_ABOUTABOUT |
- // Needs to be linkable so content can touch its own srcdoc frames
- nsIAboutModule::MAKE_LINKABLE |
- nsIAboutModule::URI_CAN_LOAD_IN_CHILD
+ nsIAboutModule::HIDE_FROM_ABOUTABOUT |
+ // Needs to be linkable so content can touch its own srcdoc frames
+ nsIAboutModule::MAKE_LINKABLE |
+ nsIAboutModule::URI_CAN_LOAD_IN_CHILD
},
{
"support", "chrome://global/content/aboutSupport.xhtml",
diff --git a/docshell/build/nsDocShellModule.cpp b/docshell/build/nsDocShellModule.cpp
index d43c305f9..e37ced4a9 100644
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -174,6 +174,9 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
#endif
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "license", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "logo", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
+#ifdef MOZ_PHOENIX
+ { NS_ABOUT_MODULE_CONTRACTID_PREFIX "logopage", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
+#endif
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "mozilla", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
index 40fd008b9..710c7a76c 100644
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -1159,7 +1159,7 @@ bool
ModuleBuilder::processExport(frontend::ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_DEFAULT));
- MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY);
+ MOZ_ASSERT(pn->getArity() == (pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY));
bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT;
ParseNode* kid = isDefault ? pn->pn_left : pn->pn_kid;
diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
index eef6fd7ec..beff58e13 100644
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2140,7 +2140,7 @@ ASTSerializer::exportDeclaration(ParseNode* pn, MutableHandleValue dst)
MOZ_ASSERT(pn->isKind(PNK_EXPORT) ||
pn->isKind(PNK_EXPORT_FROM) ||
pn->isKind(PNK_EXPORT_DEFAULT));
- MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY);
+ MOZ_ASSERT(pn->getArity() == (pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY));
MOZ_ASSERT_IF(pn->isKind(PNK_EXPORT_FROM), pn->pn_right->isKind(PNK_STRING));
RootedValue decl(cx, NullValue());
diff --git a/old-configure.in b/old-configure.in
index 7c61ebf74..bd65c9940 100644
--- a/old-configure.in
+++ b/old-configure.in
@@ -5820,6 +5820,7 @@ MOZ_BRANDING_DIRECTORY=$MOZ_BRANDING_DIRECTORY
MC_BASILISK=$MC_BASILISK
MC_PALEMOON=$MC_PALEMOON
MOZ_SANDBOX=$MOZ_SANDBOX
+MOZ_EME=$MOZ_EME
MOZ_WEBRTC=$MOZ_WEBRTC
MOZ_SYSTEM_LIBEVENT=$MOZ_SYSTEM_LIBEVENT
MOZ_SYSTEM_NSS=$MOZ_SYSTEM_NSS
diff --git a/toolkit/components/protobuf/moz.build b/toolkit/components/protobuf/moz.build
index b5015eb67..8cca3514c 100644
--- a/toolkit/components/protobuf/moz.build
+++ b/toolkit/components/protobuf/moz.build
@@ -117,10 +117,13 @@ DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True
# Suppress warnings in third-party code.
if CONFIG['GNU_CXX']:
CXXFLAGS += [
- '-Wno-null-conversion',
'-Wno-return-type',
'-Wno-sign-compare',
]
+ if CONFIG['CLANG_CXX']:
+ CXXFLAGS += [
+ '-Wno-null-conversion',
+ ]
elif CONFIG['_MSC_VER']:
CXXFLAGS += [
'-wd4005', # 'WIN32_LEAN_AND_MEAN' : macro redefinition
diff --git a/toolkit/components/telemetry/TelemetryEnvironment.jsm b/toolkit/components/telemetry/TelemetryEnvironment.jsm
index 2f4ac81ba..910d804ae 100644
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -153,7 +153,6 @@ const DEFAULT_ENVIRONMENT_PREFS = new Map([
["dom.ipc.plugins.enabled", {what: RECORD_PREF_VALUE}],
["dom.ipc.processCount", {what: RECORD_PREF_VALUE, requiresRestart: true}],
["dom.max_script_run_time", {what: RECORD_PREF_VALUE}],
- ["experiments.manifest.uri", {what: RECORD_PREF_VALUE}],
["extensions.autoDisableScopes", {what: RECORD_PREF_VALUE}],
["extensions.enabledScopes", {what: RECORD_PREF_VALUE}],
["extensions.blocklist.enabled", {what: RECORD_PREF_VALUE}],
@@ -209,7 +208,6 @@ const PREF_E10S_COHORT = "e10s.rollout.cohort";
const COMPOSITOR_CREATED_TOPIC = "compositor:created";
const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC = "distribution-customization-complete";
-const EXPERIMENTS_CHANGED_TOPIC = "experiments-changed";
const GFX_FEATURES_READY_TOPIC = "gfx-features-ready";
const SEARCH_ENGINE_MODIFIED_TOPIC = "browser-search-engine-modified";
const SEARCH_SERVICE_TOPIC = "browser-search-service";
@@ -465,7 +463,6 @@ EnvironmentAddonBuilder.prototype = {
watchForChanges: function() {
this._loaded = true;
AddonManager.addAddonListener(this);
- Services.obs.addObserver(this, EXPERIMENTS_CHANGED_TOPIC, false);
},
// AddonListener
@@ -490,7 +487,6 @@ EnvironmentAddonBuilder.prototype = {
// nsIObserver
observe: function (aSubject, aTopic, aData) {
this._environment._log.trace("observe - Topic " + aTopic);
- this._checkForChanges("experiment-changed");
},
_checkForChanges: function(changeReason) {
@@ -515,7 +511,6 @@ EnvironmentAddonBuilder.prototype = {
_shutdownBlocker: function() {
if (this._loaded) {
AddonManager.removeAddonListener(this);
- Services.obs.removeObserver(this, EXPERIMENTS_CHANGED_TOPIC);
}
return this._pendingTask;
},
@@ -545,7 +540,6 @@ EnvironmentAddonBuilder.prototype = {
theme: yield this._getActiveTheme(),
activePlugins: this._getActivePlugins(),
activeGMPlugins: yield this._getActiveGMPlugins(),
- activeExperiment: this._getActiveExperiment(),
persona: personaId,
};
@@ -718,29 +712,7 @@ EnvironmentAddonBuilder.prototype = {
}
return activeGMPlugins;
- }),
-
- /**
- * Get the active experiment data in object form.
- * @return Object containing the active experiment data.
- */
- _getActiveExperiment: function () {
- let experimentInfo = {};
- try {
- let scope = {};
- Cu.import("resource:///modules/experiments/Experiments.jsm", scope);
- let experiments = scope.Experiments.instance();
- let activeExperiment = experiments.getActiveExperimentID();
- if (activeExperiment) {
- experimentInfo.id = activeExperiment;
- experimentInfo.branch = experiments.getActiveExperimentBranch();
- }
- } catch (e) {
- // If this is not Firefox, the import will fail.
- }
-
- return experimentInfo;
- },
+ })
};
function EnvironmentCache() {
diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml
index e2885c8b8..a043dfe3b 100644
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -504,39 +504,6 @@
</table>
- <h2 class="major-section">
- &aboutSupport.experimentsTitle;
- </h2>
-
- <table>
- <thead>
- <tr>
- <th>
- &aboutSupport.experimentName;
- </th>
- <th>
- &aboutSupport.experimentId;
- </th>
- <th>
- &aboutSupport.experimentDescription;
- </th>
- <th>
- &aboutSupport.experimentActive;
- </th>
- <th>
- &aboutSupport.experimentEndDate;
- </th>
- <th>
- &aboutSupport.experimentHomepage;
- </th>
- <th>
- &aboutSupport.experimentBranch;
- </th>
- </tr>
- </thead>
- <tbody id="experiments-tbody">
- </tbody>
- </table>
<!-- - - - - - - - - - - - - - - - - - - - - -->
#if defined(MOZ_SANDBOX)
diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn
index f1a222a58..946e499f1 100644
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -54,8 +54,12 @@ toolkit.jar:
#endif
content/global/filepicker.properties
content/global/globalOverlay.js
+ content/global/memoriam.xhtml
+* content/global/mozilla.css
content/global/mozilla.xhtml
+#ifdef MOZ_PHOENIX
content/global/logopage.xhtml
+#endif
content/global/process-content.js
content/global/resetProfile.css
content/global/resetProfile.js
diff --git a/toolkit/content/memoriam.xhtml b/toolkit/content/memoriam.xhtml
new file mode 100644
index 000000000..f1a1b474d
--- /dev/null
+++ b/toolkit/content/memoriam.xhtml
@@ -0,0 +1,76 @@
+<!DOCTYPE html
+[
+ <!ENTITY % directionDTD SYSTEM "chrome://global/locale/global.dtd" >
+ %directionDTD;
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset='utf-8' />
+ <title>Mozilla: In Memoriam</title>
+
+<style>
+html {
+ background: maroon radial-gradient( circle, #a01010 0%, #800000 80%) center center / cover no-repeat;
+ color: white;
+ font-style: italic;
+ text-rendering: optimizeLegibility;
+ min-height: 100%;
+}
+
+#moztext {
+ margin-top: 15%;
+ font-size: 1.1em;
+ font-family: serif;
+ text-align: center;
+ line-height: 1.5;
+}
+
+#from {
+ font-size: 1.0em;
+ font-family: serif;
+ text-align: right;
+}
+
+em {
+ font-size: 1.3em;
+ line-height: 0;
+}
+
+a {
+ text-decoration: none;
+ color: white;
+}
+</style>
+</head>
+
+<body dir="&locale.dir;">
+
+<section>
+ <p id="moztext">
+ <h1>Mozilla: In Memoriam</h1>
+ <br/>
+ Dedicated to the tireless developers who have come and gone.<br/>
+ To those who have put their heart and soul into Mozilla products.<br/>
+ To those who have seen their good intentions and hard work squandered.<br/>
+ To those who really cared about the user, and cared about usability.<br/>
+ To those who truly understood us and desired freedom, but were unheard.<br/>
+ To those who knew that change is inevitable, but loss of vision is not.<br/>
+ To those who were forced to give up the good fight.<br/>
+ <br/>
+ <em>Thank you.</em> &brandFullName; would not have been possible without you.<br/>
+ <br/>
+ </p>
+
+ <p id="from">
+ </p>
+</section>
+
+</body>
+</html> \ No newline at end of file
diff --git a/toolkit/content/mozilla.css b/toolkit/content/mozilla.css
new file mode 100644
index 000000000..d5eae6415
--- /dev/null
+++ b/toolkit/content/mozilla.css
@@ -0,0 +1,36 @@
+html {
+%ifdef MC_PALEMOON
+ background: #333399 radial-gradient( circle at 75% 25%, #6666b0 0%, #333399 40%, #111177 80%) center center / cover no-repeat;
+%else
+ background: maroon radial-gradient( circle, #a01010 0%, #800000 80%) center center / cover no-repeat;
+%endif
+
+ color: white;
+ font-style: italic;
+ text-rendering: optimizeLegibility;
+ min-height: 100%;
+}
+
+#moztext {
+ margin-top: 15%;
+ font-size: 1.1em;
+ font-family: serif;
+ text-align: center;
+ line-height: 1.5;
+}
+
+#from {
+ font-size: 1.95em;
+ font-family: serif;
+ text-align: right;
+}
+
+em {
+ font-size: 1.3em;
+ line-height: 0;
+}
+
+a {
+ text-decoration: none;
+ color: white;
+} \ No newline at end of file
diff --git a/toolkit/content/mozilla.xhtml b/toolkit/content/mozilla.xhtml
index 2acfc9f5d..8c79b5ff9 100644
--- a/toolkit/content/mozilla.xhtml
+++ b/toolkit/content/mozilla.xhtml
@@ -15,39 +15,8 @@
<meta charset='utf-8' />
<title>&chronicles.title.55.2;</title>
-<style>
-html {
- background: maroon radial-gradient( circle, #a01010 0%, #800000 80%) center center / cover no-repeat;
- color: white;
- font-style: italic;
- text-rendering: optimizeLegibility;
- min-height: 100%;
-}
-
-#moztext {
- margin-top: 15%;
- font-size: 1.1em;
- font-family: serif;
- text-align: center;
- line-height: 1.5;
-}
-
-#from {
- font-size: 1.95em;
- font-family: serif;
- text-align: right;
-}
-
-em {
- font-size: 1.3em;
- line-height: 0;
-}
-
-a {
- text-decoration: none;
- color: white;
-}
-</style>
+ <link rel="stylesheet" href="chrome://global/content/mozilla.css"
+ type="text/css"/>
</head>
<body dir="&locale.dir;">
diff --git a/toolkit/modules/Troubleshoot.jsm b/toolkit/modules/Troubleshoot.jsm
index cc545b4c4..f259d9ae9 100644
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -12,13 +12,6 @@ Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
-var Experiments;
-try {
- Experiments = Cu.import("resource:///modules/experiments/Experiments.jsm").Experiments;
-}
-catch (e) {
-}
-
// We use a preferences whitelist to make sure we only show preferences that
// are useful for support and won't compromise the user's privacy. Note that
// entries are *prefixes*: for example, "accessibility." applies to all prefs
@@ -263,18 +256,6 @@ var dataProviders = {
});
},
- experiments: function experiments(done) {
- if (Experiments === undefined) {
- done([]);
- return;
- }
-
- // getExperiments promises experiment history
- Experiments.instance().getExperiments().then(
- experiments => done(experiments)
- );
- },
-
modifiedPreferences: function modifiedPreferences(done) {
done(getPrefList(name => Services.prefs.prefHasUserValue(name)));
},
diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js
index a799eeebb..1d8d5bb0a 100644
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -22,8 +22,6 @@ XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function () {
return Cu.import("resource://gre/modules/devtools/ToolboxProcess.jsm", {}).
BrowserToolboxProcess;
});
-XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
- "resource:///modules/experiments/Experiments.jsm");
const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
@@ -216,23 +214,6 @@ function isDiscoverEnabled() {
return true;
}
-function getExperimentEndDate(aAddon) {
- if (!("@mozilla.org/browser/experiments-service;1" in Cc)) {
- return 0;
- }
-
- if (!aAddon.isActive) {
- return aAddon.endDate;
- }
-
- let experiment = Experiments.instance().getActiveExperiment();
- if (!experiment) {
- return 0;
- }
-
- return experiment.endDate;
-}
-
/**
* Obtain the main DOMWindow for the current context.
*/
@@ -1316,28 +1297,7 @@ var gViewController = {
doCommand: function cmd_neverActivateItem_doCommand(aAddon) {
aAddon.userDisabled = true;
}
- },
-
- cmd_experimentsLearnMore: {
- isEnabled: function cmd_experimentsLearnMore_isEnabled() {
- let mainWindow = getMainWindow();
- return mainWindow && "switchToTabHavingURI" in mainWindow;
- },
- doCommand: function cmd_experimentsLearnMore_doCommand() {
- let url = Services.prefs.getCharPref("toolkit.telemetry.infoURL");
- openOptionsInTab(url);
- },
- },
-
- cmd_experimentsOpenTelemetryPreferences: {
- isEnabled: function cmd_experimentsOpenTelemetryPreferences_isEnabled() {
- return !!getMainWindowWithPreferencesPane();
- },
- doCommand: function cmd_experimentsOpenTelemetryPreferences_doCommand() {
- let mainWindow = getMainWindowWithPreferencesPane();
- mainWindow.openAdvancedPreferences("dataChoicesTab");
- },
- },
+ }
},
supportsCommand: function gVC_supportsCommand(aCommand) {
@@ -1468,10 +1428,6 @@ function createItem(aObj, aIsInstall, aIsRemote) {
// the binding handles the rest
item.setAttribute("value", aObj.id);
- if (aObj.type == "experiment") {
- item.endDate = getExperimentEndDate(aObj);
- }
-
return item;
}
@@ -2679,13 +2635,6 @@ var gListView = {
// the existing item
if (aInstall.existingAddon)
this.removeItem(aInstall, true);
-
- if (aInstall.addon.type == "experiment") {
- let item = this.getListItemForID(aInstall.addon.id);
- if (item) {
- item.endDate = getExperimentEndDate(aInstall.addon);
- }
- }
},
addItem: function gListView_addItem(aObj, aIsInstall) {
@@ -2945,34 +2894,6 @@ var gDetailView = {
}
}
- if (this._addon.type == "experiment") {
- let prefix = "details.experiment.";
- let active = this._addon.isActive;
-
- let stateKey = prefix + "state." + (active ? "active" : "complete");
- let node = document.getElementById("detail-experiment-state");
- node.value = gStrings.ext.GetStringFromName(stateKey);
-
- let now = Date.now();
- let end = getExperimentEndDate(this._addon);
- let days = Math.abs(end - now) / (24 * 60 * 60 * 1000);
-
- let timeKey = prefix + "time.";
- let timeMessage;
- if (days < 1) {
- timeKey += (active ? "endsToday" : "endedToday");
- timeMessage = gStrings.ext.GetStringFromName(timeKey);
- } else {
- timeKey += (active ? "daysRemaining" : "daysPassed");
- days = Math.round(days);
- let timeString = gStrings.ext.GetStringFromName(timeKey);
- timeMessage = PluralForm.get(days, timeString)
- .replace("#1", days);
- }
-
- document.getElementById("detail-experiment-time").value = timeMessage;
- }
-
this.fillSettingsRows(aScrollToPreferences, (function updateView_fillSettingsRows() {
this.updateState();
gViewController.notifyViewChanged();
diff --git a/toolkit/mozapps/extensions/test/browser/browser_experiments.js b/toolkit/mozapps/extensions/test/browser/browser_experiments.js
deleted file mode 100644
index 72d0ca83e..000000000
--- a/toolkit/mozapps/extensions/test/browser/browser_experiments.js
+++ /dev/null
@@ -1,645 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-Components.utils.import("resource://gre/modules/Promise.jsm", this);
-
-let {AddonTestUtils} = Components.utils.import("resource://testing-common/AddonManagerTesting.jsm", {});
-let {HttpServer} = Components.utils.import("resource://testing-common/httpd.js", {});
-
-let gManagerWindow;
-let gCategoryUtilities;
-let gExperiments;
-let gHttpServer;
-
-let gSavedManifestURI;
-let gIsEnUsLocale;
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
-
-function getExperimentAddons() {
- let deferred = Promise.defer();
- AddonManager.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- return deferred.promise;
-}
-
-function getInstallItem() {
- let doc = gManagerWindow.document;
- let view = doc.getElementById("view-port").selectedPanel;
- let list = doc.getElementById("addon-list");
-
- let node = list.firstChild;
- while (node) {
- if (node.getAttribute("status") == "installing") {
- return node;
- }
- node = node.nextSibling;
- }
-
- return null;
-}
-
-function patchPolicy(policy, data) {
- for (let key of Object.keys(data)) {
- Object.defineProperty(policy, key, {
- value: data[key],
- writable: true,
- });
- }
-}
-
-function defineNow(policy, time) {
- patchPolicy(policy, { now: () => new Date(time) });
-}
-
-function openDetailsView(aId) {
- let item = get_addon_element(gManagerWindow, aId);
- Assert.ok(item, "Should have got add-on element.");
- is_element_visible(item, "Add-on element should be visible.");
-
- EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
- EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
-
- let deferred = Promise.defer();
- wait_for_view_load(gManagerWindow, deferred.resolve);
- return deferred.promise;
-}
-
-function clickRemoveButton(addonElement) {
- let btn = gManagerWindow.document.getAnonymousElementByAttribute(addonElement, "anonid", "remove-btn");
- if (!btn) {
- return Promise.reject();
- }
-
- EventUtils.synthesizeMouseAtCenter(btn, { clickCount: 1 }, gManagerWindow);
- let deferred = Promise.defer();
- setTimeout(deferred.resolve, 0);
- return deferred;
-}
-
-function clickUndoButton(addonElement) {
- let btn = gManagerWindow.document.getAnonymousElementByAttribute(addonElement, "anonid", "undo-btn");
- if (!btn) {
- return Promise.reject();
- }
-
- EventUtils.synthesizeMouseAtCenter(btn, { clickCount: 1 }, gManagerWindow);
- let deferred = Promise.defer();
- setTimeout(deferred.resolve, 0);
- return deferred;
-}
-
-add_task(function* initializeState() {
- gManagerWindow = yield open_manager();
- gCategoryUtilities = new CategoryUtilities(gManagerWindow);
-
- registerCleanupFunction(() => {
- Services.prefs.clearUserPref("experiments.enabled");
- if (gHttpServer) {
- gHttpServer.stop(() => {});
- if (gSavedManifestURI !== undefined) {
- Services.prefs.setCharPref("experments.manifest.uri", gSavedManifestURI);
- }
- }
- if (gExperiments) {
- let tmp = {};
- Cu.import("resource:///modules/experiments/Experiments.jsm", tmp);
- gExperiments._policy = new tmp.Experiments.Policy();
- }
- });
-
- let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
- gIsEnUsLocale = chrome.getSelectedLocale("global") == "en-US";
-
- // The Experiments Manager will interfere with us by preventing installs
- // of experiments it doesn't know about. We remove it from the equation
- // because here we are only concerned with core Addon Manager operation,
- // not the superset Experiments Manager has imposed.
- if ("@mozilla.org/browser/experiments-service;1" in Components.classes) {
- let tmp = {};
- Cu.import("resource:///modules/experiments/Experiments.jsm", tmp);
- // There is a race condition between XPCOM service initialization and
- // this test running. We have to initialize the instance first, then
- // uninitialize it to prevent this.
- gExperiments = tmp.Experiments.instance();
- yield gExperiments._mainTask;
- yield gExperiments.uninit();
- }
-});
-
-// On an empty profile with no experiments, the experiment category
-// should be hidden.
-add_task(function* testInitialState() {
- Assert.ok(gCategoryUtilities.get("experiment", false), "Experiment tab is defined.");
- Assert.ok(!gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab hidden by default.");
-});
-
-add_task(function* testExperimentInfoNotVisible() {
- yield gCategoryUtilities.openType("extension");
- let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
- is_element_hidden(el, "Experiment info not visible on other types.");
-});
-
-// If we have an active experiment, we should see the experiments tab
-// and that tab should have some messages.
-add_task(function* testActiveExperiment() {
- let addon = yield install_addon("addons/browser_experiment1.xpi");
-
- Assert.ok(addon.userDisabled, "Add-on is disabled upon initial install.");
- Assert.equal(addon.isActive, false, "Add-on is not active.");
-
- Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible.");
-
- yield gCategoryUtilities.openType("experiment");
- let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
- is_element_visible(el, "Experiment info is visible on experiment tab.");
-});
-
-add_task(function* testExperimentLearnMore() {
- // Actual URL is irrelevant.
- Services.prefs.setCharPref("toolkit.telemetry.infoURL",
- "http://mochi.test:8888/server.js");
-
- yield gCategoryUtilities.openType("experiment");
- let btn = gManagerWindow.document.getElementById("experiments-learn-more");
-
- if (!gUseInContentUI) {
- is_element_hidden(btn, "Learn more button hidden if not using in-content UI.");
- Services.prefs.clearUserPref("toolkit.telemetry.infoURL");
-
- return;
- }
-
- is_element_visible(btn, "Learn more button visible.");
-
- let deferred = Promise.defer();
- window.addEventListener("DOMContentLoaded", function onLoad(event) {
- info("Telemetry privacy policy window opened.");
- window.removeEventListener("DOMContentLoaded", onLoad, false);
-
- let browser = gBrowser.selectedBrowser;
- let expected = Services.prefs.getCharPref("toolkit.telemetry.infoURL");
- Assert.equal(browser.currentURI.spec, expected, "New tab should have loaded privacy policy.");
- browser.contentWindow.close();
-
- Services.prefs.clearUserPref("toolkit.telemetry.infoURL");
-
- deferred.resolve();
- }, false);
-
- info("Opening telemetry privacy policy.");
- EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow);
-
- yield deferred.promise;
-});
-
-add_task(function* testOpenPreferences() {
- yield gCategoryUtilities.openType("experiment");
- let btn = gManagerWindow.document.getElementById("experiments-change-telemetry");
- if (!gUseInContentUI) {
- is_element_hidden(btn, "Change telemetry button not enabled in out of window UI.");
- info("Skipping preferences open test because not using in-content UI.");
- return;
- }
-
- is_element_visible(btn, "Change telemetry button visible in in-content UI.");
-
- let deferred = Promise.defer();
- Services.obs.addObserver(function observer(prefWin, topic, data) {
- Services.obs.removeObserver(observer, "advanced-pane-loaded");
- info("Advanced preference pane opened.");
- executeSoon(function() {
- // We want this test to fail if the preferences pane changes.
- let el = prefWin.document.getElementById("dataChoicesPanel");
- is_element_visible(el);
-
- prefWin.close();
- info("Closed preferences pane.");
-
- deferred.resolve();
- });
- }, "advanced-pane-loaded", false);
-
- info("Loading preferences pane.");
- EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow);
-
- yield deferred.promise;
-});
-
-add_task(function* testButtonPresence() {
- yield gCategoryUtilities.openType("experiment");
- let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
- // Corresponds to the uninstall permission.
- is_element_visible(el, "Remove button is visible.");
- // Corresponds to lack of disable permission.
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
- is_element_hidden(el, "Disable button not visible.");
- // Corresponds to lack of enable permission.
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
- is_element_hidden(el, "Enable button not visible.");
-});
-
-// Remove the add-on we've been testing with.
-add_task(function* testCleanup() {
- yield AddonTestUtils.uninstallAddonByID("test-experiment1@experiments.mozilla.org");
- // Verify some conditions, just in case.
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
-});
-
-// The following tests should ideally live in browser/experiments/. However,
-// they rely on some of the helper functions from head.js, which can't easily
-// be consumed from other directories. So, they live here.
-
-add_task(function* testActivateExperiment() {
- if (!gExperiments) {
- info("Skipping experiments test because that feature isn't available.");
- return;
- }
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let root = "http://localhost:" + gHttpServer.identity.primaryPort + "/";
- gHttpServer.registerPathHandler("/manifest", (request, response) => {
- response.setStatusLine(null, 200, "OK");
- response.write(JSON.stringify({
- "version": 1,
- "experiments": [
- {
- id: "experiment-1",
- xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
- xpiHash: "IRRELEVANT",
- startTime: Date.now() / 1000 - 3600,
- endTime: Date.now() / 1000 + 3600,
- maxActiveSeconds: 600,
- appName: [Services.appinfo.name],
- channel: [gExperiments._policy.updatechannel()],
- },
- ],
- }));
- response.processAsync();
- response.finish();
- });
-
- gSavedManifestURI = Services.prefs.getCharPref("experiments.manifest.uri");
- Services.prefs.setCharPref("experiments.manifest.uri", root + "manifest");
-
- // We need to remove the cache file to help ensure consistent state.
- yield OS.File.remove(gExperiments._cacheFilePath);
-
- Services.prefs.setBoolPref("experiments.enabled", true);
-
- info("Initializing experiments service.");
- yield gExperiments.init();
- info("Experiments service finished first run.");
-
- // Check conditions, just to be sure.
- let experiments = yield gExperiments.getExperiments();
- Assert.equal(experiments.length, 0, "No experiments known to the service.");
-
- // This makes testing easier.
- gExperiments._policy.ignoreHashes = true;
-
- info("Manually updating experiments manifest.");
- yield gExperiments.updateManifest();
- info("Experiments update complete.");
-
- let deferred = Promise.defer();
- gHttpServer.stop(() => {
- gHttpServer = null;
-
- info("getting experiment by ID");
- AddonManager.getAddonByID("test-experiment1@experiments.mozilla.org", (addon) => {
- Assert.ok(addon, "Add-on installed via Experiments manager.");
-
- deferred.resolve();
- });
- });
-
- yield deferred.promise;
-
- Assert.ok(gCategoryUtilities.isTypeVisible, "experiment", "Experiment tab visible.");
- yield gCategoryUtilities.openType("experiment");
- let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
- is_element_visible(el, "Experiment info is visible on experiment tab.");
-});
-
-add_task(function testDeactivateExperiment() {
- if (!gExperiments) {
- return;
- }
-
- // Fake an empty manifest to purge data from previous manifest.
- yield gExperiments._updateExperiments({
- "version": 1,
- "experiments": [],
- });
-
- yield gExperiments.disableExperiment("testing");
-
- // We should have a record of the previously-active experiment.
- let experiments = yield gExperiments.getExperiments();
- Assert.equal(experiments.length, 1, "1 experiment is known.");
- Assert.equal(experiments[0].active, false, "Experiment is not active.");
-
- // We should have a previous experiment in the add-ons manager.
- let deferred = Promise.defer();
- AddonManager.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- let addons = yield deferred.promise;
- Assert.equal(addons.length, 1, "1 experiment add-on known.");
- Assert.ok(addons[0].appDisabled, "It is a previous experiment.");
- Assert.equal(addons[0].id, "experiment-1", "Add-on ID matches expected.");
-
- // Verify the UI looks sane.
-
- Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible.");
- let item = get_addon_element(gManagerWindow, "experiment-1");
- Assert.ok(item, "Got add-on element.");
- Assert.ok(!item.active, "Element should not be active.");
- item.parentNode.ensureElementIsVisible(item);
-
- // User control buttons should not be present because previous experiments
- // should have no permissions.
- let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
- is_element_hidden(el, "Remove button is not visible.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
- is_element_hidden(el, "Disable button is not visible.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
- is_element_hidden(el, "Enable button is not visible.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
- is_element_hidden(el, "Preferences button is not visible.");
-});
-
-add_task(function testActivateRealExperiments() {
- if (!gExperiments) {
- info("Skipping experiments test because that feature isn't available.");
- return;
- }
-
- yield gExperiments._updateExperiments({
- "version": 1,
- "experiments": [
- {
- id: "experiment-2",
- xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
- xpiHash: "IRRELEVANT",
- startTime: Date.now() / 1000 - 3600,
- endTime: Date.now() / 1000 + 3600,
- maxActiveSeconds: 600,
- appName: [Services.appinfo.name],
- channel: [gExperiments._policy.updatechannel()],
- },
- ],
- });
- yield gExperiments._run();
-
- // Check the active experiment.
-
- let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Active");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Less than a day remaining");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "error-container");
- is_element_hidden(el, "error-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning-container");
- is_element_hidden(el, "warning-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "pending-container");
- is_element_hidden(el, "pending-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "version");
- is_element_hidden(el, "version should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
- is_element_hidden(el, "disabled-postfix should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "update-postfix");
- is_element_hidden(el, "update-postfix should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "experiment-bullet");
- is_element_visible(el, "experiment-bullet should be visible.");
-
- // Check the previous experiment.
-
- item = get_addon_element(gManagerWindow, "experiment-1");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Complete");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Less than a day ago");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "error-container");
- is_element_hidden(el, "error-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning-container");
- is_element_hidden(el, "warning-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "pending-container");
- is_element_hidden(el, "pending-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "version");
- is_element_hidden(el, "version should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
- is_element_hidden(el, "disabled-postfix should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "update-postfix");
- is_element_hidden(el, "update-postfix should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "experiment-bullet");
- is_element_visible(el, "experiment-bullet should be visible.");
-
- // Install an "older" experiment.
-
- yield gExperiments.disableExperiment("experiment-2");
-
- let now = Date.now();
- let fakeNow = now - 5 * MS_IN_ONE_DAY;
- defineNow(gExperiments._policy, fakeNow);
-
- yield gExperiments._updateExperiments({
- "version": 1,
- "experiments": [
- {
- id: "experiment-3",
- xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
- xpiHash: "IRRELEVANT",
- startTime: fakeNow / 1000 - SEC_IN_ONE_DAY,
- endTime: now / 1000 + 10 * SEC_IN_ONE_DAY,
- maxActiveSeconds: 100 * SEC_IN_ONE_DAY,
- appName: [Services.appinfo.name],
- channel: [gExperiments._policy.updatechannel()],
- },
- ],
- });
- yield gExperiments._run();
-
- // Check the active experiment.
-
- item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Active");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "10 days remaining");
- }
-
- // Disable it and check it's previous experiment entry.
-
- yield gExperiments.disableExperiment("experiment-3");
-
- item = get_addon_element(gManagerWindow, "experiment-3");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Complete");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "5 days ago");
- }
-});
-
-add_task(function testDetailView() {
- if (!gExperiments) {
- info("Skipping experiments test because that feature isn't available.");
- return;
- }
-
- defineNow(gExperiments._policy, Date.now());
- yield gExperiments._updateExperiments({
- "version": 1,
- "experiments": [
- {
- id: "experiment-4",
- xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
- xpiHash: "IRRELEVANT",
- startTime: Date.now() / 1000 - 3600,
- endTime: Date.now() / 1000 + 3600,
- maxActiveSeconds: 600,
- appName: [Services.appinfo.name],
- channel: [gExperiments._policy.updatechannel()],
- },
- ],
- });
- yield gExperiments._run();
-
- // Check active experiment.
-
- yield openDetailsView("test-experiment1@experiments.mozilla.org");
-
- let el = gManagerWindow.document.getElementById("detail-experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Active");
- }
-
- el = gManagerWindow.document.getElementById("detail-experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Less than a day remaining");
- }
-
- el = gManagerWindow.document.getElementById("detail-version");
- is_element_hidden(el, "detail-version should be hidden.");
- el = gManagerWindow.document.getElementById("detail-creator");
- is_element_hidden(el, "detail-creator should be hidden.");
- el = gManagerWindow.document.getElementById("detail-experiment-bullet");
- is_element_visible(el, "experiment-bullet should be visible.");
-
- // Check previous experiment.
-
- yield gCategoryUtilities.openType("experiment");
- yield openDetailsView("experiment-3");
-
- el = gManagerWindow.document.getElementById("detail-experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Complete");
- }
-
- el = gManagerWindow.document.getElementById("detail-experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "5 days ago");
- }
-
- el = gManagerWindow.document.getElementById("detail-version");
- is_element_hidden(el, "detail-version should be hidden.");
- el = gManagerWindow.document.getElementById("detail-creator");
- is_element_hidden(el, "detail-creator should be hidden.");
- el = gManagerWindow.document.getElementById("detail-experiment-bullet");
- is_element_visible(el, "experiment-bullet should be visible.");
-});
-
-add_task(function* testRemoveAndUndo() {
- if (!gExperiments) {
- info("Skipping experiments test because that feature isn't available.");
- return;
- }
-
- yield gCategoryUtilities.openType("experiment");
-
- let addon = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(addon, "Got add-on element.");
-
- yield clickRemoveButton(addon);
- addon.parentNode.ensureElementIsVisible(addon);
-
- let el = gManagerWindow.document.getAnonymousElementByAttribute(addon, "class", "pending");
- is_element_visible(el, "Uninstall undo information should be visible.");
-
- yield clickUndoButton(addon);
- addon = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(addon, "Got add-on element.");
-});
-
-add_task(function* testCleanup() {
- if (gExperiments) {
- Services.prefs.clearUserPref("experiments.enabled");
- Services.prefs.setCharPref("experiments.manifest.uri", gSavedManifestURI);
-
- // We perform the uninit/init cycle to purge any leftover state.
- yield OS.File.remove(gExperiments._cacheFilePath);
- yield gExperiments.uninit();
- yield gExperiments.init();
- }
-
- // Check post-conditions.
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
-
- yield close_manager(gManagerWindow);
-});
diff --git a/toolkit/mozapps/webextensions/content/extensions.js b/toolkit/mozapps/webextensions/content/extensions.js
index 5e428fe17..3159eb1e1 100644
--- a/toolkit/mozapps/webextensions/content/extensions.js
+++ b/toolkit/mozapps/webextensions/content/extensions.js
@@ -29,9 +29,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
"resource://gre/modules/Preferences.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
- "resource:///modules/experiments/Experiments.jsm");
-
const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
const PREF_XPI_ENABLED = "xpinstall.enabled";
@@ -297,23 +294,6 @@ function isDiscoverEnabled() {
return true;
}
-function getExperimentEndDate(aAddon) {
- if (!("@mozilla.org/browser/experiments-service;1" in Cc)) {
- return 0;
- }
-
- if (!aAddon.isActive) {
- return aAddon.endDate;
- }
-
- let experiment = Experiments.instance().getActiveExperiment();
- if (!experiment) {
- return 0;
- }
-
- return experiment.endDate;
-}
-
/**
* Obtain the main DOMWindow for the current context.
*/
@@ -1444,27 +1424,6 @@ var gViewController = {
}
},
- cmd_experimentsLearnMore: {
- isEnabled: function() {
- let mainWindow = getMainWindow();
- return mainWindow && "switchToTabHavingURI" in mainWindow;
- },
- doCommand: function() {
- let url = Services.prefs.getCharPref("toolkit.telemetry.infoURL");
- openOptionsInTab(url);
- },
- },
-
- cmd_experimentsOpenTelemetryPreferences: {
- isEnabled: function() {
- return !!getMainWindowWithPreferencesPane();
- },
- doCommand: function() {
- let mainWindow = getMainWindowWithPreferencesPane();
- mainWindow.openAdvancedPreferences("dataChoicesTab");
- },
- },
-
cmd_showUnsignedExtensions: {
isEnabled: function() {
return true;
@@ -1577,10 +1536,6 @@ function shouldShowVersionNumber(aAddon) {
if (!aAddon.version)
return false;
- // The version number is hidden for experiments.
- if (aAddon.type == "experiment")
- return false;
-
// The version number is hidden for lightweight themes.
if (aAddon.type == "theme")
return !/@personas\.mozilla\.org$/.test(aAddon.id);
@@ -1614,10 +1569,6 @@ function createItem(aObj, aIsInstall, aIsRemote) {
// the binding handles the rest
item.setAttribute("value", aObj.id);
- if (aObj.type == "experiment") {
- item.endDate = getExperimentEndDate(aObj);
- }
-
return item;
}
@@ -2861,13 +2812,6 @@ var gListView = {
// the existing item
if (aInstall.existingAddon)
this.removeItem(aInstall, true);
-
- if (aInstall.addon.type == "experiment") {
- let item = this.getListItemForID(aInstall.addon.id);
- if (item) {
- item.endDate = getExperimentEndDate(aInstall.addon);
- }
- }
},
addItem: function(aObj, aIsInstall) {
@@ -3126,34 +3070,6 @@ var gDetailView = {
}
}
- if (this._addon.type == "experiment") {
- let prefix = "details.experiment.";
- let active = this._addon.isActive;
-
- let stateKey = prefix + "state." + (active ? "active" : "complete");
- let node = document.getElementById("detail-experiment-state");
- node.value = gStrings.ext.GetStringFromName(stateKey);
-
- let now = Date.now();
- let end = getExperimentEndDate(this._addon);
- let days = Math.abs(end - now) / (24 * 60 * 60 * 1000);
-
- let timeKey = prefix + "time.";
- let timeMessage;
- if (days < 1) {
- timeKey += (active ? "endsToday" : "endedToday");
- timeMessage = gStrings.ext.GetStringFromName(timeKey);
- } else {
- timeKey += (active ? "daysRemaining" : "daysPassed");
- days = Math.round(days);
- let timeString = gStrings.ext.GetStringFromName(timeKey);
- timeMessage = PluralForm.get(days, timeString)
- .replace("#1", days);
- }
-
- document.getElementById("detail-experiment-time").value = timeMessage;
- }
-
this.fillSettingsRows(aScrollToPreferences, (function() {
this.updateState();
gViewController.notifyViewChanged();
diff --git a/toolkit/mozapps/webextensions/internal/XPIProvider.jsm b/toolkit/mozapps/webextensions/internal/XPIProvider.jsm
index 87e09cef1..7c3cb6763 100644
--- a/toolkit/mozapps/webextensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/webextensions/internal/XPIProvider.jsm
@@ -175,6 +175,8 @@ const RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";
const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
const TOOLKIT_ID = "toolkit@mozilla.org";
+const WEBEXTENSIONS_ID = "webextensions@mozilla.org";
+const WEBEXTENSIONS_VERSION = "52.0";
const XPI_SIGNATURE_CHECK_PERIOD = 24 * 60 * 60;
@@ -1037,7 +1039,7 @@ var loadManifestFromWebManifest = Task.async(function*(aUri) {
delete addon.defaultLocale.locales;
addon.targetApplications = [{
- id: TOOLKIT_ID,
+ id: WEBEXTENSIONS_ID,
minVersion: bss.strict_min_version,
maxVersion: bss.strict_max_version,
}];
@@ -7228,6 +7230,8 @@ AddonInternal.prototype = {
version = aAppVersion;
else if (app.id == TOOLKIT_ID)
version = aPlatformVersion
+ else if (app.id == WEBEXTENSIONS_ID)
+ version = WEBEXTENSIONS_VERSION
// Only extensions and dictionaries can be compatible by default; themes
// and language packs always use strict compatibility checking.
@@ -7250,7 +7254,7 @@ AddonInternal.prototype = {
let minCompatVersion;
if (app.id == Services.appinfo.ID)
minCompatVersion = XPIProvider.minCompatibleAppVersion;
- else if (app.id == TOOLKIT_ID)
+ else if (app.id == TOOLKIT_ID || app.id == WEBEXTENSIONS_ID)
minCompatVersion = XPIProvider.minCompatiblePlatformVersion;
if (minCompatVersion &&
@@ -7269,7 +7273,7 @@ AddonInternal.prototype = {
for (let targetApp of this.targetApplications) {
if (targetApp.id == Services.appinfo.ID)
return targetApp;
- if (targetApp.id == TOOLKIT_ID)
+ if (targetApp.id == TOOLKIT_ID || targetApp.id == WEBEXTENSIONS_ID)
app = targetApp;
}
return app;
diff --git a/toolkit/mozapps/webextensions/test/browser/browser_experiments.js b/toolkit/mozapps/webextensions/test/browser/browser_experiments.js
deleted file mode 100644
index 18a548de5..000000000
--- a/toolkit/mozapps/webextensions/test/browser/browser_experiments.js
+++ /dev/null
@@ -1,654 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-Components.utils.import("resource://gre/modules/Promise.jsm", this);
-
-var {AddonManagerTesting} = Components.utils.import("resource://testing-common/AddonManagerTesting.jsm", {});
-var {HttpServer} = Components.utils.import("resource://testing-common/httpd.js", {});
-
-var gManagerWindow;
-var gCategoryUtilities;
-var gExperiments;
-var gHttpServer;
-
-var gSavedManifestURI;
-var gIsEnUsLocale;
-
-const SEC_IN_ONE_DAY = 24 * 60 * 60;
-const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
-
-function getExperimentAddons() {
- let deferred = Promise.defer();
- AddonManager.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- return deferred.promise;
-}
-
-function getInstallItem() {
- let doc = gManagerWindow.document;
- let view = get_current_view(gManagerWindow);
- let list = doc.getElementById("addon-list");
-
- let node = list.firstChild;
- while (node) {
- if (node.getAttribute("status") == "installing") {
- return node;
- }
- node = node.nextSibling;
- }
-
- return null;
-}
-
-function patchPolicy(policy, data) {
- for (let key of Object.keys(data)) {
- Object.defineProperty(policy, key, {
- value: data[key],
- writable: true,
- });
- }
-}
-
-function defineNow(policy, time) {
- patchPolicy(policy, { now: () => new Date(time) });
-}
-
-function openDetailsView(aId) {
- let item = get_addon_element(gManagerWindow, aId);
- Assert.ok(item, "Should have got add-on element.");
- is_element_visible(item, "Add-on element should be visible.");
-
- EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
- EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
-
- let deferred = Promise.defer();
- wait_for_view_load(gManagerWindow, deferred.resolve);
- return deferred.promise;
-}
-
-function clickRemoveButton(addonElement) {
- let btn = gManagerWindow.document.getAnonymousElementByAttribute(addonElement, "anonid", "remove-btn");
- if (!btn) {
- return Promise.reject();
- }
-
- EventUtils.synthesizeMouseAtCenter(btn, { clickCount: 1 }, gManagerWindow);
- let deferred = Promise.defer();
- setTimeout(deferred.resolve, 0);
- return deferred;
-}
-
-function clickUndoButton(addonElement) {
- let btn = gManagerWindow.document.getAnonymousElementByAttribute(addonElement, "anonid", "undo-btn");
- if (!btn) {
- return Promise.reject();
- }
-
- EventUtils.synthesizeMouseAtCenter(btn, { clickCount: 1 }, gManagerWindow);
- let deferred = Promise.defer();
- setTimeout(deferred.resolve, 0);
- return deferred;
-}
-
-add_task(function* initializeState() {
- gManagerWindow = yield open_manager();
- gCategoryUtilities = new CategoryUtilities(gManagerWindow);
-
- registerCleanupFunction(() => {
- Services.prefs.clearUserPref("experiments.enabled");
- Services.prefs.clearUserPref("toolkit.telemetry.enabled");
- if (gHttpServer) {
- gHttpServer.stop(() => {});
- if (gSavedManifestURI !== undefined) {
- Services.prefs.setCharPref("experments.manifest.uri", gSavedManifestURI);
- }
- }
- if (gExperiments) {
- let tmp = {};
- Cu.import("resource:///modules/experiments/Experiments.jsm", tmp);
- gExperiments._policy = new tmp.Experiments.Policy();
- }
- });
-
- let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
- gIsEnUsLocale = chrome.getSelectedLocale("global") == "en-US";
-
- // The Experiments Manager will interfere with us by preventing installs
- // of experiments it doesn't know about. We remove it from the equation
- // because here we are only concerned with core Addon Manager operation,
- // not the superset Experiments Manager has imposed.
- if ("@mozilla.org/browser/experiments-service;1" in Components.classes) {
- let tmp = {};
- Cu.import("resource:///modules/experiments/Experiments.jsm", tmp);
- // There is a race condition between XPCOM service initialization and
- // this test running. We have to initialize the instance first, then
- // uninitialize it to prevent this.
- gExperiments = tmp.Experiments.instance();
- yield gExperiments._mainTask;
- yield gExperiments.uninit();
- }
-});
-
-// On an empty profile with no experiments, the experiment category
-// should be hidden.
-add_task(function* testInitialState() {
- Assert.ok(gCategoryUtilities.get("experiment", false), "Experiment tab is defined.");
- Assert.ok(!gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab hidden by default.");
-});
-
-add_task(function* testExperimentInfoNotVisible() {
- yield gCategoryUtilities.openType("extension");
- let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
- is_element_hidden(el, "Experiment info not visible on other types.");
-});
-
-// If we have an active experiment, we should see the experiments tab
-// and that tab should have some messages.
-add_task(function* testActiveExperiment() {
- let addon = yield install_addon("addons/browser_experiment1.xpi");
-
- Assert.ok(addon.userDisabled, "Add-on is disabled upon initial install.");
- Assert.equal(addon.isActive, false, "Add-on is not active.");
-
- Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible.");
-
- yield gCategoryUtilities.openType("experiment");
- let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
- is_element_visible(el, "Experiment info is visible on experiment tab.");
-});
-
-add_task(function* testExperimentLearnMore() {
- // Actual URL is irrelevant.
- Services.prefs.setCharPref("toolkit.telemetry.infoURL",
- "http://mochi.test:8888/server.js");
-
- yield gCategoryUtilities.openType("experiment");
- let btn = gManagerWindow.document.getElementById("experiments-learn-more");
-
- if (!gUseInContentUI) {
- is_element_hidden(btn, "Learn more button hidden if not using in-content UI.");
- Services.prefs.clearUserPref("toolkit.telemetry.infoURL");
-
- return;
- }
-
- is_element_visible(btn, "Learn more button visible.");
-
- let deferred = Promise.defer();
- window.addEventListener("DOMContentLoaded", function onLoad(event) {
- info("Telemetry privacy policy window opened.");
- window.removeEventListener("DOMContentLoaded", onLoad, false);
-
- let browser = gBrowser.selectedBrowser;
- let expected = Services.prefs.getCharPref("toolkit.telemetry.infoURL");
- Assert.equal(browser.currentURI.spec, expected, "New tab should have loaded privacy policy.");
- browser.contentWindow.close();
-
- Services.prefs.clearUserPref("toolkit.telemetry.infoURL");
-
- deferred.resolve();
- }, false);
-
- info("Opening telemetry privacy policy.");
- EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow);
-
- yield deferred.promise;
-});
-
-add_task(function* testOpenPreferences() {
- yield gCategoryUtilities.openType("experiment");
- let btn = gManagerWindow.document.getElementById("experiments-change-telemetry");
- if (!gUseInContentUI) {
- is_element_hidden(btn, "Change telemetry button not enabled in out of window UI.");
- info("Skipping preferences open test because not using in-content UI.");
- return;
- }
-
- is_element_visible(btn, "Change telemetry button visible in in-content UI.");
-
- let deferred = Promise.defer();
- Services.obs.addObserver(function observer(prefWin, topic, data) {
- Services.obs.removeObserver(observer, "advanced-pane-loaded");
- info("Advanced preference pane opened.");
- executeSoon(function() {
- // We want this test to fail if the preferences pane changes.
- let el = prefWin.document.getElementById("dataChoicesPanel");
- is_element_visible(el);
-
- prefWin.close();
- info("Closed preferences pane.");
-
- deferred.resolve();
- });
- }, "advanced-pane-loaded", false);
-
- info("Loading preferences pane.");
- // We need to focus before synthesizing the mouse event (bug 1240052) as
- // synthesizeMouseAtCenter currently only synthesizes the mouse in the child process.
- // This can cause some subtle differences if the child isn't focused.
- yield SimpleTest.promiseFocus();
- yield BrowserTestUtils.synthesizeMouseAtCenter("#experiments-change-telemetry", {},
- gBrowser.selectedBrowser);
-
- yield deferred.promise;
-});
-
-add_task(function* testButtonPresence() {
- yield gCategoryUtilities.openType("experiment");
- let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
- // Corresponds to the uninstall permission.
- is_element_visible(el, "Remove button is visible.");
- // Corresponds to lack of disable permission.
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
- is_element_hidden(el, "Disable button not visible.");
- // Corresponds to lack of enable permission.
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
- is_element_hidden(el, "Enable button not visible.");
-});
-
-// Remove the add-on we've been testing with.
-add_task(function* testCleanup() {
- yield AddonManagerTesting.uninstallAddonByID("test-experiment1@experiments.mozilla.org");
- // Verify some conditions, just in case.
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
-});
-
-// The following tests should ideally live in browser/experiments/. However,
-// they rely on some of the helper functions from head.js, which can't easily
-// be consumed from other directories. So, they live here.
-
-add_task(function* testActivateExperiment() {
- if (!gExperiments) {
- info("Skipping experiments test because that feature isn't available.");
- return;
- }
-
- gHttpServer = new HttpServer();
- gHttpServer.start(-1);
- let root = "http://localhost:" + gHttpServer.identity.primaryPort + "/";
- gHttpServer.registerPathHandler("/manifest", (request, response) => {
- response.setStatusLine(null, 200, "OK");
- response.write(JSON.stringify({
- "version": 1,
- "experiments": [
- {
- id: "experiment-1",
- xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
- xpiHash: "IRRELEVANT",
- startTime: Date.now() / 1000 - 3600,
- endTime: Date.now() / 1000 + 3600,
- maxActiveSeconds: 600,
- appName: [Services.appinfo.name],
- channel: [gExperiments._policy.updatechannel()],
- },
- ],
- }));
- response.processAsync();
- response.finish();
- });
-
- gSavedManifestURI = Services.prefs.getCharPref("experiments.manifest.uri");
- Services.prefs.setCharPref("experiments.manifest.uri", root + "manifest");
-
- // We need to remove the cache file to help ensure consistent state.
- yield OS.File.remove(gExperiments._cacheFilePath);
-
- Services.prefs.setBoolPref("toolkit.telemetry.enabled", true);
- Services.prefs.setBoolPref("experiments.enabled", true);
-
- info("Initializing experiments service.");
- yield gExperiments.init();
- info("Experiments service finished first run.");
-
- // Check conditions, just to be sure.
- let experiments = yield gExperiments.getExperiments();
- Assert.equal(experiments.length, 0, "No experiments known to the service.");
-
- // This makes testing easier.
- gExperiments._policy.ignoreHashes = true;
-
- info("Manually updating experiments manifest.");
- yield gExperiments.updateManifest();
- info("Experiments update complete.");
-
- let deferred = Promise.defer();
- gHttpServer.stop(() => {
- gHttpServer = null;
-
- info("getting experiment by ID");
- AddonManager.getAddonByID("test-experiment1@experiments.mozilla.org", (addon) => {
- Assert.ok(addon, "Add-on installed via Experiments manager.");
-
- deferred.resolve();
- });
- });
-
- yield deferred.promise;
-
- Assert.ok(gCategoryUtilities.isTypeVisible, "experiment", "Experiment tab visible.");
- yield gCategoryUtilities.openType("experiment");
- let el = gManagerWindow.document.getElementsByClassName("experiment-info-container")[0];
- is_element_visible(el, "Experiment info is visible on experiment tab.");
-});
-
-add_task(function* testDeactivateExperiment() {
- if (!gExperiments) {
- return;
- }
-
- // Fake an empty manifest to purge data from previous manifest.
- yield gExperiments._updateExperiments({
- "version": 1,
- "experiments": [],
- });
-
- yield gExperiments.disableExperiment("testing");
-
- // We should have a record of the previously-active experiment.
- let experiments = yield gExperiments.getExperiments();
- Assert.equal(experiments.length, 1, "1 experiment is known.");
- Assert.equal(experiments[0].active, false, "Experiment is not active.");
-
- // We should have a previous experiment in the add-ons manager.
- let deferred = Promise.defer();
- AddonManager.getAddonsByTypes(["experiment"], (addons) => {
- deferred.resolve(addons);
- });
- let addons = yield deferred.promise;
- Assert.equal(addons.length, 1, "1 experiment add-on known.");
- Assert.ok(addons[0].appDisabled, "It is a previous experiment.");
- Assert.equal(addons[0].id, "experiment-1", "Add-on ID matches expected.");
-
- // Verify the UI looks sane.
-
- Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible.");
- let item = get_addon_element(gManagerWindow, "experiment-1");
- Assert.ok(item, "Got add-on element.");
- Assert.ok(!item.active, "Element should not be active.");
- item.parentNode.ensureElementIsVisible(item);
-
- // User control buttons should not be present because previous experiments
- // should have no permissions.
- let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
- is_element_hidden(el, "Remove button is not visible.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
- is_element_hidden(el, "Disable button is not visible.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
- is_element_hidden(el, "Enable button is not visible.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
- is_element_hidden(el, "Preferences button is not visible.");
-});
-
-add_task(function* testActivateRealExperiments() {
- if (!gExperiments) {
- info("Skipping experiments test because that feature isn't available.");
- return;
- }
-
- yield gExperiments._updateExperiments({
- "version": 1,
- "experiments": [
- {
- id: "experiment-2",
- xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
- xpiHash: "IRRELEVANT",
- startTime: Date.now() / 1000 - 3600,
- endTime: Date.now() / 1000 + 3600,
- maxActiveSeconds: 600,
- appName: [Services.appinfo.name],
- channel: [gExperiments._policy.updatechannel()],
- },
- ],
- });
- yield gExperiments._run();
-
- // Check the active experiment.
-
- let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Active");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Less than a day remaining");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "error-container");
- is_element_hidden(el, "error-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning-container");
- is_element_hidden(el, "warning-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "pending-container");
- is_element_hidden(el, "pending-container should be hidden.");
- let { version } = yield get_tooltip_info(item);
- Assert.equal(version, undefined, "version should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
- is_element_hidden(el, "disabled-postfix should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "update-postfix");
- is_element_hidden(el, "update-postfix should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "experiment-bullet");
- is_element_visible(el, "experiment-bullet should be visible.");
-
- // Check the previous experiment.
-
- item = get_addon_element(gManagerWindow, "experiment-1");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Complete");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Less than a day ago");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "error-container");
- is_element_hidden(el, "error-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning-container");
- is_element_hidden(el, "warning-container should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "pending-container");
- is_element_hidden(el, "pending-container should be hidden.");
- ({ version } = yield get_tooltip_info(item));
- Assert.equal(version, undefined, "version should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
- is_element_hidden(el, "disabled-postfix should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "update-postfix");
- is_element_hidden(el, "update-postfix should be hidden.");
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "experiment-bullet");
- is_element_visible(el, "experiment-bullet should be visible.");
-
- // Install an "older" experiment.
-
- yield gExperiments.disableExperiment("experiment-2");
-
- let now = Date.now();
- let fakeNow = now - 5 * MS_IN_ONE_DAY;
- defineNow(gExperiments._policy, fakeNow);
-
- yield gExperiments._updateExperiments({
- "version": 1,
- "experiments": [
- {
- id: "experiment-3",
- xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
- xpiHash: "IRRELEVANT",
- startTime: fakeNow / 1000 - SEC_IN_ONE_DAY,
- endTime: now / 1000 + 10 * SEC_IN_ONE_DAY,
- maxActiveSeconds: 100 * SEC_IN_ONE_DAY,
- appName: [Services.appinfo.name],
- channel: [gExperiments._policy.updatechannel()],
- },
- ],
- });
- yield gExperiments._run();
-
- // Check the active experiment.
-
- item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Active");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "10 days remaining");
- }
-
- // Disable it and check it's previous experiment entry.
-
- yield gExperiments.disableExperiment("experiment-3");
-
- item = get_addon_element(gManagerWindow, "experiment-3");
- Assert.ok(item, "Got add-on element.");
- item.parentNode.ensureElementIsVisible(item);
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Complete");
- }
-
- el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "5 days ago");
- }
-});
-
-add_task(function* testDetailView() {
- if (!gExperiments) {
- info("Skipping experiments test because that feature isn't available.");
- return;
- }
-
- defineNow(gExperiments._policy, Date.now());
- yield gExperiments._updateExperiments({
- "version": 1,
- "experiments": [
- {
- id: "experiment-4",
- xpiURL: TESTROOT + "addons/browser_experiment1.xpi",
- xpiHash: "IRRELEVANT",
- startTime: Date.now() / 1000 - 3600,
- endTime: Date.now() / 1000 + 3600,
- maxActiveSeconds: 600,
- appName: [Services.appinfo.name],
- channel: [gExperiments._policy.updatechannel()],
- },
- ],
- });
- yield gExperiments._run();
-
- // Check active experiment.
-
- yield openDetailsView("test-experiment1@experiments.mozilla.org");
-
- let el = gManagerWindow.document.getElementById("detail-experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Active");
- }
-
- el = gManagerWindow.document.getElementById("detail-experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Less than a day remaining");
- }
-
- el = gManagerWindow.document.getElementById("detail-version");
- is_element_hidden(el, "detail-version should be hidden.");
- el = gManagerWindow.document.getElementById("detail-creator");
- is_element_hidden(el, "detail-creator should be hidden.");
- el = gManagerWindow.document.getElementById("detail-experiment-bullet");
- is_element_visible(el, "experiment-bullet should be visible.");
-
- // Check previous experiment.
-
- yield gCategoryUtilities.openType("experiment");
- yield openDetailsView("experiment-3");
-
- el = gManagerWindow.document.getElementById("detail-experiment-state");
- is_element_visible(el, "Experiment state label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "Complete");
- }
-
- el = gManagerWindow.document.getElementById("detail-experiment-time");
- is_element_visible(el, "Experiment time label should be visible.");
- if (gIsEnUsLocale) {
- Assert.equal(el.value, "5 days ago");
- }
-
- el = gManagerWindow.document.getElementById("detail-version");
- is_element_hidden(el, "detail-version should be hidden.");
- el = gManagerWindow.document.getElementById("detail-creator");
- is_element_hidden(el, "detail-creator should be hidden.");
- el = gManagerWindow.document.getElementById("detail-experiment-bullet");
- is_element_visible(el, "experiment-bullet should be visible.");
-});
-
-add_task(function* testRemoveAndUndo() {
- if (!gExperiments) {
- info("Skipping experiments test because that feature isn't available.");
- return;
- }
-
- yield gCategoryUtilities.openType("experiment");
-
- let addon = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(addon, "Got add-on element.");
-
- yield clickRemoveButton(addon);
- addon.parentNode.ensureElementIsVisible(addon);
-
- let el = gManagerWindow.document.getAnonymousElementByAttribute(addon, "class", "pending");
- is_element_visible(el, "Uninstall undo information should be visible.");
-
- yield clickUndoButton(addon);
- addon = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
- Assert.ok(addon, "Got add-on element.");
-});
-
-add_task(function* testCleanup() {
- if (gExperiments) {
- Services.prefs.clearUserPref("experiments.enabled");
- Services.prefs.setCharPref("experiments.manifest.uri", gSavedManifestURI);
-
- // We perform the uninit/init cycle to purge any leftover state.
- yield OS.File.remove(gExperiments._cacheFilePath);
- yield gExperiments.uninit();
- yield gExperiments.init();
-
- Services.prefs.clearUserPref("toolkit.telemetry.enabled");
- }
-
- // Check post-conditions.
- let addons = yield getExperimentAddons();
- Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
-
- yield close_manager(gManagerWindow);
-});