mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge inbound to m-c a=merge
This commit is contained in:
commit
f1bff82d6f
@ -889,6 +889,10 @@ pref("network.sntp.timeout", 30); // In seconds.
|
||||
|
||||
// Enable dataStore
|
||||
pref("dom.datastore.enabled", true);
|
||||
// When an entry is changed, use two timers to fire system messages in a more
|
||||
// moderate pattern.
|
||||
pref("dom.datastore.sysMsgOnChangeShortTimeoutSec", 10);
|
||||
pref("dom.datastore.sysMsgOnChangeLongTimeoutSec", 60);
|
||||
|
||||
// DOM Inter-App Communication API.
|
||||
pref("dom.inter-app-communication-api.enabled", true);
|
||||
|
@ -10,6 +10,13 @@ xul|window xul|scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Bug 1041576 - Scrollable with scrollgrab should not have scrollbars */
|
||||
@-moz-document domain(system.gaiamobile.org) {
|
||||
.browser-container > xul|scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
html xul|scrollbar[root="true"] {
|
||||
position: relative;
|
||||
z-index: 2147483647;
|
||||
|
@ -683,6 +683,13 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.form-validation-anchor {
|
||||
/* should occupy space but not be visible */
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#addon-progress-notification {
|
||||
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#addon-progress-notification");
|
||||
}
|
||||
|
@ -173,6 +173,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
|
||||
"resource:///modules/TabCrashReporter.jsm");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
|
||||
"resource:///modules/FormValidationHandler.jsm");
|
||||
|
||||
let gInitialPages = [
|
||||
"about:blank",
|
||||
"about:newtab",
|
||||
@ -665,104 +668,6 @@ var gPopupBlockerObserver = {
|
||||
}
|
||||
};
|
||||
|
||||
const gFormSubmitObserver = {
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
|
||||
|
||||
panel: null,
|
||||
|
||||
init: function()
|
||||
{
|
||||
this.panel = document.getElementById('invalid-form-popup');
|
||||
},
|
||||
|
||||
notifyInvalidSubmit : function (aFormElement, aInvalidElements)
|
||||
{
|
||||
// We are going to handle invalid form submission attempt by focusing the
|
||||
// first invalid element and show the corresponding validation message in a
|
||||
// panel attached to the element.
|
||||
if (!aInvalidElements.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't show the popup if the current tab doesn't contain the invalid form.
|
||||
if (gBrowser.contentDocument !=
|
||||
aFormElement.ownerDocument.defaultView.top.document) {
|
||||
return;
|
||||
}
|
||||
|
||||
let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
|
||||
|
||||
if (!(element instanceof HTMLInputElement ||
|
||||
element instanceof HTMLTextAreaElement ||
|
||||
element instanceof HTMLSelectElement ||
|
||||
element instanceof HTMLButtonElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.panel.firstChild.textContent = element.validationMessage;
|
||||
|
||||
element.focus();
|
||||
|
||||
// If the user interacts with the element and makes it valid or leaves it,
|
||||
// we want to remove the popup.
|
||||
// We could check for clicks but a click is already removing the popup.
|
||||
function blurHandler() {
|
||||
gFormSubmitObserver.panel.hidePopup();
|
||||
};
|
||||
function inputHandler(e) {
|
||||
if (e.originalTarget.validity.valid) {
|
||||
gFormSubmitObserver.panel.hidePopup();
|
||||
} else {
|
||||
// If the element is now invalid for a new reason, we should update the
|
||||
// error message.
|
||||
if (gFormSubmitObserver.panel.firstChild.textContent !=
|
||||
e.originalTarget.validationMessage) {
|
||||
gFormSubmitObserver.panel.firstChild.textContent =
|
||||
e.originalTarget.validationMessage;
|
||||
}
|
||||
}
|
||||
};
|
||||
element.addEventListener("input", inputHandler, false);
|
||||
element.addEventListener("blur", blurHandler, false);
|
||||
|
||||
// One event to bring them all and in the darkness bind them.
|
||||
this.panel.addEventListener("popuphiding", function onPopupHiding(aEvent) {
|
||||
aEvent.target.removeEventListener("popuphiding", onPopupHiding, false);
|
||||
element.removeEventListener("input", inputHandler, false);
|
||||
element.removeEventListener("blur", blurHandler, false);
|
||||
}, false);
|
||||
|
||||
this.panel.hidden = false;
|
||||
|
||||
// We want to show the popup at the middle of checkbox and radio buttons
|
||||
// and where the content begin for the other elements.
|
||||
let offset = 0;
|
||||
let position = "";
|
||||
|
||||
if (element.tagName == 'INPUT' &&
|
||||
(element.type == 'radio' || element.type == 'checkbox')) {
|
||||
position = "bottomcenter topleft";
|
||||
} else {
|
||||
let win = element.ownerDocument.defaultView;
|
||||
let style = win.getComputedStyle(element, null);
|
||||
let utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
if (style.direction == 'rtl') {
|
||||
offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
|
||||
} else {
|
||||
offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
|
||||
}
|
||||
|
||||
offset = Math.round(offset * utils.fullZoom);
|
||||
|
||||
position = "after_start";
|
||||
}
|
||||
|
||||
this.panel.openPopup(element, position, offset, 0);
|
||||
}
|
||||
};
|
||||
|
||||
function gKeywordURIFixup(fixupInfo, topic, data) {
|
||||
fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
|
||||
|
||||
@ -1184,13 +1089,11 @@ var gBrowserInit = {
|
||||
Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
|
||||
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
|
||||
Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
|
||||
Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
|
||||
Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup", false);
|
||||
|
||||
BrowserOffline.init();
|
||||
OfflineApps.init();
|
||||
IndexedDBPromptHelper.init();
|
||||
gFormSubmitObserver.init();
|
||||
gRemoteTabsUI.init();
|
||||
|
||||
// Initialize the full zoom setting.
|
||||
@ -1485,7 +1388,6 @@ var gBrowserInit = {
|
||||
Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
|
||||
Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
|
||||
Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
|
||||
Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
|
||||
Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
|
||||
|
||||
try {
|
||||
@ -3780,10 +3682,8 @@ var XULBrowserWindow = {
|
||||
onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) {
|
||||
var location = aLocationURI ? aLocationURI.spec : "";
|
||||
|
||||
// Hide the form invalid popup.
|
||||
if (gFormSubmitObserver.panel) {
|
||||
gFormSubmitObserver.panel.hidePopup();
|
||||
}
|
||||
// If displayed, hide the form validation popup.
|
||||
FormValidationHandler.hidePopup();
|
||||
|
||||
let pageTooltip = document.getElementById("aHTMLTooltip");
|
||||
let tooltipNode = pageTooltip.triggerNode;
|
||||
|
@ -8,6 +8,8 @@ let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler",
|
||||
"resource:///modules/ContentLinkHandler.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
|
||||
@ -18,11 +20,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
||||
"resource:///modules/UITour.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
|
||||
"resource:///modules/FormSubmitObserver.jsm");
|
||||
|
||||
// Creates a new nsIURI object.
|
||||
function makeURI(uri, originCharset, baseURI) {
|
||||
return Services.io.newURI(uri, originCharset, baseURI);
|
||||
}
|
||||
// TabChildGlobal
|
||||
var global = this;
|
||||
|
||||
// Load the form validation popup handler
|
||||
var formSubmitObserver = new FormSubmitObserver(content, this);
|
||||
|
||||
addMessageListener("Browser:HideSessionRestoreButton", function (message) {
|
||||
// Hide session restore button on about:home
|
||||
@ -328,9 +333,6 @@ let ContentSearchMediator = {
|
||||
};
|
||||
ContentSearchMediator.init(this);
|
||||
|
||||
|
||||
var global = this;
|
||||
|
||||
// Lazily load the finder code
|
||||
addMessageListener("Finder:Initialize", function () {
|
||||
let {RemoteFinderListener} = Cu.import("resource://gre/modules/RemoteFinder.jsm", {});
|
||||
@ -465,7 +467,7 @@ let ClickEventHandler = {
|
||||
// In case of XLink, we don't return the node we got href from since
|
||||
// callers expect <a>-like elements.
|
||||
// Note: makeURI() will throw if aUri is not a valid URI.
|
||||
return [href ? makeURI(href, null, baseURI).spec : null, null];
|
||||
return [href ? BrowserUtils.makeURI(href, null, baseURI).spec : null, null];
|
||||
}
|
||||
};
|
||||
ClickEventHandler.init();
|
||||
|
@ -151,6 +151,22 @@
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<property name="formValidationAnchor" readonly="true">
|
||||
<getter><![CDATA[
|
||||
if (this.mCurrentTab._formValidationAnchor) {
|
||||
return this.mCurrentTab._formValidationAnchor;
|
||||
}
|
||||
let stack = this.mCurrentBrowser.parentNode;
|
||||
// Create an anchor for the form validation popup
|
||||
const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
let formValidationAnchor = document.createElementNS(NS_XUL, "hbox");
|
||||
formValidationAnchor.className = "form-validation-anchor";
|
||||
formValidationAnchor.hidden = true;
|
||||
stack.appendChild(formValidationAnchor);
|
||||
return this.mCurrentTab._formValidationAnchor = formValidationAnchor;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<method name="isFindBarInitialized">
|
||||
<parameter name="aTab"/>
|
||||
<body><![CDATA[
|
||||
|
@ -29,6 +29,22 @@ let gObserver = {
|
||||
}
|
||||
};
|
||||
|
||||
function getDocHeader()
|
||||
{
|
||||
return "data:text/html,<html><head><meta charset='utf-8'></head><body>" + getEmptyFrame();
|
||||
}
|
||||
|
||||
function getDocFooter()
|
||||
{
|
||||
return "</body></html>";
|
||||
}
|
||||
|
||||
function getEmptyFrame()
|
||||
{
|
||||
return "<iframe style='width:100px; height:30px; margin:3px; border:1px solid lightgray;' " +
|
||||
"name='t' srcdoc=\"<html><head><meta charset='utf-8'></head><body>form target</body></html>\"></iframe>";
|
||||
}
|
||||
|
||||
var testId = -1;
|
||||
|
||||
function nextTest()
|
||||
@ -54,7 +70,7 @@ var tests = [
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<html><body><iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='s' type='submit'></form></body></html>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
@ -82,7 +98,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -102,8 +118,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -116,7 +133,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='i' required><input required><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input><input id='i' required><input required><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -136,8 +153,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -150,7 +168,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -176,8 +194,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -190,7 +209,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' id='i' required><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input type='email' id='i' required><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -216,8 +235,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -230,7 +250,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -256,8 +276,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -269,7 +290,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -295,8 +316,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -308,7 +330,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -336,8 +358,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -345,21 +368,24 @@ function()
|
||||
},
|
||||
|
||||
/**
|
||||
* In this test, we check that nothing happen (no focus nor popup) if the
|
||||
* invalid form is submitted in another tab than the current focused one
|
||||
* (submitted in background).
|
||||
* In this test, we check that nothing happen if the invalid form is
|
||||
* submitted in a background tab.
|
||||
*/
|
||||
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
|
||||
// Observers don't propagate currently across processes. We may add support for this in the
|
||||
// future via the addon compat layer.
|
||||
if (gBrowser.isRemoteBrowser) {
|
||||
nextTest();
|
||||
return;
|
||||
}
|
||||
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gObserver.notifyInvalidSubmit = function() {
|
||||
executeSoon(function() {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
isnot(doc.activeElement, doc.getElementById('i'),
|
||||
"We should not focus the invalid element when the form is submitted in background");
|
||||
|
||||
checkPopupHide();
|
||||
|
||||
// Clean-up
|
||||
@ -381,7 +407,9 @@ function()
|
||||
|
||||
isnot(gBrowser.selectedTab.linkedBrowser, browser,
|
||||
"This tab should have been loaded in background");
|
||||
browser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
browser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
|
||||
@ -393,7 +421,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -415,8 +443,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -428,7 +457,7 @@ function()
|
||||
*/
|
||||
function()
|
||||
{
|
||||
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' required id='i'><input id='s' type='submit'></form>";
|
||||
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input type='email' required id='i'><input id='s' type='submit'></form>" + getDocFooter();
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gInvalidFormPopup.addEventListener("popupshown", function() {
|
||||
@ -463,8 +492,9 @@ function()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
|
@ -29,9 +29,10 @@ function test()
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function(aEvent) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument
|
||||
.getElementById('s').click();
|
||||
executeSoon(function() {
|
||||
gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument
|
||||
.getElementById('s').click();
|
||||
});
|
||||
}, true);
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
|
@ -117,6 +117,9 @@ XPCOMUtils.defineLazyGetter(this, "ShellService", function() {
|
||||
}
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
|
||||
"resource:///modules/FormValidationHandler.jsm");
|
||||
|
||||
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
|
||||
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
|
||||
|
||||
@ -519,6 +522,7 @@ BrowserGlue.prototype = {
|
||||
SessionStore.init();
|
||||
BrowserUITelemetry.init();
|
||||
ContentSearch.init();
|
||||
FormValidationHandler.init();
|
||||
|
||||
ContentClick.init();
|
||||
RemotePrompt.init();
|
||||
@ -724,6 +728,7 @@ BrowserGlue.prototype = {
|
||||
}
|
||||
#endif
|
||||
webrtcUI.uninit();
|
||||
FormValidationHandler.uninit();
|
||||
},
|
||||
|
||||
// All initial windows have opened.
|
||||
|
@ -906,9 +906,6 @@ PdfStreamConverter.prototype = {
|
||||
aRequest.setResponseHeader('Content-Security-Policy', '', false);
|
||||
aRequest.setResponseHeader('Content-Security-Policy-Report-Only', '',
|
||||
false);
|
||||
aRequest.setResponseHeader('X-Content-Security-Policy', '', false);
|
||||
aRequest.setResponseHeader('X-Content-Security-Policy-Report-Only', '',
|
||||
false);
|
||||
}
|
||||
|
||||
PdfJsTelemetry.onViewerIsUsed();
|
||||
|
251
browser/modules/FormSubmitObserver.jsm
Normal file
251
browser/modules/FormSubmitObserver.jsm
Normal file
@ -0,0 +1,251 @@
|
||||
/* 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/. */
|
||||
|
||||
/*
|
||||
* Handles the validation callback from nsIFormFillController and
|
||||
* the display of the help panel on invalid elements.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
let HTMLInputElement = Ci.nsIDOMHTMLInputElement;
|
||||
let HTMLTextAreaElement = Ci.nsIDOMHTMLTextAreaElement;
|
||||
let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
|
||||
let HTMLButtonElement = Ci.nsIDOMHTMLButtonElement;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "FormSubmitObserver" ];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/BrowserUtils.jsm");
|
||||
|
||||
function FormSubmitObserver(aWindow, aTabChildGlobal) {
|
||||
this.init(aWindow, aTabChildGlobal);
|
||||
}
|
||||
|
||||
FormSubmitObserver.prototype =
|
||||
{
|
||||
_validationMessage: "",
|
||||
_content: null,
|
||||
_element: null,
|
||||
|
||||
/*
|
||||
* Public apis
|
||||
*/
|
||||
|
||||
init: function(aWindow, aTabChildGlobal)
|
||||
{
|
||||
this._content = aWindow;
|
||||
this._tab = aTabChildGlobal;
|
||||
this._mm =
|
||||
this._content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.sameTypeRootTreeItem
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
|
||||
// nsIFormSubmitObserver callback about invalid forms. See HTMLFormElement
|
||||
// for details.
|
||||
Services.obs.addObserver(this, "invalidformsubmit", false);
|
||||
this._tab.addEventListener("pageshow", this, false);
|
||||
this._tab.addEventListener("unload", this, false);
|
||||
},
|
||||
|
||||
uninit: function()
|
||||
{
|
||||
Services.obs.removeObserver(this, "invalidformsubmit");
|
||||
this._content.removeEventListener("pageshow", this, false);
|
||||
this._content.removeEventListener("unload", this, false);
|
||||
this._mm = null;
|
||||
this._element = null;
|
||||
this._content = null;
|
||||
this._tab = null;
|
||||
},
|
||||
|
||||
/*
|
||||
* Events
|
||||
*/
|
||||
|
||||
handleEvent: function (aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "pageshow":
|
||||
if (this._isRootDocumentEvent(aEvent)) {
|
||||
this._hidePopup();
|
||||
}
|
||||
break;
|
||||
case "unload":
|
||||
this.uninit();
|
||||
break;
|
||||
case "input":
|
||||
this._onInput(aEvent);
|
||||
break;
|
||||
case "blur":
|
||||
this._onBlur(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* nsIFormSubmitObserver
|
||||
*/
|
||||
|
||||
notifyInvalidSubmit : function (aFormElement, aInvalidElements)
|
||||
{
|
||||
// We are going to handle invalid form submission attempt by focusing the
|
||||
// first invalid element and show the corresponding validation message in a
|
||||
// panel attached to the element.
|
||||
if (!aInvalidElements.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insure that this is the FormSubmitObserver associated with the form
|
||||
// element / window this notification is about.
|
||||
if (this._content != aFormElement.ownerDocument.defaultView.top.document.defaultView) {
|
||||
return;
|
||||
}
|
||||
|
||||
let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
|
||||
if (!(element instanceof HTMLInputElement ||
|
||||
element instanceof HTMLTextAreaElement ||
|
||||
element instanceof HTMLSelectElement ||
|
||||
element instanceof HTMLButtonElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't connect up to the same element more than once.
|
||||
if (this._element == element) {
|
||||
this._showPopup(element);
|
||||
return;
|
||||
}
|
||||
this._element = element;
|
||||
|
||||
element.focus();
|
||||
|
||||
this._validationMessage = element.validationMessage;
|
||||
|
||||
// Watch for input changes which may change the validation message.
|
||||
element.addEventListener("input", this, false);
|
||||
|
||||
// Watch for focus changes so we can disconnect our listeners and
|
||||
// hide the popup.
|
||||
element.addEventListener("blur", this, false);
|
||||
|
||||
this._showPopup(element);
|
||||
},
|
||||
|
||||
/*
|
||||
* Internal
|
||||
*/
|
||||
|
||||
/*
|
||||
* Handles input changes on the form element we've associated a popup
|
||||
* with. Updates the validation message or closes the popup if form data
|
||||
* becomes valid.
|
||||
*/
|
||||
_onInput: function (aEvent) {
|
||||
let element = aEvent.originalTarget;
|
||||
|
||||
// If the form input is now valid, hide the popup.
|
||||
if (element.validity.valid) {
|
||||
this._hidePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the element is still invalid for a new reason, we should update
|
||||
// the popup error message.
|
||||
if (this._validationMessage != element.validationMessage) {
|
||||
this._validationMessage = element.validationMessage;
|
||||
this._showPopup(element);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Blur event handler in which we disconnect from the form element and
|
||||
* hide the popup.
|
||||
*/
|
||||
_onBlur: function (aEvent) {
|
||||
aEvent.originalTarget.removeEventListener("input", this, false);
|
||||
aEvent.originalTarget.removeEventListener("blur", this, false);
|
||||
this._element = null;
|
||||
this._hidePopup();
|
||||
},
|
||||
|
||||
/*
|
||||
* Send the show popup message to chrome with appropriate position
|
||||
* information. Can be called repetitively to update the currently
|
||||
* displayed popup position and text.
|
||||
*/
|
||||
_showPopup: function (aElement) {
|
||||
// Collect positional information and show the popup
|
||||
let panelData = {};
|
||||
|
||||
panelData.message = this._validationMessage;
|
||||
|
||||
// Note, this is relative to the browser and needs to be translated
|
||||
// in chrome.
|
||||
panelData.contentRect = this._msgRect(aElement);
|
||||
|
||||
// We want to show the popup at the middle of checkbox and radio buttons
|
||||
// and where the content begin for the other elements.
|
||||
let offset = 0;
|
||||
let position = "";
|
||||
|
||||
if (aElement.tagName == 'INPUT' &&
|
||||
(aElement.type == 'radio' || aElement.type == 'checkbox')) {
|
||||
panelData.position = "bottomcenter topleft";
|
||||
} else {
|
||||
let win = aElement.ownerDocument.defaultView;
|
||||
let style = win.getComputedStyle(aElement, null);
|
||||
if (style.direction == 'rtl') {
|
||||
offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
|
||||
} else {
|
||||
offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
|
||||
}
|
||||
let zoomFactor = this._getWindowUtils().fullZoom;
|
||||
panelData.offset = Math.round(offset * zoomFactor);
|
||||
panelData.position = "after_start";
|
||||
}
|
||||
this._mm.sendAsyncMessage("FormValidation:ShowPopup", panelData);
|
||||
},
|
||||
|
||||
_hidePopup: function () {
|
||||
this._mm.sendAsyncMessage("FormValidation:HidePopup", {});
|
||||
},
|
||||
|
||||
_getWindowUtils: function () {
|
||||
return this._content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
},
|
||||
|
||||
_isRootDocumentEvent: function (aEvent) {
|
||||
if (this._content == null) {
|
||||
return true;
|
||||
}
|
||||
let target = aEvent.originalTarget;
|
||||
return (target == this._content.document ||
|
||||
(target.ownerDocument && target.ownerDocument == this._content.document));
|
||||
},
|
||||
|
||||
/*
|
||||
* Return a message manager rect for the element's bounding client rect
|
||||
* in top level browser coords.
|
||||
*/
|
||||
_msgRect: function (aElement) {
|
||||
let domRect = aElement.getBoundingClientRect();
|
||||
let zoomFactor = this._getWindowUtils().fullZoom;
|
||||
let { offsetX, offsetY } = BrowserUtils.offsetToTopLevelWindow(this._content, aElement);
|
||||
return {
|
||||
left: (domRect.left + offsetX) * zoomFactor,
|
||||
top: (domRect.top + offsetY) * zoomFactor,
|
||||
width: domRect.width * zoomFactor,
|
||||
height: domRect.height * zoomFactor
|
||||
};
|
||||
},
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver])
|
||||
};
|
157
browser/modules/FormValidationHandler.jsm
Normal file
157
browser/modules/FormValidationHandler.jsm
Normal file
@ -0,0 +1,157 @@
|
||||
/* 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/. */
|
||||
|
||||
/*
|
||||
* Chrome side handling of form validation popup.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "FormValidationHandler" ];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let FormValidationHandler =
|
||||
{
|
||||
_panel: null,
|
||||
_anchor: null,
|
||||
|
||||
/*
|
||||
* Public apis
|
||||
*/
|
||||
|
||||
init: function () {
|
||||
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
|
||||
mm.addMessageListener("FormValidation:ShowPopup", this);
|
||||
mm.addMessageListener("FormValidation:HidePopup", this);
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
|
||||
mm.removeMessageListener("FormValidation:ShowPopup", this);
|
||||
mm.removeMessageListener("FormValidation:HidePopup", this);
|
||||
this._panel = null;
|
||||
this._anchor = null;
|
||||
},
|
||||
|
||||
hidePopup: function () {
|
||||
this._hidePopup();
|
||||
},
|
||||
|
||||
/*
|
||||
* Events
|
||||
*/
|
||||
|
||||
receiveMessage: function (aMessage) {
|
||||
let window = aMessage.target.ownerDocument.defaultView;
|
||||
let json = aMessage.json;
|
||||
let tabBrowser = window.gBrowser;
|
||||
switch (aMessage.name) {
|
||||
case "FormValidation:ShowPopup":
|
||||
// target is the <browser>, make sure we're receiving a message
|
||||
// from the foreground tab.
|
||||
if (tabBrowser && aMessage.target != tabBrowser.selectedBrowser) {
|
||||
return;
|
||||
}
|
||||
this._showPopup(window, json);
|
||||
break;
|
||||
case "FormValidation:HidePopup":
|
||||
this._hidePopup();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
this._hidePopup();
|
||||
},
|
||||
|
||||
handleEvent: function (aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "FullZoomChange":
|
||||
case "TextZoomChange":
|
||||
case "ZoomChangeUsingMouseWheel":
|
||||
case "scroll":
|
||||
this._hidePopup();
|
||||
break;
|
||||
case "popuphiding":
|
||||
this._onPopupHiding(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Internal
|
||||
*/
|
||||
|
||||
_onPopupHiding: function (aEvent) {
|
||||
aEvent.originalTarget.removeEventListener("popuphiding", this, true);
|
||||
let tabBrowser = aEvent.originalTarget.ownerDocument.getElementById("content");
|
||||
tabBrowser.selectedBrowser.removeEventListener("scroll", this, true);
|
||||
tabBrowser.selectedBrowser.removeEventListener("FullZoomChange", this, false);
|
||||
tabBrowser.selectedBrowser.removeEventListener("TextZoomChange", this, false);
|
||||
tabBrowser.selectedBrowser.removeEventListener("ZoomChangeUsingMouseWheel", this, false);
|
||||
|
||||
this._panel.hidden = true;
|
||||
this._panel = null;
|
||||
this._anchor.hidden = true;
|
||||
this._anchor = null;
|
||||
},
|
||||
|
||||
/*
|
||||
* Shows the form validation popup at a specified position or updates the
|
||||
* messaging and position if the popup is already displayed.
|
||||
*
|
||||
* @aWindow - the chrome window
|
||||
* @aPanelData - Object that contains popup information
|
||||
* aPanelData stucture detail:
|
||||
* contentRect - the bounding client rect of the target element. If
|
||||
* content is remote, this is relative to the browser, otherwise its
|
||||
* relative to the window.
|
||||
* position - popup positional string constants.
|
||||
* message - the form element validation message text.
|
||||
*/
|
||||
_showPopup: function (aWindow, aPanelData) {
|
||||
let previouslyShown = !!this._panel;
|
||||
this._panel = aWindow.document.getElementById("invalid-form-popup");
|
||||
this._panel.firstChild.textContent = aPanelData.message;
|
||||
this._panel.hidden = false;
|
||||
|
||||
let tabBrowser = aWindow.gBrowser;
|
||||
this._anchor = tabBrowser.formValidationAnchor;
|
||||
this._anchor.left = aPanelData.contentRect.left;
|
||||
this._anchor.top = aPanelData.contentRect.top;
|
||||
this._anchor.width = aPanelData.contentRect.width;
|
||||
this._anchor.height = aPanelData.contentRect.height;
|
||||
this._anchor.hidden = false;
|
||||
|
||||
// Display the panel if it isn't already visible.
|
||||
if (!previouslyShown) {
|
||||
// Cleanup after the popup is hidden
|
||||
this._panel.addEventListener("popuphiding", this, true);
|
||||
|
||||
// Hide if the user scrolls the page
|
||||
tabBrowser.selectedBrowser.addEventListener("scroll", this, true);
|
||||
tabBrowser.selectedBrowser.addEventListener("FullZoomChange", this, false);
|
||||
tabBrowser.selectedBrowser.addEventListener("TextZoomChange", this, false);
|
||||
tabBrowser.selectedBrowser.addEventListener("ZoomChangeUsingMouseWheel", this, false);
|
||||
|
||||
// Open the popup
|
||||
this._panel.openPopup(this._anchor, aPanelData.position, 0, 0, false);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Hide the popup if currently displayed. Will fire an event to onPopupHiding
|
||||
* above if visible.
|
||||
*/
|
||||
_hidePopup: function () {
|
||||
if (this._panel) {
|
||||
this._panel.hidePopup();
|
||||
}
|
||||
}
|
||||
};
|
@ -17,6 +17,8 @@ EXTRA_JS_MODULES += [
|
||||
'ContentSearch.jsm',
|
||||
'CustomizationTabPreloader.jsm',
|
||||
'Feeds.jsm',
|
||||
'FormSubmitObserver.jsm',
|
||||
'FormValidationHandler.jsm',
|
||||
'NetworkPrioritizer.jsm',
|
||||
'offlineAppCache.jsm',
|
||||
'PanelFrame.jsm',
|
||||
|
@ -601,7 +601,7 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
nsCOMPtr<nsIURI> uri = do_QueryInterface(aBlockedContentSource);
|
||||
// could be a string or URI
|
||||
if (uri) {
|
||||
uri->GetSpec(reportBlockedURI);
|
||||
uri->GetSpecIgnoringRef(reportBlockedURI);
|
||||
} else {
|
||||
nsCOMPtr<nsISupportsCString> cstr = do_QueryInterface(aBlockedContentSource);
|
||||
if (cstr) {
|
||||
@ -619,7 +619,7 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
// document-uri
|
||||
if (aOriginalURI) {
|
||||
nsAutoCString reportDocumentURI;
|
||||
aOriginalURI->GetSpec(reportDocumentURI);
|
||||
aOriginalURI->GetSpecIgnoringRef(reportDocumentURI);
|
||||
report.mCsp_report.mDocument_uri = NS_ConvertUTF8toUTF16(reportDocumentURI);
|
||||
}
|
||||
|
||||
@ -641,6 +641,14 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
|
||||
|
||||
// source-file
|
||||
if (!aSourceFile.IsEmpty()) {
|
||||
// if aSourceFile is a URI, we have to make sure to strip fragments
|
||||
nsCOMPtr<nsIURI> sourceURI;
|
||||
NS_NewURI(getter_AddRefs(sourceURI), aSourceFile);
|
||||
if (sourceURI) {
|
||||
nsAutoCString spec;
|
||||
sourceURI->GetSpecIgnoringRef(spec);
|
||||
aSourceFile = NS_ConvertUTF8toUTF16(spec);
|
||||
}
|
||||
report.mCsp_report.mSource_file.Construct();
|
||||
report.mCsp_report.mSource_file.Value() = aSourceFile;
|
||||
}
|
||||
|
@ -1214,8 +1214,6 @@ GK_ATOM(withParam, "with-param")
|
||||
GK_ATOM(wizard, "wizard")
|
||||
GK_ATOM(wrap, "wrap")
|
||||
GK_ATOM(headerDNSPrefetchControl,"x-dns-prefetch-control")
|
||||
GK_ATOM(headerOldCSP, "x-content-security-policy")
|
||||
GK_ATOM(headerOldCSPReportOnly, "x-content-security-policy-report-only")
|
||||
GK_ATOM(headerCSP, "content-security-policy")
|
||||
GK_ATOM(headerCSPReportOnly, "content-security-policy-report-only")
|
||||
GK_ATOM(headerXFO, "x-frame-options")
|
||||
|
@ -11,15 +11,15 @@ var thisSite = "http://mochi.test:8888";
|
||||
var otherSite = "http://example.com";
|
||||
var page = "/tests/content/base/test/csp/file_csp_redirects_page.sjs";
|
||||
|
||||
var tests = { "font-src": thisSite+page+"?testid=font-src&csp=1&spec=1",
|
||||
"frame-src": thisSite+page+"?testid=frame-src&csp=1&spec=1",
|
||||
"img-src": thisSite+page+"?testid=img-src&csp=1&spec=1",
|
||||
"media-src": thisSite+page+"?testid=media-src&csp=1&spec=1",
|
||||
"object-src": thisSite+page+"?testid=object-src&csp=1&spec=1",
|
||||
"script-src": thisSite+page+"?testid=script-src&csp=1&spec=1",
|
||||
"style-src": thisSite+page+"?testid=style-src&csp=1&spec=1",
|
||||
"worker": thisSite+page+"?testid=worker&csp=1&spec=1",
|
||||
"xhr-src": thisSite+page+"?testid=xhr-src&csp=1&spec=1",
|
||||
var tests = { "font-src": thisSite+page+"?testid=font-src&csp=1",
|
||||
"frame-src": thisSite+page+"?testid=frame-src&csp=1",
|
||||
"img-src": thisSite+page+"?testid=img-src&csp=1",
|
||||
"media-src": thisSite+page+"?testid=media-src&csp=1",
|
||||
"object-src": thisSite+page+"?testid=object-src&csp=1",
|
||||
"script-src": thisSite+page+"?testid=script-src&csp=1",
|
||||
"style-src": thisSite+page+"?testid=style-src&csp=1",
|
||||
"worker": thisSite+page+"?testid=worker&csp=1",
|
||||
"xhr-src": thisSite+page+"?testid=xhr-src&csp=1",
|
||||
};
|
||||
|
||||
var container = document.getElementById("container");
|
||||
|
@ -15,11 +15,7 @@ function handleRequest(request, response)
|
||||
|
||||
// CSP header value
|
||||
if (query["csp"] == 1) {
|
||||
if (query["spec"] == 1) {
|
||||
response.setHeader("Content-Security-Policy", "default-src 'self' ; style-src 'self' 'unsafe-inline'", false);
|
||||
} else {
|
||||
response.setHeader("X-Content-Security-Policy", "allow 'self'", false);
|
||||
}
|
||||
response.setHeader("Content-Security-Policy", "default-src 'self' ; style-src 'self' 'unsafe-inline'", false);
|
||||
}
|
||||
|
||||
// downloadable font that redirects to another site
|
||||
|
@ -37,6 +37,11 @@ const docUri = "http://mochi.test:8888/tests/content/base/test/csp/file_csp_test
|
||||
window.checkResults = function(reportObj) {
|
||||
var cspReport = reportObj["csp-report"];
|
||||
|
||||
// The following uris' fragments should be stripped before reporting:
|
||||
// * document-uri
|
||||
// * blocked-uri
|
||||
// * source-file
|
||||
// see http://www.w3.org/TR/CSP11/#violation-reports
|
||||
is(cspReport["document-uri"], docUri, "Incorrect document-uri");
|
||||
|
||||
// we can not test for the whole referrer since it includes platform specific information
|
||||
@ -130,6 +135,9 @@ var src = "file_csp_testserver.sjs";
|
||||
src += "?file=" + escape(testfile);
|
||||
// append the CSP that should be used to serve the file
|
||||
src += "&csp=" + escape(policy);
|
||||
// appending a fragment so we can test that it's correctly stripped
|
||||
// for document-uri and source-file.
|
||||
src += "#foo";
|
||||
document.getElementById("cspframe").src = src;
|
||||
|
||||
</script>
|
||||
|
@ -58,12 +58,22 @@ static const int64_t CAN_PLAY_THROUGH_MARGIN = 1;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define DECODER_LOG(type, msg, ...) \
|
||||
PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, this, ##__VA_ARGS__))
|
||||
#define DECODER_LOG(x, ...) \
|
||||
PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, ("Decoder=%p " x, this, ##__VA_ARGS__))
|
||||
#else
|
||||
#define DECODER_LOG(type, msg, ...)
|
||||
#define DECODER_LOG(x, ...)
|
||||
#endif
|
||||
|
||||
static const char* const gPlayStateStr[] = {
|
||||
"START",
|
||||
"LOADING",
|
||||
"PAUSED",
|
||||
"PLAYING",
|
||||
"SEEKING",
|
||||
"ENDED",
|
||||
"SHUTDOWN"
|
||||
};
|
||||
|
||||
class MediaMemoryTracker : public nsIMemoryReporter
|
||||
{
|
||||
virtual ~MediaMemoryTracker();
|
||||
@ -331,7 +341,7 @@ void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
DECODER_LOG(PR_LOG_DEBUG, "RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
|
||||
DECODER_LOG("RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
|
||||
|
||||
DestroyDecodedStream();
|
||||
|
||||
@ -363,7 +373,7 @@ void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
|
||||
bool aFinishWhenEnded)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DECODER_LOG(PR_LOG_DEBUG, "AddOutputStream aStream=%p!", aStream);
|
||||
DECODER_LOG("AddOutputStream aStream=%p!", aStream);
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
@ -524,10 +534,7 @@ nsresult MediaDecoder::OpenResource(nsIStreamListener** aStreamListener)
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
||||
nsresult rv = mResource->Open(aStreamListener);
|
||||
if (NS_FAILED(rv)) {
|
||||
DECODER_LOG(PR_LOG_WARNING, "Failed to open stream!");
|
||||
return rv;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -541,10 +548,7 @@ nsresult MediaDecoder::Load(nsIStreamListener** aStreamListener,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mDecoderStateMachine = CreateStateMachine();
|
||||
if (!mDecoderStateMachine) {
|
||||
DECODER_LOG(PR_LOG_WARNING, "Failed to create state machine!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
NS_ENSURE_TRUE(mDecoderStateMachine, NS_ERROR_FAILURE);
|
||||
|
||||
return InitializeStateMachine(aCloneDonor);
|
||||
}
|
||||
@ -555,11 +559,9 @@ nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
|
||||
NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
|
||||
|
||||
MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor);
|
||||
if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
|
||||
cloneDonor->mDecoderStateMachine : nullptr))) {
|
||||
DECODER_LOG(PR_LOG_WARNING, "Failed to init state machine!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsresult rv = mDecoderStateMachine->Init(
|
||||
cloneDonor ? cloneDonor->mDecoderStateMachine : nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If some parameters got set before the state machine got created,
|
||||
// set them now
|
||||
@ -693,6 +695,10 @@ void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags)
|
||||
return;
|
||||
}
|
||||
|
||||
DECODER_LOG("MetadataLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
|
||||
aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
|
||||
aInfo->HasAudio(), aInfo->HasVideo());
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
if (mPlayState == PLAY_STATE_LOADING && mIsDormant && !mIsExitingDormant) {
|
||||
@ -1006,6 +1012,8 @@ void MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
DECODER_LOG("NotifyDownloadEnded, status=%x", aStatus);
|
||||
|
||||
if (aStatus == NS_BINDING_ABORTED) {
|
||||
// Download has been cancelled by user.
|
||||
if (mOwner) {
|
||||
@ -1168,6 +1176,9 @@ void MediaDecoder::ChangeState(PlayState aState)
|
||||
mDecodedStream->mHaveBlockedForPlayState = blockForPlayState;
|
||||
}
|
||||
}
|
||||
|
||||
DECODER_LOG("ChangeState %s => %s",
|
||||
gPlayStateStr[mPlayState], gPlayStateStr[aState]);
|
||||
mPlayState = aState;
|
||||
|
||||
if (mPlayState == PLAY_STATE_PLAYING) {
|
||||
@ -1265,7 +1276,7 @@ void MediaDecoder::DurationChanged()
|
||||
SetInfinite(mDuration == -1);
|
||||
|
||||
if (mOwner && oldDuration != mDuration && !IsInfinite()) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Duration changed to %lld", mDuration);
|
||||
DECODER_LOG("Duration changed to %lld", mDuration);
|
||||
mOwner->DispatchEvent(NS_LITERAL_STRING("durationchange"));
|
||||
}
|
||||
}
|
||||
@ -1486,10 +1497,8 @@ void MediaDecoder::Invalidate()
|
||||
// Constructs the time ranges representing what segments of the media
|
||||
// are buffered and playable.
|
||||
nsresult MediaDecoder::GetBuffered(dom::TimeRanges* aBuffered) {
|
||||
if (mDecoderStateMachine) {
|
||||
return mDecoderStateMachine->GetBuffered(aBuffered);
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
NS_ENSURE_TRUE(mDecoderStateMachine, NS_ERROR_FAILURE);
|
||||
return mDecoderStateMachine->GetBuffered(aBuffered);
|
||||
}
|
||||
|
||||
size_t MediaDecoder::SizeOfVideoQueue() {
|
||||
|
@ -11,10 +11,8 @@
|
||||
#include "VideoUtils.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Required to init MSCOM by MSCOMInitThreadPoolListener.
|
||||
#include <objbase.h>
|
||||
#include "ThreadPoolCOMListener.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
@ -187,40 +185,6 @@ SharedThreadPool::~SharedThreadPool()
|
||||
MOZ_COUNT_DTOR(SharedThreadPool);
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
// Thread pool listener which ensures that MSCOM is initialized and
|
||||
// deinitialized on the thread pool thread. We may call into WMF or
|
||||
// DirectShow on this thread, so we need MSCOM working.
|
||||
class MSCOMInitThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
|
||||
~MSCOMInitThreadPoolListener() {}
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITHREADPOOLLISTENER
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MSCOMInitThreadPoolListener, nsIThreadPoolListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
MSCOMInitThreadPoolListener::OnThreadCreated()
|
||||
{
|
||||
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread.");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MSCOMInitThreadPoolListener::OnThreadShuttingDown()
|
||||
{
|
||||
CoUninitialize();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#endif // XP_WIN
|
||||
|
||||
nsresult
|
||||
SharedThreadPool::EnsureThreadLimitIsAtLeast(uint32_t aLimit)
|
||||
{
|
||||
|
30
content/media/ThreadPoolCOMListener.cpp
Normal file
30
content/media/ThreadPoolCOMListener.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "ThreadPoolCOMListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(MSCOMInitThreadPoolListener, nsIThreadPoolListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
MSCOMInitThreadPoolListener::OnThreadCreated()
|
||||
{
|
||||
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Failed to initialize MSCOM on decoder thread.");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MSCOMInitThreadPoolListener::OnThreadShuttingDown()
|
||||
{
|
||||
CoUninitialize();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
28
content/media/ThreadPoolCOMListener.h
Normal file
28
content/media/ThreadPoolCOMListener.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef MSCOMInitThreadPoolListener_h_
|
||||
#define MSCOMInitThreadPoolListener_h_
|
||||
|
||||
#include "nsIThreadPool.h"
|
||||
#include <objbase.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Thread pool listener which ensures that MSCOM is initialized and
|
||||
// deinitialized on the thread pool thread. We may call into WMF or
|
||||
// DirectShow on this thread, so we need MSCOM working.
|
||||
class MSCOMInitThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
|
||||
~MSCOMInitThreadPoolListener() {}
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITHREADPOOLLISTENER
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif // MSCOMInitThreadPoolListener_h_
|
@ -100,9 +100,22 @@ GonkAudioDecoderManager::CreateAudioData(int64_t aStreamOffset, AudioData **v) {
|
||||
size_t size;
|
||||
int64_t timeUs;
|
||||
|
||||
if (!(mAudioBuffer != nullptr && mAudioBuffer->data() != nullptr)) {
|
||||
ALOG("Audio Buffer is not valid!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (mAudioBuffer->range_length() == 0) {
|
||||
// Some decoders may return spurious empty buffers that we just want to ignore
|
||||
// quoted from Android's AwesomePlayer.cpp
|
||||
ReleaseAudioBuffer();
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
data = mAudioBuffer->data();
|
||||
dataOffset = mAudioBuffer->range_offset();
|
||||
size = mAudioBuffer->range_length();
|
||||
@ -137,16 +150,12 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset,
|
||||
switch (err) {
|
||||
case OK:
|
||||
{
|
||||
if (mAudioBuffer && mAudioBuffer->range_length() != 0) {
|
||||
int64_t timeUs;
|
||||
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
AudioData* data = nullptr;
|
||||
nsresult rv = CreateAudioData(aStreamOffset, &data);
|
||||
// Frame should be non null only when we succeeded.
|
||||
if (rv != NS_OK) {
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// Decoder outputs a empty video buffer, try again
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
} else if (rv != NS_OK || data == nullptr) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
aOutData = data;
|
||||
@ -165,7 +174,17 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset,
|
||||
}
|
||||
case android::ERROR_END_OF_STREAM:
|
||||
{
|
||||
ALOG("End of Stream");
|
||||
ALOG("Got EOS frame!");
|
||||
AudioData* data = nullptr;
|
||||
nsresult rv = CreateAudioData(aStreamOffset, &data);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// For EOS, no need to do any thing.
|
||||
return NS_ERROR_ABORT;
|
||||
} else if (rv != NS_OK || data == nullptr) {
|
||||
ALOG("Failed to create audio data!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
aOutData = data;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
case -ETIMEDOUT:
|
||||
@ -197,9 +216,20 @@ void GonkAudioDecoderManager::ReleaseAudioBuffer() {
|
||||
nsresult
|
||||
GonkAudioDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
|
||||
uint32_t length = aSample->size;
|
||||
status_t rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
|
||||
if (mDecoder == nullptr) {
|
||||
ALOG("Decoder is not inited");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
status_t rv;
|
||||
if (aSample) {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
|
||||
uint32_t length = aSample->size;
|
||||
rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
|
||||
} else {
|
||||
// Inputted data is null, so it is going to notify decoder EOS
|
||||
rv = mDecoder->Input(0, 0, 0ll, 0);
|
||||
}
|
||||
return rv == OK ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mManager(aManager)
|
||||
, mSignaledEOS(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(GonkMediaDataDecoder);
|
||||
}
|
||||
@ -77,8 +78,9 @@ GonkMediaDataDecoder::ProcessDecode(mp4_demuxer::MP4Sample* aSample)
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
mLastStreamOffset = aSample->byte_offset;
|
||||
if (aSample) {
|
||||
mLastStreamOffset = aSample->byte_offset;
|
||||
}
|
||||
ProcessOutput();
|
||||
}
|
||||
|
||||
@ -92,6 +94,9 @@ GonkMediaDataDecoder::ProcessOutput()
|
||||
if (rv == NS_OK) {
|
||||
mCallback->Output(output.forget());
|
||||
continue;
|
||||
} else if (rv == NS_ERROR_NOT_AVAILABLE && mSignaledEOS) {
|
||||
// Try to get more frames before getting EOS frame
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
@ -105,6 +110,15 @@ GonkMediaDataDecoder::ProcessOutput()
|
||||
if (rv != NS_OK) {
|
||||
NS_WARNING("GonkMediaDataDecoder failed to output data");
|
||||
ALOG("Failed to output data");
|
||||
// GonkDecoderManangers report NS_ERROR_ABORT when EOS is reached.
|
||||
if (rv == NS_ERROR_ABORT) {
|
||||
if (output.get() != nullptr) {
|
||||
mCallback->Output(output.forget());
|
||||
}
|
||||
mCallback->DrainComplete();
|
||||
mSignaledEOS = false;
|
||||
return;
|
||||
}
|
||||
mCallback->Error();
|
||||
}
|
||||
}
|
||||
@ -125,9 +139,10 @@ GonkMediaDataDecoder::Flush()
|
||||
void
|
||||
GonkMediaDataDecoder::ProcessDrain()
|
||||
{
|
||||
// Then extract all available output.
|
||||
// Notify decoder input EOS by sending a null data.
|
||||
ProcessDecode(nullptr);
|
||||
mSignaledEOS = true;
|
||||
ProcessOutput();
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -68,7 +68,9 @@ public:
|
||||
private:
|
||||
|
||||
// Called on the task queue. Inserts the sample into the decoder, and
|
||||
// extracts output if available.
|
||||
// extracts output if available, if aSample is null, it means there is
|
||||
// no data from source, it will notify the decoder EOS and flush all the
|
||||
// decoded frames.
|
||||
void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
|
||||
|
||||
// Called on the task queue. Extracts output if available, and delivers
|
||||
@ -88,6 +90,8 @@ private:
|
||||
// The last offset into the media resource that was passed into Input().
|
||||
// This is used to approximate the decoder's position in the media resource.
|
||||
int64_t mLastStreamOffset;
|
||||
// Set it ture when there is no input data
|
||||
bool mSignaledEOS;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -115,11 +115,24 @@ GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
|
||||
*v = nullptr;
|
||||
int64_t timeUs;
|
||||
int32_t keyFrame;
|
||||
|
||||
if (!(mVideoBuffer != nullptr && mVideoBuffer->data() != nullptr)) {
|
||||
ALOG("Video Buffer is not valid!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
|
||||
ALOG("Decoder did not return frame time");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (mVideoBuffer->range_length() == 0) {
|
||||
// Some decoders may return spurious empty buffers that we just want to ignore
|
||||
// quoted from Android's AwesomePlayer.cpp
|
||||
ReleaseVideoBuffer();
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (!mVideoBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) {
|
||||
keyFrame = 0;
|
||||
}
|
||||
@ -137,11 +150,6 @@ GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
|
||||
picture.height = (mFrameInfo.mHeight * mPicture.height) / mInitialFrame.height;
|
||||
}
|
||||
|
||||
if (!(mVideoBuffer != nullptr && mVideoBuffer->size() > 0 && mVideoBuffer->data() != nullptr)) {
|
||||
ALOG("mVideoBuffer is not valid!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
uint8_t *yuv420p_buffer = (uint8_t *)mVideoBuffer->data();
|
||||
int32_t stride = mFrameInfo.mStride;
|
||||
int32_t slice_height = mFrameInfo.mSliceHeight;
|
||||
@ -268,9 +276,11 @@ GonkVideoDecoderManager::Output(int64_t aStreamOffset,
|
||||
{
|
||||
VideoData* data = nullptr;
|
||||
nsresult rv = CreateVideoData(aStreamOffset, &data);
|
||||
// Frame should be non null only when we succeeded.
|
||||
if (rv != NS_OK || data == nullptr){
|
||||
ALOG("Error unexpected in CreateVideoData");
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// Decoder outputs a empty video buffer, try again
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
} else if (rv != NS_OK || data == nullptr) {
|
||||
ALOG("Failed to create VideoData");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
aOutData = data;
|
||||
@ -293,7 +303,18 @@ GonkVideoDecoderManager::Output(int64_t aStreamOffset,
|
||||
}
|
||||
case android::ERROR_END_OF_STREAM:
|
||||
{
|
||||
ALOG("End of Stream");
|
||||
ALOG("Got the EOS frame!");
|
||||
VideoData* data = nullptr;
|
||||
nsresult rv = CreateVideoData(aStreamOffset, &data);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// For EOS, no need to do any thing.
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
if (rv != NS_OK || data == nullptr) {
|
||||
ALOG("Failed to create video data");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
aOutData = data;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
case -ETIMEDOUT:
|
||||
@ -325,17 +346,24 @@ void GonkVideoDecoderManager::ReleaseVideoBuffer() {
|
||||
nsresult
|
||||
GonkVideoDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
// We must prepare samples in AVC Annex B.
|
||||
mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
|
||||
// Forward sample data to the decoder.
|
||||
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
|
||||
uint32_t length = aSample->size;
|
||||
if (mDecoder == nullptr) {
|
||||
ALOG("Decoder is not inited");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
status_t rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
|
||||
status_t rv;
|
||||
if (aSample != nullptr) {
|
||||
// We must prepare samples in AVC Annex B.
|
||||
mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
|
||||
// Forward sample data to the decoder.
|
||||
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
|
||||
uint32_t length = aSample->size;
|
||||
rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
|
||||
}
|
||||
else {
|
||||
// Inputted data is null, so it is going to notify decoder EOS
|
||||
rv = mDecoder->Input(nullptr, 0, 0ll, 0);
|
||||
}
|
||||
return (rv == OK) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -49,8 +49,11 @@ GMPChild::~GMPChild()
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPluginBinaryFile(const std::string& aPluginPath,
|
||||
nsCOMPtr<nsIFile>& aLibFile)
|
||||
GetPluginFile(const std::string& aPluginPath,
|
||||
#if defined(XP_MACOSX)
|
||||
nsCOMPtr<nsIFile>& aLibDirectory,
|
||||
#endif
|
||||
nsCOMPtr<nsIFile>& aLibFile)
|
||||
{
|
||||
nsDependentCString pluginPath(aPluginPath.c_str());
|
||||
|
||||
@ -60,6 +63,12 @@ GetPluginBinaryFile(const std::string& aPluginPath,
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
if (NS_FAILED(aLibFile->Clone(getter_AddRefs(aLibDirectory)))) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsAutoString leafName;
|
||||
if (NS_FAILED(aLibFile->GetLeafName(leafName))) {
|
||||
return false;
|
||||
@ -81,32 +90,48 @@ GetPluginBinaryFile(const std::string& aPluginPath,
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
|
||||
static bool
|
||||
GetPluginBinaryPath(const std::string& aPluginPath,
|
||||
nsCString &aPluginBinaryPath)
|
||||
GetPluginPaths(const std::string& aPluginPath,
|
||||
nsCString &aPluginDirectoryPath,
|
||||
nsCString &aPluginFilePath)
|
||||
{
|
||||
nsCOMPtr<nsIFile> libFile;
|
||||
if (!GetPluginBinaryFile(aPluginPath, libFile)) {
|
||||
nsCOMPtr<nsIFile> libDirectory, libFile;
|
||||
if (!GetPluginFile(aPluginPath, libDirectory, libFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
libFile->GetNativePath(aPluginBinaryPath);
|
||||
// Mac sandbox rules expect paths to actual files and directories -- not
|
||||
// soft links.
|
||||
bool isLink;
|
||||
libDirectory->IsSymlink(&isLink);
|
||||
if (isLink) {
|
||||
libDirectory->GetNativeTarget(aPluginDirectoryPath);
|
||||
} else {
|
||||
libDirectory->GetNativePath(aPluginDirectoryPath);
|
||||
}
|
||||
libFile->IsSymlink(&isLink);
|
||||
if (isLink) {
|
||||
libFile->GetNativeTarget(aPluginFilePath);
|
||||
} else {
|
||||
libFile->GetNativePath(aPluginFilePath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GMPChild::OnChannelConnected(int32_t aPid)
|
||||
{
|
||||
nsAutoCString pluginDirectoryPath, pluginFilePath;
|
||||
if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) {
|
||||
MOZ_CRASH("Error scanning plugin path");
|
||||
}
|
||||
|
||||
MacSandboxInfo info;
|
||||
info.type = MacSandboxType_Plugin;
|
||||
info.pluginInfo.type = MacSandboxPluginType_GMPlugin_Default;
|
||||
info.pluginInfo.pluginPath.Assign(mPluginPath.c_str());
|
||||
|
||||
nsAutoCString pluginBinaryPath;
|
||||
if (!GetPluginBinaryPath(mPluginPath, pluginBinaryPath)) {
|
||||
MOZ_CRASH("Error scanning plugin path");
|
||||
}
|
||||
mPluginBinaryPath.Assign(pluginBinaryPath);
|
||||
info.pluginInfo.pluginBinaryPath.Assign(pluginBinaryPath);
|
||||
info.pluginInfo.pluginPath.Assign(pluginDirectoryPath);
|
||||
mPluginBinaryPath.Assign(pluginFilePath);
|
||||
info.pluginInfo.pluginBinaryPath.Assign(pluginFilePath);
|
||||
|
||||
nsAutoCString err;
|
||||
if (!mozilla::StartMacSandbox(info, err)) {
|
||||
@ -165,7 +190,7 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
|
||||
mLib = PR_LoadLibrary(nativePath.get());
|
||||
#else
|
||||
nsCOMPtr<nsIFile> libFile;
|
||||
if (!GetPluginBinaryFile(aPluginPath, libFile)) {
|
||||
if (!GetPluginFile(aPluginPath, libFile)) {
|
||||
return false;
|
||||
}
|
||||
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
||||
|
@ -65,7 +65,6 @@ EXPORTS += [
|
||||
'AbstractMediaDecoder.h',
|
||||
'AudioChannelFormat.h',
|
||||
'AudioCompactor.h',
|
||||
'AudioEventTimeline.h',
|
||||
'AudioMixer.h',
|
||||
'AudioSampleFormat.h',
|
||||
'AudioSegment.h',
|
||||
@ -100,6 +99,7 @@ EXPORTS += [
|
||||
'SharedBuffer.h',
|
||||
'SharedThreadPool.h',
|
||||
'StreamBuffer.h',
|
||||
'ThreadPoolCOMListener.h',
|
||||
'TimeVarying.h',
|
||||
'TrackUnionStream.h',
|
||||
'VideoFrameContainer.h',
|
||||
@ -170,6 +170,9 @@ UNIFIED_SOURCES += [
|
||||
'WebVTTListener.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
SOURCES += [ 'ThreadPoolCOMListener.cpp' ]
|
||||
|
||||
# DecoderTraits.cpp needs to be built separately because of Mac OS X headers.
|
||||
# Latency.cpp needs to be built separately because it forces NSPR logging.
|
||||
SOURCES += [
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <stagefright/foundation/ABuffer.h>
|
||||
#include <stagefright/foundation/ADebug.h>
|
||||
#include <stagefright/MetaData.h>
|
||||
#include "stagefright/MediaErrors.h"
|
||||
|
||||
#define LOG_TAG "MediaCodecProxy"
|
||||
#include <android/log.h>
|
||||
@ -428,13 +429,19 @@ status_t MediaCodecProxy::Input(const uint8_t* aData, uint32_t aDataSize,
|
||||
ALOG("dequeueInputBuffer returned %d", err);
|
||||
return err;
|
||||
}
|
||||
const sp<ABuffer> &dstBuffer = mInputBuffers.itemAt(index);
|
||||
|
||||
CHECK_LE(aDataSize, dstBuffer->capacity());
|
||||
dstBuffer->setRange(0, aDataSize);
|
||||
if (aData) {
|
||||
const sp<ABuffer> &dstBuffer = mInputBuffers.itemAt(index);
|
||||
|
||||
CHECK_LE(aDataSize, dstBuffer->capacity());
|
||||
dstBuffer->setRange(0, aDataSize);
|
||||
|
||||
memcpy(dstBuffer->data(), aData, aDataSize);
|
||||
err = queueInputBuffer(index, 0, dstBuffer->size(), aTimestampUsecs, aflags);
|
||||
} else {
|
||||
err = queueInputBuffer(index, 0, 0, 0ll, MediaCodec::BUFFER_FLAG_EOS);
|
||||
}
|
||||
|
||||
memcpy(dstBuffer->data(), aData, aDataSize);
|
||||
err = queueInputBuffer(index, 0, dstBuffer->size(), aTimestampUsecs, aflags);
|
||||
if (err != OK) {
|
||||
ALOG("queueInputBuffer returned %d", err);
|
||||
return err;
|
||||
@ -473,6 +480,9 @@ status_t MediaCodecProxy::Output(MediaBuffer** aBuffer, int64_t aTimeoutUs)
|
||||
metaData->setInt64(kKeyTime, timeUs);
|
||||
buffer->set_range(buffer->range_offset(), size);
|
||||
*aBuffer = buffer;
|
||||
if (flags & MediaCodec::BUFFER_FLAG_EOS) {
|
||||
return ERROR_END_OF_STREAM;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ public:
|
||||
// an input/output buffer has become available, a format change is
|
||||
// pending, an error is pending.
|
||||
void requestActivityNotification(const sp<AMessage> &aNotify);
|
||||
|
||||
// If aData is null, will notify decoder input EOS
|
||||
status_t Input(const uint8_t* aData, uint32_t aDataSize,
|
||||
int64_t aTimestampUsecs, uint64_t flags);
|
||||
status_t Output(MediaBuffer** aBuffer, int64_t aTimeoutUs);
|
||||
|
@ -21,6 +21,9 @@
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "WebAudioUtils.h"
|
||||
#ifdef XP_WIN
|
||||
#include "ThreadPoolCOMListener.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -486,6 +489,12 @@ MediaBufferDecoder::EnsureThreadPoolInitialized()
|
||||
return false;
|
||||
}
|
||||
mThreadPool->SetName(NS_LITERAL_CSTRING("MediaBufferDecoder"));
|
||||
#ifdef XP_WIN
|
||||
// Ensure MSCOM is initialized on the thread pools threads.
|
||||
nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener();
|
||||
nsresult rv = mThreadPool->SetListener(listener);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
||||
EXPORTS += [
|
||||
'AudioContext.h',
|
||||
'AudioEventTimeline.h',
|
||||
'AudioNodeEngine.h',
|
||||
'AudioNodeExternalInputStream.h',
|
||||
'AudioNodeStream.h',
|
||||
|
@ -13,6 +13,7 @@ support-files =
|
||||
small-shot-expected.wav
|
||||
small-shot-mono-expected.wav
|
||||
small-shot.ogg
|
||||
small-shot.mp3
|
||||
ting-44.1k-1ch.ogg
|
||||
ting-44.1k-2ch.ogg
|
||||
ting-48k-1ch.ogg
|
||||
@ -71,6 +72,8 @@ skip-if = (toolkit == 'gonk' && !debug) || (toolkit == 'android') #bug 906752
|
||||
[test_bug956489.html]
|
||||
[test_bug964376.html]
|
||||
[test_bug972678.html]
|
||||
[test_bug1056032.html]
|
||||
skip-if = toolkit == 'android' # bug 1056706
|
||||
[test_channelMergerNode.html]
|
||||
[test_channelMergerNodeWithVolume.html]
|
||||
[test_channelSplitterNode.html]
|
||||
|
BIN
content/media/webaudio/test/small-shot.mp3
Normal file
BIN
content/media/webaudio/test/small-shot.mp3
Normal file
Binary file not shown.
35
content/media/webaudio/test/test_bug1056032.html
Normal file
35
content/media/webaudio/test/test_bug1056032.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<meta charset=utf-8>
|
||||
<head>
|
||||
<title>Test that we can decode an mp3 (bug 1056032)</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var filename = "small-shot.mp3";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", filename);
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.onload = function() {
|
||||
var context = new AudioContext();
|
||||
context.decodeAudioData(xhr.response, function(b) {
|
||||
ok(true, "We can decode an mp3 using decodeAudioData");
|
||||
SimpleTest.finish();
|
||||
}, function() {
|
||||
ok(false, "We should be able to decode an mp3 using decodeAudioData but couldn't");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
};
|
||||
xhr.send(null);
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -39,7 +39,7 @@ NS_IMPL_ISUPPORTS(MediaEngineDefaultVideoSource, nsITimerCallback)
|
||||
*/
|
||||
|
||||
MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
|
||||
: mTimer(nullptr), mMonitor("Fake video")
|
||||
: mTimer(nullptr), mMonitor("Fake video"), mCb(16), mCr(16)
|
||||
{
|
||||
mImageContainer = layers::LayerManager::CreateImageContainer();
|
||||
mState = kReleased;
|
||||
|
@ -30,7 +30,7 @@
|
||||
sendMessage("KO: " + a + " != " + b + " - " + msg);
|
||||
}
|
||||
|
||||
function testXHR(file, data_head, mapped, cb) {
|
||||
function check_XHR(file, data_head, mapped, cb) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', file);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
@ -60,19 +60,57 @@
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function check_XHR_mapped(file, cb) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', file);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onreadystatechange = function xhrReadystatechange() {
|
||||
if (xhr.readyState !== xhr.DONE) {
|
||||
return;
|
||||
}
|
||||
if (xhr.status && xhr.status == 200) {
|
||||
var ct = xhr.getResponseHeader("Content-Type");
|
||||
cb(ct.indexOf("mem-mapped") != -1);
|
||||
} else {
|
||||
cb(false);
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
// Reading multiple mapped array buffer by XHR simultaneously
|
||||
function test_simultaneous() {
|
||||
var count = 0;
|
||||
var num = 100;
|
||||
var succeed = true;
|
||||
function cb(result) {
|
||||
if (!result) {
|
||||
succeed = false;
|
||||
}
|
||||
if (++count == num) {
|
||||
ok(succeed, "Succeeded on simultaneous access test");
|
||||
runTests();
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < num; i++) {
|
||||
check_XHR_mapped('data_1.txt', cb);
|
||||
}
|
||||
}
|
||||
|
||||
// Memory-mapped array buffer.
|
||||
function test_mapped() {
|
||||
testXHR('data_1.txt', gData1, true, runTests);
|
||||
check_XHR('data_1.txt', gData1, true, runTests);
|
||||
}
|
||||
|
||||
// Non memory-mapped array buffer.
|
||||
function test_non_mapped() {
|
||||
// Make sure array buffer retrieved from compressed file in package is
|
||||
// handled by memory allocation instead of memory mapping.
|
||||
testXHR('data_2.txt', gData2, false, runTests);
|
||||
check_XHR('data_2.txt', gData2, false, runTests);
|
||||
}
|
||||
|
||||
var tests = [
|
||||
test_simultaneous,
|
||||
test_mapped,
|
||||
test_non_mapped
|
||||
];
|
||||
|
@ -174,10 +174,16 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true],
|
||||
["dom.enable_widgets", true]]}, runTest);
|
||||
["dom.enable_widgets", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
runTest();
|
||||
},
|
||||
@ -220,10 +226,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -2356,6 +2356,14 @@ CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CheckSafetyInPrerendering(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
//TODO: Check if page is being prerendered.
|
||||
//Returning false for now.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
|
||||
{
|
||||
|
@ -2937,6 +2937,10 @@ AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
|
||||
bool
|
||||
CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
|
||||
|
||||
//Returns true if page is being prerendered.
|
||||
bool
|
||||
CheckSafetyInPrerendering(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
bool
|
||||
CallerSubsumes(JSObject* aObject);
|
||||
|
||||
|
@ -6320,6 +6320,21 @@ class CGPerSignatureCall(CGThing):
|
||||
self.arguments = arguments
|
||||
self.argCount = len(arguments)
|
||||
cgThings = []
|
||||
|
||||
# Here, we check if the current getter, setter, method, interface or
|
||||
# inherited interfaces have the UnsafeInPrerendering extended attribute
|
||||
# and if so, we add a check to make sure it is safe.
|
||||
if (idlNode.getExtendedAttribute("UnsafeInPrerendering") or
|
||||
descriptor.interface.getExtendedAttribute("UnsafeInPrerendering") or
|
||||
any(i.getExtendedAttribute("UnsafeInPrerendering")
|
||||
for i in descriptor.interface.getInheritedInterfaces())):
|
||||
cgThings.append(CGGeneric(dedent(
|
||||
"""
|
||||
if (mozilla::dom::CheckSafetyInPrerendering(cx, obj)) {
|
||||
//TODO: Handle call into unsafe API during Prerendering (Bug 730101)
|
||||
return false;
|
||||
}
|
||||
""")))
|
||||
lenientFloatCode = None
|
||||
if idlNode.getExtendedAttribute('LenientFloat') is not None:
|
||||
if setter:
|
||||
|
@ -1196,6 +1196,7 @@ class IDLInterface(IDLObjectWithScope):
|
||||
identifier == "OverrideBuiltins" or
|
||||
identifier == "ChromeOnly" or
|
||||
identifier == "Unforgeable" or
|
||||
identifier == "UnsafeInPrerendering" or
|
||||
identifier == "LegacyEventInit"):
|
||||
# Known extended attributes that do not take values
|
||||
if not attr.noArguments():
|
||||
@ -3309,6 +3310,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
identifier == "Frozen" or
|
||||
identifier == "AvailableIn" or
|
||||
identifier == "NewObject" or
|
||||
identifier == "UnsafeInPrerendering" or
|
||||
identifier == "CheckPermissions"):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
||||
@ -3902,6 +3904,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
elif (identifier == "Throws" or
|
||||
identifier == "NewObject" or
|
||||
identifier == "ChromeOnly" or
|
||||
identifier == "UnsafeInPrerendering" or
|
||||
identifier == "Pref" or
|
||||
identifier == "Func" or
|
||||
identifier == "AvailableIn" or
|
||||
|
@ -184,6 +184,10 @@ public:
|
||||
int8_t CachedWritableByte();
|
||||
void SetCachedWritableByte(int8_t);
|
||||
|
||||
void UnsafePrerenderMethod();
|
||||
int32_t UnsafePrerenderWritable();
|
||||
void SetUnsafePrerenderWritable(int32_t);
|
||||
int32_t UnsafePrerenderReadonly();
|
||||
int16_t ReadonlyShort();
|
||||
int16_t WritableShort();
|
||||
void SetWritableShort(int16_t);
|
||||
|
@ -137,6 +137,12 @@ interface TestInterface {
|
||||
[StoreInSlot, Pure]
|
||||
attribute byte cachedWritableByte;
|
||||
|
||||
[UnsafeInPrerendering]
|
||||
void unsafePrerenderMethod();
|
||||
[UnsafeInPrerendering]
|
||||
attribute long unsafePrerenderWritable;
|
||||
[UnsafeInPrerendering]
|
||||
readonly attribute long unsafePrerenderReadonly;
|
||||
readonly attribute short readonlyShort;
|
||||
attribute short writableShort;
|
||||
void passShort(short arg);
|
||||
|
@ -23,12 +23,33 @@ XPCOMUtils.defineLazyServiceGetter(this, "dataStoreService",
|
||||
"@mozilla.org/datastore-service;1",
|
||||
"nsIDataStoreService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "systemMessenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
var kSysMsgOnChangeShortTimeoutSec =
|
||||
Services.prefs.getIntPref("dom.datastore.sysMsgOnChangeShortTimeoutSec");
|
||||
var kSysMsgOnChangeLongTimeoutSec =
|
||||
Services.prefs.getIntPref("dom.datastore.sysMsgOnChangeLongTimeoutSec");
|
||||
|
||||
this.DataStoreChangeNotifier = {
|
||||
children: [],
|
||||
messages: [ "DataStore:Changed", "DataStore:RegisterForMessages",
|
||||
"DataStore:UnregisterForMessages",
|
||||
"child-process-shutdown" ],
|
||||
|
||||
// These hashes are used for storing the mapping between the datastore
|
||||
// identifiers (name | owner manifest URL) and their correspondent timers.
|
||||
// The object literal is defined as below:
|
||||
//
|
||||
// {
|
||||
// "datastore name 1|owner manifest URL 1": timer1,
|
||||
// "datastore name 2|owner manifest URL 2": timer2,
|
||||
// ...
|
||||
// }
|
||||
sysMsgOnChangeShortTimers: {},
|
||||
sysMsgOnChangeLongTimers: {},
|
||||
|
||||
init: function() {
|
||||
debug("init");
|
||||
|
||||
@ -59,7 +80,8 @@ this.DataStoreChangeNotifier = {
|
||||
},
|
||||
|
||||
broadcastMessage: function broadcastMessage(aData) {
|
||||
debug("Broadast");
|
||||
debug("broadcast");
|
||||
|
||||
this.children.forEach(function(obj) {
|
||||
if (obj.store == aData.store && obj.owner == aData.owner) {
|
||||
obj.mm.sendAsyncMessage("DataStore:Changed:Return:OK", aData);
|
||||
@ -67,6 +89,69 @@ this.DataStoreChangeNotifier = {
|
||||
});
|
||||
},
|
||||
|
||||
broadcastSystemMessage: function(aStore, aOwner) {
|
||||
debug("broadcastSystemMessage");
|
||||
|
||||
// Clear relevant timers.
|
||||
var storeKey = aStore + "|" + aOwner;
|
||||
var shortTimer = this.sysMsgOnChangeShortTimers[storeKey];
|
||||
if (shortTimer) {
|
||||
shortTimer.cancel();
|
||||
delete this.sysMsgOnChangeShortTimers[storeKey];
|
||||
}
|
||||
var longTimer = this.sysMsgOnChangeLongTimers[storeKey];
|
||||
if (longTimer) {
|
||||
longTimer.cancel();
|
||||
delete this.sysMsgOnChangeLongTimers[storeKey];
|
||||
}
|
||||
|
||||
// Get all the manifest URLs of the apps which can access the datastore.
|
||||
var manifestURLs = dataStoreService.getAppManifestURLsForDataStore(aStore);
|
||||
var enumerate = manifestURLs.enumerate();
|
||||
while (enumerate.hasMoreElements()) {
|
||||
var manifestURL = enumerate.getNext().QueryInterface(Ci.nsISupportsString);
|
||||
debug("Notify app " + manifestURL + " of datastore updates");
|
||||
// Send the system message 'datastore-update-{store name}' to all the
|
||||
// pages for these apps. With the manifest URL of the owner in the message
|
||||
// payload, it notifies the consumer a sync operation should be performed.
|
||||
systemMessenger.sendMessage("datastore-update-" + aStore,
|
||||
{ owner: aOwner },
|
||||
null,
|
||||
Services.io.newURI(manifestURL, null, null));
|
||||
}
|
||||
},
|
||||
|
||||
// Use the following logic to broadcast system messages in a moderate pattern.
|
||||
// 1. When an entry is changed, start a short timer and a long timer.
|
||||
// 2. If an entry is changed while the short timer is running, reset it.
|
||||
// Do not reset the long timer.
|
||||
// 3. Once either fires, broadcast the system message and cancel both timers.
|
||||
setSystemMessageTimeout: function(aStore, aOwner) {
|
||||
debug("setSystemMessageTimeout");
|
||||
|
||||
var storeKey = aStore + "|" + aOwner;
|
||||
|
||||
// Reset the short timer.
|
||||
var shortTimer = this.sysMsgOnChangeShortTimers[storeKey];
|
||||
if (!shortTimer) {
|
||||
shortTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.sysMsgOnChangeShortTimers[storeKey] = shortTimer;
|
||||
} else {
|
||||
shortTimer.cancel();
|
||||
}
|
||||
shortTimer.initWithCallback({ notify: this.broadcastSystemMessage.bind(this, aStore, aOwner) },
|
||||
kSysMsgOnChangeShortTimeoutSec * 1000,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
|
||||
// Set the long timer if necessary.
|
||||
if (!this.sysMsgOnChangeLongTimers[storeKey]) {
|
||||
var longTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.sysMsgOnChangeLongTimers[storeKey] = longTimer;
|
||||
longTimer.initWithCallback({ notify: this.broadcastSystemMessage.bind(this, aStore, aOwner) },
|
||||
kSysMsgOnChangeLongTimeoutSec * 1000,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
debug("receiveMessage ");
|
||||
@ -89,6 +174,9 @@ this.DataStoreChangeNotifier = {
|
||||
switch (aMessage.name) {
|
||||
case "DataStore:Changed":
|
||||
this.broadcastMessage(aMessage.data);
|
||||
if (Services.prefs.getBoolPref("dom.sysmsg.enabled")) {
|
||||
this.setSystemMessageTimeout(aMessage.data.store, aMessage.data.owner);
|
||||
}
|
||||
break;
|
||||
|
||||
case "DataStore:RegisterForMessages":
|
||||
|
@ -33,9 +33,11 @@
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMGlobalPropertyInitializer.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIURI.h"
|
||||
@ -375,6 +377,24 @@ GetDataStoreInfosEnumerator(const uint32_t& aAppId,
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
GetAppManifestURLsEnumerator(const uint32_t& aAppId,
|
||||
DataStoreInfo* aInfo,
|
||||
void* aUserData)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
auto* manifestURLs = static_cast<nsIMutableArray*>(aUserData);
|
||||
nsCOMPtr<nsISupportsString> manifestURL(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
|
||||
if (manifestURL) {
|
||||
manifestURL->SetData(aInfo->mManifestURL);
|
||||
manifestURLs->AppendElement(manifestURL, false);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// This class is useful to enumerate the add permissions for each app.
|
||||
class MOZ_STACK_CLASS AddPermissionsData
|
||||
{
|
||||
@ -1057,6 +1077,31 @@ DataStoreService::GetDataStoreInfos(const nsAString& aName,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataStoreService::GetAppManifestURLsForDataStore(const nsAString& aName,
|
||||
nsIArray** aManifestURLs)
|
||||
{
|
||||
ASSERT_PARENT_PROCESS()
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIMutableArray> manifestURLs = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!manifestURLs) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
HashApp* apps = nullptr;
|
||||
if (mStores.Get(aName, &apps)) {
|
||||
apps->EnumerateRead(GetAppManifestURLsEnumerator, manifestURLs.get());
|
||||
}
|
||||
if (mAccessStores.Get(aName, &apps)) {
|
||||
apps->EnumerateRead(GetAppManifestURLsEnumerator, manifestURLs.get());
|
||||
}
|
||||
|
||||
*aManifestURLs = manifestURLs;
|
||||
NS_ADDREF(*aManifestURLs);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
DataStoreService::CheckPermission(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
|
@ -7,8 +7,9 @@
|
||||
|
||||
interface nsIDOMWindow;
|
||||
interface nsIPrincipal;
|
||||
interface nsIArray;
|
||||
|
||||
[scriptable, uuid(9b59c49a-0cd7-11e4-b096-74d02b97e723)]
|
||||
[scriptable, uuid(79944b1c-187d-11e4-abb6-74d02b97e723)]
|
||||
interface nsIDataStoreService : nsISupports
|
||||
{
|
||||
void installDataStore(in unsigned long appId,
|
||||
@ -27,5 +28,7 @@ interface nsIDataStoreService : nsISupports
|
||||
in DOMString name,
|
||||
in DOMString owner);
|
||||
|
||||
nsIArray getAppManifestURLsForDataStore(in DOMString name);
|
||||
|
||||
boolean checkPermission(in nsIPrincipal principal);
|
||||
};
|
||||
|
120
dom/datastore/tests/file_notify_system_message.html
Normal file
120
dom/datastore/tests/file_notify_system_message.html
Normal file
@ -0,0 +1,120 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for DataStore - notify updates with system messages</title>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
var gStore;
|
||||
var gChangeId = null;
|
||||
var gChangeOperation = null;
|
||||
var gIsSystemMessageFired = false;
|
||||
|
||||
function is(a, b, msg) {
|
||||
alert((a === b ? 'OK' : 'KO') + ' ' + msg)
|
||||
}
|
||||
|
||||
function ok(a, msg) {
|
||||
alert((a ? 'OK' : 'KO')+ ' ' + msg)
|
||||
}
|
||||
|
||||
function cbError() {
|
||||
alert('KO error');
|
||||
}
|
||||
|
||||
function finish() {
|
||||
alert('DONE');
|
||||
}
|
||||
|
||||
function testGetDataStores() {
|
||||
navigator.getDataStores('foo').then(function(stores) {
|
||||
is(stores.length, 1, "getDataStores('foo') returns 1 element");
|
||||
is(stores[0].name, 'foo', 'The dataStore.name is foo');
|
||||
is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
|
||||
|
||||
gStore = stores[0];
|
||||
runTest();
|
||||
}, cbError);
|
||||
}
|
||||
|
||||
function testStoreAdd(value, expectedId) {
|
||||
gStore.add(value).then(function(id) {
|
||||
is(id, expectedId, "store.add() is called");
|
||||
}, cbError);
|
||||
}
|
||||
|
||||
function eventListener(evt) {
|
||||
ok(evt instanceof DataStoreChangeEvent, "DataStoreChangeEvent has been received");
|
||||
ok(evt, "OnChangeListener is called with data");
|
||||
is(/[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}/.test(evt.revisionId), true, "event.revisionId returns something");
|
||||
is(evt.id, gChangeId, "OnChangeListener is called with the right ID: " + evt.id);
|
||||
is(evt.operation, gChangeOperation, "OnChangeListener is called with the right operation:" + evt.operation + " " + gChangeOperation);
|
||||
runTest();
|
||||
}
|
||||
|
||||
function onDatastoreUpdateFoo(message) {
|
||||
gIsSystemMessageFired = true;
|
||||
ok(true, "System message 'datastore-update-foo' has been received");
|
||||
runTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
// Test for GetDataStore.
|
||||
testGetDataStores,
|
||||
|
||||
// Add onchange = function.
|
||||
function() {
|
||||
gStore.onchange = eventListener;
|
||||
is(gStore.onchange, eventListener, "onChange is set");
|
||||
runTest();
|
||||
},
|
||||
|
||||
// Set system message handler.
|
||||
function() {
|
||||
navigator.mozSetMessageHandler('datastore-update-foo', onDatastoreUpdateFoo);
|
||||
runTest();
|
||||
},
|
||||
|
||||
// Add.
|
||||
function() { gChangeId = 1; gChangeOperation = 'added';
|
||||
testStoreAdd({ number: 42 }, 1); },
|
||||
|
||||
// Remove event listener.
|
||||
function() {
|
||||
gStore.removeEventListener('change', eventListener);
|
||||
runTest();
|
||||
},
|
||||
|
||||
// Ensure the system message has fired and no more pending ones.
|
||||
function() {
|
||||
// Periodically check whether the system message has fired.
|
||||
var timer = setInterval(function() {
|
||||
if (gIsSystemMessageFired) {
|
||||
clearInterval(timer);
|
||||
ok(true, "The system message has fired");
|
||||
ok(!navigator.mozHasPendingMessage('datastore-update-foo'), "No more pending system message");
|
||||
finish();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
if (tests.length) {
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
}
|
||||
|
||||
runTest();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -30,6 +30,7 @@ support-files =
|
||||
file_sync_common.js
|
||||
file_bug1008044.html
|
||||
file_bug957086.html
|
||||
file_notify_system_message.html
|
||||
|
||||
[test_app_install.html]
|
||||
[test_readonly.html]
|
||||
@ -50,3 +51,5 @@ support-files =
|
||||
[test_transactions.html]
|
||||
[test_bug1008044.html]
|
||||
[test_bug957086.html]
|
||||
[test_notify_system_message.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || toolkit == 'win' #bug 1053662 - Timeout prone
|
||||
|
@ -10,10 +10,6 @@
|
||||
<div id="container"></div>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var gBaseURL = 'http://test/tests/dom/datastore/tests/';
|
||||
@ -26,8 +22,14 @@
|
||||
{ "type": "webapps-manage", "allow": 1, "context": document }],
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, function() {
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
gGenerator.next(); });
|
||||
});
|
||||
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -116,10 +122,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -116,10 +122,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -116,10 +122,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,12 +77,18 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.ipc.browser_frames.oop_by_default", true],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -117,10 +123,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -116,10 +122,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -81,12 +81,18 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.ipc.browser_frames.oop_by_default", true],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -124,10 +130,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -82,11 +82,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -124,10 +130,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -75,12 +75,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
// Enabling mozBrowser
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest);
|
||||
},
|
||||
@ -134,10 +139,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,6 +77,8 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", false]]}, runTest);
|
||||
},
|
||||
@ -89,6 +91,10 @@
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -123,10 +129,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -121,12 +121,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
// Enabling mozBrowser
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest);
|
||||
},
|
||||
@ -169,10 +174,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -116,10 +122,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -116,10 +122,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
138
dom/datastore/tests/test_notify_system_message.html
Normal file
138
dom/datastore/tests/test_notify_system_message.html
Normal file
@ -0,0 +1,138 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for DataStore - notify updates with system messages</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_notify_system_message.html';
|
||||
var gHostedPageURL = 'http://mochi.test:8888/tests/dom/datastore/tests/file_notify_system_message.html';
|
||||
var gApp;
|
||||
|
||||
function cbError() {
|
||||
ok(false, "Error callback invoked");
|
||||
finish();
|
||||
}
|
||||
|
||||
function installApp() {
|
||||
var request = navigator.mozApps.install(gHostedManifestURL);
|
||||
request.onerror = cbError;
|
||||
request.onsuccess = function() {
|
||||
gApp = request.result;
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
function uninstallApp() {
|
||||
// Uninstall the app.
|
||||
var request = navigator.mozApps.mgmt.uninstall(gApp);
|
||||
request.onerror = cbError;
|
||||
request.onsuccess = function() {
|
||||
// All done.
|
||||
info("All done");
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
function testApp() {
|
||||
var ifr = document.createElement('iframe');
|
||||
ifr.setAttribute('mozbrowser', 'true');
|
||||
ifr.setAttribute('mozapp', gApp.manifestURL);
|
||||
ifr.setAttribute('src', gApp.manifest.launch_path);
|
||||
var domParent = document.getElementById('container');
|
||||
|
||||
// Set us up to listen for messages from the app.
|
||||
var listener = function(e) {
|
||||
var message = e.detail.message;
|
||||
if (/^OK/.exec(message)) {
|
||||
ok(true, "Message from app: " + message);
|
||||
} else if (/KO/.exec(message)) {
|
||||
ok(false, "Message from app: " + message);
|
||||
} else if (/DONE/.exec(message)) {
|
||||
ok(true, "Messaging from app complete");
|
||||
ifr.removeEventListener('mozbrowsershowmodalprompt', listener);
|
||||
domParent.removeChild(ifr);
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
// This event is triggered when the app calls "alert".
|
||||
ifr.addEventListener('mozbrowsershowmodalprompt', listener, false);
|
||||
domParent.appendChild(ifr);
|
||||
}
|
||||
|
||||
var tests = [
|
||||
// Permissions.
|
||||
function() {
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "browser", "allow": 1, "context": document },
|
||||
{ "type": "embed-apps", "allow": 1, "context": document },
|
||||
{ "type": "webapps-manage", "allow": 1, "context": document }], runTest);
|
||||
},
|
||||
|
||||
// Preferences.
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.sysmsg.enabled", true],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true],
|
||||
["dom.mozBrowserFramesEnabled", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
// No confirmation needed when an app is installed.
|
||||
SpecialPowers.autoConfirmAppInstall(runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
SpecialPowers.Cu.import("resource://gre/modules/Services.jsm");
|
||||
var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
var systemMessenger = SpecialPowers.Cc["@mozilla.org/system-message-internal;1"]
|
||||
.getService(SpecialPowers.Ci.nsISystemMessagesInternal);
|
||||
systemMessenger.registerPage("datastore-update-foo",
|
||||
ioService.newURI(gHostedPageURL, null, null),
|
||||
ioService.newURI(gHostedManifestURL, null, null));
|
||||
}
|
||||
runTest();
|
||||
},
|
||||
|
||||
// Installing the app.
|
||||
installApp,
|
||||
|
||||
// Run tests in app.
|
||||
testApp,
|
||||
|
||||
// Uninstall the app.
|
||||
uninstallApp
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
if (!tests.length) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -77,12 +77,18 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.ipc.browser_frames.oop_by_default", true],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -117,10 +123,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -105,12 +105,18 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.ipc.browser_frames.oop_by_default", true],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -147,10 +153,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -30,6 +30,10 @@
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
|
||||
@ -95,12 +99,10 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
</script>
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
},
|
||||
@ -115,10 +121,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
},
|
||||
@ -115,10 +121,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -77,11 +77,17 @@
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
|
||||
["dom.datastore.sysMsgOnChangeShortTimeoutSec", 1],
|
||||
["dom.datastore.sysMsgOnChangeLongTimeoutSec", 3],
|
||||
["dom.testing.ignore_ipc_principal", true],
|
||||
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTest();
|
||||
@ -116,10 +122,6 @@
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
</script>
|
||||
|
@ -8,7 +8,7 @@
|
||||
interface nsIDocument;
|
||||
interface nsIURI;
|
||||
|
||||
[uuid(9b5acea4-2601-4ac7-8836-4352ceb88178)]
|
||||
[uuid(6413f297-5581-47e8-a7f4-c509fd7a85a1)]
|
||||
interface nsIServiceWorkerManager : nsISupports
|
||||
{
|
||||
// Returns a Promise
|
||||
@ -17,6 +17,12 @@ interface nsIServiceWorkerManager : nsISupports
|
||||
// Returns a Promise
|
||||
nsISupports unregister(in nsIDOMWindow aWindow, in DOMString aScope);
|
||||
|
||||
// Returns a Promise
|
||||
nsISupports getRegistrations(in nsIDOMWindow aWindow);
|
||||
|
||||
// Returns a Promise
|
||||
nsISupports getRegistration(in nsIDOMWindow aWindow, in DOMString aScope);
|
||||
|
||||
// aTarget MUST be a ServiceWorkerRegistration.
|
||||
[noscript] void AddRegistrationEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
|
||||
[noscript] void RemoveRegistrationEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
|
||||
|
@ -3,13 +3,6 @@
|
||||
BlockMixedDisplayContent = Blocked loading mixed display content "%1$S"
|
||||
BlockMixedActiveContent = Blocked loading mixed active content "%1$S"
|
||||
|
||||
# CSP
|
||||
ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are other non-report-only CSP policies applied.
|
||||
# LOCALIZATION NOTE: Do not translate "X-Content-Security-Policy", "X-Content-Security-Policy-Report-Only", "Content-Security-Policy" or "Content-Security-Policy-Report-Only"
|
||||
OldCSPHeaderDeprecated=The X-Content-Security-Policy and X-Content-Security-Report-Only headers will be deprecated in the future. Please use the Content-Security-Policy and Content-Security-Report-Only headers with CSP spec compliant syntax instead.
|
||||
# LOCALIZATION NOTE: Do not translate "X-Content-Security-Policy/Report-Only" or "Content-Security-Policy/Report-Only"
|
||||
BothCSPHeadersPresent=This site specified both an X-Content-Security-Policy/Report-Only header and a Content-Security-Policy/Report-Only header. The X-Content-Security-Policy/Report-Only header(s) will be ignored.
|
||||
|
||||
# CORS
|
||||
CrossSiteRequestBlocked=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. This can be fixed by moving the resource to the same domain or enabling CORS.
|
||||
|
||||
|
@ -192,31 +192,48 @@ SystemMessageInternal.prototype = {
|
||||
// clean it up from the pending message queue when apps receive it.
|
||||
let messageID = gUUIDGenerator.generateUUID().toString();
|
||||
|
||||
debug("Sending " + aType + " " + JSON.stringify(aMessage) +
|
||||
" for " + aPageURI.spec + " @ " + aManifestURI.spec +
|
||||
'; extra: ' + JSON.stringify(aExtra));
|
||||
|
||||
let result = this._sendMessageCommon(aType,
|
||||
aMessage,
|
||||
messageID,
|
||||
aPageURI.spec,
|
||||
aManifestURI.spec,
|
||||
aExtra);
|
||||
debug("Returned status of sending message: " + result);
|
||||
|
||||
// Don't need to open the pages and queue the system message
|
||||
// which was not allowed to be sent.
|
||||
if (result === MSG_SENT_FAILURE_PERM_DENIED) {
|
||||
return;
|
||||
let manifestURL = aManifestURI.spec;
|
||||
let pageURLs = [];
|
||||
if (aPageURI) {
|
||||
pageURLs.push(aPageURI.spec);
|
||||
} else {
|
||||
// Send this message to all the registered pages of the app if |aPageURI|
|
||||
// is not specified.
|
||||
for (let i = 0; i < this._pages.length; i++) {
|
||||
let page = this._pages[i];
|
||||
if (page.type === aType && page.manifestURL === manifestURL) {
|
||||
pageURLs.push(page.pageURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let page = this._findPage(aType, aPageURI.spec, aManifestURI.spec);
|
||||
if (page) {
|
||||
// Queue this message in the corresponding pages.
|
||||
this._queueMessage(page, aMessage, messageID);
|
||||
pageURLs.forEach(function(aPageURL) {
|
||||
debug("Sending " + aType + " " + JSON.stringify(aMessage) +
|
||||
" for " + aPageURL + " @ " + manifestURL +
|
||||
'; extra: ' + JSON.stringify(aExtra));
|
||||
|
||||
this._openAppPage(page, aMessage, aExtra, result);
|
||||
}
|
||||
let result = this._sendMessageCommon(aType,
|
||||
aMessage,
|
||||
messageID,
|
||||
aPageURL,
|
||||
manifestURL,
|
||||
aExtra);
|
||||
debug("Returned status of sending message: " + result);
|
||||
|
||||
// Don't need to open the pages and queue the system message
|
||||
// which was not allowed to be sent.
|
||||
if (result === MSG_SENT_FAILURE_PERM_DENIED) {
|
||||
return;
|
||||
}
|
||||
|
||||
let page = this._findPage(aType, aPageURL, manifestURL);
|
||||
if (page) {
|
||||
// Queue this message in the corresponding pages.
|
||||
this._queueMessage(page, aMessage, messageID);
|
||||
|
||||
this._openAppPage(page, aMessage, aExtra, result);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
broadcastMessage: function(aType, aMessage, aExtra) {
|
||||
|
@ -15,7 +15,8 @@ Cu.import("resource://gre/modules/PermissionsTable.jsm");
|
||||
Cu.import("resource://gre/modules/PermissionSettings.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker",
|
||||
"SystemMessagePermissionsTable"];
|
||||
"SystemMessagePermissionsTable",
|
||||
"SystemMessagePrefixPermissionsTable"];
|
||||
|
||||
function debug(aStr) {
|
||||
// dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n");
|
||||
@ -122,6 +123,18 @@ this.SystemMessagePermissionsTable = {
|
||||
}
|
||||
};
|
||||
|
||||
// This table maps system message prefix to permission(s), indicating only
|
||||
// the system messages with specified prefixes granted by the page's permissions
|
||||
// are allowed to be registered or sent to that page. Note the empty permission
|
||||
// set means this type of system message is always permitted.
|
||||
//
|
||||
// Note that this table is only used when the permission checker can't find a
|
||||
// match in SystemMessagePermissionsTable listed above.
|
||||
|
||||
this.SystemMessagePrefixPermissionsTable = {
|
||||
"datastore-update-": { },
|
||||
};
|
||||
|
||||
this.SystemMessagePermissionsChecker = {
|
||||
/**
|
||||
* Return all the needed permission names for the given system message.
|
||||
@ -140,16 +153,26 @@ this.SystemMessagePermissionsChecker = {
|
||||
|
||||
let permNames = SystemMessagePermissionsTable[aSysMsgName];
|
||||
if (permNames === undefined) {
|
||||
debug("'" + aSysMsgName + "' is not associated with permissions. " +
|
||||
"Please add them to the SystemMessagePermissionsTable.");
|
||||
return null;
|
||||
// Try to look up in the prefix table.
|
||||
for (let sysMsgPrefix in SystemMessagePrefixPermissionsTable) {
|
||||
if (aSysMsgName.indexOf(sysMsgPrefix) === 0) {
|
||||
permNames = SystemMessagePrefixPermissionsTable[sysMsgPrefix];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (permNames === undefined) {
|
||||
debug("'" + aSysMsgName + "' is not associated with permissions. " +
|
||||
"Please add them to the SystemMessage[Prefix]PermissionsTable.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let object = { };
|
||||
for (let permName in permNames) {
|
||||
if (PermissionsTable[permName] === undefined) {
|
||||
debug("'" + permName + "' for '" + aSysMsgName + "' is invalid. " +
|
||||
"Please correct it in the SystemMessagePermissionsTable.");
|
||||
"Please correct it in the SystemMessage[Prefix]PermissionsTable.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -157,7 +180,7 @@ this.SystemMessagePermissionsChecker = {
|
||||
let access = permNames[permName];
|
||||
if (!access || !Array.isArray(access)) {
|
||||
debug("'" + permName + "' is not associated with access array. " +
|
||||
"Please correct it in the SystemMessagePermissionsTable.");
|
||||
"Please correct it in the SystemMessage[Prefix]PermissionsTable.");
|
||||
return null;
|
||||
}
|
||||
object[permName] = appendAccessToPermName(permName, access);
|
||||
|
@ -13,10 +13,12 @@ interface nsIDOMWindow;
|
||||
interface nsISystemMessagesInternal : nsISupports
|
||||
{
|
||||
/*
|
||||
* Allow any internal user to broadcast a message of a given type.
|
||||
* Allow any internal user to send a message of a given type to a given page
|
||||
* of an app. The message will be sent to all the registered pages of the app
|
||||
* when |pageURI| is not specified.
|
||||
* @param type The type of the message to be sent.
|
||||
* @param message The message payload.
|
||||
* @param pageURI The URI of the page that will be opened.
|
||||
* @param pageURI The URI of the page that will be opened. Nullable.
|
||||
* @param manifestURI The webapp's manifest URI.
|
||||
* @param extra Extra opaque information that will be passed around in the observer
|
||||
* notification to open the page.
|
||||
|
@ -51,12 +51,15 @@ public:
|
||||
protected:
|
||||
~PromiseTask()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseTask);
|
||||
MOZ_COUNT_DTOR(PromiseTask);
|
||||
}
|
||||
|
||||
public:
|
||||
NS_IMETHOD Run()
|
||||
NS_IMETHOD
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseTask);
|
||||
mPromise->mTaskPending = false;
|
||||
mPromise->RunTask();
|
||||
return NS_OK;
|
||||
@ -64,67 +67,40 @@ public:
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
};
|
||||
|
||||
class WorkerPromiseTask MOZ_FINAL : public WorkerSameThreadRunnable
|
||||
// This class processes the promise's callbacks with promise's result.
|
||||
class PromiseResolverTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
|
||||
: WorkerSameThreadRunnable(aWorkerPrivate)
|
||||
, mPromise(aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_COUNT_CTOR(WorkerPromiseTask);
|
||||
}
|
||||
|
||||
protected:
|
||||
~WorkerPromiseTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(WorkerPromiseTask);
|
||||
}
|
||||
|
||||
public:
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
mPromise->mTaskPending = false;
|
||||
mPromise->RunTask();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
class PromiseResolverMixin
|
||||
{
|
||||
public:
|
||||
PromiseResolverMixin(Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
PromiseResolverTask(Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: mPromise(aPromise)
|
||||
, mValue(CycleCollectedJSRuntime::Get()->Runtime(), aValue)
|
||||
, mState(aState)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(mState != Promise::Pending);
|
||||
MOZ_COUNT_CTOR(PromiseResolverMixin);
|
||||
MOZ_COUNT_CTOR(PromiseResolverTask);
|
||||
}
|
||||
|
||||
virtual ~PromiseResolverMixin()
|
||||
virtual
|
||||
~PromiseResolverTask()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
|
||||
MOZ_COUNT_DTOR(PromiseResolverMixin);
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseResolverTask);
|
||||
MOZ_COUNT_DTOR(PromiseResolverTask);
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
RunInternal()
|
||||
NS_IMETHOD
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
|
||||
NS_ASSERT_OWNINGTHREAD(PromiseResolverTask);
|
||||
mPromise->RunResolveTask(
|
||||
JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
|
||||
mState, Promise::SyncTask);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -134,50 +110,6 @@ private:
|
||||
NS_DECL_OWNINGTHREAD;
|
||||
};
|
||||
|
||||
// This class processes the promise's callbacks with promise's result.
|
||||
class PromiseResolverTask MOZ_FINAL : public nsRunnable,
|
||||
public PromiseResolverMixin
|
||||
{
|
||||
public:
|
||||
PromiseResolverTask(Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: PromiseResolverMixin(aPromise, aValue, aState)
|
||||
{}
|
||||
|
||||
~PromiseResolverTask()
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
RunInternal();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerPromiseResolverTask MOZ_FINAL : public WorkerSameThreadRunnable,
|
||||
public PromiseResolverMixin
|
||||
{
|
||||
public:
|
||||
WorkerPromiseResolverTask(WorkerPrivate* aWorkerPrivate,
|
||||
Promise* aPromise,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
Promise::PromiseState aState)
|
||||
: WorkerSameThreadRunnable(aWorkerPrivate),
|
||||
PromiseResolverMixin(aPromise, aValue, aState)
|
||||
{}
|
||||
|
||||
~WorkerPromiseResolverTask()
|
||||
{}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
RunInternal();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
enum {
|
||||
SLOT_PROMISE = 0,
|
||||
SLOT_DATA
|
||||
@ -239,11 +171,12 @@ GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
|
||||
}
|
||||
};
|
||||
|
||||
// Main thread runnable to resolve thenables.
|
||||
// Equivalent to the specification's ResolvePromiseViaThenableTask.
|
||||
class ThenableResolverMixin
|
||||
class ThenableResolverTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ThenableResolverMixin(Promise* aPromise,
|
||||
ThenableResolverTask(Promise* aPromise,
|
||||
JS::Handle<JSObject*> aThenable,
|
||||
PromiseInit* aThen)
|
||||
: mPromise(aPromise)
|
||||
@ -251,25 +184,26 @@ public:
|
||||
, mThen(aThen)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_COUNT_CTOR(ThenableResolverMixin);
|
||||
MOZ_COUNT_CTOR(ThenableResolverTask);
|
||||
}
|
||||
|
||||
virtual ~ThenableResolverMixin()
|
||||
virtual
|
||||
~ThenableResolverTask()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ThenableResolverMixin);
|
||||
MOZ_COUNT_DTOR(ThenableResolverMixin);
|
||||
NS_ASSERT_OWNINGTHREAD(ThenableResolverTask);
|
||||
MOZ_COUNT_DTOR(ThenableResolverTask);
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
RunInternal()
|
||||
NS_IMETHOD
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ThenableResolverMixin);
|
||||
NS_ASSERT_OWNINGTHREAD(ThenableResolverTask);
|
||||
ThreadsafeAutoJSContext cx;
|
||||
JS::Rooted<JSObject*> wrapper(cx, mPromise->GetWrapper());
|
||||
MOZ_ASSERT(wrapper); // It was preserved!
|
||||
if (!wrapper) {
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
|
||||
@ -278,14 +212,14 @@ protected:
|
||||
|
||||
if (!resolveFunc) {
|
||||
mPromise->HandleException(cx);
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> rejectFunc(cx,
|
||||
mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Reject));
|
||||
if (!rejectFunc) {
|
||||
mPromise->HandleException(cx);
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LinkThenableCallables(cx, resolveFunc, rejectFunc);
|
||||
@ -319,6 +253,8 @@ protected:
|
||||
// the exception. FIXME(nsm): This should be reported to the error
|
||||
// console though, for debugging.
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -328,60 +264,6 @@ private:
|
||||
NS_DECL_OWNINGTHREAD;
|
||||
};
|
||||
|
||||
// Main thread runnable to resolve thenables.
|
||||
class ThenableResolverTask MOZ_FINAL : public nsRunnable,
|
||||
public ThenableResolverMixin
|
||||
{
|
||||
public:
|
||||
ThenableResolverTask(Promise* aPromise,
|
||||
JS::Handle<JSObject*> aThenable,
|
||||
PromiseInit* aThen)
|
||||
: ThenableResolverMixin(aPromise, aThenable, aThen)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
~ThenableResolverTask()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RunInternal();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// Worker thread runnable to resolve thenables.
|
||||
class WorkerThenableResolverTask MOZ_FINAL : public WorkerSameThreadRunnable,
|
||||
public ThenableResolverMixin
|
||||
{
|
||||
public:
|
||||
WorkerThenableResolverTask(WorkerPrivate* aWorkerPrivate,
|
||||
Promise* aPromise,
|
||||
JS::Handle<JSObject*> aThenable,
|
||||
PromiseInit* aThen)
|
||||
: WorkerSameThreadRunnable(aWorkerPrivate),
|
||||
ThenableResolverMixin(aPromise, aThenable, aThen)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
~WorkerThenableResolverTask()
|
||||
{}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
RunInternal();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Promise
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
|
||||
@ -1017,19 +899,57 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
||||
// callbacks with promise's result. If promise's state is rejected, queue a
|
||||
// task to process our reject callbacks with promise's result.
|
||||
if (mState != Pending && !mTaskPending) {
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
nsRefPtr<PromiseTask> task = new PromiseTask(this);
|
||||
NS_DispatchToCurrentThread(task);
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
nsRefPtr<WorkerPromiseTask> task = new WorkerPromiseTask(worker, this);
|
||||
task->Dispatch(worker->GetJSContext());
|
||||
}
|
||||
nsRefPtr<PromiseTask> task = new PromiseTask(this);
|
||||
DispatchToMainOrWorkerThread(task);
|
||||
mTaskPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
class WrappedWorkerRunnable MOZ_FINAL : public WorkerSameThreadRunnable
|
||||
{
|
||||
public:
|
||||
WrappedWorkerRunnable(WorkerPrivate* aWorkerPrivate, nsIRunnable* aRunnable)
|
||||
: WorkerSameThreadRunnable(aWorkerPrivate)
|
||||
, mRunnable(aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
MOZ_COUNT_CTOR(WrappedWorkerRunnable);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(WrappedWorkerRunnable);
|
||||
mRunnable->Run();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual
|
||||
~WrappedWorkerRunnable()
|
||||
{
|
||||
MOZ_COUNT_DTOR(WrappedWorkerRunnable);
|
||||
NS_ASSERT_OWNINGTHREAD(WrappedWorkerRunnable);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
};
|
||||
|
||||
/* static */ void
|
||||
Promise::DispatchToMainOrWorkerThread(nsIRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
if (NS_IsMainThread()) {
|
||||
NS_DispatchToCurrentThread(aRunnable);
|
||||
return;
|
||||
}
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
nsRefPtr<WrappedWorkerRunnable> task = new WrappedWorkerRunnable(worker, aRunnable);
|
||||
task->Dispatch(worker->GetJSContext());
|
||||
}
|
||||
|
||||
void
|
||||
Promise::RunTask()
|
||||
{
|
||||
@ -1170,18 +1090,9 @@ Promise::ResolveInternal(JSContext* aCx,
|
||||
JS::Rooted<JSObject*> thenObj(aCx, &then.toObject());
|
||||
nsRefPtr<PromiseInit> thenCallback =
|
||||
new PromiseInit(thenObj, mozilla::dom::GetIncumbentGlobal());
|
||||
if (NS_IsMainThread()) {
|
||||
nsRefPtr<ThenableResolverTask> task =
|
||||
new ThenableResolverTask(this, valueObj, thenCallback);
|
||||
NS_DispatchToCurrentThread(task);
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
nsRefPtr<WorkerThenableResolverTask> task =
|
||||
new WorkerThenableResolverTask(worker, this, valueObj, thenCallback);
|
||||
task->Dispatch(worker->GetJSContext());
|
||||
}
|
||||
|
||||
nsRefPtr<ThenableResolverTask> task =
|
||||
new ThenableResolverTask(this, valueObj, thenCallback);
|
||||
DispatchToMainOrWorkerThread(task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1214,17 +1125,9 @@ Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
|
||||
// If the synchronous flag is unset, queue a task to process our
|
||||
// accept callbacks with value.
|
||||
if (aAsynchronous == AsyncTask) {
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
nsRefPtr<PromiseResolverTask> task =
|
||||
new PromiseResolverTask(this, aValue, aState);
|
||||
NS_DispatchToCurrentThread(task);
|
||||
} else {
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
nsRefPtr<WorkerPromiseResolverTask> task =
|
||||
new WorkerPromiseResolverTask(worker, this, aValue, aState);
|
||||
task->Dispatch(worker->GetJSContext());
|
||||
}
|
||||
nsRefPtr<PromiseResolverTask> task =
|
||||
new PromiseResolverTask(this, aValue, aState);
|
||||
DispatchToMainOrWorkerThread(task);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,6 @@ class Promise MOZ_FINAL : public nsISupports,
|
||||
public SupportsWeakPtr<Promise>
|
||||
{
|
||||
friend class NativePromiseCallback;
|
||||
friend class PromiseResolverMixin;
|
||||
friend class PromiseResolverTask;
|
||||
friend class PromiseTask;
|
||||
friend class PromiseReportRejectFeature;
|
||||
@ -63,9 +62,7 @@ class Promise MOZ_FINAL : public nsISupports,
|
||||
friend class PromiseWorkerProxyRunnable;
|
||||
friend class RejectPromiseCallback;
|
||||
friend class ResolvePromiseCallback;
|
||||
friend class ThenableResolverMixin;
|
||||
friend class WorkerPromiseResolverTask;
|
||||
friend class WorkerPromiseTask;
|
||||
friend class ThenableResolverTask;
|
||||
friend class WrapperPromiseCallback;
|
||||
|
||||
~Promise();
|
||||
@ -192,6 +189,10 @@ private:
|
||||
mResult = aValue;
|
||||
}
|
||||
|
||||
// Queue an async task to current main or worker thread.
|
||||
static void
|
||||
DispatchToMainOrWorkerThread(nsIRunnable* aRunnable);
|
||||
|
||||
// This method processes promise's resolve/reject callbacks with promise's
|
||||
// result. It's executed when the resolver.resolve() or resolver.reject() is
|
||||
// called or when the promise already has a result and new callbacks are
|
||||
|
@ -98,18 +98,44 @@ ServiceWorkerContainer::GetController()
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerContainer::GetRegistrations(ErrorResult& aRv)
|
||||
{
|
||||
// FIXME(nsm): Bug 1002571
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
aRv = swm->GetRegistrations(mWindow, getter_AddRefs(promise));
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> ret = static_cast<Promise*>(promise.get());
|
||||
MOZ_ASSERT(ret);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerContainer::GetRegistration(const nsAString& aDocumentURL,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// FIXME(nsm): Bug 1002571
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
aRv = swm->GetRegistration(mWindow, aDocumentURL, getter_AddRefs(promise));
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> ret = static_cast<Promise*>(promise.get());
|
||||
MOZ_ASSERT(ret);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
|
@ -539,6 +539,196 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||
return NS_DispatchToCurrentThread(registerRunnable);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the async aspects of the getRegistrations algorithm.
|
||||
*/
|
||||
class GetRegistrationsRunnable : public nsRunnable
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
public:
|
||||
GetRegistrationsRunnable(nsPIDOMWindow* aWindow, Promise* aPromise)
|
||||
: mWindow(aWindow), mPromise(aPromise)
|
||||
{ }
|
||||
|
||||
NS_IMETHODIMP
|
||||
Run()
|
||||
{
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
|
||||
nsIDocument* doc = mWindow->GetExtantDoc();
|
||||
if (!doc) {
|
||||
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
|
||||
if (!docURI) {
|
||||
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
|
||||
if (!principal) {
|
||||
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<nsRefPtr<ServiceWorkerRegistration>> array;
|
||||
|
||||
nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
|
||||
swm->GetDomainInfo(docURI);
|
||||
|
||||
if (!domainInfo) {
|
||||
mPromise->MaybeResolve(array);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < domainInfo->mOrderedScopes.Length(); ++i) {
|
||||
NS_ConvertUTF8toUTF16 scope(domainInfo->mOrderedScopes[i]);
|
||||
nsRefPtr<ServiceWorkerRegistration> swr =
|
||||
new ServiceWorkerRegistration(mWindow, scope);
|
||||
|
||||
array.AppendElement(swr);
|
||||
}
|
||||
|
||||
mPromise->MaybeResolve(array);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// If we return an error code here, the ServiceWorkerContainer will
|
||||
// automatically reject the Promise.
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::GetRegistrations(nsIDOMWindow* aWindow,
|
||||
nsISupports** aPromise)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
// XXXnsm Don't allow chrome callers for now, we don't support chrome
|
||||
// ServiceWorkers.
|
||||
MOZ_ASSERT(!nsContentUtils::IsCallerChrome());
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
||||
if (!window) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
|
||||
ErrorResult result;
|
||||
nsRefPtr<Promise> promise = Promise::Create(sgo, result);
|
||||
if (result.Failed()) {
|
||||
return result.ErrorCode();
|
||||
}
|
||||
|
||||
nsRefPtr<nsIRunnable> runnable =
|
||||
new GetRegistrationsRunnable(window, promise);
|
||||
promise.forget(aPromise);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the async aspects of the getRegistration algorithm.
|
||||
*/
|
||||
class GetRegistrationRunnable : public nsRunnable
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
nsString mDocumentURL;
|
||||
|
||||
public:
|
||||
GetRegistrationRunnable(nsPIDOMWindow* aWindow, Promise* aPromise,
|
||||
const nsAString& aDocumentURL)
|
||||
: mWindow(aWindow), mPromise(aPromise), mDocumentURL(aDocumentURL)
|
||||
{ }
|
||||
|
||||
NS_IMETHODIMP
|
||||
Run()
|
||||
{
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
|
||||
nsIDocument* doc = mWindow->GetExtantDoc();
|
||||
if (!doc) {
|
||||
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
|
||||
if (!docURI) {
|
||||
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), mDocumentURL, nullptr, docURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mPromise->MaybeReject(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
|
||||
if (!principal) {
|
||||
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = principal->CheckMayLoad(uri, true /* report */,
|
||||
false /* allowIfInheritsPrinciple */);
|
||||
if (NS_FAILED(rv)) {
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetServiceWorkerRegistrationInfo(uri);
|
||||
|
||||
if (!registration) {
|
||||
mPromise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ConvertUTF8toUTF16 scope(registration->mScope);
|
||||
nsRefPtr<ServiceWorkerRegistration> swr =
|
||||
new ServiceWorkerRegistration(mWindow, scope);
|
||||
mPromise->MaybeResolve(swr);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// If we return an error code here, the ServiceWorkerContainer will
|
||||
// automatically reject the Promise.
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::GetRegistration(nsIDOMWindow* aWindow,
|
||||
const nsAString& aDocumentURL,
|
||||
nsISupports** aPromise)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
// XXXnsm Don't allow chrome callers for now, we don't support chrome
|
||||
// ServiceWorkers.
|
||||
MOZ_ASSERT(!nsContentUtils::IsCallerChrome());
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
||||
if (!window) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
|
||||
ErrorResult result;
|
||||
nsRefPtr<Promise> promise = Promise::Create(sgo, result);
|
||||
if (result.Failed()) {
|
||||
return result.ErrorCode();
|
||||
}
|
||||
|
||||
nsRefPtr<nsIRunnable> runnable =
|
||||
new GetRegistrationRunnable(window, promise, aDocumentURL);
|
||||
promise.forget(aPromise);
|
||||
return NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::RejectUpdatePromiseObservers(ServiceWorkerRegistrationInfo* aRegistration,
|
||||
nsresult aRv)
|
||||
@ -1290,8 +1480,6 @@ ServiceWorkerManager::AddScope(nsTArray<nsCString>& aList, const nsACString& aSc
|
||||
/* static */ nsCString
|
||||
ServiceWorkerManager::FindScopeForPath(nsTArray<nsCString>& aList, const nsACString& aPath)
|
||||
{
|
||||
MOZ_ASSERT(aPath.FindChar('*') == -1);
|
||||
|
||||
nsCString match;
|
||||
|
||||
for (uint32_t i = 0; i < aList.Length(); ++i) {
|
||||
@ -1441,6 +1629,7 @@ ServiceWorkerManager::AddRegistrationEventListener(nsIURI* aDocumentURI, nsIDOME
|
||||
|
||||
// TODO: this is very very bad:
|
||||
ServiceWorkerRegistration* registration = static_cast<ServiceWorkerRegistration*>(aListener);
|
||||
MOZ_ASSERT(!domainInfo->mServiceWorkerRegistrations.Contains(registration));
|
||||
domainInfo->mServiceWorkerRegistrations.AppendElement(registration);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1455,6 +1644,7 @@ ServiceWorkerManager::RemoveRegistrationEventListener(nsIURI* aDocumentURI, nsID
|
||||
}
|
||||
|
||||
ServiceWorkerRegistration* registration = static_cast<ServiceWorkerRegistration*>(aListener);
|
||||
MOZ_ASSERT(domainInfo->mServiceWorkerRegistrations.Contains(registration));
|
||||
domainInfo->mServiceWorkerRegistrations.RemoveElement(registration);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -195,6 +195,8 @@ class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
|
||||
friend class CallInstallRunnable;
|
||||
friend class CancelServiceWorkerInstallationRunnable;
|
||||
friend class ServiceWorkerUpdateInstance;
|
||||
friend class GetRegistrationsRunnable;
|
||||
friend class GetRegistrationRunnable;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -8,11 +8,14 @@
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "ServiceWorker.h"
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
using namespace mozilla::dom::workers;
|
||||
@ -21,6 +24,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
|
||||
@ -37,11 +41,21 @@ ServiceWorkerRegistration::ServiceWorkerRegistration(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aScope)
|
||||
: mWindow(aWindow)
|
||||
, mScope(aScope)
|
||||
, mInnerID(0)
|
||||
, mIsListeningForEvents(false)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(aWindow->IsInnerWindow());
|
||||
|
||||
SetIsDOMBinding();
|
||||
StartListeningForEvents();
|
||||
|
||||
mInnerID = aWindow->WindowID();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, "inner-window-destroyed", false);
|
||||
}
|
||||
}
|
||||
|
||||
ServiceWorkerRegistration::~ServiceWorkerRegistration()
|
||||
@ -166,17 +180,25 @@ ServiceWorkerRegistration::InvalidateWorkerReference(WhichServiceWorker aWhichOn
|
||||
void
|
||||
ServiceWorkerRegistration::StartListeningForEvents()
|
||||
{
|
||||
MOZ_ASSERT(!mIsListeningForEvents);
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
MOZ_ASSERT(mWindow);
|
||||
|
||||
if (swm) {
|
||||
swm->AddRegistrationEventListener(GetDocumentURI(), this);
|
||||
}
|
||||
|
||||
mIsListeningForEvents = true;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistration::StopListeningForEvents()
|
||||
{
|
||||
if (!mIsListeningForEvents) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
|
||||
// StopListeningForEvents is called in the dtor, and it can happen that
|
||||
@ -184,6 +206,8 @@ ServiceWorkerRegistration::StopListeningForEvents()
|
||||
if (swm && mWindow) {
|
||||
swm->RemoveRegistrationEventListener(GetDocumentURI(), this);
|
||||
}
|
||||
|
||||
mIsListeningForEvents = false;
|
||||
}
|
||||
|
||||
nsIURI*
|
||||
@ -193,5 +217,37 @@ ServiceWorkerRegistration::GetDocumentURI() const
|
||||
return mWindow->GetDocumentURI();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistration::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (strcmp(aTopic, "inner-window-destroyed")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mIsListeningForEvents) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
|
||||
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
|
||||
|
||||
uint64_t innerID;
|
||||
nsresult rv = wrapper->GetData(&innerID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (innerID == mInnerID) {
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, "inner-window-destroyed");
|
||||
}
|
||||
|
||||
StopListeningForEvents();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
@ -22,9 +22,11 @@ class ServiceWorker;
|
||||
}
|
||||
|
||||
class ServiceWorkerRegistration MOZ_FINAL : public DOMEventTargetHelper
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistration,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
@ -91,6 +93,9 @@ private:
|
||||
nsRefPtr<workers::ServiceWorker> mActiveWorker;
|
||||
|
||||
const nsString mScope;
|
||||
|
||||
uint64_t mInnerID;
|
||||
bool mIsListeningForEvents;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -2,10 +2,59 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
navigator.serviceWorker.onupdatefound = function(e) {
|
||||
window.parent.postMessage("updatefound", "*");
|
||||
var expectedEvents = 2;
|
||||
function eventReceived() {
|
||||
window.parent.postMessage({ type: "check", status: expectedEvents > 0, msg: "updatefound received" }, "*");
|
||||
|
||||
if (--expectedEvents) {
|
||||
window.parent.postMessage({ type: "finish" }, "*");
|
||||
}
|
||||
}
|
||||
window.parent.postMessage("ready", "*");
|
||||
|
||||
navigator.serviceWorker.getRegistrations().then(function(a) {
|
||||
window.parent.postMessage({ type: "check", status: Array.isArray(a),
|
||||
msg: "getRegistrations returns an array" }, "*");
|
||||
window.parent.postMessage({ type: "check", status: a.length > 0,
|
||||
msg: "getRegistrations returns an array with 1 item" }, "*");
|
||||
for (var i = 0; i < a.length; ++i) {
|
||||
window.parent.postMessage({ type: "check", status: a[i] instanceof ServiceWorkerRegistration,
|
||||
msg: "getRegistrations returns an array of ServiceWorkerRegistration objects" }, "*");
|
||||
if (a[i].scope.match(/simpleregister\/\*/)) {
|
||||
a[i].onupdatefound = function(e) {
|
||||
eventReceived();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.getRegistration('http://mochi.test:8888/tests/dom/workers/test/serviceworkers/simpleregister/')
|
||||
.then(function(a) {
|
||||
window.parent.postMessage({ type: "check", status: a instanceof ServiceWorkerRegistration,
|
||||
msg: "getRegistration returns a ServiceWorkerRegistration" }, "*");
|
||||
a.onupdatefound = function(e) {
|
||||
eventReceived();
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.getRegistration('http://mochi.test:8888/with_star/*')
|
||||
.then(function(a) {
|
||||
window.parent.postMessage({ type: "check", status: true,
|
||||
msg: "getRegistration returns a ServiceWorkerRegistration" }, "*");
|
||||
a.onupdatefound = function(e) {
|
||||
eventReceived();
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.getRegistration('http://www.something_else.net/')
|
||||
.then(function(a) {
|
||||
window.parent.postMessage({ type: "check", status: false,
|
||||
msg: "getRegistration should throw for security error!" }, "*");
|
||||
}, function(a) {
|
||||
window.parent.postMessage({ type: "check", status: true,
|
||||
msg: "getRegistration should throw for security error!" }, "*");
|
||||
});
|
||||
|
||||
window.parent.postMessage({ type: "ready" }, "*");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -108,7 +108,6 @@
|
||||
|
||||
// FIXME(nsm): test for parse error when Update step doesn't happen (directly from register).
|
||||
|
||||
/* FIXME bug 1002571 - re-enable this test when GetRegistration is implemented.
|
||||
function updatefound() {
|
||||
var frame = document.createElement("iframe");
|
||||
frame.setAttribute("id", "simpleregister-frame");
|
||||
@ -125,9 +124,9 @@
|
||||
}
|
||||
|
||||
window.onmessage = function(e) {
|
||||
if (e.data == "ready") {
|
||||
if (e.data.type == "ready") {
|
||||
continueTest();
|
||||
} else if (e.data == "updatefound") {
|
||||
} else if (e.data.type == "finish") {
|
||||
window.onmessage = null;
|
||||
// We have to make frame navigate away, otherwise it will call
|
||||
// MaybeStopControlling() when this document is unloaded. At that point
|
||||
@ -135,11 +134,12 @@
|
||||
// return since it is currently gated.
|
||||
frame.setAttribute("src", new URL("about:blank").href);
|
||||
resolve();
|
||||
} else if (e.data.type == "check") {
|
||||
ok(e.data.status, e.data.msg);
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
*/
|
||||
|
||||
function runTest() {
|
||||
simpleRegister()
|
||||
@ -150,6 +150,7 @@
|
||||
.then(abortPrevious)
|
||||
.then(networkError404)
|
||||
.then(parseError)
|
||||
.then(updatefound)
|
||||
// put more tests here.
|
||||
.then(function() {
|
||||
SimpleTest.finish();
|
||||
|
@ -1134,6 +1134,7 @@ _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
|
||||
_cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)
|
||||
{
|
||||
double sf;
|
||||
cairo_matrix_t scale;
|
||||
|
||||
if (xdim > ydim)
|
||||
sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
|
||||
@ -1145,7 +1146,8 @@ _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
|
||||
p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
|
||||
p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
|
||||
|
||||
cairo_matrix_scale (&matrix, sf, sf);
|
||||
cairo_matrix_init_scale (&scale, sf, sf);
|
||||
cairo_matrix_multiply (&matrix, &matrix, &scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1182,9 +1184,9 @@ _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
|
||||
if (unlikely (pixman_image == NULL))
|
||||
return NULL;
|
||||
|
||||
tx = pattern->base.matrix.x0;
|
||||
ty = pattern->base.matrix.y0;
|
||||
if (! _cairo_matrix_is_translation (&pattern->base.matrix) ||
|
||||
tx = matrix.x0;
|
||||
ty = matrix.y0;
|
||||
if (! _cairo_matrix_is_translation (&matrix) ||
|
||||
! _nearest_sample (pattern->base.filter, &tx, &ty))
|
||||
{
|
||||
pixman_transform_t pixman_transform;
|
||||
@ -1192,21 +1194,30 @@ _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
|
||||
if (tx != 0. || ty != 0.) {
|
||||
cairo_matrix_t m, inv;
|
||||
cairo_status_t status;
|
||||
double x, y;
|
||||
double x, y, max_x, max_y;
|
||||
|
||||
/* pixman also limits the [xy]_offset to 16 bits so evenly
|
||||
* spread the bits between the two.
|
||||
/* Pixman also limits the [xy]_offset to 16 bits. We try to evenly
|
||||
* spread the bits between the two, but we need to ensure that
|
||||
* fabs (tx + extents->x + extents->width) < PIXMAN_MAX_INT &&
|
||||
* fabs (ty + extents->y + extents->height) < PIXMAN_MAX_INT,
|
||||
* otherwise the gradient won't render.
|
||||
*/
|
||||
inv = pattern->base.matrix;
|
||||
inv = matrix;
|
||||
status = cairo_matrix_invert (&inv);
|
||||
assert (status == CAIRO_STATUS_SUCCESS);
|
||||
|
||||
x = _cairo_lround (inv.x0 / 2);
|
||||
y = _cairo_lround (inv.y0 / 2);
|
||||
|
||||
max_x = PIXMAN_MAX_INT - 1 - fabs (extents->x + extents->width);
|
||||
x = x > max_x ? max_x : (x < -max_x ? -max_x : x);
|
||||
max_y = PIXMAN_MAX_INT - 1 - fabs (extents->y + extents->height);
|
||||
y = y > max_y ? max_y : (y < -max_y ? -max_y : y);
|
||||
|
||||
tx = -x;
|
||||
ty = -y;
|
||||
cairo_matrix_init_translate (&inv, x, y);
|
||||
cairo_matrix_multiply (&m, &inv, &pattern->base.matrix);
|
||||
cairo_matrix_multiply (&m, &inv, &matrix);
|
||||
_cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
|
||||
extents->x + extents->width/2.,
|
||||
extents->y + extents->height/2.);
|
||||
|
@ -1387,6 +1387,7 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
|
||||
_cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)
|
||||
{
|
||||
double sf;
|
||||
cairo_matrix_t scale;
|
||||
|
||||
if (xdim > ydim)
|
||||
sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
|
||||
@ -1398,7 +1399,8 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
|
||||
p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
|
||||
p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
|
||||
|
||||
cairo_matrix_scale (&matrix, sf, sf);
|
||||
cairo_matrix_init_scale (&scale, sf, sf);
|
||||
cairo_matrix_multiply (&matrix, &matrix, &scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -62,21 +62,20 @@ LayerManager::GetPrimaryScrollableLayer()
|
||||
nsTArray<Layer*> queue;
|
||||
queue.AppendElement(mRoot);
|
||||
while (queue.Length()) {
|
||||
ContainerLayer* containerLayer = queue[0]->AsContainerLayer();
|
||||
Layer* layer = queue[0];
|
||||
queue.RemoveElementAt(0);
|
||||
if (!containerLayer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics();
|
||||
const FrameMetrics& frameMetrics = layer->GetFrameMetrics();
|
||||
if (frameMetrics.IsScrollable()) {
|
||||
return containerLayer;
|
||||
return layer;
|
||||
}
|
||||
|
||||
Layer* child = containerLayer->GetFirstChild();
|
||||
while (child) {
|
||||
queue.AppendElement(child);
|
||||
child = child->GetNextSibling();
|
||||
if (ContainerLayer* containerLayer = layer->AsContainerLayer()) {
|
||||
Layer* child = containerLayer->GetFirstChild();
|
||||
while (child) {
|
||||
queue.AppendElement(child);
|
||||
child = child->GetNextSibling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,22 +92,21 @@ LayerManager::GetScrollableLayers(nsTArray<Layer*>& aArray)
|
||||
nsTArray<Layer*> queue;
|
||||
queue.AppendElement(mRoot);
|
||||
while (!queue.IsEmpty()) {
|
||||
ContainerLayer* containerLayer = queue.LastElement()->AsContainerLayer();
|
||||
Layer* layer = queue.LastElement();
|
||||
queue.RemoveElementAt(queue.Length() - 1);
|
||||
if (!containerLayer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const FrameMetrics& frameMetrics = containerLayer->GetFrameMetrics();
|
||||
const FrameMetrics& frameMetrics = layer->GetFrameMetrics();
|
||||
if (frameMetrics.IsScrollable()) {
|
||||
aArray.AppendElement(containerLayer);
|
||||
aArray.AppendElement(layer);
|
||||
continue;
|
||||
}
|
||||
|
||||
Layer* child = containerLayer->GetFirstChild();
|
||||
while (child) {
|
||||
queue.AppendElement(child);
|
||||
child = child->GetNextSibling();
|
||||
if (ContainerLayer* containerLayer = layer->AsContainerLayer()) {
|
||||
Layer* child = containerLayer->GetFirstChild();
|
||||
while (child) {
|
||||
queue.AppendElement(child);
|
||||
child = child->GetNextSibling();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +343,8 @@ public:
|
||||
|
||||
/**
|
||||
* Returns a list of all descendant layers for which
|
||||
* GetFrameMetrics().IsScrollable() is true.
|
||||
* GetFrameMetrics().IsScrollable() is true and that
|
||||
* do not already have an ancestor in the return list.
|
||||
*/
|
||||
void GetScrollableLayers(nsTArray<Layer*>& aArray);
|
||||
|
||||
|
@ -1020,7 +1020,7 @@ nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent
|
||||
nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
|
||||
APZC_LOG("%p got a touch-start in state %d\n", this, mState);
|
||||
mPanDirRestricted = false;
|
||||
ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
|
||||
ScreenPoint point = GetFirstTouchScreenPoint(aEvent);
|
||||
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
@ -1351,8 +1351,8 @@ AsyncPanZoomController::ConvertToGecko(const ScreenPoint& aPoint, CSSPoint* aOut
|
||||
nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
|
||||
|
||||
mX.StartTouch(aEvent.mPanStartPoint.x.Truncated(), aEvent.mTime);
|
||||
mY.StartTouch(aEvent.mPanStartPoint.y.Truncated(), aEvent.mTime);
|
||||
mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
|
||||
mY.StartTouch(aEvent.mPanStartPoint.y, aEvent.mTime);
|
||||
if (mPanGestureState) {
|
||||
mPanGestureState->GetOverscrollHandoffChain()->CancelAnimations();
|
||||
} else {
|
||||
@ -1377,8 +1377,8 @@ nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent)
|
||||
|
||||
mPanGestureState = MakeUnique<InputBlockState>(BuildOverscrollHandoffChain());
|
||||
|
||||
mX.StartTouch(aEvent.mPanStartPoint.x.Truncated(), aEvent.mTime);
|
||||
mY.StartTouch(aEvent.mPanStartPoint.y.Truncated(), aEvent.mTime);
|
||||
mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
|
||||
mY.StartTouch(aEvent.mPanStartPoint.y, aEvent.mTime);
|
||||
|
||||
if (GetAxisLockMode() == FREE) {
|
||||
SetState(PANNING);
|
||||
@ -1401,13 +1401,16 @@ nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool
|
||||
// size and position. We need to do so even if this is a momentum pan (i.e.
|
||||
// aFingersOnTouchpad == false); in that case the "with touch" part is not
|
||||
// really appropriate, so we may want to rethink this at some point.
|
||||
mX.UpdateWithTouchAtDevicePoint(aEvent.mPanStartPoint.x.Truncated(), aEvent.mTime);
|
||||
mY.UpdateWithTouchAtDevicePoint(aEvent.mPanStartPoint.y.Truncated(), aEvent.mTime);
|
||||
mX.UpdateWithTouchAtDevicePoint(aEvent.mPanStartPoint.x, aEvent.mTime);
|
||||
mY.UpdateWithTouchAtDevicePoint(aEvent.mPanStartPoint.y, aEvent.mTime);
|
||||
|
||||
HandlePanningUpdate(aEvent.mPanDisplacement.x, aEvent.mPanDisplacement.y);
|
||||
|
||||
CallDispatchScroll(aEvent.mPanStartPoint, aEvent.mPanStartPoint + aEvent.mPanDisplacement,
|
||||
*mPanGestureState->GetOverscrollHandoffChain(), 0);
|
||||
// TODO: Handle pan events sent without pan begin / pan end events properly.
|
||||
if (mPanGestureState) {
|
||||
CallDispatchScroll(aEvent.mPanStartPoint, aEvent.mPanStartPoint + aEvent.mPanDisplacement,
|
||||
*mPanGestureState->GetOverscrollHandoffChain(), 0);
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
@ -1652,7 +1655,7 @@ void AsyncPanZoomController::HandlePanningUpdate(float aDX, float aDY) {
|
||||
nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
|
||||
ScreenPoint point = GetFirstTouchScreenPoint(aEvent);
|
||||
float dx = mX.PanDistance(point.x);
|
||||
float dy = mY.PanDistance(point.y);
|
||||
|
||||
@ -1685,7 +1688,7 @@ nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
|
||||
ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
|
||||
ScreenPoint point = GetFirstTouchScreenPoint(aEvent);
|
||||
mX.UpdateWithTouchAtDevicePoint(point.x, aEvent.mTime);
|
||||
mY.UpdateWithTouchAtDevicePoint(point.y, aEvent.mTime);
|
||||
}
|
||||
@ -1854,8 +1857,8 @@ bool AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint,
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
|
||||
ScreenIntPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
|
||||
ScreenIntPoint touchPoint = GetFirstTouchScreenPoint(aEvent);
|
||||
ScreenPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
|
||||
ScreenPoint touchPoint = GetFirstTouchScreenPoint(aEvent);
|
||||
|
||||
float dx = mX.PanDistance(touchPoint.x);
|
||||
float dy = mY.PanDistance(touchPoint.y);
|
||||
@ -1869,7 +1872,7 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
ScreenIntPoint& AsyncPanZoomController::GetFirstTouchScreenPoint(const MultiTouchInput& aEvent) {
|
||||
ScreenPoint AsyncPanZoomController::GetFirstTouchScreenPoint(const MultiTouchInput& aEvent) {
|
||||
return ((SingleTouchData&)aEvent.mTouches[0]).mScreenPoint;
|
||||
}
|
||||
|
||||
|
@ -467,11 +467,10 @@ protected:
|
||||
const ScreenPoint GetVelocityVector();
|
||||
|
||||
/**
|
||||
* Gets a reference to the first touch point from a MultiTouchInput. This
|
||||
* gets only the first one and assumes the rest are either missing or not
|
||||
* relevant.
|
||||
* Gets the first touch point from a MultiTouchInput. This gets only
|
||||
* the first one and assumes the rest are either missing or not relevant.
|
||||
*/
|
||||
ScreenIntPoint& GetFirstTouchScreenPoint(const MultiTouchInput& aEvent);
|
||||
ScreenPoint GetFirstTouchScreenPoint(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Sets the panning state basing on the pan direction angle and current touch-action value.
|
||||
|
@ -33,7 +33,7 @@ Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
{
|
||||
}
|
||||
|
||||
void Axis::UpdateWithTouchAtDevicePoint(ScreenIntCoord aPos, uint32_t aTimestampMs) {
|
||||
void Axis::UpdateWithTouchAtDevicePoint(ScreenCoord aPos, uint32_t aTimestampMs) {
|
||||
// mVelocityQueue is controller-thread only
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
@ -46,7 +46,7 @@ void Axis::UpdateWithTouchAtDevicePoint(ScreenIntCoord aPos, uint32_t aTimestamp
|
||||
return;
|
||||
}
|
||||
|
||||
float newVelocity = mAxisLocked ? 0 : (float)(mPos - aPos) / (float)(aTimestampMs - mPosTimeMs);
|
||||
float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos) / (float)(aTimestampMs - mPosTimeMs);
|
||||
if (gfxPrefs::APZMaxVelocity() > 0.0f) {
|
||||
newVelocity = std::min(newVelocity, gfxPrefs::APZMaxVelocity() * APZCTreeManager::GetDPI());
|
||||
}
|
||||
@ -62,7 +62,7 @@ void Axis::UpdateWithTouchAtDevicePoint(ScreenIntCoord aPos, uint32_t aTimestamp
|
||||
}
|
||||
}
|
||||
|
||||
void Axis::StartTouch(ScreenIntCoord aPos, uint32_t aTimestampMs) {
|
||||
void Axis::StartTouch(ScreenCoord aPos, uint32_t aTimestampMs) {
|
||||
mStartPos = aPos;
|
||||
mPos = aPos;
|
||||
mPosTimeMs = aTimestampMs;
|
||||
@ -196,7 +196,7 @@ float Axis::PanDistance() {
|
||||
return fabsf((mPos - mStartPos).value);
|
||||
}
|
||||
|
||||
float Axis::PanDistance(ScreenIntCoord aPos) {
|
||||
float Axis::PanDistance(ScreenCoord aPos) {
|
||||
return fabsf((aPos - mStartPos).value);
|
||||
}
|
||||
|
||||
|
@ -55,13 +55,13 @@ public:
|
||||
* Notify this Axis that a new touch has been received, including a timestamp
|
||||
* for when the touch was received. This triggers a recalculation of velocity.
|
||||
*/
|
||||
void UpdateWithTouchAtDevicePoint(ScreenIntCoord aPos, uint32_t aTimestampMs);
|
||||
void UpdateWithTouchAtDevicePoint(ScreenCoord aPos, uint32_t aTimestampMs);
|
||||
|
||||
/**
|
||||
* Notify this Axis that a touch has begun, i.e. the user has put their finger
|
||||
* on the screen but has not yet tried to pan.
|
||||
*/
|
||||
void StartTouch(ScreenIntCoord aPos, uint32_t aTimestampMs);
|
||||
void StartTouch(ScreenCoord aPos, uint32_t aTimestampMs);
|
||||
|
||||
/**
|
||||
* Notify this Axis that a touch has ended gracefully. This may perform
|
||||
@ -129,7 +129,7 @@ public:
|
||||
* Gets the distance between the starting position of the touch supplied in
|
||||
* startTouch() and the supplied position.
|
||||
*/
|
||||
float PanDistance(ScreenIntCoord aPos);
|
||||
float PanDistance(ScreenCoord aPos);
|
||||
|
||||
/**
|
||||
* Applies friction during a fling, or cancels the fling if the velocity is
|
||||
@ -211,16 +211,16 @@ public:
|
||||
CSSCoord GetCompositionEnd() const;
|
||||
CSSCoord GetPageEnd() const;
|
||||
|
||||
ScreenIntCoord GetPos() const { return mPos; }
|
||||
ScreenCoord GetPos() const { return mPos; }
|
||||
|
||||
virtual CSSCoord GetPointOffset(const CSSPoint& aPoint) const = 0;
|
||||
virtual CSSCoord GetRectLength(const CSSRect& aRect) const = 0;
|
||||
virtual CSSCoord GetRectOffset(const CSSRect& aRect) const = 0;
|
||||
|
||||
protected:
|
||||
ScreenIntCoord mPos;
|
||||
ScreenCoord mPos;
|
||||
uint32_t mPosTimeMs;
|
||||
ScreenIntCoord mStartPos;
|
||||
ScreenCoord mStartPos;
|
||||
float mVelocity;
|
||||
bool mAxisLocked; // Whether movement on this axis is locked.
|
||||
AsyncPanZoomController* mAsyncPanZoomController;
|
||||
|
@ -242,7 +242,9 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
|
||||
mRootLayerTreeID = AllocateLayerTreeId();
|
||||
sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
|
||||
|
||||
mApzcTreeManager = new APZCTreeManager();
|
||||
if (gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
mApzcTreeManager = new APZCTreeManager();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@ -277,8 +279,10 @@ CompositorParent::Destroy()
|
||||
mCompositor = nullptr;
|
||||
|
||||
mCompositionManager = nullptr;
|
||||
mApzcTreeManager->ClearTree();
|
||||
mApzcTreeManager = nullptr;
|
||||
if (mApzcTreeManager) {
|
||||
mApzcTreeManager->ClearTree();
|
||||
mApzcTreeManager = nullptr;
|
||||
}
|
||||
sIndirectLayerTrees.erase(mRootLayerTreeID);
|
||||
}
|
||||
|
||||
|
@ -690,11 +690,7 @@ LayerTransactionParent::RecvSetAsyncScrollOffset(PLayerParent* aLayer,
|
||||
if (!layer) {
|
||||
return false;
|
||||
}
|
||||
ContainerLayer* containerLayer = layer->AsContainerLayer();
|
||||
if (!containerLayer) {
|
||||
return false;
|
||||
}
|
||||
AsyncPanZoomController* controller = containerLayer->GetAsyncPanZoomController();
|
||||
AsyncPanZoomController* controller = layer->GetAsyncPanZoomController();
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
|
@ -401,6 +401,35 @@ ComputeColorMatrix(uint32_t aColorMatrixType, const nsTArray<float>& aValues,
|
||||
break;
|
||||
}
|
||||
|
||||
case SVG_FECOLORMATRIX_TYPE_SEPIA:
|
||||
{
|
||||
if (aValues.Length() != 1)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
float amount = aValues[0];
|
||||
|
||||
if (amount < 0 || amount > 1)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PodCopy(aOutMatrix, identityMatrix, 20);
|
||||
|
||||
float s = 1 - amount;
|
||||
|
||||
aOutMatrix[0] = 0.393f + 0.607f * s;
|
||||
aOutMatrix[1] = 0.769f - 0.769f * s;
|
||||
aOutMatrix[2] = 0.189f - 0.189f * s;
|
||||
|
||||
aOutMatrix[5] = 0.349f - 0.349f * s;
|
||||
aOutMatrix[6] = 0.686f + 0.314f * s;
|
||||
aOutMatrix[7] = 0.168f - 0.168f * s;
|
||||
|
||||
aOutMatrix[10] = 0.272f - 0.272f * s;
|
||||
aOutMatrix[11] = 0.534f - 0.534f * s;
|
||||
aOutMatrix[12] = 0.131f + 0.869f * s;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -29,6 +29,8 @@ const unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_HUE_ROTATE = 3;
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCE_TO_ALPHA = 4;
|
||||
// ColorMatrix types for CSS filters
|
||||
const unsigned short SVG_FECOLORMATRIX_TYPE_SEPIA = 5;
|
||||
|
||||
// ComponentTransfer types
|
||||
const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN = 0;
|
||||
|
@ -1731,6 +1731,10 @@ protected:
|
||||
UniquePtr<ScopedLayerTreeRegistration> registration;
|
||||
TestAsyncPanZoomController* rootApzc;
|
||||
|
||||
void SetScrollHandoff(Layer* aChild, Layer* aParent) {
|
||||
aChild->SetScrollHandoffParentId(aParent->GetFrameMetrics().GetScrollId());
|
||||
}
|
||||
|
||||
void CreateOverscrollHandoffLayerTree1() {
|
||||
const char* layerTreeSyntax = "c(c)";
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
@ -1740,6 +1744,7 @@ protected:
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
|
||||
SetScrollHandoff(layers[1], root);
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(0, root, mcc);
|
||||
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
|
||||
rootApzc = (TestAsyncPanZoomController*)root->GetAsyncPanZoomController();
|
||||
@ -1756,6 +1761,8 @@ protected:
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 2, CSSRect(-100, -100, 200, 200));
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
|
||||
SetScrollHandoff(layers[1], root);
|
||||
SetScrollHandoff(layers[2], layers[1]);
|
||||
// No ScopedLayerTreeRegistration as that just needs to be done once per test
|
||||
// and this is the second layer tree for a particular test.
|
||||
MOZ_ASSERT(registration);
|
||||
@ -1777,6 +1784,8 @@ protected:
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
|
||||
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 50, 100, 100));
|
||||
SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 3, CSSRect(0, 50, 100, 100));
|
||||
SetScrollHandoff(layers[2], layers[1]);
|
||||
SetScrollHandoff(layers[4], layers[3]);
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(0, root, mcc);
|
||||
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
|
||||
}
|
||||
@ -1790,6 +1799,7 @@ protected:
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 120));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
|
||||
SetScrollHandoff(layers[1], root);
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(0, root, mcc);
|
||||
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
|
||||
rootApzc = (TestAsyncPanZoomController*)root->GetAsyncPanZoomController();
|
||||
|
@ -235,7 +235,9 @@ MessageChannel::MessageChannel(MessageListener *aListener)
|
||||
mSawInterruptOutMsg(false),
|
||||
mAbortOnError(false),
|
||||
mBlockScripts(false),
|
||||
mFlags(REQUIRE_DEFAULT)
|
||||
mFlags(REQUIRE_DEFAULT),
|
||||
mPeerPidSet(false),
|
||||
mPeerPid(-1)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ipc::MessageChannel);
|
||||
|
||||
@ -248,6 +250,10 @@ MessageChannel::MessageChannel(MessageListener *aListener)
|
||||
this,
|
||||
&MessageChannel::OnMaybeDequeueOne));
|
||||
|
||||
mOnChannelConnectedTask = new RefCountedTask(NewRunnableMethod(
|
||||
this,
|
||||
&MessageChannel::DispatchOnChannelConnected));
|
||||
|
||||
#ifdef OS_WIN
|
||||
mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
NS_ASSERTION(mEvent, "CreateEvent failed! Nothing is going to work!");
|
||||
@ -310,6 +316,8 @@ MessageChannel::Clear()
|
||||
delete mLink;
|
||||
mLink = nullptr;
|
||||
|
||||
mOnChannelConnectedTask->Cancel();
|
||||
|
||||
if (mChannelErrorTask) {
|
||||
mChannelErrorTask->Cancel();
|
||||
mChannelErrorTask = nullptr;
|
||||
@ -1491,19 +1499,19 @@ MessageChannel::SetReplyTimeoutMs(int32_t aTimeoutMs)
|
||||
void
|
||||
MessageChannel::OnChannelConnected(int32_t peer_id)
|
||||
{
|
||||
mWorkerLoop->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this,
|
||||
&MessageChannel::DispatchOnChannelConnected,
|
||||
peer_id));
|
||||
MOZ_ASSERT(!mPeerPidSet);
|
||||
mPeerPidSet = true;
|
||||
mPeerPid = peer_id;
|
||||
mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mOnChannelConnectedTask));
|
||||
}
|
||||
|
||||
void
|
||||
MessageChannel::DispatchOnChannelConnected(int32_t peer_pid)
|
||||
MessageChannel::DispatchOnChannelConnected()
|
||||
{
|
||||
AssertWorkerThread();
|
||||
MOZ_ASSERT(mPeerPidSet);
|
||||
if (mListener)
|
||||
mListener->OnChannelConnected(peer_pid);
|
||||
mListener->OnChannelConnected(mPeerPid);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "base/basictypes.h"
|
||||
#include "base/message_loop.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
@ -209,7 +210,7 @@ class MessageChannel : HasResultCodes
|
||||
void Clear();
|
||||
|
||||
// Send OnChannelConnected notification to listeners.
|
||||
void DispatchOnChannelConnected(int32_t peer_pid);
|
||||
void DispatchOnChannelConnected();
|
||||
|
||||
// Any protocol that requires blocking until a reply arrives, will send its
|
||||
// outgoing message through this function. Currently, two protocols do this:
|
||||
@ -665,6 +666,13 @@ class MessageChannel : HasResultCodes
|
||||
|
||||
// See SetChannelFlags
|
||||
ChannelFlags mFlags;
|
||||
|
||||
// Task and state used to asynchronously notify channel has been connected
|
||||
// safely. This is necessary to be able to cancel notification if we are
|
||||
// closed at the same time.
|
||||
nsRefPtr<RefCountedTask> mOnChannelConnectedTask;
|
||||
DebugOnly<bool> mPeerPidSet;
|
||||
int32_t mPeerPid;
|
||||
};
|
||||
|
||||
bool
|
||||
|
@ -344,16 +344,25 @@ ProcessLink::OnChannelConnected(int32_t peer_pid)
|
||||
{
|
||||
AssertIOThread();
|
||||
|
||||
bool notifyChannel = false;
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(*mChan->mMonitor);
|
||||
mChan->mChannelState = ChannelConnected;
|
||||
mChan->mMonitor->Notify();
|
||||
// Only update channel state if its still thinks its opening. Do not
|
||||
// force it into connected if it has errored out, started closing, etc.
|
||||
if (mChan->mChannelState == ChannelOpening) {
|
||||
mChan->mChannelState = ChannelConnected;
|
||||
mChan->mMonitor->Notify();
|
||||
notifyChannel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mExistingListener)
|
||||
mExistingListener->OnChannelConnected(peer_pid);
|
||||
|
||||
mChan->OnChannelConnected(peer_pid);
|
||||
if (notifyChannel) {
|
||||
mChan->OnChannelConnected(peer_pid);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user