mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Backed out 3 changesets (bug 1622088) for XPCshell and Browser-chrome failurs on browser/abouthomecache/browser_process_crash.js. CLOSED TREE
Backed out changeset 238fa307504a (bug 1622088) Backed out changeset ceaa7857baea (bug 1622088) Backed out changeset 9c75ae56f50e (bug 1622088)
This commit is contained in:
parent
001136fb24
commit
13c718e2f2
@ -5031,10 +5031,6 @@ var AboutHomeStartupCache = {
|
||||
Services.obs.addObserver(this, "ipc:content-created");
|
||||
Services.obs.addObserver(this, "ipc:content-shutdown");
|
||||
|
||||
this._cacheEntryPromise = new Promise(resolve => {
|
||||
this._cacheEntryResolver = resolve;
|
||||
});
|
||||
|
||||
let lci = Services.loadContextInfo.default;
|
||||
let storage = Services.cache2.diskCacheStorage(lci, false);
|
||||
try {
|
||||
@ -5083,9 +5079,6 @@ var AboutHomeStartupCache = {
|
||||
this._initted = false;
|
||||
this._cacheEntry = null;
|
||||
this._hasWrittenThisSession = false;
|
||||
this._cacheEntryPromise = null;
|
||||
this._cacheEntryResolver = null;
|
||||
|
||||
this.log.trace("Uninitialized.");
|
||||
this.log.removeAppender(this._appender);
|
||||
this.log = null;
|
||||
@ -5143,7 +5136,7 @@ var AboutHomeStartupCache = {
|
||||
* Resolves when a fresh version of the cache has been written.
|
||||
*/
|
||||
async cacheNow() {
|
||||
this.log.trace("Caching now.");
|
||||
this._hasWrittenThisSession = true;
|
||||
this._cacheProgress = "Getting cache streams";
|
||||
let { pageInputStream, scriptInputStream } = await this.requestCache();
|
||||
|
||||
@ -5155,8 +5148,6 @@ var AboutHomeStartupCache = {
|
||||
this._cacheProgress = "Writing to cache";
|
||||
await this.populateCache(pageInputStream, scriptInputStream);
|
||||
this._cacheProgress = "Done";
|
||||
this.log.trace("Done writing to cache.");
|
||||
this._hasWrittenThisSession = true;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -5274,7 +5265,7 @@ var AboutHomeStartupCache = {
|
||||
if (parseInt(version, 10) != this.CACHE_VERSION) {
|
||||
this.log.info("Version does not match! Dooming and closing streams.\n");
|
||||
// This cache is no good - doom it, and prepare for a new one.
|
||||
this.clearCache();
|
||||
this._cacheEntry = this._cacheEntry.recreate();
|
||||
this.pagePipe.outputStream.close();
|
||||
this.scriptPipe.outputStream.close();
|
||||
return;
|
||||
@ -5375,120 +5366,40 @@ var AboutHomeStartupCache = {
|
||||
* A stream containing the HTML markup to be saved to the cache.
|
||||
* @param scriptInputStream (nsIInputStream)
|
||||
* A stream containing the JS hydration script to be saved to the cache.
|
||||
* @returns Promise
|
||||
* @resolves undefined
|
||||
* When the cache has been successfully written to.
|
||||
* @rejects Error
|
||||
* Rejects with a JS Error if writing any part of the cache happens to
|
||||
* fail.
|
||||
*/
|
||||
async populateCache(pageInputStream, scriptInputStream) {
|
||||
await this.ensureCacheEntry();
|
||||
populateCache(pageInputStream, scriptInputStream) {
|
||||
// Doom the old cache entry, so we can start writing to a new one.
|
||||
this.log.trace("Populating the cache. Dooming old entry.");
|
||||
this._cacheEntry = this._cacheEntry.recreate();
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
// Doom the old cache entry, so we can start writing to a new one.
|
||||
this.log.trace("Populating the cache. Dooming old entry.");
|
||||
this.clearCache();
|
||||
this.log.trace("Opening the page output stream.");
|
||||
let pageOutputStream = this._cacheEntry.openOutputStream(0, -1);
|
||||
|
||||
this.log.trace("Opening the page output stream.");
|
||||
let pageOutputStream;
|
||||
try {
|
||||
pageOutputStream = this._cacheEntry.openOutputStream(0, -1);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
this.log.info("Writing the page cache.");
|
||||
NetUtil.asyncCopy(pageInputStream, pageOutputStream, () => {
|
||||
this.log.trace(
|
||||
"Writing the page data is complete. Now opening the " +
|
||||
"script output stream."
|
||||
);
|
||||
|
||||
this.log.info("Writing the page cache.");
|
||||
NetUtil.asyncCopy(pageInputStream, pageOutputStream, pageResult => {
|
||||
if (!Components.isSuccessCode(pageResult)) {
|
||||
this.log.error("Failed to write page. Result: " + pageResult);
|
||||
reject(new Error(pageResult));
|
||||
return;
|
||||
}
|
||||
let scriptOutputStream = this._cacheEntry.openAlternativeOutputStream(
|
||||
"script",
|
||||
-1
|
||||
);
|
||||
|
||||
this.log.trace(
|
||||
"Writing the page data is complete. Now opening the " +
|
||||
"script output stream."
|
||||
);
|
||||
|
||||
let scriptOutputStream;
|
||||
try {
|
||||
scriptOutputStream = this._cacheEntry.openAlternativeOutputStream(
|
||||
"script",
|
||||
-1
|
||||
);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
this.log.info("Writing the script cache.");
|
||||
NetUtil.asyncCopy(
|
||||
scriptInputStream,
|
||||
scriptOutputStream,
|
||||
scriptResult => {
|
||||
if (!Components.isSuccessCode(scriptResult)) {
|
||||
this.log.error("Failed to write script. Result: " + scriptResult);
|
||||
reject(new Error(scriptResult));
|
||||
return;
|
||||
}
|
||||
|
||||
this.log.trace(
|
||||
"Writing the script cache is done. Setting version."
|
||||
);
|
||||
try {
|
||||
this._cacheEntry.setMetaDataElement(
|
||||
"version",
|
||||
String(this.CACHE_VERSION)
|
||||
);
|
||||
} catch (e) {
|
||||
this.log.error("Failed to write version.");
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
this.log.trace(`Version is set to ${this.CACHE_VERSION}.`);
|
||||
this.log.info("Caching of page and script is done.");
|
||||
resolve();
|
||||
}
|
||||
this.log.info("Writing the script cache.");
|
||||
NetUtil.asyncCopy(scriptInputStream, scriptOutputStream, () => {
|
||||
this.log.trace("Writing the script cache is done. Setting version.");
|
||||
this._cacheEntry.setMetaDataElement(
|
||||
"version",
|
||||
String(this.CACHE_VERSION)
|
||||
);
|
||||
this.log.trace(`Version is set to ${this.CACHE_VERSION}.`);
|
||||
this.log.info("Caching of page and script is done.");
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves once the nsICacheEntry for the cache
|
||||
* is available to write to and read from.
|
||||
*
|
||||
* @returns Promise
|
||||
* @resolves nsICacheEntry
|
||||
* Once the cache entry has become available.
|
||||
* @rejects String
|
||||
* Rejects with an error message if getting the cache entry is attempted
|
||||
* before the AboutHomeStartupCache component has been initialized.
|
||||
*/
|
||||
ensureCacheEntry() {
|
||||
if (!this._initted) {
|
||||
return Promise.reject(
|
||||
"Cannot ensureCacheEntry - AboutHomeStartupCache is not initted"
|
||||
);
|
||||
}
|
||||
|
||||
return this._cacheEntryPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the contents of the cache.
|
||||
*/
|
||||
clearCache() {
|
||||
this.log.trace("Clearing the cache.");
|
||||
this._cacheEntry = this._cacheEntry.recreate();
|
||||
this._cacheEntryPromise = new Promise(resolve => {
|
||||
resolve(this._cacheEntry);
|
||||
});
|
||||
this._hasWrittenThisSession = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a content process is created. If this is the "privileged
|
||||
* about content process", then the cache streams will be sent to it.
|
||||
@ -5609,7 +5520,5 @@ var AboutHomeStartupCache = {
|
||||
this._cacheEntry = aEntry;
|
||||
this.makePipes();
|
||||
this.maybeConnectToPipes();
|
||||
|
||||
this._cacheEntryResolver(this._cacheEntry);
|
||||
},
|
||||
};
|
||||
|
@ -52,8 +52,6 @@ const { E10SUtils } = ChromeUtils.import(
|
||||
|
||||
const PREF_ABOUT_HOME_CACHE_ENABLED =
|
||||
"browser.startup.homepage.abouthome_cache.enabled";
|
||||
const PREF_ABOUT_HOME_CACHE_TESTING =
|
||||
"browser.startup.homepage.abouthome_cache.testing";
|
||||
const PREF_SEPARATE_ABOUT_WELCOME = "browser.aboutwelcome.enabled";
|
||||
const SEPARATE_ABOUT_WELCOME_URL =
|
||||
"resource://activity-stream/aboutwelcome/aboutwelcome.html";
|
||||
@ -122,24 +120,6 @@ const AboutHomeStartupCacheChild = {
|
||||
this._initted = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* A function that lets us put the AboutHomeStartupCacheChild back into
|
||||
* its initial state. This is used by tests to let us simulate the startup
|
||||
* behaviour of the module without having to manually launch a new privileged
|
||||
* about content process every time.
|
||||
*/
|
||||
uninit() {
|
||||
if (!Services.prefs.getBoolPref(PREF_ABOUT_HOME_CACHE_TESTING, false)) {
|
||||
throw new Error(
|
||||
"Cannot uninit AboutHomeStartupCacheChild unless testing."
|
||||
);
|
||||
}
|
||||
|
||||
this._pageInputStream = null;
|
||||
this._scriptInputStream = null;
|
||||
this._initted = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* A public method called from nsIAboutNewTabService that attempts
|
||||
* return an nsIChannel for a cached about:home document that we
|
||||
|
@ -7,10 +7,7 @@
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Firefox", "New Tab Page")
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'test/browser/abouthomecache/browser.ini',
|
||||
'test/browser/browser.ini',
|
||||
]
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
|
||||
|
||||
SPHINX_TREES['docs'] = 'docs'
|
||||
SPHINX_TREES['content-src/asrouter/docs'] = 'content-src/asrouter/docs'
|
||||
|
@ -1,20 +0,0 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
prefs =
|
||||
browser.tabs.remote.separatePrivilegedContentProcess=true
|
||||
browser.startup.homepage.abouthome_cache.enabled=true
|
||||
browser.startup.homepage.abouthome_cache.cache_on_shutdown=false
|
||||
browser.startup.homepage.abouthome_cache.testing=true
|
||||
browser.startup.page=1
|
||||
browser.newtabpage.activity-stream.discoverystream.endpoints=data:
|
||||
browser.newtabpage.activity-stream.feeds.section.topstories=true
|
||||
browser.newtabpage.activity-stream.feeds.section.topstories.options={"provider_name":""}
|
||||
browser.newtabpage.activity-stream.telemetry.structuredIngestion=false
|
||||
|
||||
[browser_basic_endtoend.js]
|
||||
[browser_bump_version.js]
|
||||
[browser_no_cache.js]
|
||||
[browser_overwrite_cache.js]
|
||||
[browser_process_crash.js]
|
||||
skip-if = !e10s || !crashreporter
|
@ -1,22 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that the about:home cache gets written on shutdown, and read
|
||||
* from in the subsequent startup.
|
||||
*/
|
||||
add_task(async function test_basic_behaviour() {
|
||||
await BrowserTestUtils.withNewTab("about:home", async browser => {
|
||||
// First, clear the cache to test the base case.
|
||||
await clearCache();
|
||||
await simulateRestart(browser);
|
||||
await ensureCachedAboutHome(browser);
|
||||
|
||||
// Next, test that a subsequent restart also shows the cached
|
||||
// about:home.
|
||||
await simulateRestart(browser);
|
||||
await ensureCachedAboutHome(browser);
|
||||
});
|
||||
});
|
@ -1,21 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test that if the "version" metadata on the cache entry doesn't match
|
||||
* the expectation that we ignore the cache and load the dynamic about:home
|
||||
* document.
|
||||
*/
|
||||
add_task(async function test_bump_version() {
|
||||
await BrowserTestUtils.withNewTab("about:home", async browser => {
|
||||
// First, ensure that a pre-existing cache exists.
|
||||
await simulateRestart(browser);
|
||||
|
||||
let cacheEntry = await AboutHomeStartupCache.ensureCacheEntry();
|
||||
cacheEntry.setMetaDataElement("version", "somethingnew");
|
||||
await simulateRestart(browser, false /* withAutoShutdownWrite */);
|
||||
await ensureDynamicAboutHome(browser);
|
||||
});
|
||||
});
|
@ -1,18 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
/**
|
||||
* Test that if there's no cache written, that we load the dynamic
|
||||
* about:home document on startup.
|
||||
*/
|
||||
add_task(async function test_no_cache() {
|
||||
await BrowserTestUtils.withNewTab("about:home", async browser => {
|
||||
await clearCache();
|
||||
await simulateRestart(browser, false /* withAutoShutdownWrite */);
|
||||
await ensureDynamicAboutHome(browser);
|
||||
});
|
||||
});
|
@ -1,37 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that if a pre-existing about:home cache exists, that it can
|
||||
* be overwritten with new information.
|
||||
*/
|
||||
add_task(async function test_overwrite_cache() {
|
||||
await BrowserTestUtils.withNewTab("about:home", async browser => {
|
||||
await simulateRestart(browser);
|
||||
const TEST_ID = "test_overwrite_cache_h1";
|
||||
|
||||
// We need the CSP meta tag in about: pages, otherwise we hit assertions in
|
||||
// debug builds.
|
||||
await injectIntoCache(
|
||||
`
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="${TEST_ID}">Something new</h1>
|
||||
</body>
|
||||
<script src="about:home?jscache"></script>
|
||||
</html>`,
|
||||
"window.__FROM_STARTUP_CACHE__ = true;"
|
||||
);
|
||||
await simulateRestart(browser, false /* withAutoShutdownWrite */);
|
||||
|
||||
await SpecialPowers.spawn(browser, [TEST_ID], async testID => {
|
||||
let target = content.document.getElementById(testID);
|
||||
Assert.ok(target, "Found the target element");
|
||||
});
|
||||
});
|
||||
});
|
@ -1,41 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test that if the "privileged about content process" crashes, that it
|
||||
* drops its internal reference to the "privileged about content process"
|
||||
* process manager, and that a subsequent restart of that process type
|
||||
* results in a new cached document load. Also tests that crashing of
|
||||
* any other content process type doesn't clear the process manager
|
||||
* reference.
|
||||
*/
|
||||
add_task(async function test_bump_version() {
|
||||
await BrowserTestUtils.withNewTab("about:home", async browser => {
|
||||
await simulateRestart(browser);
|
||||
let origProcManager = AboutHomeStartupCache._procManager;
|
||||
|
||||
await BrowserTestUtils.crashFrame(browser);
|
||||
Assert.notEqual(
|
||||
origProcManager,
|
||||
AboutHomeStartupCache._procManager,
|
||||
"Should have dropped the reference to the crashed process"
|
||||
);
|
||||
});
|
||||
|
||||
let latestProcManager = AboutHomeStartupCache._procManager;
|
||||
|
||||
await BrowserTestUtils.withNewTab("about:home", async browser => {
|
||||
await ensureCachedAboutHome(browser);
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab("http://example.com", async browser => {
|
||||
await BrowserTestUtils.crashFrame(browser);
|
||||
Assert.equal(
|
||||
latestProcManager,
|
||||
AboutHomeStartupCache._procManager,
|
||||
"Should still have the reference to the privileged about process"
|
||||
);
|
||||
});
|
||||
});
|
@ -1,238 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let { AboutHomeStartupCache } = ChromeUtils.import(
|
||||
"resource:///modules/BrowserGlue.jsm"
|
||||
);
|
||||
|
||||
/**
|
||||
* Shuts down the AboutHomeStartupCache components in the parent process
|
||||
* and privileged about content process, and then restarts them, simulating
|
||||
* the parent process having restarted.
|
||||
*
|
||||
* @param browser (<xul:browser>)
|
||||
* A <xul:browser> with about:home running in it. This will be reloaded
|
||||
* after the restart simultion is complete, and that reload will attempt
|
||||
* to read any about:home cache contents.
|
||||
* @param options (object, optional)
|
||||
*
|
||||
* An object with the following properties:
|
||||
*
|
||||
* withAutoShutdownWrite (boolean, optional):
|
||||
* Whether or not the shutdown part of the simulation should cause the
|
||||
* shutdown handler to run, which normally causes the cache to be
|
||||
* written. Setting this to false is handy if the cache has been
|
||||
* specially prepared for the subsequent startup, and we don't want to
|
||||
* overwrite it. This defaults to true.
|
||||
*
|
||||
* ensureCacheWinsRace (boolean, optional):
|
||||
* Ensures that the privileged about content process will be able to
|
||||
* read the bytes from the streams sent down from the HTTP cache. Use
|
||||
* this to avoid the HTTP cache "losing the race" against reading the
|
||||
* about:home document from the omni.ja. This defaults to true.
|
||||
*
|
||||
* @returns Promise
|
||||
* @resolves undefined
|
||||
* Resolves once the restart simulation is complete, and the <xul:browser>
|
||||
* pointed at about:home finishes reloading.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function simulateRestart(
|
||||
browser,
|
||||
{ withAutoShutdownWrite, ensureCacheWinsRace } = {
|
||||
withAutoShutdownWrite: true,
|
||||
ensureCacheWinsRace: true,
|
||||
}
|
||||
) {
|
||||
info("Simulating restart of the browser");
|
||||
let processManager = browser.messageManager.processMessageManager;
|
||||
if (processManager.remoteType !== E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE) {
|
||||
throw new Error(
|
||||
"prepareLoadFromCache should only be called on a browser " +
|
||||
"loaded in the privileged about content process."
|
||||
);
|
||||
}
|
||||
|
||||
if (withAutoShutdownWrite) {
|
||||
info("Simulating shutdown write");
|
||||
await AboutHomeStartupCache.onShutdown();
|
||||
info("Shutdown write done");
|
||||
} else {
|
||||
info("Intentionally skipping shutdown write");
|
||||
}
|
||||
|
||||
AboutHomeStartupCache.uninit();
|
||||
|
||||
info("Waiting for AboutHomeStartupCacheChild to uninit");
|
||||
await SpecialPowers.spawn(browser, [], async () => {
|
||||
let { AboutHomeStartupCacheChild } = ChromeUtils.import(
|
||||
"resource:///modules/AboutNewTabService.jsm"
|
||||
);
|
||||
AboutHomeStartupCacheChild.uninit();
|
||||
});
|
||||
info("AboutHomeStartupCacheChild uninitted");
|
||||
|
||||
AboutHomeStartupCache.init();
|
||||
|
||||
AboutHomeStartupCache.sendCacheInputStreams(processManager);
|
||||
|
||||
info("Waiting for AboutHomeStartupCache cache entry");
|
||||
await AboutHomeStartupCache.ensureCacheEntry();
|
||||
info("Got AboutHomeStartupCache cache entry");
|
||||
|
||||
if (ensureCacheWinsRace) {
|
||||
info("Ensuring cache bytes are available");
|
||||
await SpecialPowers.spawn(browser, [], async () => {
|
||||
let { AboutHomeStartupCacheChild } = ChromeUtils.import(
|
||||
"resource:///modules/AboutNewTabService.jsm"
|
||||
);
|
||||
let pageStream = AboutHomeStartupCacheChild._pageInputStream;
|
||||
let scriptStream = AboutHomeStartupCacheChild._scriptInputStream;
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return pageStream.available() && scriptStream.available();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
info("Waiting for about:home to load");
|
||||
let loaded = BrowserTestUtils.browserLoaded(browser, false, "about:home");
|
||||
BrowserTestUtils.loadURI(browser, "about:home");
|
||||
await loaded;
|
||||
info("about:home loaded");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a page string and a script string into the cache for
|
||||
* the next about:home load.
|
||||
*
|
||||
* @param page (String)
|
||||
* The HTML content to write into the cache. This cannot be the empty
|
||||
* string.
|
||||
* @param script (String)
|
||||
* The JS content to write into the cache that can be loaded via
|
||||
* about:home?jscache. This cannot be the empty string.
|
||||
* @returns Promise
|
||||
* @resolves undefined
|
||||
* When the page and script content has been successfully written.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function injectIntoCache(page, script) {
|
||||
if (!page || !script) {
|
||||
throw new Error("Cannot injectIntoCache with falsey values");
|
||||
}
|
||||
|
||||
await AboutHomeStartupCache.ensureCacheEntry();
|
||||
|
||||
let pageInputStream = Cc[
|
||||
"@mozilla.org/io/string-input-stream;1"
|
||||
].createInstance(Ci.nsIStringInputStream);
|
||||
|
||||
pageInputStream.setUTF8Data(page);
|
||||
|
||||
let scriptInputStream = Cc[
|
||||
"@mozilla.org/io/string-input-stream;1"
|
||||
].createInstance(Ci.nsIStringInputStream);
|
||||
|
||||
scriptInputStream.setUTF8Data(script);
|
||||
|
||||
await AboutHomeStartupCache.populateCache(pageInputStream, scriptInputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out any pre-existing about:home cache.
|
||||
* @returns Promise
|
||||
* @resolves undefined
|
||||
* Resolves when the cache is cleared.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function clearCache() {
|
||||
info("Test is clearing the cache");
|
||||
AboutHomeStartupCache.clearCache();
|
||||
await AboutHomeStartupCache.ensureCacheEntry();
|
||||
info("Test has cleared the cache.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the about:home document loaded in a passed <xul:browser> was
|
||||
* one from the cache.
|
||||
*
|
||||
* We test for this by looking for some tell-tale signs of the cached
|
||||
* document:
|
||||
*
|
||||
* 1. The about:home?jscache <script> element
|
||||
* 2. The __FROM_STARTUP_CACHE__ expando on the window
|
||||
* 3. The "activity-stream" class on the document body
|
||||
* 4. The top sites section
|
||||
*
|
||||
* @returns Promise
|
||||
* @resolves undefined
|
||||
* Resolves once the cache entry has been destroyed.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function ensureCachedAboutHome(browser) {
|
||||
await SpecialPowers.spawn(browser, [], async () => {
|
||||
let scripts = Array.from(content.document.querySelectorAll("script"));
|
||||
Assert.ok(!!scripts.length, "There should be page scripts.");
|
||||
let [lastScript] = scripts.reverse();
|
||||
Assert.equal(
|
||||
lastScript.src,
|
||||
"about:home?jscache",
|
||||
"Found about:home?jscache script tag, indicating the cached doc"
|
||||
);
|
||||
Assert.ok(
|
||||
Cu.waiveXrays(content).__FROM_STARTUP_CACHE__,
|
||||
"Should have found window.__FROM_STARTUP_CACHE__"
|
||||
);
|
||||
Assert.ok(
|
||||
content.document.body.classList.contains("activity-stream"),
|
||||
"Should have found activity-stream class on <body> element"
|
||||
);
|
||||
Assert.ok(
|
||||
content.document.querySelector("[data-section-id='topsites']"),
|
||||
"Should have found the Discovery Stream top sites."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the about:home document loaded in a passed <xul:browser> was
|
||||
* dynamically generated, and _not_ from the cache.
|
||||
*
|
||||
* We test for this by looking for some tell-tale signs of the dynamically
|
||||
* generated document:
|
||||
*
|
||||
* 1. No <script> elements (the scripts are loaded from the ScriptPreloader
|
||||
* via AboutNewTabChild when the "privileged about content process" is
|
||||
* enabled)
|
||||
* 2. No __FROM_STARTUP_CACHE__ expando on the window
|
||||
* 3. The "activity-stream" class on the document body
|
||||
* 4. The top sites section
|
||||
*
|
||||
* @returns Promise
|
||||
* @resolves undefined
|
||||
* Resolves once the cache entry has been destroyed.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function ensureDynamicAboutHome(browser) {
|
||||
await SpecialPowers.spawn(browser, [], async () => {
|
||||
let scripts = Array.from(content.document.querySelectorAll("script"));
|
||||
Assert.equal(scripts.length, 0, "There should be no page scripts.");
|
||||
|
||||
Assert.equal(
|
||||
Cu.waiveXrays(content).__FROM_STARTUP_CACHE__,
|
||||
undefined,
|
||||
"Should not have found window.__FROM_STARTUP_CACHE__"
|
||||
);
|
||||
|
||||
Assert.ok(
|
||||
content.document.body.classList.contains("activity-stream"),
|
||||
"Should have found activity-stream class on <body> element"
|
||||
);
|
||||
Assert.ok(
|
||||
content.document.querySelector("[data-section-id='topsites']"),
|
||||
"Should have found the Discovery Stream top sites."
|
||||
);
|
||||
});
|
||||
}
|
@ -1 +0,0 @@
|
||||
{"spocs":{"url":"","spocs_per_domain":1},"layout":[{"width":12,"components":[{"type":"TopSites","header":{"title":"Top Sites"},"properties":null},{"type":"Message","header":{"title":"Recommended by Pocket","subtitle":"","link_text":"How it works","link_url":"https:\/\/getpocket.com\/firefox\/new_tab_learn_more","icon":"resource:\/\/activity-stream\/data\/content\/assets\/glyph-pocket-16.svg"},"properties":null,"styles":{".ds-message":"margin-bottom: -20px"}},{"type":"CardGrid","properties":{"items":3},"header":{"title":""},"feed":{"embed_reference":null,"url":"http://example.com/topstories.json"},"spocs":{"probability":1,"positions":[{"index":2}]}},{"type":"Navigation","properties":{"alignment":"left-align","links":[{"name":"Must Reads","url":"https:\/\/getpocket.com\/explore\/must-reads?src=fx_new_tab"},{"name":"Productivity","url":"https:\/\/getpocket.com\/explore\/productivity?src=fx_new_tab"},{"name":"Health","url":"https:\/\/getpocket.com\/explore\/health?src=fx_new_tab"},{"name":"Finance","url":"https:\/\/getpocket.com\/explore\/finance?src=fx_new_tab"},{"name":"Technology","url":"https:\/\/getpocket.com\/explore\/technology?src=fx_new_tab"},{"name":"More Recommendations \u203a","url":"https:\/\/getpocket.com\/explore\/trending?src=fx_new_tab"}]}}]}],"feeds":{},"error":0,"status":1}
|
@ -1,220 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* This test ensures that the about:home startup cache worker
|
||||
* script can correctly convert a state object from the Activity
|
||||
* Stream Redux store into an HTML document and script.
|
||||
*/
|
||||
|
||||
const { AddonTestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/AddonTestUtils.jsm"
|
||||
);
|
||||
const { TestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/TestUtils.jsm"
|
||||
);
|
||||
|
||||
AddonTestUtils.init(this);
|
||||
AddonTestUtils.createAppInfo(
|
||||
"xpcshell@tests.mozilla.org",
|
||||
"XPCShell",
|
||||
"42",
|
||||
"42"
|
||||
);
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
const { AboutNewTab } = ChromeUtils.import(
|
||||
"resource:///modules/AboutNewTab.jsm"
|
||||
);
|
||||
const { PREFS_CONFIG } = ChromeUtils.import(
|
||||
"resource://activity-stream/lib/ActivityStream.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"BasePromiseWorker",
|
||||
"resource://gre/modules/PromiseWorker.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser"]);
|
||||
|
||||
const CACHE_WORKER_URL = "resource://activity-stream/lib/cache-worker.js";
|
||||
|
||||
/**
|
||||
* In order to make this test less brittle, much of Activity Stream is
|
||||
* initialized here in order to generate a state object at runtime, rather
|
||||
* than hard-coding one in. This requires quite a bit of machinery in order
|
||||
* to work properly. Specifically, we need to launch an HTTP server to serve
|
||||
* a dynamic layout, and then have that layout point to a local feed rather
|
||||
* than one from the Pocket CDN.
|
||||
*/
|
||||
add_task(async function setup() {
|
||||
do_get_profile();
|
||||
// The SearchService is also needed in order to construct the initial state,
|
||||
// which means that the AddonManager needs to be available.
|
||||
await AddonTestUtils.promiseStartupManager();
|
||||
|
||||
// The example.com domain will be used to host the dynamic layout JSON and
|
||||
// the top stories JSON.
|
||||
let server = AddonTestUtils.createHttpServer({ hosts: ["example.com"] });
|
||||
server.registerDirectory("/", do_get_cwd());
|
||||
|
||||
// Top Stories are disabled by default in our testing profiles.
|
||||
Services.prefs.setBoolPref(
|
||||
"browser.newtabpage.activity-stream.feeds.section.topstories",
|
||||
true
|
||||
);
|
||||
|
||||
let defaultDSConfig = JSON.parse(
|
||||
PREFS_CONFIG.get("discoverystream.config").getValue({
|
||||
geo: "US",
|
||||
locale: "en-US",
|
||||
})
|
||||
);
|
||||
|
||||
let newConfig = Object.assign(defaultDSConfig, {
|
||||
show_spocs: false,
|
||||
hardcoded_layout: false,
|
||||
layout_endpoint: "http://example.com/ds_layout.json",
|
||||
});
|
||||
|
||||
// Configure Activity Stream to query for the layout JSON file that points
|
||||
// at the local top stories feed.
|
||||
Services.prefs.setCharPref(
|
||||
"browser.newtabpage.activity-stream.discoverystream.config",
|
||||
JSON.stringify(newConfig)
|
||||
);
|
||||
|
||||
// We need to allow example.com as a place to get both the layout and the
|
||||
// top stories from.
|
||||
Services.prefs.setCharPref(
|
||||
"browser.newtabpage.activity-stream.discoverystream.endpoints",
|
||||
`http://example.com`
|
||||
);
|
||||
|
||||
Services.prefs.setBoolPref(
|
||||
"browser.newtabpage.activity-stream.telemetry.structuredIngestion",
|
||||
false
|
||||
);
|
||||
Services.prefs.setBoolPref("browser.ping-centre.telemetry", false);
|
||||
|
||||
// We need a default search engine set up for rendering the search input.
|
||||
let engine = await Services.search.addEngineWithDetails("Test engine", {
|
||||
template: "http://example.com/?s=%S",
|
||||
alias: "@testengine",
|
||||
});
|
||||
Services.search.defaultEngine = engine;
|
||||
|
||||
// Initialize Activity Stream, and pretend that a new window has been loaded
|
||||
// to kick off initializing all of the feeds.
|
||||
AboutNewTab.init();
|
||||
AboutNewTab.onBrowserReady();
|
||||
|
||||
// Much of Activity Stream initializes asynchronously. This is the easiest way
|
||||
// I could find to ensure that enough of the feeds had initialized to produce
|
||||
// a meaningful cached document.
|
||||
await TestUtils.waitForCondition(() => {
|
||||
let feed = AboutNewTab.activityStream.store.feeds.get(
|
||||
"feeds.discoverystreamfeed"
|
||||
);
|
||||
return feed.loaded;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets the Activity Stream Redux state from Activity Stream and sends it
|
||||
* into an instance of the cache worker to ensure that the resulting markup
|
||||
* and script makes sense.
|
||||
*/
|
||||
add_task(async function test_cache_worker() {
|
||||
let state = AboutNewTab.activityStream.store.getState();
|
||||
|
||||
let cacheWorker = new BasePromiseWorker(CACHE_WORKER_URL);
|
||||
let { page, script } = await cacheWorker.post("construct", [state]);
|
||||
ok(!!page.length, "Got page content");
|
||||
ok(!!script.length, "Got script content");
|
||||
|
||||
// The template strings should have been replaced.
|
||||
equal(
|
||||
page.indexOf("{{ MARKUP }}"),
|
||||
-1,
|
||||
"Page template should have {{ MARKUP }} replaced"
|
||||
);
|
||||
equal(
|
||||
page.indexOf("{{ CACHE_TIME }}"),
|
||||
-1,
|
||||
"Page template should have {{ CACHE_TIME }} replaced"
|
||||
);
|
||||
equal(
|
||||
script.indexOf("{{ STATE }}"),
|
||||
-1,
|
||||
"Script template should have {{ STATE }} replaced"
|
||||
);
|
||||
|
||||
// Now let's make sure that the generated script makes sense. We'll
|
||||
// evaluate it in a sandbox to make sure broken JS doesn't break the
|
||||
// test.
|
||||
let sandbox = Cu.Sandbox(Cu.getGlobalForObject({}));
|
||||
let passedState = null;
|
||||
|
||||
// window.NewtabRenderUtils.renderCache is the exposed API from
|
||||
// activity-stream.jsx that the script is expected to call to hydrate
|
||||
// the pre-rendered markup. We'll implement that, and use that to ensure
|
||||
// that the passed in state object matches the state we sent into the
|
||||
// worker.
|
||||
sandbox.window = {
|
||||
NewtabRenderUtils: {
|
||||
renderCache(aState) {
|
||||
passedState = aState;
|
||||
},
|
||||
},
|
||||
};
|
||||
Cu.evalInSandbox(script, sandbox);
|
||||
equal(
|
||||
sandbox.window.__FROM_STARTUP_CACHE__,
|
||||
true,
|
||||
"Should have set __FROM_STARTUP_CACHE__ to true"
|
||||
);
|
||||
|
||||
// The worker is expected to modify the state slightly before running
|
||||
// it through ReactDOMServer by setting App.isForStartupCache to true.
|
||||
// This allows React components to change their behaviour if the cache
|
||||
// is being generated.
|
||||
state.App.isForStartupCache = true;
|
||||
|
||||
// Some of the properties on the state might have values set to undefined.
|
||||
// There is no way to express a named undefined property on an object in
|
||||
// JSON, so we filter those out by stringifying and re-parsing.
|
||||
state = JSON.parse(JSON.stringify(state));
|
||||
|
||||
Assert.deepEqual(
|
||||
passedState,
|
||||
state,
|
||||
"Should have called renderCache with the expected state"
|
||||
);
|
||||
|
||||
// Now let's do a quick smoke-test on the markup to ensure that the
|
||||
// one Top Story from topstories.json is there.
|
||||
let parser = new DOMParser();
|
||||
let doc = parser.parseFromString(page, "text/html");
|
||||
let root = doc.getElementById("root");
|
||||
ok(root.childElementCount, "There are children on the root node");
|
||||
|
||||
// There should be the 1 top story, and 2 placeholders.
|
||||
equal(
|
||||
Array.from(root.querySelectorAll(".ds-card")).length,
|
||||
3,
|
||||
"There are 3 DSCards"
|
||||
);
|
||||
let cardHostname = doc.querySelector("[data-section-id='topstories'] .source")
|
||||
.innerText;
|
||||
equal(cardHostname, "bbc.com", "Card hostname is bbc.com");
|
||||
|
||||
let placeholders = doc.querySelectorAll(".ds-card.placeholder");
|
||||
equal(placeholders.length, 2, "There should be 2 placeholders");
|
||||
});
|
@ -1 +0,0 @@
|
||||
{"status":1,"settings":{"spocsPerNewTabs":0.5,"domainAffinityParameterSets":{"default":{"recencyFactor":0.5,"frequencyFactor":0.5,"combinedDomainFactor":0.5,"perfectFrequencyVisits":10,"perfectCombinedDomainScore":2,"multiDomainBoost":0,"itemScoreFactor":1},"fully-personalized":{"recencyFactor":0.5,"frequencyFactor":0.5,"combinedDomainFactor":0.5,"perfectFrequencyVisits":10,"perfectCombinedDomainScore":2,"itemScoreFactor":0.01,"multiDomainBoost":0}},"timeSegments":[{"id":"week","startTime":604800,"endTime":0,"weightPosition":1},{"id":"month","startTime":2592000,"endTime":604800,"weightPosition":0.5}],"recsExpireTime":5400,"version":"2c2aa06dac65ddb647d8902aaa60263c8e119ff2"},"spocs":[],"recommendations":[{"id":53093,"url":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAA/UlEQVR4nO3RMQ0AMAzAsPIn3d5DsBw2gkiZJWV+B/AyJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQmAP4K6zWNUjE4wAAAABJRU5ErkJggg==","domain":"bbc.com","title":"Why vegan junk food may be even worse for your health","excerpt":"While we might switch to a plant-based diet with the best intentions, the unseen risks of vegan fast foods might not show up for years.","image_src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAA/UlEQVR4nO3RMQ0AMAzAsPIn3d5DsBw2gkiZJWV+B/AyJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQGENiDIkxJMaQmAP4K6zWNUjE4wAAAABJRU5ErkJggg==","published_timestamp":"1580277600","engagement":"","parameter_set":"default","domain_affinities":{},"item_score":1}]}
|
@ -3,11 +3,6 @@ head =
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android'
|
||||
|
||||
[test_AboutHomeStartupCacheWorker.js]
|
||||
support-files =
|
||||
ds_layout.json
|
||||
topstories.json
|
||||
|
||||
[test_AboutNewTab.js]
|
||||
[test_ASRouterTargeting_attribution.js]
|
||||
skip-if = toolkit != "cocoa" # osx specific tests
|
||||
|
@ -13,7 +13,7 @@
|
||||
"tests": ["tart_flex", "ts_paint_flex"]
|
||||
},
|
||||
"other": {
|
||||
"tests": ["a11yr", "ts_paint", "twinopen", "sessionrestore", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup", "startup_about_home_paint", "startup_about_home_paint_cached", "pdfpaint"]
|
||||
"tests": ["a11yr", "ts_paint", "twinopen", "sessionrestore", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup", "startup_about_home_paint", "pdfpaint"]
|
||||
},
|
||||
"sessionrestore-many-windows": {
|
||||
"tests": ["sessionrestore_many_windows"]
|
||||
|
@ -187,33 +187,10 @@ class ts_paint_flex(ts_paint):
|
||||
|
||||
@register_test()
|
||||
class startup_about_home_paint(ts_paint):
|
||||
"""
|
||||
Tests loading about:home on startup with the about:home startup cache
|
||||
disabled, to more accurately simulate startup when the cache does not
|
||||
exist.
|
||||
"""
|
||||
url = None
|
||||
cycles = 20
|
||||
extensions = ['${talos}/startup_test/startup_about_home_paint/addon']
|
||||
tpmanifest = '${talos}/startup_test/startup_about_home_paint/startup_about_home_paint.manifest'
|
||||
preferences = {
|
||||
'browser.startup.homepage.abouthome_cache.enabled': False,
|
||||
}
|
||||
|
||||
|
||||
@register_test()
|
||||
class startup_about_home_paint_cached(ts_paint):
|
||||
"""
|
||||
Tests loading about:home on startup with the about:home startup cache
|
||||
enabled.
|
||||
"""
|
||||
url = None
|
||||
cycles = 20
|
||||
extensions = ['${talos}/startup_test/startup_about_home_paint/addon']
|
||||
tpmanifest = '${talos}/startup_test/startup_about_home_paint/startup_about_home_paint.manifest'
|
||||
preferences = {
|
||||
'browser.startup.homepage.abouthome_cache.enabled': True,
|
||||
}
|
||||
|
||||
|
||||
@register_test()
|
||||
|
Loading…
Reference in New Issue
Block a user