forked from mirrors/gecko-dev
Bug 1555507 - Add modal escaping, simple prerendering and bug fixes to Activity Stream r=k88hudson
Differential Revision: https://phabricator.services.mozilla.com/D33089 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
cc371c6881
commit
bd822928e5
446 changed files with 2519 additions and 8614 deletions
|
|
@ -57,7 +57,6 @@ browser/components/pocket/content/panels/js/vendor/**
|
|||
|
||||
# Ignore newtab files
|
||||
# Kept in sync with browser/components/newtab/.eslintignore
|
||||
browser/components/newtab/bin/prerender.js
|
||||
browser/components/newtab/data/
|
||||
browser/components/newtab/logs/
|
||||
browser/components/newtab/prerendered/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
bin/prerender.js
|
||||
data/
|
||||
logs/
|
||||
prerendered/
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ npm-debug.log
|
|||
.gitignore
|
||||
|
||||
/.git/
|
||||
/bin/prerender.js
|
||||
/bin/prerender.js.map
|
||||
/data/locales.json
|
||||
/dist/
|
||||
/logs/
|
||||
|
|
|
|||
|
|
@ -31,14 +31,12 @@ const IS_PRIVILEGED_PROCESS = Services.appinfo.remoteType === E10SUtils.PRIVILEG
|
|||
const IS_RELEASE_OR_BETA = AppConstants.RELEASE_OR_BETA;
|
||||
|
||||
const PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS = "browser.tabs.remote.separatePrivilegedContentProcess";
|
||||
const PREF_ACTIVITY_STREAM_PRERENDER_ENABLED = "browser.newtabpage.activity-stream.prerender";
|
||||
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
|
||||
|
||||
function AboutNewTabService() {
|
||||
Services.obs.addObserver(this, TOPIC_APP_QUIT);
|
||||
Services.obs.addObserver(this, TOPIC_LOCALES_CHANGE);
|
||||
Services.prefs.addObserver(PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS, this);
|
||||
Services.prefs.addObserver(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED, this);
|
||||
if (!IS_RELEASE_OR_BETA) {
|
||||
Services.prefs.addObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
|
||||
}
|
||||
|
|
@ -90,7 +88,6 @@ AboutNewTabService.prototype = {
|
|||
|
||||
_newTabURL: ABOUT_URL,
|
||||
_activityStreamEnabled: false,
|
||||
_activityStreamPrerender: false,
|
||||
_activityStreamPath: "",
|
||||
_activityStreamDebug: false,
|
||||
_privilegedAboutContentProcess: false,
|
||||
|
|
@ -110,9 +107,6 @@ AboutNewTabService.prototype = {
|
|||
this._privilegedAboutContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS);
|
||||
this.updatePrerenderedPath();
|
||||
this.notifyChange();
|
||||
} else if (data === PREF_ACTIVITY_STREAM_PRERENDER_ENABLED) {
|
||||
this._activityStreamPrerender = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED);
|
||||
this.notifyChange();
|
||||
} else if (!IS_RELEASE_OR_BETA && data === PREF_ACTIVITY_STREAM_DEBUG) {
|
||||
this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false);
|
||||
this.updatePrerenderedPath();
|
||||
|
|
@ -158,10 +152,6 @@ AboutNewTabService.prototype = {
|
|||
`${BASE_URL}data/content/activity-stream.bundle.js`,
|
||||
];
|
||||
|
||||
if (this._activityStreamPrerender) {
|
||||
scripts.unshift(`${BASE_URL}prerendered/static/activity-stream-initial-state.js`);
|
||||
}
|
||||
|
||||
for (let script of scripts) {
|
||||
Services.scriptloader.loadSubScript(script, win); // Synchronous call
|
||||
}
|
||||
|
|
@ -217,7 +207,6 @@ AboutNewTabService.prototype = {
|
|||
this._activityStreamEnabled = false;
|
||||
}
|
||||
this._privilegedAboutContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS);
|
||||
this._activityStreamPrerender = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED);
|
||||
if (!IS_RELEASE_OR_BETA) {
|
||||
this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false);
|
||||
}
|
||||
|
|
@ -245,13 +234,11 @@ AboutNewTabService.prototype = {
|
|||
get defaultURL() {
|
||||
// Generate the desired activity stream resource depending on state, e.g.,
|
||||
// resource://activity-stream/prerendered/ar/activity-stream.html
|
||||
// resource://activity-stream/prerendered/en-US/activity-stream-prerendered.html
|
||||
// resource://activity-stream/prerendered/static/activity-stream-debug.html
|
||||
return [
|
||||
"resource://activity-stream/prerendered/",
|
||||
this._activityStreamPath,
|
||||
"activity-stream",
|
||||
this._activityStreamPrerender ? "-prerendered" : "",
|
||||
// Debug version loads dev scripts but noscripts separately loads scripts
|
||||
this._activityStreamDebug && !this._privilegedAboutContentProcess ? "-debug" : "",
|
||||
this._privilegedAboutContentProcess ? "-noscripts" : "",
|
||||
|
|
@ -262,15 +249,10 @@ AboutNewTabService.prototype = {
|
|||
/*
|
||||
* Returns the about:welcome URL
|
||||
*
|
||||
* This is calculated in the same way the default URL is, except that we don't
|
||||
* allow prerendering.
|
||||
* This is calculated in the same way the default URL is.
|
||||
*/
|
||||
get welcomeURL() {
|
||||
const prerenderEnabled = this._activityStreamPrerender;
|
||||
this._activityStreamPrerender = false;
|
||||
const url = this.defaultURL;
|
||||
this._activityStreamPrerender = prerenderEnabled;
|
||||
return url;
|
||||
return this.defaultURL;
|
||||
},
|
||||
|
||||
get newTabURL() {
|
||||
|
|
@ -301,10 +283,6 @@ AboutNewTabService.prototype = {
|
|||
return this._activityStreamEnabled;
|
||||
},
|
||||
|
||||
get activityStreamPrerender() {
|
||||
return this._activityStreamPrerender;
|
||||
},
|
||||
|
||||
get activityStreamDebug() {
|
||||
return this._activityStreamDebug;
|
||||
},
|
||||
|
|
@ -352,7 +330,6 @@ AboutNewTabService.prototype = {
|
|||
Services.obs.removeObserver(this, TOPIC_APP_QUIT);
|
||||
Services.obs.removeObserver(this, TOPIC_LOCALES_CHANGE);
|
||||
Services.prefs.removeObserver(PREF_SEPARATE_PRIVILEGEDABOUT_CONTENT_PROCESS, this);
|
||||
Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED, this);
|
||||
if (!IS_RELEASE_OR_BETA) {
|
||||
Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ const fs = require("fs");
|
|||
const {mkdir} = require("shelljs");
|
||||
const path = require("path");
|
||||
|
||||
// Note: this file is generated by webpack from content-src/activity-stream-prerender.jsx
|
||||
const {prerender} = require("./prerender");
|
||||
|
||||
const {CENTRAL_LOCALES, DEFAULT_LOCALE} = require("./locales");
|
||||
|
||||
// Note: DEFAULT_OPTIONS.baseUrl should match BASE_URL in aboutNewTabService.js
|
||||
|
|
@ -64,11 +61,9 @@ function getTextDirection(locale) {
|
|||
* {str} options.baseUrl The base URL for all local assets
|
||||
* {bool} options.debug Should we use dev versions of JS libraries?
|
||||
* {bool} options.noscripts Should we include scripts in the prerendered files?
|
||||
* @param {str} html The prerendered HTML created with React.renderToString (optional)
|
||||
* @return {str} An HTML document as a string
|
||||
*/
|
||||
function templateHTML(options, html) {
|
||||
const isPrerendered = !!html;
|
||||
function templateHTML(options) {
|
||||
const debugString = options.debug ? "-dev" : "";
|
||||
const scripts = [
|
||||
"chrome://browser/content/contentSearchUI.js",
|
||||
|
|
@ -82,9 +77,6 @@ function templateHTML(options, html) {
|
|||
`${options.baseUrl}prerendered/${options.locale}/activity-stream-strings.js`,
|
||||
`${options.baseUrl}data/content/activity-stream.bundle.js`,
|
||||
];
|
||||
if (isPrerendered) {
|
||||
scripts.unshift(`${options.baseUrl}prerendered/static/activity-stream-initial-state.js`);
|
||||
}
|
||||
|
||||
// Add spacing and script tags
|
||||
const scriptRender = `\n${scripts.map(script => ` <script src="${script}"></script>`).join("\n")}`;
|
||||
|
|
@ -101,7 +93,7 @@ function templateHTML(options, html) {
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root">${isPrerendered ? html : "<!-- Regular React Rendering -->"}</div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>${options.noscripts ? "" : scriptRender}
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -135,25 +127,20 @@ window.${name} = ${JSON.stringify(state, null, 2)};
|
|||
* @param {string} name Something to identify in the console
|
||||
* @param {string} destPath Path to write the files to
|
||||
* @param {Map} filesMap Mapping of a string file name to templater
|
||||
* @param {Object} prerenderData Contains the html and state
|
||||
* @param {Object} options Various options for the templater
|
||||
*/
|
||||
function writeFiles(name, destPath, filesMap, {html, state}, options) {
|
||||
function writeFiles(name, destPath, filesMap, options) {
|
||||
for (const [file, templater] of filesMap) {
|
||||
fs.writeFileSync(path.join(destPath, file), templater({html, options, state}));
|
||||
fs.writeFileSync(path.join(destPath, file), templater({options}));
|
||||
}
|
||||
console.log("\x1b[32m", `✓ ${name}`, "\x1b[0m");
|
||||
}
|
||||
|
||||
const STATIC_FILES = new Map([
|
||||
["activity-stream-debug.html", ({options}) => templateHTML(options)],
|
||||
["activity-stream-initial-state.js", ({state}) => templateJs("gActivityStreamPrerenderedState", "static", state)],
|
||||
["activity-stream-prerendered-debug.html", ({html, options}) => templateHTML(options, html)],
|
||||
]);
|
||||
|
||||
const LOCALIZED_FILES = new Map([
|
||||
["activity-stream-prerendered.html", ({html, options}) => templateHTML(options, html)],
|
||||
["activity-stream-prerendered-noscripts.html", ({html, options}) => templateHTML(Object.assign({}, options, {noscripts: true}), html)],
|
||||
["activity-stream-strings.js", ({options: {locale, strings}}) => templateJs("gActivityStreamStrings", locale, strings)],
|
||||
["activity-stream.html", ({options}) => templateHTML(options)],
|
||||
["activity-stream-noscripts.html", ({options}) => templateHTML(Object.assign({}, options, {noscripts: true}))],
|
||||
|
|
@ -200,7 +187,6 @@ function main() { // eslint-disable-line max-statements
|
|||
continue;
|
||||
}
|
||||
|
||||
const prerenderData = prerender(locale, strings);
|
||||
const options = Object.assign({}, baseOptions, {
|
||||
direction: getTextDirection(locale),
|
||||
locale,
|
||||
|
|
@ -210,13 +196,13 @@ function main() { // eslint-disable-line max-statements
|
|||
// Put locale-specific files in their own directory
|
||||
const localePath = path.join(prerenderedPath, "locales", locale);
|
||||
mkdir("-p", localePath);
|
||||
writeFiles(locale, localePath, LOCALIZED_FILES, prerenderData, options);
|
||||
writeFiles(locale, localePath, LOCALIZED_FILES, options);
|
||||
|
||||
// Only write static files once for the default locale
|
||||
if (locale === DEFAULT_LOCALE) {
|
||||
const staticPath = path.join(prerenderedPath, "static");
|
||||
mkdir("-p", staticPath);
|
||||
writeFiles(`${locale} (static)`, staticPath, STATIC_FILES, prerenderData,
|
||||
writeFiles(`${locale} (static)`, staticPath, STATIC_FILES,
|
||||
Object.assign({}, options, {debug: true}));
|
||||
|
||||
// Save the default strings to compare against other locales' strings
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ for (const type of [
|
|||
"OPEN_NEW_WINDOW",
|
||||
"OPEN_PRIVATE_WINDOW",
|
||||
"OPEN_WEBEXT_SETTINGS",
|
||||
"PAGE_PRERENDERED",
|
||||
"PLACES_BOOKMARK_ADDED",
|
||||
"PLACES_BOOKMARK_REMOVED",
|
||||
"PLACES_HISTORY_CLEARED",
|
||||
|
|
|
|||
|
|
@ -15,17 +15,10 @@ if (typeof Services !== "undefined") {
|
|||
// Borrow the high-resolution timer from the hidden window....
|
||||
// eslint-disable-next-line block-scoped-var
|
||||
usablePerfObj = Services.appShell.hiddenDOMWindow.performance;
|
||||
} else if (typeof performance !== "undefined") {
|
||||
} else {
|
||||
// we must be running in content space
|
||||
// eslint-disable-next-line no-undef
|
||||
usablePerfObj = performance;
|
||||
} else {
|
||||
// This is a dummy object so this file doesn't crash in the node prerendering
|
||||
// task.
|
||||
usablePerfObj = {
|
||||
now() {},
|
||||
mark() {},
|
||||
};
|
||||
}
|
||||
|
||||
function _PerfService(options) {
|
||||
|
|
|
|||
|
|
@ -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,45 +0,0 @@
|
|||
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>);
|
||||
|
||||
// If this happens, it means pre-rendering is effectively disabled, so we
|
||||
// need to sound the alarms:
|
||||
if (!html || !html.length) {
|
||||
throw new Error("no HTML returned");
|
||||
}
|
||||
|
||||
return {
|
||||
html,
|
||||
state: store.getState(),
|
||||
store,
|
||||
};
|
||||
}
|
||||
|
|
@ -7,21 +7,15 @@ 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}
|
||||
locale={global.document.documentElement.lang}
|
||||
strings={global.gActivityStreamStrings} />
|
||||
</Provider>, document.getElementById("root"));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {addLocaleData, IntlProvider} from "react-intl";
|
||||
import {actionCreators as ac} from "common/Actions.jsm";
|
||||
import {OUTGOING_MESSAGE_NAME as AS_GENERAL_OUTGOING_MESSAGE_NAME} from "content-src/lib/init-store";
|
||||
import {generateMessages} from "./rich-text-strings";
|
||||
import {generateBundles} from "./rich-text-strings";
|
||||
import {ImpressionsWrapper} from "./components/ImpressionsWrapper/ImpressionsWrapper";
|
||||
import {LocalizationProvider} from "fluent-react";
|
||||
import {NEWTAB_DARK_THEME} from "content-src/lib/constants";
|
||||
|
|
@ -257,7 +257,7 @@ export class ASRouterUISurface extends React.PureComponent {
|
|||
shouldSendImpressionOnUpdate={shouldSendImpressionOnUpdate}
|
||||
// This helps with testing
|
||||
document={this.props.document}>
|
||||
<LocalizationProvider messages={generateMessages(content)}>
|
||||
<LocalizationProvider bundles={generateBundles(content)}>
|
||||
<SnippetComponent
|
||||
{...this.state.message}
|
||||
UISurface="NEWTAB_FOOTER_BAR"
|
||||
|
|
@ -298,7 +298,7 @@ export class ASRouterUISurface extends React.PureComponent {
|
|||
} else if (message.template === "return_to_amo_overlay") {
|
||||
global.document.body.classList.add("amo");
|
||||
return (
|
||||
<LocalizationProvider messages={generateMessages({"amo_html": message.content.text})}>
|
||||
<LocalizationProvider messages={generateBundles({"amo_html": message.content.text})}>
|
||||
<ReturnToAMO
|
||||
{...message}
|
||||
UISurface="NEWTAB_OVERLAY"
|
||||
|
|
|
|||
|
|
@ -24,9 +24,13 @@ export class ModalOverlayWrapper extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const {props} = this;
|
||||
let className = props.unstyled ? "" : "modalOverlayInner active";
|
||||
if (props.innerClassName) {
|
||||
className += ` ${props.innerClassName}`;
|
||||
}
|
||||
return (<React.Fragment>
|
||||
<div className="modalOverlayOuter active" onClick={props.onClose} role="presentation" />
|
||||
<div className={`modalOverlayInner active ${props.innerClassName || ""}`}
|
||||
<div className="modalOverlayOuter active" onClick={props.onClose} onKeyDown={this.onKeyDown} role="presentation" />
|
||||
<div className={className}
|
||||
aria-labelledby={props.headerId}
|
||||
id={props.id}
|
||||
role="dialog">
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: none;
|
||||
z-index: 1100;
|
||||
|
|
@ -20,10 +21,10 @@
|
|||
|
||||
.modalOverlayInner {
|
||||
width: 960px;
|
||||
height: 570px;
|
||||
position: fixed;
|
||||
top: calc(50% - 285px); // halfway down minus half the height of the modal
|
||||
top: 20%;
|
||||
left: calc(50% - 480px); // halfway across minus half the width of the modal
|
||||
max-height: calc(100% - 100px);
|
||||
background: $white;
|
||||
box-shadow: 0 1px 15px 0 $black-30;
|
||||
border-radius: 4px;
|
||||
|
|
@ -75,6 +76,7 @@
|
|||
|
||||
.footer {
|
||||
border-top: 1px solid $grey-30;
|
||||
border-radius: 4px;
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import {MessageContext} from "fluent";
|
||||
import {FluentBundle} from "fluent";
|
||||
|
||||
/**
|
||||
* Properties that allow rich text MUST be added to this list.
|
||||
|
|
@ -21,10 +21,10 @@ export const RICH_TEXT_KEYS = Object.keys(RICH_TEXT_CONFIG);
|
|||
* Generates an array of messages suitable for fluent's localization provider
|
||||
* including all needed strings for rich text.
|
||||
* @param {object} content A .content object from an ASR message (i.e. message.content)
|
||||
* @returns {MessageContext[]} A array containing the fluent message context
|
||||
* @returns {FluentBundle[]} A array containing the fluent message context
|
||||
*/
|
||||
export function generateMessages(content) {
|
||||
const cx = new MessageContext("en-US");
|
||||
export function generateBundles(content) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
|
||||
RICH_TEXT_KEYS.forEach(key => {
|
||||
const attrs = RICH_TEXT_CONFIG[key];
|
||||
|
|
@ -34,7 +34,7 @@ export function generateMessages(content) {
|
|||
const attr = attrsToTry.pop();
|
||||
string = content[attr];
|
||||
}
|
||||
cx.addMessages(`${key} = ${string}`);
|
||||
bundle.addMessages(`${key} = ${string}`);
|
||||
});
|
||||
return [cx];
|
||||
return [bundle];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export class OnboardingMessage extends React.PureComponent {
|
|||
const {props} = this;
|
||||
const {button_label, header} = props.extraTemplateStrings;
|
||||
return (
|
||||
<ModalOverlay {...props} button_label={button_label} title={header}>
|
||||
<ModalOverlay {...props} button_label={button_label} title={header} >
|
||||
<div className="onboardingMessageContainer">
|
||||
{props.bundle.map(message => (
|
||||
<OnboardingCard key={message.id}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
grid-template-columns: auto auto auto;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
min-height: 500px;
|
||||
|
||||
// at 850px, the cards go from vertical layout to horizontal layout
|
||||
@media(max-width: 850px) {
|
||||
|
|
|
|||
|
|
@ -539,7 +539,7 @@ export class ASRouterAdminInner extends React.PureComponent {
|
|||
}
|
||||
const errors = this.refs.targetingParamsEval && this.refs.targetingParamsEval.innerText.length;
|
||||
return (
|
||||
<ModalOverlay title="New targeting parameters" button_label={errors ? "Cancel" : "Done"} onDoneButton={this.onPasteTargetingParams}>
|
||||
<ModalOverlay innerStyle="pasteModal" title="New targeting parameters" button_label={errors ? "Cancel" : "Done"} onDismissBundle={this.onPasteTargetingParams}>
|
||||
<div className="onboardingMessage">
|
||||
<p>
|
||||
<textarea onChange={this.onNewTargetingParams} value={this.state.newStringTargetingParameters} rows="20" cols="60" />
|
||||
|
|
|
|||
|
|
@ -219,4 +219,8 @@
|
|||
.ds-component {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.modalOverlayInner {
|
||||
height: 80%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
@ -47,16 +46,6 @@ export class _Base extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Request state AFTER the first render to ensure we don't cause the
|
||||
// prerendered DOM to be unmounted. Otherwise, NEW_TAB_STATE_REQUEST is
|
||||
// dispatched right after the store is ready.
|
||||
if (this.props.isPrerendered) {
|
||||
this.props.dispatch(ac.AlsoToMain({type: at.NEW_TAB_STATE_REQUEST}));
|
||||
this.props.dispatch(ac.AlsoToMain({type: at.PAGE_PRERENDERED}));
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.updateTheme();
|
||||
}
|
||||
|
|
@ -80,10 +69,9 @@ export class _Base extends React.PureComponent {
|
|||
render() {
|
||||
const {props} = this;
|
||||
const {App, locale, strings} = props;
|
||||
const {initialized} = App;
|
||||
const isDevtoolsEnabled = props.Prefs.values["asrouter.devtoolsEnabled"];
|
||||
|
||||
if (!props.isPrerendered && !initialized) {
|
||||
if (!App.initialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +122,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 +136,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;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ export class ContextMenu extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
return (<span role="menu" className="context-menu" onClick={this.onClick} onKeyDown={this.onClick} tabIndex="0" >
|
||||
// Disabling focus on the menu span allows the first tab to focus on the first menu item instead of the wrapper.
|
||||
// eslint-disable-next-line jsx-a11y/interactive-supports-focus
|
||||
return (<span role="menu" className="context-menu" onClick={this.onClick} onKeyDown={this.onClick} >
|
||||
<ul className="context-menu-list">
|
||||
{this.props.options.map((option, i) => (option.type === "separator" ?
|
||||
(<li key={i} className="separator" />) :
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
.impression-observer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {CollapsibleSection} from "content-src/components/CollapsibleSection/Coll
|
|||
import {ComponentPerfTimer} from "content-src/components/ComponentPerfTimer/ComponentPerfTimer";
|
||||
import {connect} from "react-redux";
|
||||
import {injectIntl} from "react-intl";
|
||||
import {ModalOverlayWrapper} from "../../asrouter/components/ModalOverlay/ModalOverlay";
|
||||
import React from "react";
|
||||
import {SearchShortcutsForm} from "./SearchShortcutsForm";
|
||||
import {TOP_SITES_MAX_SITES_PER_ROW} from "common/Reducers.jsm";
|
||||
|
|
@ -139,26 +140,24 @@ export class _TopSites extends React.PureComponent {
|
|||
<div className="edit-topsites-wrapper">
|
||||
{editForm &&
|
||||
<div className="edit-topsites">
|
||||
<div className="modal-overlay" onClick={this.onEditFormClose} role="presentation" />
|
||||
<div className="modal">
|
||||
<ModalOverlayWrapper unstyled={true} onClose={this.onEditFormClose} innerClassName="modal" >
|
||||
<TopSiteForm
|
||||
site={props.TopSites.rows[editForm.index]}
|
||||
onClose={this.onEditFormClose}
|
||||
dispatch={this.props.dispatch}
|
||||
intl={this.props.intl}
|
||||
{...editForm} />
|
||||
</div>
|
||||
</ModalOverlayWrapper>
|
||||
</div>
|
||||
}
|
||||
{showSearchShortcutsForm &&
|
||||
<div className="edit-search-shortcuts">
|
||||
<div className="modal-overlay" onClick={this.onSearchShortcutsFormClose} role="presentation" />
|
||||
<div className="modal">
|
||||
<ModalOverlayWrapper unstyled={true} onClose={this.onSearchShortcutsFormClose} innerClassName="modal" >
|
||||
<SearchShortcutsForm
|
||||
TopSites={props.TopSites}
|
||||
onClose={this.onSearchShortcutsFormClose}
|
||||
dispatch={this.props.dispatch} />
|
||||
</div>
|
||||
</ModalOverlayWrapper>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {applyMiddleware, combineReducers, createStore} from "redux";
|
|||
export const MERGE_STORE_ACTION = "NEW_TAB_INITIAL_STATE";
|
||||
export const OUTGOING_MESSAGE_NAME = "ActivityStream:ContentToMain";
|
||||
export const INCOMING_MESSAGE_NAME = "ActivityStream:MainToContent";
|
||||
export const EARLY_QUEUED_ACTIONS = [at.SAVE_SESSION_PERF_DATA, at.PAGE_PRERENDERED];
|
||||
export const EARLY_QUEUED_ACTIONS = [at.SAVE_SESSION_PERF_DATA];
|
||||
|
||||
/**
|
||||
* A higher-order function which returns a reducer that, on MERGE_STORE action,
|
||||
|
|
@ -114,10 +114,9 @@ export const queueEarlyMessageMiddleware = store => next => action => {
|
|||
* @param {object} intialState (optional) The initial state of the store, if desired
|
||||
* @return {object} A redux store
|
||||
*/
|
||||
export function initStore(reducers, initialState) {
|
||||
export function initStore(reducers) {
|
||||
const store = createStore(
|
||||
mergeStateReducer(combineReducers(reducers)),
|
||||
initialState,
|
||||
global.RPMAddMessageListener && applyMiddleware(rehydrationMiddleware, queueEarlyMessageMiddleware, messageMiddleware)
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -363,8 +363,6 @@ input[type='text'], input[type='search'] {
|
|||
flex-grow: 1;
|
||||
min-height: 100vh;
|
||||
padding: 30px 32px 32px; }
|
||||
.outer-wrapper.fixed-to-top {
|
||||
display: block; }
|
||||
.outer-wrapper.only-search {
|
||||
display: block;
|
||||
padding-top: 134px; }
|
||||
|
|
@ -1787,6 +1785,8 @@ main {
|
|||
border: 1px solid var(--newtab-border-secondary-color); }
|
||||
.asrouter-admin .ds-component {
|
||||
margin-bottom: 20px; }
|
||||
.asrouter-admin .modalOverlayInner {
|
||||
height: 80%; }
|
||||
|
||||
.pocket-logged-in-cta {
|
||||
font-size: 13px;
|
||||
|
|
@ -2736,6 +2736,7 @@ main {
|
|||
|
||||
.impression-observer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none; }
|
||||
|
|
@ -2895,6 +2896,7 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: none;
|
||||
z-index: 1100; }
|
||||
|
|
@ -2903,10 +2905,10 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
|
||||
.modalOverlayInner {
|
||||
width: 960px;
|
||||
height: 570px;
|
||||
position: fixed;
|
||||
top: calc(50% - 285px);
|
||||
top: 20%;
|
||||
left: calc(50% - 480px);
|
||||
max-height: calc(100% - 100px);
|
||||
background: #FFF;
|
||||
box-shadow: 0 1px 15px 0 rgba(0, 0, 0, 0.3);
|
||||
border-radius: 4px;
|
||||
|
|
@ -2944,6 +2946,7 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
margin-top: 30px; } }
|
||||
.modalOverlayInner .footer {
|
||||
border-top: 1px solid #D7D7DB;
|
||||
border-radius: 4px;
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
|
@ -3318,7 +3321,8 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
grid-column-gap: 21px;
|
||||
grid-template-columns: auto auto auto;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px; }
|
||||
padding-right: 30px;
|
||||
min-height: 500px; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessageContainer {
|
||||
grid-template-columns: none;
|
||||
|
|
|
|||
|
|
@ -366,8 +366,6 @@ input[type='text'], input[type='search'] {
|
|||
flex-grow: 1;
|
||||
min-height: 100vh;
|
||||
padding: 30px 32px 32px; }
|
||||
.outer-wrapper.fixed-to-top {
|
||||
display: block; }
|
||||
.outer-wrapper.only-search {
|
||||
display: block;
|
||||
padding-top: 134px; }
|
||||
|
|
@ -1790,6 +1788,8 @@ main {
|
|||
border: 1px solid var(--newtab-border-secondary-color); }
|
||||
.asrouter-admin .ds-component {
|
||||
margin-bottom: 20px; }
|
||||
.asrouter-admin .modalOverlayInner {
|
||||
height: 80%; }
|
||||
|
||||
.pocket-logged-in-cta {
|
||||
font-size: 13px;
|
||||
|
|
@ -2739,6 +2739,7 @@ main {
|
|||
|
||||
.impression-observer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none; }
|
||||
|
|
@ -2898,6 +2899,7 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: none;
|
||||
z-index: 1100; }
|
||||
|
|
@ -2906,10 +2908,10 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
|
||||
.modalOverlayInner {
|
||||
width: 960px;
|
||||
height: 570px;
|
||||
position: fixed;
|
||||
top: calc(50% - 285px);
|
||||
top: 20%;
|
||||
left: calc(50% - 480px);
|
||||
max-height: calc(100% - 100px);
|
||||
background: #FFF;
|
||||
box-shadow: 0 1px 15px 0 rgba(0, 0, 0, 0.3);
|
||||
border-radius: 4px;
|
||||
|
|
@ -2947,6 +2949,7 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
margin-top: 30px; } }
|
||||
.modalOverlayInner .footer {
|
||||
border-top: 1px solid #D7D7DB;
|
||||
border-radius: 4px;
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
|
@ -3321,7 +3324,8 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
grid-column-gap: 21px;
|
||||
grid-template-columns: auto auto auto;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px; }
|
||||
padding-right: 30px;
|
||||
min-height: 500px; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessageContainer {
|
||||
grid-template-columns: none;
|
||||
|
|
|
|||
|
|
@ -363,8 +363,6 @@ input[type='text'], input[type='search'] {
|
|||
flex-grow: 1;
|
||||
min-height: 100vh;
|
||||
padding: 30px 32px 32px; }
|
||||
.outer-wrapper.fixed-to-top {
|
||||
display: block; }
|
||||
.outer-wrapper.only-search {
|
||||
display: block;
|
||||
padding-top: 134px; }
|
||||
|
|
@ -1787,6 +1785,8 @@ main {
|
|||
border: 1px solid var(--newtab-border-secondary-color); }
|
||||
.asrouter-admin .ds-component {
|
||||
margin-bottom: 20px; }
|
||||
.asrouter-admin .modalOverlayInner {
|
||||
height: 80%; }
|
||||
|
||||
.pocket-logged-in-cta {
|
||||
font-size: 13px;
|
||||
|
|
@ -2736,6 +2736,7 @@ main {
|
|||
|
||||
.impression-observer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none; }
|
||||
|
|
@ -2895,6 +2896,7 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: none;
|
||||
z-index: 1100; }
|
||||
|
|
@ -2903,10 +2905,10 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
|
||||
.modalOverlayInner {
|
||||
width: 960px;
|
||||
height: 570px;
|
||||
position: fixed;
|
||||
top: calc(50% - 285px);
|
||||
top: 20%;
|
||||
left: calc(50% - 480px);
|
||||
max-height: calc(100% - 100px);
|
||||
background: #FFF;
|
||||
box-shadow: 0 1px 15px 0 rgba(0, 0, 0, 0.3);
|
||||
border-radius: 4px;
|
||||
|
|
@ -2944,6 +2946,7 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
margin-top: 30px; } }
|
||||
.modalOverlayInner .footer {
|
||||
border-top: 1px solid #D7D7DB;
|
||||
border-radius: 4px;
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
|
@ -3318,7 +3321,8 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
|||
grid-column-gap: 21px;
|
||||
grid-template-columns: auto auto auto;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px; }
|
||||
padding-right: 30px;
|
||||
min-height: 500px; }
|
||||
@media (max-width: 850px) {
|
||||
.onboardingMessageContainer {
|
||||
grid-template-columns: none;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -254,7 +254,6 @@ and losing focus. | :one:
|
|||
| `block` | [Optional] An integer to record the 0-based index when user blocks a Pocket tile. | :one:
|
||||
| `pocket` | [Optional] An integer to record the 0-based index when user saves a Pocket tile to Pocket. | :one:
|
||||
| `user_prefs` | [Required] The encoded integer of user's preferences. | :one: & :four:
|
||||
| `is_prerendered` | [Required] A boolean to signify whether the page is prerendered or not | :one:
|
||||
| `is_preloaded` | [Required] A boolean to signify whether the page is preloaded or not | :one:
|
||||
| `icon_type` | [Optional] ("tippytop", "rich_icon", "screenshot_with_icon", "screenshot", "no_image") | :one:
|
||||
| `region` | [Optional] A string maps to pref "browser.search.region", which is essentially the two letter ISO 3166-1 country code populated by the Firefox search service. Note that: 1). it reports "OTHER" for those regions with smaller Firefox user base (less than 10000) so that users cannot be uniquely identified; 2). it reports "UNSET" if this pref is missing; 3). it reports "EMPTY" if the value of this pref is an empty string. | :one:
|
||||
|
|
|
|||
|
|
@ -537,9 +537,6 @@ perf: {
|
|||
|
||||
// Whether the page is preloaded or not.
|
||||
"is_preloaded": [true|false],
|
||||
|
||||
// Whether the page is prerendered or not.
|
||||
"is_prerendered": [true|false]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -53,3 +53,8 @@ Services.prefs.setBoolPref("browser.newtabpage.activity-stream.asrouter.devtools
|
|||
|
||||
**4. Go to `about:newtab#devtools`**
|
||||
There should be a "cfr-remote" provider listed.
|
||||
|
||||
## Using the staging server for Remote CFR
|
||||
|
||||
If your message is published in the staging environment the easiest way to test is using the [Remote Settings Devtools](https://github.com/mozilla/remote-settings-devtools/releases) addon. You can install this by going to `about:debugging` and using the `Load Temporary Addon` feature.
|
||||
The devtools allow you to switch your profile between production and staging and takes care of correctly flipping all the required preferences.
|
||||
|
|
|
|||
|
|
@ -2,18 +2,12 @@
|
|||
|
||||
Adding telemetry generally involves a few steps:
|
||||
|
||||
1. - [ ] File a "user story issue" [in ping-centre](https://github.com/mozilla/ping-centre) about who wants what question answered. This will be used to track server-side data handling implementation (ETL, dashboard implementation...).
|
||||
|
||||
> As an engineer, I see the distribution of times between triggering a new tab load by tab menu entry, plus button or keyboard shortcut and when the visibility event is fired on that page, so that I can understand how long it takes before the page becomes visible and how close to instantaneous that feels.
|
||||
|
||||
1. - [ ] File or update your existing client-side implementation bug so that it points to the "user story issue".
|
||||
1. - [ ] Talk to Marina (@emtwo) about the dashboard you're hoping to see from the data you're adding, and work with her to create a data model (or request her review on it after the fact).
|
||||
1. - [ ] Update `system-addon/test/schemas/pings.js` with a commented JOI schema of your changes, and add tests to system-addon/test/unit/TelemetryFeed.test.js to exercise the ping creation.
|
||||
1. - [ ] Update [data_events.md](data_events.md) with an example of the data in question
|
||||
1. - [ ] Update any fields that you've added, deleted, or changed in [data_dictionary.md](data_dictionary.md)
|
||||
1. - [ ] Get review from Nan (@ncloudioj) and/or Marina (@emtwo) on the data schema and the documentation changes.
|
||||
1. - [ ] Request `data-review` of your documentation changes from a [data steward](https://wiki.mozilla.org/Firefox/Data_Collection) to ensure suitability for collection controlled by the opt-out `datareporting.healthreport.uploadEnabled` pref. Download and fill out the [data review request form](https://github.com/mozilla/data-review/blob/master/request.md) and then attach it as a text file on Bugzilla so you can r? a data steward. We've been working with Francois Marier (:francois / @fmarier), so he's a good candidate as he's likely to have the most context.
|
||||
1. - [ ] Implement as usual...
|
||||
1. - [ ] After landing the implementation, check with Nan (@ncloudioj) to make sure the pings are making it to the database
|
||||
1. - [ ] Update the [ping-centre](https://github.com/mozilla/ping-centre/issues) user story issue for the dashboard, stating that the data should now be available for implementation, and removing any `blocked` tag if appropriate.
|
||||
|
||||
1. File a "user story" bug about who wants what question answered. This will be used to track the client-side implementation as well as the data review request. If the server side changes are needed, ask Nan (:nanj / @ncloudio) if in doubt, bugs will be filed separately as dependencies.
|
||||
1. Implement as usual...
|
||||
1. Update `system-addon/test/schemas/pings.js` with a commented JOI schema of your changes, and add tests to system-addon/test/unit/TelemetryFeed.test.js to exercise the ping creation.
|
||||
1. Update [data_events.md](data_events.md) with an example of the data in question.
|
||||
1. Update any fields that you've added, deleted, or changed in [data_dictionary.md](data_dictionary.md).
|
||||
1. Get review from Nan on the data schema and the documentation changes.
|
||||
1. Request `data-review` of your documentation changes from a [data steward](https://wiki.mozilla.org/Firefox/Data_Collection) to ensure suitability for collection controlled by the opt-out `datareporting.healthreport.uploadEnabled` pref. Download and fill out the [data review request form](https://github.com/mozilla/data-review/blob/master/request.md) and then attach it as a text file on Bugzilla so you can r? a data steward. We've been working with Chris H-C (:chutten) for the Firefox specific telemetry, and Kenny Long (kenny@getpocket.com) for the Pocket specific telemetry, they are the best candidates for the review work as they know well about the context.
|
||||
1. After landing the implementation, check with Nan to make sure the pings are making it to the database.
|
||||
1. Once data flows in, you can build dashboard for the new telemetry on [Redash](https://sql.telemetry.mozilla.org/dashboards). If you're looking for some help about Redash or dashboard building, Nan is the guy for that.
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ browser.jar:
|
|||
#else
|
||||
res/activity-stream/css/activity-stream.css (./css/activity-stream-linux.css)
|
||||
#endif
|
||||
res/activity-stream/prerendered/static/activity-stream-initial-state.js (./prerendered/static/activity-stream-initial-state.js)
|
||||
#ifndef RELEASE_OR_BETA
|
||||
res/activity-stream/prerendered/static/activity-stream-debug.html (./prerendered/static/activity-stream-debug.html)
|
||||
res/activity-stream/prerendered/static/activity-stream-prerendered-debug.html (./prerendered/static/activity-stream-prerendered-debug.html)
|
||||
#endif
|
||||
res/activity-stream/prerendered/ (./prerendered/locales/*)
|
||||
|
|
|
|||
|
|
@ -1085,7 +1085,7 @@ defaultLayoutResp = {
|
|||
{
|
||||
"type": "CardGrid",
|
||||
"header": {
|
||||
"title": "Tech 🖥",
|
||||
"title": "Tech 💻",
|
||||
},
|
||||
"feed": {
|
||||
"embed_reference": null,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => new TextDecoder());
|
|||
|
||||
XPCOMUtils.defineLazyGetter(this, "baseAttachmentsURL", async () => {
|
||||
const server = Services.prefs.getCharPref("services.settings.server");
|
||||
const serverInfo = await (await fetch(`${server}/`)).json();
|
||||
const serverInfo = await (await fetch(`${server}/`, {credentials: "omit"})).json();
|
||||
const {capabilities: {attachments: {base_url}}} = serverInfo;
|
||||
return base_url;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -316,7 +316,6 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
perf: {
|
||||
load_trigger_type,
|
||||
is_preloaded: false,
|
||||
is_prerendered: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -409,22 +408,6 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* handlePagePrerendered - Set the session as prerendered
|
||||
*
|
||||
* @param {string} portID the portID of the target session
|
||||
*/
|
||||
handlePagePrerendered(portID) {
|
||||
const session = this.sessions.get(portID);
|
||||
|
||||
if (!session) {
|
||||
// It's possible the tab was never visible – in which case, there was no user session.
|
||||
return;
|
||||
}
|
||||
|
||||
session.perf.is_prerendered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* handleNewTabInit - Handle NEW_TAB_INIT, which creates a new session and sets the a flag
|
||||
* for session.perf based on whether or not this new tab is preloaded
|
||||
|
|
@ -742,9 +725,6 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
case at.NEW_TAB_UNLOAD:
|
||||
this.endSession(au.getPortIdOfSender(action));
|
||||
break;
|
||||
case at.PAGE_PRERENDERED:
|
||||
this.handlePagePrerendered(au.getPortIdOfSender(action));
|
||||
break;
|
||||
case at.SAVE_SESSION_PERF_DATA:
|
||||
this.saveSessionPerfData(au.getPortIdOfSender(action), action.data);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ module.exports = function inject(src) {
|
|||
if (this.cacheable) {
|
||||
this.cacheable();
|
||||
}
|
||||
const regex = createRequireStringRegex(loaderUtils.parseQuery(this.query));
|
||||
const regex = createRequireStringRegex(loaderUtils.getOptions(this) || {});
|
||||
|
||||
return `module.exports = function inject(injections) {
|
||||
var module = {exports: {}};
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ prefs_home_header=Cynnwys Cartref Firefox
|
|||
prefs_home_description=Dewis pa gynnwys rydych eisiau ar eich sgrin Firefox Cartref.
|
||||
|
||||
prefs_content_discovery_header=Cartref Firefox
|
||||
|
||||
prefs_content_discovery_description=Mae Darganfod Cynnwys yng Nghartref Firefox yn caniatáu i chi ddarganfod erthyglau perthnasol o ansawdd uchel ar draws y we.
|
||||
prefs_content_discovery_button=Diffodd Ddarganfod Cynnwys
|
||||
|
||||
|
|
@ -190,14 +191,14 @@ section_menu_action_privacy_notice=Hysbysiad Preifatrwydd
|
|||
# LOCALIZATION NOTE (firstrun_*). These strings are displayed only once, on the
|
||||
# firstrun of the browser, they give an introduction to Firefox and Sync.
|
||||
firstrun_title=Mynd â Firefox gyda Chi
|
||||
firstrun_content=Cewch eich nodau tudalen, hanes, cyfrineiriau a gosodiadau eraill ar eich holl ddyfeisiau.
|
||||
firstrun_content=Cael eich nodau tudalen, hanes, cyfrineiriau a gosodiadau eraill ar eich holl ddyfeisiau.
|
||||
firstrun_learn_more_link=Dysgu rhagor am Gyfrif Firefox
|
||||
|
||||
# LOCALIZATION NOTE (firstrun_form_header and firstrun_form_sub_header):
|
||||
# firstrun_form_sub_header is a continuation of firstrun_form_header, they are one sentence.
|
||||
# firstrun_form_header is displayed more boldly as the call to action.
|
||||
firstrun_form_header=Rhowch eich e-bost
|
||||
firstrun_form_sub_header=i barhau i Firefox Sync
|
||||
firstrun_form_sub_header=ac ymlaen i Firefox Sync
|
||||
|
||||
firstrun_email_input_placeholder=E-bost
|
||||
firstrun_invalid_input=Mae angen e-bost dilys
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ type_label_downloaded=Descaregou
|
|||
menu_action_bookmark=Azonzi a-i segnalibbri
|
||||
menu_action_remove_bookmark=Scancella segnalibbro
|
||||
menu_action_open_new_window=Arvi in neuvo barcon
|
||||
menu_action_open_private_window=Arvi in neuvo barcon privou
|
||||
menu_action_open_private_window=Arvi in neuvo barcon privòu
|
||||
menu_action_dismiss=Scancella
|
||||
menu_action_delete=Scancella da-a stöia
|
||||
menu_action_pin=Azonzi a-a bacheca
|
||||
|
|
@ -78,7 +78,7 @@ search_web_placeholder=Çerca inta Ræ
|
|||
# LOCALIZATION NOTE (section_disclaimer_topstories): This is shown below
|
||||
# the topstories section title to provide additional information about
|
||||
# how the stories are selected.
|
||||
section_disclaimer_topstories=E stöie ciù interesanti do Web, seleçionæ in baze a quello che ti lezi. Pigiæ da Pocket, che oua o l'é parte de Mozilla.
|
||||
section_disclaimer_topstories=E stöie ciù interesanti do Web, seleçionæ in baze a quello che ti lezi. Pigiæ da Pocket, che òua o l'é parte de Mozilla.
|
||||
section_disclaimer_topstories_linktext=Descòvri comme fonçionn-a.
|
||||
# LOCALIZATION NOTE (section_disclaimer_topstories_buttontext): The text of
|
||||
# the button used to acknowledge, and hide this disclaimer in the future.
|
||||
|
|
@ -93,6 +93,7 @@ prefs_home_header=Pagina iniçiâ de Firefox
|
|||
prefs_home_description=Çerni i contegnui che ti veu vedde inta pagina iniçiâ de Firefox.
|
||||
|
||||
prefs_content_discovery_header=Pagina iniçiâ de Firefox
|
||||
|
||||
prefs_content_discovery_button=Dizabilita a descoverta de neuvi contegnui
|
||||
|
||||
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
|
||||
|
|
@ -166,7 +167,7 @@ manual_migration_explanation2=Preuva Firefox con i segnalibbri, a stöia e-e par
|
|||
manual_migration_cancel_button=No graçie
|
||||
# LOCALIZATION NOTE (manual_migration_import_button): This message is shown on a button that starts the process
|
||||
# of importing another browser’s profile profile into Firefox.
|
||||
manual_migration_import_button=Inpòrta oua
|
||||
manual_migration_import_button=Inpòrta òua
|
||||
|
||||
# LOCALIZATION NOTE (error_fallback_default_*): This message and suggested
|
||||
# action link are shown in each section of UI that fails to render
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ section_menu_action_add_topsite=Adaugă site de top
|
|||
section_menu_action_add_search_engine=Adaugă motor de căutare
|
||||
section_menu_action_move_up=Mută în sus
|
||||
section_menu_action_move_down=Mută în jos
|
||||
section_menu_action_privacy_notice=Politica de confidențialitate
|
||||
section_menu_action_privacy_notice=Declarație de confidențialitate
|
||||
|
||||
# LOCALIZATION NOTE (firstrun_*). These strings are displayed only once, on the
|
||||
# firstrun of the browser, they give an introduction to Firefox and Sync.
|
||||
|
|
@ -207,7 +207,7 @@ firstrun_invalid_input=Necesită o adresă de e-mail validă
|
|||
# {privacy} is equal to firstrun_privacy_notice. {terms} and {privacy} are clickable links.
|
||||
firstrun_extra_legal_links=Prin continuare, ești de acord cu {terms} și {privacy}.
|
||||
firstrun_terms_of_service=Termenii de utilizare a serviciului
|
||||
firstrun_privacy_notice=Politica de confidențialitate
|
||||
firstrun_privacy_notice=Declarație de confidențialitate
|
||||
|
||||
firstrun_continue_to_login=Continuă
|
||||
firstrun_skip_login=Omite acest pas
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ cd /activity-stream && npm install . && npm run buildmc
|
|||
|
||||
# Build latest m-c with Activity Stream changes
|
||||
cd /mozilla-central && ./mach build \
|
||||
&& ./mach lint -l codespell browser/components/newtab \
|
||||
&& ./mach lint browser/components/newtab \
|
||||
&& ./mach test browser/components/newtab/test/browser --headless \
|
||||
&& ./mach test browser/components/newtab/test/xpcshell \
|
||||
&& ./mach test --log-tbpl test_run_log \
|
||||
|
|
|
|||
|
|
@ -44,11 +44,6 @@ interface nsIAboutNewTabService : nsISupports
|
|||
*/
|
||||
readonly attribute bool activityStreamEnabled;
|
||||
|
||||
/**
|
||||
* Returns true if the the prerendering pref for activity stream is true
|
||||
*/
|
||||
readonly attribute bool activityStreamPrerender;
|
||||
|
||||
/**
|
||||
* Returns true if the the debug pref for activity stream is true
|
||||
*/
|
||||
|
|
|
|||
861
browser/components/newtab/package-lock.json
generated
861
browser/components/newtab/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -7,36 +7,36 @@
|
|||
"url": "https://github.com/mozilla/activity-stream/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"fluent": "0.6.4",
|
||||
"fluent-react": "0.7.0",
|
||||
"fluent": "0.12.0",
|
||||
"fluent-react": "0.8.4",
|
||||
"react": "16.8.6",
|
||||
"react-dom": "16.8.6",
|
||||
"react-intl": "2.8.0",
|
||||
"react-intl": "2.9.0",
|
||||
"react-redux": "7.0.3",
|
||||
"redux": "4.0.1",
|
||||
"reselect": "4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.4.4",
|
||||
"@babel/core": "7.4.5",
|
||||
"@babel/plugin-proposal-async-generator-functions": "7.2.0",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"acorn": "6.1.1",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-loader": "8.0.5",
|
||||
"babel-loader": "8.0.6",
|
||||
"babel-plugin-jsm-to-commonjs": "0.5.0",
|
||||
"babel-plugin-jsm-to-esmodules": "0.6.0",
|
||||
"chai": "4.2.0",
|
||||
"chai-json-schema": "1.5.0",
|
||||
"chai-json-schema": "1.5.1",
|
||||
"cpx": "1.5.0",
|
||||
"enzyme": "3.9.0",
|
||||
"enzyme-adapter-react-16": "1.12.1",
|
||||
"enzyme-adapter-react-16": "1.13.2",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-plugin-fetch-options": "0.0.4",
|
||||
"eslint-plugin-import": "2.17.2",
|
||||
"eslint-plugin-fetch-options": "0.0.5",
|
||||
"eslint-plugin-import": "2.17.3",
|
||||
"eslint-plugin-jsx-a11y": "6.2.1",
|
||||
"eslint-plugin-mozilla": "1.2.1",
|
||||
"eslint-plugin-no-unsanitized": "3.0.2",
|
||||
"eslint-plugin-react": "7.12.4",
|
||||
"eslint-plugin-react": "7.13.0",
|
||||
"eslint-plugin-react-hooks": "1.6.0",
|
||||
"istanbul-instrumenter-loader": "3.0.1",
|
||||
"joi-browser": "13.4.0",
|
||||
|
|
@ -49,11 +49,11 @@
|
|||
"karma-sinon": "1.0.5",
|
||||
"karma-sourcemap-loader": "0.3.7",
|
||||
"karma-webpack": "3.0.5",
|
||||
"loader-utils": "0.2.16",
|
||||
"loader-utils": "1.2.3",
|
||||
"minimist": "1.2.0",
|
||||
"mocha": "6.1.4",
|
||||
"mock-raf": "1.0.1",
|
||||
"node-fetch": "2.5.0",
|
||||
"node-fetch": "2.6.0",
|
||||
"node-sass": "4.12.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"pontoon-to-json": "2.0.0",
|
||||
|
|
@ -61,12 +61,12 @@
|
|||
"raw-loader": "2.0.0",
|
||||
"react-test-renderer": "16.8.6",
|
||||
"rimraf": "2.6.3",
|
||||
"sass": "1.19.0",
|
||||
"sass": "1.20.1",
|
||||
"sass-lint": "1.13.1",
|
||||
"shelljs": "0.8.3",
|
||||
"sinon": "7.3.2",
|
||||
"webpack": "4.30.0",
|
||||
"webpack-cli": "3.3.1",
|
||||
"webpack": "4.32.2",
|
||||
"webpack-cli": "3.3.2",
|
||||
"yamscripts": "0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
"bundle:locales": "pontoon-to-json --src $npm_package_config_locales_dir --dest data",
|
||||
"bundle:webpack": "webpack --config webpack.system-addon.config.js",
|
||||
"bundle:css": "node-sass content-src/styles -o css",
|
||||
"bundle:html": "rimraf prerendered && webpack --config webpack.prerender.config.js && node ./bin/render-activity-stream-html.js",
|
||||
"bundle:html": "rimraf prerendered && node ./bin/render-activity-stream-html.js",
|
||||
"buildmc": "npm-run-all buildmc:*",
|
||||
"prebuildmc": "rimraf $npm_package_config_mc_dir/browser/components/newtab/",
|
||||
"buildmc:bundle": "npm run bundle",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
<script src="chrome://browser/content/contentSearchUI.js"></script>
|
||||
<script src="chrome://browser/content/contentTheme.js"></script>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body class="activity-stream">
|
||||
<div id="header-asrouter-container" role="presentation"></div>
|
||||
<div id="root"><!-- Regular React Rendering --></div>
|
||||
<div id="root"></div>
|
||||
<div id="footer-asrouter-container" role="presentation"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue