зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound.
This commit is contained in:
Коммит
0b9a3405dc
|
@ -381,6 +381,7 @@ pref("dom.ipc.browser_frames.oop_by_default", false);
|
|||
// Temporary permission hack for WebSMS
|
||||
pref("dom.sms.enabled", true);
|
||||
pref("dom.sms.strict7BitEncoding", false); // Disabled by default.
|
||||
pref("dom.sms.requestStatusReport", false); // Disabled by default.
|
||||
|
||||
// Temporary permission hack for WebContacts
|
||||
pref("dom.mozContacts.enabled", true);
|
||||
|
|
|
@ -164,6 +164,11 @@ SettingsListener.observe('language.current', 'en-US', function(value) {
|
|||
function(value) {
|
||||
Services.prefs.setBoolPref('dom.sms.strict7BitEncoding', value);
|
||||
});
|
||||
|
||||
SettingsListener.observe('ril.sms.requestStatusReport.enabled', false,
|
||||
function(value) {
|
||||
Services.prefs.setBoolPref('dom.sms.requestStatusReport', value);
|
||||
});
|
||||
})();
|
||||
|
||||
//=================== DeviceInfo ====================
|
||||
|
|
|
@ -967,8 +967,8 @@ let SessionStoreInternal = {
|
|||
this.onTabRemove(aWindow, tabbrowser.tabs[i], true);
|
||||
}
|
||||
|
||||
// cache the window state until the window is completely gone
|
||||
aWindow.__SS_dyingCache = winData;
|
||||
// Cache the window state until it is completely gone.
|
||||
DyingWindowCache.set(aWindow, winData);
|
||||
|
||||
delete aWindow.__SSi;
|
||||
},
|
||||
|
@ -1057,9 +1057,8 @@ let SessionStoreInternal = {
|
|||
this._lastSessionState = null;
|
||||
let openWindows = {};
|
||||
this._forEachBrowserWindow(function(aWindow) {
|
||||
Array.forEach(aWindow.gBrowser.tabs, function(aTab) {
|
||||
delete aTab.linkedBrowser.__SS_data;
|
||||
delete aTab.linkedBrowser.__SS_tabStillLoading;
|
||||
Array.forEach(aWindow.gBrowser.tabs, aTab => {
|
||||
RestoringTabsData.remove(aTab.linkedBrowser);
|
||||
delete aTab.linkedBrowser.__SS_formDataSaved;
|
||||
delete aTab.linkedBrowser.__SS_hostSchemeData;
|
||||
if (aTab.linkedBrowser.__SS_restoreState)
|
||||
|
@ -1250,8 +1249,7 @@ let SessionStoreInternal = {
|
|||
let mm = browser.messageManager;
|
||||
MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));
|
||||
|
||||
delete browser.__SS_data;
|
||||
delete browser.__SS_tabStillLoading;
|
||||
RestoringTabsData.remove(aTab.linkedBrowser);
|
||||
delete browser.__SS_formDataSaved;
|
||||
delete browser.__SS_hostSchemeData;
|
||||
|
||||
|
@ -1328,8 +1326,7 @@ let SessionStoreInternal = {
|
|||
return;
|
||||
}
|
||||
|
||||
delete aBrowser.__SS_data;
|
||||
delete aBrowser.__SS_tabStillLoading;
|
||||
RestoringTabsData.remove(aBrowser);
|
||||
delete aBrowser.__SS_formDataSaved;
|
||||
this.saveStateDelayed(aWindow);
|
||||
|
||||
|
@ -1448,12 +1445,16 @@ let SessionStoreInternal = {
|
|||
},
|
||||
|
||||
getWindowState: function ssi_getWindowState(aWindow) {
|
||||
if (!aWindow.__SSi && !aWindow.__SS_dyingCache)
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
if ("__SSi" in aWindow) {
|
||||
return this._toJSONString(this._getWindowState(aWindow));
|
||||
}
|
||||
|
||||
if (!aWindow.__SSi)
|
||||
return this._toJSONString({ windows: [aWindow.__SS_dyingCache] });
|
||||
return this._toJSONString(this._getWindowState(aWindow));
|
||||
if (DyingWindowCache.has(aWindow)) {
|
||||
let data = DyingWindowCache.get(aWindow);
|
||||
return this._toJSONString({ windows: [data] });
|
||||
}
|
||||
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
|
||||
setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite) {
|
||||
|
@ -1509,22 +1510,28 @@ let SessionStoreInternal = {
|
|||
},
|
||||
|
||||
getClosedTabCount: function ssi_getClosedTabCount(aWindow) {
|
||||
if (!aWindow.__SSi && aWindow.__SS_dyingCache)
|
||||
return aWindow.__SS_dyingCache._closedTabs.length;
|
||||
if (!aWindow.__SSi)
|
||||
// XXXzeniko shouldn't we throw here?
|
||||
return 0; // not a browser window, or not otherwise tracked by SS.
|
||||
if ("__SSi" in aWindow) {
|
||||
return this._windows[aWindow.__SSi]._closedTabs.length;
|
||||
}
|
||||
|
||||
return this._windows[aWindow.__SSi]._closedTabs.length;
|
||||
if (DyingWindowCache.has(aWindow)) {
|
||||
return DyingWindowCache.get(aWindow)._closedTabs.length;
|
||||
}
|
||||
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
|
||||
getClosedTabData: function ssi_getClosedTabDataAt(aWindow) {
|
||||
if (!aWindow.__SSi && !aWindow.__SS_dyingCache)
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
if ("__SSi" in aWindow) {
|
||||
return this._toJSONString(this._windows[aWindow.__SSi]._closedTabs);
|
||||
}
|
||||
|
||||
if (!aWindow.__SSi)
|
||||
return this._toJSONString(aWindow.__SS_dyingCache._closedTabs);
|
||||
return this._toJSONString(this._windows[aWindow.__SSi]._closedTabs);
|
||||
if (DyingWindowCache.has(aWindow)) {
|
||||
let data = DyingWindowCache.get(aWindow);
|
||||
return this._toJSONString(data._closedTabs);
|
||||
}
|
||||
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
|
||||
undoCloseTab: function ssi_undoCloseTab(aWindow, aIndex) {
|
||||
|
@ -1604,14 +1611,16 @@ let SessionStoreInternal = {
|
|||
},
|
||||
|
||||
getWindowValue: function ssi_getWindowValue(aWindow, aKey) {
|
||||
if (aWindow.__SSi) {
|
||||
if ("__SSi" in aWindow) {
|
||||
var data = this._windows[aWindow.__SSi].extData || {};
|
||||
return data[aKey] || "";
|
||||
}
|
||||
if (aWindow.__SS_dyingCache) {
|
||||
data = aWindow.__SS_dyingCache.extData || {};
|
||||
|
||||
if (DyingWindowCache.has(aWindow)) {
|
||||
let data = DyingWindowCache.get(aWindow).extData || {};
|
||||
return data[aKey] || "";
|
||||
}
|
||||
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
},
|
||||
|
||||
|
@ -1638,10 +1647,13 @@ let SessionStoreInternal = {
|
|||
let data = {};
|
||||
if (aTab.__SS_extdata) {
|
||||
data = aTab.__SS_extdata;
|
||||
}
|
||||
else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
|
||||
// If the tab hasn't been fully restored, get the data from the to-be-restored data
|
||||
data = aTab.linkedBrowser.__SS_data.extData;
|
||||
} else {
|
||||
let tabData = RestoringTabsData.get(aTab.linkedBrowser);
|
||||
if (tabData && "extData" in tabData) {
|
||||
// If the tab hasn't been fully restored yet,
|
||||
// get the data from the to-be-restored data.
|
||||
data = tabData.extData;
|
||||
}
|
||||
}
|
||||
return data[aKey] || "";
|
||||
},
|
||||
|
@ -1652,13 +1664,14 @@ let SessionStoreInternal = {
|
|||
let saveTo;
|
||||
if (aTab.__SS_extdata) {
|
||||
saveTo = aTab.__SS_extdata;
|
||||
}
|
||||
else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
|
||||
saveTo = aTab.linkedBrowser.__SS_data.extData;
|
||||
}
|
||||
else {
|
||||
aTab.__SS_extdata = {};
|
||||
saveTo = aTab.__SS_extdata;
|
||||
} else {
|
||||
let tabData = RestoringTabsData.get(aTab.linkedBrowser);
|
||||
if (tabData && "extData" in tabData) {
|
||||
saveTo = tabData.extData;
|
||||
} else {
|
||||
aTab.__SS_extdata = {};
|
||||
saveTo = aTab.__SS_extdata;
|
||||
}
|
||||
}
|
||||
saveTo[aKey] = aStringValue;
|
||||
this.saveStateDelayed(aTab.ownerDocument.defaultView);
|
||||
|
@ -1666,14 +1679,16 @@ let SessionStoreInternal = {
|
|||
|
||||
deleteTabValue: function ssi_deleteTabValue(aTab, aKey) {
|
||||
// We want to make sure that if data is accessed early, we attempt to delete
|
||||
// that data from __SS_data as well. Otherwise we'll throw in cases where
|
||||
// data can be set or read.
|
||||
// that data from to-be-restored data as well. Otherwise we'll throw in
|
||||
// cases where data can be set or read.
|
||||
let deleteFrom;
|
||||
if (aTab.__SS_extdata) {
|
||||
deleteFrom = aTab.__SS_extdata;
|
||||
}
|
||||
else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
|
||||
deleteFrom = aTab.linkedBrowser.__SS_data.extData;
|
||||
} else {
|
||||
let tabData = RestoringTabsData.get(aTab.linkedBrowser);
|
||||
if (tabData && "extData" in tabData) {
|
||||
deleteFrom = tabData.extData;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteFrom && deleteFrom[aKey])
|
||||
|
@ -1887,9 +1902,9 @@ let SessionStoreInternal = {
|
|||
if (!browser || !browser.currentURI)
|
||||
// can happen when calling this function right after .addTab()
|
||||
return tabData;
|
||||
else if (browser.__SS_data && browser.__SS_tabStillLoading) {
|
||||
else if (RestoringTabsData.has(browser)) {
|
||||
// use the data to be restored when the tab hasn't been completely loaded
|
||||
tabData = browser.__SS_data;
|
||||
tabData = RestoringTabsData.get(browser);
|
||||
if (aTab.pinned)
|
||||
tabData.pinned = true;
|
||||
else
|
||||
|
@ -1912,16 +1927,7 @@ let SessionStoreInternal = {
|
|||
}
|
||||
catch (ex) { } // this could happen if we catch a tab during (de)initialization
|
||||
|
||||
// XXXzeniko anchor navigation doesn't reset __SS_data, so we could reuse
|
||||
// data even when we shouldn't (e.g. Back, different anchor)
|
||||
if (history && browser.__SS_data &&
|
||||
browser.__SS_data.entries[history.index] &&
|
||||
browser.__SS_data.entries[history.index].url == browser.currentURI.spec &&
|
||||
history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
|
||||
tabData = browser.__SS_data;
|
||||
tabData.index = history.index + 1;
|
||||
}
|
||||
else if (history && history.count > 0) {
|
||||
if (history && history.count > 0) {
|
||||
browser.__SS_hostSchemeData = [];
|
||||
try {
|
||||
for (var j = 0; j < history.count; j++) {
|
||||
|
@ -1950,10 +1956,6 @@ let SessionStoreInternal = {
|
|||
}
|
||||
}
|
||||
tabData.index = history.index + 1;
|
||||
|
||||
// make sure not to cache privacy sensitive data which shouldn't get out
|
||||
if (!aFullData)
|
||||
browser.__SS_data = tabData;
|
||||
}
|
||||
else if (browser.currentURI.spec != "about:blank" ||
|
||||
browser.contentDocument.body.hasChildNodes()) {
|
||||
|
@ -2180,7 +2182,7 @@ let SessionStoreInternal = {
|
|||
_updateTextAndScrollDataForTab:
|
||||
function ssi_updateTextAndScrollDataForTab(aWindow, aBrowser, aTabData, aFullData) {
|
||||
// we shouldn't update data for incompletely initialized tabs
|
||||
if (aBrowser.__SS_data && aBrowser.__SS_tabStillLoading)
|
||||
if (RestoringTabsData.has(aBrowser))
|
||||
return;
|
||||
|
||||
var tabIndex = (aTabData.index || aTabData.entries.length) - 1;
|
||||
|
@ -2984,11 +2986,9 @@ let SessionStoreInternal = {
|
|||
for (let name in tabData.attributes)
|
||||
this.xulAttributes[name] = true;
|
||||
|
||||
browser.__SS_tabStillLoading = true;
|
||||
|
||||
// keep the data around to prevent dataloss in case
|
||||
// a tab gets closed before it's been properly restored
|
||||
browser.__SS_data = tabData;
|
||||
RestoringTabsData.set(browser, tabData);
|
||||
browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
|
||||
browser.setAttribute("pending", "true");
|
||||
tab.setAttribute("pending", "true");
|
||||
|
@ -3157,7 +3157,7 @@ let SessionStoreInternal = {
|
|||
restoreTab: function ssi_restoreTab(aTab) {
|
||||
let window = aTab.ownerDocument.defaultView;
|
||||
let browser = aTab.linkedBrowser;
|
||||
let tabData = browser.__SS_data;
|
||||
let tabData = RestoringTabsData.get(browser);
|
||||
|
||||
// There are cases within where we haven't actually started a load. In that
|
||||
// that case we'll reset state changes we made and return false to the caller
|
||||
|
@ -4095,15 +4095,13 @@ let SessionStoreInternal = {
|
|||
/**
|
||||
* Determine if we can restore history into this tab.
|
||||
* This will be false when a tab has been removed (usually between
|
||||
* restoreHistoryPrecursor && restoreHistory) or if the tab is still marked
|
||||
* as loading.
|
||||
* restoreHistoryPrecursor && restoreHistory).
|
||||
*
|
||||
* @param aTab
|
||||
* @returns boolean
|
||||
*/
|
||||
_canRestoreTabHistory: function ssi_canRestoreTabHistory(aTab) {
|
||||
return aTab.parentNode && aTab.linkedBrowser &&
|
||||
aTab.linkedBrowser.__SS_tabStillLoading;
|
||||
return aTab.parentNode && aTab.linkedBrowser;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -4576,6 +4574,54 @@ let TabRestoreQueue = {
|
|||
}
|
||||
};
|
||||
|
||||
// A map storing tabData belonging to xul:browsers of tabs. This will hold data
|
||||
// while a tab is restoring (i.e. loading). Because we can't query or use the
|
||||
// incomplete state of a loading tab we'll use data stored in the map if browser
|
||||
// state is collected while a tab is still restoring or if it's closed before
|
||||
// having restored fully.
|
||||
let RestoringTabsData = {
|
||||
_data: new WeakMap(),
|
||||
|
||||
has: function (browser) {
|
||||
return this._data.has(browser);
|
||||
},
|
||||
|
||||
get: function (browser) {
|
||||
return this._data.get(browser);
|
||||
},
|
||||
|
||||
set: function (browser, data) {
|
||||
this._data.set(browser, data);
|
||||
},
|
||||
|
||||
remove: function (browser) {
|
||||
this._data.delete(browser);
|
||||
}
|
||||
};
|
||||
|
||||
// A map storing a closed window's state data until it goes aways (is GC'ed).
|
||||
// This ensures that API clients can still read (but not write) states of
|
||||
// windows they still hold a reference to but we don't.
|
||||
let DyingWindowCache = {
|
||||
_data: new WeakMap(),
|
||||
|
||||
has: function (window) {
|
||||
return this._data.has(window);
|
||||
},
|
||||
|
||||
get: function (window) {
|
||||
return this._data.get(window);
|
||||
},
|
||||
|
||||
set: function (window, data) {
|
||||
this._data.set(window, data);
|
||||
},
|
||||
|
||||
remove: function (window) {
|
||||
this._data.delete(window);
|
||||
}
|
||||
};
|
||||
|
||||
// This is used to help meter the number of restoring tabs. This is the control
|
||||
// point for telling the next tab to restore. It gets attached to each gBrowser
|
||||
// via gBrowser.addTabsProgressListener
|
||||
|
|
|
@ -22,6 +22,7 @@ DISABLED_XPCSHELL_TESTS = \
|
|||
MOCHITEST_BROWSER_FILES = \
|
||||
head.js \
|
||||
browser_capabilities.js \
|
||||
browser_dying_cache.js \
|
||||
browser_form_restore_events.js \
|
||||
browser_form_restore_events_sample.html \
|
||||
browser_formdata_format.js \
|
||||
|
|
|
@ -18,9 +18,6 @@ function test() {
|
|||
// Undo pinning
|
||||
gBrowser.unpinTab(tab1);
|
||||
|
||||
is(tab1.linkedBrowser.__SS_tabStillLoading, true,
|
||||
"_tabStillLoading should be true.");
|
||||
|
||||
// Close and restore tab
|
||||
gBrowser.removeTab(tab1);
|
||||
let savedState = JSON.parse(ss.getClosedTabData(window))[0].state;
|
||||
|
|
|
@ -39,7 +39,6 @@ function firstOnLoad(aEvent) {
|
|||
ss.getBrowserState();
|
||||
|
||||
is(gBrowser.tabs[1], tab, "newly created tab should exist by now");
|
||||
ok(tab.linkedBrowser.__SS_data, "newly created tab should be in save state");
|
||||
|
||||
tab.linkedBrowser.loadURI(URI_TO_LOAD);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that after closing a window we keep its state data around
|
||||
* as long as something keeps a reference to it. It should only be possible to
|
||||
* read data after closing - writing should fail.
|
||||
*/
|
||||
|
||||
function runTests() {
|
||||
// Open a new window.
|
||||
let win = OpenBrowserWindow();
|
||||
yield whenWindowLoaded(win);
|
||||
|
||||
// Load some URL in the current tab.
|
||||
win.gBrowser.selectedBrowser.loadURI("about:robots");
|
||||
yield whenBrowserLoaded(win.gBrowser.selectedBrowser);
|
||||
|
||||
// Open a second tab and close the first one.
|
||||
let tab = win.gBrowser.addTab("about:mozilla");
|
||||
yield whenBrowserLoaded(tab.linkedBrowser);
|
||||
win.gBrowser.removeTab(win.gBrowser.tabs[0]);
|
||||
|
||||
// Make sure our window is still tracked by sessionstore
|
||||
// and the window state is as expected.
|
||||
ok("__SSi" in win, "window is being tracked by sessionstore");
|
||||
ss.setWindowValue(win, "foo", "bar");
|
||||
checkWindowState(win);
|
||||
|
||||
let state = ss.getWindowState(win);
|
||||
let closedTabData = ss.getClosedTabData(win);
|
||||
|
||||
// Close our window and wait a tick.
|
||||
whenWindowClosed(win);
|
||||
yield win.close();
|
||||
|
||||
// SessionStore should no longer track our window
|
||||
// but it should still report the same state.
|
||||
ok(!("__SSi" in win), "sessionstore does no longer track our window");
|
||||
checkWindowState(win);
|
||||
|
||||
// Make sure we're not allowed to modify state data.
|
||||
ok(shouldThrow(() => ss.setWindowState(win, {})),
|
||||
"we're not allowed to modify state data anymore");
|
||||
ok(shouldThrow(() => ss.setWindowValue(win, "foo", "baz")),
|
||||
"we're not allowed to modify state data anymore");
|
||||
}
|
||||
|
||||
function checkWindowState(window) {
|
||||
let {windows: [{tabs}]} = JSON.parse(ss.getWindowState(window));
|
||||
is(tabs.length, 1, "the window has a single tab");
|
||||
is(tabs[0].entries[0].url, "about:mozilla", "the tab is about:mozilla");
|
||||
|
||||
is(ss.getClosedTabCount(window), 1, "the window has one closed tab");
|
||||
let [{state: {entries: [{url}]}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(url, "about:robots", "the closed tab is about:robots");
|
||||
|
||||
is(ss.getWindowValue(window, "foo"), "bar", "correct extData value");
|
||||
}
|
||||
|
||||
function shouldThrow(f) {
|
||||
try {
|
||||
f();
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function whenWindowClosed(window) {
|
||||
window.addEventListener("SSWindowClosing", function onClosing() {
|
||||
window.removeEventListener("SSWindowClosing", onClosing);
|
||||
executeSoon(next);
|
||||
});
|
||||
}
|
|
@ -187,14 +187,14 @@ function waitForSaveState(aSaveStateCallback) {
|
|||
Services.obs.addObserver(observer, topic, false);
|
||||
};
|
||||
|
||||
function whenBrowserLoaded(aBrowser, aCallback) {
|
||||
function whenBrowserLoaded(aBrowser, aCallback = next) {
|
||||
aBrowser.addEventListener("load", function onLoad() {
|
||||
aBrowser.removeEventListener("load", onLoad, true);
|
||||
executeSoon(aCallback);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function whenWindowLoaded(aWindow, aCallback) {
|
||||
function whenWindowLoaded(aWindow, aCallback = next) {
|
||||
aWindow.addEventListener("load", function windowLoadListener() {
|
||||
aWindow.removeEventListener("load", windowLoadListener, false);
|
||||
executeSoon(function executeWhenWindowLoaded() {
|
||||
|
|
|
@ -21,6 +21,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
|
|||
"resource:///modules/devtools/gDevTools.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
|
||||
"resource:///modules/devtools/gDevTools.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
|
||||
"resource:///modules/devtools/AppCacheUtils.jsm");
|
||||
|
||||
/* CmdAddon ---------------------------------------------------------------- */
|
||||
|
||||
|
@ -2064,3 +2066,256 @@ XPCOMUtils.defineLazyModuleGetter(this, "devtools",
|
|||
target.once("navigate", fireChange);
|
||||
}
|
||||
}(this));
|
||||
|
||||
/* CmdAppCache ------------------------------------------------------- */
|
||||
|
||||
(function(module) {
|
||||
/**
|
||||
* 'appcache' command
|
||||
*/
|
||||
|
||||
gcli.addCommand({
|
||||
name: 'appcache',
|
||||
description: gcli.lookup('appCacheDesc')
|
||||
});
|
||||
|
||||
gcli.addConverter({
|
||||
from: "appcacheerrors",
|
||||
to: "view",
|
||||
exec: function([errors, manifestURI], context) {
|
||||
if (errors.length == 0) {
|
||||
return context.createView({
|
||||
html: "<span>" + gcli.lookup("appCacheValidatedSuccessfully") + "</span>"
|
||||
});
|
||||
}
|
||||
|
||||
let appcacheValidateHtml =
|
||||
"<h4>Manifest URI: ${manifestURI}</h4>" +
|
||||
"<ol>" +
|
||||
" <li foreach='error in ${errors}'>" +
|
||||
" ${error.msg}" +
|
||||
" </li>" +
|
||||
"</ol>";
|
||||
|
||||
return context.createView({
|
||||
html: "<div>" + appcacheValidateHtml + "</div>",
|
||||
data: {
|
||||
errors: errors,
|
||||
manifestURI: manifestURI
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
gcli.addCommand({
|
||||
name: 'appcache validate',
|
||||
description: gcli.lookup('appCacheValidateDesc'),
|
||||
manual: gcli.lookup('appCacheValidateManual'),
|
||||
returnType: 'appcacheerrors',
|
||||
params: [{
|
||||
group: "options",
|
||||
params: [
|
||||
{
|
||||
type: "string",
|
||||
name: "uri",
|
||||
description: gcli.lookup("appCacheValidateUriDesc"),
|
||||
defaultValue: null,
|
||||
}
|
||||
]
|
||||
}],
|
||||
exec: function(args, context) {
|
||||
let utils;
|
||||
let promise = context.createPromise();
|
||||
|
||||
if (args.uri) {
|
||||
utils = new AppCacheUtils(args.uri);
|
||||
} else {
|
||||
let doc = context.environment.contentDocument;
|
||||
utils = new AppCacheUtils(doc);
|
||||
}
|
||||
|
||||
utils.validateManifest().then(function(errors) {
|
||||
promise.resolve([errors, utils.manifestURI || "-"]);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
});
|
||||
|
||||
gcli.addCommand({
|
||||
name: 'appcache clear',
|
||||
description: gcli.lookup('appCacheClearDesc'),
|
||||
manual: gcli.lookup('appCacheClearManual'),
|
||||
exec: function(args, context) {
|
||||
let utils = new AppCacheUtils(args.uri);
|
||||
utils.clearAll();
|
||||
|
||||
return gcli.lookup("appCacheClearCleared");
|
||||
}
|
||||
});
|
||||
|
||||
gcli.addConverter({
|
||||
from: "appcacheentries",
|
||||
to: "view",
|
||||
exec: function(entries, context) {
|
||||
if (!entries) {
|
||||
return context.createView({
|
||||
html: "<span>" + gcli.lookup("appCacheManifestContainsErrors") + "</span>"
|
||||
});
|
||||
}
|
||||
|
||||
if (entries.length == 0) {
|
||||
return context.createView({
|
||||
html: "<span>" + gcli.lookup("appCacheNoResults") + "</span>"
|
||||
});
|
||||
}
|
||||
|
||||
let appcacheListEntries = "" +
|
||||
"<ul class='gcli-appcache-list'>" +
|
||||
" <li foreach='entry in ${entries}'>" +
|
||||
" <table class='gcli-appcache-detail'>" +
|
||||
" <tr>" +
|
||||
" <td>" + gcli.lookup("appCacheListKey") + "</td>" +
|
||||
" <td>${entry.key}</td>" +
|
||||
" </tr>" +
|
||||
" <tr>" +
|
||||
" <td>" + gcli.lookup("appCacheListFetchCount") + "</td>" +
|
||||
" <td>${entry.fetchCount}</td>" +
|
||||
" </tr>" +
|
||||
" <tr>" +
|
||||
" <td>" + gcli.lookup("appCacheListLastFetched") + "</td>" +
|
||||
" <td>${entry.lastFetched}</td>" +
|
||||
" </tr>" +
|
||||
" <tr>" +
|
||||
" <td>" + gcli.lookup("appCacheListLastModified") + "</td>" +
|
||||
" <td>${entry.lastModified}</td>" +
|
||||
" </tr>" +
|
||||
" <tr>" +
|
||||
" <td>" + gcli.lookup("appCacheListExpirationTime") + "</td>" +
|
||||
" <td>${entry.expirationTime}</td>" +
|
||||
" </tr>" +
|
||||
" <tr>" +
|
||||
" <td>" + gcli.lookup("appCacheListDataSize") + "</td>" +
|
||||
" <td>${entry.dataSize}</td>" +
|
||||
" </tr>" +
|
||||
" <tr>" +
|
||||
" <td>" + gcli.lookup("appCacheListDeviceID") + "</td>" +
|
||||
" <td>${entry.deviceID} <span class='gcli-out-shortcut' " +
|
||||
"onclick='${onclick}' ondblclick='${ondblclick}' " +
|
||||
"data-command='appcache viewentry ${entry.key}'" +
|
||||
">" + gcli.lookup("appCacheListViewEntry") + "</span>" +
|
||||
" </td>" +
|
||||
" </tr>" +
|
||||
" </table>" +
|
||||
" </li>" +
|
||||
"</ul>";
|
||||
|
||||
return context.createView({
|
||||
html: appcacheListEntries,
|
||||
data: {
|
||||
entries: entries,
|
||||
onclick: createUpdateHandler(context),
|
||||
ondblclick: createExecuteHandler(context),
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
gcli.addCommand({
|
||||
name: 'appcache list',
|
||||
description: gcli.lookup('appCacheListDesc'),
|
||||
manual: gcli.lookup('appCacheListManual'),
|
||||
returnType: "appcacheentries",
|
||||
params: [{
|
||||
group: "options",
|
||||
params: [
|
||||
{
|
||||
type: "string",
|
||||
name: "search",
|
||||
description: gcli.lookup("appCacheListSearchDesc"),
|
||||
defaultValue: null,
|
||||
},
|
||||
]
|
||||
}],
|
||||
exec: function(args, context) {
|
||||
let doc = context.environment.contentDocument;
|
||||
let utils = new AppCacheUtils();
|
||||
|
||||
let entries = utils.listEntries(args.search);
|
||||
return entries;
|
||||
}
|
||||
});
|
||||
|
||||
gcli.addCommand({
|
||||
name: 'appcache viewentry',
|
||||
description: gcli.lookup('appCacheViewEntryDesc'),
|
||||
manual: gcli.lookup('appCacheViewEntryManual'),
|
||||
params: [
|
||||
{
|
||||
type: "string",
|
||||
name: "key",
|
||||
description: gcli.lookup("appCacheViewEntryKey"),
|
||||
defaultValue: null,
|
||||
}
|
||||
],
|
||||
exec: function(args, context) {
|
||||
let doc = context.environment.contentDocument;
|
||||
let utils = new AppCacheUtils();
|
||||
|
||||
let result = utils.viewEntry(args.key);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper to find the 'data-command' attribute and call some action on it.
|
||||
* @see |updateCommand()| and |executeCommand()|
|
||||
*/
|
||||
function withCommand(element, action) {
|
||||
let command = element.getAttribute("data-command");
|
||||
if (!command) {
|
||||
command = element.querySelector("*[data-command]")
|
||||
.getAttribute("data-command");
|
||||
}
|
||||
|
||||
if (command) {
|
||||
action(command);
|
||||
}
|
||||
else {
|
||||
console.warn("Missing data-command for " + util.findCssSelector(element));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler to update the requisition to contain the text held in the
|
||||
* first matching data-command attribute under the currentTarget of the event.
|
||||
* @param context Either a Requisition or an ExecutionContext or another object
|
||||
* that contains an |update()| function that follows a similar contract.
|
||||
*/
|
||||
function createUpdateHandler(context) {
|
||||
return function(ev) {
|
||||
withCommand(ev.currentTarget, function(command) {
|
||||
context.update(command);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler to execute the text held in the data-command attribute
|
||||
* under the currentTarget of the event.
|
||||
* @param context Either a Requisition or an ExecutionContext or another object
|
||||
* that contains an |update()| function that follows a similar contract.
|
||||
*/
|
||||
function createExecuteHandler(context) {
|
||||
return function(ev) {
|
||||
withCommand(ev.currentTarget, function(command) {
|
||||
context.exec({
|
||||
visible: true,
|
||||
typed: command
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}(this));
|
||||
|
|
|
@ -44,6 +44,16 @@
|
|||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.gcli-appcache-list {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.gcli-appcache-detail {
|
||||
padding-left: 20px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.gcli-row-out .nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,21 @@ MOCHITEST_BROWSER_FILES = \
|
|||
browser_cmd_addon.js \
|
||||
$(browser_cmd_calllog.js disabled until bug 845831 is fixed) \
|
||||
$(browser_cmd_calllog_chrome.js disabled until bug 845831 is fixed) \
|
||||
browser_cmd_appcache_invalid.js \
|
||||
browser_cmd_appcache_invalid_appcache.appcache \
|
||||
browser_cmd_appcache_invalid_appcache.appcache^headers^ \
|
||||
browser_cmd_appcache_invalid_index.html \
|
||||
browser_cmd_appcache_invalid_page1.html \
|
||||
browser_cmd_appcache_invalid_page2.html \
|
||||
browser_cmd_appcache_invalid_page3.html \
|
||||
browser_cmd_appcache_invalid_page3.html^headers^ \
|
||||
browser_cmd_appcache_valid.js \
|
||||
browser_cmd_appcache_valid_appcache.appcache \
|
||||
browser_cmd_appcache_valid_appcache.appcache^headers^ \
|
||||
browser_cmd_appcache_valid_index.html \
|
||||
browser_cmd_appcache_valid_page1.html \
|
||||
browser_cmd_appcache_valid_page2.html \
|
||||
browser_cmd_appcache_valid_page3.html \
|
||||
browser_cmd_commands.js \
|
||||
browser_cmd_cookie.js \
|
||||
browser_cmd_jsb.js \
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the appcache validate works as they should with an invalid
|
||||
// manifest.
|
||||
|
||||
const TEST_URI = "http://sub1.test1.example.com/browser/browser/devtools/commandline/" +
|
||||
"test/browser_cmd_appcache_invalid_index.html";
|
||||
|
||||
let tests = {};
|
||||
|
||||
function test() {
|
||||
helpers.addTabWithToolbar(TEST_URI, function(options) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// Wait for site to be cached.
|
||||
gBrowser.contentWindow.applicationCache.addEventListener('error', function BCAI_error() {
|
||||
gBrowser.contentWindow.applicationCache.removeEventListener('error', BCAI_error);
|
||||
|
||||
info("Site now cached, running tests.");
|
||||
|
||||
deferred.resolve(helpers.audit(options, [
|
||||
{
|
||||
setup: 'appcache validate',
|
||||
check: {
|
||||
input: 'appcache validate',
|
||||
markup: 'VVVVVVVVVVVVVVVVV',
|
||||
status: 'VALID',
|
||||
args: {}
|
||||
},
|
||||
exec: {
|
||||
completed: false,
|
||||
output: [
|
||||
/Manifest has a character encoding of ISO-8859-1\. Manifests must have the utf-8 character encoding\./,
|
||||
/The first line of the manifest must be "CACHE MANIFEST" at line 1\./,
|
||||
/"CACHE MANIFEST" is only valid on the first line at line 3\./,
|
||||
/images\/sound-icon\.png points to a resource that is not available at line 9\./,
|
||||
/images\/background\.png points to a resource that is not available at line 10\./,
|
||||
/NETWORK section line 13 \(\/checking\.cgi\) prevents caching of line 13 \(\/checking\.cgi\) in the NETWORK section\./,
|
||||
/\/checking\.cgi points to a resource that is not available at line 13\./,
|
||||
/Asterisk used as a wildcard in the NETWORK section at line 14\. A single line containing an asterisk is called the online whitelist wildcard flag and is only valid in the NETWORK section\. Other uses of the \* character are prohibited\. The presence of this flag indicates that any URI not listed as cached is to be implicitly treated as being in the online whitelist namespaces\. If the flag is not present then the blocking state indicates that URIs not listed explicitly in the manifest are to be treated as unavailable\./,
|
||||
/\.\.\/rel\.html points to a resource that is not available at line 17\./,
|
||||
/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 18\./,
|
||||
/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 19\./,
|
||||
/\.\.\/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 20\./,
|
||||
/\.\.\/\.\.\/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 21\./,
|
||||
/\/\.\.\/ is not a valid URI prefix at line 22\./,
|
||||
/\/test\.css points to a resource that is not available at line 23\./,
|
||||
/\/test\.js points to a resource that is not available at line 24\./,
|
||||
/test\.png points to a resource that is not available at line 25\./,
|
||||
/\/main\/features\.js points to a resource that is not available at line 27\./,
|
||||
/\/main\/settings\/index\.css points to a resource that is not available at line 28\./,
|
||||
/http:\/\/example\.com\/scene\.jpg points to a resource that is not available at line 29\./,
|
||||
/\/section1\/blockedbyfallback\.html points to a resource that is not available at line 30\./,
|
||||
/http:\/\/example\.com\/images\/world\.jpg points to a resource that is not available at line 31\./,
|
||||
/\/section2\/blockedbyfallback\.html points to a resource that is not available at line 32\./,
|
||||
/\/main\/home points to a resource that is not available at line 34\./,
|
||||
/main\/app\.js points to a resource that is not available at line 35\./,
|
||||
/\/settings\/home points to a resource that is not available at line 37\./,
|
||||
/\/settings\/app\.js points to a resource that is not available at line 38\./,
|
||||
/The file http:\/\/sub1\.test1\.example\.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_appcache_invalid_page3\.html was modified after http:\/\/sub1\.test1\.example\.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_appcache_invalid_appcache\.appcache\. Unless the text in the manifest file is changed the cached version will be used instead at line 39\./,
|
||||
/browser_cmd_appcache_invalid_page3\.html has cache-control set to no-store\. This will prevent the application cache from storing the file at line 39\./,
|
||||
/http:\/\/example\.com\/logo\.png points to a resource that is not available at line 40\./,
|
||||
/http:\/\/example\.com\/check\.png points to a resource that is not available at line 41\./,
|
||||
/Spaces in URIs need to be replaced with % at line 42\./,
|
||||
/http:\/\/example\.com\/cr oss\.png points to a resource that is not available at line 42\./,
|
||||
/Asterisk used as a wildcard in the CACHE section at line 43\. A single line containing an asterisk is called the online whitelist wildcard flag and is only valid in the NETWORK section\. Other uses of the \* character are prohibited\. The presence of this flag indicates that any URI not listed as cached is to be implicitly treated as being in the online whitelist namespaces\. If the flag is not present then the blocking state indicates that URIs not listed explicitly in the manifest are to be treated as unavailable\./,
|
||||
/The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line 47\./,
|
||||
/FALLBACK section line 50 \(\/section1\/ \/offline1\.html\) prevents caching of line 30 \(\/section1\/blockedbyfallback\.html\) in the CACHE section\./,
|
||||
/\/offline1\.html points to a resource that is not available at line 50\./,
|
||||
/FALLBACK section line 51 \(\/section2\/ offline2\.html\) prevents caching of line 32 \(\/section2\/blockedbyfallback\.html\) in the CACHE section\./,
|
||||
/offline2\.html points to a resource that is not available at line 51\./,
|
||||
/Only two URIs separated by spaces are allowed in the FALLBACK section at line 52\./,
|
||||
/Asterisk \(\*\) incorrectly used as a wildcard in a fallback namespace at line 53\. Namespaces simply need to match a path prefix\./,
|
||||
/offline3\.html points to a resource that is not available at line 53\./,
|
||||
/Invalid section name \(BLAH\) at line 55\./,
|
||||
/Only two URIs separated by spaces are allowed in the FALLBACK section at line 55\./
|
||||
]
|
||||
},
|
||||
},
|
||||
]));
|
||||
});
|
||||
|
||||
acceptOfflineCachePrompt();
|
||||
|
||||
return deferred.promise;
|
||||
}).then(finish);
|
||||
|
||||
function acceptOfflineCachePrompt() {
|
||||
// Pages containing an appcache the notification bar gives options to allow
|
||||
// or deny permission for the app to save data offline. Let's click Allow.
|
||||
let notificationID = "offline-app-requested-sub1.test1.example.com";
|
||||
let notification = PopupNotifications.getNotification(notificationID, gBrowser.selectedBrowser);
|
||||
|
||||
if (notification) {
|
||||
info("Authorizing offline storage.");
|
||||
notification.mainAction.callback();
|
||||
} else {
|
||||
info("No notification box is available.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
# some comment
|
||||
|
||||
CACHE MANIFEST
|
||||
# the above is a required line
|
||||
# this is a comment
|
||||
# spaces are ignored
|
||||
# blank lines are ignored
|
||||
|
||||
images/sound-icon.png
|
||||
images/background.png
|
||||
|
||||
NETWORK:
|
||||
/checking.cgi
|
||||
/checking.*
|
||||
|
||||
CACHE:
|
||||
../rel.html
|
||||
../../rel.html
|
||||
../../../rel.html
|
||||
../../../../rel.html
|
||||
../../../../../rel.html
|
||||
/../invalid.html
|
||||
/test.css
|
||||
/test.js
|
||||
test.png
|
||||
browser_cmd_appcache_invalid_index.html
|
||||
/main/features.js
|
||||
/main/settings/index.css
|
||||
http://example.com/scene.jpg
|
||||
/section1/blockedbyfallback.html
|
||||
http://example.com/images/world.jpg
|
||||
/section2/blockedbyfallback.html
|
||||
browser_cmd_appcache_invalid_page1.html
|
||||
/main/home
|
||||
main/app.js
|
||||
browser_cmd_appcache_invalid_page2.html
|
||||
/settings/home
|
||||
/settings/app.js
|
||||
browser_cmd_appcache_invalid_page3.html
|
||||
http://example.com/logo.png
|
||||
http://example.com/check.png
|
||||
http://example.com/cr oss.png
|
||||
/checking*.png
|
||||
|
||||
SETTINGS:
|
||||
prefer-online
|
||||
fast
|
||||
|
||||
FALLBACK:
|
||||
/section1/ /offline1.html
|
||||
/section2/ offline2.html
|
||||
dadsdsd
|
||||
* offline3.html
|
||||
|
||||
BLAH:
|
|
@ -0,0 +1 @@
|
|||
Content-Type: text/cache-manifest; charset=ISO-8859-1
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html manifest="browser_cmd_appcache_invalid_appcache.appcache">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<body>
|
||||
<h1>Example index.html</h1>
|
||||
<br />
|
||||
<a href="browser_cmd_appcache_invalid_index.html">Home</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page1.html">Page 1</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page2.html">Page 2</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page3.html">Page 3</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html manifest="browser_cmd_appcache_invalid_appcache.appcache">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<body>
|
||||
<h1>Example page1.html</h1>
|
||||
<br />
|
||||
<a href="browser_cmd_appcache_invalid_index.html">Home</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page1.html">Page 1</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page2.html">Page 2</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page3.html">Page 3</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html manifest="browser_cmd_appcache_invalid_appcache.appcache">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<body>
|
||||
<h1>Example page2.html</h1>
|
||||
<br />
|
||||
<a href="browser_cmd_appcache_invalid_index.html">Home</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page1.html">Page 1</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page2.html">Page 2</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page3.html">Page 3</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html manifest="browser_cmd_appcache_invalid_appcache.appcache">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<body>
|
||||
<h1>Example page3.html</h1>
|
||||
<br />
|
||||
<a href="browser_cmd_appcache_invalid_index.html">Home</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page1.html">Page 1</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page2.html">Page 2</a> |
|
||||
<a href="browser_cmd_appcache_invalid_page3.html">Page 3</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,2 @@
|
|||
Cache-Control: no-store, no-cache
|
||||
Last-Modified: Tue, 23 Apr 9999 11:41:13 GMT
|
|
@ -0,0 +1,160 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the appcache commands works as they should
|
||||
|
||||
const TEST_URI = "http://sub1.test2.example.com/browser/browser/devtools/" +
|
||||
"commandline/test/browser_cmd_appcache_valid_index.html";
|
||||
let tests = {};
|
||||
|
||||
function test() {
|
||||
helpers.addTabWithToolbar(TEST_URI, function(options) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
info("adding cache listener.");
|
||||
|
||||
// Wait for site to be cached.
|
||||
gBrowser.contentWindow.applicationCache.addEventListener('cached', function BCAV_cached() {
|
||||
gBrowser.contentWindow.applicationCache.removeEventListener('cached', BCAV_cached);
|
||||
|
||||
info("Site now cached, running tests.");
|
||||
|
||||
deferred.resolve(helpers.audit(options, [
|
||||
{
|
||||
setup: 'appcache',
|
||||
check: {
|
||||
input: 'appcache',
|
||||
markup: 'IIIIIIII',
|
||||
status: 'ERROR',
|
||||
args: {}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
setup: 'appcache list',
|
||||
check: {
|
||||
input: 'appcache list',
|
||||
markup: 'VVVVVVVVVVVVV',
|
||||
status: 'VALID',
|
||||
args: {},
|
||||
},
|
||||
exec: {
|
||||
output: [ /index/, /page1/, /page2/, /page3/ ]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
setup: 'appcache list page',
|
||||
check: {
|
||||
input: 'appcache list page',
|
||||
markup: 'VVVVVVVVVVVVVVVVVV',
|
||||
status: 'VALID',
|
||||
args: {
|
||||
search: { value: 'page' },
|
||||
}
|
||||
},
|
||||
exec: {
|
||||
output: [ /page1/, /page2/, /page3/ ]
|
||||
},
|
||||
post: function(output) {
|
||||
ok(!output.contains("index"), "index is not contained in output");
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
setup: 'appcache validate',
|
||||
check: {
|
||||
input: 'appcache validate',
|
||||
markup: 'VVVVVVVVVVVVVVVVV',
|
||||
status: 'VALID',
|
||||
args: {}
|
||||
},
|
||||
exec: {
|
||||
completed: false,
|
||||
output: [ /successfully/ ]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
setup: 'appcache validate ' + TEST_URI,
|
||||
check: {
|
||||
input: 'appcache validate ' + TEST_URI,
|
||||
// appcache validate http://sub1.test2.example.com/browser/browser/devtools/commandline/test/browser_cmd_appcache_valid_index.html
|
||||
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
|
||||
status: 'VALID',
|
||||
args: {
|
||||
uri: {
|
||||
value: TEST_URI
|
||||
},
|
||||
}
|
||||
},
|
||||
exec: {
|
||||
completed: false,
|
||||
output: [ /successfully/ ]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
setup: 'appcache clear',
|
||||
check: {
|
||||
input: 'appcache clear',
|
||||
markup: 'VVVVVVVVVVVVVV',
|
||||
status: 'VALID',
|
||||
args: {},
|
||||
},
|
||||
exec: {
|
||||
output: [ /successfully/ ]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
setup: 'appcache list',
|
||||
check: {
|
||||
input: 'appcache list',
|
||||
markup: 'VVVVVVVVVVVVV',
|
||||
status: 'VALID',
|
||||
args: {},
|
||||
},
|
||||
exec: {
|
||||
output: [ /no results/ ]
|
||||
},
|
||||
post: function(output) {
|
||||
ok(!output.contains("index"), "index is not contained in output");
|
||||
ok(!output.contains("page1"), "page1 is not contained in output");
|
||||
ok(!output.contains("page2"), "page1 is not contained in output");
|
||||
ok(!output.contains("page3"), "page1 is not contained in output");
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
setup: 'appcache viewentry --key ' + TEST_URI,
|
||||
check: {
|
||||
input: 'appcache viewentry --key ' + TEST_URI,
|
||||
// appcache viewentry --key http://sub1.test2.example.com/browser/browser/devtools/commandline/test/browser_cmd_appcache_valid_index.html
|
||||
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
|
||||
status: 'VALID',
|
||||
args: {}
|
||||
},
|
||||
},
|
||||
]));
|
||||
});
|
||||
|
||||
acceptOfflineCachePrompt();
|
||||
|
||||
return deferred.promise;
|
||||
}).then(finish);
|
||||
|
||||
function acceptOfflineCachePrompt() {
|
||||
// Pages containing an appcache the notification bar gives options to allow
|
||||
// or deny permission for the app to save data offline. Let's click Allow.
|
||||
let notificationID = "offline-app-requested-sub1.test2.example.com";
|
||||
let notification = PopupNotifications.getNotification(notificationID, gBrowser.selectedBrowser);
|
||||
|
||||
if (notification) {
|
||||
info("Authorizing offline storage.");
|
||||
notification.mainAction.callback();
|
||||
} else {
|
||||
info("No notification box is available.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
CACHE MANIFEST
|
||||
browser_cmd_appcache_valid_index.html
|
||||
browser_cmd_appcache_valid_page1.html
|
||||
browser_cmd_appcache_valid_page2.html
|
||||
browser_cmd_appcache_valid_page3.html
|
|
@ -0,0 +1 @@
|
|||
Content-Type: text/cache-manifest
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html manifest="browser_cmd_appcache_valid_appcache.appcache">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<body>
|
||||
<h1>Example index.html</h1>
|
||||
<a href="browser_cmd_appcache_valid_index.html">Home</a> |
|
||||
<a href="browser_cmd_appcache_valid_page1.html">Page 1</a> |
|
||||
<a href="browser_cmd_appcache_valid_page2.html">Page 2</a> |
|
||||
<a href="browser_cmd_appcache_valid_page3.html">Page 3</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html manifest="browser_cmd_appcache_valid_appcache.appcache">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<body>
|
||||
<h1>Example page1.html</h1>
|
||||
<a href="browser_cmd_appcache_valid_index.html">Home</a> |
|
||||
<a href="browser_cmd_appcache_valid_page1.html">Page 1</a> |
|
||||
<a href="browser_cmd_appcache_valid_page2.html">Page 2</a> |
|
||||
<a href="browser_cmd_appcache_valid_page3.html">Page 3</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html manifest="browser_cmd_appcache_valid_appcache.appcache">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<body>
|
||||
<h1>Example page2.html</h1>
|
||||
<a href="browser_cmd_appcache_valid_index.html">Home</a> |
|
||||
<a href="browser_cmd_appcache_valid_page1.html">Page 1</a> |
|
||||
<a href="browser_cmd_appcache_valid_page2.html">Page 2</a> |
|
||||
<a href="browser_cmd_appcache_valid_page3.html">Page 3</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html manifest="browser_cmd_appcache_valid_appcache.appcache">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<body>
|
||||
<h1>Example page3.html</h1>
|
||||
<a href="browser_cmd_appcache_valid_index.html">Home</a> |
|
||||
<a href="browser_cmd_appcache_valid_page1.html">Page 1</a> |
|
||||
<a href="browser_cmd_appcache_valid_page2.html">Page 2</a> |
|
||||
<a href="browser_cmd_appcache_valid_page3.html">Page 3</a>
|
||||
</body>
|
||||
</html>
|
|
@ -733,10 +733,13 @@ helpers._exec = function(options, name, expected) {
|
|||
var actualOutput = div.textContent.trim();
|
||||
|
||||
var doTest = function(match, against) {
|
||||
if (!match.test(against)) {
|
||||
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
|
||||
log('Actual textContent');
|
||||
log(against);
|
||||
if (match.test(against)) {
|
||||
assert.ok(true, 'html output for ' + name + ' should match ' +
|
||||
match.source);
|
||||
} else {
|
||||
assert.ok(false, 'html output for ' + name + ' should match ' +
|
||||
match.source +
|
||||
'. Actual textContent: "' + against + '"');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -754,7 +757,7 @@ helpers._exec = function(options, name, expected) {
|
|||
doTest(expected.output, actualOutput);
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
deferred.resolve(actualOutput);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -792,9 +795,9 @@ helpers._setup = function(options, name, action) {
|
|||
/**
|
||||
* Helper to shutdown the test
|
||||
*/
|
||||
helpers._post = function(name, action) {
|
||||
helpers._post = function(name, action, output) {
|
||||
if (typeof action === 'function') {
|
||||
return Promise.resolve(action());
|
||||
return Promise.resolve(action(output));
|
||||
}
|
||||
return Promise.resolve(action);
|
||||
};
|
||||
|
@ -951,8 +954,8 @@ helpers.audit = function(options, audits) {
|
|||
var checkDone = helpers._check(options, name, audit.check);
|
||||
return checkDone.then(function() {
|
||||
var execDone = helpers._exec(options, name, audit.exec);
|
||||
return execDone.then(function() {
|
||||
return helpers._post(name, audit.post).then(function() {
|
||||
return execDone.then(function(output) {
|
||||
return helpers._post(name, audit.post, output).then(function() {
|
||||
if (assert.testLogging) {
|
||||
log('- END \'' + name + '\' in ' + assert.currentTest);
|
||||
}
|
||||
|
|
|
@ -488,13 +488,16 @@ ChromeDebuggerProcess.prototype = {
|
|||
close: function RDP_close() {
|
||||
dumpn("Closing chrome debugging process");
|
||||
if (!this.globalUI) {
|
||||
dumpn("globalUI is missing");
|
||||
return;
|
||||
}
|
||||
delete this.globalUI._chromeDebugger;
|
||||
|
||||
if (this._dbgProcess.isRunning) {
|
||||
dumpn("Killing chrome debugging process...");
|
||||
this._dbgProcess.kill();
|
||||
}
|
||||
dumpn("...done.");
|
||||
if (typeof this._closeCallback == "function") {
|
||||
this._closeCallback.call({}, this);
|
||||
}
|
||||
|
|
|
@ -48,8 +48,8 @@ function testSimpleCall() {
|
|||
ok(gProcess._dbgProfile.name,
|
||||
"The remote debugger profile doesn't have a name...");
|
||||
|
||||
info("profile localDir: " + gProcess._dbgProfile.localDir);
|
||||
info("profile rootDir: " + gProcess._dbgProfile.rootDir);
|
||||
info("profile localDir: " + gProcess._dbgProfile.localDir.path);
|
||||
info("profile rootDir: " + gProcess._dbgProfile.rootDir.path);
|
||||
info("profile name: " + gProcess._dbgProfile.name);
|
||||
|
||||
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
|
||||
|
@ -64,7 +64,8 @@ function testSimpleCall() {
|
|||
is(profile.rootDir.path, gProcess._dbgProfile.rootDir.path,
|
||||
"The remote debugger profile doesn't have the correct rootDir!");
|
||||
|
||||
DebuggerUI.toggleChromeDebugger();
|
||||
let chromeDebug = DebuggerUI.toggleChromeDebugger();
|
||||
info("toggleChromeDebugger() returned " + chromeDebug);
|
||||
}}, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -733,10 +733,13 @@ helpers._exec = function(options, name, expected) {
|
|||
var actualOutput = div.textContent.trim();
|
||||
|
||||
var doTest = function(match, against) {
|
||||
if (!match.test(against)) {
|
||||
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
|
||||
log('Actual textContent');
|
||||
log(against);
|
||||
if (match.test(against)) {
|
||||
assert.ok(true, 'html output for ' + name + ' should match ' +
|
||||
match.source);
|
||||
} else {
|
||||
assert.ok(false, 'html output for ' + name + ' should match ' +
|
||||
match.source +
|
||||
'. Actual textContent: "' + against + '"');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -754,7 +757,7 @@ helpers._exec = function(options, name, expected) {
|
|||
doTest(expected.output, actualOutput);
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
deferred.resolve(actualOutput);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -792,9 +795,9 @@ helpers._setup = function(options, name, action) {
|
|||
/**
|
||||
* Helper to shutdown the test
|
||||
*/
|
||||
helpers._post = function(name, action) {
|
||||
helpers._post = function(name, action, output) {
|
||||
if (typeof action === 'function') {
|
||||
return Promise.resolve(action());
|
||||
return Promise.resolve(action(output));
|
||||
}
|
||||
return Promise.resolve(action);
|
||||
};
|
||||
|
@ -951,8 +954,8 @@ helpers.audit = function(options, audits) {
|
|||
var checkDone = helpers._check(options, name, audit.check);
|
||||
return checkDone.then(function() {
|
||||
var execDone = helpers._exec(options, name, audit.exec);
|
||||
return execDone.then(function() {
|
||||
return helpers._post(name, audit.post).then(function() {
|
||||
return execDone.then(function(output) {
|
||||
return helpers._post(name, audit.post, output).then(function() {
|
||||
if (assert.testLogging) {
|
||||
log('- END \'' + name + '\' in ' + assert.currentTest);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).req
|
|||
Components.utils.import("resource:///modules/devtools/gcli.jsm", {});
|
||||
|
||||
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
|
||||
let {devtools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
let devtools = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools;
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
|
||||
let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
|
||||
|
@ -733,10 +733,13 @@ helpers._exec = function(options, name, expected) {
|
|||
var actualOutput = div.textContent.trim();
|
||||
|
||||
var doTest = function(match, against) {
|
||||
if (!match.test(against)) {
|
||||
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
|
||||
log('Actual textContent');
|
||||
log(against);
|
||||
if (match.test(against)) {
|
||||
assert.ok(true, 'html output for ' + name + ' should match ' +
|
||||
match.source);
|
||||
} else {
|
||||
assert.ok(false, 'html output for ' + name + ' should match ' +
|
||||
match.source +
|
||||
'. Actual textContent: "' + against + '"');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -754,7 +757,7 @@ helpers._exec = function(options, name, expected) {
|
|||
doTest(expected.output, actualOutput);
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
deferred.resolve(actualOutput);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -792,9 +795,9 @@ helpers._setup = function(options, name, action) {
|
|||
/**
|
||||
* Helper to shutdown the test
|
||||
*/
|
||||
helpers._post = function(name, action) {
|
||||
helpers._post = function(name, action, output) {
|
||||
if (typeof action === 'function') {
|
||||
return Promise.resolve(action());
|
||||
return Promise.resolve(action(output));
|
||||
}
|
||||
return Promise.resolve(action);
|
||||
};
|
||||
|
@ -951,8 +954,8 @@ helpers.audit = function(options, audits) {
|
|||
var checkDone = helpers._check(options, name, audit.check);
|
||||
return checkDone.then(function() {
|
||||
var execDone = helpers._exec(options, name, audit.exec);
|
||||
return execDone.then(function() {
|
||||
return helpers._post(name, audit.post).then(function() {
|
||||
return execDone.then(function(output) {
|
||||
return helpers._post(name, audit.post, output).then(function() {
|
||||
if (assert.testLogging) {
|
||||
log('- END \'' + name + '\' in ' + assert.currentTest);
|
||||
}
|
||||
|
|
|
@ -733,10 +733,13 @@ helpers._exec = function(options, name, expected) {
|
|||
var actualOutput = div.textContent.trim();
|
||||
|
||||
var doTest = function(match, against) {
|
||||
if (!match.test(against)) {
|
||||
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
|
||||
log('Actual textContent');
|
||||
log(against);
|
||||
if (match.test(against)) {
|
||||
assert.ok(true, 'html output for ' + name + ' should match ' +
|
||||
match.source);
|
||||
} else {
|
||||
assert.ok(false, 'html output for ' + name + ' should match ' +
|
||||
match.source +
|
||||
'. Actual textContent: "' + against + '"');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -754,7 +757,7 @@ helpers._exec = function(options, name, expected) {
|
|||
doTest(expected.output, actualOutput);
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
deferred.resolve(actualOutput);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -792,9 +795,9 @@ helpers._setup = function(options, name, action) {
|
|||
/**
|
||||
* Helper to shutdown the test
|
||||
*/
|
||||
helpers._post = function(name, action) {
|
||||
helpers._post = function(name, action, output) {
|
||||
if (typeof action === 'function') {
|
||||
return Promise.resolve(action());
|
||||
return Promise.resolve(action(output));
|
||||
}
|
||||
return Promise.resolve(action);
|
||||
};
|
||||
|
@ -951,8 +954,8 @@ helpers.audit = function(options, audits) {
|
|||
var checkDone = helpers._check(options, name, audit.check);
|
||||
return checkDone.then(function() {
|
||||
var execDone = helpers._exec(options, name, audit.exec);
|
||||
return execDone.then(function() {
|
||||
return helpers._post(name, audit.post).then(function() {
|
||||
return execDone.then(function(output) {
|
||||
return helpers._post(name, audit.post, output).then(function() {
|
||||
if (assert.testLogging) {
|
||||
log('- END \'' + name + '\' in ' + assert.currentTest);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,623 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* validateManifest() warns of the following errors:
|
||||
* - No manifest specified in page
|
||||
* - Manifest is not utf-8
|
||||
* - Manifest mimetype not text/cache-manifest
|
||||
* - Manifest does not begin with "CACHE MANIFEST"
|
||||
* - Page modified since appcache last changed
|
||||
* - Duplicate entries
|
||||
* - Conflicting entries e.g. in both CACHE and NETWORK sections or in cache
|
||||
* but blocked by FALLBACK namespace
|
||||
* - Detect referenced files that are not available
|
||||
* - Detect referenced files that have cache-control set to no-store
|
||||
* - Wildcards used in a section other than NETWORK
|
||||
* - Spaces in URI not replaced with %20
|
||||
* - Completely invalid URIs
|
||||
* - Too many dot dot slash operators
|
||||
* - SETTINGS section is valid
|
||||
* - Invalid section name
|
||||
* - etc.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
||||
let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
let { Promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["AppCacheUtils"];
|
||||
|
||||
function AppCacheUtils(documentOrUri) {
|
||||
this._parseManifest = this._parseManifest.bind(this);
|
||||
|
||||
if (documentOrUri) {
|
||||
if (typeof documentOrUri == "string") {
|
||||
this.uri = documentOrUri;
|
||||
}
|
||||
if (/HTMLDocument/.test(documentOrUri.toString())) {
|
||||
this.doc = documentOrUri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppCacheUtils.prototype = {
|
||||
get cachePath() {
|
||||
return "";
|
||||
},
|
||||
|
||||
validateManifest: function ACU_validateManifest() {
|
||||
let deferred = Promise.defer();
|
||||
this.errors = [];
|
||||
// Check for missing manifest.
|
||||
this._getManifestURI().then(manifestURI => {
|
||||
this.manifestURI = manifestURI;
|
||||
|
||||
if (!this.manifestURI) {
|
||||
this._addError(0, "noManifest");
|
||||
deferred.resolve(this.errors);
|
||||
}
|
||||
|
||||
this._getURIInfo(this.manifestURI).then(uriInfo => {
|
||||
this._parseManifest(uriInfo).then(() => {
|
||||
// Sort errors by line number.
|
||||
this.errors.sort(function(a, b) {
|
||||
return a.line - b.line;
|
||||
});
|
||||
deferred.resolve(this.errors);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
_parseManifest: function ACU__parseManifest(uriInfo) {
|
||||
let deferred = Promise.defer();
|
||||
let manifestName = uriInfo.name;
|
||||
let manifestLastModified = new Date(uriInfo.responseHeaders["Last-Modified"]);
|
||||
|
||||
if (uriInfo.charset.toLowerCase() != "utf-8") {
|
||||
this._addError(0, "notUTF8", uriInfo.charset);
|
||||
}
|
||||
|
||||
if (uriInfo.mimeType != "text/cache-manifest") {
|
||||
this._addError(0, "badMimeType", uriInfo.mimeType);
|
||||
}
|
||||
|
||||
let parser = new ManifestParser(uriInfo.text, this.manifestURI);
|
||||
let parsed = parser.parse();
|
||||
|
||||
if (parsed.errors.length > 0) {
|
||||
this.errors.push.apply(this.errors, parsed.errors);
|
||||
}
|
||||
|
||||
// Check for duplicate entries.
|
||||
let dupes = {};
|
||||
for (let parsedUri of parsed.uris) {
|
||||
dupes[parsedUri.uri] = dupes[parsedUri.uri] || [];
|
||||
dupes[parsedUri.uri].push({
|
||||
line: parsedUri.line,
|
||||
section: parsedUri.section,
|
||||
original: parsedUri.original
|
||||
});
|
||||
}
|
||||
for (let [uri, value] of Iterator(dupes)) {
|
||||
if (value.length > 1) {
|
||||
this._addError(0, "duplicateURI", uri, JSON.stringify(value));
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through network entries making sure that fallback and cache don't
|
||||
// contain uris starting with the network uri.
|
||||
for (let neturi of parsed.uris) {
|
||||
if (neturi.section == "NETWORK") {
|
||||
for (let parsedUri of parsed.uris) {
|
||||
if (parsedUri.uri.startsWith(neturi.uri)) {
|
||||
this._addError(neturi.line, "networkBlocksURI", neturi.line,
|
||||
neturi.original, parsedUri.line, parsedUri.original,
|
||||
parsedUri.section);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through fallback entries making sure that fallback and cache don't
|
||||
// contain uris starting with the network uri.
|
||||
for (let fb of parsed.fallbacks) {
|
||||
for (let parsedUri of parsed.uris) {
|
||||
if (parsedUri.uri.startsWith(fb.namespace)) {
|
||||
this._addError(fb.line, "fallbackBlocksURI", fb.line,
|
||||
fb.original, parsedUri.line, parsedUri.original,
|
||||
parsedUri.section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all resources exist and that their cach-control headers are
|
||||
// not set to no-store.
|
||||
let current = -1;
|
||||
for (let i = 0, len = parsed.uris.length; i < len; i++) {
|
||||
let parsedUri = parsed.uris[i];
|
||||
this._getURIInfo(parsedUri.uri).then(uriInfo => {
|
||||
current++;
|
||||
|
||||
if (uriInfo.success) {
|
||||
// Check that the resource was not modified after the manifest was last
|
||||
// modified. If it was then the manifest file should be refreshed.
|
||||
let resourceLastModified =
|
||||
new Date(uriInfo.responseHeaders["Last-Modified"]);
|
||||
|
||||
if (manifestLastModified < resourceLastModified) {
|
||||
this._addError(parsedUri.line, "fileChangedButNotManifest",
|
||||
uriInfo.name, manifestName, parsedUri.line);
|
||||
}
|
||||
|
||||
// If cache-control: no-store the file will not be added to the
|
||||
// appCache.
|
||||
if (uriInfo.nocache) {
|
||||
this._addError(parsedUri.line, "cacheControlNoStore",
|
||||
parsedUri.original, parsedUri.line);
|
||||
}
|
||||
} else {
|
||||
this._addError(parsedUri.line, "notAvailable",
|
||||
parsedUri.original, parsedUri.line);
|
||||
}
|
||||
|
||||
if (current == len - 1) {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
_getURIInfo: function ACU__getURIInfo(uri) {
|
||||
let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
let deferred = Promise.defer();
|
||||
let channelCharset = "";
|
||||
let buffer = "";
|
||||
let channel = Services.io.newChannel(uri, null, null);
|
||||
|
||||
// Avoid the cache:
|
||||
channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
|
||||
channel.asyncOpen({
|
||||
onStartRequest: function (request, context) {
|
||||
// This empty method is needed in order for onDataAvailable to be
|
||||
// called.
|
||||
},
|
||||
|
||||
onDataAvailable: function (request, context, stream, offset, count) {
|
||||
request.QueryInterface(Ci.nsIHttpChannel);
|
||||
inputStream.init(stream);
|
||||
buffer = buffer.concat(inputStream.read(count));
|
||||
},
|
||||
|
||||
onStopRequest: function onStartRequest(request, context, statusCode) {
|
||||
if (statusCode == 0) {
|
||||
request.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
let result = {
|
||||
name: request.name,
|
||||
success: request.requestSucceeded,
|
||||
status: request.responseStatus + " - " + request.responseStatusText,
|
||||
charset: request.contentCharset || "utf-8",
|
||||
mimeType: request.contentType,
|
||||
contentLength: request.contentLength,
|
||||
nocache: request.isNoCacheResponse() || request.isNoStoreResponse(),
|
||||
prePath: request.URI.prePath + "/",
|
||||
text: buffer
|
||||
};
|
||||
|
||||
result.requestHeaders = {};
|
||||
request.visitRequestHeaders(function(header, value) {
|
||||
result.requestHeaders[header] = value;
|
||||
});
|
||||
|
||||
result.responseHeaders = {};
|
||||
request.visitResponseHeaders(function(header, value) {
|
||||
result.responseHeaders[header] = value;
|
||||
});
|
||||
|
||||
deferred.resolve(result);
|
||||
} else {
|
||||
deferred.resolve({
|
||||
name: request.name,
|
||||
success: false
|
||||
});
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
listEntries: function ACU_show(searchTerm) {
|
||||
let entries = [];
|
||||
|
||||
Services.cache.visitEntries({
|
||||
visitDevice: function(deviceID, deviceInfo) {
|
||||
return true;
|
||||
},
|
||||
|
||||
visitEntry: function(deviceID, entryInfo) {
|
||||
if (entryInfo.deviceID == "offline") {
|
||||
let entry = {};
|
||||
let lowerKey = entryInfo.key.toLowerCase();
|
||||
|
||||
if (searchTerm && lowerKey.indexOf(searchTerm.toLowerCase()) == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let [key, value] of Iterator(entryInfo)) {
|
||||
if (key == "QueryInterface") {
|
||||
continue;
|
||||
}
|
||||
if (key == "clientID") {
|
||||
entry.key = entryInfo.key;
|
||||
}
|
||||
if (key == "expirationTime" || key == "lastFetched" || key == "lastModified") {
|
||||
value = new Date(value * 1000);
|
||||
}
|
||||
entry[key] = value;
|
||||
}
|
||||
entries.push(entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return entries;
|
||||
},
|
||||
|
||||
viewEntry: function ACU_viewEntry(key) {
|
||||
let uri;
|
||||
|
||||
Services.cache.visitEntries({
|
||||
visitDevice: function(deviceID, deviceInfo) {
|
||||
return true;
|
||||
},
|
||||
|
||||
visitEntry: function(deviceID, entryInfo) {
|
||||
if (entryInfo.deviceID == "offline" && entryInfo.key == key) {
|
||||
uri = "about:cache-entry?client=" + entryInfo.clientID +
|
||||
"&sb=1&key=" + entryInfo.key;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (uri) {
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
let win = wm.getMostRecentWindow("navigator:browser");
|
||||
win.gBrowser.selectedTab = win.gBrowser.addTab(uri);
|
||||
} else {
|
||||
return l10n.GetStringFromName("entryNotFound");
|
||||
}
|
||||
},
|
||||
|
||||
clearAll: function ACU_clearAll() {
|
||||
Services.cache.evictEntries(Ci.nsICache.STORE_OFFLINE);
|
||||
},
|
||||
|
||||
_getManifestURI: function ACU__getManifestURI() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let getURI = node => {
|
||||
let htmlNode = this.doc.querySelector("html[manifest]");
|
||||
if (htmlNode) {
|
||||
let pageUri = this.doc.location ? this.doc.location.href : this.uri;
|
||||
let origin = pageUri.substr(0, pageUri.lastIndexOf("/") + 1);
|
||||
return origin + htmlNode.getAttribute("manifest");
|
||||
}
|
||||
};
|
||||
|
||||
if (this.doc) {
|
||||
let uri = getURI(this.doc);
|
||||
return Promise.resolve(uri);
|
||||
} else {
|
||||
this._getURIInfo(this.uri).then(uriInfo => {
|
||||
if (uriInfo.success) {
|
||||
let html = uriInfo.text;
|
||||
let parser = _DOMParser;
|
||||
this.doc = parser.parseFromString(html, "text/html");
|
||||
let uri = getURI(this.doc);
|
||||
deferred.resolve(uri);
|
||||
} else {
|
||||
this.errors.push({
|
||||
line: 0,
|
||||
msg: "The URI passed to AppCacheUtils is invalid."
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
_addError: function ACU__addError(line, l10nString, ...params) {
|
||||
let msg;
|
||||
|
||||
if (params) {
|
||||
msg = l10n.formatStringFromName(l10nString, params, params.length);
|
||||
} else {
|
||||
msg = l10n.GetStringFromName(l10nString);
|
||||
}
|
||||
|
||||
this.errors.push({
|
||||
line: line,
|
||||
msg: msg
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* We use our own custom parser because we need far more detailed information
|
||||
* than the system manifest parser provides.
|
||||
*
|
||||
* @param {String} manifestText
|
||||
* The text content of the manifest file.
|
||||
* @param {String} manifestURI
|
||||
* The URI of the manifest file. This is used in calculating the path of
|
||||
* relative URIs.
|
||||
*/
|
||||
function ManifestParser(manifestText, manifestURI) {
|
||||
this.manifestText = manifestText;
|
||||
this.origin = manifestURI.substr(0, manifestURI.lastIndexOf("/") + 1)
|
||||
.replace(" ", "%20");
|
||||
}
|
||||
|
||||
ManifestParser.prototype = {
|
||||
parse: function OCIMP_parse() {
|
||||
let lines = this.manifestText.split(/\r?\n/);
|
||||
let fallbacks = this.fallbacks = [];
|
||||
let settings = this.settings = [];
|
||||
let errors = this.errors = [];
|
||||
let uris = this.uris = [];
|
||||
|
||||
this.currSection = "CACHE";
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let text = this.text = lines[i].replace(/^\s+|\s+$/g);
|
||||
this.currentLine = i + 1;
|
||||
|
||||
if (i == 0 && text != "CACHE MANIFEST") {
|
||||
this._addError(1, "firstLineMustBeCacheManifest", 1);
|
||||
}
|
||||
|
||||
// Ignore comments
|
||||
if (/^#/.test(text) || !text.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (text == "CACHE MANIFEST") {
|
||||
if (this.currentLine != 1) {
|
||||
this._addError(this.currentLine, "cacheManifestOnlyFirstLine",
|
||||
this.currentLine);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this._maybeUpdateSectionName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (this.currSection) {
|
||||
case "CACHE":
|
||||
case "NETWORK":
|
||||
this.parseLine();
|
||||
break;
|
||||
case "FALLBACK":
|
||||
this.parseFallbackLine();
|
||||
break;
|
||||
case "SETTINGS":
|
||||
this.parseSettingsLine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
uris: uris,
|
||||
fallbacks: fallbacks,
|
||||
settings: settings,
|
||||
errors: errors
|
||||
};
|
||||
},
|
||||
|
||||
parseLine: function OCIMP_parseLine() {
|
||||
let text = this.text;
|
||||
|
||||
if (text.indexOf("*") != -1) {
|
||||
if (this.currSection != "NETWORK" || text.length != 1) {
|
||||
this._addError(this.currentLine, "asteriskInWrongSection",
|
||||
this.currSection, this.currentLine);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (/\s/.test(text)) {
|
||||
this._addError(this.currentLine, "escapeSpaces", this.currentLine);
|
||||
text = text.replace(/\s/g, "%20")
|
||||
}
|
||||
|
||||
if (text[0] == "/") {
|
||||
if (text.substr(0, 4) == "/../") {
|
||||
this._addError(this.currentLine, "slashDotDotSlashBad", this.currentLine);
|
||||
} else {
|
||||
this.uris.push(this._wrapURI(this.origin + text.substring(1)));
|
||||
}
|
||||
} else if (text.substr(0, 2) == "./") {
|
||||
this.uris.push(this._wrapURI(this.origin + text.substring(2)));
|
||||
} else if (text.substr(0, 4) == "http") {
|
||||
this.uris.push(this._wrapURI(text));
|
||||
} else {
|
||||
let origin = this.origin;
|
||||
let path = text;
|
||||
|
||||
while (path.substr(0, 3) == "../" && /^https?:\/\/.*?\/.*?\//.test(origin)) {
|
||||
let trimIdx = origin.substr(0, origin.length - 1).lastIndexOf("/") + 1;
|
||||
origin = origin.substr(0, trimIdx);
|
||||
path = path.substr(3);
|
||||
}
|
||||
|
||||
if (path.substr(0, 3) == "../") {
|
||||
this._addError(this.currentLine, "tooManyDotDotSlashes", this.currentLine);
|
||||
return;
|
||||
}
|
||||
|
||||
if (/^https?:\/\//.test(path)) {
|
||||
this.uris.push(this._wrapURI(path));
|
||||
return;
|
||||
}
|
||||
this.uris.push(this._wrapURI(origin + path));
|
||||
}
|
||||
},
|
||||
|
||||
parseFallbackLine: function OCIMP_parseFallbackLine() {
|
||||
let split = this.text.split(/\s+/);
|
||||
let origURI = this.text;
|
||||
|
||||
if (split.length != 2) {
|
||||
this._addError(this.currentLine, "fallbackUseSpaces", this.currentLine);
|
||||
return;
|
||||
}
|
||||
|
||||
let [ namespace, fallback ] = split;
|
||||
|
||||
if (namespace.indexOf("*") != -1) {
|
||||
this._addError(this.currentLine, "fallbackAsterisk", this.currentLine);
|
||||
}
|
||||
|
||||
if (/\s/.test(namespace)) {
|
||||
this._addError(this.currentLine, "escapeSpaces", this.currentLine);
|
||||
namespace = namespace.replace(/\s/g, "%20")
|
||||
}
|
||||
|
||||
if (namespace.substr(0, 4) == "/../") {
|
||||
this._addError(this.currentLine, "slashDotDotSlashBad", this.currentLine);
|
||||
}
|
||||
|
||||
if (namespace.substr(0, 2) == "./") {
|
||||
namespace = this.origin + namespace.substring(2);
|
||||
}
|
||||
|
||||
if (namespace.substr(0, 4) != "http") {
|
||||
let origin = this.origin;
|
||||
let path = namespace;
|
||||
|
||||
while (path.substr(0, 3) == "../" && /^https?:\/\/.*?\/.*?\//.test(origin)) {
|
||||
let trimIdx = origin.substr(0, origin.length - 1).lastIndexOf("/") + 1;
|
||||
origin = origin.substr(0, trimIdx);
|
||||
path = path.substr(3);
|
||||
}
|
||||
|
||||
if (path.substr(0, 3) == "../") {
|
||||
this._addError(this.currentLine, "tooManyDotDotSlashes", this.currentLine);
|
||||
}
|
||||
|
||||
if (/^https?:\/\//.test(path)) {
|
||||
namespace = path;
|
||||
} else {
|
||||
if (path[0] == "/") {
|
||||
path = path.substring(1);
|
||||
}
|
||||
namespace = origin + path;
|
||||
}
|
||||
}
|
||||
|
||||
this.text = fallback;
|
||||
this.parseLine();
|
||||
|
||||
this.fallbacks.push({
|
||||
line: this.currentLine,
|
||||
original: origURI,
|
||||
namespace: namespace,
|
||||
fallback: fallback
|
||||
});
|
||||
},
|
||||
|
||||
parseSettingsLine: function OCIMP_parseSettingsLine() {
|
||||
let text = this.text;
|
||||
|
||||
if (this.settings.length == 1 || !/prefer-online|fast/.test(text)) {
|
||||
this._addError(this.currentLine, "settingsBadValue", this.currentLine);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (text) {
|
||||
case "prefer-online":
|
||||
this.settings.push(this._wrapURI(text));
|
||||
break;
|
||||
case "fast":
|
||||
this.settings.push(this._wrapURI(text));
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_wrapURI: function OCIMP__wrapURI(uri) {
|
||||
return {
|
||||
section: this.currSection,
|
||||
line: this.currentLine,
|
||||
uri: uri,
|
||||
original: this.text
|
||||
};
|
||||
},
|
||||
|
||||
_addError: function OCIMP__addError(line, l10nString, ...params) {
|
||||
let msg;
|
||||
|
||||
if (params) {
|
||||
msg = l10n.formatStringFromName(l10nString, params, params.length);
|
||||
} else {
|
||||
msg = l10n.GetStringFromName(l10nString);
|
||||
}
|
||||
|
||||
this.errors.push({
|
||||
line: line,
|
||||
msg: msg
|
||||
});
|
||||
},
|
||||
|
||||
_maybeUpdateSectionName: function OCIMP__maybeUpdateSectionName() {
|
||||
let text = this.text;
|
||||
|
||||
if (text == text.toUpperCase() && text.charAt(text.length - 1) == ":") {
|
||||
text = text.substr(0, text.length - 1);
|
||||
|
||||
switch (text) {
|
||||
case "CACHE":
|
||||
case "NETWORK":
|
||||
case "FALLBACK":
|
||||
case "SETTINGS":
|
||||
this.currSection = text;
|
||||
return true;
|
||||
default:
|
||||
this._addError(this.currentLine,
|
||||
"invalidSectionName", text, this.currentLine);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "l10n", function() Services.strings
|
||||
.createBundle("chrome://browser/locale/devtools/appcacheutils.properties"));
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "appcacheservice", function() {
|
||||
return Cc["@mozilla.org/network/application-cache-service;1"]
|
||||
.getService(Ci.nsIApplicationCacheService);
|
||||
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_DOMParser", function() {
|
||||
return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
|
||||
});
|
|
@ -733,10 +733,13 @@ helpers._exec = function(options, name, expected) {
|
|||
var actualOutput = div.textContent.trim();
|
||||
|
||||
var doTest = function(match, against) {
|
||||
if (!match.test(against)) {
|
||||
assert.ok(false, 'html output for ' + name + ' against ' + match.source);
|
||||
log('Actual textContent');
|
||||
log(against);
|
||||
if (match.test(against)) {
|
||||
assert.ok(true, 'html output for ' + name + ' should match ' +
|
||||
match.source);
|
||||
} else {
|
||||
assert.ok(false, 'html output for ' + name + ' should match ' +
|
||||
match.source +
|
||||
'. Actual textContent: "' + against + '"');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -754,7 +757,7 @@ helpers._exec = function(options, name, expected) {
|
|||
doTest(expected.output, actualOutput);
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
deferred.resolve(actualOutput);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -792,9 +795,9 @@ helpers._setup = function(options, name, action) {
|
|||
/**
|
||||
* Helper to shutdown the test
|
||||
*/
|
||||
helpers._post = function(name, action) {
|
||||
helpers._post = function(name, action, output) {
|
||||
if (typeof action === 'function') {
|
||||
return Promise.resolve(action());
|
||||
return Promise.resolve(action(output));
|
||||
}
|
||||
return Promise.resolve(action);
|
||||
};
|
||||
|
@ -951,8 +954,8 @@ helpers.audit = function(options, audits) {
|
|||
var checkDone = helpers._check(options, name, audit.check);
|
||||
return checkDone.then(function() {
|
||||
var execDone = helpers._exec(options, name, audit.exec);
|
||||
return execDone.then(function() {
|
||||
return helpers._post(name, audit.post).then(function() {
|
||||
return execDone.then(function(output) {
|
||||
return helpers._post(name, audit.post, output).then(function() {
|
||||
if (assert.testLogging) {
|
||||
log('- END \'' + name + '\' in ' + assert.currentTest);
|
||||
}
|
||||
|
|
|
@ -122,20 +122,22 @@ MOCHITEST_BROWSER_FILES = \
|
|||
browser_console.js \
|
||||
browser_longstring_hang.js \
|
||||
browser_console_consolejsm_output.js \
|
||||
browser_webconsole_bug_837351_securityerrors.js \
|
||||
browser_webconsole_bug_837351_securityerrors.js \
|
||||
browser_bug_865871_variables_view_close_on_esc_key.js \
|
||||
browser_bug_865288_repeat_different_objects.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH), Darwin)
|
||||
MOCHITEST_BROWSER_FILES += \
|
||||
browser_webconsole_bug_804845_ctrl_key_nav.js \
|
||||
browser_webconsole_bug_804845_ctrl_key_nav.js \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
MOCHITEST_BROWSER_FILES += \
|
||||
browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js \
|
||||
$(NULL)
|
||||
browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
MOCHITEST_BROWSER_FILES += \
|
||||
|
@ -225,7 +227,7 @@ MOCHITEST_BROWSER_FILES += \
|
|||
test-bug-821877-csperrors.html^headers^ \
|
||||
test-eval-in-stackframe.html \
|
||||
test-bug-859170-longstring-hang.html \
|
||||
test-bug-837351-security-errors.html \
|
||||
test-bug-837351-security-errors.html \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Tests that makes sure messages are not considered repeated when console.log()
|
||||
// is invoked with different objects, see bug 865288.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-repeated-messages.html";
|
||||
|
||||
let hud = null;
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
openConsole(null, consoleOpened);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function consoleOpened(aHud) {
|
||||
hud = aHud;
|
||||
|
||||
// Check that css warnings are not coalesced if they come from different lines.
|
||||
info("waiting for 3 console.log objects");
|
||||
|
||||
hud.jsterm.clearOutput(true);
|
||||
content.wrappedJSObject.testConsoleObjects();
|
||||
|
||||
waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
name: "3 console.log messages",
|
||||
text: "abba",
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
count: 3,
|
||||
repeats: 1,
|
||||
objects: true,
|
||||
}],
|
||||
}).then(checkMessages);
|
||||
}
|
||||
|
||||
function checkMessages(aResults)
|
||||
{
|
||||
let result = aResults[0];
|
||||
let msgs = [...result.matched];
|
||||
is(msgs.length, 3, "3 message elements");
|
||||
let m = -1;
|
||||
|
||||
function nextMessage()
|
||||
{
|
||||
let msg = msgs[++m];
|
||||
if (msg) {
|
||||
ok(msg, "message element #" + m);
|
||||
|
||||
let clickable = msg.querySelector(".hud-clickable");
|
||||
ok(clickable, "clickable object #" + m);
|
||||
|
||||
scrollOutputToNode(msg);
|
||||
clickObject(clickable);
|
||||
}
|
||||
else {
|
||||
finishTest();
|
||||
}
|
||||
}
|
||||
|
||||
nextMessage();
|
||||
|
||||
function clickObject(aObject)
|
||||
{
|
||||
hud.jsterm.once("variablesview-fetched", onObjectFetch);
|
||||
EventUtils.synthesizeMouse(aObject, 2, 2, {}, hud.iframeWindow);
|
||||
}
|
||||
|
||||
function onObjectFetch(aEvent, aVar)
|
||||
{
|
||||
findVariableViewProperties(aVar, [
|
||||
{ name: "id", value: "abba" + m },
|
||||
], { webconsole: hud }).then(nextMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Check that the variables view sidebar can be closed by pressing Escape in the
|
||||
// web console.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-eval-in-stackframe.html";
|
||||
|
||||
let gWebConsole, gJSTerm, gVariablesView;
|
||||
|
||||
function test()
|
||||
{
|
||||
registerCleanupFunction(() => {
|
||||
gWebConsole = gJSTerm = gVariablesView = null;
|
||||
});
|
||||
|
||||
addTab(TEST_URI);
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
openConsole(null, consoleOpened);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function consoleOpened(hud)
|
||||
{
|
||||
gWebConsole = hud;
|
||||
gJSTerm = hud.jsterm;
|
||||
gJSTerm.execute("fooObj", onExecuteFooObj);
|
||||
}
|
||||
|
||||
function onExecuteFooObj()
|
||||
{
|
||||
let msg = gWebConsole.outputNode.querySelector(".webconsole-msg-output");
|
||||
ok(msg, "output message found");
|
||||
isnot(msg.textContent.indexOf("[object Object]"), -1, "message text check");
|
||||
|
||||
gJSTerm.once("variablesview-fetched", onFooObjFetch);
|
||||
EventUtils.synthesizeMouse(msg, 2, 2, {}, gWebConsole.iframeWindow)
|
||||
}
|
||||
|
||||
function onFooObjFetch(aEvent, aVar)
|
||||
{
|
||||
gVariablesView = aVar._variablesView;
|
||||
ok(gVariablesView, "variables view object");
|
||||
|
||||
findVariableViewProperties(aVar, [
|
||||
{ name: "testProp", value: "testValue" },
|
||||
], { webconsole: gWebConsole }).then(onTestPropFound);
|
||||
}
|
||||
|
||||
function onTestPropFound(aResults)
|
||||
{
|
||||
let prop = aResults[0].matchedProp;
|
||||
ok(prop, "matched the |testProp| property in the variables view");
|
||||
|
||||
is(content.wrappedJSObject.fooObj.testProp, aResults[0].value,
|
||||
"|fooObj.testProp| value is correct");
|
||||
|
||||
gVariablesView.window.focus();
|
||||
gJSTerm.once("sidebar-closed", onSidebarClosed);
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, gVariablesView.window);
|
||||
}
|
||||
|
||||
function onSidebarClosed()
|
||||
{
|
||||
gJSTerm.clearOutput();
|
||||
gJSTerm.execute("window", onExecuteWindow);
|
||||
}
|
||||
|
||||
function onExecuteWindow()
|
||||
{
|
||||
let msg = gWebConsole.outputNode.querySelector(".webconsole-msg-output");
|
||||
ok(msg, "output message found");
|
||||
isnot(msg.textContent.indexOf("[object Window]"), -1, "message text check");
|
||||
|
||||
gJSTerm.once("variablesview-fetched", onWindowFetch);
|
||||
EventUtils.synthesizeMouse(msg, 2, 2, {}, gWebConsole.iframeWindow)
|
||||
}
|
||||
|
||||
function onWindowFetch(aEvent, aVar)
|
||||
{
|
||||
gVariablesView = aVar._variablesView;
|
||||
ok(gVariablesView, "variables view object");
|
||||
|
||||
findVariableViewProperties(aVar, [
|
||||
{ name: "foo", value: "globalFooBug783499" },
|
||||
], { webconsole: gWebConsole }).then(onFooFound);
|
||||
}
|
||||
|
||||
function onFooFound(aResults)
|
||||
{
|
||||
gVariablesView.window.focus();
|
||||
gJSTerm.once("sidebar-closed", finishTest);
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, gVariablesView.window);
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
// Test the basic features of the Browser Console, bug 587757.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-eval-in-stackframe.html";
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html?" + Date.now();
|
||||
|
||||
function test()
|
||||
{
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Test that makes sure web console eval happens in the user-selected stackframe
|
||||
// from the js debugger.
|
||||
// Check that variables view works as expected in the web console.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-eval-in-stackframe.html";
|
||||
|
||||
|
|
|
@ -869,6 +869,8 @@ function getMessageElementText(aElement)
|
|||
* Properties:
|
||||
* - text: string or RegExp to match the textContent of each new
|
||||
* message.
|
||||
* - noText: string or RegExp that must not match in the message
|
||||
* textContent.
|
||||
* - repeats: the number of message repeats, as displayed by the Web
|
||||
* Console.
|
||||
* - category: match message category. See CATEGORY_* constants at
|
||||
|
@ -877,8 +879,31 @@ function getMessageElementText(aElement)
|
|||
* the top of this file.
|
||||
* - count: how many unique web console messages should be matched by
|
||||
* this rule.
|
||||
* - consoleTrace: boolean, set to |true| to match a console.trace()
|
||||
* message. Optionally this can be an object of the form
|
||||
* { file, fn, line } that can match the specified file, function
|
||||
* and/or line number in the trace message.
|
||||
* - consoleTime: string that matches a console.time() timer name.
|
||||
* Provide this if you want to match a console.time() message.
|
||||
* - consoleTimeEnd: same as above, but for console.timeEnd().
|
||||
* - consoleDir: boolean, set to |true| to match a console.dir()
|
||||
* message.
|
||||
* - longString: boolean, set to |true} to match long strings in the
|
||||
* message.
|
||||
* - objects: boolean, set to |true| if you expect inspectable
|
||||
* objects in the message.
|
||||
* @return object
|
||||
* A Promise object is returned once the messages you want are found.
|
||||
* The promise is resolved with the array of rule objects you give in
|
||||
* the |messages| property. Each objects is the same as provided, with
|
||||
* additional properties:
|
||||
* - matched: a Set of web console messages that matched the rule.
|
||||
* - clickableElements: a list of inspectable objects. This is available
|
||||
* if any of the following properties are present in the rule:
|
||||
* |consoleTrace| or |objects|.
|
||||
* - longStrings: a list of long string ellipsis elements you can click
|
||||
* in the message element, to expand a long string. This is available
|
||||
* only if |longString| is present in the matching rule.
|
||||
*/
|
||||
function waitForMessages(aOptions)
|
||||
{
|
||||
|
|
|
@ -2,12 +2,18 @@
|
|||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Test for bugs 720180 and 800510</title>
|
||||
<title>Test for bugs 720180, 800510 and 865288</title>
|
||||
<script>
|
||||
function testConsole() {
|
||||
console.log("foo repeat"); console.log("foo repeat");
|
||||
console.log("foo repeat"); console.error("foo repeat");
|
||||
}
|
||||
function testConsoleObjects() {
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var o = { id: "abba" + i };
|
||||
console.log("abba", o);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
|
|
|
@ -1062,7 +1062,8 @@ WebConsoleFrame.prototype = {
|
|||
if (!timer) {
|
||||
return;
|
||||
}
|
||||
body = l10n.getFormatStr("timeEnd", [timer.name, timer.duration]);
|
||||
let duration = Math.round(timer.duration * 100) / 100;
|
||||
body = l10n.getFormatStr("timeEnd", [timer.name, duration]);
|
||||
clipboardText = body;
|
||||
break;
|
||||
}
|
||||
|
@ -1097,6 +1098,9 @@ WebConsoleFrame.prototype = {
|
|||
|
||||
if (objectActors.size > 0) {
|
||||
node._objectActors = objectActors;
|
||||
|
||||
let repeatNode = node.querySelector(".webconsole-msg-repeat");
|
||||
repeatNode._uid += [...objectActors].join("-");
|
||||
}
|
||||
|
||||
// Make the node bring up the variables view, to allow the user to inspect
|
||||
|
@ -1105,7 +1109,10 @@ WebConsoleFrame.prototype = {
|
|||
node._stacktrace = aMessage.stacktrace;
|
||||
|
||||
this.makeOutputMessageLink(node, () =>
|
||||
this.jsterm.openVariablesView({ rawObject: node._stacktrace }));
|
||||
this.jsterm.openVariablesView({
|
||||
rawObject: node._stacktrace,
|
||||
autofocus: true,
|
||||
}));
|
||||
}
|
||||
|
||||
return node;
|
||||
|
@ -1136,11 +1143,11 @@ WebConsoleFrame.prototype = {
|
|||
*/
|
||||
_consoleLogClick: function WCF__consoleLogClick(aAnchor, aObjectActor)
|
||||
{
|
||||
let options = {
|
||||
this.jsterm.openVariablesView({
|
||||
label: aAnchor.textContent,
|
||||
objectActor: aObjectActor,
|
||||
};
|
||||
this.jsterm.openVariablesView(options);
|
||||
autofocus: true,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2648,6 +2655,7 @@ function JSTerm(aWebConsoleFrame)
|
|||
this._inputEventHandler = this.inputEventHandler.bind(this);
|
||||
this._fetchVarProperties = this._fetchVarProperties.bind(this);
|
||||
this._fetchVarLongString = this._fetchVarLongString.bind(this);
|
||||
this._onKeypressInVariablesView = this._onKeypressInVariablesView.bind(this);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
@ -3006,6 +3014,8 @@ JSTerm.prototype = {
|
|||
* - targetElement: optional nsIDOMElement to append the variables view
|
||||
* to. An iframe element is used as a container for the view. If this
|
||||
* option is not used, then the variables view opens in the sidebar.
|
||||
* - autofocus: optional boolean, |true| if you want to give focus to
|
||||
* the variables view window after open, |false| otherwise.
|
||||
* @return object
|
||||
* A Promise object that is resolved when the variables view has
|
||||
* opened. The new variables view instance is given to the callbacks.
|
||||
|
@ -3023,10 +3033,15 @@ JSTerm.prototype = {
|
|||
view = this._createVariablesView(viewOptions);
|
||||
if (!aOptions.targetElement) {
|
||||
this._variablesView = view;
|
||||
aWindow.addEventListener("keypress", this._onKeypressInVariablesView);
|
||||
}
|
||||
}
|
||||
aOptions.view = view;
|
||||
this._updateVariablesView(aOptions);
|
||||
|
||||
this.sidebar.show();
|
||||
aOptions.autofocus && aWindow.focus();
|
||||
|
||||
this.emit("variablesview-open", view, aOptions);
|
||||
return view;
|
||||
};
|
||||
|
@ -3048,7 +3063,9 @@ JSTerm.prototype = {
|
|||
aOptions.targetElement.appendChild(iframe);
|
||||
}
|
||||
else {
|
||||
this._createSidebar();
|
||||
if (!this.sidebar) {
|
||||
this._createSidebar();
|
||||
}
|
||||
promise = this._addVariablesViewSidebarTab();
|
||||
}
|
||||
|
||||
|
@ -3063,11 +3080,9 @@ JSTerm.prototype = {
|
|||
*/
|
||||
_createSidebar: function JST__createSidebar()
|
||||
{
|
||||
if (!this.sidebar) {
|
||||
let tabbox = this.hud.document.querySelector("#webconsole-sidebar");
|
||||
let ToolSidebar = devtools.require("devtools/framework/sidebar").ToolSidebar;
|
||||
this.sidebar = new ToolSidebar(tabbox, this);
|
||||
}
|
||||
let tabbox = this.hud.document.querySelector("#webconsole-sidebar");
|
||||
let ToolSidebar = devtools.require("devtools/framework/sidebar").ToolSidebar;
|
||||
this.sidebar = new ToolSidebar(tabbox, this);
|
||||
this.sidebar.show();
|
||||
},
|
||||
|
||||
|
@ -3105,6 +3120,27 @@ JSTerm.prototype = {
|
|||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* The keypress event handler for the Variables View sidebar. Currently this
|
||||
* is used for removing the sidebar when Escape is pressed.
|
||||
*
|
||||
* @private
|
||||
* @param nsIDOMEvent aEvent
|
||||
* The keypress DOM event object.
|
||||
*/
|
||||
_onKeypressInVariablesView: function JST__onKeypressInVariablesView(aEvent)
|
||||
{
|
||||
let tag = aEvent.target.nodeName;
|
||||
if (aEvent.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE || aEvent.shiftKey ||
|
||||
aEvent.altKey || aEvent.ctrlKey || aEvent.metaKey ||
|
||||
["input", "textarea", "select", "textbox"].indexOf(tag) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sidebarDestroy();
|
||||
this.inputNode.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a variables view instance.
|
||||
*
|
||||
|
@ -3762,6 +3798,9 @@ JSTerm.prototype = {
|
|||
this.clearCompletion();
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
else if (this.sidebar) {
|
||||
this._sidebarDestroy();
|
||||
}
|
||||
break;
|
||||
|
||||
case Ci.nsIDOMKeyEvent.DOM_VK_RETURN:
|
||||
|
@ -4167,13 +4206,15 @@ JSTerm.prototype = {
|
|||
this.openVariablesView({
|
||||
label: VariablesView.getString(aResponse.result),
|
||||
objectActor: aResponse.result,
|
||||
autofocus: true,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the JSTerm object. Call this method to avoid memory leaks.
|
||||
* Destroy the sidebar.
|
||||
* @private
|
||||
*/
|
||||
destroy: function JST_destroy()
|
||||
_sidebarDestroy: function JST__sidebarDestroy()
|
||||
{
|
||||
if (this._variablesView) {
|
||||
let actors = this._objectActorsInVariablesViews.get(this._variablesView);
|
||||
|
@ -4185,10 +4226,21 @@ JSTerm.prototype = {
|
|||
}
|
||||
|
||||
if (this.sidebar) {
|
||||
this.sidebar.hide();
|
||||
this.sidebar.destroy();
|
||||
this.sidebar = null;
|
||||
}
|
||||
|
||||
this.emit("sidebar-closed");
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the JSTerm object. Call this method to avoid memory leaks.
|
||||
*/
|
||||
destroy: function JST_destroy()
|
||||
{
|
||||
this._sidebarDestroy();
|
||||
|
||||
this.clearCompletion();
|
||||
this.clearOutput();
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# LOCALIZATION NOTE These strings are used inside the Inspector
|
||||
# which is available from the Web Developer sub-menu -> 'Inspect'.
|
||||
#
|
||||
# The correct localization of this file might be to keep it in
|
||||
# English, or another language commonly spoken among web developers.
|
||||
# You want to make that choice consistent across the developer tools.
|
||||
# A good criteria is the language in which you'd find the best
|
||||
# documentation on web development on the web.
|
||||
|
||||
# LOCALIZATION NOTE (noManifest): Used when an attempt is made to validate a
|
||||
# page using AppCacheUtils.jsm that has no cache manifest.
|
||||
noManifest=The specified page has no manifest.
|
||||
|
||||
# LOCALIZATION NOTE (notUTF8): Used when an attempt is made to validate a
|
||||
# cache manifest using AppCacheUtils.jsm having a character encoding that is not
|
||||
# UTF-8.
|
||||
notUTF8=Manifest has a character encoding of %S. Manifests must have the utf-8 character encoding.
|
||||
|
||||
# LOCALIZATION NOTE (badMimeType): Used when an attempt is made to validate a
|
||||
# cache manifest using AppCacheUtils.jsm having a mimetype that is not
|
||||
# text/cache-manifest.
|
||||
badMimeType=Manifest has a mimetype of %S. Manifests must have a mimetype of text/cache-manifest.
|
||||
|
||||
# LOCALIZATION NOTE (duplicateURI): Used when an attempt is made to validate a
|
||||
# page using AppCacheUtils.jsm that has a cache manifest which references the
|
||||
# same URI from multiple locations.
|
||||
duplicateURI=URI %S is referenced in multiple locations. This is not allowed: %S.
|
||||
|
||||
# LOCALIZATION NOTE (networkBlocksURI): Used when an attempt is made to validate
|
||||
# a page using AppCacheUtils.jsm that has a cache manifest which references the
|
||||
# same URI in the NETWORK section as it does in other sections.
|
||||
networkBlocksURI=NETWORK section line %S (%S) prevents caching of line %S (%S) in the %S section.
|
||||
|
||||
# LOCALIZATION NOTE (fallbackBlocksURI): Used when an attempt is made to
|
||||
# validate a page using AppCacheUtils.jsm that has a cache manifest which
|
||||
# references the same URI in the FALLBACK section as it does in other sections.
|
||||
fallbackBlocksURI=FALLBACK section line %S (%S) prevents caching of line %S (%S) in the %S section.
|
||||
|
||||
# LOCALIZATION NOTE (fileChangedButNotManifest): Used when an attempt is made to
|
||||
# validate a page using AppCacheUtils.jsm that has a cache manifest which
|
||||
# references a URI that has a file modified after the cache manifest.
|
||||
fileChangedButNotManifest=The file %S was modified after %S. Unless the text in the manifest file is changed the cached version will be used instead at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (cacheControlNoStore): Used when an attempt is made to
|
||||
# validate a page using AppCacheUtils.jsm that has a header preventing caching
|
||||
# or storing information.
|
||||
cacheControlNoStore=%S has cache-control set to no-store. This will prevent the application cache from storing the file at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (notAvailable): Used when an attempt is made to validate a
|
||||
# page using AppCacheUtils.jsm that is not available.
|
||||
notAvailable=%S points to a resource that is not available at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (firstLineMustBeCacheManifest): Used when an attempt is made
|
||||
# to validate a cache manifest using AppCacheUtils.jsm having a first line that
|
||||
# is not "CACHE MANIFEST."
|
||||
firstLineMustBeCacheManifest=The first line of the manifest must be "CACHE MANIFEST" at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (cacheManifestOnlyFirstLine): Used when an attempt is made
|
||||
# to validate a cache manifest using AppCacheUtils.jsm having "CACHE MANIFEST"
|
||||
# on a line other than the first line.
|
||||
cacheManifestOnlyFirstLine="CACHE MANIFEST" is only valid on the first line at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (asteriskInWrongSection): Used when an attempt is made
|
||||
# to validate a cache manifest using AppCacheUtils.jsm having an asterisk (*) in
|
||||
# a section other than the network section.
|
||||
asteriskInWrongSection=Asterisk used as a wildcard in the %S section at line %S. A single line containing an asterisk is called the online whitelist wildcard flag and is only valid in the NETWORK section. Other uses of the * character are prohibited. The presence of this flag indicates that any URI not listed as cached is to be implicitly treated as being in the online whitelist namespaces. If the flag is not present then the blocking state indicates that URIs not listed explicitly in the manifest are to be treated as unavailable.
|
||||
|
||||
# LOCALIZATION NOTE (escapeSpaces): Used when an attempt is made to validate a
|
||||
# cache manifest using AppCacheUtils.jsm having a space in a URI. Spaces are to
|
||||
# be replaced with %20.
|
||||
escapeSpaces=Spaces in URIs need to be replaced with %20 at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (slashDotDotSlashBad): Used when an attempt is made to
|
||||
# validate a cache manifest using AppCacheUtils.jsm having a URI containing
|
||||
# /../, which is invalid.
|
||||
slashDotDotSlashBad=/../ is not a valid URI prefix at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (tooManyDotDotSlashes): Used when an attempt is made to
|
||||
# validate a cache manifest using AppCacheUtils.jsm having a URI containing
|
||||
# too many ../ operators. Too many of these operaters mean that the file would
|
||||
# be below the root of the site, which is not possible.
|
||||
tooManyDotDotSlashes=Too many dot dot slash operators (../) at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (fallbackUseSpaces): Used when an attempt is made to
|
||||
# validate a cache manifest using AppCacheUtils.jsm having a FALLBACK section
|
||||
# containing more or less than the standard two URIs seperated by a single
|
||||
# space.
|
||||
fallbackUseSpaces=Only two URIs separated by spaces are allowed in the FALLBACK section at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (fallbackAsterisk): Used when an attempt is made to validate
|
||||
# a cache manifest using AppCacheUtils.jsm having a FALLBACK section that
|
||||
# attempts to use an asterisk (*) as a wildcard. In this section the URI is
|
||||
# simply a path prefix.
|
||||
fallbackAsterisk=Asterisk (*) incorrectly used as a wildcard in a fallback namespace at line %S. Namespaces simply need to match a path prefix.
|
||||
|
||||
# LOCALIZATION NOTE (settingsBadValue): Used when an attempt is made to validate
|
||||
# a cache manifest using AppCacheUtils.jsm having a SETTINGS section containing
|
||||
# something other than the valid "prefer-online" or "fast."
|
||||
settingsBadValue=The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (invalidSectionName): Used when an attempt is made to
|
||||
# validate a cache manifest using AppCacheUtils.jsm containing an invalid
|
||||
# section name.
|
||||
invalidSectionName=Invalid section name (%S) at line %S.
|
||||
|
||||
# LOCALIZATION NOTE (entryNotFound): Used when an attempt is made to view a
|
||||
# cache entry that does not exist.
|
||||
entryNotFound=Entry not found.
|
|
@ -1125,6 +1125,85 @@ paintflashingManual=Draw repainted areas in different colors
|
|||
# tooltip of button in devtools toolbox which toggles paint flashing.
|
||||
paintflashingTooltip=Highlight painted area
|
||||
|
||||
# LOCALIZATION NOTE (appCacheDesc) A very short string used to describe the
|
||||
# function of the "appcache" command
|
||||
appCacheDesc=Application cache utilities
|
||||
|
||||
# LOCALIZATION NOTE (appCacheValidateDesc) A very short string used to describe
|
||||
# the function of the "appcache validate" command.
|
||||
appCacheValidateDesc=Validate cache manifest
|
||||
|
||||
# LOCALIZATION NOTE (appCacheValidateManual) A fuller description of the
|
||||
# 'validate' parameter to the 'appcache' command, displayed when the user asks
|
||||
# for help on what it does.
|
||||
appCacheValidateManual=Find issues relating to a cache manifest and the files that it references
|
||||
|
||||
# LOCALIZATION NOTE (appCacheValidateUriDesc) A very short string used to describe
|
||||
# the function of the "uri" parameter of the appcache validate" command.
|
||||
appCacheValidateUriDesc=URI to check
|
||||
|
||||
# LOCALIZATION NOTE (appCacheValidated) Displayed by the "appcache validate"
|
||||
# command when it has been successfully validated.
|
||||
appCacheValidatedSuccessfully=Appcache validated successfully.
|
||||
|
||||
# LOCALIZATION NOTE (appCacheClearDesc) A very short string used to describe
|
||||
# the function of the "appcache clear" command.
|
||||
appCacheClearDesc=Clear entries from the application cache
|
||||
|
||||
# LOCALIZATION NOTE (appCacheClearManual) A fuller description of the
|
||||
# 'appcache clear' command, displayed when the user asks for help on what it does.
|
||||
appCacheClearManual=Clear one or more entries from the application cache
|
||||
|
||||
# LOCALIZATION NOTE (appCacheClearCleared) Displayed by the "appcache clear"
|
||||
# command when entries are successfully cleared.
|
||||
appCacheClearCleared=Entries cleared successfully.
|
||||
|
||||
# LOCALIZATION NOTE (AppCacheListDesc) A very short string used to describe
|
||||
# the function of the "appcache list" command.
|
||||
appCacheListDesc=Display a list of application cache entries.
|
||||
|
||||
# LOCALIZATION NOTE (AppCacheListManual) A fuller description of the
|
||||
# 'appcache list' command, displayed when the user asks for help on what it does.
|
||||
appCacheListManual=Display a list of all application cache entries. If the search parameter is used then the table displays the entries containing the search term.
|
||||
|
||||
# LOCALIZATION NOTE (AppCacheListSearchDesc) A very short string used to describe
|
||||
# the function of the "search" parameter of the appcache list" command.
|
||||
appCacheListSearchDesc=Filter results using a search term.
|
||||
|
||||
# LOCALIZATION NOTE (AppCacheList*) Row headers for the 'appcache list' command.
|
||||
appCacheListKey=Key:
|
||||
appCacheListDataSize=Data size:
|
||||
appCacheListDeviceID=Device ID:
|
||||
appCacheListExpirationTime=Expires:
|
||||
appCacheListFetchCount=Fetch count:
|
||||
appCacheListLastFetched=Last fetched:
|
||||
appCacheListLastModified=Last modified:
|
||||
|
||||
# LOCALIZATION NOTE (appCacheListViewEntry) The text for the view entry button
|
||||
# of the 'appcache list' command.
|
||||
appCacheListViewEntry=View Entry
|
||||
|
||||
# LOCALIZATION NOTE (appCacheManifestContainsErrors) Displayed by the
|
||||
# "appcache list" command when the manifest is not valid.
|
||||
appCacheManifestContainsErrors=The manifest contains errors. Please run appcache validate and resolve the errors before trying again.
|
||||
|
||||
# LOCALIZATION NOTE (appCacheNoResults) Displayed by the "appcache list" command
|
||||
# when a search returns no results.
|
||||
appCacheNoResults=Your search returned no results.
|
||||
|
||||
# LOCALIZATION NOTE (appCacheViewEntryDesc) A very short string used to describe
|
||||
# the function of the "appcache viewentry" command.
|
||||
appCacheViewEntryDesc=Open a new tab containing the specified cache entry information.
|
||||
|
||||
# LOCALIZATION NOTE (appCacheViewEntryManual) A fuller description of the
|
||||
# 'appcache viewentry' command, displayed when the user asks for help on what it
|
||||
# does.
|
||||
appCacheViewEntryManual=Open a new tab containing the specified cache entry information.
|
||||
|
||||
# LOCALIZATION NOTE (appCacheViewEntryKey) A very short string used to describe
|
||||
# the function of the "key" parameter of the 'appcache viewentry' command.
|
||||
appCacheViewEntryKey=The key for the entry to display.
|
||||
|
||||
# LOCALIZATION NOTE (profilerDesc) A very short string used to describe the
|
||||
# function of the profiler command.
|
||||
profilerDesc=Manage profiler
|
||||
|
@ -1189,4 +1268,4 @@ profilerStopping2=Stopping…
|
|||
|
||||
# LOCALIZATION NOTE (profilerNotReady) A message that is displayed whenever
|
||||
# an operation cannot be completed because the profiler has not been opened yet.
|
||||
profilerNotReady=For this command to work you need to open the profiler first
|
||||
profilerNotReady=For this command to work you need to open the profiler first
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
locale/browser/browser.dtd (%chrome/browser/browser.dtd)
|
||||
locale/browser/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)
|
||||
locale/browser/browser.properties (%chrome/browser/browser.properties)
|
||||
locale/browser/devtools/appcacheutils.properties (%chrome/browser/devtools/appcacheutils.properties)
|
||||
locale/browser/devtools/debugger.dtd (%chrome/browser/devtools/debugger.dtd)
|
||||
locale/browser/devtools/debugger.properties (%chrome/browser/devtools/debugger.properties)
|
||||
locale/browser/devtools/netmonitor.dtd (%chrome/browser/devtools/netmonitor.dtd)
|
||||
|
|
|
@ -119,6 +119,11 @@ this.PermissionsTable = { geolocation: {
|
|||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
mobilenetwork: {
|
||||
app: DENY_ACTION,
|
||||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
power: {
|
||||
app: DENY_ACTION,
|
||||
privileged: DENY_ACTION,
|
||||
|
|
|
@ -180,7 +180,7 @@ ConsoleAPI.prototype = {
|
|||
this._queuedCalls = [];
|
||||
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._window = Cu.getWeakReference(aWindow);
|
||||
this.timerRegistry = {};
|
||||
this.timerRegistry = new Map();
|
||||
|
||||
return contentObj;
|
||||
},
|
||||
|
@ -193,7 +193,7 @@ ConsoleAPI.prototype = {
|
|||
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||
this._windowDestroyed = true;
|
||||
if (!this._timerInitialized) {
|
||||
this.timerRegistry = {};
|
||||
this.timerRegistry.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,12 +209,17 @@ ConsoleAPI.prototype = {
|
|||
*/
|
||||
queueCall: function CA_queueCall(aMethod, aArguments)
|
||||
{
|
||||
let window = this._window.get();
|
||||
let metaForCall = {
|
||||
isPrivate: PrivateBrowsingUtils.isWindowPrivate(this._window.get()),
|
||||
isPrivate: PrivateBrowsingUtils.isWindowPrivate(window),
|
||||
timeStamp: Date.now(),
|
||||
stack: this.getStackTrace(aMethod != "trace" ? 1 : null),
|
||||
};
|
||||
|
||||
if (aMethod == "time" || aMethod == "timeEnd") {
|
||||
metaForCall.monotonicTimer = window.performance.now();
|
||||
}
|
||||
|
||||
this._queuedCalls.push([aMethod, aArguments, metaForCall]);
|
||||
|
||||
if (!this._timerInitialized) {
|
||||
|
@ -239,7 +244,7 @@ ConsoleAPI.prototype = {
|
|||
|
||||
if (this._windowDestroyed) {
|
||||
ConsoleAPIStorage.clearEvents(this._innerID);
|
||||
this.timerRegistry = {};
|
||||
this.timerRegistry.clear();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -293,10 +298,10 @@ ConsoleAPI.prototype = {
|
|||
case "dir":
|
||||
break;
|
||||
case "time":
|
||||
consoleEvent.timer = this.startTimer(args[0], meta.timeStamp);
|
||||
consoleEvent.timer = this.startTimer(args[0], meta.monotonicTimer);
|
||||
break;
|
||||
case "timeEnd":
|
||||
consoleEvent.timer = this.stopTimer(args[0], meta.timeStamp);
|
||||
consoleEvent.timer = this.stopTimer(args[0], meta.monotonicTimer);
|
||||
break;
|
||||
default:
|
||||
// unknown console API method!
|
||||
|
@ -417,10 +422,8 @@ ConsoleAPI.prototype = {
|
|||
},
|
||||
|
||||
/*
|
||||
* A registry of started timers. Timer maps are key-value pairs of timer
|
||||
* names to timer start times, for all timers defined in the page. Timer
|
||||
* names are prepended with the inner window ID in order to avoid conflicts
|
||||
* with Object.prototype functions.
|
||||
* A registry of started timers.
|
||||
* @type Map
|
||||
*/
|
||||
timerRegistry: null,
|
||||
|
||||
|
@ -429,8 +432,9 @@ ConsoleAPI.prototype = {
|
|||
*
|
||||
* @param string aName
|
||||
* The name of the timer.
|
||||
* @param number [aTimestamp=Date.now()]
|
||||
* Optional timestamp that tells when the timer was originally started.
|
||||
* @param number aTimestamp
|
||||
* A monotonic strictly-increasing timing value that tells when the
|
||||
* timer was started.
|
||||
* @return object
|
||||
* The name property holds the timer name and the started property
|
||||
* holds the time the timer was started. In case of error, it returns
|
||||
|
@ -439,16 +443,16 @@ ConsoleAPI.prototype = {
|
|||
**/
|
||||
startTimer: function CA_startTimer(aName, aTimestamp) {
|
||||
if (!aName) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
if (Object.keys(this.timerRegistry).length > MAX_PAGE_TIMERS - 1) {
|
||||
return { error: "maxTimersExceeded" };
|
||||
if (this.timerRegistry.size > MAX_PAGE_TIMERS - 1) {
|
||||
return { error: "maxTimersExceeded" };
|
||||
}
|
||||
let key = this._innerID + "-" + aName.toString();
|
||||
if (!(key in this.timerRegistry)) {
|
||||
this.timerRegistry[key] = aTimestamp || Date.now();
|
||||
let key = aName.toString();
|
||||
if (!this.timerRegistry.has(key)) {
|
||||
this.timerRegistry.set(key, aTimestamp);
|
||||
}
|
||||
return { name: aName, started: this.timerRegistry[key] };
|
||||
return { name: aName, started: this.timerRegistry.get(key) };
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -456,22 +460,23 @@ ConsoleAPI.prototype = {
|
|||
*
|
||||
* @param string aName
|
||||
* The name of the timer.
|
||||
* @param number [aTimestamp=Date.now()]
|
||||
* Optional timestamp that tells when the timer was originally stopped.
|
||||
* @param number aTimestamp
|
||||
* A monotonic strictly-increasing timing value that tells when the
|
||||
* timer was stopped.
|
||||
* @return object
|
||||
* The name property holds the timer name and the duration property
|
||||
* holds the number of milliseconds since the timer was started.
|
||||
**/
|
||||
stopTimer: function CA_stopTimer(aName, aTimestamp) {
|
||||
if (!aName) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
let key = this._innerID + "-" + aName.toString();
|
||||
if (!(key in this.timerRegistry)) {
|
||||
return;
|
||||
let key = aName.toString();
|
||||
if (!this.timerRegistry.has(key)) {
|
||||
return;
|
||||
}
|
||||
let duration = (aTimestamp || Date.now()) - this.timerRegistry[key];
|
||||
delete this.timerRegistry[key];
|
||||
let duration = aTimestamp - this.timerRegistry.get(key);
|
||||
this.timerRegistry.delete(key);
|
||||
return { name: aName, duration: duration };
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1359,7 +1359,8 @@ Navigator::GetMozMobileConnection(nsIDOMMozMobileConnection** aMobileConnection)
|
|||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_TRUE(window, NS_OK);
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
if (!CheckPermission("mobileconnection") &&
|
||||
!CheckPermission("mobilenetwork")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ interface nsIDOMMozMobileCellInfo;
|
|||
interface nsIDOMMozIccManager;
|
||||
interface nsIDOMMozMobileCFInfo;
|
||||
|
||||
[scriptable, builtinclass, uuid(bde7e16c-ff1f-4c7f-b1cd-480984cbb206)]
|
||||
[scriptable, builtinclass, uuid(780de142-562c-4141-bd5c-5413fb1952d2)]
|
||||
interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
||||
{
|
||||
const long ICC_SERVICE_CLASS_VOICE = (1 << 0);
|
||||
|
@ -26,6 +26,13 @@ interface nsIDOMMozMobileConnection : nsIDOMEventTarget
|
|||
const long ICC_SERVICE_CLASS_PAD = (1 << 7);
|
||||
const long ICC_SERVICE_CLASS_MAX = (1 << 7);
|
||||
|
||||
/**
|
||||
* These two fields can be accessed by privileged applications with the
|
||||
* 'mobilenetwork' permission.
|
||||
*/
|
||||
readonly attribute DOMString lastKnownNetwork;
|
||||
readonly attribute DOMString lastKnownHomeNetwork;
|
||||
|
||||
/**
|
||||
* Indicates the state of the device's ICC card.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include "nsIDOMCFStateChangeEvent.h"
|
||||
#include "nsIDOMICCCardLockErrorEvent.h"
|
||||
#include "GeneratedEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsJSUtils.h"
|
||||
|
@ -81,6 +83,7 @@ NS_IMPL_EVENT_HANDLER(MobileConnection, cfstatechange)
|
|||
MobileConnection::MobileConnection()
|
||||
{
|
||||
mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
|
||||
mWindow = nullptr;
|
||||
|
||||
// Not being able to acquire the provider isn't fatal since we check
|
||||
// for it explicitly below.
|
||||
|
@ -88,11 +91,6 @@ MobileConnection::MobileConnection()
|
|||
NS_WARNING("Could not acquire nsIMobileConnectionProvider!");
|
||||
return;
|
||||
}
|
||||
|
||||
mListener = new Listener(this);
|
||||
DebugOnly<nsresult> rv = mProvider->RegisterMobileConnectionMsg(mListener);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
|
||||
"Failed registering mobile connection messages with provider");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -100,8 +98,19 @@ MobileConnection::Init(nsPIDOMWindow* aWindow)
|
|||
{
|
||||
BindToOwner(aWindow);
|
||||
|
||||
mIccManager = new icc::IccManager();
|
||||
mIccManager->Init(aWindow);
|
||||
mWindow = do_GetWeakReference(aWindow);
|
||||
mListener = new Listener(this);
|
||||
|
||||
if (!CheckPermission("mobilenetwork") &&
|
||||
CheckPermission("mobileconnection")) {
|
||||
DebugOnly<nsresult> rv = mProvider->RegisterMobileConnectionMsg(mListener);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
|
||||
"Failed registering mobile connection messages with provider");
|
||||
|
||||
mIccManager = new icc::IccManager();
|
||||
mIccManager->Init(aWindow);
|
||||
printf_stderr("MobileConnection & IccManager initialized");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -122,11 +131,55 @@ MobileConnection::Shutdown()
|
|||
|
||||
// nsIDOMMozMobileConnection
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnection::GetLastKnownNetwork(nsAString& network)
|
||||
{
|
||||
network.SetIsVoid(true);
|
||||
|
||||
if (!CheckPermission("mobilenetwork")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
network = mozilla::Preferences::GetString("ril.lastKnownNetwork");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnection::GetLastKnownHomeNetwork(nsAString& network)
|
||||
{
|
||||
network.SetIsVoid(true);
|
||||
|
||||
if (!CheckPermission("mobilenetwork")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
network = mozilla::Preferences::GetString("ril.lastKnownHomeNetwork");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// All fields below require the "mobileconnection" permission.
|
||||
|
||||
bool
|
||||
MobileConnection::CheckPermission(const char* type)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_TRUE(window, false);
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(permMgr, false);
|
||||
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
permMgr->TestPermissionFromWindow(window, type, &permission);
|
||||
return permission == nsIPermissionManager::ALLOW_ACTION;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnection::GetCardState(nsAString& cardState)
|
||||
{
|
||||
if (!mProvider) {
|
||||
cardState.SetIsVoid(true);
|
||||
cardState.SetIsVoid(true);
|
||||
|
||||
if (!mProvider || !CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
return mProvider->GetCardState(cardState);
|
||||
|
@ -135,8 +188,9 @@ MobileConnection::GetCardState(nsAString& cardState)
|
|||
NS_IMETHODIMP
|
||||
MobileConnection::GetIccInfo(nsIDOMMozMobileICCInfo** aIccInfo)
|
||||
{
|
||||
if (!mProvider) {
|
||||
*aIccInfo = nullptr;
|
||||
*aIccInfo = nullptr;
|
||||
|
||||
if (!mProvider || !CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
return mProvider->GetIccInfo(aIccInfo);
|
||||
|
@ -145,8 +199,9 @@ MobileConnection::GetIccInfo(nsIDOMMozMobileICCInfo** aIccInfo)
|
|||
NS_IMETHODIMP
|
||||
MobileConnection::GetVoice(nsIDOMMozMobileConnectionInfo** voice)
|
||||
{
|
||||
if (!mProvider) {
|
||||
*voice = nullptr;
|
||||
*voice = nullptr;
|
||||
|
||||
if (!mProvider || !CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
return mProvider->GetVoiceConnectionInfo(voice);
|
||||
|
@ -155,8 +210,9 @@ MobileConnection::GetVoice(nsIDOMMozMobileConnectionInfo** voice)
|
|||
NS_IMETHODIMP
|
||||
MobileConnection::GetData(nsIDOMMozMobileConnectionInfo** data)
|
||||
{
|
||||
if (!mProvider) {
|
||||
*data = nullptr;
|
||||
*data = nullptr;
|
||||
|
||||
if (!mProvider || !CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
return mProvider->GetDataConnectionInfo(data);
|
||||
|
@ -165,9 +221,10 @@ MobileConnection::GetData(nsIDOMMozMobileConnectionInfo** data)
|
|||
NS_IMETHODIMP
|
||||
MobileConnection::GetNetworkSelectionMode(nsAString& networkSelectionMode)
|
||||
{
|
||||
if (!mProvider) {
|
||||
networkSelectionMode.SetIsVoid(true);
|
||||
return NS_OK;
|
||||
networkSelectionMode.SetIsVoid(true);
|
||||
|
||||
if (!mProvider || !CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
return mProvider->GetNetworkSelectionMode(networkSelectionMode);
|
||||
}
|
||||
|
@ -175,6 +232,12 @@ MobileConnection::GetNetworkSelectionMode(nsAString& networkSelectionMode)
|
|||
NS_IMETHODIMP
|
||||
MobileConnection::GetIcc(nsIDOMMozIccManager** aIcc)
|
||||
{
|
||||
*aIcc = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(*aIcc = mIccManager);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -184,6 +247,10 @@ MobileConnection::GetNetworks(nsIDOMDOMRequest** request)
|
|||
{
|
||||
*request = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -196,6 +263,10 @@ MobileConnection::SelectNetwork(nsIDOMMozMobileNetworkInfo* network, nsIDOMDOMRe
|
|||
{
|
||||
*request = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -208,6 +279,10 @@ MobileConnection::SelectNetworkAutomatically(nsIDOMDOMRequest** request)
|
|||
{
|
||||
*request = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -220,6 +295,10 @@ MobileConnection::GetCardLock(const nsAString& aLockType, nsIDOMDOMRequest** aDo
|
|||
{
|
||||
*aDomRequest = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -233,6 +312,10 @@ MobileConnection::UnlockCardLock(const JS::Value& aInfo,
|
|||
{
|
||||
*aDomRequest = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -246,6 +329,10 @@ MobileConnection::SetCardLock(const JS::Value& aInfo,
|
|||
{
|
||||
*aDomRequest = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -257,6 +344,10 @@ NS_IMETHODIMP
|
|||
MobileConnection::SendMMI(const nsAString& aMMIString,
|
||||
nsIDOMDOMRequest** request)
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -267,6 +358,10 @@ MobileConnection::SendMMI(const nsAString& aMMIString,
|
|||
NS_IMETHODIMP
|
||||
MobileConnection::CancelMMI(nsIDOMDOMRequest** request)
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -280,6 +375,10 @@ MobileConnection::GetCallForwardingOption(uint16_t aReason,
|
|||
{
|
||||
*aRequest = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -293,6 +392,10 @@ MobileConnection::SetCallForwardingOption(nsIDOMMozMobileCFInfo* aCFInfo,
|
|||
{
|
||||
*aRequest = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -305,6 +408,10 @@ MobileConnection::GetCallWaitingOption(nsIDOMDOMRequest** aRequest)
|
|||
{
|
||||
*aRequest = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -318,6 +425,10 @@ MobileConnection::SetCallWaitingOption(bool aEnabled,
|
|||
{
|
||||
*aRequest = nullptr;
|
||||
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -330,24 +441,40 @@ MobileConnection::SetCallWaitingOption(bool aEnabled,
|
|||
NS_IMETHODIMP
|
||||
MobileConnection::NotifyVoiceChanged()
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return DispatchTrustedEvent(NS_LITERAL_STRING("voicechange"));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnection::NotifyDataChanged()
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return DispatchTrustedEvent(NS_LITERAL_STRING("datachange"));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnection::NotifyCardStateChanged()
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return DispatchTrustedEvent(NS_LITERAL_STRING("cardstatechange"));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MobileConnection::NotifyIccInfoChanged()
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return DispatchTrustedEvent(NS_LITERAL_STRING("iccinfochange"));
|
||||
}
|
||||
|
||||
|
@ -355,6 +482,10 @@ NS_IMETHODIMP
|
|||
MobileConnection::NotifyUssdReceived(const nsAString& aMessage,
|
||||
bool aSessionEnded)
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
NS_NewDOMUSSDReceivedEvent(getter_AddRefs(event), this, nullptr, nullptr);
|
||||
|
||||
|
@ -370,6 +501,10 @@ MobileConnection::NotifyUssdReceived(const nsAString& aMessage,
|
|||
NS_IMETHODIMP
|
||||
MobileConnection::NotifyDataError(const nsAString& aMessage)
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
NS_NewDOMDataErrorEvent(getter_AddRefs(event), this, nullptr, nullptr);
|
||||
|
||||
|
@ -385,6 +520,10 @@ NS_IMETHODIMP
|
|||
MobileConnection::NotifyIccCardLockError(const nsAString& aLockType,
|
||||
uint32_t aRetryCount)
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
NS_NewDOMICCCardLockErrorEvent(getter_AddRefs(event), this, nullptr, nullptr);
|
||||
|
||||
|
@ -405,6 +544,10 @@ MobileConnection::NotifyCFStateChange(bool aSuccess,
|
|||
unsigned short aSeconds,
|
||||
unsigned short aServiceClass)
|
||||
{
|
||||
if (!CheckPermission("mobileconnection")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
NS_NewDOMCFStateChangeEvent(getter_AddRefs(event), this, nullptr, nullptr);
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ private:
|
|||
nsCOMPtr<nsIMobileConnectionProvider> mProvider;
|
||||
nsRefPtr<Listener> mListener;
|
||||
nsRefPtr<icc::IccManager> mIccManager;
|
||||
nsWeakPtr mWindow;
|
||||
|
||||
bool CheckPermission(const char* type);
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
|
|
|
@ -258,7 +258,8 @@ function RadioInterfaceLayer() {
|
|||
};
|
||||
|
||||
try {
|
||||
this.rilContext.voice.lastKnownMcc = Services.prefs.getCharPref("ril.lastKnownMcc");
|
||||
this.rilContext.voice.lastKnownMcc =
|
||||
Services.prefs.getCharPref("ril.lastKnownMcc");
|
||||
} catch (e) {}
|
||||
|
||||
this.voicemailInfo = {
|
||||
|
@ -1080,6 +1081,14 @@ RadioInterfaceLayer.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
// Update lastKnownNetwork
|
||||
if (message.mcc && message.mnc) {
|
||||
try {
|
||||
Services.prefs.setCharPref("ril.lastKnownNetwork",
|
||||
message.mcc + "-" + message.mnc);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
voice.network = message;
|
||||
if (!message.batch) {
|
||||
this._sendMobileConnectionMessage("RIL:VoiceInfoChanged", voice);
|
||||
|
@ -1789,6 +1798,14 @@ RadioInterfaceLayer.prototype = {
|
|||
// when the MCC or MNC codes have changed.
|
||||
this._sendMobileConnectionMessage("RIL:IccInfoChanged", message);
|
||||
|
||||
// Update lastKnownHomeNetwork.
|
||||
if (message.mcc && message.mnc) {
|
||||
try {
|
||||
Services.prefs.setCharPref("ril.lastKnownHomeNetwork",
|
||||
message.mcc + "-" + message.mnc);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// If spn becomes available, we should check roaming again.
|
||||
let oldSpn = oldIccInfo ? oldIccInfo.spn : null;
|
||||
if (!oldSpn && message.spn) {
|
||||
|
@ -2679,7 +2696,14 @@ RadioInterfaceLayer.prototype = {
|
|||
let options = this._fragmentText(message, null, strict7BitEncoding);
|
||||
options.rilMessageType = "sendSMS";
|
||||
options.number = PhoneNumberUtils.normalize(number);
|
||||
options.requestStatusReport = true;
|
||||
let requestStatusReport;
|
||||
try {
|
||||
requestStatusReport =
|
||||
Services.prefs.getBoolPref("dom.sms.requestStatusReport");
|
||||
} catch (e) {
|
||||
requestStatusReport = false;
|
||||
}
|
||||
options.requestStatusReport = requestStatusReport;
|
||||
if (options.segmentMaxSeq > 1) {
|
||||
options.segmentRef16Bit = this.segmentRef16Bit;
|
||||
options.segmentRef = this.nextSegmentRef;
|
||||
|
|
|
@ -497,6 +497,11 @@ bool sScreenEnabled = true;
|
|||
// internal wake locks aren't counted here.)
|
||||
bool sCpuSleepAllowed = true;
|
||||
|
||||
// Some CPU wake locks may be acquired internally in HAL. We use a counter to
|
||||
// keep track of these needs. Note we have to hold |sInternalLockCpuMonitor|
|
||||
// when reading or writing this variable to ensure thread-safe.
|
||||
int32_t sInternalLockCpuCount = 0;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool
|
||||
|
@ -549,6 +554,30 @@ SetScreenBrightness(double brightness)
|
|||
hal::SetLight(hal::eHalLightID_Buttons, aConfig);
|
||||
}
|
||||
|
||||
static Monitor* sInternalLockCpuMonitor = nullptr;
|
||||
|
||||
static void
|
||||
UpdateCpuSleepState()
|
||||
{
|
||||
sInternalLockCpuMonitor->AssertCurrentThreadOwns();
|
||||
bool allowed = sCpuSleepAllowed && !sInternalLockCpuCount;
|
||||
WriteToFile(allowed ? wakeUnlockFilename : wakeLockFilename, "gecko");
|
||||
}
|
||||
|
||||
static void
|
||||
InternalLockCpu() {
|
||||
MonitorAutoLock monitor(*sInternalLockCpuMonitor);
|
||||
++sInternalLockCpuCount;
|
||||
UpdateCpuSleepState();
|
||||
}
|
||||
|
||||
static void
|
||||
InternalUnlockCpu() {
|
||||
MonitorAutoLock monitor(*sInternalLockCpuMonitor);
|
||||
--sInternalLockCpuCount;
|
||||
UpdateCpuSleepState();
|
||||
}
|
||||
|
||||
bool
|
||||
GetCpuSleepAllowed()
|
||||
{
|
||||
|
@ -558,8 +587,9 @@ GetCpuSleepAllowed()
|
|||
void
|
||||
SetCpuSleepAllowed(bool aAllowed)
|
||||
{
|
||||
WriteToFile(aAllowed ? wakeUnlockFilename : wakeLockFilename, "gecko");
|
||||
MonitorAutoLock monitor(*sInternalLockCpuMonitor);
|
||||
sCpuSleepAllowed = aAllowed;
|
||||
UpdateCpuSleepState();
|
||||
}
|
||||
|
||||
static light_device_t* sLights[hal::eHalLightID_Count]; // will be initialized to NULL
|
||||
|
@ -732,7 +762,7 @@ SetTimezone(const nsCString& aTimezoneSpec)
|
|||
|
||||
int32_t oldTimezoneOffsetMinutes = GetTimezoneOffset();
|
||||
property_set("persist.sys.timezone", aTimezoneSpec.get());
|
||||
// this function is automatically called by the other time conversion
|
||||
// This function is automatically called by the other time conversion
|
||||
// functions that depend on the timezone. To be safe, we call it manually.
|
||||
tzset();
|
||||
int32_t newTimezoneOffsetMinutes = GetTimezoneOffset();
|
||||
|
@ -799,38 +829,41 @@ UnlockScreenOrientation()
|
|||
OrientationObserver::GetInstance()->UnlockScreenOrientation();
|
||||
}
|
||||
|
||||
|
||||
// This thread will wait for the alarm firing by a blocking IO.
|
||||
static pthread_t sAlarmFireWatcherThread;
|
||||
|
||||
// If |sAlarmData| is non-null, it's owned by the watcher thread.
|
||||
typedef struct AlarmData {
|
||||
|
||||
// If |sAlarmData| is non-null, it's owned by the alarm-watcher thread.
|
||||
struct AlarmData {
|
||||
public:
|
||||
AlarmData(int aFd) : mFd(aFd), mGeneration(sNextGeneration++), mShuttingDown(false) {}
|
||||
AlarmData(int aFd) : mFd(aFd),
|
||||
mGeneration(sNextGeneration++),
|
||||
mShuttingDown(false) {}
|
||||
ScopedClose mFd;
|
||||
int mGeneration;
|
||||
bool mShuttingDown;
|
||||
|
||||
static int sNextGeneration;
|
||||
|
||||
} AlarmData;
|
||||
};
|
||||
|
||||
int AlarmData::sNextGeneration = 0;
|
||||
|
||||
AlarmData* sAlarmData = NULL;
|
||||
|
||||
class AlarmFiredEvent : public nsRunnable {
|
||||
|
||||
public:
|
||||
AlarmFiredEvent(int aGeneration) : mGeneration(aGeneration) {}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
// Guard against spurious notifications caused by an alarm firing
|
||||
// concurrently with it being disabled.
|
||||
if (sAlarmData && !sAlarmData->mShuttingDown && mGeneration == sAlarmData->mGeneration) {
|
||||
if (sAlarmData && !sAlarmData->mShuttingDown &&
|
||||
mGeneration == sAlarmData->mGeneration) {
|
||||
hal::NotifyAlarmFired();
|
||||
}
|
||||
|
||||
// The fired alarm event has been delivered to the observer (if needed);
|
||||
// we can now release a CPU wake lock.
|
||||
InternalUnlockCpu();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -871,11 +904,18 @@ WaitForAlarm(void* aData)
|
|||
// while awaiting an alarm to be programmed.
|
||||
do {
|
||||
alarmTypeFlags = ioctl(alarmData->mFd, ANDROID_ALARM_WAIT);
|
||||
} while (alarmTypeFlags < 0 && errno == EINTR && !alarmData->mShuttingDown);
|
||||
} while (alarmTypeFlags < 0 && errno == EINTR &&
|
||||
!alarmData->mShuttingDown);
|
||||
|
||||
if (!alarmData->mShuttingDown &&
|
||||
alarmTypeFlags >= 0 && (alarmTypeFlags & ANDROID_ALARM_RTC_WAKEUP_MASK)) {
|
||||
NS_DispatchToMainThread(new AlarmFiredEvent(alarmData->mGeneration));
|
||||
if (!alarmData->mShuttingDown && alarmTypeFlags >= 0 &&
|
||||
(alarmTypeFlags & ANDROID_ALARM_RTC_WAKEUP_MASK)) {
|
||||
// To make sure the observer can get the alarm firing notification
|
||||
// *on time* (the system won't sleep during the process in any way),
|
||||
// we need to acquire a CPU wake lock before firing the alarm event.
|
||||
InternalLockCpu();
|
||||
nsRefPtr<AlarmFiredEvent> event =
|
||||
new AlarmFiredEvent(alarmData->mGeneration);
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -910,10 +950,15 @@ EnableAlarm()
|
|||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
int status = pthread_create(&sAlarmFireWatcherThread, &attr, WaitForAlarm, alarmData.get());
|
||||
// Initialize the monitor for internally locking CPU to ensure thread-safe
|
||||
// before running the alarm-watcher thread.
|
||||
sInternalLockCpuMonitor = new Monitor("sInternalLockCpuMonitor");
|
||||
int status = pthread_create(&sAlarmFireWatcherThread, &attr, WaitForAlarm,
|
||||
alarmData.get());
|
||||
if (status) {
|
||||
alarmData = NULL;
|
||||
HAL_LOG(("Failed to create alarm watcher thread. Status: %d.", status));
|
||||
delete sInternalLockCpuMonitor;
|
||||
HAL_LOG(("Failed to create alarm-watcher thread. Status: %d.", status));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -936,6 +981,8 @@ DisableAlarm()
|
|||
// data pointed at by sAlarmData.
|
||||
DebugOnly<int> err = pthread_kill(sAlarmFireWatcherThread, SIGUSR1);
|
||||
MOZ_ASSERT(!err);
|
||||
|
||||
delete sInternalLockCpuMonitor;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -950,8 +997,9 @@ SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
|
|||
ts.tv_sec = aSeconds;
|
||||
ts.tv_nsec = aNanoseconds;
|
||||
|
||||
// currently we only support RTC wakeup alarm type
|
||||
const int result = ioctl(sAlarmData->mFd, ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP), &ts);
|
||||
// Currently we only support RTC wakeup alarm type.
|
||||
const int result = ioctl(sAlarmData->mFd,
|
||||
ANDROID_ALARM_SET(ANDROID_ALARM_RTC_WAKEUP), &ts);
|
||||
|
||||
if (result < 0) {
|
||||
HAL_LOG(("Unable to set alarm: %s.", strerror(errno)));
|
||||
|
|
|
@ -4068,6 +4068,7 @@ pref("dom.sms.enabled", false);
|
|||
// Enable Latin characters replacement with corresponding ones in GSM SMS
|
||||
// 7-bit default alphabet.
|
||||
pref("dom.sms.strict7BitEncoding", false);
|
||||
pref("dom.sms.requestStatusReport", false);
|
||||
|
||||
// WebContacts
|
||||
pref("dom.mozContacts.enabled", false);
|
||||
|
|
|
@ -1590,7 +1590,20 @@ this.JSTermHelpers = function JSTermHelpers(aOwner)
|
|||
if (!window) {
|
||||
return null;
|
||||
}
|
||||
let target = devtools.TargetFactory.forTab(window.gBrowser.selectedTab);
|
||||
|
||||
let target = null;
|
||||
try {
|
||||
target = devtools.TargetFactory.forTab(window.gBrowser.selectedTab);
|
||||
}
|
||||
catch (ex) {
|
||||
// If we report this exception the user will get it in the Browser
|
||||
// Console every time when she evaluates any string.
|
||||
}
|
||||
|
||||
if (!target) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
let panel = toolbox ? toolbox.getPanel("inspector") : null;
|
||||
let node = panel ? panel.selection.node : null;
|
||||
|
|
|
@ -412,6 +412,8 @@ function UpdateParser(aId, aUpdateKey, aUrl, aObserver) {
|
|||
this.request.open("GET", this.url, true);
|
||||
this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
|
||||
this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to cache.
|
||||
this.request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
this.request.overrideMimeType("text/xml");
|
||||
var self = this;
|
||||
this.request.addEventListener("load", function loadEventListener(event) { self.onLoad() }, false);
|
||||
|
|
Загрузка…
Ссылка в новой задаче