This commit is contained in:
Nick Alexander 2014-01-07 15:22:58 -08:00
Родитель 5e15d1adc9 3a5c8b3e9c
Коммит 00a971da6b
773 изменённых файлов: 14114 добавлений и 10499 удалений

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

@ -878,3 +878,13 @@ pref("identity.fxaccounts.auth.uri", "https://api-accounts.dev.lcip.org/v1");
// Gaia relies heavily on scroll events for now, so lets fire them
// more often than the default value (100).
pref("apz.asyncscroll.throttle", 40);
// This preference allows FirefoxOS apps (and content, I think) to force
// the use of software (instead of hardware accelerated) 2D canvases by
// creating a context like this:
//
// canvas.getContext('2d', { willReadFrequently: true })
//
// Using a software canvas can save memory when JS calls getImageData()
// on the canvas frequently. See bug 884226.
pref("gfx.canvas.willReadFrequently.enable", true);

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

@ -8,6 +8,8 @@ let Cu = Components.utils;
let Cc = Components.classes;
let Ci = Components.interfaces;
dump("############ ErrorPage.js\n");
let ErrorPageHandler = {
_reload: function() {
docShell.QueryInterface(Ci.nsIWebNavigation).reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
@ -31,12 +33,8 @@ let ErrorPageHandler = {
}
},
domContentLoadedHandler: function(e) {
let target = e.originalTarget;
let targetDocShell = target.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation);
if (targetDocShell != docShell) {
_bindPageEvent: function(target) {
if (!target) {
return;
}
@ -52,11 +50,23 @@ let ErrorPageHandler = {
}
},
domContentLoadedHandler: function(e) {
let target = e.originalTarget;
let targetDocShell = target.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation);
if (targetDocShell != docShell) {
return;
}
this._bindPageEvent(target);
},
init: function() {
addMessageListener("ErrorPage:ReloadPage", this._reload.bind(this));
addEventListener('DOMContentLoaded',
this.domContentLoadedHandler.bind(this),
true);
this._bindPageEvent(content.document);
}
};

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

@ -149,6 +149,26 @@ let ErrorPage = {
}
},
_listenError: function(frameLoader) {
let self = this;
let frameElement = frameLoader.ownerElement;
let injectErrorPageScript = function() {
let mm = frameLoader.messageManager;
try {
mm.loadFrameScript(kErrorPageFrameScript, true, true);
} catch (e) {
dump('Error loading ' + kErrorPageFrameScript + ' as frame script: ' + e + '\n');
}
mm.addMessageListener('ErrorPage:AddCertException', self._addCertException.bind(self));
frameElement.removeEventListener('mozbrowsererror', injectErrorPageScript, true);
};
frameElement.addEventListener('mozbrowsererror',
injectErrorPageScript,
true // use capture
);
},
init: function errorPageInit() {
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false);
Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
@ -156,17 +176,7 @@ let ErrorPage = {
observe: function errorPageObserve(aSubject, aTopic, aData) {
let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
let mm = frameLoader.messageManager;
// This won't happen from dom/ipc/preload.js in non-OOP builds.
try {
if (Services.prefs.getBoolPref("dom.ipc.tabs.disabled") === true) {
mm.loadFrameScript(kErrorPageFrameScript, true, true);
}
} catch (e) {
dump('Error loading ' + kErrorPageFrameScript + ' as frame script: ' + e + '\n');
}
mm.addMessageListener('ErrorPage:AddCertException', this._addCertException.bind(this));
this._listenError(frameLoader);
}
};

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

@ -1,4 +1,4 @@
{
"revision": "ac80bbe24c39a813ab65b98bb56dce2e42207b22",
"revision": "d5d2b5614c32e17c5c3c736181a66bfa3fc5c3ef",
"repo_path": "/integration/gaia-central"
}

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

@ -202,16 +202,8 @@
align="start"
orient="vertical"
role="alert">
<hbox>
<vbox>
<image id="UITourTooltipIcon"/>
</vbox>
<vbox flex="1">
<label id="UITourTooltipTitle" flex="1"/>
<description id="UITourTooltipDescription" flex="1"/>
<hbox id="UITourTooltipButtons" flex="1" align="end"/>
</vbox>
</hbox>
<label id="UITourTooltipTitle" flex="1"/>
<description id="UITourTooltipDescription" flex="1"/>
</panel>
<panel id="UITourHighlightContainer"
hidden="true"

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

@ -108,9 +108,9 @@ skip-if = toolkit == "gtk2" || toolkit == "gtk3" # browser_CTP_context_menu.js
run-if = crashreporter
[browser_CTP_data_urls.js]
[browser_CTP_drag_drop.js]
[browser_CTP_hideBar.js]
[browser_CTP_multi_allow.js]
[browser_CTP_nonplugins.js]
[browser_CTP_notificationBar.js]
[browser_CTP_resize.js]
[browser_URLBarSetURI.js]
[browser_aboutAccounts.js]

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

@ -1,98 +0,0 @@
var rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir;
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
var gTestBrowser = null;
var gNextTest = null;
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions();
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
});
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_small.html");
}
function finishTest() {
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() {
// The plugin events are async dispatched and can come after the load event
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Test that the notification bar is getting dismissed when directly activating plugins
// via the doorhanger.
function test1() {
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
test2,
"Test 1, expected a notification bar for hidden plugins");
}
function test2() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 2, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 2, Found plugin in page");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 2, Plugin should be click-to-play");
// simulate "always allow"
notification.reshow();
PopupNotifications.panel.firstChild._primaryButton.click();
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
test3,
"Test 2, expected the notification bar for hidden plugins to get dismissed");
}
function test3() {
let plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 3, Found plugin in page");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
waitForCondition(() => objLoadingContent.activated, finishTest,
"Test 3, Waited too long for plugin to activate");
}

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

@ -0,0 +1,179 @@
var rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir;
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
var gTestBrowser = null;
var gNextTest = null;
Components.utils.import("resource://gre/modules/Services.jsm");
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
clearAllPluginPermissions();
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
});
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
gTestBrowser = gBrowser.selectedBrowser;
gTestBrowser.addEventListener("load", pageLoad, true);
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_small.html");
}
function finishTest() {
gTestBrowser.removeEventListener("load", pageLoad, true);
gBrowser.removeCurrentTab();
window.focus();
finish();
}
function pageLoad() {
// The plugin events are async dispatched and can come after the load event
// This just allows the events to fire before we then go on to test the states
executeSoon(gNextTest);
}
function prepareTest(nextTest, url) {
gNextTest = nextTest;
gTestBrowser.contentWindow.location = url;
}
// Due to layout being async, "PluginBindAttached" may trigger later.
// This wraps a function to force a layout flush, thus triggering it,
// and schedules the function execution so they're definitely executed
// afterwards.
function runAfterPluginBindingAttached(func) {
return function() {
let doc = gTestBrowser.contentDocument;
let elems = doc.getElementsByTagName('embed');
if (elems.length < 1) {
elems = doc.getElementsByTagName('object');
}
elems[0].clientTop;
executeSoon(func);
};
}
// Tests for the notification bar for hidden plugins.
function test1() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 1: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
() => {
// Don't use setTestPluginEnabledState here because we already saved the
// prior value
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
prepareTest(test2, gTestRoot + "plugin_small.html");
},
"Test 1, expected to have a plugin notification bar");
}
function test2() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 2: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
() => {
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
prepareTest(test3, gTestRoot + "plugin_overlayed.html");
},
"Test 2, expected to not have a plugin notification bar");
}
function test3() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 3: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
test3b,
"Test 3, expected the plugin infobar to be triggered when plugin was overlayed");
}
function test3b()
{
let doc = gTestBrowser.contentDocument;
let plugin = doc.getElementById("test");
ok(plugin, "Test 3b, Found plugin in page");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 3b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
ok(!plugin.activated, "Test 3b, Plugin should not be activated");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(!overlay.classList.contains("visible"), "Test 3b, Plugin overlay should be hidden");
prepareTest(test4, gTestRoot + "plugin_positioned.html");
}
function test4() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 4: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
test4b,
"Test 4, expected the plugin infobar to be triggered when plugin was overlayed");
}
function test4b() {
let doc = gTestBrowser.contentDocument;
let plugin = doc.getElementById("test");
ok(plugin, "Test 4b, Found plugin in page");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 4b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
ok(!plugin.activated, "Test 4b, Plugin should not be activated");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(!overlay.classList.contains("visible"), "Test 4b, Plugin overlay should be hidden");
prepareTest(runAfterPluginBindingAttached(test5), gHttpTestRoot + "plugin_small.html");
}
// Test that the notification bar is getting dismissed when directly activating plugins
// via the doorhanger.
function test5() {
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
test6,
"Test 5, expected a notification bar for hidden plugins");
}
function test6() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 6, Should have a click-to-play notification");
let plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 6, Found plugin in page");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"Test 6, Plugin should be click-to-play");
// simulate "always allow"
notification.reshow();
PopupNotifications.panel.firstChild._primaryButton.click();
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
test7,
"Test 6, expected the notification bar for hidden plugins to get dismissed");
}
function test7() {
let plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 7, Found plugin in page");
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
waitForCondition(() => objLoadingContent.activated, finishTest,
"Test 7, Waited too long for plugin to activate");
}

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

@ -844,82 +844,5 @@ function test25() {
ok(notification, "Test 25: There should be a plugin notification even if the plugin was immediately removed");
ok(notification.dismissed, "Test 25: The notification should be dismissed by default");
prepareTest(() => executeSoon(test26), gTestRoot + "plugin_small.html");
}
function test26() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 26: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
() => {
// Don't use setTestPluginEnabledState here because we already saved the
// prior value
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
prepareTest(test27, gTestRoot + "plugin_small.html");
},
"Test 26, expected to have a plugin notification bar");
}
function test27() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 27: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
() => {
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
prepareTest(test28, gTestRoot + "plugin_overlayed.html");
},
"Test 27, expected to not have a plugin notification bar");
}
function test28() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 28: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
test28b,
"Test 28, expected the plugin infobar to be triggered when plugin was overlayed");
}
function test28b()
{
let doc = gTestBrowser.contentDocument;
let plugin = doc.getElementById("test");
ok(plugin, "Test 28b, Found plugin in page");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 28b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
ok(!plugin.activated, "Test 28b, Plugin should not be activated");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(!overlay.classList.contains("visible"), "Test 28b, Plugin overlay should be hidden");
prepareTest(test29, gTestRoot + "plugin_positioned.html");
}
function test29() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 29: There should be a plugin notification");
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
test29b,
"Test 29, expected the plugin infobar to be triggered when plugin was overlayed");
}
function test29b() {
let doc = gTestBrowser.contentDocument;
let plugin = doc.getElementById("test");
ok(plugin, "Test 29b, Found plugin in page");
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 29b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
ok(!plugin.activated, "Test 29b, Plugin should not be activated");
let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
ok(!overlay.classList.contains("visible"), "Test 29b, Plugin overlay should be hidden");
finishTest();
}

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

@ -301,18 +301,13 @@ let DocShellCapabilitiesListener = {
_latestCapabilities: "",
init: function () {
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
gFrameTree.addObserver(this);
},
/**
* onLocationChange() is called as soon as we start loading a page after
* we are certain that there's nothing blocking the load (e.g. a content
* policy added by AdBlock or the like).
* onFrameTreeReset() is called as soon as we start loading a page.
*/
onLocationChange: function() {
onFrameTreeReset: function() {
// The order of docShell capabilities cannot change while we're running
// so calling join() without sorting before is totally sufficient.
let caps = DocShellCapabilities.collect(docShell).join(",");
@ -322,15 +317,7 @@ let DocShellCapabilitiesListener = {
this._latestCapabilities = caps;
MessageQueue.push("disallow", () => caps || null);
}
},
onStateChange: function () {},
onProgressChange: function () {},
onStatusChange: function () {},
onSecurityChange: function () {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
}
};
/**
@ -346,12 +333,12 @@ let SessionStorageListener = {
init: function () {
addEventListener("MozStorageChanged", this);
Services.obs.addObserver(this, "browser:purge-domain-data", true);
Services.obs.addObserver(this, "browser:purge-session-history", true);
gFrameTree.addObserver(this);
},
handleEvent: function (event) {
// Ignore events triggered by localStorage or globalStorage changes.
if (isSessionStorageEvent(event)) {
if (gFrameTree.contains(event.target) && isSessionStorageEvent(event)) {
this.collect();
}
},
@ -363,7 +350,17 @@ let SessionStorageListener = {
},
collect: function () {
MessageQueue.push("storage", () => SessionStorage.collect(docShell));
if (docShell) {
MessageQueue.push("storage", () => SessionStorage.collect(docShell, gFrameTree));
}
},
onFrameTreeCollected: function () {
this.collect();
},
onFrameTreeReset: function () {
this.collect();
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,

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

@ -11,7 +11,7 @@ const Ci = Components.interfaces;
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
const EXPORTED_METHODS = ["addObserver", "contains", "map"];
const EXPORTED_METHODS = ["addObserver", "contains", "map", "forEach"];
/**
* A FrameTree represents all frames that were reachable when the document
@ -161,6 +161,34 @@ FrameTreeInternal.prototype = {
return walk(this.content);
},
/**
* Applies the given function |cb| to all frames stored in the tree. Use this
* method if |map()| doesn't suit your needs and you want more control over
* how data is collected.
*
* @param cb (function)
* This callback receives the current frame as the only argument.
*/
forEach: function (cb) {
let frames = this._frames;
function walk(frame) {
cb(frame);
if (!frames.has(frame)) {
return;
}
Array.forEach(frame.frames, subframe => {
if (frames.has(subframe)) {
cb(subframe);
}
});
}
walk(this.content);
},
/**
* Stores a given |frame| and its children in the frame tree.
*

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

@ -36,6 +36,8 @@ Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
"resource://gre/modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
@ -102,7 +104,7 @@ const TaskUtils = {
return promise.then(
null,
function onError(reason) {
Cu.reportError("Uncaught asynchronous error: " + reason + " at\n" + reason.stack);
console.error("Uncaught asynchronous error", reason, "at", reason.stack);
throw reason;
}
);
@ -179,8 +181,7 @@ let SessionFileInternal = {
this._recordTelemetry(msg.telemetry);
} catch (ex) {
TelemetryStopwatch.cancel("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
Cu.reportError("Could not write session state file " + this.path
+ ": " + ex);
console.error("Could not write session state file ", this.path, ex);
}
if (isFinalWrite) {
@ -193,7 +194,7 @@ let SessionFileInternal = {
SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => {
this._recordTelemetry(msg.telemetry);
return msg;
}, Cu.reportError);
}, console.error);
},
createBackupCopy: function (ext) {

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

@ -17,7 +17,8 @@ Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
"resource:///modules/sessionstore/SessionFile.jsm");
@ -309,7 +310,7 @@ let SessionSaverInternal = {
return SessionFile.write(data).then(() => {
this.updateLastSaveTime();
notify(null, "sessionstore-state-write-complete");
}, Cu.reportError);
}, console.error);
},
/**

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

@ -12,20 +12,29 @@ const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
"resource:///modules/sessionstore/PrivacyLevel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
// Returns the principal for a given |frame| contained in a given |docShell|.
function getPrincipalForFrame(docShell, frame) {
let ssm = Services.scriptSecurityManager;
let uri = frame.document.documentURIObject;
return ssm.getDocShellCodebasePrincipal(uri, docShell);
}
this.SessionStorage = Object.freeze({
/**
* Updates all sessionStorage "super cookies"
* @param aDocShell
* @param docShell
* That tab's docshell (containing the sessionStorage)
* @param frameTree
* The docShell's FrameTree instance.
* @return Returns a nested object that will have hosts as keys and per-host
* session storage data as values. For example:
* {"example.com": {"key": "value", "my_number": 123}}
*/
collect: function (aDocShell) {
return SessionStorageInternal.collect(aDocShell);
collect: function (docShell, frameTree) {
return SessionStorageInternal.collect(docShell, frameTree);
},
/**
@ -45,36 +54,40 @@ this.SessionStorage = Object.freeze({
let SessionStorageInternal = {
/**
* Reads all session storage data from the given docShell.
* @param aDocShell
* @param docShell
* A tab's docshell (containing the sessionStorage)
* @param frameTree
* The docShell's FrameTree instance.
* @return Returns a nested object that will have hosts as keys and per-host
* session storage data as values. For example:
* {"example.com": {"key": "value", "my_number": 123}}
*/
collect: function (aDocShell) {
collect: function (docShell, frameTree) {
let data = {};
let webNavigation = aDocShell.QueryInterface(Ci.nsIWebNavigation);
let shistory = webNavigation.sessionHistory;
let visitedOrigins = new Set();
for (let i = 0; shistory && i < shistory.count; i++) {
let principal = History.getPrincipalForEntry(shistory, i, aDocShell);
frameTree.forEach(frame => {
let principal = getPrincipalForFrame(docShell, frame);
if (!principal) {
continue;
return;
}
// Get the root domain of the current history entry
// and use that as a key for the per-host storage data.
let origin = principal.jarPrefix + principal.origin;
if (data.hasOwnProperty(origin)) {
if (visitedOrigins.has(origin)) {
// Don't read a host twice.
continue;
return;
}
let originData = this._readEntry(principal, aDocShell);
// Mark the current origin as visited.
visitedOrigins.add(origin);
let originData = this._readEntry(principal, docShell);
if (Object.keys(originData).length) {
data[origin] = originData;
}
}
});
return Object.keys(data).length ? data : null;
},
@ -89,10 +102,11 @@ let SessionStorageInternal = {
* {"example.com": {"key": "value", "my_number": 123}}
*/
restore: function (aDocShell, aStorageData) {
for (let [host, data] in Iterator(aStorageData)) {
for (let host of Object.keys(aStorageData)) {
let data = aStorageData[host];
let uri = Services.io.newURI(host, null, null);
let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
// There is no need to pass documentURI, it's only used to fill documentURI property of
// domstorage event, which in this case has no consumer. Prevention of events in case
@ -104,7 +118,7 @@ let SessionStorageInternal = {
storage.setItem(key, value);
} catch (e) {
// throws e.g. for URIs that can't have sessionStorage
Cu.reportError(e);
console.error(e);
}
}
}
@ -122,7 +136,7 @@ let SessionStorageInternal = {
let storage;
try {
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
storage = storageManager.getStorage(aPrincipal);
} catch (e) {
// sessionStorage might throw if it's turned off, see bug 458954
@ -142,25 +156,3 @@ let SessionStorageInternal = {
return hostData;
}
};
let History = {
/**
* Returns a given history entry's URI.
* @param aHistory
* That tab's session history
* @param aIndex
* The history entry's index
* @param aDocShell
* That tab's docshell
*/
getPrincipalForEntry: function History_getPrincipalForEntry(aHistory,
aIndex,
aDocShell) {
try {
return Services.scriptSecurityManager.getDocShellCodebasePrincipal(
aHistory.getEntryAtIndex(aIndex, false).URI, aDocShell);
} catch (e) {
// This might throw for some reason.
}
},
};

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

@ -102,6 +102,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager",
XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
"@mozilla.org/base/telemetry;1", "nsITelemetry");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
"resource:///modules/sessionstore/DocShellCapabilities.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Messenger",
@ -959,7 +961,7 @@ let SessionStoreInternal = {
// Let everyone know we're done.
this._deferredInitialized.resolve();
}
}, Cu.reportError);
}, console.error);
};
aWindow.addEventListener("load", onload);
@ -3136,7 +3138,7 @@ let SessionStoreInternal = {
cookie.value, !!cookie.secure, !!cookie.httponly, true,
"expiry" in cookie ? cookie.expiry : MAX_EXPIRY);
}
catch (ex) { Cu.reportError(ex); } // don't let a single cookie stop recovering
catch (ex) { console.error(ex); } // don't let a single cookie stop recovering
}
},

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

@ -12,6 +12,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Messenger",
"resource:///modules/sessionstore/Messenger.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
@ -328,7 +330,7 @@ let TabStateInternal = {
history = syncHandler.collectSessionHistory(includePrivateData);
} catch (e) {
// This may happen if the tab has crashed.
Cu.reportError(e);
console.error(e);
return tabData;
}
@ -364,7 +366,7 @@ let TabStateInternal = {
if (key != "storage" || includePrivateData) {
tabData[key] = data[key];
} else {
tabData.storage = {};
let storage = {};
let isPinned = tab.pinned;
// If we're not allowed to include private data, let's filter out hosts
@ -372,9 +374,13 @@ let TabStateInternal = {
for (let host of Object.keys(data.storage)) {
let isHttps = host.startsWith("https:");
if (PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
tabData.storage[host] = data.storage[host];
storage[host] = data.storage[host];
}
}
if (Object.keys(storage).length) {
tabData.storage = storage;
}
}
}
},

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

@ -43,6 +43,8 @@ Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
"resource:///modules/sessionstore/SessionFile.jsm");
@ -85,7 +87,7 @@ SessionStartup.prototype = {
SessionFile.read().then(
this._onSessionFileRead.bind(this),
Cu.reportError
console.error
);
},

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

@ -20,6 +20,7 @@ support-files =
browser_pageStyle_sample_nested.html
browser_scrollPositions_sample.html
browser_scrollPositions_sample_frameset.html
browser_sessionStorage.html
browser_248970_b_sample.html
browser_339445_sample.html
browser_346337_sample.html

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

@ -59,12 +59,12 @@ add_task(function flush_on_duplicate() {
"sessionStorage data has been flushed when duplicating tabs");
yield promiseTabRestored(tab2);
let {storage} = JSON.parse(ss.getTabState(tab2));
gBrowser.removeTab(tab2)
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, "on-duplicate",
"sessionStorage data has been flushed when duplicating tabs");
gBrowser.removeTab(tab);
gBrowser.removeTab(tab2);
});
/**

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

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>browser_sessionStorage.html</title>
</head>
<body>
<script type="text/javascript;version=1.8">
let isOuter = window == window.top;
let args = window.location.search.slice(1).split("&");
let rand = args[0];
if (isOuter) {
let iframe = document.createElement("iframe");
let isSecure = args.indexOf("secure") > -1;
let scheme = isSecure ? "https" : "http";
iframe.setAttribute("src", scheme + "://example.com" + location.pathname + "?" + rand);
document.body.appendChild(iframe);
}
if (sessionStorage.length === 0) {
sessionStorage.test = (isOuter ? "outer" : "inner") + "-value-" + rand;
}
</script>
</body>
</html>

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

@ -3,35 +3,39 @@
"use strict";
let tmp = {};
Cu.import("resource://gre/modules/Promise.jsm", tmp);
let {Promise} = tmp;
const RAND = Math.random();
const URL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_sessionStorage.html" +
"?" + RAND;
const INITIAL_VALUE = "initial-value-" + Date.now();
const OUTER_VALUE = "outer-value-" + RAND;
const INNER_VALUE = "inner-value-" + RAND;
/**
* This test ensures that setting, modifying and restoring sessionStorage data
* works as expected.
*/
add_task(function session_storage() {
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
let tab = gBrowser.addTab(URL);
let browser = tab.linkedBrowser;
yield promiseBrowserLoaded(browser);
// Flush to make sure chrome received all data.
SyncHandlers.get(browser).flush();
let {storage} = JSON.parse(ss.getTabState(tab));
is(storage["http://example.com"].test, INITIAL_VALUE,
is(storage["http://example.com"].test, INNER_VALUE,
"sessionStorage data for example.com has been serialized correctly");
is(storage["http://mochi.test:8888"].test, INITIAL_VALUE,
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
"sessionStorage data for mochi.test has been serialized correctly");
// Ensure that modifying sessionStore values works.
yield modifySessionStorage(browser, {test: "modified"});
yield modifySessionStorage2(browser, {test: "modified2"});
SyncHandlers.get(browser).flush();
let {storage} = JSON.parse(ss.getTabState(tab));
is(storage["http://example.com"].test, INITIAL_VALUE,
is(storage["http://example.com"].test, "modified2",
"sessionStorage data for example.com has been serialized correctly");
is(storage["http://mochi.test:8888"].test, "modified",
"sessionStorage data for mochi.test has been serialized correctly");
@ -45,22 +49,40 @@ add_task(function session_storage() {
SyncHandlers.get(browser2).flush();
let {storage} = JSON.parse(ss.getTabState(tab2));
is(storage["http://example.com"].test, INITIAL_VALUE,
is(storage["http://example.com"].test, "modified2",
"sessionStorage data for example.com has been duplicated correctly");
is(storage["http://mochi.test:8888"].test, "modified",
"sessionStorage data for mochi.test has been duplicated correctly");
// Ensure that the content script retains restored data
// (by e.g. duplicateTab) and send it along with new data.
yield modifySessionStorage(browser2, {test: "modified2"});
// (by e.g. duplicateTab) and sends it along with new data.
yield modifySessionStorage(browser2, {test: "modified3"});
SyncHandlers.get(browser2).flush();
let {storage} = JSON.parse(ss.getTabState(tab2));
is(storage["http://example.com"].test, INITIAL_VALUE,
is(storage["http://example.com"].test, "modified2",
"sessionStorage data for example.com has been duplicated correctly");
is(storage["http://mochi.test:8888"].test, "modified2",
is(storage["http://mochi.test:8888"].test, "modified3",
"sessionStorage data for mochi.test has been duplicated correctly");
// Check that loading a new URL discards data.
browser2.loadURI("http://mochi.test:8888/");
yield promiseBrowserLoaded(browser2);
SyncHandlers.get(browser2).flush();
let {storage} = JSON.parse(ss.getTabState(tab2));
is(storage["http://mochi.test:8888"].test, "modified3",
"navigating retains correct storage data");
ok(!storage["http://example.com"], "storage data was discarded");
// Check that loading a new URL discards data.
browser2.loadURI("about:mozilla");
yield promiseBrowserLoaded(browser2);
SyncHandlers.get(browser2).flush();
let state = JSON.parse(ss.getTabState(tab2));
ok(!state.hasOwnProperty("storage"), "storage data was discarded");
// Clean up.
gBrowser.removeTab(tab);
gBrowser.removeTab(tab2);
@ -71,10 +93,12 @@ add_task(function session_storage() {
* sessionStorage data collected for tabs.
*/
add_task(function purge_domain() {
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
let tab = gBrowser.addTab(URL);
let browser = tab.linkedBrowser;
yield promiseBrowserLoaded(browser);
yield notifyObservers(browser, "browser:purge-domain-data", "mochi.test");
// Purge data for "mochi.test".
yield purgeDomainData(browser, "mochi.test");
// Flush to make sure chrome received all data.
SyncHandlers.get(browser).flush();
@ -82,56 +106,36 @@ add_task(function purge_domain() {
let {storage} = JSON.parse(ss.getTabState(tab));
ok(!storage["http://mochi.test:8888"],
"sessionStorage data for mochi.test has been purged");
is(storage["http://example.com"].test, INITIAL_VALUE,
is(storage["http://example.com"].test, INNER_VALUE,
"sessionStorage data for example.com has been preserved");
gBrowser.removeTab(tab);
});
/**
* This test ensures that purging session history data also purges data from
* sessionStorage data collected for tabs
*/
add_task(function purge_shistory() {
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
let browser = tab.linkedBrowser;
yield notifyObservers(browser, "browser:purge-session-history");
// Flush to make sure chrome received all data.
SyncHandlers.get(browser).flush();
let {storage} = JSON.parse(ss.getTabState(tab));
ok(!storage["http://example.com"],
"sessionStorage data for example.com has been purged");
is(storage["http://mochi.test:8888"].test, INITIAL_VALUE,
"sessionStorage data for mochi.test has been preserved");
gBrowser.removeTab(tab);
});
/**
* This test ensures that collecting sessionStorage data respects the privacy
* levels as set by the user.
*/
add_task(function respect_privacy_level() {
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
let tab = gBrowser.addTab(URL + "&secure");
yield promiseBrowserLoaded(tab.linkedBrowser);
gBrowser.removeTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, INITIAL_VALUE,
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
"http sessionStorage data has been saved");
is(storage["https://example.com"].test, INITIAL_VALUE,
is(storage["https://example.com"].test, INNER_VALUE,
"https sessionStorage data has been saved");
// Disable saving data for encrypted sites.
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 1);
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
let tab = gBrowser.addTab(URL + "&secure");
yield promiseBrowserLoaded(tab.linkedBrowser);
gBrowser.removeTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, INITIAL_VALUE,
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
"http sessionStorage data has been saved");
ok(!storage["https://example.com"],
"https sessionStorage data has *not* been saved");
@ -140,17 +144,15 @@ add_task(function respect_privacy_level() {
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
// Check that duplicating a tab copies all private data.
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
let tab = gBrowser.addTab(URL + "&secure");
yield promiseBrowserLoaded(tab.linkedBrowser);
let tab2 = gBrowser.duplicateTab(tab);
yield promiseBrowserLoaded(tab2.linkedBrowser);
yield promiseTabRestored(tab2);
gBrowser.removeTab(tab);
// With privacy_level=2 the |tab| shouldn't have any sessionStorage data.
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
ok(!storage["http://example.com"],
"http sessionStorage data has *not* been saved");
ok(!storage["https://example.com"],
"https sessionStorage data has *not* been saved");
ok(!storage, "sessionStorage data has *not* been saved");
// Restore the default privacy level and close the duplicated tab.
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
@ -158,42 +160,26 @@ add_task(function respect_privacy_level() {
// With privacy_level=0 the duplicated |tab2| should persist all data.
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, INITIAL_VALUE,
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
"http sessionStorage data has been saved");
is(storage["https://example.com"].test, INITIAL_VALUE,
is(storage["https://example.com"].test, INNER_VALUE,
"https sessionStorage data has been saved");
});
function createTabWithStorageData(urls) {
return Task.spawn(function task() {
let tab = gBrowser.addTab();
let browser = tab.linkedBrowser;
for (let url of urls) {
browser.loadURI(url);
yield promiseBrowserLoaded(browser);
yield modifySessionStorage(browser, {test: INITIAL_VALUE});
}
throw new Task.Result(tab);
});
}
function waitForStorageEvent(browser) {
return promiseContentMessage(browser, "ss-test:MozStorageChanged");
}
function waitForUpdateMessage(browser) {
return promiseContentMessage(browser, "SessionStore:update");
}
function modifySessionStorage(browser, data) {
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage", data);
return waitForStorageEvent(browser);
}
function notifyObservers(browser, topic, data) {
let msg = {topic: topic, data: data};
browser.messageManager.sendAsyncMessage("ss-test:notifyObservers", msg);
return waitForUpdateMessage(browser);
function modifySessionStorage2(browser, data) {
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage2", data);
return waitForStorageEvent(browser);
}
function purgeDomainData(browser, domain) {
return sendMessage(browser, "ss-test:purgeDomainData", domain);
}

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

@ -33,8 +33,15 @@ addMessageListener("ss-test:modifySessionStorage", function (msg) {
}
});
addMessageListener("ss-test:notifyObservers", function ({data: {topic, data}}) {
Services.obs.notifyObservers(null, topic, data || "");
addMessageListener("ss-test:modifySessionStorage2", function (msg) {
for (let key of Object.keys(msg.data)) {
content.frames[0].sessionStorage[key] = msg.data[key];
}
});
addMessageListener("ss-test:purgeDomainData", function ({data: domain}) {
Services.obs.notifyObservers(null, "browser:purge-domain-data", domain);
content.setTimeout(() => sendAsyncMessage("ss-test:purgeDomainData"));
});
addMessageListener("ss-test:getStyleSheets", function (msg) {

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

@ -61,44 +61,59 @@ ManifestEditor.prototype = {
return this.update();
},
_onEval: function(evalString) {
let manifest = this.manifest;
eval("manifest" + evalString);
_onEval: function(variable, value) {
let parent = this._descend(variable.ownerView.symbolicPath);
try {
parent[variable.name] = JSON.parse(value);
} catch(e) {
Cu.reportError(e);
}
this.update();
},
_onSwitch: function(variable, newName) {
let manifest = this.manifest;
let newSymbolicName = variable.ownerView.symbolicName +
"['" + newName + "']";
if (newSymbolicName == variable.symbolicName) {
if (variable.name == newName) {
return;
}
let evalString = "manifest" + newSymbolicName + " = " +
"manifest" + variable.symbolicName + ";" +
"delete manifest" + variable.symbolicName;
let parent = this._descend(variable.ownerView.symbolicPath);
parent[newName] = parent[variable.name];
delete parent[variable.name];
eval(evalString);
this.update();
},
_onDelete: function(variable) {
let manifest = this.manifest;
let evalString = "delete manifest" + variable.symbolicName;
eval(evalString);
let parent = this._descend(variable.ownerView.symbolicPath);
delete parent[variable.name];
},
_onNew: function(variable, newName, newValue) {
let manifest = this.manifest;
let symbolicName = variable.symbolicName + "['" + newName + "']";
let evalString = "manifest" + symbolicName + " = " + newValue + ";";
eval(evalString);
let parent = this._descend(variable.symbolicPath);
try {
parent[newName] = JSON.parse(newValue);
} catch(e) {
Cu.reportError(e);
}
this.update();
},
/**
* Returns the value located at a given path in the manifest.
* @param path array
* A string for each path component: ["developer", "name"]
*/
_descend: function(path) {
let parent = this.manifest;
while (path.length) {
parent = parent[path.shift()];
}
return parent;
},
update: function() {
this.editor.createHierarchy();
this.editor.rawObject = this.manifest;
this.editor.commitHierarchy();

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

@ -6,7 +6,6 @@ const {Services} = Cu.import("resource://gre/modules/Services.jsm");
const MANIFEST_EDITOR_ENABLED = "devtools.appmanager.manifestEditor.enabled";
let gManifestWindow, gManifestEditor;
function test() {
@ -22,7 +21,9 @@ function test() {
gManifestWindow = getManifestWindow();
gManifestEditor = getProjectsWindow().UI.manifestEditor;
yield changeManifestValue("name", "the best app");
yield changeManifestValueBad("name", "the worst app");
yield addNewManifestProperty("developer", "foo", "bar");
yield addNewManifestPropertyBad("developer", "blob", "bob");
gManifestWindow = null;
gManifestEditor = null;
@ -64,6 +65,33 @@ function changeManifestValue(key, value) {
});
}
function changeManifestValueBad(key, value) {
return Task.spawn(function() {
let propElem = gManifestWindow.document
.querySelector("[id ^= '" + key + "']");
is(propElem.querySelector(".name").value, key,
"Key doesn't match expected value");
let valueElem = propElem.querySelector(".value");
EventUtils.sendMouseEvent({ type: "mousedown" }, valueElem, gManifestWindow);
let valueInput = propElem.querySelector(".element-value-input");
// Leaving out quotes will result in an error, so no change should be made.
valueInput.value = value;
EventUtils.sendKey("RETURN", gManifestWindow);
yield waitForUpdate();
// Elements have all been replaced, re-select them
propElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
valueElem = propElem.querySelector(".value");
isnot(valueElem.value, '"' + value + '"',
"Value was changed, but it should not have been");
isnot(gManifestEditor.manifest[key], value,
"Manifest was changed, but it should not have been");
});
}
function addNewManifestProperty(parent, key, value) {
return Task.spawn(function() {
let parentElem = gManifestWindow.document
@ -103,3 +131,34 @@ function addNewManifestProperty(parent, key, value) {
"Manifest doesn't contain expected value");
});
}
function addNewManifestPropertyBad(parent, key, value) {
return Task.spawn(function() {
let parentElem = gManifestWindow.document
.querySelector("[id ^= '" + parent + "']");
ok(parentElem,
"Found parent element");
let addPropertyElem = parentElem
.querySelector(".variables-view-add-property");
ok(addPropertyElem,
"Found add-property button");
EventUtils.sendMouseEvent({ type: "mousedown" }, addPropertyElem, gManifestWindow);
let nameInput = parentElem.querySelector(".element-name-input");
nameInput.value = key;
EventUtils.sendKey("TAB", gManifestWindow);
let valueInput = parentElem.querySelector(".element-value-input");
// Leaving out quotes will result in an error, so no change should be made.
valueInput.value = value;
EventUtils.sendKey("RETURN", gManifestWindow);
yield waitForUpdate();
let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
ok(!newElem, "Key was added, but it should not have been");
ok(!(key in gManifestEditor.manifest[parent]),
"Manifest contains key, but it should not");
});
}

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

@ -841,7 +841,6 @@ StackFrames.prototype = {
// Start recording any added variables or properties in any scope and
// clear existing scopes to create each one dynamically.
DebuggerView.Variables.createHierarchy();
DebuggerView.Variables.empty();
// If watch expressions evaluation results are available, create a scope
@ -1540,6 +1539,7 @@ Tracer.prototype = {
location: location,
id: this._idCounter++
};
this._stack.push(item);
DebuggerView.Tracer.addTrace({
type: "call",
@ -1598,7 +1598,7 @@ Tracer.prototype = {
getOwnPropertyNames: (callback) => {
callback({
ownPropertyNames: aObject.ownProperties
? Object.keys(aObject.ownProperties)
? Object.keys(aObject.ownProperties)
: []
});
},

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

@ -1088,8 +1088,10 @@ function TracerView() {
DevToolsUtils.makeInfallible(this._onToggleTracing.bind(this));
this._onStartTracing =
DevToolsUtils.makeInfallible(this._onStartTracing.bind(this));
this._onClear = DevToolsUtils.makeInfallible(this._onClear.bind(this));
this._onSelect = DevToolsUtils.makeInfallible(this._onSelect.bind(this));
this._onClear =
DevToolsUtils.makeInfallible(this._onClear.bind(this));
this._onSelect =
DevToolsUtils.makeInfallible(this._onSelect.bind(this));
this._onMouseOver =
DevToolsUtils.makeInfallible(this._onMouseOver.bind(this));
this._onSearch = DevToolsUtils.makeInfallible(this._onSearch.bind(this));
@ -1118,13 +1120,10 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
}
this.widget = new FastListWidget(document.getElementById("tracer-traces"));
this._traceButton.removeAttribute("hidden");
this._tracerTab.removeAttribute("hidden");
this._tracerDeck = document.getElementById("tracer-deck");
this._search = document.getElementById("tracer-search");
this._template = document.getElementsByClassName("trace-item-template")[0];
this._templateItem = this._template.getElementsByClassName("trace-item")[0];
this._templateTypeIcon = this._template.getElementsByClassName("trace-type")[0];
@ -1133,12 +1132,15 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
this.widget.addEventListener("select", this._onSelect, false);
this.widget.addEventListener("mouseover", this._onMouseOver, false);
this.widget.addEventListener("mouseout", this._unhighlightMatchingItems, false);
this._search.addEventListener("input", this._onSearch, false);
this._startTooltip = L10N.getStr("startTracingTooltip");
this._stopTooltip = L10N.getStr("stopTracingTooltip");
this._tracingNotStartedString = L10N.getStr("tracingNotStartedText");
this._noFunctionCallsString = L10N.getStr("noFunctionCallsText");
this._traceButton.setAttribute("tooltiptext", this._startTooltip);
this.emptyText = this._tracingNotStartedString;
},
/**
@ -1171,23 +1173,38 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
/**
* Function invoked either by the "startTracing" command or by
* _onToggleTracing to start execution tracing in the backend.
*
* @return object
* A promise resolved once the tracing has successfully started.
*/
_onStartTracing: function() {
this._tracerDeck.selectedIndex = 0;
this._traceButton.setAttribute("checked", true);
this._traceButton.setAttribute("tooltiptext", this._stopTooltip);
this.empty();
DebuggerController.Tracer.startTracing();
this.emptyText = this._noFunctionCallsString;
let deferred = promise.defer();
DebuggerController.Tracer.startTracing(deferred.resolve);
return deferred.promise;
},
/**
* Function invoked by _onToggleTracing to stop execution tracing in the
* backend.
*
* @return object
* A promise resolved once the tracing has successfully stopped.
*/
_onStopTracing: function() {
this._traceButton.removeAttribute("checked");
this._traceButton.setAttribute("tooltiptext", this._startTooltip);
DebuggerController.Tracer.stopTracing();
this.emptyText = this._tracingNotStartedString;
let deferred = promise.defer();
DebuggerController.Tracer.stopTracing(deferred.resolve);
return deferred.promise;
},
/**
@ -1336,7 +1353,6 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
selectTab: function() {
const tabs = this._tracerTab.parentElement;
tabs.selectedIndex = Array.indexOf(tabs.children, this._tracerTab);
this._tracerDeck.selectedIndex = 0;
},
/**
@ -1856,8 +1872,9 @@ VariableBubbleView.prototype = {
this._tooltip.setVariableContent(objectActor, {
searchPlaceholder: L10N.getStr("emptyPropertiesFilterText"),
searchEnabled: Prefs.variablesSearchboxVisible,
eval: aString => {
DebuggerController.StackFrames.evaluate(aString);
eval: (variable, value) => {
let string = variable.evaluationMacro(variable, value);
DebuggerController.StackFrames.evaluate(string);
DebuggerView.VariableBubble.hideContents();
}
}, {

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

@ -162,7 +162,10 @@ let DebuggerView = {
emptyText: L10N.getStr("emptyVariablesText"),
onlyEnumVisible: Prefs.variablesOnlyEnumVisible,
searchEnabled: Prefs.variablesSearchboxVisible,
eval: DebuggerController.StackFrames.evaluate,
eval: (variable, value) => {
let string = variable.evaluationMacro(variable, value);
DebuggerController.StackFrames.evaluate(string);
},
lazyEmpty: true
});

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

@ -372,36 +372,25 @@
<tabpanel id="callstack-tabpanel">
<vbox id="callstack-list" flex="1"/>
</tabpanel>
<tabpanel id="tracer-tabpanel" flex="1">
<deck id="tracer-deck" selectedIndex="1" flex="1">
<vbox flex="1">
<vbox id="tracer-traces" flex="1">
<hbox class="trace-item-template" hidden="true">
<hbox class="trace-item" align="center" flex="1" crop="end">
<label class="trace-type plain"/>
<label class="trace-name plain" crop="end"/>
</hbox>
</hbox>
</vbox>
<toolbar id="tracer-toolbar" class="devtools-toolbar">
<toolbarbutton id="clear-tracer"
label="&debuggerUI.clearButton;"
tooltiptext="&debuggerUI.clearButton.tooltip;"
command="clearTraces"
class="devtools-toolbarbutton"/>
<textbox id="tracer-search"
class="devtools-searchinput"
flex="1"
type="search"/>
</toolbar>
</vbox>
<vbox id="tracer-message"
flex="1"
align="center"
pack="center">
<description value="&debuggerUI.tracingNotStarted.label;" />
</vbox>
</deck>
<tabpanel id="tracer-tabpanel">
<vbox id="tracer-traces" flex="1"/>
<hbox class="trace-item-template" hidden="true">
<hbox class="trace-item" align="center" flex="1" crop="end">
<label class="trace-type plain"/>
<label class="trace-name plain" crop="end"/>
</hbox>
</hbox>
<toolbar id="tracer-toolbar" class="devtools-toolbar">
<toolbarbutton id="clear-tracer"
label="&debuggerUI.clearButton;"
tooltiptext="&debuggerUI.clearButton.tooltip;"
command="clearTraces"
class="devtools-toolbarbutton"/>
<textbox id="tracer-search"
class="devtools-searchinput"
flex="1"
type="search"/>
</toolbar>
</tabpanel>
</tabpanels>
</tabbox>

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

@ -203,6 +203,7 @@ support-files =
[browser_dbg_tracing-02.js]
[browser_dbg_tracing-03.js]
[browser_dbg_tracing-04.js]
[browser_dbg_tracing-05.js]
[browser_dbg_variables-view-01.js]
[browser_dbg_variables-view-02.js]
[browser_dbg_variables-view-03.js]

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

@ -7,7 +7,7 @@
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
let gTab, gDebuggee, gPanel, gDebugger, gVariables;
let gTab, gDebuggee, gPanel, gDebugger;
function test() {
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
@ -16,7 +16,6 @@ function test() {
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gVariables = gDebugger.DebuggerView.Variables;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(() => startTracing(gPanel))
@ -82,5 +81,4 @@ registerCleanupFunction(function() {
gDebuggee = null;
gPanel = null;
gDebugger = null;
gVariables = null;
});

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

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that text describing the tracing state is correctly displayed.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gTracer, gL10N;
function test() {
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gTracer = gDebugger.DebuggerView.Tracer;
gL10N = gDebugger.L10N;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(testTracingNotStartedText)
.then(() => gTracer._onStartTracing())
.then(testFunctionCallsUnavailableText)
.then(clickButton)
.then(() => waitForClientEvents(aPanel, "traces"))
.then(testNoEmptyText)
.then(() => gTracer._onClear())
.then(testFunctionCallsUnavailableText)
.then(() => gTracer._onStopTracing())
.then(testTracingNotStartedText)
.then(() => gTracer._onClear())
.then(testTracingNotStartedText)
.then(() => {
const deferred = promise.defer();
SpecialPowers.popPrefEnv(deferred.resolve);
return deferred.promise;
})
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
DevToolsUtils.reportException("browser_dbg_tracing-05.js", aError);
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
});
}
function testTracingNotStartedText() {
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
ok(label,
"A label is displayed in the tracer tabpanel.");
is(label.getAttribute("value"), gL10N.getStr("tracingNotStartedText"),
"The correct {{tracingNotStartedText}} is displayed in the tracer tabpanel.");
}
function testFunctionCallsUnavailableText() {
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
ok(label,
"A label is displayed in the tracer tabpanel.");
is(label.getAttribute("value"), gL10N.getStr("noFunctionCallsText"),
"The correct {{noFunctionCallsText}} is displayed in the tracer tabpanel.");
}
function testNoEmptyText() {
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
ok(!label,
"No label should be displayed in the tracer tabpanel.");
}
function clickButton() {
EventUtils.sendMouseEvent({ type: "click" },
gDebuggee.document.querySelector("button"),
gDebuggee);
}
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gTracer = null;
gL10N = null;
});

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

@ -45,6 +45,7 @@ const FastListWidget = module.exports = function FastListWidget(aNode) {
// Delegate some of the associated node's methods to satisfy the interface
// required by MenuContainer instances.
ViewHelpers.delegateWidgetAttributeMethods(this, aNode);
ViewHelpers.delegateWidgetEventMethods(this, aNode);
}
@ -152,18 +153,6 @@ FastListWidget.prototype = {
return this._orderedMenuElementsArray[index];
},
/**
* Returns the value of the named attribute on this container.
*
* @param string name
* The name of the attribute.
* @return string
* The current attribute value.
*/
getAttribute: function(name) {
return this._parent.getAttribute(name);
},
/**
* Adds a new attribute or changes an existing attribute on this container.
*
@ -174,6 +163,10 @@ FastListWidget.prototype = {
*/
setAttribute: function(name, value) {
this._parent.setAttribute(name, value);
if (name == "emptyText") {
this._textWhenEmpty = value;
}
},
/**
@ -184,6 +177,10 @@ FastListWidget.prototype = {
*/
removeAttribute: function(name) {
this._parent.removeAttribute(name);
if (name == "emptyText") {
this._removeEmptyText();
}
},
/**
@ -203,11 +200,51 @@ FastListWidget.prototype = {
boxObject.scrollBy(-this._list.clientWidth, 0);
},
/**
* Sets the text displayed in this container when empty.
* @param string aValue
*/
set _textWhenEmpty(aValue) {
if (this._emptyTextNode) {
this._emptyTextNode.setAttribute("value", aValue);
}
this._emptyTextValue = aValue;
this._showEmptyText();
},
/**
* Creates and appends a label signaling that this container is empty.
*/
_showEmptyText: function() {
if (this._emptyTextNode || !this._emptyTextValue) {
return;
}
let label = this.document.createElement("label");
label.className = "plain fast-list-widget-empty-text";
label.setAttribute("value", this._emptyTextValue);
this._parent.insertBefore(label, this._list);
this._emptyTextNode = label;
},
/**
* Removes the label signaling that this container is empty.
*/
_removeEmptyText: function() {
if (!this._emptyTextNode) {
return;
}
this._parent.removeChild(this._emptyTextNode);
this._emptyTextNode = null;
},
window: null,
document: null,
_parent: null,
_list: null,
_selectedItem: null,
_orderedMenuElementsArray: null,
_itemsByElement: null
_itemsByElement: null,
_emptyTextNode: null,
_emptyTextValue: ""
};

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

@ -295,7 +295,7 @@ SideMenuWidget.prototype = {
},
/**
* Sets the text displayed in this container as a when empty.
* Sets the text displayed in this container when empty.
* @param string aValue
*/
set _textWhenEmpty(aValue) {

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

@ -189,7 +189,7 @@ SimpleListWidget.prototype = {
},
/**
* Sets the text displayed in this container as a when empty.
* Sets the text displayed in this container when empty.
* @param string aValue
*/
set _textWhenEmpty(aValue) {

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

@ -154,21 +154,22 @@ VariablesView.prototype = {
if (!this._store.length) {
return;
}
this._store.length = 0;
this._itemsByElement.clear();
this._prevHierarchy = this._currHierarchy;
this._currHierarchy = new Map(); // Don't clear, this is just simple swapping.
// Check if this empty operation may be executed lazily.
if (this.lazyEmpty && aTimeout > 0) {
this._emptySoon(aTimeout);
return;
}
let list = this._list;
while (list.hasChildNodes()) {
list.firstChild.remove();
while (this._list.hasChildNodes()) {
this._list.firstChild.remove();
}
this._store.length = 0;
this._itemsByElement.clear();
this._appendEmptyNotice();
this._toggleSearchVisibility(false);
},
@ -192,9 +193,6 @@ VariablesView.prototype = {
let prevList = this._list;
let currList = this._list = this.document.createElement("scrollbox");
this._store.length = 0;
this._itemsByElement.clear();
this.window.setTimeout(() => {
prevList.removeEventListener("keypress", this._onViewKeyPress, false);
prevList.removeEventListener("keydown", this._onViewKeyDown, false);
@ -1173,7 +1171,7 @@ VariablesView.getterOrSetterDeleteCallback = function(aItem) {
// Make sure the right getter/setter to value override macro is applied
// to the target object.
aItem.ownerView.eval(aItem.evaluationMacro(aItem, ""));
aItem.ownerView.eval(aItem, "");
return true; // Don't hide the element.
};
@ -2291,12 +2289,42 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
},
/**
* Gets this variable's path to the topmost scope.
* Gets this variable's path to the topmost scope in the form of a string
* meant for use via eval() or a similar approach.
* For example, a symbolic name may look like "arguments['0']['foo']['bar']".
* @return string
*/
get symbolicName() this._symbolicName,
/**
* Gets this variable's symbolic path to the topmost scope.
* @return array
* @see Variable._buildSymbolicPath
*/
get symbolicPath() {
if (this._symbolicPath) {
return this._symbolicPath;
}
this._symbolicPath = this._buildSymbolicPath();
return this._symbolicPath;
},
/**
* Build this variable's path to the topmost scope in form of an array of
* strings, one for each segment of the path.
* For example, a symbolic path may look like ["0", "foo", "bar"].
* @return array
*/
_buildSymbolicPath: function(path = []) {
if (this.name) {
path.unshift(this.name);
if (this.ownerView instanceof Variable) {
return this.ownerView._buildSymbolicPath(path);
}
}
return path;
},
/**
* Returns this variable's value from the descriptor if available.
* @return any
@ -2715,7 +2743,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
if (!this._variablesView.preventDisableOnChange) {
this._disable();
}
this.ownerView.eval(this.evaluationMacro(this, aString));
this.ownerView.eval(this, aString);
}
}, e);
},
@ -2806,6 +2834,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
},
_symbolicName: "",
_symbolicPath: null,
_absoluteName: "",
_initialDescriptor: null,
_separatorLabel: null,
@ -2858,7 +2887,7 @@ Property.prototype["@@iterator"] = function*() {
/**
* Forget everything recorded about added scopes, variables or properties.
* @see VariablesView.createHierarchy
* @see VariablesView.commitHierarchy
*/
VariablesView.prototype.clearHierarchy = function() {
this._prevHierarchy.clear();
@ -2866,17 +2895,13 @@ VariablesView.prototype.clearHierarchy = function() {
};
/**
* Start recording a hierarchy of any added scopes, variables or properties.
* @see VariablesView.commitHierarchy
*/
VariablesView.prototype.createHierarchy = function() {
this._prevHierarchy = this._currHierarchy;
this._currHierarchy = new Map(); // Don't clear, this is just simple swapping.
};
/**
* Briefly flash the variables that changed between the previous and current
* scope/variable/property hierarchies and reopen previously expanded nodes.
* Perform operations on all the VariablesView Scopes, Variables and Properties
* after you've added all the items you wanted.
*
* Calling this method is optional, and does the following:
* - styles the items overridden by other items in parent scopes
* - reopens the items which were previously expanded
* - flashes the items whose values changed
*/
VariablesView.prototype.commitHierarchy = function() {
for (let [, currItem] of this._currHierarchy) {

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

@ -767,7 +767,11 @@ this.WidgetMethods = {
*/
set emptyText(aValue) {
this._emptyText = aValue;
this._widget.setAttribute("emptyText", aValue);
// Apply the emptyText attribute right now if there are no child items.
if (!this._itemsByElement.size) {
this._widget.setAttribute("emptyText", aValue);
}
},
/**

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

@ -20,6 +20,12 @@
overflow-y: auto;
}
/* FastListWidget */
.fast-list-widget-container {
overflow: auto;
}
/* SideMenuWidget */
.side-menu-widget-container {

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

@ -3469,7 +3469,6 @@ JSTerm.prototype = {
_updateVariablesView: function JST__updateVariablesView(aOptions)
{
let view = aOptions.view;
view.createHierarchy();
view.empty();
// We need to avoid pruning the object inspection starting point.
@ -3516,20 +3515,24 @@ JSTerm.prototype = {
* @private
* @param object aOptions
* The options used for |this._updateVariablesView()|.
* @param string aString
* The string that the variables view wants to evaluate.
* @param object aVar
* The Variable object instance for the edited property.
* @param string aValue
* The value the edited property was changed to.
*/
_variablesViewEvaluate: function JST__variablesViewEvaluate(aOptions, aString)
_variablesViewEvaluate:
function JST__variablesViewEvaluate(aOptions, aVar, aValue)
{
let updater = this._updateVariablesView.bind(this, aOptions);
let onEval = this._silentEvalCallback.bind(this, updater);
let string = aVar.evaluationMacro(aVar, aValue);
let evalOptions = {
frame: this.SELECTED_FRAME,
bindObjectActor: aOptions.objectActor.actor,
};
this.requestEvaluation(aString, evalOptions).then(onEval, onEval);
this.requestEvaluation(string, evalOptions).then(onEval, onEval);
},
/**
@ -4444,6 +4447,7 @@ var Utils = {
case "Invalid HSTS Headers":
case "Insecure Password Field":
case "SSL":
case "CORS":
return CATEGORY_SECURITY;
default:

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

@ -333,9 +333,14 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY customizeMenu.addToPanel.label "Add to Menu">
<!ENTITY customizeMenu.addToPanel.accesskey "M">
<!ENTITY customizeMenu.moveToToolbar.label "Move to Toolbar">
<!ENTITY customizeMenu.moveToToolbar.accesskey "M">
<!ENTITY customizeMenu.moveToToolbar.accesskey "o">
<!-- LOCALIZATION NOTE (customizeMenu.moveToPanel.accesskey) can appear on the
same context menu as menubarCmd and personalbarCmd, so they should have
different access keys. customizeMenu.moveToToolbar and
customizeMenu.moveToPanel are mutually exclusive, so can share access
keys. -->
<!ENTITY customizeMenu.moveToPanel.label "Move to Menu">
<!ENTITY customizeMenu.moveToPanel.accesskey "M">
<!ENTITY customizeMenu.moveToPanel.accesskey "o">
<!ENTITY customizeMenu.removeFromToolbar.label "Remove from Toolbar">
<!ENTITY customizeMenu.removeFromToolbar.accesskey "R">
<!ENTITY customizeMenu.removeFromMenu.label "Remove from Menu">

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

@ -50,10 +50,6 @@
- button that toggles all breakpoints for all sources. -->
<!ENTITY debuggerUI.sources.toggleBreakpoints "Enable/disable all breakpoints">
<!-- LOCALIZATION NOTE (debuggerUI.tracingNotStarted.label): This is the text
- displayed when tracing hasn't started in the debugger UI. -->
<!ENTITY debuggerUI.tracingNotStarted.label "Tracing has not started.">
<!-- LOCALIZATION NOTE (debuggerUI.startTracing): This is the text displayed in
- the button to start execution tracing. -->
<!ENTITY debuggerUI.startTracing "Start Tracing">

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

@ -85,6 +85,14 @@ noEventListenersText=No event listeners to display
# when there are no stack frames.
noStackFramesText=No stack frames to display
# LOCALIZATION NOTE (noStackFramesText): The text to display in the traces tab
# when there are no function calls.
noFunctionCallsText=No function calls to display
# LOCALIZATION NOTE (tracingNotStartedText): The text to display in the traces tab
# when when tracing hasn't started yet.
tracingNotStartedText=Tracing has not started
# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when
# the user hovers over the checkbox used to toggle an event breakpoint.
eventCheckboxTooltip=Toggle breaking on this event

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

@ -30,7 +30,8 @@
</content>
<handlers>
<handler event="click" clickcount="1" action="this._onClick()"/>
<handler event="click" button="0" clickcount="1" action="this._onClick()"/>
<handler event="click" button="1" clickcount="1" action="event.stopPropagation(); this._onClose()"/>
<handler event="dblclick" action="this._onDoubleClick(); event.stopPropagation();"/>
</handlers>

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

@ -32,15 +32,6 @@ function testState(aState) {
}
}
function notifyPrecise()
{
Services.obs.notifyObservers(null, "metro_precise_input", null);
}
function notifyImprecise()
{
Services.obs.notifyObservers(null, "metro_imprecise_input", null);
}
gTests.push({
desc: "precise/imprecise input switcher",

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

@ -0,0 +1,114 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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/. */
"use strict";
function test() {
runTests();
}
function setup() {
yield waitForCondition(function () {
return Elements.tabList && Elements.tabList.strip;
});
if (!ContextUI.tabbarVisible) {
ContextUI.displayTabs();
}
}
function tearDown() {
cleanUpOpenedTabs();
}
function isElementVisible(elm) {
return elm.ownerDocument.defaultView.getComputedStyle(elm).getPropertyValue("visibility") == "visible";
}
gTests.push({
desc: "No scrollbuttons when tab strip doesnt require overflow",
setUp: setup,
run: function () {
let tabStrip = Elements.tabList.strip;
let tabs = Elements.tabList.strip.querySelectorAll("documenttab");
// sanity check tab count:
is(tabs.length, 1, "1 tab present");
// test imprecise mode
let imprecisePromise = waitForObserver("metro_imprecise_input");
notifyImprecise();
yield imprecisePromise;
ok(!isElementVisible(tabStrip._scrollButtonUp), "left scrollbutton is hidden in imprecise mode");
ok(!isElementVisible(tabStrip._scrollButtonDown), "right scrollbutton is hidden in imprecise mode");
// test precise mode
let precisePromise = waitForObserver("metro_precise_input");
notifyPrecise();
yield precisePromise;
todo(!isElementVisible(tabStrip._scrollButtonUp), "Bug 952297 - left scrollbutton is hidden in precise mode");
ok(!isElementVisible(tabStrip._scrollButtonDown), "right scrollbutton is hidden in precise mode");
},
tearDown: tearDown
});
gTests.push({
desc: "Scrollbuttons not visible when tabstrip has overflow in imprecise input mode",
setUp: function(){
yield setup();
// ensure we're in imprecise mode
let imprecisePromise = waitForObserver("metro_imprecise_input");
notifyImprecise();
yield imprecisePromise;
},
run: function () {
// add enough tabs to get overflow in the tabstrip
let strip = Elements.tabList.strip;
ok(strip && strip.scrollClientSize && strip.scrollSize, "Sanity check tabstrip strip is present and expected properties available");
while (strip.scrollSize <= strip.scrollClientSize) {
yield addTab("about:mozilla");
}
ok(!isElementVisible(Elements.tabList.strip._scrollButtonUp), "left scrollbutton is hidden in imprecise mode");
ok(!isElementVisible(Elements.tabList.strip._scrollButtonDown), "right scrollbutton is hidden in imprecise mode");
}
});
gTests.push({
desc: "Scrollbuttons become visible when tabstrip has overflow in precise input mode",
setUp: function(){
yield setup();
// ensure we're in precise mode
let precisePromise = waitForObserver("metro_precise_input");
notifyPrecise();
yield precisePromise;
},
run: function () {
let strip = Elements.tabList.strip;
ok(strip && strip.scrollClientSize && strip.scrollSize, "Sanity check tabstrip is present and expected properties available");
// add enough tabs to get overflow in the tabstrip
while (strip.scrollSize <= strip.scrollClientSize) {
yield addTab("about:mozilla");
}
let tabs = Elements.tabList.strip.querySelectorAll("documenttab");
// select the first tab
Elements.tabs.selectedTab = tabs[0];
ok(isElementVisible(Elements.tabList.strip._scrollButtonDown), "right scrollbutton should be visible when tabList has overflow");
todo(!isElementVisible(Elements.tabList.strip._scrollButtonUp), "Bug 952297 - left scrollbutton should not visible when 1st tab is selected and tablist has overflow");
// select the last tab
Elements.tabs.selectedTab = tabs[tabs.length-1];
ok(isElementVisible(Elements.tabList.strip._scrollButtonUp), "left scrollbutton should be visible when tablist has overflow and last tab is selected");
todo(!isElementVisible(Elements.tabList.strip._scrollButtonDown), "Bug 952297 - right scrollbutton should not visible when last tab is selected and tablist has overflow");
}
});

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

@ -556,6 +556,25 @@ function waitForObserver(aObsEvent, aTimeoutMs) {
}
}
/*=============================================================================
* Input mode helpers - these helpers notify observers to metro_precise_input
* and metro_imprecise_input respectively, triggering the same behaviour as user touch or mouse input
*
* Usage: let promise = waitForObservers("metro_imprecise_input");
* notifyImprecise();
* yield promise; // you are now in imprecise mode
*===========================================================================*/
function notifyPrecise()
{
Services.obs.notifyObservers(null, "metro_precise_input", null);
}
function notifyImprecise()
{
Services.obs.notifyObservers(null, "metro_imprecise_input", null);
}
/*=============================================================================
* Native input helpers - these helpers send input directly to the os
* generating os level input events that get processed by widget and

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

@ -51,6 +51,7 @@ support-files =
[browser_remotetabs.js]
[browser_snappedState.js]
[browser_tabs.js]
[browser_tabs_container.js]
[browser_test.js]
[browser_tiles.js]
[browser_topsites.js]

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

@ -22,7 +22,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
const UITOUR_PERMISSION = "uitour";
const PREF_PERM_BRANCH = "browser.uitour.";
const MAX_BUTTONS = 4;
this.UITour = {
@ -126,34 +125,7 @@ this.UITour = {
Cu.reportError("UITour: Target could not be resolved: " + data.target);
return;
}
let iconURL = null;
if (typeof data.icon == "string")
iconURL = this.resolveURL(contentDocument, data.icon);
let buttons = [];
if (Array.isArray(data.buttons) && data.buttons.length > 0) {
for (let buttonData of data.buttons) {
if (typeof buttonData == "object" &&
typeof buttonData.label == "string" &&
typeof buttonData.callbackID == "string") {
let button = {
label: buttonData.label,
callbackID: buttonData.callbackID,
};
if (typeof buttonData.icon == "string")
button.iconURL = this.resolveURL(contentDocument, buttonData.icon);
buttons.push(button);
if (buttons.length == MAX_BUTTONS)
break;
}
}
}
this.showInfo(contentDocument, target, data.title, data.text, iconURL, buttons);
this.showInfo(target, data.title, data.text);
}).then(null, Cu.reportError);
break;
}
@ -331,7 +303,11 @@ this.UITour = {
if (uri.schemeIs("chrome"))
return true;
if (!this.isSafeScheme(uri))
let allowedSchemes = new Set(["https"]);
if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
allowedSchemes.add("http");
if (!allowedSchemes.has(uri.scheme))
return false;
this.importPermissions();
@ -339,50 +315,6 @@ this.UITour = {
return permission == Services.perms.ALLOW_ACTION;
},
isSafeScheme: function(aURI) {
let allowedSchemes = new Set(["https"]);
if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
allowedSchemes.add("http");
if (!allowedSchemes.has(aURI.scheme))
return false;
return true;
},
resolveURL: function(aDocument, aURL) {
try {
let uri = Services.io.newURI(aURL, null, aDocument.documentURIObject);
if (!this.isSafeScheme(uri))
return null;
return uri.spec;
} catch (e) {}
return null;
},
sendPageCallback: function(aDocument, aCallbackID, aData = {}) {
let detail = Cu.createObjectIn(aDocument.defaultView);
detail.data = Cu.createObjectIn(detail);
for (let key of Object.keys(aData))
detail.data[key] = aData[key];
Cu.makeObjectPropsNormal(detail.data);
Cu.makeObjectPropsNormal(detail);
detail.callbackID = aCallbackID;
let event = new aDocument.defaultView.CustomEvent("mozUITourResponse", {
bubbles: true,
detail: detail
});
aDocument.dispatchEvent(event);
},
getTarget: function(aWindow, aTargetName, aSticky = false) {
let deferred = Promise.defer();
if (typeof aTargetName != "string" || !aTargetName) {
@ -574,7 +506,7 @@ this.UITour = {
this._setAppMenuStateForAnnotation(aWindow, "highlight", false);
},
showInfo: function(aContentDocument, aAnchor, aTitle = "", aDescription = "", aIconURL = "", aButtons = []) {
showInfo: function(aAnchor, aTitle, aDescription) {
function showInfoPanel(aAnchorEl) {
aAnchorEl.focus();
@ -582,37 +514,13 @@ this.UITour = {
let tooltip = document.getElementById("UITourTooltip");
let tooltipTitle = document.getElementById("UITourTooltipTitle");
let tooltipDesc = document.getElementById("UITourTooltipDescription");
let tooltipIcon = document.getElementById("UITourTooltipIcon");
let tooltipButtons = document.getElementById("UITourTooltipButtons");
if (tooltip.state == "open") {
tooltip.hidePopup();
}
tooltipTitle.textContent = aTitle || "";
tooltipDesc.textContent = aDescription || "";
tooltipIcon.src = aIconURL || "";
tooltipIcon.hidden = !aIconURL;
while (tooltipButtons.firstChild)
tooltipButtons.firstChild.remove();
for (let button of aButtons) {
let el = document.createElement("button");
el.setAttribute("label", button.label);
if (button.iconURL)
el.setAttribute("image", button.iconURL);
let callbackID = button.callbackID;
el.addEventListener("command", event => {
tooltip.hidePopup();
this.sendPageCallback(aContentDocument, callbackID);
});
tooltipButtons.appendChild(el);
}
tooltipButtons.hidden = !aButtons.length;
tooltipTitle.textContent = aTitle;
tooltipDesc.textContent = aDescription;
tooltip.hidden = false;
let alignment = "bottomcenter topright";
@ -625,15 +533,9 @@ this.UITour = {
},
hideInfo: function(aWindow) {
let document = aWindow.document;
let tooltip = document.getElementById("UITourTooltip");
let tooltip = aWindow.document.getElementById("UITourTooltip");
tooltip.hidePopup();
this._setAppMenuStateForAnnotation(aWindow, "info", false);
let tooltipButtons = document.getElementById("UITourTooltipButtons");
while (tooltipButtons.firstChild)
tooltipButtons.firstChild.remove();
},
showMenu: function(aWindow, aMenuName, aOpenCallback = null) {

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

@ -5,6 +5,6 @@ support-files =
[browser_NetworkPrioritizer.js]
[browser_SignInToWebsite.js]
[browser_UITour.js]
support-files = uitour.* image.png
support-files = uitour.*
[browser_taskbar_preview.js]
run-if = os == "win"

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

@ -4,7 +4,6 @@
"use strict";
let gTestTab;
let gContentWindow;
let gContentAPI;
Components.utils.import("resource:///modules/UITour.jsm");
@ -67,10 +66,10 @@ function loadTestPage(callback, host = "https://example.com/") {
gTestTab.linkedBrowser.addEventListener("load", function onLoad() {
gTestTab.linkedBrowser.removeEventListener("load", onLoad);
gContentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
gContentAPI = gContentWindow.Mozilla.UITour;
let contentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
gContentAPI = contentWindow.Mozilla.UITour;
waitForFocus(callback, gContentWindow);
waitForFocus(callback, contentWindow);
}, true);
}
@ -83,7 +82,6 @@ function test() {
registerCleanupFunction(function() {
delete window.UITour;
delete window.gContentWindow;
delete window.gContentAPI;
if (gTestTab)
gBrowser.removeTab(gTestTab);
@ -287,16 +285,11 @@ let tests = [
let popup = document.getElementById("UITourTooltip");
let title = document.getElementById("UITourTooltipTitle");
let desc = document.getElementById("UITourTooltipDescription");
let icon = document.getElementById("UITourTooltipIcon");
let buttons = document.getElementById("UITourTooltipButtons");
popup.addEventListener("popupshown", function onPopupShown() {
popup.removeEventListener("popupshown", onPopupShown);
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
is(title.textContent, "test title", "Popup should have correct title");
is(desc.textContent, "test text", "Popup should have correct description text");
is(icon.src, "", "Popup should have no icon");
is(buttons.hasChildNodes(), false, "Popup should have no buttons");
popup.addEventListener("popuphidden", function onPopupHidden() {
popup.removeEventListener("popuphidden", onPopupHidden);
@ -318,16 +311,11 @@ let tests = [
let popup = document.getElementById("UITourTooltip");
let title = document.getElementById("UITourTooltipTitle");
let desc = document.getElementById("UITourTooltipDescription");
let icon = document.getElementById("UITourTooltipIcon");
let buttons = document.getElementById("UITourTooltipButtons");
popup.addEventListener("popupshown", function onPopupShown() {
popup.removeEventListener("popupshown", onPopupShown);
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
is(title.textContent, "urlbar title", "Popup should have correct title");
is(desc.textContent, "urlbar text", "Popup should have correct description text");
is(icon.src, "", "Popup should have no icon");
is(buttons.hasChildNodes(), false, "Popup should have no buttons");
gContentAPI.showInfo("search", "search title", "search text");
executeSoon(function() {
@ -389,114 +377,6 @@ let tests = [
}, "Info should be shown after showInfo() for fixed menu panel items");
});
},
function test_info_icon(done) {
let popup = document.getElementById("UITourTooltip");
let title = document.getElementById("UITourTooltipTitle");
let desc = document.getElementById("UITourTooltipDescription");
let icon = document.getElementById("UITourTooltipIcon");
let buttons = document.getElementById("UITourTooltipButtons");
popup.addEventListener("popupshown", function onPopupShown() {
popup.removeEventListener("popupshown", onPopupShown);
is(title.textContent, "a title", "Popup should have correct title");
is(desc.textContent, "some text", "Popup should have correct description text");
let imageURL = getRootDirectory(gTestPath) + "image.png";
imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
is(icon.src, imageURL, "Popup should have correct icon shown");
is(buttons.hasChildNodes(), false, "Popup should have no buttons");
done();
});
gContentAPI.showInfo("urlbar", "a title", "some text", "image.png");
},
function test_info_buttons_1(done) {
let popup = document.getElementById("UITourTooltip");
let title = document.getElementById("UITourTooltipTitle");
let desc = document.getElementById("UITourTooltipDescription");
let icon = document.getElementById("UITourTooltipIcon");
popup.addEventListener("popupshown", function onPopupShown() {
popup.removeEventListener("popupshown", onPopupShown);
is(title.textContent, "another title", "Popup should have correct title");
is(desc.textContent, "moar text", "Popup should have correct description text");
let imageURL = getRootDirectory(gTestPath) + "image.png";
imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
is(icon.src, imageURL, "Popup should have correct icon shown");
let buttons = document.getElementById("UITourTooltipButtons");
is(buttons.childElementCount, 2, "Popup should have two buttons");
is(buttons.childNodes[0].getAttribute("label"), "Button 1", "First button should have correct label");
is(buttons.childNodes[0].getAttribute("image"), "", "First button should have no image");
is(buttons.childNodes[1].getAttribute("label"), "Button 2", "Second button should have correct label");
is(buttons.childNodes[1].getAttribute("image"), imageURL, "Second button should have correct image");
popup.addEventListener("popuphidden", function onPopupHidden() {
popup.removeEventListener("popuphidden", onPopupHidden);
ok(true, "Popup should close automatically");
executeSoon(function() {
is(gContentWindow.callbackResult, "button1", "Correct callback should have been called");
done();
});
});
EventUtils.synthesizeMouseAtCenter(buttons.childNodes[0], {}, window);
});
let buttons = gContentWindow.makeButtons();
gContentAPI.showInfo("urlbar", "another title", "moar text", "./image.png", buttons);
},
function test_info_buttons_2(done) {
let popup = document.getElementById("UITourTooltip");
let title = document.getElementById("UITourTooltipTitle");
let desc = document.getElementById("UITourTooltipDescription");
let icon = document.getElementById("UITourTooltipIcon");
popup.addEventListener("popupshown", function onPopupShown() {
popup.removeEventListener("popupshown", onPopupShown);
is(title.textContent, "another title", "Popup should have correct title");
is(desc.textContent, "moar text", "Popup should have correct description text");
let imageURL = getRootDirectory(gTestPath) + "image.png";
imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
is(icon.src, imageURL, "Popup should have correct icon shown");
let buttons = document.getElementById("UITourTooltipButtons");
is(buttons.childElementCount, 2, "Popup should have two buttons");
is(buttons.childNodes[0].getAttribute("label"), "Button 1", "First button should have correct label");
is(buttons.childNodes[0].getAttribute("image"), "", "First button should have no image");
is(buttons.childNodes[1].getAttribute("label"), "Button 2", "Second button should have correct label");
is(buttons.childNodes[1].getAttribute("image"), imageURL, "Second button should have correct image");
popup.addEventListener("popuphidden", function onPopupHidden() {
popup.removeEventListener("popuphidden", onPopupHidden);
ok(true, "Popup should close automatically");
executeSoon(function() {
is(gContentWindow.callbackResult, "button2", "Correct callback should have been called");
done();
});
});
EventUtils.synthesizeMouseAtCenter(buttons.childNodes[1], {}, window);
});
let buttons = gContentWindow.makeButtons();
gContentAPI.showInfo("urlbar", "another title", "moar text", "./image.png", buttons);
},
function test_pinnedTab(done) {
is(UITour.pinnedTabs.get(window), null, "Should not already have a pinned tab");

Двоичные данные
browser/modules/test/image.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 55 KiB

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

@ -5,22 +5,6 @@
<title>UITour test</title>
<script type="application/javascript" src="uitour.js">
</script>
<script type="application/javascript">
var callbackResult;
function makeCallback(name) {
return (function() {
callbackResult = name;
});
}
// Defined in content to avoid weird issues when crossing between chrome/content.
function makeButtons() {
return [
{label: "Button 1", callback: makeCallback("button1")},
{label: "Button 2", callback: makeCallback("button2"), icon: "image.png"}
];
}
</script>
</head>
<body>
<h1>UITour tests</h1>

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

@ -25,6 +25,7 @@ if (typeof Mozilla == 'undefined') {
}
}
function _sendEvent(action, data) {
var event = new CustomEvent('mozUITour', {
bubbles: true,
@ -33,31 +34,10 @@ if (typeof Mozilla == 'undefined') {
data: data || {}
}
});
console.log("Sending mozUITour event: ", event);
document.dispatchEvent(event);
}
function _generateCallbackID() {
return Math.random().toString(36).replace(/[^a-z]+/g, '');
}
function _waitForCallback(callback) {
var id = _generateCallbackID();
function listener(event) {
if (typeof event.detail != "object")
return;
if (event.detail.callbackID != id)
return;
document.removeEventListener("mozUITourResponse", listener);
callback(event.detail.data);
}
document.addEventListener("mozUITourResponse", listener);
return id;
}
Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY = 10 * 1000;
Mozilla.UITour.showHighlight = function(target, effect) {
@ -71,24 +51,11 @@ if (typeof Mozilla == 'undefined') {
_sendEvent('hideHighlight');
};
Mozilla.UITour.showInfo = function(target, title, text, icon, buttons) {
var buttonData = [];
if (Array.isArray(buttons)) {
for (var i = 0; i < buttons.length; i++) {
buttonData.push({
label: buttons[i].label,
icon: buttons[i].icon,
callbackID: _waitForCallback(buttons[i].callback)
});
}
}
Mozilla.UITour.showInfo = function(target, title, text) {
_sendEvent('showInfo', {
target: target,
title: title,
text: text,
icon: icon,
buttons: buttonData
text: text
});
};

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

@ -1233,15 +1233,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
transition: height 100ms ease-out, width 100ms ease-out;
}
#social-share-button {
list-style-image: url("chrome://browser/skin/social/share-button.png");
}
#social-share-button[open],
#social-share-button:hover:active {
list-style-image: url("chrome://browser/skin/social/share-button-active.png");
}
.social-share-toolbar {
border-right: 1px solid #dedede;
background: linear-gradient(to bottom, rgba(247,247,247,.99), rgba(247,247,247,.95));

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

@ -101,37 +101,17 @@
-moz-image-region: rect(0px,32px,16px,16px);
}
#start-tracing {
padding: 4px;
margin: 4px;
}
#clear-tracer {
/* Make this button as narrow as the text inside it. */
min-width: 1px;
}
#tracer-message {
/* Prevent the container deck from aquiring the height from this message. */
min-height: 1px;
}
.trace-name {
-moz-padding-start: 4px !important;
}
#tracer-traces > scrollbox {
overflow: scroll;
/* Hack to enable hardware acceleration */
transform: translateZ(0);
}
/* Tracer dark theme */
.theme-dark #tracer-message {
color: #f5f7fa; /* Light foreground text */
}
.theme-dark .trace-item {
color: #f5f7fa; /* Light foreground text */
}
@ -167,10 +147,6 @@
/* Tracer light theme */
.theme-light #tracer-message {
color: #292e33; /* Dark foreground text */
}
.theme-light .trace-item {
color: #292e33; /* Dark foreground text */
}

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

@ -283,6 +283,24 @@
padding: 4px 8px;
}
/* FastListWidget */
.fast-list-widget-container {
/* Hack: force hardware acceleration */
transform: translateZ(1px);
}
.theme-dark .fast-list-widget-empty-text {
padding: 12px;
font-weight: 600;
color: #fff;
}
.theme-light .fast-list-widget-empty-text {
padding: 4px 8px;
color: GrayText;
}
/* SideMenuWidget */
.side-menu-widget-container {

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

@ -103,37 +103,17 @@
-moz-image-region: rect(0px,32px,16px,16px);
}
#start-tracing {
padding: 4px;
margin: 4px;
}
#clear-tracer {
/* Make this button as narrow as the text inside it. */
min-width: 1px;
}
#tracer-message {
/* Prevent the container deck from aquiring the height from this message. */
min-height: 1px;
}
.trace-name {
-moz-padding-start: 4px !important;
}
#tracer-traces > scrollbox {
overflow: scroll;
/* Hack to enable hardware acceleration */
transform: translateZ(0);
}
/* Tracer dark theme */
.theme-dark #tracer-message {
color: #f5f7fa; /* Light foreground text */
}
.theme-dark .trace-item {
color: #f5f7fa; /* Light foreground text */
}
@ -169,10 +149,6 @@
/* Tracer light theme */
.theme-light #tracer-message {
color: #292e33; /* Dark foreground text */
}
.theme-light .trace-item {
color: #292e33; /* Dark foreground text */
}

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

@ -283,6 +283,24 @@
padding: 4px 8px;
}
/* FastListWidget */
.fast-list-widget-container {
/* Hack: force hardware acceleration */
transform: translateZ(1px);
}
.theme-dark .fast-list-widget-empty-text {
padding: 12px;
font-weight: 600;
color: #fff;
}
.theme-light .fast-list-widget-empty-text {
padding: 4px 8px;
color: GrayText;
}
/* SideMenuWidget */
.side-menu-widget-container {

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

@ -22,10 +22,8 @@
min-width: 32px;
}
#UITourTooltipIcon {
width: 48px;
height: 48px;
padding: 8px;
#UITourTooltip {
max-width: 20em;
}
#UITourTooltipTitle {
@ -37,13 +35,3 @@
#UITourTooltipDescription {
max-width: 20em;
}
#UITourTooltipButtons {
height: 5em;
}
#UITourTooltipButtons > button[image] > .button-box > .button-icon {
width: 16px;
height: 16px;
-moz-margin-end: 5px;
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 695 B

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

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

@ -101,37 +101,17 @@
-moz-image-region: rect(0px,32px,16px,16px);
}
#start-tracing {
padding: 4px;
margin: 4px;
}
#clear-tracer {
/* Make this button as narrow as the text inside it. */
min-width: 1px;
}
#tracer-message {
/* Prevent the container deck from aquiring the height from this message. */
min-height: 1px;
}
.trace-name {
-moz-padding-start: 4px !important;
}
#tracer-traces > scrollbox {
overflow: scroll;
/* Hack to enable hardware acceleration */
transform: translateZ(0);
}
/* Tracer dark theme */
.theme-dark #tracer-message {
color: #f5f7fa; /* Light foreground text */
}
.theme-dark .trace-item {
color: #f5f7fa; /* Light foreground text */
}
@ -167,10 +147,6 @@
/* Tracer light theme */
.theme-light #tracer-message {
color: #292e33; /* Dark foreground text */
}
.theme-light .trace-item {
color: #292e33; /* Dark foreground text */
}

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

@ -287,6 +287,24 @@
padding: 4px 8px;
}
/* FastListWidget */
.fast-list-widget-container {
/* Hack: force hardware acceleration */
transform: translateZ(1px);
}
.theme-dark .fast-list-widget-empty-text {
padding: 12px;
font-weight: 600;
color: #fff;
}
.theme-light .fast-list-widget-empty-text {
padding: 4px 8px;
color: GrayText;
}
/* SideMenuWidget */
.side-menu-widget-container {

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

@ -73,7 +73,7 @@ public class CodeGenerator {
" JNIEnv *env = GetJNIForThread();\n\n" +
" if (!env) {\n" +
" ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
" return NULL;\n" +
" return nullptr;\n" +
" }\n\n" +
" ").append(mCClassName).append("* ret = new ").append(mCClassName).append("(obj, env);\n" +
" env->DeleteLocalRef(obj);\n" +
@ -407,7 +407,7 @@ public class CodeGenerator {
// Tack on the arguments, if any..
.append(argumentContent)
.append("), env);\n" +
" env->PopLocalFrame(NULL);\n" +
" env->PopLocalFrame(nullptr);\n" +
"}\n");
}
@ -484,7 +484,7 @@ public class CodeGenerator {
" ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
" env->ExceptionDescribe();\n" +
" env->ExceptionClear();\n" +
" env->PopLocalFrame(NULL);\n" +
" env->PopLocalFrame(nullptr);\n" +
" return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
" }\n\n");
@ -498,11 +498,11 @@ public class CodeGenerator {
} else if (!returnType.getCanonicalName().equals("void")) {
// If we're a primitive-returning function, just return the directly-obtained primative
// from the call to Java.
wrapperMethodBodies.append(" env->PopLocalFrame(NULL);\n" +
wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n" +
" return temp;\n");
} else {
// If we don't return anything, just pop the stack frame and move on with life.
wrapperMethodBodies.append(" env->PopLocalFrame(NULL);\n");
wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n");
}
wrapperMethodBodies.append("}\n");
}

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

@ -880,6 +880,10 @@ EXPAND_MOZLIBNAME = $(foreach lib,$(1),$(DIST)/lib/$(LIB_PREFIX)$(lib).$(LIB_SUF
PLY_INCLUDE = -I$(topsrcdir)/other-licenses/ply
export CL_INCLUDES_PREFIX
# Make sure that the build system can handle non-ASCII characters
# in environment variables to prevent it from breking silently on
# non-English systems.
export NONASCII
ifdef MOZ_GTK2_CFLAGS
MOZ_GTK2_CFLAGS := -I$(topsrcdir)/widget/gtk/compat $(MOZ_GTK2_CFLAGS)

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

@ -71,7 +71,7 @@ GCONF_VERSION=1.2.1
GIO_VERSION=2.20
STARTUP_NOTIFICATION_VERSION=0.8
DBUS_VERSION=0.60
SQLITE_VERSION=3.8.1
SQLITE_VERSION=3.8.2
MSMANIFEST_TOOL=
@ -7705,6 +7705,12 @@ else
fi
fi
dnl Make sure that the build system can handle non-ASCII characters
dnl in environment variables to prevent it from breking silently on
dnl non-English systems.
NONASCII=$'\241\241'
AC_SUBST(NONASCII)
dnl ========================================================
dnl =
dnl = Static Build Options

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

@ -86,7 +86,6 @@ class nsIURI;
class nsIWidget;
class nsIWordBreaker;
class nsIXPConnect;
class nsIXPConnectJSObjectHolder;
class nsNodeInfoManager;
class nsPIDOMWindow;
class nsPresContext;
@ -1638,39 +1637,31 @@ public:
*/
static bool CanAccessNativeAnon();
MOZ_WARN_UNUSED_RESULT
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, const nsIID* aIID,
JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the Value alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr,
bool aAllowWrapping = false)
{
return WrapNative(cx, scope, native, nullptr, aIID, vp, aHolder,
aAllowWrapping);
return WrapNative(cx, scope, native, nullptr, aIID, vp, aAllowWrapping);
}
// Same as the WrapNative above, but use this one if aIID is nsISupports' IID.
MOZ_WARN_UNUSED_RESULT
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the Value alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr,
bool aAllowWrapping = false)
{
return WrapNative(cx, scope, native, nullptr, nullptr, vp, aHolder,
aAllowWrapping);
return WrapNative(cx, scope, native, nullptr, nullptr, vp, aAllowWrapping);
}
MOZ_WARN_UNUSED_RESULT
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, nsWrapperCache *cache,
JS::MutableHandle<JS::Value> vp,
// If non-null aHolder will keep the Value alive
// while there's a ref to it
nsIXPConnectJSObjectHolder** aHolder = nullptr,
bool aAllowWrapping = false)
{
return WrapNative(cx, scope, native, cache, nullptr, vp, aHolder,
aAllowWrapping);
return WrapNative(cx, scope, native, cache, nullptr, vp, aAllowWrapping);
}
/**
@ -2133,7 +2124,6 @@ private:
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, nsWrapperCache *cache,
const nsIID* aIID, JS::MutableHandle<JS::Value> vp,
nsIXPConnectJSObjectHolder** aHolder,
bool aAllowWrapping);
static nsresult DispatchEvent(nsIDocument* aDoc,

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

@ -106,17 +106,21 @@ DOMParser::ParseFromString(const nsAString& str,
return rv;
}
NS_ConvertUTF16toUTF8 data(str);
nsAutoCString utf8str;
// Convert from UTF16 to UTF8 using fallible allocations
if (!AppendUTF16toUTF8(str, utf8str, mozilla::fallible_t())) {
return NS_ERROR_OUT_OF_MEMORY;
}
// The new stream holds a reference to the buffer
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewByteInputStream(getter_AddRefs(stream),
data.get(), data.Length(),
utf8str.get(), utf8str.Length(),
NS_ASSIGNMENT_DEPEND);
if (NS_FAILED(rv))
return rv;
return ParseFromStream(stream, "UTF-8", data.Length(), contentType, aResult);
return ParseFromStream(stream, "UTF-8", utf8str.Length(), contentType, aResult);
}
already_AddRefed<nsIDocument>

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

@ -55,7 +55,8 @@ namespace dom {
#define DEFAULT_RECONNECTION_TIME_VALUE 5000
#define MAX_RECONNECTION_TIME_VALUE PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
EventSource::EventSource() :
EventSource::EventSource(nsPIDOMWindow* aOwnerWindow) :
nsDOMEventTargetHelper(aOwnerWindow),
mStatus(PARSE_STATE_OFF),
mFrozen(false),
mErrorLoadOnRedirect(false),
@ -68,7 +69,6 @@ EventSource::EventSource() :
mScriptLine(0),
mInnerWindowID(0)
{
SetIsDOMBinding();
}
EventSource::~EventSource()
@ -186,10 +186,6 @@ EventSource::Init(nsISupports* aOwner,
return NS_ERROR_DOM_SECURITY_ERR;
}
nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
NS_ENSURE_STATE(ownerWindow);
MOZ_ASSERT(ownerWindow->IsInnerWindow());
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
NS_ENSURE_STATE(sgo);
nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
@ -203,7 +199,6 @@ EventSource::Init(nsISupports* aOwner,
mPrincipal = principal;
mWithCredentials = aWithCredentials;
BindToOwner(ownerWindow);
// The conditional here is historical and not necessarily sane.
if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
@ -286,7 +281,15 @@ EventSource::Constructor(const GlobalObject& aGlobal,
const EventSourceInit& aEventSourceInitDict,
ErrorResult& aRv)
{
nsRefPtr<EventSource> eventSource = new EventSource();
nsCOMPtr<nsPIDOMWindow> ownerWindow =
do_QueryInterface(aGlobal.GetAsSupports());
if (!ownerWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
MOZ_ASSERT(ownerWindow->IsInnerWindow());
nsRefPtr<EventSource> eventSource = new EventSource(ownerWindow);
aRv = eventSource->Init(aGlobal.GetAsSupports(), aURL,
aEventSourceInitDict.mWithCredentials);
return eventSource.forget();

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

@ -25,13 +25,6 @@
#include "nsDeque.h"
#include "nsIUnicodeDecoder.h"
#define NS_EVENTSOURCE_CID \
{ /* 755e2d2d-a836-4539-83f4-16b51156341f */ \
0x755e2d2d, 0xa836, 0x4539, \
{0x83, 0xf4, 0x16, 0xb5, 0x11, 0x56, 0x34, 0x1f} }
#define NS_EVENTSOURCE_CONTRACTID "@mozilla.org/eventsource;1"
class nsPIDOMWindow;
namespace mozilla {
@ -53,7 +46,7 @@ class EventSource : public nsDOMEventTargetHelper
friend class AsyncVerifyRedirectCallbackFwr;
public:
EventSource();
EventSource(nsPIDOMWindow* aOwnerWindow);
virtual ~EventSource();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(EventSource,

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

@ -442,8 +442,9 @@ WebSocket::GetInterface(const nsIID& aIID, void** aResult)
// WebSocket
////////////////////////////////////////////////////////////////////////////////
WebSocket::WebSocket()
: mKeepingAlive(false),
WebSocket::WebSocket(nsPIDOMWindow* aOwnerWindow)
: nsDOMEventTargetHelper(aOwnerWindow),
mKeepingAlive(false),
mCheckMustKeepAlive(true),
mOnCloseScheduled(false),
mFailed(false),
@ -457,8 +458,8 @@ WebSocket::WebSocket()
mInnerWindowID(0)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
SetIsDOMBinding();
MOZ_ASSERT(aOwnerWindow);
MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
}
WebSocket::~WebSocket()
@ -560,8 +561,8 @@ WebSocket::Constructor(const GlobalObject& aGlobal,
protocolArray.AppendElement(protocolElement);
}
nsRefPtr<WebSocket> webSocket = new WebSocket();
nsresult rv = webSocket->Init(aGlobal.GetContext(), principal, ownerWindow,
nsRefPtr<WebSocket> webSocket = new WebSocket(ownerWindow);
nsresult rv = webSocket->Init(aGlobal.GetContext(), principal,
aUrl, protocolArray);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
@ -640,21 +641,17 @@ WebSocket::DisconnectFromOwner()
nsresult
WebSocket::Init(JSContext* aCx,
nsIPrincipal* aPrincipal,
nsPIDOMWindow* aOwnerWindow,
const nsAString& aURL,
nsTArray<nsString>& aProtocolArray)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aOwnerWindow);
MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
if (!PrefEnabled()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
mPrincipal = aPrincipal;
BindToOwner(aOwnerWindow);
// Attempt to kill "ghost" websocket: but usually too early for check to fail
nsresult rv = CheckInnerWindowCorrectness();

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

@ -140,13 +140,12 @@ public: // WebIDL interface:
ErrorResult& aRv);
private: // constructor && distructor
WebSocket();
WebSocket(nsPIDOMWindow* aOwnerWindow);
virtual ~WebSocket();
protected:
nsresult Init(JSContext* aCx,
nsIPrincipal* aPrincipal,
nsPIDOMWindow* aOwnerWindow,
const nsAString& aURL,
nsTArray<nsString>& aProtocolArray);

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

@ -200,13 +200,13 @@ LOCAL_INCLUDES += [
'/content/xbl/src',
'/content/xml/content/src',
'/content/xml/document/src',
'/content/xslt/src/xpath',
'/content/xul/content/src',
'/content/xul/document/src',
'/docshell/base',
'/dom/base',
'/dom/ipc',
'/dom/workers',
'/dom/xslt/xpath',
'/image/src',
'/js/ipc',
'/js/xpconnect/src',

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

@ -5613,12 +5613,9 @@ nsresult
nsContentUtils::WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, nsWrapperCache *cache,
const nsIID* aIID, JS::MutableHandle<JS::Value> vp,
nsIXPConnectJSObjectHolder **aHolder,
bool aAllowWrapping)
{
if (!native) {
NS_ASSERTION(!aHolder || !*aHolder, "*aHolder should be null!");
vp.setNull();
return NS_OK;
@ -5638,7 +5635,7 @@ nsContentUtils::WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsresult rv = NS_OK;
AutoPushJSContext context(cx);
rv = sXPConnect->WrapNativeToJSVal(context, scope, native, cache, aIID,
aAllowWrapping, vp.address(), aHolder);
aAllowWrapping, vp.address());
return rv;
}
@ -5681,7 +5678,7 @@ nsContentUtils::CreateBlobBuffer(JSContext* aCx,
return NS_ERROR_OUT_OF_MEMORY;
}
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
return nsContentUtils::WrapNative(aCx, scope, blob, aBlob, nullptr, true);
return nsContentUtils::WrapNative(aCx, scope, blob, aBlob, true);
}
void

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

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
@ -26,6 +26,12 @@
#include "nsHashKeys.h"
#include "nsStreamUtils.h"
#include "mozilla/Preferences.h"
#include "nsIScriptError.h"
#include "nsILoadGroup.h"
#include "nsILoadContext.h"
#include "nsIConsoleService.h"
#include "nsIDOMWindowUtils.h"
#include "nsIDOMWindow.h"
#include <algorithm>
using namespace mozilla;
@ -35,6 +41,75 @@ using namespace mozilla;
static bool gDisableCORS = false;
static bool gDisableCORSPrivateData = false;
static nsresult
LogBlockedRequest(nsIRequest* aRequest)
{
nsresult rv = NS_OK;
// Get the innerWindowID associated with the XMLHTTPRequest
PRUint64 innerWindowID = 0;
nsCOMPtr<nsILoadGroup> loadGroup;
aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
if (loadGroup) {
nsCOMPtr<nsIInterfaceRequestor> callbacks;
loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
if(loadContext) {
nsCOMPtr<nsIDOMWindow> window;
loadContext->GetAssociatedWindow(getter_AddRefs(window));
if (window) {
nsCOMPtr<nsIDOMWindowUtils> du = do_GetInterface(window);
du->GetCurrentInnerWindowID(&innerWindowID);
}
}
}
}
if (!innerWindowID) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
nsCOMPtr<nsIURI> aUri;
channel->GetURI(getter_AddRefs(aUri));
nsAutoCString spec;
if (aUri) {
aUri->GetSpec(spec);
}
// Generate the error message
nsXPIDLString blockedMessage;
NS_ConvertUTF8toUTF16 specUTF16(spec);
const PRUnichar* params[] = { specUTF16.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
"CrossSiteRequestBlocked",
params,
blockedMessage);
// Build the error object and log it to the console
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIScriptError> scriptError = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString msg(blockedMessage.get());
rv = scriptError->InitWithWindowID(msg,
NS_ConvertUTF8toUTF16(spec),
EmptyString(),
0,
0,
nsIScriptError::warningFlag,
"CORS",
innerWindowID);
NS_ENSURE_SUCCESS(rv, rv);
rv = console->LogMessage(scriptError);
return rv;
}
//////////////////////////////////////////////////////////////////////////
// Preflight cache
@ -398,8 +473,11 @@ NS_IMETHODIMP
nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest));
nsresult rv = CheckRequestApproved(aRequest);
mRequestApproved = NS_SUCCEEDED(rv);
if (!mRequestApproved) {
rv = LogBlockedRequest(aRequest);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to log blocked cross-site request");
if (sPreflightCache) {
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
if (channel) {
@ -622,6 +700,9 @@ nsCORSListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
rv = CheckRequestApproved(aOldChannel);
if (NS_FAILED(rv)) {
rv = LogBlockedRequest(aOldChannel);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to log blocked cross-site request");
if (sPreflightCache) {
nsCOMPtr<nsIURI> oldURI;
NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));

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

@ -80,11 +80,13 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDataChannel)
NS_INTERFACE_MAP_ENTRY(nsIDOMDataChannel)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
nsDOMDataChannel::nsDOMDataChannel(already_AddRefed<mozilla::DataChannel> aDataChannel)
: mDataChannel(aDataChannel)
nsDOMDataChannel::nsDOMDataChannel(already_AddRefed<mozilla::DataChannel> aDataChannel,
nsPIDOMWindow* aWindow)
: nsDOMEventTargetHelper(aWindow && aWindow->IsOuterWindow() ?
aWindow->GetCurrentInnerWindow() : aWindow)
, mDataChannel(aDataChannel)
, mBinaryType(DC_BINARY_TYPE_BLOB)
{
SetIsDOMBinding();
}
nsresult
@ -107,13 +109,6 @@ nsDOMDataChannel::Init(nsPIDOMWindow* aDOMWindow)
nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
NS_ENSURE_STATE(principal);
if (aDOMWindow) {
BindToOwner(aDOMWindow->IsOuterWindow() ?
aDOMWindow->GetCurrentInnerWindow() : aDOMWindow);
} else {
BindToOwner(aDOMWindow);
}
// Attempt to kill "ghost" DataChannel (if one can happen): but usually too early for check to fail
rv = CheckInnerWindowCorrectness();
NS_ENSURE_SUCCESS(rv,rv);
@ -499,7 +494,8 @@ NS_NewDOMDataChannel(already_AddRefed<mozilla::DataChannel> aDataChannel,
nsPIDOMWindow* aWindow,
nsIDOMDataChannel** aDomDataChannel)
{
nsRefPtr<nsDOMDataChannel> domdc = new nsDOMDataChannel(aDataChannel);
nsRefPtr<nsDOMDataChannel> domdc =
new nsDOMDataChannel(aDataChannel, aWindow);
nsresult rv = domdc->Init(aWindow);
NS_ENSURE_SUCCESS(rv,rv);

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

@ -25,7 +25,8 @@ class nsDOMDataChannel : public nsDOMEventTargetHelper,
public mozilla::DataChannelListener
{
public:
nsDOMDataChannel(already_AddRefed<mozilla::DataChannel> aDataChannel);
nsDOMDataChannel(already_AddRefed<mozilla::DataChannel> aDataChannel,
nsPIDOMWindow* aWindow);
~nsDOMDataChannel();
nsresult Init(nsPIDOMWindow* aDOMWindow);

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

@ -6805,7 +6805,7 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
JS::Rooted<JS::Value> v(cx);
rv = nsContentUtils::WrapNative(cx, global, this, this, &v,
nullptr, /* aAllowWrapping = */ false);
/* aAllowWrapping = */ false);
if (rv.Failed())
return nullptr;
newScope = &v.toObject();
@ -11469,11 +11469,9 @@ nsIDocument::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aScope)
JSAutoCompartment ac(aCx, obj);
JS::Rooted<JS::Value> winVal(aCx);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsresult rv = nsContentUtils::WrapNative(aCx, obj, win,
&NS_GET_IID(nsIDOMWindow),
&winVal,
getter_AddRefs(holder),
false);
if (NS_FAILED(rv)) {
Throw(aCx, rv);

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

@ -926,8 +926,8 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
JS::Rooted<JS::Value> targetv(ctx);
JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
nsContentUtils::WrapNative(ctx, global, aTarget, &targetv,
nullptr, true);
nsresult rv = nsContentUtils::WrapNative(ctx, global, aTarget, &targetv, true);
NS_ENSURE_SUCCESS(rv, rv);
JS::Rooted<JSObject*> cpows(ctx);
if (aCpows) {
@ -1017,8 +1017,8 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
defaultThisValue = aTarget;
}
JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
nsContentUtils::WrapNative(ctx, global, defaultThisValue,
&thisValue, nullptr, true);
nsresult rv = nsContentUtils::WrapNative(ctx, global, defaultThisValue, &thisValue, true);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// If the listener is a JS object which has receiveMessage function:
if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) ||

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

@ -990,7 +990,7 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, ErrorResult& aRv)
JS::Rooted<JS::Value> result(aCx, JSVAL_NULL);
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, &result,
nullptr, true);
true);
return result;
}
case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
@ -1002,7 +1002,7 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, ErrorResult& aRv)
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
JS::Rooted<JS::Value> result(aCx, JSVAL_NULL);
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, &result,
nullptr, true);
true);
return result;
}
case XML_HTTP_RESPONSE_TYPE_JSON:

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

@ -100,6 +100,16 @@ protected:
class nsXHREventTarget : public nsDOMEventTargetHelper,
public nsIXMLHttpRequestEventTarget
{
protected:
nsXHREventTarget(nsDOMEventTargetHelper* aOwner)
: nsDOMEventTargetHelper(aOwner)
{
}
nsXHREventTarget()
{
}
public:
typedef mozilla::dom::XMLHttpRequestResponseType
XMLHttpRequestResponseType;
@ -129,10 +139,10 @@ class nsXMLHttpRequestUpload MOZ_FINAL : public nsXHREventTarget,
{
public:
nsXMLHttpRequestUpload(nsDOMEventTargetHelper* aOwner)
: nsXHREventTarget(aOwner)
{
BindToOwner(aOwner);
SetIsDOMBinding();
}
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsXHREventTarget::)
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsXHREventTarget)

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

@ -580,3 +580,4 @@ support-files =
[test_xhr_send_readystate.html]
[test_xhr_withCredentials.html]
[test_file_from_blob.html]
[test_warning_for_blocked_cross_site_request.html]

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

@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=713980
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 713980</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<!-- Load a cross-origin webfont without CORS (common pain point -->
<style>
@font-face {
font-family: "bad_cross_origin_webfont";
src: url('http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
}
div#bad_webfont { font-family: "bad_cross_origin_webfont"; }
</style>
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var tests = {
font : {
uri_test : "font_bad",
result : null
},
xhr : {
uri_test : "http://invalid",
result : null
},
}
function testsComplete() {
for (var testName in tests) {
var test = tests[testName];
if (test.result == null)
return false;
}
return true;
}
SpecialPowers.registerConsoleListener(function CORSMsgListener(aMsg) {
if (!/Cross-Origin Request Blocked/.test(aMsg.message))
return;
for (var testName in tests) {
var test = tests[testName];
if (test.result != null)
continue;
var testRegexp = new RegExp(test.uri_test);
if (testRegexp.test(aMsg.message)) {
test.result = true;
ok(true, "Got \"Cross-site request blocked\" warning message for " + testName);
ok(aMsg.category == "CORS", "Got warning message with category \"" + aMsg.category + "\", expected \"CORS\"");
// Got the message we wanted - make sure it is destined for a valid inner window
ok(aMsg.windowID != 0, "Valid (non-zero) windowID for the cross-site request blocked message.");
break;
}
}
if (testsComplete()) {
SimpleTest.executeSoon(cleanup);
}
});
function cleanup() {
SpecialPowers.postConsoleSentinel();
SimpleTest.finish();
}
// Send a cross-origin XHR request without CORS
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.org/tests/content/base/test/file_CrossSiteXHR_server.sjs?allowOrigin=http://invalid", true);
xhr.send(null);
// Create a div that triggers a cross-origin webfont request
// We do this in Javascript in order to guarantee the console listener has
// already been registered; otherwise, there could be a race.
var badDiv = document.createElement('div');
badDiv.setAttribute('id', 'bad_webfont');
document.body.appendChild(badDiv);
</script>
</pre>
</body>
</html>

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

@ -0,0 +1 @@
conformance/more/conformance/quickCheckAPI-B2.html

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

@ -516,6 +516,7 @@ function start() {
break;
default:
failingTestsFilename = 'failing_tests_linux.txt';
skippedTestsFilename = 'skipped_tests_linux.txt';
break;
}
break;

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

@ -89,6 +89,7 @@ nsDOMEventTargetHelper::~nsDOMEventTargetHelper()
void
nsDOMEventTargetHelper::BindToOwner(nsPIDOMWindow* aOwner)
{
MOZ_ASSERT(!aOwner || aOwner->IsInnerWindow());
nsCOMPtr<nsIGlobalObject> glob = do_QueryInterface(aOwner);
BindToOwner(glob);
}

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

@ -43,6 +43,15 @@ public:
// All objects coming through here are WebIDL objects
SetIsDOMBinding();
}
nsDOMEventTargetHelper(nsDOMEventTargetHelper* aOther)
: mParentObject(nullptr)
, mOwnerWindow(nullptr)
, mHasOrHasHadOwnerWindow(false)
{
BindToOwner(aOther);
// All objects coming through here are WebIDL objects
SetIsDOMBinding();
}
virtual ~nsDOMEventTargetHelper();
NS_DECL_CYCLE_COLLECTING_ISUPPORTS

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

@ -39,6 +39,7 @@ class VideoFrameContainer;
namespace dom {
class TextTrack;
class TimeRanges;
class WakeLock;
}
}
@ -588,7 +589,7 @@ protected:
*/
virtual void WakeLockCreate();
virtual void WakeLockRelease();
nsCOMPtr<nsIDOMMozWakeLock> mWakeLock;
nsRefPtr<WakeLock> mWakeLock;
/**
* Logs a warning message to the web console to report various failures.

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

@ -14,6 +14,7 @@
namespace mozilla {
namespace dom {
class WakeLock;
class VideoPlaybackQuality;
class HTMLVideoElement MOZ_FINAL : public HTMLMediaElement,
@ -115,7 +116,7 @@ protected:
virtual void WakeLockRelease();
void WakeLockUpdate();
nsCOMPtr<nsIDOMMozWakeLock> mScreenWakeLock;
nsRefPtr<WakeLock> mScreenWakeLock;
private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,

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

@ -0,0 +1,22 @@
<!DOCTYPE html>
<div style="width: 15em">
<pre>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre wrap>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre wrap>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre wrap>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
</div>
12

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

@ -0,0 +1,22 @@
<!DOCTYPE html>
<div style="width: 15em">
<pre>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre width=12>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre cols=12>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre wrap>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre wrap width=12>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
<pre wrap cols=12>
MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM MMMMM
</pre>
</div>
<script>document.write(document.querySelectorAll('pre')[1].width);</script>

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

@ -27,6 +27,7 @@ skip-if(Android||B2G) == 649134-2.html 649134-2-ref.html
== href-attr-change-restyles.html href-attr-change-restyles-ref.html
== figure.html figure-ref.html
== pre-1.html pre-1-ref.html
== table-border-1.html table-border-1-ref.html
== table-border-2.html table-border-2-ref.html
!= table-border-2.html table-border-2-notref.html

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

@ -75,10 +75,10 @@
#include "nsCSSParser.h"
#include "nsIMediaList.h"
#include "nsIDOMWakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/WakeLock.h"
#include "ImageContainer.h"
#include "nsIPowerManagerService.h"
#include "nsRange.h"
#include <algorithm>
@ -2245,13 +2245,14 @@ void
HTMLMediaElement::WakeLockCreate()
{
if (!mWakeLock) {
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
nsRefPtr<power::PowerManagerService> pmService =
power::PowerManagerService::GetInstance();
NS_ENSURE_TRUE_VOID(pmService);
pmService->NewWakeLock(NS_LITERAL_STRING("cpu"),
OwnerDoc()->GetWindow(),
getter_AddRefs(mWakeLock));
ErrorResult rv;
mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("cpu"),
OwnerDoc()->GetInnerWindow(),
rv);
}
}
@ -2259,7 +2260,9 @@ void
HTMLMediaElement::WakeLockRelease()
{
if (mWakeLock) {
mWakeLock->Unlock();
ErrorResult rv;
mWakeLock->Unlock(rv);
NS_WARN_IF_FALSE(!rv.Failed(), "Failed to unlock the wakelock.");
mWakeLock = nullptr;
}
}

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

@ -48,29 +48,12 @@ void
HTMLPreElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
nsCSSValue* width = aData->ValueForWidth();
if (width->GetUnit() == eCSSUnit_Null) {
// width: int (html4 attribute)
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
if (value && value->Type() == nsAttrValue::eInteger)
width->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Char);
}
}
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
nsCSSValue* whiteSpace = aData->ValueForWhiteSpace();
if (whiteSpace->GetUnit() == eCSSUnit_Null) {
// wrap: empty
if (aAttributes->GetAttr(nsGkAtoms::wrap))
whiteSpace->SetIntValue(NS_STYLE_WHITESPACE_PRE_WRAP, eCSSUnit_Enumerated);
// width: int (html4 attribute)
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
if (value && value->Type() == nsAttrValue::eInteger) {
// Force wrap property on since we want to wrap at a width
// boundary not just a newline.
whiteSpace->SetIntValue(NS_STYLE_WHITESPACE_PRE_WRAP, eCSSUnit_Enumerated);
}
}
}
@ -82,7 +65,6 @@ HTMLPreElement::IsAttributeMapped(const nsIAtom* aAttribute) const
{
static const MappedAttributeEntry attributes[] = {
{ &nsGkAtoms::wrap },
{ &nsGkAtoms::width },
{ nullptr },
};

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

@ -27,11 +27,11 @@
#include "nsEventDispatcher.h"
#include "nsIDOMProgressEvent.h"
#include "nsIPowerManagerService.h"
#include "MediaError.h"
#include "MediaDecoder.h"
#include "mozilla/Preferences.h"
#include "nsIDOMWakeLock.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "nsPerformance.h"
#include "mozilla/dom/VideoPlaybackQuality.h"
@ -302,19 +302,22 @@ HTMLVideoElement::WakeLockUpdate()
bool hidden = OwnerDoc()->Hidden();
if (mScreenWakeLock && (mPaused || hidden)) {
mScreenWakeLock->Unlock();
ErrorResult rv;
mScreenWakeLock->Unlock(rv);
NS_WARN_IF_FALSE(!rv.Failed(), "Failed to unlock the wakelock.");
mScreenWakeLock = nullptr;
return;
}
if (!mScreenWakeLock && !mPaused && !hidden) {
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
nsRefPtr<power::PowerManagerService> pmService =
power::PowerManagerService::GetInstance();
NS_ENSURE_TRUE_VOID(pmService);
pmService->NewWakeLock(NS_LITERAL_STRING("screen"),
OwnerDoc()->GetWindow(),
getter_AddRefs(mScreenWakeLock));
ErrorResult rv;
mScreenWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("screen"),
OwnerDoc()->GetInnerWindow(),
rv);
}
}

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

@ -324,7 +324,8 @@ ConvertSegmentToAudioBlock(AudioSegment* aSegment, AudioChunk* aBlock)
}
void
AudioNodeExternalInputStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
AudioNodeExternalInputStream::ProduceOutput(GraphTime aFrom, GraphTime aTo,
uint32_t aFlags)
{
// According to spec, number of outputs is always 1.
mLastChunks.SetLength(1);

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

@ -25,7 +25,7 @@ public:
AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate);
~AudioNodeExternalInputStream();
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo) MOZ_OVERRIDE;
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
private:
// For storing pointers and data about input tracks, like the last TrackTick which

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

@ -269,8 +269,7 @@ AudioNodeStream::ObtainInputBlock(AudioChunk& aTmpChunk, uint32_t aPortIndex)
MediaStream* s = mInputs[i]->GetSource();
AudioNodeStream* a = static_cast<AudioNodeStream*>(s);
MOZ_ASSERT(a == s->AsAudioNodeStream());
if (a->IsFinishedOnGraphThread() ||
a->IsAudioParamStream()) {
if (a->IsAudioParamStream()) {
continue;
}
@ -399,29 +398,20 @@ AudioNodeStream::UpMixDownMixChunk(const AudioChunk* aChunk,
// The MediaStreamGraph guarantees that this is actually one block, for
// AudioNodeStreams.
void
AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
{
if (mMarkAsFinishedAfterThisBlock) {
// This stream was finished the last time that we looked at it, and all
// of the depending streams have finished their output as well, so now
// it's time to mark this stream as finished.
FinishOutput();
}
EnsureTrack(AUDIO_TRACK, mSampleRate);
// No more tracks will be coming
mBuffer.AdvanceKnownTracksTime(STREAM_TIME_MAX);
uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount());
mLastChunks.SetLength(outputCount);
if (mMuted) {
if (mMuted || IsFinishedOnGraphThread()) {
for (uint16_t i = 0; i < outputCount; ++i) {
mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
}
} else {
for (uint16_t i = 0; i < outputCount; ++i) {
mLastChunks[i].SetNull(0);
}
// We need to generate at least one input
uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
OutputChunks inputChunks;
@ -430,11 +420,21 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
ObtainInputBlock(inputChunks[i], i);
}
bool finished = false;
#ifdef DEBUG
for (uint16_t i = 0; i < outputCount; ++i) {
// Clear chunks so we can detect if ProduceAudioBlock fails to set them.
mLastChunks[i].SetNull(0);
}
#endif
if (maxInputs <= 1 && mEngine->OutputCount() <= 1) {
mEngine->ProduceAudioBlock(this, inputChunks[0], &mLastChunks[0], &finished);
} else {
mEngine->ProduceAudioBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
}
for (uint16_t i = 0; i < outputCount; ++i) {
NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,
"Invalid WebAudio chunk size");
}
if (finished) {
mMarkAsFinishedAfterThisBlock = true;
}
@ -446,7 +446,16 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
}
}
AdvanceOutputSegment();
if (!IsFinishedOnGraphThread()) {
// Don't output anything after we've finished!
AdvanceOutputSegment();
if (mMarkAsFinishedAfterThisBlock && (aFlags & ALLOW_FINISH)) {
// This stream was finished the last time that we looked at it, and all
// of the depending streams have finished their output as well, so now
// it's time to mark this stream as finished.
FinishOutput();
}
}
}
void

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

@ -84,7 +84,7 @@ public:
mAudioParamStream = true;
}
virtual AudioNodeStream* AsAudioNodeStream() { return this; }
virtual AudioNodeStream* AsAudioNodeStream() MOZ_OVERRIDE { return this; }
// Graph thread only
void SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
@ -92,7 +92,7 @@ public:
void SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
dom::ChannelCountMode aChannelCountMoe,
dom::ChannelInterpretation aChannelInterpretation);
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo);
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
TrackTicks GetCurrentPosition();
bool IsAudioParamStream() const
{

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

@ -200,29 +200,20 @@ MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder,
mHaveBlockedForPlayState(false),
mHaveBlockedForStateMachineNotPlaying(false)
{
mStream->AddMainThreadListener(this);
mListener = new DecodedStreamGraphListener(mStream);
mListener = new DecodedStreamGraphListener(mStream, this);
mStream->AddListener(mListener);
}
MediaDecoder::DecodedStreamData::~DecodedStreamData()
{
mStream->RemoveMainThreadListener(this);
mListener->Forget();
mStream->Destroy();
}
void
MediaDecoder::DecodedStreamData::NotifyMainThreadStateChanged()
{
mDecoder->NotifyDecodedStreamMainThreadStateChanged();
if (mStream->IsFinished()) {
mListener->SetFinishedOnMainThread(true);
}
}
MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream)
: mMutex("MediaDecoder::DecodedStreamData::mMutex"),
MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream,
DecodedStreamData* aData)
: mData(aData),
mMutex("MediaDecoder::DecodedStreamData::mMutex"),
mStream(aStream),
mLastOutputTime(aStream->GetCurrentTime()),
mStreamFinishedOnMainThread(false)
@ -239,6 +230,29 @@ MediaDecoder::DecodedStreamGraphListener::NotifyOutput(MediaStreamGraph* aGraph,
}
}
void
MediaDecoder::DecodedStreamGraphListener::DoNotifyFinished()
{
if (mData && mData->mDecoder) {
if (mData->mDecoder->GetState() == PLAY_STATE_PLAYING) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mData->mDecoder, &MediaDecoder::PlaybackEnded);
NS_DispatchToCurrentThread(event);
}
}
MutexAutoLock lock(mMutex);
mStreamFinishedOnMainThread = true;
}
void
MediaDecoder::DecodedStreamGraphListener::NotifyFinished(MediaStreamGraph* aGraph)
{
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
}
void MediaDecoder::DestroyDecodedStream()
{
MOZ_ASSERT(NS_IsMainThread());
@ -326,19 +340,6 @@ void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
}
}
void MediaDecoder::NotifyDecodedStreamMainThreadStateChanged()
{
if (mTriggerPlaybackEndedWhenSourceStreamFinishes && mDecodedStream &&
mDecodedStream->mStream->IsFinished()) {
mTriggerPlaybackEndedWhenSourceStreamFinishes = false;
if (GetState() == PLAY_STATE_PLAYING) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &MediaDecoder::PlaybackEnded);
NS_DispatchToCurrentThread(event);
}
}
}
void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded)
{
@ -421,7 +422,6 @@ MediaDecoder::MediaDecoder() :
mCalledResourceLoaded(false),
mIgnoreProgressData(false),
mInfiniteStream(false),
mTriggerPlaybackEndedWhenSourceStreamFinishes(false),
mOwner(nullptr),
mFrameBufferLength(0),
mPinnedForSeek(false),
@ -936,12 +936,6 @@ void MediaDecoder::PlaybackEnded()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mDecodedStream && !mDecodedStream->mStream->IsFinished()) {
// Wait for it to finish before firing PlaybackEnded()
mTriggerPlaybackEndedWhenSourceStreamFinishes = true;
return;
}
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
OutputStreamData& os = mOutputStreams[i];
if (os.mStream->IsDestroyed()) {
@ -1289,7 +1283,12 @@ void MediaDecoder::PlaybackPositionChanged()
// and we don't want to override the seek algorithm and change the
// current time after the seek has started but before it has
// completed.
mCurrentTime = mDecoderStateMachine->GetCurrentTime();
if (GetDecodedStream()) {
mCurrentTime = mDecoderStateMachine->GetCurrentTimeViaMediaStreamSync()/
static_cast<double>(USECS_PER_S);
} else {
mCurrentTime = mDecoderStateMachine->GetCurrentTime();
}
}
mDecoderStateMachine->ClearPositionChangeFlag();
}

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

@ -350,13 +350,11 @@ public:
// replaying after the input as ended. In the latter case, the new source is
// not connected to streams created by captureStreamUntilEnded.
struct DecodedStreamData MOZ_FINAL : public MainThreadMediaStreamListener {
struct DecodedStreamData {
DecodedStreamData(MediaDecoder* aDecoder,
int64_t aInitialTime, SourceMediaStream* aStream);
~DecodedStreamData();
virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
StreamTime GetLastOutputTime() { return mListener->GetLastOutputTime(); }
bool IsFinished() { return mListener->IsFinishedOnMainThread(); }
@ -400,8 +398,11 @@ public:
class DecodedStreamGraphListener : public MediaStreamListener {
public:
DecodedStreamGraphListener(MediaStream* aStream);
DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData);
virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) MOZ_OVERRIDE;
virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
void DoNotifyFinished();
StreamTime GetLastOutputTime()
{
@ -410,13 +411,18 @@ public:
}
void Forget()
{
NS_ASSERTION(NS_IsMainThread(), "Main thread only");
mData = nullptr;
MutexAutoLock lock(mMutex);
mStream = nullptr;
}
void SetFinishedOnMainThread(bool aFinished)
bool SetFinishedOnMainThread(bool aFinished)
{
MutexAutoLock lock(mMutex);
bool result = !mStreamFinishedOnMainThread;
mStreamFinishedOnMainThread = aFinished;
return result;
}
bool IsFinishedOnMainThread()
{
@ -424,6 +430,9 @@ public:
return mStreamFinishedOnMainThread;
}
private:
// Main thread only
DecodedStreamData* mData;
Mutex mMutex;
// Protected by mMutex
nsRefPtr<MediaStream> mStream;
@ -465,12 +474,6 @@ public:
* Decoder monitor must be held.
*/
void UpdateStreamBlockingForStateMachinePlaying();
/**
* Called when the state of mDecodedStream as visible on the main thread
* has changed. In particular we want to know when the stream has finished
* so we can call PlaybackEnded.
*/
void NotifyDecodedStreamMainThreadStateChanged();
nsTArray<OutputStreamData>& OutputStreams()
{
GetReentrantMonitor().AssertCurrentThreadIn();
@ -1134,10 +1137,6 @@ protected:
// True if the stream is infinite (e.g. a webradio).
bool mInfiniteStream;
// True if NotifyDecodedStreamMainThreadStateChanged should retrigger
// PlaybackEnded when mDecodedStream->mStream finishes.
bool mTriggerPlaybackEndedWhenSourceStreamFinishes;
// Start timer to update download progress information.
nsresult StartProgress();

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

@ -681,34 +681,34 @@ void MediaDecoderStateMachine::SendStreamData()
nsAutoTArray<VideoData*,10> video;
// It's OK to hold references to the VideoData only the decoder thread
// pops from the queue.
mReader->VideoQueue().GetElementsAfter(stream->mNextVideoTime + mStartTime, &video);
mReader->VideoQueue().GetElementsAfter(stream->mNextVideoTime, &video);
VideoSegment output;
for (uint32_t i = 0; i < video.Length(); ++i) {
VideoData* v = video[i];
if (stream->mNextVideoTime + mStartTime < v->mTime) {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing last video to MediaStream %p for %lld ms",
if (stream->mNextVideoTime < v->mTime) {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing last video to MediaStream %p for %lldus",
mDecoder.get(), mediaStream,
v->mTime - (stream->mNextVideoTime + mStartTime)));
v->mTime - stream->mNextVideoTime));
// Write last video frame to catch up. mLastVideoImage can be null here
// which is fine, it just means there's no video.
WriteVideoToMediaStream(stream->mLastVideoImage,
v->mTime - (stream->mNextVideoTime + mStartTime), stream->mLastVideoImageDisplaySize,
v->mTime - stream->mNextVideoTime, stream->mLastVideoImageDisplaySize,
&output);
stream->mNextVideoTime = v->mTime - mStartTime;
stream->mNextVideoTime = v->mTime;
}
if (stream->mNextVideoTime + mStartTime < v->GetEndTime()) {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing video frame %lld to MediaStream %p for %lld ms",
if (stream->mNextVideoTime < v->GetEndTime()) {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing video frame %lldus to MediaStream %p for %lldus",
mDecoder.get(), v->mTime, mediaStream,
v->GetEndTime() - (stream->mNextVideoTime + mStartTime)));
v->GetEndTime() - stream->mNextVideoTime));
WriteVideoToMediaStream(v->mImage,
v->GetEndTime() - (stream->mNextVideoTime + mStartTime), v->mDisplay,
v->GetEndTime() - stream->mNextVideoTime, v->mDisplay,
&output);
stream->mNextVideoTime = v->GetEndTime() - mStartTime;
stream->mNextVideoTime = v->GetEndTime();
stream->mLastVideoImage = v->mImage;
stream->mLastVideoImageDisplaySize = v->mDisplay;
} else {
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder skipping writing video frame %lld to MediaStream",
mDecoder.get(), v->mTime));
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder skipping writing video frame %lldus (end %lldus) to MediaStream",
mDecoder.get(), v->mTime, v->GetEndTime()));
}
}
if (output.GetDuration() > 0) {
@ -1313,7 +1313,7 @@ void MediaDecoderStateMachine::StopPlayback()
mDecoder->NotifyPlaybackStopped();
if (IsPlaying()) {
mPlayDuration += DurationToUsecs(TimeStamp::Now() - mPlayStartTime);
mPlayDuration = GetClock();
mPlayStartTime = TimeStamp();
}
// Notify the audio thread, so that it notices that we've stopped playing,
@ -1336,6 +1336,15 @@ void MediaDecoderStateMachine::SetSyncPointForMediaStream()
mSyncPointInDecodedStream = mStartTime + mPlayDuration;
}
int64_t MediaDecoderStateMachine::GetCurrentTimeViaMediaStreamSync()
{
AssertCurrentThreadInMonitor();
NS_ASSERTION(mSyncPointInDecodedStream >= 0, "Should have set up sync point");
DecodedStreamData* stream = mDecoder->GetDecodedStream();
StreamTime streamDelta = stream->GetLastOutputTime() - mSyncPointInMediaStream;
return mSyncPointInDecodedStream + MediaTimeToMicroseconds(streamDelta);
}
void MediaDecoderStateMachine::StartPlayback()
{
DECODER_LOG(PR_LOG_DEBUG, ("%p StartPlayback()", mDecoder.get()));
@ -2369,10 +2378,14 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
}
StopAudioThread();
if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING) {
// When we're decoding to a stream, the stream's main-thread finish signal
// will take care of calling MediaDecoder::PlaybackEnded.
if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
!mDecoder->GetDecodedStream()) {
int64_t videoTime = HasVideo() ? mVideoFrameEndTime : 0;
int64_t clockTime = std::max(mEndTime, std::max(videoTime, GetAudioClock()));
UpdatePlaybackPosition(clockTime);
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
@ -2409,7 +2422,6 @@ void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
int64_t
MediaDecoderStateMachine::GetAudioClock()
{
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
// We must hold the decoder monitor while using the audio stream off the
// audio thread to ensure that it doesn't get destroyed on the audio thread
// while we're using it.
@ -2444,8 +2456,8 @@ int64_t MediaDecoderStateMachine::GetVideoStreamPosition()
return mBasePosition + pos * mPlaybackRate + mStartTime;
}
int64_t MediaDecoderStateMachine::GetClock() {
NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
int64_t MediaDecoderStateMachine::GetClock()
{
AssertCurrentThreadInMonitor();
// Determine the clock time. If we've got audio, and we've not reached
@ -2457,9 +2469,7 @@ int64_t MediaDecoderStateMachine::GetClock() {
if (!IsPlaying()) {
clock_time = mPlayDuration + mStartTime;
} else if (stream) {
NS_ASSERTION(mSyncPointInDecodedStream >= 0, "Should have set up sync point");
StreamTime streamDelta = stream->GetLastOutputTime() - mSyncPointInMediaStream;
clock_time = mSyncPointInDecodedStream + MediaTimeToMicroseconds(streamDelta);
clock_time = GetCurrentTimeViaMediaStreamSync();
} else {
int64_t audio_time = GetAudioClock();
if (HasAudio() && !mAudioCompleted && audio_time != -1) {

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше