mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge m-c to fx-team
This commit is contained in:
commit
3dab13ddf4
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 944533 requires clobber to force a Proguard refresh
|
||||
Bug 958185 requires clobber on Android to force a Proguard refresh
|
||||
|
@ -58,6 +58,22 @@ this.SessionFile = {
|
||||
write: function (aData) {
|
||||
return SessionFileInternal.write(aData);
|
||||
},
|
||||
/**
|
||||
* Gather telemetry statistics.
|
||||
*
|
||||
*
|
||||
* Most of the work is done off the main thread but there is a main
|
||||
* thread cost involved to send data to the worker thread. This method
|
||||
* should therefore be called only when we know that it will not disrupt
|
||||
* the user's experience, e.g. on idle-daily.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @promise {object} An object holding all the information to be submitted
|
||||
* to Telemetry.
|
||||
*/
|
||||
gatherTelemetry: function(aData) {
|
||||
return SessionFileInternal.gatherTelemetry(aData);
|
||||
},
|
||||
/**
|
||||
* Writes the initial state to disk again only to change the session's load
|
||||
* state. This must only be called once, it will throw an error otherwise.
|
||||
@ -122,6 +138,14 @@ let SessionFileInternal = {
|
||||
});
|
||||
},
|
||||
|
||||
gatherTelemetry: function(aStateString) {
|
||||
return Task.spawn(function() {
|
||||
let msg = yield SessionWorker.post("gatherTelemetry", [aStateString]);
|
||||
this._recordTelemetry(msg.telemetry);
|
||||
throw new Task.Result(msg.telemetry);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
write: function (aData) {
|
||||
if (this._isClosed) {
|
||||
return Promise.reject(new Error("SessionFile is closed"));
|
||||
@ -177,8 +201,18 @@ let SessionFileInternal = {
|
||||
},
|
||||
|
||||
_recordTelemetry: function(telemetry) {
|
||||
for (let histogramId in telemetry){
|
||||
Telemetry.getHistogramById(histogramId).add(telemetry[histogramId]);
|
||||
for (let id of Object.keys(telemetry)){
|
||||
let value = telemetry[id];
|
||||
let samples = [];
|
||||
if (Array.isArray(value)) {
|
||||
samples.push(...value);
|
||||
} else {
|
||||
samples.push(value);
|
||||
}
|
||||
let histogram = Telemetry.getHistogramById(id);
|
||||
for (let sample of samples) {
|
||||
histogram.add(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -196,9 +230,12 @@ let SessionWorker = (function () {
|
||||
// Decode any serialized error
|
||||
if (error instanceof PromiseWorker.WorkerError) {
|
||||
throw OS.File.Error.fromMsg(error.data);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
// Extract something meaningful from ErrorEvent
|
||||
if (error instanceof ErrorEvent) {
|
||||
throw new Error(error.message, error.filename, error.lineno);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -35,7 +35,8 @@ const OBSERVING = [
|
||||
"quit-application-requested", "quit-application-granted",
|
||||
"browser-lastwindow-close-granted",
|
||||
"quit-application", "browser:purge-session-history",
|
||||
"browser:purge-domain-data"
|
||||
"browser:purge-domain-data",
|
||||
"gather-telemetry",
|
||||
];
|
||||
|
||||
// XUL Window properties to (re)store
|
||||
@ -587,6 +588,9 @@ let SessionStoreInternal = {
|
||||
case "nsPref:changed": // catch pref changes
|
||||
this.onPrefChange(aData);
|
||||
break;
|
||||
case "gather-telemetry":
|
||||
this.onGatherTelemetry();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1457,6 +1461,16 @@ let SessionStoreInternal = {
|
||||
this.saveStateDelayed(aWindow);
|
||||
},
|
||||
|
||||
onGatherTelemetry: function() {
|
||||
// On the first gather-telemetry notification of the session,
|
||||
// gather telemetry data.
|
||||
Services.obs.removeObserver(this, "gather-telemetry");
|
||||
this.fillTabCachesAsynchronously().then(function() {
|
||||
let stateString = SessionStore.getBrowserState();
|
||||
return SessionFile.gatherTelemetry(stateString);
|
||||
});
|
||||
},
|
||||
|
||||
/* ........ nsISessionStore API .............. */
|
||||
|
||||
getBrowserState: function ssi_getBrowserState() {
|
||||
|
@ -130,6 +130,16 @@ let Agent = {
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* Extract all sorts of useful statistics from a state string,
|
||||
* for use with Telemetry.
|
||||
*
|
||||
* @return {object}
|
||||
*/
|
||||
gatherTelemetry: function (stateString) {
|
||||
return Statistics.collect(stateString);
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes the session state to disk again but changes session.state to
|
||||
* 'running' before doing so. This is intended to be called only once, shortly
|
||||
@ -236,3 +246,144 @@ let Agent = {
|
||||
function isNoSuchFileEx(aReason) {
|
||||
return aReason instanceof OS.File.Error && aReason.becauseNoSuchFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate the number of bytes that a data structure will use on disk
|
||||
* once serialized.
|
||||
*/
|
||||
function getByteLength(str) {
|
||||
return Encoder.encode(JSON.stringify(str)).byteLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools for gathering statistics on a state string.
|
||||
*/
|
||||
let Statistics = {
|
||||
collect: function(stateString) {
|
||||
let start = Date.now();
|
||||
let TOTAL_PREFIX = "FX_SESSION_RESTORE_TOTAL_";
|
||||
let INDIVIDUAL_PREFIX = "FX_SESSION_RESTORE_INDIVIDUAL_";
|
||||
let SIZE_SUFFIX = "_SIZE_BYTES";
|
||||
|
||||
let state = JSON.parse(stateString);
|
||||
|
||||
// Gather all data
|
||||
let subsets = {};
|
||||
this.gatherSimpleData(state, subsets);
|
||||
this.gatherComplexData(state, subsets);
|
||||
|
||||
// Extract telemetry
|
||||
let telemetry = {};
|
||||
for (let k of Object.keys(subsets)) {
|
||||
let obj = subsets[k];
|
||||
telemetry[TOTAL_PREFIX + k + SIZE_SUFFIX] = getByteLength(obj);
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
let size = obj.map(getByteLength);
|
||||
telemetry[INDIVIDUAL_PREFIX + k + SIZE_SUFFIX] = size;
|
||||
}
|
||||
}
|
||||
|
||||
let stop = Date.now();
|
||||
telemetry["FX_SESSION_RESTORE_EXTRACTING_STATISTICS_DURATION_MS"] = stop - start;
|
||||
return {
|
||||
telemetry: telemetry
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Collect data that doesn't require a recursive walk through the
|
||||
* data structure.
|
||||
*/
|
||||
gatherSimpleData: function(state, subsets) {
|
||||
// The subset of sessionstore.js dealing with open windows
|
||||
subsets.OPEN_WINDOWS = state.windows;
|
||||
|
||||
// The subset of sessionstore.js dealing with closed windows
|
||||
subsets.CLOSED_WINDOWS = state._closedWindows;
|
||||
|
||||
// The subset of sessionstore.js dealing with closed tabs
|
||||
// in open windows
|
||||
subsets.CLOSED_TABS_IN_OPEN_WINDOWS = [];
|
||||
|
||||
// The subset of sessionstore.js dealing with cookies
|
||||
// in both open and closed windows
|
||||
subsets.COOKIES = [];
|
||||
|
||||
for (let winData of state.windows) {
|
||||
let closedTabs = winData._closedTabs || [];
|
||||
subsets.CLOSED_TABS_IN_OPEN_WINDOWS.push(...closedTabs);
|
||||
|
||||
let cookies = winData.cookies || [];
|
||||
subsets.COOKIES.push(...cookies);
|
||||
}
|
||||
|
||||
for (let winData of state._closedWindows) {
|
||||
let cookies = winData.cookies || [];
|
||||
subsets.COOKIES.push(...cookies);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Walk through a data structure, recursively.
|
||||
*
|
||||
* @param {object} root The object from which to start walking.
|
||||
* @param {function(key, value)} cb Callback, called for each
|
||||
* item except the root. Returns |true| to walk the subtree rooted
|
||||
* at |value|, |false| otherwise */
|
||||
walk: function(root, cb) {
|
||||
if (!root || typeof root !== "object") {
|
||||
return;
|
||||
}
|
||||
for (let k of Object.keys(root)) {
|
||||
let obj = root[k];
|
||||
let stepIn = cb(k, obj);
|
||||
if (stepIn) {
|
||||
this.walk(obj, cb);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Collect data that requires walking through the data structure
|
||||
*/
|
||||
gatherComplexData: function(state, subsets) {
|
||||
// The subset of sessionstore.js dealing with DOM storage
|
||||
subsets.DOM_STORAGE = [];
|
||||
// The subset of sessionstore.js storing form data
|
||||
subsets.FORMDATA = [];
|
||||
// The subset of sessionstore.js storing POST data in history
|
||||
subsets.POSTDATA = [];
|
||||
// The subset of sessionstore.js storing history
|
||||
subsets.HISTORY = [];
|
||||
|
||||
|
||||
this.walk(state, function(k, value) {
|
||||
let dest;
|
||||
switch (k) {
|
||||
case "entries":
|
||||
subsets.HISTORY.push(value);
|
||||
return true;
|
||||
case "storage":
|
||||
subsets.DOM_STORAGE.push(value);
|
||||
// Never visit storage, it's full of weird stuff
|
||||
return false;
|
||||
case "formdata":
|
||||
subsets.FORMDATA.push(value);
|
||||
// Never visit formdata, it's full of weird stuff
|
||||
return false;
|
||||
case "postdata_b64":
|
||||
subsets.POSTDATA.push(value);
|
||||
return false; // Nothing to visit anyway
|
||||
case "cookies": // Don't visit these places, they are full of weird stuff
|
||||
case "extData":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return subsets;
|
||||
},
|
||||
|
||||
};
|
||||
|
@ -295,12 +295,8 @@ let TabStateCacheInternal = {
|
||||
for (let key of Object.keys(newData)) {
|
||||
let value = newData[key];
|
||||
if (value === null) {
|
||||
// Remove the field if the value is null.
|
||||
this.removeField(browser, key);
|
||||
delete data[key];
|
||||
} else {
|
||||
// Update the field otherwise.
|
||||
this.updateField(browser, key, value);
|
||||
data[key] = value;
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ support-files =
|
||||
[browser_sessionStorage.js]
|
||||
[browser_swapDocShells.js]
|
||||
[browser_tabStateCache.js]
|
||||
[browser_telemetry.js]
|
||||
[browser_upgrade_backup.js]
|
||||
[browser_windowRestore_perwindowpb.js]
|
||||
[browser_248970_b_perwindowpb.js]
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const INITIAL_VALUE = "initial-value-" + Date.now();
|
||||
const INITIAL_VALUE = "browser_broadcast.js-initial-value-" + Date.now();
|
||||
|
||||
/**
|
||||
* This test ensures we won't lose tab data queued in the content script when
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
let Imports = {};
|
||||
Cu.import("resource:///modules/sessionstore/SessionSaver.jsm", Imports);
|
||||
let {SessionSaver} = Imports;
|
||||
let {Task, SessionSaver} = Imports;
|
||||
|
||||
add_task(function cleanup() {
|
||||
info("Forgetting closed tabs");
|
||||
|
266
browser/components/sessionstore/test/browser_telemetry.js
Normal file
266
browser/components/sessionstore/test/browser_telemetry.js
Normal file
@ -0,0 +1,266 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/sessionstore/SessionFile.jsm", tmp);
|
||||
Cu.import("resource:///modules/sessionstore/TabStateCache.jsm", tmp);
|
||||
let {SessionFile, TabStateCache} = tmp;
|
||||
|
||||
// Shortcuts for histogram names
|
||||
let Keys = {};
|
||||
for (let k of ["HISTORY", "FORMDATA", "OPEN_WINDOWS", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS", "DOM_STORAGE", "POSTDATA"]) {
|
||||
Keys[k] = "FX_SESSION_RESTORE_TOTAL_" + k + "_SIZE_BYTES";
|
||||
}
|
||||
|
||||
function lt(a, b, message) {
|
||||
isnot(a, undefined, message + " (sanity check)");
|
||||
isnot(b, undefined, message + " (sanity check)");
|
||||
ok(a < b, message + " ( " + a + " < " + b + ")");
|
||||
}
|
||||
function gt(a, b, message) {
|
||||
isnot(a, undefined, message + " (sanity check)");
|
||||
isnot(b, undefined, message + " (sanity check)");
|
||||
ok(a > b, message + " ( " + a + " > " + b + ")");
|
||||
}
|
||||
|
||||
add_task(function init() {
|
||||
for (let i = ss.getClosedWindowCount() - 1; i >= 0; --i) {
|
||||
ss.forgetClosedWindow(i);
|
||||
}
|
||||
for (let i = ss.getClosedTabCount(window) - 1; i >= 0; --i) {
|
||||
ss.forgetClosedTab(window, i);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that Telemetry collection doesn't cause any error.
|
||||
*/
|
||||
add_task(function() {
|
||||
info("Checking a little bit of consistency");
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
for (let k of Object.keys(statistics)) {
|
||||
let data = statistics[k];
|
||||
info("Data for " + k + ": " + data);
|
||||
if (Array.isArray(data)) {
|
||||
ok(data.every(x => x >= 0), "Data for " + k + " is >= 0");
|
||||
} else {
|
||||
ok(data >= 0, "Data for " + k + " is >= 0");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test HISTORY key.
|
||||
*/
|
||||
add_task(function history() {
|
||||
let KEY = Keys.HISTORY;
|
||||
let tab = gBrowser.addTab("http://example.org:80/?");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
info("Now changing history");
|
||||
tab.linkedBrowser.contentWindow.history.pushState({foo:1}, "ref");
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
// We have changed history, so it must have increased
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of HISTORY has increased");
|
||||
|
||||
// Almost nothing else should
|
||||
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
} finally {
|
||||
if (tab) {
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test CLOSED_TABS_IN_OPEN_WINDOWS key.
|
||||
*/
|
||||
add_task(function close_tab() {
|
||||
let KEY = Keys.CLOSED_TABS_IN_OPEN_WINDOWS;
|
||||
let tab = gBrowser.addTab("http://example.org:80/?close_tab");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
info("Now closing a tab");
|
||||
gBrowser.removeTab(tab);
|
||||
tab = null;
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of CLOSED_TABS_IN_OPEN_WINDOWS has increased");
|
||||
|
||||
// Almost nothing else should change
|
||||
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (tab) {
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test OPEN_WINDOWS key.
|
||||
*/
|
||||
add_task(function open_window() {
|
||||
let KEY = Keys.OPEN_WINDOWS;
|
||||
let win;
|
||||
try {
|
||||
let statistics = yield promiseStats();
|
||||
win = yield promiseNewWindowLoaded("http://example.org:80/?open_window");
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of OPEN_WINDOWS has increased");
|
||||
|
||||
// Almost nothing else should change
|
||||
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (win) {
|
||||
yield promiseWindowClosed(win);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test CLOSED_WINDOWS key.
|
||||
*/
|
||||
add_task(function close_window() {
|
||||
let KEY = Keys.CLOSED_WINDOWS;
|
||||
let win = yield promiseNewWindowLoaded("http://example.org:80/?close_window");
|
||||
|
||||
// We need to add something to the window, otherwise it won't be saved
|
||||
let tab = win.gBrowser.addTab("http://example.org:80/?close_tab");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
let statistics = yield promiseStats();
|
||||
yield promiseWindowClosed(win);
|
||||
win = null;
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of CLOSED_WINDOWS has increased");
|
||||
lt(statistics2[Keys.OPEN_WINDOWS], statistics[Keys.OPEN_WINDOWS], "The total size of OPEN_WINDOWS has decreased");
|
||||
|
||||
// Almost nothing else should change
|
||||
for (let k of ["FORMDATA", "DOM_STORAGE", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (win) {
|
||||
yield promiseWindowClosed(win);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Test DOM_STORAGE key.
|
||||
*/
|
||||
add_task(function dom_storage() {
|
||||
let KEY = Keys.DOM_STORAGE;
|
||||
let tab = gBrowser.addTab("http://example.org:80/?dom_storage");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
info("Now adding some storage");
|
||||
yield modifySessionStorage(tab.linkedBrowser, {foo: "bar"});
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of DOM_STORAGE has increased");
|
||||
|
||||
// Almost nothing else should change
|
||||
for (let k of ["CLOSED_TABS_IN_OPEN_WINDOWS", "FORMDATA", "CLOSED_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (tab) {
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Test FORMDATA key.
|
||||
*/
|
||||
add_task(function formdata() {
|
||||
let KEY = Keys.FORMDATA;
|
||||
let tab = gBrowser.addTab("data:text/html;charset=utf-8,<input%20id='input'>");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
try {
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
let statistics = yield promiseStats();
|
||||
|
||||
info("Now changing form data");
|
||||
|
||||
yield modifyFormData(tab.linkedBrowser, {input: "This is some form data "});
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
TabStateCache.delete(tab.linkedBrowser);
|
||||
|
||||
let statistics2 = yield promiseStats();
|
||||
|
||||
isnot(statistics[KEY], undefined, "Key was defined");
|
||||
isnot(statistics2[KEY], undefined, "Key is still defined");
|
||||
gt(statistics2[KEY], statistics[KEY], "The total size of FORMDATA has increased");
|
||||
|
||||
// Almost nothing else should
|
||||
for (let k of ["DOM_STORAGE", "CLOSED_WINDOWS", "CLOSED_TABS_IN_OPEN_WINDOWS"]) {
|
||||
is(statistics2[Keys[k]], statistics[Keys[k]], "The total size of " + k + " has not increased");
|
||||
}
|
||||
} finally {
|
||||
if (tab) {
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Get the latest statistics.
|
||||
*/
|
||||
function promiseStats() {
|
||||
let state = ss.getBrowserState();
|
||||
info("Stats: " + state);
|
||||
return SessionFile.gatherTelemetry(state);
|
||||
}
|
||||
|
||||
|
||||
function modifySessionStorage(browser, data) {
|
||||
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage", data);
|
||||
return promiseContentMessage(browser, "ss-test:MozStorageChanged");
|
||||
}
|
||||
|
||||
function modifyFormData(browser, data) {
|
||||
browser.messageManager.sendAsyncMessage("ss-test:modifyFormData", data);
|
||||
return promiseContentMessage(browser, "ss-test:modifyFormData:done");
|
||||
}
|
||||
|
@ -39,6 +39,13 @@ addMessageListener("ss-test:modifySessionStorage2", function (msg) {
|
||||
}
|
||||
});
|
||||
|
||||
addMessageListener("ss-test:modifyFormData", function (msg) {
|
||||
for (let id of Object.keys(msg.data)) {
|
||||
content.document.getElementById(id).value = msg.data[id];
|
||||
}
|
||||
sendSyncMessage("ss-test:modifyFormData:done");
|
||||
});
|
||||
|
||||
addMessageListener("ss-test:purgeDomainData", function ({data: domain}) {
|
||||
Services.obs.notifyObservers(null, "browser:purge-domain-data", domain);
|
||||
content.setTimeout(() => sendAsyncMessage("ss-test:purgeDomainData"));
|
||||
|
@ -617,9 +617,9 @@
|
||||
@BINPATH@/browser/chrome.manifest
|
||||
@BINPATH@/browser/chrome/browser@JAREXT@
|
||||
@BINPATH@/browser/chrome/browser.manifest
|
||||
#ifdef NIGHTLY_BUILD
|
||||
@BINPATH@/browser/chrome/pdfjs.manifest
|
||||
@BINPATH@/browser/chrome/pdfjs/*
|
||||
#ifdef NIGHTLY_BUILD
|
||||
@BINPATH@/browser/chrome/shumway.manifest
|
||||
@BINPATH@/browser/chrome/shumway/*
|
||||
#endif
|
||||
@ -845,9 +845,9 @@ bin/libfreebl_32int64_3.so
|
||||
#ifdef NIGHTLY_BUILD
|
||||
@BINPATH@/metro/chrome/shumway.manifest
|
||||
@BINPATH@/metro/chrome/shumway/*
|
||||
#endif
|
||||
@BINPATH@/metro/chrome/pdfjs.manifest
|
||||
@BINPATH@/metro/chrome/pdfjs/*
|
||||
#endif
|
||||
@BINPATH@/metro/components
|
||||
@BINPATH@/metro/defaults
|
||||
@BINPATH@/metro/modules
|
||||
|
@ -2,23 +2,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Content Security Policy Connections</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe2'></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var path = "/tests/content/base/test/csp/";
|
||||
|
||||
// These are test results: -1 means it hasn't run,
|
||||
// These are test results: -1 means it hasn't run,
|
||||
// true/false is the pass/fail result.
|
||||
window.tests = {
|
||||
img_good: -1,
|
||||
@ -55,37 +52,34 @@ window.tests = {
|
||||
object_spec_compliant_bad: -1,
|
||||
};
|
||||
|
||||
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
//_good things better be allowed!
|
||||
//_bad things better be stopped!
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
//these things were allowed by CSP
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
// This is a special observer topic that is proxied from
|
||||
// http-on-modify-request in the parent process to inform us when a URI is
|
||||
// loaded
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
var uri = data;
|
||||
if (!testpat.test(uri)) return;
|
||||
var testid = testpat.exec(uri)[1];
|
||||
|
||||
window.testResult(testid,
|
||||
/_good/.test(testid),
|
||||
asciiSpec + " allowed by csp");
|
||||
uri + " allowed by csp");
|
||||
}
|
||||
|
||||
if(topic === "csp-on-violate-policy") {
|
||||
//these were blocked... record that they were blocked
|
||||
if (topic === "csp-on-violate-policy") {
|
||||
// these were blocked... record that they were blocked
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
@ -95,11 +89,11 @@ examiner.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
// must eventually call this to remove the listener,
|
||||
// must eventually call this to remove the listener,
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +120,13 @@ window.testResult = function(testname, result, msg) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{'set':[["security.csp.speccompliant", true]]},
|
||||
{'set':[["security.csp.speccompliant", true],
|
||||
// This defaults to 0 ("preload none") on mobile (B2G/Android), which
|
||||
// blocks loading the resource until the user interacts with a
|
||||
// corresponding widget, which breaks the media_* tests. We set it
|
||||
// back to the default used by desktop Firefox to get consistent
|
||||
// behavior.
|
||||
["media.preload.default", 2]]},
|
||||
function() {
|
||||
// save this for last so that our listeners are registered.
|
||||
// ... this loads the testbed of good and bad requests.
|
||||
|
@ -26,22 +26,16 @@ window.tests = {
|
||||
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-zA-Z]+)");
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
//these things were allowed by CSP
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec))
|
||||
return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
var uri = data;
|
||||
if (!testpat.test(uri)) return;
|
||||
var testid = testpat.exec(uri)[1];
|
||||
window.testResult(testid,
|
||||
/Loaded/.test(testid),
|
||||
"resource loaded");
|
||||
@ -70,7 +64,7 @@ examiner.prototype = {
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,24 +19,20 @@
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
completedTests: 0,
|
||||
totalTests: 4,
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
//these things were allowed by CSP
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
// these things were allowed by CSP
|
||||
var uri = data;
|
||||
if (!testpat.test(uri)) return;
|
||||
var testid = testpat.exec(uri)[1];
|
||||
if (testid === "img_bad") {
|
||||
// img_bad should be *allowed* because the policy is report-only
|
||||
ok(true, "Inline scripts should execute (because the policy is report-only)");
|
||||
@ -69,7 +65,7 @@ examiner.prototype = {
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,29 +33,25 @@ window.tests = {
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
}
|
||||
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
//_good things better be allowed!
|
||||
//_bad things better be stopped!
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
//these things were allowed by CSP
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
var uri = data;
|
||||
if (!testpat.test(uri)) return;
|
||||
var testid = testpat.exec(uri)[1];
|
||||
|
||||
window.testResult(testid,
|
||||
/_good/.test(testid),
|
||||
asciiSpec + " allowed by csp");
|
||||
uri + " allowed by csp");
|
||||
}
|
||||
|
||||
if(topic === "csp-on-violate-policy") {
|
||||
@ -73,7 +69,7 @@ examiner.prototype = {
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Content Security Policy "no eval" base restriction</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
|
||||
<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
|
||||
<iframe style="width:100%;height:300px;" id='cspframe3'></iframe>
|
||||
@ -23,7 +20,6 @@ var evalScriptsThatRan = 0;
|
||||
var evalScriptsBlocked = 0;
|
||||
var evalScriptsTotal = 24;
|
||||
|
||||
|
||||
// called by scripts that run
|
||||
var scriptRan = function(shouldrun, testname, data) {
|
||||
evalScriptsThatRan++;
|
||||
|
@ -2,16 +2,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Content Security Policy "no eval" in crypto.getCRMFRequest()</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
|
||||
<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
|
||||
<iframe style="width:100%;height:300px;" id='cspframe3'></iframe>
|
||||
@ -24,7 +21,6 @@ var evalScriptsThatRan = 0;
|
||||
var evalScriptsBlocked = 0;
|
||||
var evalScriptsTotal = 4;
|
||||
|
||||
|
||||
// called by scripts that run
|
||||
var scriptRan = function(shouldrun, testname, data) {
|
||||
evalScriptsThatRan++;
|
||||
@ -39,7 +35,6 @@ var scriptBlocked = function(shouldrun, testname, data) {
|
||||
checkTestResults();
|
||||
}
|
||||
|
||||
|
||||
// Check to see if all the tests have run
|
||||
var checkTestResults = function() {
|
||||
// if any test is incomplete, keep waiting
|
||||
|
@ -2,23 +2,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Content Security Policy Frame Ancestors directive</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
|
||||
<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var path = "/tests/content/base/test/csp/";
|
||||
|
||||
// These are test results: -1 means it hasn't run,
|
||||
// These are test results: -1 means it hasn't run,
|
||||
// true/false is the pass/fail result.
|
||||
var framesThatShouldLoad = {
|
||||
aa_allow: -1, /* innermost frame allows a */
|
||||
@ -47,7 +44,7 @@ var framesThatShouldLoad = {
|
||||
|
||||
var expectedViolationsLeft = 14;
|
||||
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
@ -57,7 +54,7 @@ examiner.prototype = {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
|
||||
if (topic === "csp-on-violate-policy") {
|
||||
//these were blocked... record that they were blocked
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
|
||||
@ -65,7 +62,7 @@ examiner.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
// must eventually call this to remove the listener,
|
||||
// must eventually call this to remove the listener,
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
|
@ -24,18 +24,14 @@ var totalTests = 2;
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if(!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (asciiSpec == prefixedHeaderImgURL || asciiSpec == unprefixedHeaderImgURL) {
|
||||
is(asciiSpec, unprefixedHeaderImgURL, "Load was allowed - should be allowed by unprefixed header (blocked by prefixed)");
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
var allowedUri = data;
|
||||
if (allowedUri == prefixedHeaderImgURL || allowedUri == unprefixedHeaderImgURL) {
|
||||
is(allowedUri, unprefixedHeaderImgURL, "Load was allowed - should be allowed by unprefixed header (blocked by prefixed)");
|
||||
testRan();
|
||||
}
|
||||
}
|
||||
@ -55,7 +51,7 @@ examiner.prototype = {
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,22 +43,16 @@ window.violation_reports = {
|
||||
// get sent out to the wire. This also watches for violation reports to go out.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if(!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(
|
||||
SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"),
|
||||
"URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
var uri = data;
|
||||
if (!testpat.test(uri)) return;
|
||||
var testid = testpat.exec(uri)[1];
|
||||
|
||||
// violation reports don't come through here, but the requested resources do
|
||||
// if the test has already finished, move on. Some things throw multiple
|
||||
@ -68,8 +62,8 @@ examiner.prototype = {
|
||||
} catch(e) { return; }
|
||||
|
||||
// these are requests that were allowed by CSP
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
window.testResult(testid, 'allowed', asciiSpec + " allowed by csp");
|
||||
var testid = testpat.exec(uri)[1];
|
||||
window.testResult(testid, 'allowed', uri + " allowed by csp");
|
||||
}
|
||||
|
||||
if(topic === "csp-on-violate-policy") {
|
||||
@ -112,7 +106,7 @@ examiner.prototype = {
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
window.bug836922examiner = new examiner();
|
||||
|
@ -9,10 +9,7 @@
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<iframe style="width:200px;height:200px;" id='cspframe' sandbox="allow-same-origin"></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe2' sandbox></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe3' sandbox="allow-same-origin"></iframe>
|
||||
@ -96,28 +93,24 @@ function ok_wrapper(result, desc) {
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
//_good things better be allowed!
|
||||
//_bad things better be stopped!
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
//these things were allowed by CSP
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
var uri = data;
|
||||
if (!testpat.test(uri)) return;
|
||||
var testid = testpat.exec(uri)[1];
|
||||
|
||||
window.testResult(testid,
|
||||
/_good/.test(testid),
|
||||
asciiSpec + " allowed by csp");
|
||||
uri + " allowed by csp");
|
||||
}
|
||||
|
||||
if(topic === "csp-on-violate-policy") {
|
||||
@ -135,7 +128,7 @@ examiner.prototype = {
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ var path = "/tests/content/base/test/csp/";
|
||||
// debugging
|
||||
function log(s) {
|
||||
return;
|
||||
dump("**" + s + "\n");
|
||||
var log = document.getElementById("log");
|
||||
log.textContent = log.textContent+s+"\n";
|
||||
}
|
||||
@ -27,26 +28,22 @@ function log(s) {
|
||||
// used to watch if requests are blocked by CSP or allowed through
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9-]+)");
|
||||
var asciiSpec;
|
||||
var testid;
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
// request was sent
|
||||
asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
testid = testpat.exec(asciiSpec)[1];
|
||||
var allowedUri = data;
|
||||
if (!testpat.test(allowedUri)) return;
|
||||
testid = testpat.exec(allowedUri)[1];
|
||||
if (testExpectedResults[testid] == "completed") return;
|
||||
log("allowed: "+asciiSpec);
|
||||
window.testResult(testid, asciiSpec, true);
|
||||
log("allowed: "+allowedUri);
|
||||
window.testResult(testid, allowedUri, true);
|
||||
}
|
||||
|
||||
else if (topic === "csp-on-violate-policy") {
|
||||
@ -65,7 +62,7 @@ examiner.prototype = {
|
||||
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
window.examiner = new examiner();
|
||||
@ -132,7 +129,13 @@ var testResult = function(testName, url, result) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{'set':[["security.csp.speccompliant", true]]},
|
||||
{'set':[["security.csp.speccompliant", true],
|
||||
// This defaults to 0 ("preload none") on mobile (B2G/Android), which
|
||||
// blocks loading the resource until the user interacts with a
|
||||
// corresponding widget, which breaks the media_* tests. We set it
|
||||
// back to the default used by desktop Firefox to get consistent
|
||||
// behavior.
|
||||
["media.preload.default", 2]]},
|
||||
function() {
|
||||
// save this for last so that our listeners are registered.
|
||||
// ... this loads the testbed of good and bad requests.
|
||||
|
@ -26,25 +26,21 @@ var expectedScriptInlineViolations = 1;
|
||||
|
||||
// This is used to watch the blocked data bounce off CSP
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
}
|
||||
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testid_re = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
//_good things better be allowed!
|
||||
//_bad things better be blocked!
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
// these things were allowed by CSP
|
||||
var allowed_uri = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (!testid_re.test(allowed_uri)) return;
|
||||
var testid = testid_re.exec(allowed_uri)[1];
|
||||
if (topic === "specialpowers-http-notify-request") {
|
||||
var uri = data;
|
||||
if (!testid_re.test(uri)) return;
|
||||
var testid = testid_re.exec(uri)[1];
|
||||
ok(/_good/.test(testid), "Allowed URI with testid " + testid);
|
||||
ranTests(1);
|
||||
}
|
||||
@ -59,6 +55,7 @@ examiner.prototype = {
|
||||
ranTests(1);
|
||||
} catch (e) {
|
||||
// if the subject is blocked inline, data will be a violation msg (defined at the top of contentSecurityPolicy.js)
|
||||
//dump("** exception in csp-on-violate-policy: " + e + "\n");
|
||||
var violation_msg = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsISupportsCString"), "data");
|
||||
if (/Inline Script/.test(violation_msg)) {
|
||||
if (/Inline Script had invalid nonce/.test(violation_msg))
|
||||
@ -73,7 +70,7 @@ examiner.prototype = {
|
||||
},
|
||||
// must eventually call this to remove the listener, or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
dist_dest = $(DIST)/$(APP_NAME).app
|
||||
|
||||
PREF_JS_EXPORTS = $(srcdir)/mobile.js
|
||||
DIST_FILES = recommended-addons.json
|
||||
|
||||
ifndef LIBXUL_SDK
|
||||
ifneq (Android,$(OS_TARGET))
|
||||
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"addons": [{
|
||||
"id": "fullscreen@mbrubeck.limpet.net",
|
||||
"name": "Full Screen",
|
||||
"version": "3.4",
|
||||
"iconURL": "https://addons.cdn.mozilla.net/img/uploads/addon_icons/252/252573-32.png?modified=1354183977",
|
||||
"learnmoreURL": "https://addons.mozilla.org/en-US/android/addon/full-screen-252573/?src=api"
|
||||
}, {
|
||||
"id": "cloudviewer@starkravingfinkle.org",
|
||||
"name": "Cloud Viewer",
|
||||
"version": "2.1",
|
||||
"iconURL": "https://addons.cdn.mozilla.net/img/uploads/addon_icons/295/295895-32.png?modified=1353947644",
|
||||
"learnmoreURL": "https://addons.mozilla.org/en-US/android/addon/cloud-viewer/?src=api"
|
||||
}]
|
||||
}
|
@ -32,8 +32,8 @@ import java.util.List;
|
||||
/**
|
||||
* A page in about:home that displays a ListView of bookmarks.
|
||||
*/
|
||||
public class BookmarksPage extends HomeFragment {
|
||||
public static final String LOGTAG = "GeckoBookmarksPage";
|
||||
public class BookmarksPanel extends HomeFragment {
|
||||
public static final String LOGTAG = "GeckoBookmarksPanel";
|
||||
|
||||
// Cursor loader ID for list of bookmarks.
|
||||
private static final int LOADER_ID_BOOKMARKS_LIST = 0;
|
||||
@ -61,7 +61,7 @@ public class BookmarksPage extends HomeFragment {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.home_bookmarks_page, container, false);
|
||||
final View view = inflater.inflate(R.layout.home_bookmarks_panel, container, false);
|
||||
|
||||
mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list);
|
||||
|
@ -19,17 +19,17 @@ import android.view.LayoutInflater;
|
||||
|
||||
import android.widget.ImageButton;
|
||||
|
||||
public class HistoryPage extends HomeFragment
|
||||
implements IconTabWidget.OnTabChangedListener {
|
||||
public class HistoryPanel extends HomeFragment
|
||||
implements IconTabWidget.OnTabChangedListener {
|
||||
// Logging tag name
|
||||
private static final String LOGTAG = "GeckoHistoryPage";
|
||||
private static final String LOGTAG = "GeckoHistoryPanel";
|
||||
private IconTabWidget mTabWidget;
|
||||
private int mSelectedTab;
|
||||
private boolean initializeRecentPage;
|
||||
private boolean initializeRecentPanel;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.home_history_page, container, false);
|
||||
return inflater.inflate(R.layout.home_history_panel, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,11 +49,11 @@ public class HistoryPage extends HomeFragment
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
// Show most recent page as the initial page.
|
||||
// Show most recent panel as the initial panel.
|
||||
// Since we detach/attach on config change, this prevents from replacing current fragment.
|
||||
if (!initializeRecentPage) {
|
||||
showMostRecentPage();
|
||||
initializeRecentPage = true;
|
||||
if (!initializeRecentPanel) {
|
||||
showMostRecentPanel();
|
||||
initializeRecentPanel = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,9 +64,9 @@ public class HistoryPage extends HomeFragment
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
showMostRecentPage();
|
||||
showMostRecentPanel();
|
||||
} else if (index == 1) {
|
||||
showLastTabsPage();
|
||||
showLastTabsPanel();
|
||||
}
|
||||
|
||||
mTabWidget.setCurrentTab(index);
|
||||
@ -86,23 +86,23 @@ public class HistoryPage extends HomeFragment
|
||||
}
|
||||
}
|
||||
|
||||
private void showSubPage(Fragment subPage) {
|
||||
private void showSubPanel(Fragment subPanel) {
|
||||
final Bundle args = new Bundle();
|
||||
args.putBoolean(HomePager.CAN_LOAD_ARG, getCanLoadHint());
|
||||
subPage.setArguments(args);
|
||||
subPanel.setArguments(args);
|
||||
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.addToBackStack(null).replace(R.id.history_page_container, subPage)
|
||||
.addToBackStack(null).replace(R.id.history_panel_container, subPanel)
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
private void showMostRecentPage() {
|
||||
final MostRecentPage mostRecentPage = MostRecentPage.newInstance();
|
||||
showSubPage(mostRecentPage);
|
||||
private void showMostRecentPanel() {
|
||||
final MostRecentPanel mostRecentPanel = MostRecentPanel.newInstance();
|
||||
showSubPanel(mostRecentPanel);
|
||||
}
|
||||
|
||||
private void showLastTabsPage() {
|
||||
final LastTabsPage lastTabsPage = LastTabsPage.newInstance();
|
||||
showSubPage(lastTabsPage);
|
||||
private void showLastTabsPanel() {
|
||||
final LastTabsPanel lastTabsPanel = LastTabsPanel.newInstance();
|
||||
showSubPanel(lastTabsPanel);
|
||||
}
|
||||
}
|
@ -5,8 +5,8 @@
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageType;
|
||||
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
|
||||
import org.mozilla.gecko.home.HomeConfig.PanelType;
|
||||
import org.mozilla.gecko.home.HomePager;
|
||||
|
||||
import android.content.Context;
|
||||
@ -23,15 +23,15 @@ import java.util.List;
|
||||
class HomeAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private final Context mContext;
|
||||
private final ArrayList<PageInfo> mPageInfos;
|
||||
private final HashMap<String, Fragment> mPages;
|
||||
private final ArrayList<PanelInfo> mPanelInfos;
|
||||
private final HashMap<String, Fragment> mPanels;
|
||||
|
||||
private boolean mCanLoadHint;
|
||||
|
||||
private OnAddPageListener mAddPageListener;
|
||||
private OnAddPanelListener mAddPanelListener;
|
||||
|
||||
interface OnAddPageListener {
|
||||
public void onAddPage(String title);
|
||||
interface OnAddPanelListener {
|
||||
public void onAddPanel(String title);
|
||||
}
|
||||
|
||||
public HomeAdapter(Context context, FragmentManager fm) {
|
||||
@ -40,25 +40,25 @@ class HomeAdapter extends FragmentStatePagerAdapter {
|
||||
mContext = context;
|
||||
mCanLoadHint = HomeFragment.DEFAULT_CAN_LOAD_HINT;
|
||||
|
||||
mPageInfos = new ArrayList<PageInfo>();
|
||||
mPages = new HashMap<String, Fragment>();
|
||||
mPanelInfos = new ArrayList<PanelInfo>();
|
||||
mPanels = new HashMap<String, Fragment>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mPageInfos.size();
|
||||
return mPanelInfos.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
PageInfo info = mPageInfos.get(position);
|
||||
PanelInfo info = mPanelInfos.get(position);
|
||||
return Fragment.instantiate(mContext, info.getClassName(), info.getArgs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
if (mPageInfos.size() > 0) {
|
||||
PageInfo info = mPageInfos.get(position);
|
||||
if (mPanelInfos.size() > 0) {
|
||||
PanelInfo info = mPanelInfos.get(position);
|
||||
return info.getTitle().toUpperCase();
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ class HomeAdapter extends FragmentStatePagerAdapter {
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
Fragment fragment = (Fragment) super.instantiateItem(container, position);
|
||||
mPages.put(mPageInfos.get(position).getId(), fragment);
|
||||
mPanels.put(mPanelInfos.get(position).getId(), fragment);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
@ -76,17 +76,17 @@ class HomeAdapter extends FragmentStatePagerAdapter {
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
super.destroyItem(container, position, object);
|
||||
mPages.remove(mPageInfos.get(position).getId());
|
||||
mPanels.remove(mPanelInfos.get(position).getId());
|
||||
}
|
||||
|
||||
public void setOnAddPageListener(OnAddPageListener listener) {
|
||||
mAddPageListener = listener;
|
||||
public void setOnAddPanelListener(OnAddPanelListener listener) {
|
||||
mAddPanelListener = listener;
|
||||
}
|
||||
|
||||
public int getItemPosition(String pageId) {
|
||||
for (int i = 0; i < mPageInfos.size(); i++) {
|
||||
final String id = mPageInfos.get(i).getId();
|
||||
if (id.equals(pageId)) {
|
||||
public int getItemPosition(String panelId) {
|
||||
for (int i = 0; i < mPanelInfos.size(); i++) {
|
||||
final String id = mPanelInfos.get(i).getId();
|
||||
if (id.equals(panelId)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -94,32 +94,32 @@ class HomeAdapter extends FragmentStatePagerAdapter {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public String getPageIdAtPosition(int position) {
|
||||
// getPageAtPosition() might be called before HomeAdapter
|
||||
// has got its initial list of PageEntries. Just bail.
|
||||
if (mPageInfos.isEmpty()) {
|
||||
public String getPanelIdAtPosition(int position) {
|
||||
// getPanelIdAtPosition() might be called before HomeAdapter
|
||||
// has got its initial list of PanelConfigs. Just bail.
|
||||
if (mPanelInfos.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mPageInfos.get(position).getId();
|
||||
return mPanelInfos.get(position).getId();
|
||||
}
|
||||
|
||||
private void addPage(PageInfo info) {
|
||||
mPageInfos.add(info);
|
||||
private void addPanel(PanelInfo info) {
|
||||
mPanelInfos.add(info);
|
||||
|
||||
if (mAddPageListener != null) {
|
||||
mAddPageListener.onAddPage(info.getTitle());
|
||||
if (mAddPanelListener != null) {
|
||||
mAddPanelListener.onAddPanel(info.getTitle());
|
||||
}
|
||||
}
|
||||
|
||||
public void update(List<PageEntry> pageEntries) {
|
||||
mPages.clear();
|
||||
mPageInfos.clear();
|
||||
public void update(List<PanelConfig> panelConfigs) {
|
||||
mPanels.clear();
|
||||
mPanelInfos.clear();
|
||||
|
||||
if (pageEntries != null) {
|
||||
for (PageEntry pageEntry : pageEntries) {
|
||||
final PageInfo info = new PageInfo(pageEntry);
|
||||
addPage(info);
|
||||
if (panelConfigs != null) {
|
||||
for (PanelConfig panelConfig : panelConfigs) {
|
||||
final PanelInfo info = new PanelInfo(panelConfig);
|
||||
addPanel(info);
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,34 +132,34 @@ class HomeAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
public void setCanLoadHint(boolean canLoadHint) {
|
||||
// We cache the last hint value so that we can use it when
|
||||
// creating new pages. See PageInfo.getArgs().
|
||||
// creating new panels. See PanelInfo.getArgs().
|
||||
mCanLoadHint = canLoadHint;
|
||||
|
||||
// Enable/disable loading on all existing pages
|
||||
for (Fragment page : mPages.values()) {
|
||||
final HomeFragment homePage = (HomeFragment) page;
|
||||
homePage.setCanLoadHint(canLoadHint);
|
||||
// Enable/disable loading on all existing panels
|
||||
for (Fragment panelFragment : mPanels.values()) {
|
||||
final HomeFragment panel = (HomeFragment) panelFragment;
|
||||
panel.setCanLoadHint(canLoadHint);
|
||||
}
|
||||
}
|
||||
|
||||
private final class PageInfo {
|
||||
private final PageEntry mPageEntry;
|
||||
private final class PanelInfo {
|
||||
private final PanelConfig mPanelConfig;
|
||||
|
||||
PageInfo(PageEntry pageEntry) {
|
||||
mPageEntry = pageEntry;
|
||||
PanelInfo(PanelConfig panelConfig) {
|
||||
mPanelConfig = panelConfig;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mPageEntry.getId();
|
||||
return mPanelConfig.getId();
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mPageEntry.getTitle();
|
||||
return mPanelConfig.getTitle();
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
final PageType type = mPageEntry.getType();
|
||||
return type.getPageClass().getName();
|
||||
final PanelType type = mPanelConfig.getType();
|
||||
return type.getPanelClass().getName();
|
||||
}
|
||||
|
||||
public Bundle getArgs() {
|
||||
@ -167,9 +167,9 @@ class HomeAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
args.putBoolean(HomePager.CAN_LOAD_ARG, mCanLoadHint);
|
||||
|
||||
// Only list pages need the page entry
|
||||
if (mPageEntry.getType() == PageType.LIST) {
|
||||
args.putParcelable(HomePager.PAGE_ENTRY_ARG, mPageEntry);
|
||||
// Only ListPanel's need the PanelConfig argument
|
||||
if (mPanelConfig.getType() == PanelType.LIST) {
|
||||
args.putParcelable(HomePager.PANEL_CONFIG_ARG, mPanelConfig);
|
||||
}
|
||||
|
||||
return args;
|
||||
|
@ -14,33 +14,33 @@ import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
final class HomeConfig {
|
||||
public static enum PageType implements Parcelable {
|
||||
TOP_SITES("top_sites", TopSitesPage.class),
|
||||
BOOKMARKS("bookmarks", BookmarksPage.class),
|
||||
HISTORY("history", HistoryPage.class),
|
||||
READING_LIST("reading_list", ReadingListPage.class),
|
||||
LIST("list", ListPage.class);
|
||||
public static enum PanelType implements Parcelable {
|
||||
TOP_SITES("top_sites", TopSitesPanel.class),
|
||||
BOOKMARKS("bookmarks", BookmarksPanel.class),
|
||||
HISTORY("history", HistoryPanel.class),
|
||||
READING_LIST("reading_list", ReadingListPanel.class),
|
||||
LIST("list", ListPanel.class);
|
||||
|
||||
private final String mId;
|
||||
private final Class<?> mPageClass;
|
||||
private final Class<?> mPanelClass;
|
||||
|
||||
PageType(String id, Class<?> pageClass) {
|
||||
PanelType(String id, Class<?> panelClass) {
|
||||
mId = id;
|
||||
mPageClass = pageClass;
|
||||
mPanelClass = panelClass;
|
||||
}
|
||||
|
||||
public static PageType fromId(String id) {
|
||||
public static PanelType fromId(String id) {
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Could not convert null String to PageType");
|
||||
throw new IllegalArgumentException("Could not convert null String to PanelType");
|
||||
}
|
||||
|
||||
for (PageType page : PageType.values()) {
|
||||
if (TextUtils.equals(page.mId, id.toLowerCase())) {
|
||||
return page;
|
||||
for (PanelType panelType : PanelType.values()) {
|
||||
if (TextUtils.equals(panelType.mId, id.toLowerCase())) {
|
||||
return panelType;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Could not convert String id to PageType");
|
||||
throw new IllegalArgumentException("Could not convert String id to PanelType");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -48,8 +48,8 @@ final class HomeConfig {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public Class<?> getPageClass() {
|
||||
return mPageClass;
|
||||
public Class<?> getPanelClass() {
|
||||
return mPanelClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,72 +62,72 @@ final class HomeConfig {
|
||||
dest.writeInt(ordinal());
|
||||
}
|
||||
|
||||
public static final Creator<PageType> CREATOR = new Creator<PageType>() {
|
||||
public static final Creator<PanelType> CREATOR = new Creator<PanelType>() {
|
||||
@Override
|
||||
public PageType createFromParcel(final Parcel source) {
|
||||
return PageType.values()[source.readInt()];
|
||||
public PanelType createFromParcel(final Parcel source) {
|
||||
return PanelType.values()[source.readInt()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageType[] newArray(final int size) {
|
||||
return new PageType[size];
|
||||
public PanelType[] newArray(final int size) {
|
||||
return new PanelType[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class PageEntry implements Parcelable {
|
||||
private final PageType mType;
|
||||
public static class PanelConfig implements Parcelable {
|
||||
private final PanelType mType;
|
||||
private final String mTitle;
|
||||
private final String mId;
|
||||
private final EnumSet<Flags> mFlags;
|
||||
|
||||
public enum Flags {
|
||||
DEFAULT_PAGE
|
||||
DEFAULT_PANEL
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PageEntry(Parcel in) {
|
||||
mType = (PageType) in.readParcelable(getClass().getClassLoader());
|
||||
public PanelConfig(Parcel in) {
|
||||
mType = (PanelType) in.readParcelable(getClass().getClassLoader());
|
||||
mTitle = in.readString();
|
||||
mId = in.readString();
|
||||
mFlags = (EnumSet<Flags>) in.readSerializable();
|
||||
}
|
||||
|
||||
public PageEntry(PageType type, String title) {
|
||||
public PanelConfig(PanelType type, String title) {
|
||||
this(type, title, EnumSet.noneOf(Flags.class));
|
||||
}
|
||||
|
||||
public PageEntry(PageType type, String title, EnumSet<Flags> flags) {
|
||||
public PanelConfig(PanelType type, String title, EnumSet<Flags> flags) {
|
||||
this(type, title, type.toString(), flags);
|
||||
}
|
||||
|
||||
public PageEntry(PageType type, String title, String id) {
|
||||
public PanelConfig(PanelType type, String title, String id) {
|
||||
this(type, title, id, EnumSet.noneOf(Flags.class));
|
||||
}
|
||||
|
||||
public PageEntry(PageType type, String title, String id, EnumSet<Flags> flags) {
|
||||
public PanelConfig(PanelType type, String title, String id, EnumSet<Flags> flags) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Can't create PageEntry with null type");
|
||||
throw new IllegalArgumentException("Can't create PanelConfig with null type");
|
||||
}
|
||||
mType = type;
|
||||
|
||||
if (title == null) {
|
||||
throw new IllegalArgumentException("Can't create PageEntry with null title");
|
||||
throw new IllegalArgumentException("Can't create PanelConfig with null title");
|
||||
}
|
||||
mTitle = title;
|
||||
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Can't create PageEntry with null id");
|
||||
throw new IllegalArgumentException("Can't create PanelConfig with null id");
|
||||
}
|
||||
mId = id;
|
||||
|
||||
if (flags == null) {
|
||||
throw new IllegalArgumentException("Can't create PageEntry with null flags");
|
||||
throw new IllegalArgumentException("Can't create PanelConfig with null flags");
|
||||
}
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
public PageType getType() {
|
||||
public PanelType getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ final class HomeConfig {
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return mFlags.contains(Flags.DEFAULT_PAGE);
|
||||
return mFlags.contains(Flags.DEFAULT_PANEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,15 +156,15 @@ final class HomeConfig {
|
||||
dest.writeSerializable(mFlags);
|
||||
}
|
||||
|
||||
public static final Creator<PageEntry> CREATOR = new Creator<PageEntry>() {
|
||||
public static final Creator<PanelConfig> CREATOR = new Creator<PanelConfig>() {
|
||||
@Override
|
||||
public PageEntry createFromParcel(final Parcel in) {
|
||||
return new PageEntry(in);
|
||||
public PanelConfig createFromParcel(final Parcel in) {
|
||||
return new PanelConfig(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageEntry[] newArray(final int size) {
|
||||
return new PageEntry[size];
|
||||
public PanelConfig[] newArray(final int size) {
|
||||
return new PanelConfig[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -174,8 +174,8 @@ final class HomeConfig {
|
||||
}
|
||||
|
||||
public interface HomeConfigBackend {
|
||||
public List<PageEntry> load();
|
||||
public void save(List<PageEntry> entries);
|
||||
public List<PanelConfig> load();
|
||||
public void save(List<PanelConfig> entries);
|
||||
public void setOnChangeListener(OnChangeListener listener);
|
||||
}
|
||||
|
||||
@ -185,11 +185,11 @@ final class HomeConfig {
|
||||
mBackend = backend;
|
||||
}
|
||||
|
||||
public List<PageEntry> load() {
|
||||
public List<PanelConfig> load() {
|
||||
return mBackend.load();
|
||||
}
|
||||
|
||||
public void save(List<PageEntry> entries) {
|
||||
public void save(List<PanelConfig> entries) {
|
||||
mBackend.save(entries);
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
|
||||
import org.mozilla.gecko.home.HomeConfig.OnChangeListener;
|
||||
|
||||
import android.content.Context;
|
||||
@ -13,9 +13,9 @@ import android.support.v4.content.AsyncTaskLoader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HomeConfigLoader extends AsyncTaskLoader<List<PageEntry>> {
|
||||
public class HomeConfigLoader extends AsyncTaskLoader<List<PanelConfig>> {
|
||||
private final HomeConfig mConfig;
|
||||
private List<PageEntry> mPageEntries;
|
||||
private List<PanelConfig> mPanelConfigs;
|
||||
|
||||
public HomeConfigLoader(Context context, HomeConfig homeConfig) {
|
||||
super(context);
|
||||
@ -23,32 +23,32 @@ public class HomeConfigLoader extends AsyncTaskLoader<List<PageEntry>> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PageEntry> loadInBackground() {
|
||||
public List<PanelConfig> loadInBackground() {
|
||||
return mConfig.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliverResult(List<PageEntry> pageEntries) {
|
||||
public void deliverResult(List<PanelConfig> panelConfigs) {
|
||||
if (isReset()) {
|
||||
mPageEntries = null;
|
||||
mPanelConfigs = null;
|
||||
return;
|
||||
}
|
||||
|
||||
mPageEntries = pageEntries;
|
||||
mPanelConfigs = panelConfigs;
|
||||
mConfig.setOnChangeListener(new ForceLoadChangeListener());
|
||||
|
||||
if (isStarted()) {
|
||||
super.deliverResult(pageEntries);
|
||||
super.deliverResult(panelConfigs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
if (mPageEntries != null) {
|
||||
deliverResult(mPageEntries);
|
||||
if (mPanelConfigs != null) {
|
||||
deliverResult(mPanelConfigs);
|
||||
}
|
||||
|
||||
if (takeContentChanged() || mPageEntries == null) {
|
||||
if (takeContentChanged() || mPanelConfigs == null) {
|
||||
forceLoad();
|
||||
}
|
||||
}
|
||||
@ -59,8 +59,8 @@ public class HomeConfigLoader extends AsyncTaskLoader<List<PageEntry>> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(List<PageEntry> pageEntries) {
|
||||
mPageEntries = null;
|
||||
public void onCanceled(List<PanelConfig> panelConfigs) {
|
||||
mPanelConfigs = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,7 +70,7 @@ public class HomeConfigLoader extends AsyncTaskLoader<List<PageEntry>> {
|
||||
// Ensure the loader is stopped.
|
||||
onStopLoading();
|
||||
|
||||
mPageEntries = null;
|
||||
mPanelConfigs = null;
|
||||
mConfig.setOnChangeListener(null);
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,8 @@ package org.mozilla.gecko.home;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.home.HomeConfig.HomeConfigBackend;
|
||||
import org.mozilla.gecko.home.HomeConfig.OnChangeListener;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageType;
|
||||
import org.mozilla.gecko.home.ListManager.ListInfo;
|
||||
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
|
||||
import org.mozilla.gecko.home.HomeConfig.PanelType;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
@ -54,129 +53,129 @@ class HomeConfigPrefsBackend implements HomeConfigBackend {
|
||||
return PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
}
|
||||
|
||||
private List<PageEntry> loadDefaultConfig() {
|
||||
final ArrayList<PageEntry> pageEntries = new ArrayList<PageEntry>();
|
||||
private List<PanelConfig> loadDefaultConfig() {
|
||||
final ArrayList<PanelConfig> panelConfigs = new ArrayList<PanelConfig>();
|
||||
|
||||
pageEntries.add(new PageEntry(PageType.TOP_SITES,
|
||||
panelConfigs.add(new PanelConfig(PanelType.TOP_SITES,
|
||||
mContext.getString(R.string.home_top_sites_title),
|
||||
EnumSet.of(PageEntry.Flags.DEFAULT_PAGE)));
|
||||
EnumSet.of(PanelConfig.Flags.DEFAULT_PANEL)));
|
||||
|
||||
pageEntries.add(new PageEntry(PageType.BOOKMARKS,
|
||||
panelConfigs.add(new PanelConfig(PanelType.BOOKMARKS,
|
||||
mContext.getString(R.string.bookmarks_title)));
|
||||
|
||||
// We disable reader mode support on low memory devices. Hence the
|
||||
// reading list page should not show up on such devices.
|
||||
// reading list panel should not show up on such devices.
|
||||
if (!HardwareUtils.isLowMemoryPlatform()) {
|
||||
pageEntries.add(new PageEntry(PageType.READING_LIST,
|
||||
panelConfigs.add(new PanelConfig(PanelType.READING_LIST,
|
||||
mContext.getString(R.string.reading_list_title)));
|
||||
}
|
||||
|
||||
final PageEntry historyEntry = new PageEntry(PageType.HISTORY,
|
||||
final PanelConfig historyEntry = new PanelConfig(PanelType.HISTORY,
|
||||
mContext.getString(R.string.home_history_title));
|
||||
|
||||
// On tablets, the history page is the last.
|
||||
// On phones, the history page is the first one.
|
||||
// On tablets, the history panel is the last.
|
||||
// On phones, the history panel is the first one.
|
||||
if (HardwareUtils.isTablet()) {
|
||||
pageEntries.add(historyEntry);
|
||||
panelConfigs.add(historyEntry);
|
||||
} else {
|
||||
pageEntries.add(0, historyEntry);
|
||||
panelConfigs.add(0, historyEntry);
|
||||
}
|
||||
|
||||
return pageEntries;
|
||||
return panelConfigs;
|
||||
}
|
||||
|
||||
private List<PageEntry> loadConfigFromString(String jsonString) {
|
||||
final JSONArray jsonPageEntries;
|
||||
private List<PanelConfig> loadConfigFromString(String jsonString) {
|
||||
final JSONArray jsonPanelConfigs;
|
||||
try {
|
||||
jsonPageEntries = new JSONArray(jsonString);
|
||||
jsonPanelConfigs = new JSONArray(jsonString);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "Error loading the list of home pages from JSON prefs", e);
|
||||
Log.e(LOGTAG, "Error loading the list of home panels from JSON prefs", e);
|
||||
|
||||
// Fallback to default config
|
||||
return loadDefaultConfig();
|
||||
}
|
||||
|
||||
final ArrayList<PageEntry> pageEntries = new ArrayList<PageEntry>();
|
||||
final ArrayList<PanelConfig> panelConfigs = new ArrayList<PanelConfig>();
|
||||
|
||||
final int count = jsonPageEntries.length();
|
||||
final int count = jsonPanelConfigs.length();
|
||||
for (int i = 0; i < count; i++) {
|
||||
try {
|
||||
final JSONObject jsonPageEntry = jsonPageEntries.getJSONObject(i);
|
||||
final JSONObject jsonPanelConfig = jsonPanelConfigs.getJSONObject(i);
|
||||
|
||||
final PageEntry pageEntry = loadPageEntryFromJSON(jsonPageEntry);
|
||||
pageEntries.add(pageEntry);
|
||||
final PanelConfig panelConfig = loadPanelConfigFromJSON(jsonPanelConfig);
|
||||
panelConfigs.add(panelConfig);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception loading page entry from JSON", e);
|
||||
Log.e(LOGTAG, "Exception loading PanelConfig from JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
return pageEntries;
|
||||
return panelConfigs;
|
||||
}
|
||||
|
||||
private PageEntry loadPageEntryFromJSON(JSONObject jsonPageEntry)
|
||||
private PanelConfig loadPanelConfigFromJSON(JSONObject jsonPanelConfig)
|
||||
throws JSONException, IllegalArgumentException {
|
||||
final PageType type = PageType.fromId(jsonPageEntry.getString(JSON_KEY_TYPE));
|
||||
final String title = jsonPageEntry.getString(JSON_KEY_TITLE);
|
||||
final String id = jsonPageEntry.getString(JSON_KEY_ID);
|
||||
final PanelType type = PanelType.fromId(jsonPanelConfig.getString(JSON_KEY_TYPE));
|
||||
final String title = jsonPanelConfig.getString(JSON_KEY_TITLE);
|
||||
final String id = jsonPanelConfig.getString(JSON_KEY_ID);
|
||||
|
||||
final EnumSet<PageEntry.Flags> flags = EnumSet.noneOf(PageEntry.Flags.class);
|
||||
final boolean isDefault = (jsonPageEntry.optInt(JSON_KEY_DEFAULT, -1) == IS_DEFAULT);
|
||||
final EnumSet<PanelConfig.Flags> flags = EnumSet.noneOf(PanelConfig.Flags.class);
|
||||
final boolean isDefault = (jsonPanelConfig.optInt(JSON_KEY_DEFAULT, -1) == IS_DEFAULT);
|
||||
if (isDefault) {
|
||||
flags.add(PageEntry.Flags.DEFAULT_PAGE);
|
||||
flags.add(PanelConfig.Flags.DEFAULT_PANEL);
|
||||
}
|
||||
|
||||
return new PageEntry(type, title, id, flags);
|
||||
return new PanelConfig(type, title, id, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PageEntry> load() {
|
||||
public List<PanelConfig> load() {
|
||||
final SharedPreferences prefs = getSharedPreferences();
|
||||
final String jsonString = prefs.getString(PREFS_KEY, null);
|
||||
|
||||
final List<PageEntry> pageEntries;
|
||||
final List<PanelConfig> panelConfigs;
|
||||
if (TextUtils.isEmpty(jsonString)) {
|
||||
pageEntries = loadDefaultConfig();
|
||||
panelConfigs = loadDefaultConfig();
|
||||
} else {
|
||||
pageEntries = loadConfigFromString(jsonString);
|
||||
panelConfigs = loadConfigFromString(jsonString);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(pageEntries);
|
||||
return Collections.unmodifiableList(panelConfigs);
|
||||
}
|
||||
|
||||
private JSONObject convertPageEntryToJSON(PageEntry pageEntry) throws JSONException {
|
||||
final JSONObject jsonPageEntry = new JSONObject();
|
||||
private JSONObject convertPanelConfigToJSON(PanelConfig PanelConfig) throws JSONException {
|
||||
final JSONObject jsonPanelConfig = new JSONObject();
|
||||
|
||||
jsonPageEntry.put(JSON_KEY_TYPE, pageEntry.getType().toString());
|
||||
jsonPageEntry.put(JSON_KEY_TITLE, pageEntry.getTitle());
|
||||
jsonPageEntry.put(JSON_KEY_ID, pageEntry.getId());
|
||||
jsonPanelConfig.put(JSON_KEY_TYPE, PanelConfig.getType().toString());
|
||||
jsonPanelConfig.put(JSON_KEY_TITLE, PanelConfig.getTitle());
|
||||
jsonPanelConfig.put(JSON_KEY_ID, PanelConfig.getId());
|
||||
|
||||
if (pageEntry.isDefault()) {
|
||||
jsonPageEntry.put(JSON_KEY_DEFAULT, IS_DEFAULT);
|
||||
if (PanelConfig.isDefault()) {
|
||||
jsonPanelConfig.put(JSON_KEY_DEFAULT, IS_DEFAULT);
|
||||
}
|
||||
|
||||
return jsonPageEntry;
|
||||
return jsonPanelConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(List<PageEntry> pageEntries) {
|
||||
final JSONArray jsonPageEntries = new JSONArray();
|
||||
public void save(List<PanelConfig> panelConfigs) {
|
||||
final JSONArray jsonPanelConfigs = new JSONArray();
|
||||
|
||||
final int count = pageEntries.size();
|
||||
final int count = panelConfigs.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
try {
|
||||
final PageEntry pageEntry = pageEntries.get(i);
|
||||
final PanelConfig PanelConfig = panelConfigs.get(i);
|
||||
|
||||
final JSONObject jsonPageEntry = convertPageEntryToJSON(pageEntry);
|
||||
jsonPageEntries.put(jsonPageEntry);
|
||||
final JSONObject jsonPanelConfig = convertPanelConfigToJSON(PanelConfig);
|
||||
jsonPanelConfigs.put(jsonPanelConfig);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception loading page entry from JSON", e);
|
||||
Log.e(LOGTAG, "Exception converting PanelConfig to JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
final SharedPreferences prefs = getSharedPreferences();
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
final String jsonString = jsonPageEntries.toString();
|
||||
final String jsonString = jsonPanelConfigs.toString();
|
||||
editor.putString(PREFS_KEY, jsonString);
|
||||
editor.commit();
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ package org.mozilla.gecko.home;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.home.HomeAdapter.OnAddPageListener;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageType;
|
||||
import org.mozilla.gecko.home.HomeAdapter.OnAddPanelListener;
|
||||
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
|
||||
import org.mozilla.gecko.home.HomeConfig.PanelType;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
|
||||
import android.content.Context;
|
||||
@ -40,12 +40,12 @@ public class HomePager extends ViewPager {
|
||||
private Decor mDecor;
|
||||
private View mTabStrip;
|
||||
|
||||
private final OnAddPageListener mAddPageListener;
|
||||
private final OnAddPanelListener mAddPanelListener;
|
||||
|
||||
private final HomeConfig mConfig;
|
||||
private ConfigLoaderCallbacks mConfigLoaderCallbacks;
|
||||
|
||||
private String mInitialPageId;
|
||||
private String mInitialPanelId;
|
||||
|
||||
// Whether or not we need to restart the loader when we show the HomePager.
|
||||
private boolean mRestartLoader;
|
||||
@ -88,7 +88,7 @@ public class HomePager extends ViewPager {
|
||||
}
|
||||
|
||||
static final String CAN_LOAD_ARG = "canLoad";
|
||||
static final String PAGE_ENTRY_ARG = "pageEntry";
|
||||
static final String PANEL_CONFIG_ARG = "panelConfig";
|
||||
|
||||
public HomePager(Context context) {
|
||||
this(context, null);
|
||||
@ -101,16 +101,16 @@ public class HomePager extends ViewPager {
|
||||
mConfig = HomeConfig.getDefault(mContext);
|
||||
mConfigLoaderCallbacks = new ConfigLoaderCallbacks();
|
||||
|
||||
mAddPageListener = new OnAddPageListener() {
|
||||
mAddPanelListener = new OnAddPanelListener() {
|
||||
@Override
|
||||
public void onAddPage(String title) {
|
||||
public void onAddPanel(String title) {
|
||||
if (mDecor != null) {
|
||||
mDecor.onAddPagerView(title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is to keep all 4 pages in memory after they are
|
||||
// This is to keep all 4 panels in memory after they are
|
||||
// selected in the pager.
|
||||
setOffscreenPageLimit(3);
|
||||
|
||||
@ -173,17 +173,17 @@ public class HomePager extends ViewPager {
|
||||
private void redisplay(LoaderManager lm, FragmentManager fm) {
|
||||
final HomeAdapter adapter = (HomeAdapter) getAdapter();
|
||||
|
||||
// If mInitialPageId is non-null, this means the HomePager hasn't
|
||||
// If mInitialPanelId is non-null, this means the HomePager hasn't
|
||||
// finished loading its config yet. Simply re-show() with the
|
||||
// current target page.
|
||||
final String currentPageId;
|
||||
if (mInitialPageId != null) {
|
||||
currentPageId = mInitialPageId;
|
||||
// current target panel.
|
||||
final String currentPanelId;
|
||||
if (mInitialPanelId != null) {
|
||||
currentPanelId = mInitialPanelId;
|
||||
} else {
|
||||
currentPageId = adapter.getPageIdAtPosition(getCurrentItem());
|
||||
currentPanelId = adapter.getPanelIdAtPosition(getCurrentItem());
|
||||
}
|
||||
|
||||
show(lm, fm, currentPageId, null);
|
||||
show(lm, fm, currentPanelId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,25 +191,25 @@ public class HomePager extends ViewPager {
|
||||
*
|
||||
* @param fm FragmentManager for the adapter
|
||||
*/
|
||||
public void show(LoaderManager lm, FragmentManager fm, String pageId, PropertyAnimator animator) {
|
||||
public void show(LoaderManager lm, FragmentManager fm, String panelId, PropertyAnimator animator) {
|
||||
mLoaded = true;
|
||||
mInitialPageId = pageId;
|
||||
mInitialPanelId = panelId;
|
||||
|
||||
// Only animate on post-HC devices, when a non-null animator is given
|
||||
final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11);
|
||||
|
||||
final HomeAdapter adapter = new HomeAdapter(mContext, fm);
|
||||
adapter.setOnAddPageListener(mAddPageListener);
|
||||
adapter.setOnAddPanelListener(mAddPanelListener);
|
||||
adapter.setCanLoadHint(!shouldAnimate);
|
||||
setAdapter(adapter);
|
||||
|
||||
setVisibility(VISIBLE);
|
||||
|
||||
// Don't show the tabs strip until we have the
|
||||
// list of pages in place.
|
||||
// list of panels in place.
|
||||
mTabStrip.setVisibility(View.INVISIBLE);
|
||||
|
||||
// Load list of pages from configuration. Restart the loader if necessary.
|
||||
// Load list of panels from configuration. Restart the loader if necessary.
|
||||
if (mRestartLoader) {
|
||||
lm.restartLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
|
||||
mRestartLoader = false;
|
||||
@ -279,7 +279,7 @@ public class HomePager extends ViewPager {
|
||||
return super.onInterceptTouchEvent(event);
|
||||
}
|
||||
|
||||
private void updateUiFromPageEntries(List<PageEntry> pageEntries) {
|
||||
private void updateUiFromPanelConfigs(List<PanelConfig> panelConfigs) {
|
||||
// We only care about the adapter if HomePager is currently
|
||||
// loaded, which means it's visible in the activity.
|
||||
if (!mLoaded) {
|
||||
@ -293,27 +293,27 @@ public class HomePager extends ViewPager {
|
||||
final HomeAdapter adapter = (HomeAdapter) getAdapter();
|
||||
|
||||
// Disable loading until the final current item is defined
|
||||
// after loading the page entries. This is to stop any temporary
|
||||
// after loading the panel configs. This is to stop any temporary
|
||||
// active item from loading.
|
||||
boolean originalCanLoadHint = adapter.getCanLoadHint();
|
||||
adapter.setCanLoadHint(false);
|
||||
|
||||
// Update the adapter with the new page entries
|
||||
adapter.update(pageEntries);
|
||||
// Update the adapter with the new panel configs
|
||||
adapter.update(panelConfigs);
|
||||
|
||||
final int count = (pageEntries != null ? pageEntries.size() : 0);
|
||||
final int count = (panelConfigs != null ? panelConfigs.size() : 0);
|
||||
mTabStrip.setVisibility(count > 0 ? View.VISIBLE : View.INVISIBLE);
|
||||
|
||||
// Use the default page as defined in the HomePager's configuration
|
||||
// if the initial page wasn't explicitly set by the show() caller.
|
||||
if (mInitialPageId != null) {
|
||||
// XXX: Handle the case where the desired page isn't currently in the adapter (bug 949178)
|
||||
setCurrentItem(adapter.getItemPosition(mInitialPageId), false);
|
||||
mInitialPageId = null;
|
||||
// Use the default panel as defined in the HomePager's configuration
|
||||
// if the initial panel wasn't explicitly set by the show() caller.
|
||||
if (mInitialPanelId != null) {
|
||||
// XXX: Handle the case where the desired panel isn't currently in the adapter (bug 949178)
|
||||
setCurrentItem(adapter.getItemPosition(mInitialPanelId), false);
|
||||
mInitialPanelId = null;
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
final PageEntry pageEntry = pageEntries.get(i);
|
||||
if (pageEntry.isDefault()) {
|
||||
final PanelConfig panelConfig = panelConfigs.get(i);
|
||||
if (panelConfig.isDefault()) {
|
||||
setCurrentItem(i, false);
|
||||
break;
|
||||
}
|
||||
@ -325,20 +325,20 @@ public class HomePager extends ViewPager {
|
||||
adapter.setCanLoadHint(originalCanLoadHint);
|
||||
}
|
||||
|
||||
private class ConfigLoaderCallbacks implements LoaderCallbacks<List<PageEntry>> {
|
||||
private class ConfigLoaderCallbacks implements LoaderCallbacks<List<PanelConfig>> {
|
||||
@Override
|
||||
public Loader<List<PageEntry>> onCreateLoader(int id, Bundle args) {
|
||||
public Loader<List<PanelConfig>> onCreateLoader(int id, Bundle args) {
|
||||
return new HomeConfigLoader(mContext, mConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<PageEntry>> loader, List<PageEntry> pageEntries) {
|
||||
updateUiFromPageEntries(pageEntries);
|
||||
public void onLoadFinished(Loader<List<PanelConfig>> loader, List<PanelConfig> panelConfigs) {
|
||||
updateUiFromPanelConfigs(panelConfigs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<PageEntry>> loader) {
|
||||
updateUiFromPageEntries(null);
|
||||
public void onLoaderReset(Loader<List<PanelConfig>> loader) {
|
||||
updateUiFromPanelConfigs(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,9 @@ import android.widget.TextView;
|
||||
/**
|
||||
* Fragment that displays tabs from last session in a ListView.
|
||||
*/
|
||||
public class LastTabsPage extends HomeFragment {
|
||||
public class LastTabsPanel extends HomeFragment {
|
||||
// Logging tag name
|
||||
private static final String LOGTAG = "GeckoLastTabsPage";
|
||||
private static final String LOGTAG = "GeckoLastTabsPanel";
|
||||
|
||||
// Cursor loader ID for the session parser
|
||||
private static final int LOADER_ID_LAST_TABS = 0;
|
||||
@ -49,7 +49,7 @@ public class LastTabsPage extends HomeFragment {
|
||||
// The view shown by the fragment.
|
||||
private ListView mList;
|
||||
|
||||
// The title for this HomeFragment page.
|
||||
// The title for this HomeFragment panel.
|
||||
private TextView mTitle;
|
||||
|
||||
// The button view for restoring tabs from last session.
|
||||
@ -64,11 +64,11 @@ public class LastTabsPage extends HomeFragment {
|
||||
// On new tabs listener
|
||||
private OnNewTabsListener mNewTabsListener;
|
||||
|
||||
public static LastTabsPage newInstance() {
|
||||
return new LastTabsPage();
|
||||
public static LastTabsPanel newInstance() {
|
||||
return new LastTabsPanel();
|
||||
}
|
||||
|
||||
public LastTabsPage() {
|
||||
public LastTabsPanel() {
|
||||
mNewTabsListener = null;
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ public class LastTabsPage extends HomeFragment {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.home_last_tabs_page, container, false);
|
||||
return inflater.inflate(R.layout.home_last_tabs_panel, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -169,7 +169,7 @@ public class LastTabsPage extends HomeFragment {
|
||||
mRestoreButton.setVisibility(View.GONE);
|
||||
|
||||
if (mEmptyView == null) {
|
||||
// Set empty page view. We delay this so that the empty view won't flash.
|
||||
// Set empty panel view. We delay this so that the empty view won't flash.
|
||||
final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
|
||||
mEmptyView = emptyViewStub.inflate();
|
||||
|
@ -9,7 +9,7 @@ import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.db.BrowserContract.HomeListItems;
|
||||
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
@ -32,14 +32,14 @@ import java.util.EnumSet;
|
||||
/**
|
||||
* Fragment that displays custom lists.
|
||||
*/
|
||||
public class ListPage extends HomeFragment {
|
||||
private static final String LOGTAG = "GeckoListPage";
|
||||
public class ListPanel extends HomeFragment {
|
||||
private static final String LOGTAG = "GeckoListPanel";
|
||||
|
||||
// Cursor loader ID for the lists
|
||||
private static final int LOADER_ID_LIST = 0;
|
||||
|
||||
// The page entry associated with this page
|
||||
private PageEntry mPageEntry;
|
||||
// The configuration associated with this panel
|
||||
private PanelConfig mPanelConfig;
|
||||
|
||||
// Adapter for the list
|
||||
private HomeListAdapter mAdapter;
|
||||
@ -78,11 +78,11 @@ public class ListPage extends HomeFragment {
|
||||
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
mPageEntry = (PageEntry) args.getParcelable(HomePager.PAGE_ENTRY_ARG);
|
||||
mPanelConfig = (PanelConfig) args.getParcelable(HomePager.PANEL_CONFIG_ARG);
|
||||
}
|
||||
|
||||
if (mPageEntry == null) {
|
||||
throw new IllegalStateException("Can't create a ListPage without a PageEntry");
|
||||
if (mPanelConfig == null) {
|
||||
throw new IllegalStateException("Can't create a ListPanel without a PanelConfig");
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ public class ListPage extends HomeFragment {
|
||||
private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return new HomeListLoader(getActivity(), mPageEntry.getId());
|
||||
return new HomeListLoader(getActivity(), mPanelConfig.getId());
|
||||
}
|
||||
|
||||
@Override
|
@ -35,9 +35,9 @@ import java.util.EnumSet;
|
||||
/**
|
||||
* Fragment that displays recent history in a ListView.
|
||||
*/
|
||||
public class MostRecentPage extends HomeFragment {
|
||||
public class MostRecentPanel extends HomeFragment {
|
||||
// Logging tag name
|
||||
private static final String LOGTAG = "GeckoMostRecentPage";
|
||||
private static final String LOGTAG = "GeckoMostRecentPanel";
|
||||
|
||||
// Cursor loader ID for history query
|
||||
private static final int LOADER_ID_HISTORY = 0;
|
||||
@ -57,11 +57,11 @@ public class MostRecentPage extends HomeFragment {
|
||||
// On URL open listener
|
||||
private OnUrlOpenListener mUrlOpenListener;
|
||||
|
||||
public static MostRecentPage newInstance() {
|
||||
return new MostRecentPage();
|
||||
public static MostRecentPanel newInstance() {
|
||||
return new MostRecentPanel();
|
||||
}
|
||||
|
||||
public MostRecentPage() {
|
||||
public MostRecentPanel() {
|
||||
mUrlOpenListener = null;
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ public class MostRecentPage extends HomeFragment {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.home_most_recent_page, container, false);
|
||||
return inflater.inflate(R.layout.home_most_recent_panel, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,7 +155,7 @@ public class MostRecentPage extends HomeFragment {
|
||||
|
||||
// Cursor is empty, so set the empty view if it hasn't been set already.
|
||||
if (mEmptyView == null) {
|
||||
// Set empty page view. We delay this so that the empty view won't flash.
|
||||
// Set empty panel view. We delay this so that the empty view won't flash.
|
||||
final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
|
||||
mEmptyView = emptyViewStub.inflate();
|
||||
|
@ -21,14 +21,14 @@ import android.util.Log;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ListManager implements GeckoEventListener {
|
||||
private static final String LOGTAG = "GeckoListManager";
|
||||
public class PanelManager implements GeckoEventListener {
|
||||
private static final String LOGTAG = "GeckoPanelManager";
|
||||
|
||||
public class ListInfo {
|
||||
public class PanelInfo {
|
||||
public final String id;
|
||||
public final String title;
|
||||
|
||||
public ListInfo(String id, String title) {
|
||||
public PanelInfo(String id, String title) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
}
|
||||
@ -36,20 +36,20 @@ public class ListManager implements GeckoEventListener {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public ListManager(Context context) {
|
||||
public PanelManager(Context context) {
|
||||
mContext = context;
|
||||
|
||||
// Add a listener to handle any new lists that are added after the lists have been loaded.
|
||||
GeckoAppShell.getEventDispatcher().registerEventListener("HomeLists:Added", this);
|
||||
// Add a listener to handle any new panels that are added after the panels have been loaded.
|
||||
GeckoAppShell.getEventDispatcher().registerEventListener("HomePanels:Added", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads list info from SharedPreferences. Don't call this on the main thread!
|
||||
*
|
||||
* @return List<ListInfo> A list of ListInfos for each registered list.
|
||||
* @return List<PanelInfo> A list of PanelInfos for each registered list.
|
||||
*/
|
||||
public List<ListInfo> getListInfos() {
|
||||
final ArrayList<ListInfo> listInfos = new ArrayList<ListInfo>();
|
||||
public List<PanelInfo> getPanelInfos() {
|
||||
final ArrayList<PanelInfo> panelInfos = new ArrayList<PanelInfo>();
|
||||
|
||||
// XXX: We need to use PreferenceManager right now because that's what SharedPreferences.jsm uses (see bug 940575)
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
@ -60,23 +60,23 @@ public class ListManager implements GeckoEventListener {
|
||||
final JSONArray lists = new JSONArray(prefValue);
|
||||
for (int i = 0; i < lists.length(); i++) {
|
||||
final JSONObject list = lists.getJSONObject(i);
|
||||
final ListInfo info = new ListInfo(list.getString("id"), list.getString("title"));
|
||||
listInfos.add(info);
|
||||
final PanelInfo info = new PanelInfo(list.getString("id"), list.getString("title"));
|
||||
panelInfos.add(info);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "Exception getting list info", e);
|
||||
}
|
||||
}
|
||||
return listInfos;
|
||||
return panelInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for "HomeLists:Added"
|
||||
* Listens for "HomePanels:Added"
|
||||
*/
|
||||
@Override
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
try {
|
||||
final ListInfo info = new ListInfo(message.getString("id"), message.getString("title"));
|
||||
final PanelInfo info = new PanelInfo(message.getString("id"), message.getString("title"));
|
||||
|
||||
// Do something to update the set of list pages.
|
||||
|
@ -111,7 +111,7 @@ class PinSiteDialog extends DialogFragment {
|
||||
// If the user manually entered a search term or URL, wrap the value in
|
||||
// a special URI until we can get a valid URL for this bookmark.
|
||||
final String text = mSearch.getText().toString();
|
||||
final String url = TopSitesPage.encodeUserEnteredUrl(text);
|
||||
final String url = TopSitesPanel.encodeUserEnteredUrl(text);
|
||||
mOnSiteSelectedListener.onSiteSelected(url, text);
|
||||
|
||||
dismiss();
|
||||
|
@ -37,7 +37,7 @@ import java.util.EnumSet;
|
||||
/**
|
||||
* Fragment that displays reading list contents in a ListView.
|
||||
*/
|
||||
public class ReadingListPage extends HomeFragment {
|
||||
public class ReadingListPanel extends HomeFragment {
|
||||
// Cursor loader ID for reading list
|
||||
private static final int LOADER_ID_READING_LIST = 0;
|
||||
|
||||
@ -80,7 +80,7 @@ public class ReadingListPage extends HomeFragment {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.home_reading_list_page, container, false);
|
||||
return inflater.inflate(R.layout.home_reading_list_panel, container, false);
|
||||
}
|
||||
|
||||
@Override
|
@ -193,7 +193,7 @@ public class TopSitesGridItemView extends RelativeLayout {
|
||||
|
||||
// The dirty state forces the state update to return true
|
||||
// so that the adapter loads favicons once the thumbnails
|
||||
// are loaded in TopSitesPage/TopSitesGridAdapter.
|
||||
// are loaded in TopSitesPanel/TopSitesGridAdapter.
|
||||
changed = (changed || mIsDirty);
|
||||
mIsDirty = false;
|
||||
|
||||
|
@ -106,7 +106,7 @@ public class TopSitesGridView extends GridView {
|
||||
TopSitesGridItemView row = (TopSitesGridItemView) view;
|
||||
|
||||
// Decode "user-entered" URLs before loading them.
|
||||
String url = TopSitesPage.decodeUserEnteredUrl(row.getUrl());
|
||||
String url = TopSitesPanel.decodeUserEnteredUrl(row.getUrl());
|
||||
|
||||
// If the url is empty, the user can pin a site.
|
||||
// If not, navigate to the page given by the url.
|
||||
|
@ -62,9 +62,9 @@ import java.util.Map;
|
||||
/**
|
||||
* Fragment that displays frecency search results in a ListView.
|
||||
*/
|
||||
public class TopSitesPage extends HomeFragment {
|
||||
public class TopSitesPanel extends HomeFragment {
|
||||
// Logging tag name
|
||||
private static final String LOGTAG = "GeckoTopSitesPage";
|
||||
private static final String LOGTAG = "GeckoTopSitesPanel";
|
||||
|
||||
// Cursor loader ID for the top sites
|
||||
private static final int LOADER_ID_TOP_SITES = 0;
|
||||
@ -114,11 +114,11 @@ public class TopSitesPage extends HomeFragment {
|
||||
// Time in ms until the Gecko thread is reset to normal priority.
|
||||
private static final long PRIORITY_RESET_TIMEOUT = 10000;
|
||||
|
||||
public static TopSitesPage newInstance() {
|
||||
return new TopSitesPage();
|
||||
public static TopSitesPanel newInstance() {
|
||||
return new TopSitesPanel();
|
||||
}
|
||||
|
||||
public TopSitesPage() {
|
||||
public TopSitesPanel() {
|
||||
mUrlOpenListener = null;
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ public class TopSitesPage extends HomeFragment {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.home_top_sites_page, container, false);
|
||||
final View view = inflater.inflate(R.layout.home_top_sites_panel, container, false);
|
||||
|
||||
mList = (HomeListView) view.findViewById(R.id.list);
|
||||
|
||||
@ -212,7 +212,7 @@ public class TopSitesPage extends HomeFragment {
|
||||
mList.setOnTouchListener(new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
TopSitesPage.this.handleListTouchEvent(event);
|
||||
TopSitesPanel.this.handleListTouchEvent(event);
|
||||
return false;
|
||||
}
|
||||
});
|
@ -208,10 +208,10 @@ gbjar.sources += [
|
||||
'home/BookmarkFolderView.java',
|
||||
'home/BookmarksListAdapter.java',
|
||||
'home/BookmarksListView.java',
|
||||
'home/BookmarksPage.java',
|
||||
'home/BookmarksPanel.java',
|
||||
'home/BrowserSearch.java',
|
||||
'home/FadedTextView.java',
|
||||
'home/HistoryPage.java',
|
||||
'home/HistoryPanel.java',
|
||||
'home/HomeAdapter.java',
|
||||
'home/HomeBanner.java',
|
||||
'home/HomeConfig.java',
|
||||
@ -221,13 +221,13 @@ gbjar.sources += [
|
||||
'home/HomeListView.java',
|
||||
'home/HomePager.java',
|
||||
'home/HomePagerTabStrip.java',
|
||||
'home/LastTabsPage.java',
|
||||
'home/ListManager.java',
|
||||
'home/ListPage.java',
|
||||
'home/MostRecentPage.java',
|
||||
'home/LastTabsPanel.java',
|
||||
'home/ListPanel.java',
|
||||
'home/MostRecentPanel.java',
|
||||
'home/MultiTypeCursorAdapter.java',
|
||||
'home/PanelManager.java',
|
||||
'home/PinSiteDialog.java',
|
||||
'home/ReadingListPage.java',
|
||||
'home/ReadingListPanel.java',
|
||||
'home/SearchEngine.java',
|
||||
'home/SearchEngineRow.java',
|
||||
'home/SearchLoader.java',
|
||||
@ -236,7 +236,7 @@ gbjar.sources += [
|
||||
'home/TabMenuStrip.java',
|
||||
'home/TopSitesGridItemView.java',
|
||||
'home/TopSitesGridView.java',
|
||||
'home/TopSitesPage.java',
|
||||
'home/TopSitesPanel.java',
|
||||
'home/TopSitesThumbnailView.java',
|
||||
'home/TwoLinePageRow.java',
|
||||
'InputMethods.java',
|
||||
|
@ -16,7 +16,7 @@
|
||||
android:layout="@layout/home_history_tabs_indicator"
|
||||
gecko:display="text"/>
|
||||
|
||||
<FrameLayout android:id="@+id/history_page_container"
|
||||
<FrameLayout android:id="@+id/history_panel_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1" />
|
@ -16,7 +16,7 @@
|
||||
android:layout="@layout/home_history_tabs_indicator"
|
||||
gecko:display="text"/>
|
||||
|
||||
<FrameLayout android:id="@+id/history_page_container"
|
||||
<FrameLayout android:id="@+id/history_panel_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1" />
|
@ -11,7 +11,7 @@
|
||||
android:layout_height="fill_parent"/>
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
style="@style/Widget.Home.HistoryPageTitle"
|
||||
style="@style/Widget.Home.HistoryPanelTitle"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.home.HomeListView
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout android:id="@+id/history_page_container"
|
||||
<FrameLayout android:id="@+id/history_panel_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
@ -53,7 +53,7 @@
|
||||
|
||||
<!--
|
||||
The content of the banner should align with the Grid/List views
|
||||
in BookmarksPage. BookmarksListView has a 120dp padding and
|
||||
in BookmarksPanel. BookmarksListView has a 120dp padding and
|
||||
the TwoLinePageRows have a 50dp padding. Hence HomeBanner should
|
||||
have 170dp padding.
|
||||
-->
|
||||
|
@ -91,7 +91,7 @@
|
||||
|
||||
<style name="Widget.ReadingListView" parent="Widget.BookmarksListView"/>
|
||||
|
||||
<style name="Widget.Home.HistoryPageTitle" parent="Widget.Home.HistoryTabIndicator">
|
||||
<style name="Widget.Home.HistoryPanelTitle" parent="Widget.Home.HistoryTabIndicator">
|
||||
<item name="android:layout_marginLeft">32dp</item>
|
||||
<item name="android:layout_marginRight">32dp</item>
|
||||
</style>
|
||||
|
@ -209,14 +209,14 @@
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">32dp</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.Widget.Home.PageTitle</item>
|
||||
<item name="android:background">@drawable/home_page_title_background</item>
|
||||
<item name="android:background">@drawable/home_panel_title_background</item>
|
||||
<item name="android:focusable">false</item>
|
||||
<item name="android:gravity">center|left</item>
|
||||
<item name="android:paddingLeft">10dip</item>
|
||||
<item name="android:paddingRight">10dip</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Home.HistoryPageTitle" parent="Widget.Home.HistoryTabIndicator"/>
|
||||
<style name="Widget.Home.HistoryPanelTitle" parent="Widget.Home.HistoryTabIndicator"/>
|
||||
|
||||
<!--
|
||||
TextAppearance
|
||||
|
@ -24,8 +24,8 @@ import android.view.View;
|
||||
public class AboutHomeComponent extends BaseComponent {
|
||||
private static final String LOGTAG = AboutHomeComponent.class.getSimpleName();
|
||||
|
||||
// The different types of pages that can be present on about:home
|
||||
public enum PageType {
|
||||
// The different types of panels that can be present on about:home
|
||||
public enum PanelType {
|
||||
HISTORY,
|
||||
TOP_SITES,
|
||||
BOOKMARKS,
|
||||
@ -62,7 +62,7 @@ public class AboutHomeComponent extends BaseComponent {
|
||||
return (ViewPager) mSolo.getView(R.id.home_pager);
|
||||
}
|
||||
|
||||
public AboutHomeComponent assertCurrentPage(final PageType expectedPage) {
|
||||
public AboutHomeComponent assertCurrentPage(final PanelType expectedPage) {
|
||||
assertVisible();
|
||||
|
||||
final int expectedPageIndex = getPageIndexForDevice(expectedPage.ordinal());
|
||||
@ -132,10 +132,10 @@ public class AboutHomeComponent extends BaseComponent {
|
||||
|
||||
/**
|
||||
* Gets the page index in the device specific Page enum for the given index in the
|
||||
* PageType enum.
|
||||
* PanelType enum.
|
||||
*/
|
||||
private int getPageIndexForDevice(final int pageIndex) {
|
||||
final String pageName = PageType.values()[pageIndex].name();
|
||||
final String pageName = PanelType.values()[pageIndex].name();
|
||||
final Class devicePageEnum =
|
||||
DeviceHelper.isTablet() ? TabletPage.class : PhonePage.class;
|
||||
return Enum.valueOf(devicePageEnum, pageName).ordinal();
|
||||
|
@ -9,7 +9,7 @@ skip-if = processor == "x86"
|
||||
# disabled on x86 only; bug 927476
|
||||
skip-if = processor == "x86"
|
||||
# [testBookmark] # see bug 915350
|
||||
[testBookmarksPage]
|
||||
[testBookmarksPanel]
|
||||
[testBookmarkFolders]
|
||||
# [testBookmarklets] # see bug 915350
|
||||
# [testBookmarkKeyword] # see bug 915350
|
||||
|
@ -2,7 +2,7 @@ package org.mozilla.gecko.tests;
|
||||
|
||||
import static org.mozilla.gecko.tests.helpers.AssertionHelper.*;
|
||||
|
||||
import org.mozilla.gecko.tests.components.AboutHomeComponent.PageType;
|
||||
import org.mozilla.gecko.tests.components.AboutHomeComponent.PanelType;
|
||||
import org.mozilla.gecko.tests.helpers.*;
|
||||
|
||||
/**
|
||||
@ -16,13 +16,13 @@ public class testAboutHomePageNavigation extends UITest {
|
||||
GeckoHelper.blockForReady();
|
||||
|
||||
mAboutHome.assertVisible()
|
||||
.assertCurrentPage(PageType.TOP_SITES);
|
||||
.assertCurrentPage(PanelType.TOP_SITES);
|
||||
|
||||
mAboutHome.swipeToPageOnRight();
|
||||
mAboutHome.assertCurrentPage(PageType.BOOKMARKS);
|
||||
mAboutHome.assertCurrentPage(PanelType.BOOKMARKS);
|
||||
|
||||
mAboutHome.swipeToPageOnRight();
|
||||
mAboutHome.assertCurrentPage(PageType.READING_LIST);
|
||||
mAboutHome.assertCurrentPage(PanelType.READING_LIST);
|
||||
|
||||
// Ideally these helpers would just be their own tests. However, by keeping this within
|
||||
// one method, we're saving test setUp and tearDown resources.
|
||||
@ -35,46 +35,46 @@ public class testAboutHomePageNavigation extends UITest {
|
||||
|
||||
private void helperTestTablet() {
|
||||
mAboutHome.swipeToPageOnRight();
|
||||
mAboutHome.assertCurrentPage(PageType.HISTORY);
|
||||
mAboutHome.assertCurrentPage(PanelType.HISTORY);
|
||||
|
||||
// Edge case.
|
||||
mAboutHome.swipeToPageOnRight();
|
||||
mAboutHome.assertCurrentPage(PageType.HISTORY);
|
||||
mAboutHome.assertCurrentPage(PanelType.HISTORY);
|
||||
|
||||
mAboutHome.swipeToPageOnLeft();
|
||||
mAboutHome.assertCurrentPage(PageType.READING_LIST);
|
||||
mAboutHome.assertCurrentPage(PanelType.READING_LIST);
|
||||
|
||||
mAboutHome.swipeToPageOnLeft();
|
||||
mAboutHome.assertCurrentPage(PageType.BOOKMARKS);
|
||||
mAboutHome.assertCurrentPage(PanelType.BOOKMARKS);
|
||||
|
||||
mAboutHome.swipeToPageOnLeft();
|
||||
mAboutHome.assertCurrentPage(PageType.TOP_SITES);
|
||||
mAboutHome.assertCurrentPage(PanelType.TOP_SITES);
|
||||
|
||||
// Edge case.
|
||||
mAboutHome.swipeToPageOnLeft();
|
||||
mAboutHome.assertCurrentPage(PageType.TOP_SITES);
|
||||
mAboutHome.assertCurrentPage(PanelType.TOP_SITES);
|
||||
}
|
||||
|
||||
private void helperTestPhone() {
|
||||
// Edge case.
|
||||
mAboutHome.swipeToPageOnRight();
|
||||
mAboutHome.assertCurrentPage(PageType.READING_LIST);
|
||||
mAboutHome.assertCurrentPage(PanelType.READING_LIST);
|
||||
|
||||
mAboutHome.swipeToPageOnLeft();
|
||||
mAboutHome.assertCurrentPage(PageType.BOOKMARKS);
|
||||
mAboutHome.assertCurrentPage(PanelType.BOOKMARKS);
|
||||
|
||||
mAboutHome.swipeToPageOnLeft();
|
||||
mAboutHome.assertCurrentPage(PageType.TOP_SITES);
|
||||
mAboutHome.assertCurrentPage(PanelType.TOP_SITES);
|
||||
|
||||
mAboutHome.swipeToPageOnLeft();
|
||||
mAboutHome.assertCurrentPage(PageType.HISTORY);
|
||||
mAboutHome.assertCurrentPage(PanelType.HISTORY);
|
||||
|
||||
// Edge case.
|
||||
mAboutHome.swipeToPageOnLeft();
|
||||
mAboutHome.assertCurrentPage(PageType.HISTORY);
|
||||
mAboutHome.assertCurrentPage(PanelType.HISTORY);
|
||||
|
||||
mAboutHome.swipeToPageOnRight();
|
||||
mAboutHome.assertCurrentPage(PageType.TOP_SITES);
|
||||
mAboutHome.assertCurrentPage(PanelType.TOP_SITES);
|
||||
}
|
||||
|
||||
// TODO: bug 943706 - reimplement this old test code.
|
||||
|
@ -2,7 +2,7 @@ package org.mozilla.gecko.tests;
|
||||
|
||||
import static org.mozilla.gecko.tests.helpers.AssertionHelper.*;
|
||||
|
||||
import org.mozilla.gecko.tests.components.AboutHomeComponent.PageType;
|
||||
import org.mozilla.gecko.tests.components.AboutHomeComponent.PanelType;
|
||||
import org.mozilla.gecko.tests.helpers.*;
|
||||
|
||||
/**
|
||||
@ -15,7 +15,7 @@ public class testAboutHomeVisibility extends UITest {
|
||||
// Check initial state on about:home.
|
||||
mToolbar.assertTitle(StringHelper.ABOUT_HOME_TITLE);
|
||||
mAboutHome.assertVisible()
|
||||
.assertCurrentPage(PageType.TOP_SITES);
|
||||
.assertCurrentPage(PanelType.TOP_SITES);
|
||||
|
||||
// Go to blank 01.
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
@ -30,7 +30,7 @@ public class testAboutHomeVisibility extends UITest {
|
||||
// Enter editing mode, where the about:home UI should be visible.
|
||||
mToolbar.enterEditingMode();
|
||||
mAboutHome.assertVisible()
|
||||
.assertCurrentPage(PageType.TOP_SITES);
|
||||
.assertCurrentPage(PanelType.TOP_SITES);
|
||||
|
||||
// Dismiss editing mode, where the about:home UI should be gone.
|
||||
mToolbar.dismissEditingMode();
|
||||
@ -40,7 +40,7 @@ public class testAboutHomeVisibility extends UITest {
|
||||
NavigationHelper.enterAndLoadUrl(StringHelper.ABOUT_HOME_URL);
|
||||
mToolbar.assertTitle(StringHelper.ABOUT_HOME_TITLE);
|
||||
mAboutHome.assertVisible()
|
||||
.assertCurrentPage(PageType.TOP_SITES);
|
||||
.assertCurrentPage(PanelType.TOP_SITES);
|
||||
|
||||
// TODO: Type in a url and assert the go button is visible.
|
||||
}
|
||||
|
@ -2,13 +2,13 @@ package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
|
||||
public class testBookmarksPage extends AboutHomeTest {
|
||||
public class testBookmarksPanel extends AboutHomeTest {
|
||||
|
||||
protected int getTestType() {
|
||||
return TEST_MOCHITEST;
|
||||
}
|
||||
|
||||
public void testBookmarksPage() {
|
||||
public void testBookmarksPanel() {
|
||||
final String BOOKMARK_URL = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
|
||||
// Add a mobile bookmark
|
@ -125,23 +125,36 @@ var SelectionHandler = {
|
||||
this._ignoreSelectionChanges = true;
|
||||
// Check to see if the handles should be reversed.
|
||||
let isStartHandle = JSON.parse(aData).handleType == this.HANDLE_TYPE_START;
|
||||
let selectionReversed = this._updateCacheForSelection(isStartHandle);
|
||||
if (selectionReversed) {
|
||||
// Reverse the anchor and focus to correspond to the new start and end handles.
|
||||
let selection = this._getSelection();
|
||||
let anchorNode = selection.anchorNode;
|
||||
let anchorOffset = selection.anchorOffset;
|
||||
selection.collapse(selection.focusNode, selection.focusOffset);
|
||||
selection.extend(anchorNode, anchorOffset);
|
||||
|
||||
try {
|
||||
let selectionReversed = this._updateCacheForSelection(isStartHandle);
|
||||
if (selectionReversed) {
|
||||
// Reverse the anchor and focus to correspond to the new start and end handles.
|
||||
let selection = this._getSelection();
|
||||
let anchorNode = selection.anchorNode;
|
||||
let anchorOffset = selection.anchorOffset;
|
||||
selection.collapse(selection.focusNode, selection.focusOffset);
|
||||
selection.extend(anchorNode, anchorOffset);
|
||||
}
|
||||
} catch (e) {
|
||||
// User finished handle positioning with one end off the screen
|
||||
this._closeSelection();
|
||||
break;
|
||||
}
|
||||
|
||||
// Act on selectionChange notifications after handle movement ends
|
||||
this._ignoreSelectionChanges = false;
|
||||
this._positionHandles();
|
||||
|
||||
} else if (this._activeType == this.TYPE_CURSOR) {
|
||||
// Act on IMM composition notifications after caret movement ends
|
||||
this._ignoreCompositionChanges = false;
|
||||
this._positionHandles();
|
||||
|
||||
} else {
|
||||
Cu.reportError("Ignored \"TextSelection:Position\" message during invalid selection status");
|
||||
}
|
||||
this._positionHandles();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -743,8 +756,12 @@ var SelectionHandler = {
|
||||
// Returns true if the selection has been reversed. Takes optional aIsStartHandle
|
||||
// param to decide whether the selection has been reversed.
|
||||
_updateCacheForSelection: function sh_updateCacheForSelection(aIsStartHandle) {
|
||||
let selection = this._getSelection();
|
||||
let rects = selection.getRangeAt(0).getClientRects();
|
||||
let rects = this._getSelection().getRangeAt(0).getClientRects();
|
||||
if (!rects[0]) {
|
||||
// nsISelection object exists, but there's nothing actually selected
|
||||
throw "Failed to update cache for invalid selection";
|
||||
}
|
||||
|
||||
let start = { x: this._isRTL ? rects[0].right : rects[0].left, y: rects[0].bottom };
|
||||
let end = { x: this._isRTL ? rects[rects.length - 1].left : rects[rects.length - 1].right, y: rects[rects.length - 1].bottom };
|
||||
|
||||
|
@ -68,8 +68,6 @@ AddonUpdateService.prototype = {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
RecommendedSearchResults.search();
|
||||
}
|
||||
};
|
||||
|
||||
@ -113,71 +111,5 @@ UpdateCheckListener.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// RecommendedSearchResults fetches add-on data and saves it to a cache
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
var RecommendedSearchResults = {
|
||||
_getFile: function() {
|
||||
let dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
let file = dirService.get("ProfD", Ci.nsILocalFile);
|
||||
file.append("recommended-addons.json");
|
||||
return file;
|
||||
},
|
||||
|
||||
_writeFile: function (aFile, aData) {
|
||||
if (!aData)
|
||||
return;
|
||||
|
||||
// Asynchronously copy the data to the file.
|
||||
let array = new TextEncoder().encode(aData);
|
||||
OS.File.writeAtomic(aFile.path, array, { tmpPath: aFile.path + ".tmp" }).then(function onSuccess() {
|
||||
Services.obs.notifyObservers(null, "recommended-addons-cache-updated", "");
|
||||
});
|
||||
},
|
||||
|
||||
searchSucceeded: function(aAddons, aAddonCount, aTotalResults) {
|
||||
let self = this;
|
||||
|
||||
// Filter addons already installed
|
||||
AddonManager.getAllAddons(function(aAllAddons) {
|
||||
let addons = aAddons.filter(function(addon) {
|
||||
for (let i = 0; i < aAllAddons.length; i++)
|
||||
if (addon.id == aAllAddons[i].id)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
let json = {
|
||||
addons: []
|
||||
};
|
||||
|
||||
addons.forEach(function(aAddon) {
|
||||
json.addons.push({
|
||||
id: aAddon.id,
|
||||
name: aAddon.name,
|
||||
version: aAddon.version,
|
||||
learnmoreURL: aAddon.learnmoreURL,
|
||||
iconURL: aAddon.iconURL
|
||||
})
|
||||
});
|
||||
|
||||
let file = self._getFile();
|
||||
self._writeFile(file, JSON.stringify(json));
|
||||
});
|
||||
},
|
||||
|
||||
searchFailed: function searchFailed() { },
|
||||
|
||||
search: function() {
|
||||
const kAddonsMaxDisplay = 2;
|
||||
|
||||
if (AddonRepository.isSearching)
|
||||
AddonRepository.cancelSearch();
|
||||
AddonRepository.retrieveRecommendedAddons(kAddonsMaxDisplay, RecommendedSearchResults);
|
||||
}
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AddonUpdateService]);
|
||||
|
||||
|
@ -87,7 +87,6 @@
|
||||
@BINPATH@/res/drawable
|
||||
@BINPATH@/res/drawable-hdpi
|
||||
@BINPATH@/res/layout
|
||||
@BINPATH@/recommended-addons.json
|
||||
@BINPATH@/distribution/*
|
||||
|
||||
[browser]
|
||||
|
@ -137,7 +137,7 @@ let HomeBanner = {
|
||||
}
|
||||
};
|
||||
|
||||
function List(options) {
|
||||
function Panel(options) {
|
||||
if ("id" in options)
|
||||
this.id = options.id;
|
||||
|
||||
@ -145,11 +145,12 @@ function List(options) {
|
||||
this.title = options.title;
|
||||
}
|
||||
|
||||
function HomeLists() {
|
||||
function HomePanels() {
|
||||
// XXX: Not renaming this because it is going away in bug 958192
|
||||
this.PREF_KEY = "home_lists";
|
||||
|
||||
this._sharedPrefs = new SharedPreferences();
|
||||
this._lists = {};
|
||||
this._panels = {};
|
||||
|
||||
let prefValue = this._sharedPrefs.getCharPref(this.PREF_KEY);
|
||||
if (!prefValue) {
|
||||
@ -157,47 +158,47 @@ function HomeLists() {
|
||||
}
|
||||
|
||||
JSON.parse(prefValue).forEach(data => {
|
||||
let list = new List(data);
|
||||
this._lists[list.id] = list;
|
||||
let panel = new Panel(data);
|
||||
this._panels[panel.id] = panel;
|
||||
});
|
||||
}
|
||||
|
||||
HomeLists.prototype = {
|
||||
HomePanels.prototype = {
|
||||
add: function(options) {
|
||||
let list = new List(options);
|
||||
if (!list.id || !list.title) {
|
||||
throw "Can't create a home list without an id and title!";
|
||||
let panel = new Panel(options);
|
||||
if (!panel.id || !panel.title) {
|
||||
throw "Can't create a home panel without an id and title!";
|
||||
}
|
||||
|
||||
// Bail if the list already exists
|
||||
if (list.id in this._lists) {
|
||||
throw "List already exists: " + list.id;
|
||||
// Bail if the panel already exists
|
||||
if (panel.id in this._panels) {
|
||||
throw "Panel already exists: " + panel.id;
|
||||
}
|
||||
|
||||
this._lists[list.id] = list;
|
||||
this._panels[panel.id] = panel;
|
||||
this._updateSharedPref();
|
||||
|
||||
// Send a message to Java to update the home pager if it's currently showing
|
||||
sendMessageToJava({
|
||||
type: "HomeLists:Added",
|
||||
id: list.id,
|
||||
title: list.title
|
||||
type: "HomePanels:Added",
|
||||
id: panel.id,
|
||||
title: panel.title
|
||||
});
|
||||
},
|
||||
|
||||
remove: function(id) {
|
||||
delete this._lists[id];
|
||||
delete this._panels[id];
|
||||
this._updateSharedPref();
|
||||
},
|
||||
|
||||
// Set a shared pref so that Java can know about this list before Gecko is running
|
||||
// Set a shared pref so that Java can know about this panel before Gecko is running
|
||||
_updateSharedPref: function() {
|
||||
let lists = [];
|
||||
for (let id in this._lists) {
|
||||
let list = this._lists[id];
|
||||
lists.push({ id: list.id, title: list.title});
|
||||
let panels = [];
|
||||
for (let id in this._panels) {
|
||||
let panel = this._panels[id];
|
||||
panels.push({ id: panel.id, title: panel.title});
|
||||
}
|
||||
this._sharedPrefs.setCharPref(this.PREF_KEY, JSON.stringify(lists));
|
||||
this._sharedPrefs.setCharPref(this.PREF_KEY, JSON.stringify(panels));
|
||||
}
|
||||
|
||||
};
|
||||
@ -205,5 +206,5 @@ HomeLists.prototype = {
|
||||
// Public API
|
||||
this.Home = {
|
||||
banner: HomeBanner,
|
||||
lists: new HomeLists()
|
||||
panels: new HomePanels()
|
||||
}
|
||||
|
@ -205,18 +205,6 @@
|
||||
"content/base/test/test_object.html":"needs plugin support",
|
||||
"content/base/test/test_bug827160.html": "needs plugin support",
|
||||
|
||||
"content/base/test/csp/test_CSP_evalscript.html":"observer not working",
|
||||
"content/base/test/csp/test_CSP_evalscript_getCRMFRequest.html":"observer not working",
|
||||
"content/base/test/csp/test_CSP_frameancestors.html":"observer not working",
|
||||
"content/base/test/csp/test_CSP.html":"observer not working",
|
||||
"content/base/test/csp/test_bug836922_npolicies.html":"observer not working",
|
||||
"content/base/test/csp/test_bug886164.html":"observer not working",
|
||||
"content/base/test/csp/test_CSP_bug916446.html":"observer not working",
|
||||
"content/base/test/csp/test_CSP_bug909029.html":"observer not working",
|
||||
"content/base/test/csp/test_policyuri_regression_from_multipolicy.html":"observer not working",
|
||||
"content/base/test/csp/test_nonce_source.html":"observer not working",
|
||||
"content/base/test/csp/test_CSP_bug941404.html":"observer not working",
|
||||
|
||||
"content/base/test/test_CrossSiteXHR_origin.html":"https not working, bug 907770",
|
||||
"content/base/test/test_plugin_freezing.html":"",
|
||||
"content/base/test/test_bug466409.html":"",
|
||||
@ -229,7 +217,6 @@
|
||||
"content/base/test/test_bug717511.html":"",
|
||||
"content/base/test/test_copypaste.xhtml":"bug 904183",
|
||||
"content/base/test/test_copypaste.xul":"bug 904183",
|
||||
"content/base/test/csp/test_csp_redirects.html":"",
|
||||
"content/base/test/test_fileapi_slice.html":"",
|
||||
"content/base/test/test_mixed_content_blocker.html":"",
|
||||
"content/base/test/test_mixed_content_blocker_bug803225.html":"",
|
||||
@ -255,7 +242,6 @@
|
||||
"content/svg/content/test/test_text_selection.html":"Mouse selection not workin on b2g",
|
||||
"content/svg/content/test/test_SVGAnimatedImageSMILDisabled.html":"",
|
||||
"content/xml/document/test/test_bug392338.html":"",
|
||||
"content/base/test/csp/test_bothCSPheaders.html":"",
|
||||
"content/base/test/test_bug383430.html":"",
|
||||
"content/base/test/test_bug422403-2.xhtml":"",
|
||||
"content/base/test/test_bug424359-1.html":"",
|
||||
@ -433,6 +419,8 @@
|
||||
"layout/style/test/test_visited_reftests.html":"bug 870262, :visited support",
|
||||
|
||||
"Harness_sanity/test_sanityEventUtils.html": "bug 688052",
|
||||
"Harness_sanity/test_sanitySimpletest.html": "bug 688052"
|
||||
"Harness_sanity/test_sanitySimpletest.html": "bug 688052",
|
||||
|
||||
"content/base/test/csp/test_CSP_evalscript_getCRMFRequest.html":"no window.crypto support in multiprocess"
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,13 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
|
||||
}
|
||||
break;
|
||||
|
||||
case "http-on-modify-request":
|
||||
if (aSubject instanceof Ci.nsIChannel) {
|
||||
let uri = aSubject.URI.spec;
|
||||
this._sendAsyncMessage("specialpowers-http-notify-request", { uri: uri });
|
||||
}
|
||||
break;
|
||||
|
||||
case "xpcom-shutdown":
|
||||
this.uninit();
|
||||
break;
|
||||
@ -99,6 +106,7 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
|
||||
var obs = Services.obs;
|
||||
obs.addObserver(this, "xpcom-shutdown", false);
|
||||
obs.addObserver(this, "chrome-document-global-created", false);
|
||||
obs.addObserver(this, "http-on-modify-request", false);
|
||||
|
||||
if (messageManager) {
|
||||
this._messageManager = messageManager;
|
||||
@ -110,6 +118,7 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
|
||||
{
|
||||
var obs = Services.obs;
|
||||
obs.removeObserver(this, "chrome-document-global-created");
|
||||
obs.removeObserver(this, "http-on-modify-request");
|
||||
this._removeProcessCrashObservers();
|
||||
};
|
||||
|
||||
|
@ -973,7 +973,27 @@ SpecialPowersAPI.prototype = {
|
||||
return this._sendSyncMessage("SPWebAppService", message);
|
||||
},
|
||||
|
||||
_proxiedObservers: {
|
||||
"specialpowers-http-notify-request": function(aMessage) {
|
||||
let uri = aMessage.json.uri;
|
||||
Services.obs.notifyObservers(null, "specialpowers-http-notify-request", uri);
|
||||
},
|
||||
},
|
||||
|
||||
_addObserverProxy: function(notification) {
|
||||
if (notification in this._proxiedObservers) {
|
||||
this._addMessageListener(notification, this._proxiedObservers[notification]);
|
||||
}
|
||||
},
|
||||
|
||||
_removeObserverProxy: function(notification) {
|
||||
if (notification in this._proxiedObservers) {
|
||||
this._removeMessageListener(notification, this._proxiedObservers[notification]);
|
||||
}
|
||||
},
|
||||
|
||||
addObserver: function(obs, notification, weak) {
|
||||
this._addObserverProxy(notification);
|
||||
if (typeof obs == 'object' && obs.observe.name != 'SpecialPowersCallbackWrapper')
|
||||
obs.observe = wrapCallback(obs.observe);
|
||||
var obsvc = Cc['@mozilla.org/observer-service;1']
|
||||
@ -981,6 +1001,7 @@ SpecialPowersAPI.prototype = {
|
||||
obsvc.addObserver(obs, notification, weak);
|
||||
},
|
||||
removeObserver: function(obs, notification) {
|
||||
this._removeObserverProxy(notification);
|
||||
var obsvc = Cc['@mozilla.org/observer-service;1']
|
||||
.getService(Ci.nsIObserverService);
|
||||
obsvc.removeObserver(obs, notification);
|
||||
|
@ -3351,6 +3351,126 @@
|
||||
"n_values": 101,
|
||||
"description": "Session restore: Number of times the tab state cache has been cleared during a session divided by number of total accesses during the session (percentage)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_EXTRACTING_STATISTICS_DURATION_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Session restore: Duration of the off main thread statistics extraction mechanism (ms)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TOTAL_OPEN_WINDOWS_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "Session restore: The subset of sessionrestore.js representing open windows (total size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TOTAL_CLOSED_WINDOWS_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "Session restore: The subset of sessionrestore.js representing closed windows (total size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TOTAL_CLOSED_TABS_IN_OPEN_WINDOWS_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "Sessionrestore: The subset of sesionstore.js representing closed tabs in open windows (total size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TOTAL_COOKIES_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with cookies (total size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TOTAL_DOM_STORAGE_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with DOM storage (total size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TOTAL_FORMDATA_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with storing form data (total size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TOTAL_HISTORY_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with storing history (total size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_TOTAL_POSTDATA_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with storing POST data (total size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_INDIVIDUAL_OPEN_WINDOWS_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "5000000",
|
||||
"n_buckets": 30,
|
||||
"description": "Session restore: The subset of sessionrestore.js representing open windows (item size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_INDIVIDUAL_CLOSED_WINDOWS_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "5000000",
|
||||
"n_buckets": 30,
|
||||
"description": "Session restore: The subset of sessionrestore.js representing closed windows (item size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_INDIVIDUAL_CLOSED_TABS_IN_OPEN_WINDOWS_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "5000000",
|
||||
"n_buckets": 30,
|
||||
"description": "Sessionrestore: The subset of sesionstore.js representing closed tabs in open windows (item size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_INDIVIDUAL_COOKIES_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "50000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with cookies (item size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_INDIVIDUAL_DOM_STORAGE_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "5000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with DOM storage (item size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_INDIVIDUAL_FORMDATA_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "5000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with storing form data (item size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_INDIVIDUAL_HISTORY_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "5000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with storing history (item size, in bytes)"
|
||||
},
|
||||
"FX_SESSION_RESTORE_INDIVIDUAL_POSTDATA_SIZE_BYTES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "5000000",
|
||||
"n_buckets": 30,
|
||||
"description": "The subset of sessionstore.js dealing with storing history POST data (item size, in bytes)"
|
||||
},
|
||||
"INNERWINDOWS_WITH_MUTATION_LISTENERS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "boolean",
|
||||
|
@ -337,7 +337,6 @@ DIST_FILES += \
|
||||
chrome.manifest \
|
||||
update.locale \
|
||||
removed-files \
|
||||
recommended-addons.json \
|
||||
distribution \
|
||||
$(NULL)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user