Bug 1555391 - Remove PrerenderData.jsm and usages (#5080)

This commit is contained in:
Kate Hudson 2019-05-29 17:35:15 -04:00 коммит произвёл Ed Lee
Родитель 1c086133c9
Коммит 570c5211da
9 изменённых файлов: 29 добавлений и 490 удалений

Просмотреть файл

@ -1,126 +0,0 @@
class _PrerenderData {
constructor(options) {
this.initialPrefs = options.initialPrefs;
this.initialSections = options.initialSections;
this._setValidation(options.validation);
}
get validation() {
return this._validation;
}
set validation(value) {
this._setValidation(value);
}
get invalidatingPrefs() {
return this._invalidatingPrefs;
}
// This is needed so we can use it in the constructor
_setValidation(value = []) {
this._validation = value;
this._invalidatingPrefs = value.reduce((result, next) => {
if (typeof next === "string") {
result.push(next);
return result;
} else if (next && next.oneOf) {
return result.concat(next.oneOf);
} else if (next && next.indexedDB) {
return result.concat(next.indexedDB);
} else if (next && next.jsonPrefs) {
return result.concat(next.jsonPrefs);
}
throw new Error("Your validation configuration is not properly configured");
}, []);
}
_isPrefEnabled(prefObj) {
try {
let data = JSON.parse(prefObj);
return (data && data.enabled) ? true : false; // eslint-disable-line no-unneeded-ternary
} catch (e) {
return false;
}
}
arePrefsValid(getPref, indexedDBPrefs) {
for (const prefs of this.validation) {
// {oneOf: ["foo", "bar"]}
if (prefs && prefs.oneOf && !prefs.oneOf.some(name => getPref(name) === this.initialPrefs[name])) {
return false;
// {indexedDB: ["foo", "bar"]}
} else if (indexedDBPrefs && prefs && prefs.indexedDB) {
const anyModifiedPrefs = prefs.indexedDB.some(prefName => indexedDBPrefs.some(pref => pref && pref[prefName]));
if (anyModifiedPrefs) {
return false;
}
// {jsonPrefs: ["foo", "bar"]}
} else if (prefs && prefs.jsonPrefs) {
const isPrefModified =
prefs.jsonPrefs.some(name => this._isPrefEnabled(getPref(name)) !== this.initialPrefs[name].enabled);
if (isPrefModified) {
return false;
}
// "foo"
} else if (getPref(prefs) !== this.initialPrefs[prefs]) {
return false;
}
}
return true;
}
}
this.PrerenderData = new _PrerenderData({
initialPrefs: {
"feeds.topsites": true,
"showSearch": true,
"topSitesRows": 1,
"feeds.section.topstories": true,
"feeds.section.highlights": true,
"sectionOrder": "topsites,topstories,highlights",
"collapsed": false,
"discoverystream.config": {"enabled": false},
},
// Prefs listed as invalidating will prevent the prerendered version
// of AS from being used if their value is something other than what is listed
// here. This is required because some preferences cause the page layout to be
// too different for the prerendered version to be used. Unfortunately, this
// will result in users who have modified some of their preferences not being
// able to get the benefits of prerendering.
validation: [
"feeds.topsites",
"showSearch",
"topSitesRows",
"sectionOrder",
// This means if either of these are set to their default values,
// prerendering can be used.
{oneOf: ["feeds.section.topstories", "feeds.section.highlights"]},
// If any component has the following preference set to `true` it will
// invalidate the prerendered version.
{indexedDB: ["collapsed"]},
// For below prefs, parse value to check enabled property. If enabled property
// differs from initial prefs enabled value, prerendering cannot be used
{jsonPrefs: ["discoverystream.config"]},
],
initialSections: [
{
enabled: true,
icon: "pocket",
id: "topstories",
order: 1,
title: {id: "header_recommended_by", values: {provider: "Pocket"}},
},
{
enabled: true,
id: "highlights",
icon: "highlights",
order: 2,
title: {id: "header_highlights"},
},
],
});
this._PrerenderData = _PrerenderData;
const EXPORTED_SYMBOLS = ["PrerenderData", "_PrerenderData"];

Просмотреть файл

@ -1,35 +1,9 @@
import {INITIAL_STATE, reducers} from "common/Reducers.jsm";
import {actionTypes as at} from "common/Actions.jsm";
import {Base} from "content-src/components/Base/Base";
import {initStore} from "content-src/lib/init-store";
import {PrerenderData} from "common/PrerenderData.jsm";
import {Provider} from "react-redux";
import React from "react";
import ReactDOMServer from "react-dom/server";
/**
* prerenderStore - Generate a store with the initial state required for a prerendered page
*
* @return {obj} A store
*/
export function prerenderStore() {
const store = initStore(reducers, INITIAL_STATE);
store.dispatch({type: at.PREFS_INITIAL_VALUES, data: PrerenderData.initialPrefs});
PrerenderData.initialSections.forEach(data => store.dispatch({type: at.SECTION_REGISTER, data}));
return store;
}
export function prerender(locale, strings,
renderToString = ReactDOMServer.renderToString) {
const store = prerenderStore();
const html = renderToString(
<Provider store={store}>
<Base
isPrerendered={true}
locale={locale}
strings={strings} />
</Provider>);
const html = renderToString(<div />);
// If this happens, it means pre-rendering is effectively disabled, so we
// need to sound the alarms:
@ -39,7 +13,7 @@ export function prerender(locale, strings,
return {
html,
state: store.getState(),
store,
state: {},
store: {getState() {}},
};
}

Просмотреть файл

@ -7,21 +7,19 @@ import React from "react";
import ReactDOM from "react-dom";
import {reducers} from "common/Reducers.jsm";
const store = initStore(reducers, global.gActivityStreamPrerenderedState);
const store = initStore(reducers);
new DetectUserSessionStart(store).sendEventOrAddListener();
// If we are starting in a prerendered state, we must wait until the first render
// to request state rehydration (see Base.jsx). If we are NOT in a prerendered state,
// we can request it immedately.
if (!global.gActivityStreamPrerenderedState) {
store.dispatch(ac.AlsoToMain({type: at.NEW_TAB_STATE_REQUEST}));
}
store.dispatch(ac.AlsoToMain({type: at.NEW_TAB_STATE_REQUEST}));
ReactDOM.hydrate(<Provider store={store}>
<Base
isFirstrun={global.document.location.href === "about:welcome"}
isPrerendered={!!global.gActivityStreamPrerenderedState}
isPrerendered={false}
locale={global.document.documentElement.lang}
strings={global.gActivityStreamStrings} />
</Provider>, document.getElementById("root"));

Просмотреть файл

@ -6,7 +6,6 @@ import {ConfirmDialog} from "content-src/components/ConfirmDialog/ConfirmDialog"
import {connect} from "react-redux";
import {DiscoveryStreamBase} from "content-src/components/DiscoveryStreamBase/DiscoveryStreamBase";
import {ErrorBoundary} from "content-src/components/ErrorBoundary/ErrorBoundary";
import {PrerenderData} from "common/PrerenderData.jsm";
import React from "react";
import {Search} from "content-src/components/Search/Search";
import {Sections} from "content-src/components/Sections/Sections";
@ -134,7 +133,6 @@ export class BaseContent extends React.PureComponent {
const {initialized} = App;
const prefs = props.Prefs.values;
const shouldBeFixedToTop = PrerenderData.arePrefsValid(name => prefs[name]);
const isDiscoveryStream = props.DiscoveryStream.config && props.DiscoveryStream.config.enabled;
let filteredSections = props.Sections;
@ -149,7 +147,6 @@ export class BaseContent extends React.PureComponent {
"outer-wrapper",
isDiscoveryStream && "ds-outer-wrapper-search-alignment",
isDiscoveryStream && "ds-outer-wrapper-breakpoint-override",
shouldBeFixedToTop && "fixed-to-top",
prefs.showSearch && this.state.fixedSearch && !noSectionsEnabled && "fixed-search",
prefs.showSearch && noSectionsEnabled && "only-search",
].filter(v => v).join(" ");

Просмотреть файл

@ -5,10 +5,6 @@
min-height: 100vh;
padding: ($section-spacing + $section-vertical-padding) $base-gutter $base-gutter;
&.fixed-to-top {
display: block;
}
&.only-search {
display: block;
padding-top: 134px;

Просмотреть файл

@ -5,7 +5,6 @@
const {actionCreators: ac, actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm");
const {Prefs} = ChromeUtils.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm");
const {PrerenderData} = ChromeUtils.import("resource://activity-stream/common/PrerenderData.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
@ -20,27 +19,11 @@ this.PrefsFeed = class PrefsFeed {
this._prefs = new Prefs();
}
// If any of the prefs are set to something other than what the
// prerendered version of AS expects, we can't use it.
async _setPrerenderPref() {
const indexedDBPrefs = await this._storage.getAll();
const prefsAreValid = PrerenderData.arePrefsValid(pref => this._prefs.get(pref), indexedDBPrefs);
this._prefs.set("prerender", prefsAreValid);
}
_checkPrerender(name) {
if (PrerenderData.invalidatingPrefs.includes(name)) {
this._setPrerenderPref();
}
}
onPrefChanged(name, value) {
const prefItem = this._prefMap.get(name);
if (prefItem) {
this.store.dispatch(ac[prefItem.skipBroadcast ? "OnlyToMain" : "BroadcastToContent"]({type: at.PREF_CHANGED, data: {name, value}}));
}
this._checkPrerender(name);
}
init() {
@ -78,8 +61,6 @@ this.PrefsFeed = class PrefsFeed {
// Set the initial state of all prefs in redux
this.store.dispatch(ac.BroadcastToContent({type: at.PREFS_INITIAL_VALUES, data: values}));
this._setPrerenderPref();
}
removeListeners() {
@ -90,7 +71,6 @@ this.PrefsFeed = class PrefsFeed {
const name = id === "topsites" ? id : `feeds.section.${id}`;
try {
await this._storage.set(name, value);
this._setPrerenderPref();
} catch (e) {
Cu.reportError("Could not set section preferences.");
}

Просмотреть файл

@ -1,50 +0,0 @@
import {prerender, prerenderStore} from "content-src/activity-stream-prerender";
import {PrerenderData} from "common/PrerenderData.jsm";
const messages = require("data/locales.json")["en-US"]; // eslint-disable-line import/no-commonjs
describe("prerenderStore", () => {
it("should start uninitialized", () => {
const store = prerenderStore();
const state = store.getState();
assert.equal(state.App.initialized, false);
});
it("should add the right initial prefs", () => {
const store = prerenderStore();
const state = store.getState();
assert.equal(state.Prefs.values, PrerenderData.initialPrefs);
});
it("should add TopStories as the first section", () => {
const store = prerenderStore();
const state = store.getState();
// TopStories
const [firstSection] = state.Sections;
assert.equal(firstSection.id, "topstories");
// it should start uninitialized
assert.equal(firstSection.initialized, false);
});
});
describe("prerender", () => {
it("should provide initial rendered state", () => {
const {store} = prerender("en-US", messages);
const state = store.getState();
assert.equal(state.App.initialized, false);
});
it("should throw if zero-length HTML content is returned", () => {
const boundPrerender = prerender.bind(null, "en-US", messages, () => "");
assert.throws(boundPrerender, Error, /no HTML returned/);
});
it("should throw if falsy HTML content is returned", () => {
const boundPrerender = prerender.bind(null, "en-US", messages, () => null);
assert.throws(boundPrerender, Error, /no HTML returned/);
});
});

Просмотреть файл

@ -1,134 +0,0 @@
import {_PrerenderData, PrerenderData} from "common/PrerenderData.jsm";
describe("_PrerenderData", () => {
describe("properties", () => {
it("should set .initialPrefs", () => {
const initialPrefs = {foo: true};
const instance = new _PrerenderData({initialPrefs});
assert.equal(instance.initialPrefs, initialPrefs);
});
it("should set .initialSections", () => {
const initialSections = [{id: "foo"}];
const instance = new _PrerenderData({initialSections});
assert.equal(instance.initialSections, initialSections);
});
it("should set .validation and .invalidatingPrefs in the constructor", () => {
const validation = ["foo", "bar", {oneOf: ["baz", "qux"]}];
const instance = new _PrerenderData({validation});
assert.equal(instance.validation, validation);
assert.deepEqual(instance.invalidatingPrefs, ["foo", "bar", "baz", "qux"]);
});
it("should also set .invalidatingPrefs when .validation is set", () => {
const validation = ["foo", "bar", {oneOf: ["baz", "qux"]}];
const instance = new _PrerenderData({validation});
const newValidation = ["foo", {oneOf: ["blah", "gloo"]}];
instance.validation = newValidation;
assert.equal(instance.validation, newValidation);
assert.deepEqual(instance.invalidatingPrefs, ["foo", "blah", "gloo"]);
});
it("should throw if an invalid validation config is set", () => {
// {stuff: []} is not a valid configuration type
assert.throws(() => new _PrerenderData({validation: ["foo", {stuff: ["bar"]}]}));
});
});
describe("#arePrefsValid", () => {
let FAKE_PREFS;
const getPrefs = pref => FAKE_PREFS[pref];
beforeEach(() => {
FAKE_PREFS = {};
});
it("should return true if all prefs match", () => {
FAKE_PREFS = {foo: true, bar: false};
const instance = new _PrerenderData({
initialPrefs: FAKE_PREFS,
validation: ["foo", "bar"],
});
assert.isTrue(instance.arePrefsValid(getPrefs));
});
it("should return true if all *invalidating* prefs match", () => {
FAKE_PREFS = {foo: true, bar: false};
const instance = new _PrerenderData({
initialPrefs: {foo: true, bar: true},
validation: ["foo"],
});
assert.isTrue(instance.arePrefsValid(getPrefs));
});
it("should return true if one each oneOf group matches", () => {
FAKE_PREFS = {foo: false, floo: true, bar: false, blar: true};
const instance = new _PrerenderData({
initialPrefs: {foo: true, floo: true, bar: true, blar: true},
validation: [{oneOf: ["foo", "floo"]}, {oneOf: ["bar", "blar"]}],
});
assert.isTrue(instance.arePrefsValid(getPrefs));
});
it("should return false if an invalidating pref is mismatched", () => {
FAKE_PREFS = {foo: true, bar: false};
const instance = new _PrerenderData({
initialPrefs: {foo: true, bar: true},
validation: ["foo", "bar"],
});
assert.isFalse(instance.arePrefsValid(getPrefs));
});
it("should return false if none of the oneOf group matches", () => {
FAKE_PREFS = {foo: true, bar: false, baz: false};
const instance = new _PrerenderData({
initialPrefs: {foo: true, bar: true, baz: true},
validation: ["foo", {oneOf: ["bar", "baz"]}],
});
assert.isFalse(instance.arePrefsValid(getPrefs));
});
it("should return false if an indexedDB pref is changed", () => {
FAKE_PREFS = {foo: true, bar: false};
const instance = new _PrerenderData({
initialPrefs: {foo: true, bar: true},
validation: ["foo", {indexedDB: ["collapsed"]}],
});
assert.isFalse(instance.arePrefsValid(getPrefs, [{collapsed: true}]));
});
it("should return false if any of jsonPrefs group enabled value not matches", () => {
FAKE_PREFS = {foo: true, bar: "{\"enabled\": true}", baz: "{\"enabled\": true}"};
const instance = new _PrerenderData({
initialPrefs: {foo: true, bar: {"enabled": true}, baz: {"enabled": false}},
validation: ["foo", {jsonPrefs: ["baz", "bar"]}],
});
assert.isFalse(instance.arePrefsValid(getPrefs));
});
it("should treat invalid json as having the component disabled", () => {
FAKE_PREFS = {foo: true, bar: "{\"enabled\": true}", baz: {}};
const instance = new _PrerenderData({
initialPrefs: {foo: true, bar: {"enabled": true}, baz: {"enabled": false}},
validation: ["foo", {jsonPrefs: ["baz", "bar"]}],
});
assert.isTrue(instance.arePrefsValid(getPrefs));
});
it("should return true if all of jsonPrefs group enabled value matches", () => {
FAKE_PREFS = {foo: true, bar: "{\"enabled\": true}", baz: "{\"enabled\": false}"};
const instance = new _PrerenderData({
initialPrefs: {foo: true, bar: {"enabled": true}, baz: {"enabled": false}},
validation: ["foo", {jsonPrefs: ["baz", "bar"]}],
});
assert.isTrue(instance.arePrefsValid(getPrefs));
});
});
});
// This is the instance used by Activity Stream
describe("PrerenderData", () => {
it("should set initial values for all invalidating prefs", () => {
PrerenderData.invalidatingPrefs.forEach(pref => {
assert.property(PrerenderData.initialPrefs, pref);
});
});
});

Просмотреть файл

@ -1,10 +1,6 @@
import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
import {GlobalOverrider} from "test/unit/utils";
import {PrefsFeed} from "lib/PrefsFeed.jsm";
import {PrerenderData} from "common/PrerenderData.jsm";
const {initialPrefs} = PrerenderData;
const PRERENDER_PREF_NAME = "prerender";
let overrider = new GlobalOverrider();
@ -14,7 +10,7 @@ describe("PrefsFeed", () => {
let sandbox;
beforeEach(() => {
sandbox = sinon.createSandbox();
FAKE_PREFS = new Map([["foo", 1], ["bar", 2]]);
FAKE_PREFS = new Map([["foo", 1], ["bar", 2], ["baz", {value: 1, skipBroadcast: true}]]);
feed = new PrefsFeed(FAKE_PREFS);
const storage = {
getAll: sandbox.stub().resolves(),
@ -43,11 +39,6 @@ describe("PrefsFeed", () => {
sandbox.restore();
});
function setFakePrefsWithInitialValue() {
Object.keys(initialPrefs).forEach(name => FAKE_PREFS.set(name,
typeof(initialPrefs[name]) === "object" ? JSON.stringify(initialPrefs[name]) : initialPrefs[name]));
}
it("should set a pref when a SET_PREF action is received", () => {
feed.onAction(ac.SetPref("foo", 2));
assert.calledWith(feed._prefs.set, "foo", 2);
@ -81,118 +72,31 @@ describe("PrefsFeed", () => {
feed.onPrefChanged("foo", 2);
assert.calledWith(feed.store.dispatch, ac.BroadcastToContent({type: at.PREF_CHANGED, data: {name: "foo", value: 2}}));
});
describe("INIT prerendering", () => {
it("should set a prerender pref on init", async () => {
sandbox.stub(feed, "_setPrerenderPref");
await feed.init();
assert.calledOnce(feed._setPrerenderPref);
it("should set storage pref on UPDATE_SECTION_PREFS", async () => {
await feed.onAction({
type: at.UPDATE_SECTION_PREFS,
data: {id: "topsites", value: {collapsed: false}},
});
it("should set prerender pref to true if prefs match initial values", async () => {
setFakePrefsWithInitialValue();
await feed._setPrerenderPref();
assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, true);
assert.calledWith(feed._storage.set, "topsites", {collapsed: false});
});
it("should set storage pref with section prefix on UPDATE_SECTION_PREFS", async () => {
await feed.onAction({
type: at.UPDATE_SECTION_PREFS,
data: {id: "topstories", value: {collapsed: false}},
});
it("should set prerender pref to false if a pref does not match its initial value", async () => {
setFakePrefsWithInitialValue();
FAKE_PREFS.set("showSearch", false);
await feed._setPrerenderPref();
assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, false);
});
it("should set prerender pref to true if indexedDB prefs are unchanged", async () => {
setFakePrefsWithInitialValue();
feed._storage.getAll.resolves([{collapsed: false}, {collapsed: false}]);
await feed._setPrerenderPref();
assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, true);
});
it("should set prerender pref to false if a indexedDB pref changed value", async () => {
setFakePrefsWithInitialValue();
FAKE_PREFS.set("showSearch", false);
feed._storage.getAll.resolves([{collapsed: false}, {collapsed: true}]);
await feed._setPrerenderPref();
assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, false);
assert.calledWith(feed._storage.set, "feeds.section.topstories", {collapsed: false});
});
it("should catch errors on UPDATE_SECTION_PREFS", async () => {
feed._storage.set.throws(new Error("foo"));
assert.doesNotThrow(async () => {
await feed.onAction({
type: at.UPDATE_SECTION_PREFS,
data: {id: "topstories", value: {collapsed: false}},
});
});
});
describe("indexedDB changes", () => {
it("should call _setIndexedDBPref on UPDATE_SECTION_PREFS", () => {
sandbox.stub(feed, "_setIndexedDBPref");
feed.onAction({type: at.UPDATE_SECTION_PREFS, data: {}});
assert.calledOnce(feed._setIndexedDBPref);
});
it("should store the pref value", async () => {
sandbox.stub(feed, "_setPrerenderPref");
await feed._setIndexedDBPref("topsites", "foo");
assert.calledOnce(feed._storage.set);
assert.calledWith(feed._storage.set, "topsites", "foo");
});
it("should call _setPrerenderPref", async () => {
sandbox.stub(feed, "_setPrerenderPref");
await feed._setIndexedDBPref("topsites", "foo");
assert.calledOnce(feed._setPrerenderPref);
});
it("should catch any save errors", () => {
const globals = new GlobalOverrider();
globals.sandbox.spy(global.Cu, "reportError");
feed._storage.set.throws(new Error());
assert.doesNotThrow(() => feed._setIndexedDBPref());
assert.calledOnce(Cu.reportError);
globals.restore();
});
});
describe("onPrefChanged prerendering", () => {
it("should not change the prerender pref if the pref is not included in invalidatingPrefs", () => {
feed.onPrefChanged("foo123", true);
assert.notCalled(feed._prefs.set);
});
it("should set the prerender pref to false if a pref in invalidatingPrefs is changed from its original value", () => {
sandbox.stub(feed, "_setPrerenderPref");
setFakePrefsWithInitialValue();
feed._prefs.set("showSearch", false);
feed.onPrefChanged("showSearch", false);
assert.calledOnce(feed._setPrerenderPref);
});
it("should set the prerender pref back to true if the invalidatingPrefs are changed back to their original values", () => {
sandbox.stub(feed, "_setPrerenderPref");
setFakePrefsWithInitialValue();
FAKE_PREFS.set("showSearch", false);
feed._prefs.set("showSearch", true);
feed.onPrefChanged("showSearch", true);
assert.calledOnce(feed._setPrerenderPref);
});
it("should set the prerendered pref to true", async () => {
setFakePrefsWithInitialValue();
FAKE_PREFS.set("showSearch", false);
feed._prefs.set("showSearch", true);
feed.onPrefChanged("showSearch", true);
await feed._setPrerenderPref();
assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, true);
});
it("should set the prerendered pref to false", async () => {
setFakePrefsWithInitialValue();
FAKE_PREFS.set("showSearch", false);
feed._prefs.set("showSearch", false);
feed.onPrefChanged("showSearch", false);
await feed._setPrerenderPref();
assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, false);
});
it("should send OnlyToMain pref update if config for pref has skipBroadcast: true", async () => {
feed.onPrefChanged("baz", {value: 2, skipBroadcast: true});
assert.calledWith(feed.store.dispatch, ac.OnlyToMain({type: at.PREF_CHANGED, data: {name: "baz", value: {value: 2, skipBroadcast: true}}}));
});
});