Merge inbound to m-c a=merge

This commit is contained in:
Wes Kocher 2014-08-21 18:22:11 -07:00
commit f1bff82d6f
197 changed files with 4542 additions and 2282 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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");
}

View File

@ -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;

View File

@ -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();

View File

@ -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[

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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();

View 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])
};

View 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();
}
}
};

View File

@ -17,6 +17,8 @@ EXTRA_JS_MODULES += [
'ContentSearch.jsm',
'CustomizationTabPreloader.jsm',
'Feeds.jsm',
'FormSubmitObserver.jsm',
'FormValidationHandler.jsm',
'NetworkPrioritizer.jsm',
'offlineAppCache.jsm',
'PanelFrame.jsm',

View File

@ -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;
}

View File

@ -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")

View File

@ -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");

View File

@ -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

View File

@ -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>

View File

@ -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() {

View File

@ -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)
{

View 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;
}
}

View 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_

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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 += [

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -19,6 +19,7 @@ BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
EXPORTS += [
'AudioContext.h',
'AudioEventTimeline.h',
'AudioNodeEngine.h',
'AudioNodeExternalInputStream.h',
'AudioNodeStream.h',

View File

@ -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]

Binary file not shown.

View 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>

View File

@ -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;

View File

@ -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
];

View File

@ -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>

View File

@ -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)
{

View File

@ -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);

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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":

View File

@ -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)
{

View File

@ -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);
};

View 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>

View File

@ -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

View File

@ -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(); });
});

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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.

View File

@ -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) {

View File

@ -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);

View File

@ -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.

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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();

View File

@ -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.);

View File

@ -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
{

View File

@ -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();
}
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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.

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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