mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge inbound to m-c.
This commit is contained in:
commit
6f4ced18e9
@ -293,11 +293,12 @@ function eventQueue(aEventType)
|
||||
{
|
||||
// Some scenario was matched, we wait on next invoker processing.
|
||||
if (this.mNextInvokerStatus == kInvokerCanceled) {
|
||||
this.mNextInvokerStatus = kInvokerNotScheduled;
|
||||
this.setInvokerStatus(kInvokerNotScheduled,
|
||||
"scenario was matched, wait for next invoker activation");
|
||||
return;
|
||||
}
|
||||
|
||||
this.mNextInvokerStatus = kInvokerNotScheduled;
|
||||
this.setInvokerStatus(kInvokerNotScheduled, "the next invoker is processed now");
|
||||
|
||||
// Finish processing of the current invoker if any.
|
||||
var testFailed = false;
|
||||
@ -433,7 +434,7 @@ function eventQueue(aEventType)
|
||||
this.processNextInvokerInTimeout =
|
||||
function eventQueue_processNextInvokerInTimeout(aUncondProcess)
|
||||
{
|
||||
this.mNextInvokerStatus = kInvokerPending;
|
||||
this.setInvokerStatus(kInvokerPending, "Process next invoker in timeout");
|
||||
|
||||
// No need to wait extra timeout when a) we know we don't need to do that
|
||||
// and b) there's no any single unexpected event.
|
||||
@ -541,15 +542,22 @@ function eventQueue(aEventType)
|
||||
}
|
||||
|
||||
// If we don't have more events to wait then schedule next invoker.
|
||||
if (this.hasMatchedScenario() &&
|
||||
(this.mNextInvokerStatus == kInvokerNotScheduled)) {
|
||||
this.processNextInvokerInTimeout();
|
||||
if (this.hasMatchedScenario()) {
|
||||
if (this.mNextInvokerStatus == kInvokerNotScheduled) {
|
||||
this.processNextInvokerInTimeout();
|
||||
|
||||
} else if (this.mNextInvokerStatus == kInvokerCanceled) {
|
||||
this.setInvokerStatus(kInvokerPending,
|
||||
"Full match. Void the cancelation of next invoker processing");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have scheduled a next invoker then cancel in case of match.
|
||||
if ((this.mNextInvokerStatus == kInvokerPending) && hasMatchedCheckers)
|
||||
this.mNextInvokerStatus = kInvokerCanceled;
|
||||
if ((this.mNextInvokerStatus == kInvokerPending) && hasMatchedCheckers) {
|
||||
this.setInvokerStatus(kInvokerCanceled,
|
||||
"Cancel the scheduled invoker in case of match");
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
@ -622,6 +630,17 @@ function eventQueue(aEventType)
|
||||
return true;
|
||||
}
|
||||
|
||||
this.isUnexpectedEventScenario =
|
||||
function eventQueue_isUnexpectedEventsScenario(aScenario)
|
||||
{
|
||||
for (var idx = 0; idx < aScenario.length; idx++) {
|
||||
if (!aScenario[idx].unexpected)
|
||||
break;
|
||||
}
|
||||
|
||||
return idx == aScenario.length;
|
||||
}
|
||||
|
||||
this.hasUnexpectedEventsScenario =
|
||||
function eventQueue_hasUnexpectedEventsScenario()
|
||||
{
|
||||
@ -629,23 +648,19 @@ function eventQueue(aEventType)
|
||||
return true;
|
||||
|
||||
for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
|
||||
var eventSeq = this.mScenarios[scnIdx];
|
||||
for (var idx = 0; idx < eventSeq.length; idx++) {
|
||||
if (!eventSeq[idx].unexpected)
|
||||
break;
|
||||
}
|
||||
if (idx == eventSeq.length)
|
||||
if (this.isUnexpectedEventScenario(this.mScenarios[scnIdx]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
this.hasMatchedScenario =
|
||||
function eventQueue_hasMatchedScenario()
|
||||
{
|
||||
for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) {
|
||||
if (!this.areExpectedEventsLeft(this.mScenarios[scnIdx]))
|
||||
var scn = this.mScenarios[scnIdx];
|
||||
if (!this.isUnexpectedEventScenario(scn) && !this.areExpectedEventsLeft(scn))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -775,6 +790,14 @@ function eventQueue(aEventType)
|
||||
return invoker.getID();
|
||||
}
|
||||
|
||||
this.setInvokerStatus = function eventQueue_setInvokerStatus(aStatus, aLogMsg)
|
||||
{
|
||||
this.mNextInvokerStatus = aStatus;
|
||||
|
||||
// Uncomment it to debug invoker processing logic.
|
||||
//gLogger.log(eventQueue.invokerStatusToMsg(aStatus, aLogMsg));
|
||||
}
|
||||
|
||||
this.mDefEventType = aEventType;
|
||||
|
||||
this.mInvokers = new Array();
|
||||
@ -872,24 +895,10 @@ eventQueue.isSameEvent = function eventQueue_isSameEvent(aChecker, aEvent)
|
||||
!(aEvent instanceof nsIAccessibleStateChangeEvent);
|
||||
}
|
||||
|
||||
eventQueue.logEvent = function eventQueue_logEvent(aOrigEvent, aMatchedChecker,
|
||||
aScenarioIdx, aEventIdx,
|
||||
aAreExpectedEventsLeft,
|
||||
aInvokerStatus)
|
||||
eventQueue.invokerStatusToMsg =
|
||||
function eventQueue_invokerStatusToMsg(aInvokerStatus, aMsg)
|
||||
{
|
||||
if (!gLogger.isEnabled()) // debug stuff
|
||||
return;
|
||||
|
||||
// Dump DOM event information. Skip a11y event since it is dumped by
|
||||
// gA11yEventObserver.
|
||||
if (aOrigEvent instanceof nsIDOMEvent) {
|
||||
var info = "Event type: " + eventQueue.getEventTypeAsString(aOrigEvent);
|
||||
info += ". Target: " + eventQueue.getEventTargetDescr(aOrigEvent);
|
||||
gLogger.logToDOM(info);
|
||||
}
|
||||
|
||||
var msg = "unhandled expected events: " + aAreExpectedEventsLeft +
|
||||
", invoker status: ";
|
||||
var msg = "invoker status: ";
|
||||
switch (aInvokerStatus) {
|
||||
case kInvokerNotScheduled:
|
||||
msg += "not scheduled";
|
||||
@ -902,23 +911,37 @@ eventQueue.logEvent = function eventQueue_logEvent(aOrigEvent, aMatchedChecker,
|
||||
break;
|
||||
}
|
||||
|
||||
gLogger.logToConsole(msg);
|
||||
gLogger.logToDOM(msg);
|
||||
if (aMsg)
|
||||
msg += " (" + aMsg + ")";
|
||||
|
||||
if (!aMatchedChecker)
|
||||
return;
|
||||
return msg;
|
||||
}
|
||||
|
||||
var msg = "EQ: ";
|
||||
var emphText = "matched ";
|
||||
eventQueue.logEvent = function eventQueue_logEvent(aOrigEvent, aMatchedChecker,
|
||||
aScenarioIdx, aEventIdx,
|
||||
aAreExpectedEventsLeft,
|
||||
aInvokerStatus)
|
||||
{
|
||||
// Dump DOM event information. Skip a11y event since it is dumped by
|
||||
// gA11yEventObserver.
|
||||
if (aOrigEvent instanceof nsIDOMEvent) {
|
||||
var info = "Event type: " + eventQueue.getEventTypeAsString(aOrigEvent);
|
||||
info += ". Target: " + eventQueue.getEventTargetDescr(aOrigEvent);
|
||||
gLogger.logToDOM(info);
|
||||
}
|
||||
|
||||
var infoMsg = "unhandled expected events: " + aAreExpectedEventsLeft +
|
||||
", " + eventQueue.invokerStatusToMsg(aInvokerStatus);
|
||||
|
||||
var currType = eventQueue.getEventTypeAsString(aMatchedChecker);
|
||||
var currTargetDescr = eventQueue.getEventTargetDescr(aMatchedChecker);
|
||||
var consoleMsg = "*****\nScenario " + aScenarioIdx +
|
||||
", event " + aEventIdx + " matched: " + currType + "\n*****";
|
||||
", event " + aEventIdx + " matched: " + currType + "\n" + infoMsg + "\n*****";
|
||||
gLogger.logToConsole(consoleMsg);
|
||||
|
||||
msg += " event, type: " + currType + ", target: " + currTargetDescr;
|
||||
|
||||
var emphText = "matched ";
|
||||
var msg = "EQ event, type: " + currType + ", target: " + currTargetDescr +
|
||||
", " + infoMsg;
|
||||
gLogger.logToDOM(msg, true, emphText);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
gA11yEventDumpToConsole = true; // debugging stuff
|
||||
//gA11yEventDumpToConsole = true; // debugging stuff
|
||||
|
||||
function loadFile()
|
||||
{
|
||||
|
@ -747,9 +747,23 @@ pref("ping.manifestURL", "https://marketplace.firefox.com/packaged.webapp");
|
||||
// Enable the disk space watcher
|
||||
pref("disk_space_watcher.enabled", true);
|
||||
|
||||
// SNTP preferences.
|
||||
pref("network.sntp.maxRetryCount", 10);
|
||||
pref("network.sntp.refreshPeriod", 86400); // In seconds.
|
||||
pref("network.sntp.pools", // Servers separated by ';'.
|
||||
"0.pool.ntp.org;1.pool.ntp.org;2.pool.ntp.org;3.pool.ntp.org");
|
||||
pref("network.sntp.port", 123);
|
||||
pref("network.sntp.timeout", 30); // In seconds.
|
||||
|
||||
// Enable promise
|
||||
pref("dom.promise.enabled", false);
|
||||
|
||||
// DOM Inter-App Communication API.
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Enable this only for gonk-specific build but not for desktop build.
|
||||
pref("dom.inter-app-communication-api.enabled", true);
|
||||
#endif
|
||||
|
||||
// Allow ADB to run for this many hours before disabling
|
||||
// (only applies when marionette is disabled)
|
||||
// 0 disables the timer.
|
||||
|
@ -1069,10 +1069,14 @@ let CompositionManager = {
|
||||
_isStarted: false,
|
||||
_text: '',
|
||||
_clauseAttrMap: {
|
||||
'raw-input': domWindowUtils.COMPOSITION_ATTR_RAWINPUT,
|
||||
'selected-raw-text': domWindowUtils.COMPOSITION_ATTR_SELECTEDRAWTEXT,
|
||||
'converted-text': domWindowUtils.COMPOSITION_ATTR_CONVERTEDTEXT,
|
||||
'selected-converted-text': domWindowUtils.COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT
|
||||
'raw-input':
|
||||
Ci.nsICompositionStringSynthesizer.ATTR_RAWINPUT,
|
||||
'selected-raw-text':
|
||||
Ci.nsICompositionStringSynthesizer.ATTR_SELECTEDRAWTEXT,
|
||||
'converted-text':
|
||||
Ci.nsICompositionStringSynthesizer.ATTR_CONVERTEDTEXT,
|
||||
'selected-converted-text':
|
||||
Ci.nsICompositionStringSynthesizer.ATTR_SELECTEDCONVERTEDTEXT
|
||||
},
|
||||
|
||||
setComposition: function cm_setComposition(element, text, cursor, clauses) {
|
||||
@ -1081,20 +1085,14 @@ let CompositionManager = {
|
||||
return;
|
||||
}
|
||||
let len = text.length;
|
||||
if (cursor < 0) {
|
||||
cursor = 0;
|
||||
} else if (cursor > len) {
|
||||
if (cursor > len) {
|
||||
cursor = len;
|
||||
}
|
||||
let clauseLens = [len, 0, 0];
|
||||
let clauseAttrs = [domWindowUtils.COMPOSITION_ATTR_RAWINPUT,
|
||||
domWindowUtils.COMPOSITION_ATTR_RAWINPUT,
|
||||
domWindowUtils.COMPOSITION_ATTR_RAWINPUT];
|
||||
let clauseLens = [];
|
||||
let clauseAttrs = [];
|
||||
if (clauses) {
|
||||
let remainingLength = len;
|
||||
// Currently we don't support 4 or more clauses composition string.
|
||||
let clauseNum = Math.min(3, clauses.length);
|
||||
for (let i = 0; i < clauseNum; i++) {
|
||||
for (let i = 0; i < clauses.length; i++) {
|
||||
if (clauses[i]) {
|
||||
let clauseLength = clauses[i].length || 0;
|
||||
// Make sure the total clauses length is not bigger than that of the
|
||||
@ -1103,16 +1101,19 @@ let CompositionManager = {
|
||||
clauseLength = remainingLength;
|
||||
}
|
||||
remainingLength -= clauseLength;
|
||||
clauseLens[i] = clauseLength;
|
||||
clauseAttrs[i] = this._clauseAttrMap[clauses[i].selectionType] ||
|
||||
domWindowUtils.COMPOSITION_ATTR_RAWINPUT;
|
||||
clauseLens.push(clauseLength);
|
||||
clauseAttrs.push(this._clauseAttrMap[clauses[i].selectionType] ||
|
||||
Ci.nsICompositionStringSynthesizer.ATTR_RAWINPUT);
|
||||
}
|
||||
}
|
||||
// If the total clauses length is less than that of the composition
|
||||
// string, extend the last clause to the end of the composition string.
|
||||
if (remainingLength > 0) {
|
||||
clauseLens[2] += remainingLength;
|
||||
clauseLens[clauseLens.length - 1] += remainingLength;
|
||||
}
|
||||
} else {
|
||||
clauseLens.push(len);
|
||||
clauseAttrs.push(Ci.nsICompositionStringSynthesizer.ATTR_RAWINPUT);
|
||||
}
|
||||
|
||||
// Start composition if need to.
|
||||
@ -1127,11 +1128,15 @@ let CompositionManager = {
|
||||
this._text = text;
|
||||
domWindowUtils.sendCompositionEvent('compositionupdate', text, '');
|
||||
}
|
||||
domWindowUtils.sendTextEvent(text,
|
||||
clauseLens[0], clauseAttrs[0],
|
||||
clauseLens[1], clauseAttrs[1],
|
||||
clauseLens[2], clauseAttrs[2],
|
||||
cursor, 0);
|
||||
let compositionString = domWindowUtils.createCompositionStringSynthesizer();
|
||||
compositionString.setString(text);
|
||||
for (var i = 0; i < clauseLens.length; i++) {
|
||||
compositionString.appendClause(clauseLens[i], clauseAttrs[i]);
|
||||
}
|
||||
if (cursor >= 0) {
|
||||
compositionString.setCaret(cursor, 0);
|
||||
}
|
||||
compositionString.dispatchEvent();
|
||||
},
|
||||
|
||||
endComposition: function cm_endComposition(text) {
|
||||
@ -1142,9 +1147,12 @@ let CompositionManager = {
|
||||
if (this._text !== text) {
|
||||
domWindowUtils.sendCompositionEvent('compositionupdate', text, '');
|
||||
}
|
||||
let compositionString = domWindowUtils.createCompositionStringSynthesizer();
|
||||
compositionString.setString(text);
|
||||
// Set the cursor position to |text.length| so that the text will be
|
||||
// committed before the cursor position.
|
||||
domWindowUtils.sendTextEvent(text, 0, 0, 0, 0, 0, 0, text.length, 0);
|
||||
compositionString.setCaret(text.length, 0);
|
||||
compositionString.dispatchEvent();
|
||||
domWindowUtils.sendCompositionEvent('compositionend', text, '');
|
||||
this._text = '';
|
||||
this._isStarted = false;
|
||||
|
@ -631,9 +631,18 @@ Services.obs.addObserver(function onSystemMessageOpenApp(subject, topic, data) {
|
||||
shell.openAppForSystemMessage(msg);
|
||||
}, 'system-messages-open-app', false);
|
||||
|
||||
Services.obs.addObserver(function(aSubject, aTopic, aData) {
|
||||
Services.obs.addObserver(function onInterAppCommConnect(subject, topic, data) {
|
||||
data = JSON.parse(data);
|
||||
shell.sendChromeEvent({ type: "inter-app-comm-permission",
|
||||
chromeEventID: data.callerID,
|
||||
manifestURL: data.manifestURL,
|
||||
keyword: data.keyword,
|
||||
peers: data.appsToSelect });
|
||||
}, 'inter-app-comm-select-app', false);
|
||||
|
||||
Services.obs.addObserver(function onFullscreenOriginChange(subject, topic, data) {
|
||||
shell.sendChromeEvent({ type: "fullscreenoriginchange",
|
||||
fullscreenorigin: aData });
|
||||
fullscreenorigin: data });
|
||||
}, "fullscreen-origin-change", false);
|
||||
|
||||
Services.obs.addObserver(function onWebappsStart(subject, topic, data) {
|
||||
@ -700,6 +709,13 @@ var CustomEventManager = {
|
||||
case 'captive-portal-login-cancel':
|
||||
CaptivePortalLoginHelper.handleEvent(detail);
|
||||
break;
|
||||
case 'inter-app-comm-permission':
|
||||
Services.obs.notifyObservers(null, 'inter-app-comm-select-app-result',
|
||||
JSON.stringify({ callerID: detail.chromeEventID,
|
||||
keyword: detail.keyword,
|
||||
manifestURL: detail.manifestURL,
|
||||
selectedApps: detail.peers }));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -505,6 +505,11 @@
|
||||
@BINPATH@/components/Push.manifest
|
||||
@BINPATH@/components/PushServiceLauncher.js
|
||||
|
||||
@BINPATH@/components/InterAppComm.manifest
|
||||
@BINPATH@/components/InterAppCommService.js
|
||||
@BINPATH@/components/InterAppConnection.js
|
||||
@BINPATH@/components/InterAppMessagePort.js
|
||||
|
||||
@BINPATH@/components/nsDOMIdentity.js
|
||||
@BINPATH@/components/nsIDService.js
|
||||
@BINPATH@/components/Identity.manifest
|
||||
|
@ -374,10 +374,13 @@ def parsefile(pathname):
|
||||
|
||||
# colon followed by anything except a slash (Windows path detection)
|
||||
_depfilesplitter = re.compile(r':(?![\\/])')
|
||||
# simple variable references
|
||||
_vars = re.compile('\$\((\w+)\)')
|
||||
|
||||
def parsedepfile(pathname):
|
||||
"""
|
||||
Parse a filename listing only depencencies into a parserdata.StatementList.
|
||||
Simple variable references are allowed in such files.
|
||||
"""
|
||||
def continuation_iter(lines):
|
||||
current_line = []
|
||||
@ -394,12 +397,29 @@ def parsedepfile(pathname):
|
||||
if current_line:
|
||||
yield ''.join(current_line)
|
||||
|
||||
def get_expansion(s):
|
||||
if '$' in s:
|
||||
expansion = data.Expansion()
|
||||
# for an input like e.g. "foo $(bar) baz",
|
||||
# _vars.split returns ["foo", "bar", "baz"]
|
||||
# every other element is a variable name.
|
||||
for i, element in enumerate(_vars.split(s)):
|
||||
if i % 2:
|
||||
expansion.appendfunc(functions.VariableRef(None,
|
||||
data.StringExpansion(element, None)))
|
||||
elif element:
|
||||
expansion.appendstr(element)
|
||||
|
||||
return expansion
|
||||
|
||||
return data.StringExpansion(s, None)
|
||||
|
||||
pathname = os.path.realpath(pathname)
|
||||
stmts = parserdata.StatementList()
|
||||
for line in continuation_iter(open(pathname).readlines()):
|
||||
target, deps = _depfilesplitter.split(line, 1)
|
||||
stmts.append(parserdata.Rule(data.StringExpansion(target, None),
|
||||
data.StringExpansion(deps, None), False))
|
||||
stmts.append(parserdata.Rule(get_expansion(target),
|
||||
get_expansion(deps), False))
|
||||
return stmts
|
||||
|
||||
def parsestring(s, filename):
|
||||
|
1
build/pymake/tests/includedeps-variables.deps
Normal file
1
build/pymake/tests/includedeps-variables.deps
Normal file
@ -0,0 +1 @@
|
||||
$(FILE)1: filemissing
|
10
build/pymake/tests/includedeps-variables.mk
Normal file
10
build/pymake/tests/includedeps-variables.mk
Normal file
@ -0,0 +1,10 @@
|
||||
#T gmake skip
|
||||
|
||||
FILE = includedeps-variables
|
||||
|
||||
all: $(FILE)1
|
||||
|
||||
includedeps $(TESTPATH)/includedeps-variables.deps
|
||||
|
||||
filemissing:
|
||||
@echo TEST-PASS
|
@ -10,11 +10,6 @@ NO_PROFILE_GUIDED_OPTIMIZE = 1
|
||||
|
||||
VPATH += $(topsrcdir)/build
|
||||
|
||||
HOST_CPPSRCS = \
|
||||
elf.cpp \
|
||||
elfhack.cpp \
|
||||
$(NULL)
|
||||
|
||||
OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions
|
||||
|
||||
ifndef CROSS_COMPILE
|
||||
|
@ -12,3 +12,7 @@ CSRCS += [
|
||||
'test-ctors.c',
|
||||
]
|
||||
|
||||
HOST_CPPSRCS += [
|
||||
'elf.cpp',
|
||||
'elfhack.cpp',
|
||||
]
|
||||
|
@ -7,10 +7,6 @@ STL_FLAGS =
|
||||
NO_EXPAND_LIBS = 1
|
||||
NO_PROFILE_GUIDED_OPTIMIZE = 1
|
||||
|
||||
ifdef MOZ_LIBSTDCXX_HOST_VERSION
|
||||
HOST_CPPSRCS = stdc++compat.cpp
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_TARGET_VERSION)
|
||||
|
@ -12,4 +12,7 @@ if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION']:
|
||||
|
||||
if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
|
||||
HOST_LIBRARY_NAME = 'host_stdc++compat'
|
||||
HOST_CPPSRCS += [
|
||||
'stdc++compat.cpp',
|
||||
]
|
||||
|
||||
|
@ -10,10 +10,12 @@ interface nsIDocShell;
|
||||
|
||||
/**
|
||||
* nsIContentSecurityPolicy
|
||||
* Describes an XPCOM component used to model an enforce CSPs.
|
||||
* Describes an XPCOM component used to model and enforce CSPs. Instances of
|
||||
* this class may have multiple policies within them, but there should only be
|
||||
* one of these per document/principal.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(230b126d-afc3-4588-9794-3e135594d626)]
|
||||
[scriptable, uuid(e5020ec3-1437-46f5-b4eb-8b60766d02c0)]
|
||||
interface nsIContentSecurityPolicy : nsISupports
|
||||
{
|
||||
|
||||
@ -25,61 +27,87 @@ interface nsIContentSecurityPolicy : nsISupports
|
||||
attribute boolean isInitialized;
|
||||
|
||||
/**
|
||||
* When set to true, content load-blocking and fail-closed are disabled: CSP
|
||||
* will ONLY send reports, and not modify behavior.
|
||||
* Accessor method for a read-only string version of the policy at a given
|
||||
* index.
|
||||
*/
|
||||
attribute boolean reportOnlyMode;
|
||||
AString getPolicy(in unsigned long index);
|
||||
|
||||
/**
|
||||
* A read-only string version of the policy for debugging.
|
||||
* Returns the number of policies attached to this CSP instance. Useful with
|
||||
* getPolicy().
|
||||
*/
|
||||
readonly attribute AString policy;
|
||||
attribute long policyCount;
|
||||
|
||||
/**
|
||||
* Remove a policy associated with this CSP context.
|
||||
* @throws NS_ERROR_FAILURE if the index is out of bounds or invalid.
|
||||
*/
|
||||
void removePolicy(in unsigned long index);
|
||||
|
||||
/**
|
||||
* Parse and install a CSP policy.
|
||||
* @param aPolicy
|
||||
* String representation of the policy (e.g., header value)
|
||||
* @param selfURI
|
||||
* the URI of the protected document/principal
|
||||
* @param reportOnly
|
||||
* Should this policy affect content, script and style processing or
|
||||
* just send reports if it is violated?
|
||||
* @param specCompliant
|
||||
* Whether or not the policy conforms to the W3C specification.
|
||||
* If this is false, that indicates this policy is from the older
|
||||
* implementation with different semantics and directive names.
|
||||
*/
|
||||
void appendPolicy(in AString policyString, in nsIURI selfURI,
|
||||
in boolean reportOnly, in boolean specCompliant);
|
||||
|
||||
/**
|
||||
* Whether this policy allows in-page script.
|
||||
* @param shouldReportViolation
|
||||
* @param shouldReportViolations
|
||||
* Whether or not the use of inline script should be reported.
|
||||
* This function always returns "true" for report-only policies, but when
|
||||
* the report-only policy is violated, shouldReportViolation is true as
|
||||
* well.
|
||||
* any policy (report-only or otherwise) is violated,
|
||||
* shouldReportViolations is true as well.
|
||||
* @return
|
||||
* Whether or not the effects of the inline script should be allowed
|
||||
* (block the compilation if false).
|
||||
*/
|
||||
boolean getAllowsInlineScript(out boolean shouldReportViolation);
|
||||
boolean getAllowsInlineScript(out boolean shouldReportViolations);
|
||||
|
||||
/**
|
||||
* whether this policy allows eval and eval-like functions
|
||||
* such as setTimeout("code string", time).
|
||||
* @param shouldReportViolation
|
||||
* @param shouldReportViolations
|
||||
* Whether or not the use of eval should be reported.
|
||||
* This function always returns "true" for report-only policies, but when
|
||||
* the report-only policy is violated, shouldReportViolation is true as
|
||||
* well.
|
||||
* This function returns "true" when violating report-only policies, but
|
||||
* when any policy (report-only or otherwise) is violated,
|
||||
* shouldReportViolations is true as well.
|
||||
* @return
|
||||
* Whether or not the effects of the eval call should be allowed
|
||||
* (block the call if false).
|
||||
*/
|
||||
boolean getAllowsEval(out boolean shouldReportViolation);
|
||||
boolean getAllowsEval(out boolean shouldReportViolations);
|
||||
|
||||
/**
|
||||
* Whether this policy allows in-page styles.
|
||||
* This includes <style> tags with text content and style="" attributes in
|
||||
* HTML elements.
|
||||
* @param shouldReportViolation
|
||||
* Whether or not the use of eval should be reported.
|
||||
* This function always returns "true" for report-only policies, but when
|
||||
* the report-only policy is violated, shouldReportViolation is true as
|
||||
* well.
|
||||
* @param shouldReportViolations
|
||||
* Whether or not the use of inline style should be reported.
|
||||
* If there are report-only policies, this function may return true
|
||||
* (don't block), but one or more policy may still want to send
|
||||
* violation reports so shouldReportViolations will be true even if the
|
||||
* inline style should be permitted.
|
||||
* @return
|
||||
* Whether or not the effects of the eval call should be allowed
|
||||
* (block the call if false).
|
||||
* Whether or not the effects of the inline style should be allowed
|
||||
* (block the rules if false).
|
||||
*/
|
||||
boolean getAllowsInlineStyle(out boolean shouldReportViolation);
|
||||
boolean getAllowsInlineStyle(out boolean shouldReportViolations);
|
||||
|
||||
/**
|
||||
* Log policy violation on the Error Console and send a report if a report-uri
|
||||
* is present in the policy
|
||||
* For each violated policy (of type violationType), log policy violation on
|
||||
* the Error Console and send a report to report-uris present in the violated
|
||||
* policies.
|
||||
*
|
||||
* @param violationType
|
||||
* one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
|
||||
@ -99,47 +127,24 @@ interface nsIContentSecurityPolicy : nsISupports
|
||||
const unsigned short VIOLATION_TYPE_EVAL = 2;
|
||||
const unsigned short VIOLATION_TYPE_INLINE_STYLE = 3;
|
||||
|
||||
/**
|
||||
* Manually triggers violation report sending given a URI and reason.
|
||||
* The URI may be null, in which case "self" is sent.
|
||||
* @param blockedURI
|
||||
* the URI that violated the policy
|
||||
* @param violatedDirective
|
||||
* the directive that was violated.
|
||||
* @param scriptSample
|
||||
* a sample of the violating inline script
|
||||
* @param lineNum
|
||||
* source line number of the violation (if available)
|
||||
* @return
|
||||
* nothing.
|
||||
*/
|
||||
void sendReports(in AString blockedURI,
|
||||
in AString violatedDirective,
|
||||
in AString scriptSample,
|
||||
in int32_t lineNum);
|
||||
|
||||
/**
|
||||
* Called after the CSP object is created to fill in the appropriate request
|
||||
* and request header information needed in case a report needs to be sent.
|
||||
*/
|
||||
void scanRequestData(in nsIHttpChannel aChannel);
|
||||
|
||||
/**
|
||||
* Updates the policy currently stored in the CSP to be "refined" or
|
||||
* tightened by the one specified in the string policyString.
|
||||
*/
|
||||
void refinePolicy(in AString policyString, in nsIURI selfURI, in boolean specCompliant);
|
||||
|
||||
/**
|
||||
* Verifies ancestry as permitted by the policy.
|
||||
*
|
||||
* Calls to this may trigger violation reports when queried, so
|
||||
* this value should not be cached.
|
||||
* NOTE: Calls to this may trigger violation reports when queried, so this
|
||||
* value should not be cached.
|
||||
*
|
||||
* @param docShell
|
||||
* containing the protected resource
|
||||
* @return
|
||||
* true if the frame's ancestors are all permitted by policy
|
||||
* true if the frame's ancestors are all allowed by policy (except for
|
||||
* report-only policies, which will send reports and then return true
|
||||
* here when violated).
|
||||
*/
|
||||
boolean permitsAncestry(in nsIDocShell docShell);
|
||||
|
||||
@ -151,11 +156,11 @@ interface nsIContentSecurityPolicy : nsISupports
|
||||
* Calls to this may trigger violation reports when queried, so
|
||||
* this value should not be cached.
|
||||
*/
|
||||
short shouldLoad(in unsigned long aContentType,
|
||||
in nsIURI aContentLocation,
|
||||
in nsIURI aRequestOrigin,
|
||||
in nsISupports aContext,
|
||||
in ACString aMimeTypeGuess,
|
||||
short shouldLoad(in unsigned long aContentType,
|
||||
in nsIURI aContentLocation,
|
||||
in nsIURI aRequestOrigin,
|
||||
in nsISupports aContext,
|
||||
in ACString aMimeTypeGuess,
|
||||
in nsISupports aExtra);
|
||||
|
||||
/**
|
||||
@ -163,10 +168,10 @@ interface nsIContentSecurityPolicy : nsISupports
|
||||
* document are being processed. Given a bit of information about the request,
|
||||
* decides whether or not the policy is satisfied.
|
||||
*/
|
||||
short shouldProcess(in unsigned long aContentType,
|
||||
in nsIURI aContentLocation,
|
||||
in nsIURI aRequestOrigin,
|
||||
in nsISupports aContext,
|
||||
short shouldProcess(in unsigned long aContentType,
|
||||
in nsIURI aContentLocation,
|
||||
in nsIURI aRequestOrigin,
|
||||
in nsISupports aContext,
|
||||
in ACString aMimeType,
|
||||
in nsISupports aExtra);
|
||||
|
||||
|
@ -108,7 +108,7 @@ this.CSPdebug = function CSPdebug(aMsg) {
|
||||
}
|
||||
|
||||
// Callback to resume a request once the policy-uri has been fetched
|
||||
function CSPPolicyURIListener(policyURI, docRequest, csp) {
|
||||
function CSPPolicyURIListener(policyURI, docRequest, csp, reportOnly) {
|
||||
this._policyURI = policyURI; // location of remote policy
|
||||
this._docRequest = docRequest; // the parent document request
|
||||
this._csp = csp; // parent document's CSP
|
||||
@ -116,6 +116,7 @@ function CSPPolicyURIListener(policyURI, docRequest, csp) {
|
||||
this._wrapper = null; // nsIScriptableInputStream
|
||||
this._docURI = docRequest.QueryInterface(Ci.nsIChannel)
|
||||
.URI; // parent document URI (to be used as 'self')
|
||||
this._reportOnly = reportOnly;
|
||||
}
|
||||
|
||||
CSPPolicyURIListener.prototype = {
|
||||
@ -147,8 +148,8 @@ CSPPolicyURIListener.prototype = {
|
||||
if (Components.isSuccessCode(status)) {
|
||||
// send the policy we received back to the parent document's CSP
|
||||
// for parsing
|
||||
this._csp.refinePolicy(this._policy, this._docURI,
|
||||
this._csp._specCompliant);
|
||||
this._csp.appendPolicy(this._policy, this._docURI,
|
||||
this._reportOnly, this._csp._specCompliant);
|
||||
}
|
||||
else {
|
||||
// problem fetching policy so fail closed
|
||||
@ -177,6 +178,7 @@ this.CSPRep = function CSPRep(aSpecCompliant) {
|
||||
|
||||
this._allowEval = false;
|
||||
this._allowInlineScripts = false;
|
||||
this._reportOnlyMode = false;
|
||||
|
||||
// don't auto-populate _directives, so it is easier to find bugs
|
||||
this._directives = {};
|
||||
@ -244,12 +246,14 @@ CSPRep.ALLOW_DIRECTIVE = "allow";
|
||||
* @returns
|
||||
* an instance of CSPRep
|
||||
*/
|
||||
CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
||||
CSPRep.fromString = function(aStr, self, docRequest, csp, reportOnly) {
|
||||
if (typeof reportOnly === 'undefined') reportOnly = false;
|
||||
var SD = CSPRep.SRC_DIRECTIVES_OLD;
|
||||
var UD = CSPRep.URI_DIRECTIVES;
|
||||
var aCSPR = new CSPRep();
|
||||
aCSPR._originalText = aStr;
|
||||
aCSPR._innerWindowID = innerWindowFromRequest(docRequest);
|
||||
aCSPR._reportOnlyMode = reportOnly;
|
||||
|
||||
var selfUri = null;
|
||||
if (self instanceof Ci.nsIURI) {
|
||||
@ -451,7 +455,7 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
||||
// policy-uri can't be abused for CSRF
|
||||
chan.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
|
||||
chan.loadGroup = docRequest.loadGroup;
|
||||
chan.asyncOpen(new CSPPolicyURIListener(uri, docRequest, csp), null);
|
||||
chan.asyncOpen(new CSPPolicyURIListener(uri, docRequest, csp, reportOnly), null);
|
||||
}
|
||||
catch (e) {
|
||||
// resume the document request and apply most restrictive policy
|
||||
@ -461,8 +465,8 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
|
||||
// return a fully-open policy to be intersected with the contents of the
|
||||
// policy-uri when it returns
|
||||
// return a fully-open policy to be used until the contents of the
|
||||
// policy-uri come back.
|
||||
return CSPRep.fromString("default-src *");
|
||||
}
|
||||
|
||||
@ -499,12 +503,15 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
||||
*/
|
||||
// When we deprecate our original CSP implementation, we rename this to
|
||||
// CSPRep.fromString and remove the existing CSPRep.fromString above.
|
||||
CSPRep.fromStringSpecCompliant = function(aStr, self, docRequest, csp) {
|
||||
CSPRep.fromStringSpecCompliant = function(aStr, self, docRequest, csp, reportOnly) {
|
||||
if (typeof reportOnly === 'undefined') reportOnly = false;
|
||||
|
||||
var SD = CSPRep.SRC_DIRECTIVES_NEW;
|
||||
var UD = CSPRep.URI_DIRECTIVES;
|
||||
var aCSPR = new CSPRep(true);
|
||||
aCSPR._originalText = aStr;
|
||||
aCSPR._innerWindowID = innerWindowFromRequest(docRequest);
|
||||
aCSPR._reportOnlyMode = reportOnly;
|
||||
|
||||
var selfUri = null;
|
||||
if (self instanceof Ci.nsIURI) {
|
||||
@ -716,8 +723,8 @@ CSPRep.fromStringSpecCompliant = function(aStr, self, docRequest, csp) {
|
||||
return CSPRep.fromStringSpecCompliant("default-src 'none'");
|
||||
}
|
||||
|
||||
// return a fully-open policy to be intersected with the contents of the
|
||||
// policy-uri when it returns
|
||||
// return a fully-open policy to be used until the contents of the
|
||||
// policy-uri come back
|
||||
return CSPRep.fromStringSpecCompliant("default-src *");
|
||||
}
|
||||
|
||||
@ -836,120 +843,6 @@ CSPRep.prototype = {
|
||||
return this._specCompliant;
|
||||
},
|
||||
|
||||
/**
|
||||
* Intersects with another CSPRep, deciding the subset policy
|
||||
* that should be enforced, and returning a new instance.
|
||||
* This assumes that either both CSPReps are specCompliant or they are both
|
||||
* not.
|
||||
* @param aCSPRep
|
||||
* a CSPRep instance to use as "other" CSP
|
||||
* @returns
|
||||
* a new CSPRep instance of the intersection
|
||||
*/
|
||||
intersectWith:
|
||||
function cspsd_intersectWith(aCSPRep) {
|
||||
var newRep = new CSPRep();
|
||||
|
||||
let DIRS = aCSPRep._specCompliant ? CSPRep.SRC_DIRECTIVES_NEW :
|
||||
CSPRep.SRC_DIRECTIVES_OLD;
|
||||
|
||||
// one or more of the two CSPReps may not have any given directive. In
|
||||
// these cases, we need to pick "all" or "none" based on the type of CSPRep
|
||||
// (spec compliant or not).
|
||||
let thisHasDefault = this._directives.hasOwnProperty(DIRS.DEFAULT_SRC),
|
||||
thatHasDefault = aCSPRep._directives.hasOwnProperty(DIRS.DEFAULT_SRC);
|
||||
for (var dir in DIRS) {
|
||||
let dirv = DIRS[dir];
|
||||
let thisHasDir = this._directives.hasOwnProperty(dirv),
|
||||
thatHasDir = aCSPRep._directives.hasOwnProperty(dirv);
|
||||
|
||||
|
||||
// if both specific src directives are absent, skip this (new policy will
|
||||
// rely on default-src)
|
||||
if (!thisHasDir && !thatHasDir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// frame-ancestors is a special case; it doesn't fall back to
|
||||
// default-src, so only add it to newRep if one or both of the policies
|
||||
// have it.
|
||||
if (dirv === DIRS.FRAME_ANCESTORS) {
|
||||
if (thisHasDir && thatHasDir) {
|
||||
// both have frame-ancestors, intersect them.
|
||||
newRep._directives[dirv] =
|
||||
aCSPRep._directives[dirv].intersectWith(this._directives[dirv]);
|
||||
} else if (thisHasDir || thatHasDir) {
|
||||
// one or the other has frame-ancestors, copy it.
|
||||
newRep._directives[dirv] =
|
||||
( thisHasDir ? this : aCSPRep )._directives[dirv].clone();
|
||||
}
|
||||
}
|
||||
else if (aCSPRep._specCompliant) {
|
||||
// CSP 1.0 doesn't require default-src, so an intersection only makes
|
||||
// sense if there is a default-src or both policies have the directive.
|
||||
|
||||
if (!thisHasDir && !thisHasDefault) {
|
||||
// only aCSPRep has a relevant directive
|
||||
newRep._directives[dirv] = aCSPRep._directives[dirv].clone();
|
||||
}
|
||||
else if (!thatHasDir && !thatHasDefault) {
|
||||
// only "this" has a relevant directive
|
||||
newRep._directives[dirv] = this._directives[dirv].clone();
|
||||
}
|
||||
else {
|
||||
// both policies have a relevant directive (may be default-src)
|
||||
var isect1 = thisHasDir ?
|
||||
this._directives[dirv] :
|
||||
this._directives[DIRS.DEFAULT_SRC];
|
||||
var isect2 = thatHasDir ?
|
||||
aCSPRep._directives[dirv] :
|
||||
aCSPRep._directives[DIRS.DEFAULT_SRC];
|
||||
newRep._directives[dirv] = isect1.intersectWith(isect2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// pre-1.0 CSP requires a default-src, so we can assume it's here
|
||||
// (since the parser created one).
|
||||
var isect1 = thisHasDir ?
|
||||
this._directives[dirv] :
|
||||
this._directives[DIRS.DEFAULT_SRC];
|
||||
var isect2 = thatHasDir ?
|
||||
aCSPRep._directives[dirv] :
|
||||
aCSPRep._directives[DIRS.DEFAULT_SRC];
|
||||
newRep._directives[dirv] = isect1.intersectWith(isect2);
|
||||
}
|
||||
}
|
||||
|
||||
// REPORT_URI
|
||||
var reportURIDir = CSPRep.URI_DIRECTIVES.REPORT_URI;
|
||||
if (this._directives[reportURIDir] && aCSPRep._directives[reportURIDir]) {
|
||||
newRep._directives[reportURIDir] =
|
||||
this._directives[reportURIDir].concat(aCSPRep._directives[reportURIDir]);
|
||||
}
|
||||
else if (this._directives[reportURIDir]) {
|
||||
// blank concat makes a copy of the string.
|
||||
newRep._directives[reportURIDir] = this._directives[reportURIDir].concat();
|
||||
}
|
||||
else if (aCSPRep._directives[reportURIDir]) {
|
||||
// blank concat makes a copy of the string.
|
||||
newRep._directives[reportURIDir] = aCSPRep._directives[reportURIDir].concat();
|
||||
}
|
||||
|
||||
newRep._allowEval = this.allowsEvalInScripts
|
||||
&& aCSPRep.allowsEvalInScripts;
|
||||
|
||||
newRep._allowInlineScripts = this.allowsInlineScripts
|
||||
&& aCSPRep.allowsInlineScripts;
|
||||
|
||||
newRep._allowInlineStyles = this.allowsInlineStyles
|
||||
&& aCSPRep.allowsInlineStyles;
|
||||
|
||||
newRep._innerWindowID = this._innerWindowID ?
|
||||
this._innerWindowID : aCSPRep._innerWindowID;
|
||||
|
||||
return newRep;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if "eval" is enabled through the "eval" keyword.
|
||||
*/
|
||||
@ -1192,62 +1085,6 @@ CSPSourceList.prototype = {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Intersects with another CSPSourceList, deciding the subset directive
|
||||
* that should be enforced, and returning a new instance.
|
||||
* @param that
|
||||
* the other CSPSourceList to intersect "this" with
|
||||
* @returns
|
||||
* a new instance of a CSPSourceList representing the intersection
|
||||
*/
|
||||
intersectWith:
|
||||
function cspsd_intersectWith(that) {
|
||||
|
||||
var newCSPSrcList = null;
|
||||
|
||||
if (!that) return this.clone();
|
||||
|
||||
if (this.isNone() || that.isNone())
|
||||
newCSPSrcList = CSPSourceList.fromString("'none'", this._CSPRep);
|
||||
|
||||
if (this.isAll()) newCSPSrcList = that.clone();
|
||||
if (that.isAll()) newCSPSrcList = this.clone();
|
||||
|
||||
if (!newCSPSrcList) {
|
||||
// the shortcuts didn't apply, must do intersection the hard way.
|
||||
// -- find only common sources
|
||||
|
||||
// XXX (sid): we should figure out a better algorithm for this.
|
||||
// This is horribly inefficient.
|
||||
var isrcs = [];
|
||||
for (var i in this._sources) {
|
||||
for (var j in that._sources) {
|
||||
var s = that._sources[j].intersectWith(this._sources[i]);
|
||||
if (s) {
|
||||
isrcs.push(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Next, remove duplicates
|
||||
dup: for (var i = 0; i < isrcs.length; i++) {
|
||||
for (var j = 0; j < i; j++) {
|
||||
if (isrcs[i].equals(isrcs[j])) {
|
||||
isrcs.splice(i, 1);
|
||||
i--;
|
||||
continue dup;
|
||||
}
|
||||
}
|
||||
}
|
||||
newCSPSrcList = new CSPSourceList();
|
||||
newCSPSrcList._sources = isrcs;
|
||||
}
|
||||
|
||||
if ((!newCSPSrcList._CSPRep) && that._CSPRep) {
|
||||
newCSPSrcList._CSPRep = that._CSPRep;
|
||||
}
|
||||
return newCSPSrcList;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1661,93 +1498,6 @@ CSPSource.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the intersection of two sources.
|
||||
* Returns a null object if intersection generates no
|
||||
* hosts that satisfy it.
|
||||
* @param that
|
||||
* the other CSPSource to intersect "this" with
|
||||
* @returns
|
||||
* a new instance of a CSPSource representing the intersection
|
||||
*/
|
||||
intersectWith:
|
||||
function(that) {
|
||||
var newSource = new CSPSource();
|
||||
|
||||
// 'self' is not part of the intersection. Intersect the raw values from
|
||||
// the source, self must be set by someone creating this source.
|
||||
// When intersecting, we take the more specific of the two: if one scheme,
|
||||
// host or port is undefined, the other is taken. (This is contrary to
|
||||
// when "permits" is called -- there, the value of 'self' is looked at
|
||||
// when a scheme, host or port is undefined.)
|
||||
|
||||
// port
|
||||
if (!this.port)
|
||||
newSource._port = that.port;
|
||||
else if (!that.port)
|
||||
newSource._port = this.port;
|
||||
else if (this.port === "*")
|
||||
newSource._port = that.port;
|
||||
else if (that.port === "*")
|
||||
newSource._port = this.port;
|
||||
else if (that.port === this.port)
|
||||
newSource._port = this.port;
|
||||
else {
|
||||
let msg = CSPLocalizer.getFormatStr("notIntersectPort",
|
||||
[this.toString(), that.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
// scheme
|
||||
if (!this.scheme)
|
||||
newSource._scheme = that.scheme;
|
||||
else if (!that.scheme)
|
||||
newSource._scheme = this.scheme;
|
||||
if (this.scheme === "*")
|
||||
newSource._scheme = that.scheme;
|
||||
else if (that.scheme === "*")
|
||||
newSource._scheme = this.scheme;
|
||||
else if (that.scheme === this.scheme)
|
||||
newSource._scheme = this.scheme;
|
||||
else {
|
||||
var msg = CSPLocalizer.getFormatStr("notIntersectScheme",
|
||||
[this.toString(), that.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
// NOTE: Both sources must have a host, if they don't, something funny is
|
||||
// going on. The fromString() factory method should have set the host to
|
||||
// * if there's no host specified in the input. Regardless, if a host is
|
||||
// not present either the scheme is hostless or any host should be allowed.
|
||||
// This means we can use the other source's host as the more restrictive
|
||||
// host expression, or if neither are present, we can use "*", but the
|
||||
// error should still be reported.
|
||||
|
||||
// host
|
||||
if (this.host && that.host) {
|
||||
newSource._host = this.host.intersectWith(that.host);
|
||||
} else if (this.host) {
|
||||
let msg = CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost",
|
||||
[that.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
newSource._host = this.host.clone();
|
||||
} else if (that.host) {
|
||||
let msg = CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost",
|
||||
[this.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
newSource._host = that.host.clone();
|
||||
} else {
|
||||
let msg = CSPLocalizer.getFormatStr("intersectingSourcesWithUndefinedHosts",
|
||||
[this.toString(), that.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
newSource._host = CSPHost.fromString("*");
|
||||
}
|
||||
|
||||
return newSource;
|
||||
},
|
||||
|
||||
/**
|
||||
* Compares one CSPSource to another.
|
||||
*
|
||||
@ -1896,36 +1646,6 @@ CSPHost.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the intersection of two Hosts.
|
||||
* Basically, they must be the same, or one must have a wildcard.
|
||||
* @param that
|
||||
* the other CSPHost to intersect "this" with
|
||||
* @returns
|
||||
* a new instance of a CSPHost representing the intersection
|
||||
* (or null, if they can't be intersected)
|
||||
*/
|
||||
intersectWith:
|
||||
function(that) {
|
||||
if (!(this.permits(that) || that.permits(this))) {
|
||||
// host definitions cannot co-exist without a more general host
|
||||
// ... one must be a subset of the other, or intersection makes no sense.
|
||||
return null;
|
||||
}
|
||||
|
||||
// pick the more specific one, if both are same length.
|
||||
if (this._segments.length == that._segments.length) {
|
||||
// *.a vs b.a : b.a
|
||||
return (this._segments[0] === "*") ? that.clone() : this.clone();
|
||||
}
|
||||
|
||||
// different lengths...
|
||||
// *.b.a vs *.a : *.b.a
|
||||
// *.b.a vs d.c.b.a : d.c.b.a
|
||||
return (this._segments.length > that._segments.length) ?
|
||||
this.clone() : that.clone();
|
||||
},
|
||||
|
||||
/**
|
||||
* Compares one CSPHost to another.
|
||||
*
|
||||
|
@ -28,6 +28,10 @@ const CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT = "csp_type_websocket_spec_compliant";
|
||||
const WARN_FLAG = Ci.nsIScriptError.warningFlag;
|
||||
const ERROR_FLAG = Ci.nsIScriptError.ERROR_FLAG;
|
||||
|
||||
const INLINE_STYLE_VIOLATION_OBSERVER_SUBJECT = 'violated base restriction: Inline Stylesheets will not apply';
|
||||
const INLINE_SCRIPT_VIOLATION_OBSERVER_SUBJECT = 'violated base restriction: Inline Scripts will not execute';
|
||||
const EVAL_VIOLATION_OBSERVER_SUBJECT = 'violated base restriction: Code will not be created from strings';
|
||||
|
||||
// The cutoff length of content location in creating CSP cache key.
|
||||
const CSP_CACHE_URI_CUTOFF_SIZE = 512;
|
||||
|
||||
@ -40,21 +44,15 @@ Cu.import("resource://gre/modules/CSPUtils.jsm");
|
||||
function ContentSecurityPolicy() {
|
||||
CSPdebug("CSP CREATED");
|
||||
this._isInitialized = false;
|
||||
this._reportOnlyMode = false;
|
||||
|
||||
this._policy = CSPRep.fromString("default-src *");
|
||||
|
||||
// default options "wide open" since this policy will be intersected soon
|
||||
this._policy._allowInlineScripts = true;
|
||||
this._policy._allowInlineStyles = true;
|
||||
this._policy._allowEval = true;
|
||||
this._policies = [];
|
||||
|
||||
this._request = "";
|
||||
this._requestOrigin = "";
|
||||
this._requestPrincipal = "";
|
||||
this._referrer = "";
|
||||
this._docRequest = null;
|
||||
CSPdebug("CSP POLICY INITED TO 'default-src *'");
|
||||
CSPdebug("CSP object initialized, no policies to enforce yet");
|
||||
|
||||
this._cache = { };
|
||||
}
|
||||
@ -131,37 +129,68 @@ ContentSecurityPolicy.prototype = {
|
||||
this._isInitialized = foo;
|
||||
},
|
||||
|
||||
get policy () {
|
||||
return this._policy.toString();
|
||||
_getPolicyInternal: function(index) {
|
||||
if (index < 0 || index >= this._policies.length) {
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
return this._policies[index];
|
||||
},
|
||||
|
||||
getAllowsInlineScript: function(shouldReportViolation) {
|
||||
// report it?
|
||||
shouldReportViolation.value = !this._policy.allowsInlineScripts;
|
||||
|
||||
// allow it to execute?
|
||||
return this._reportOnlyMode || this._policy.allowsInlineScripts;
|
||||
},
|
||||
|
||||
getAllowsEval: function(shouldReportViolation) {
|
||||
// report it?
|
||||
shouldReportViolation.value = !this._policy.allowsEvalInScripts;
|
||||
|
||||
// allow it to execute?
|
||||
return this._reportOnlyMode || this._policy.allowsEvalInScripts;
|
||||
},
|
||||
|
||||
getAllowsInlineStyle: function(shouldReportViolation) {
|
||||
// report it?
|
||||
shouldReportViolation.value = !this._policy.allowsInlineStyles;
|
||||
|
||||
// allow it to execute?
|
||||
return this._reportOnlyMode || this._policy.allowsInlineStyles;
|
||||
_buildViolatedDirectiveString:
|
||||
function(aDirectiveName, aPolicy) {
|
||||
var SD = CSPRep.SRC_DIRECTIVES_NEW;
|
||||
var cspContext = (SD[aDirectiveName] in aPolicy._directives) ? SD[aDirectiveName] : SD.DEFAULT_SRC;
|
||||
var directive = aPolicy._directives[cspContext];
|
||||
return cspContext + ' ' + directive.toString();
|
||||
},
|
||||
|
||||
/**
|
||||
* Log policy violation on the Error Console and send a report if a report-uri
|
||||
* is present in the policy
|
||||
* Returns policy string representing the policy at "index".
|
||||
*/
|
||||
getPolicy: function(index) {
|
||||
return this._getPolicyInternal(index).toString();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns count of policies.
|
||||
*/
|
||||
get numPolicies() {
|
||||
return this._policies.length;
|
||||
},
|
||||
|
||||
getAllowsInlineScript: function(shouldReportViolations) {
|
||||
// report it? (for each policy, is it violated?)
|
||||
shouldReportViolations.value = this._policies.some(function(a) { return !a.allowsInlineScripts; });
|
||||
|
||||
// allow it to execute? (Do all the policies allow it to execute)?
|
||||
return this._policies.every(function(a) {
|
||||
return a._reportOnlyMode || a.allowsInlineScripts;
|
||||
});
|
||||
},
|
||||
|
||||
getAllowsEval: function(shouldReportViolations) {
|
||||
// report it? (for each policy, is it violated?)
|
||||
shouldReportViolations.value = this._policies.some(function(a) { return !a.allowsEvalInScripts; });
|
||||
|
||||
// allow it to execute? (Do all the policies allow it to execute)?
|
||||
return this._policies.every(function(a) {
|
||||
return a._reportOnlyMode || a.allowsEvalInScripts;
|
||||
});
|
||||
},
|
||||
|
||||
getAllowsInlineStyle: function(shouldReportViolations) {
|
||||
// report it? (for each policy, is it violated?)
|
||||
shouldReportViolations.value = this._policies.some(function(a) { return !a.allowsInlineStyles; });
|
||||
|
||||
// allow it to execute? (Do all the policies allow it to execute)?
|
||||
return this._policies.every(function(a) {
|
||||
return a._reportOnlyMode || a.allowsInlineStyles;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* For each policy, log any violation on the Error Console and send a report
|
||||
* if a report-uri is present in the policy
|
||||
*
|
||||
* @param aViolationType
|
||||
* one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
|
||||
@ -173,48 +202,44 @@ ContentSecurityPolicy.prototype = {
|
||||
* source line number of the violation (if available)
|
||||
*/
|
||||
logViolationDetails:
|
||||
function(aViolationType, aSourceFile, aScriptSample, aLineNum) {
|
||||
// allowsInlineScript and allowsEval both return true when report-only mode
|
||||
// is enabled, resulting in a call to this function. Therefore we need to
|
||||
// check that the policy was in fact violated before logging any violations
|
||||
switch (aViolationType) {
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_STYLE:
|
||||
if (!this._policy.allowsInlineStyles)
|
||||
this._asyncReportViolation('self', null, CSPLocalizer.getStr("inlineStyleBlocked"),
|
||||
'violated base restriction: Inline Stylesheets will not apply',
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
break;
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT:
|
||||
if (!this._policy.allowsInlineScripts)
|
||||
this._asyncReportViolation('self', null, CSPLocalizer.getStr("inlineScriptBlocked"),
|
||||
'violated base restriction: Inline Scripts will not execute',
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
break;
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL:
|
||||
if (!this._policy.allowsEvalInScripts)
|
||||
this._asyncReportViolation('self', null, CSPLocalizer.getStr("scriptFromStringBlocked"),
|
||||
'violated base restriction: Code will not be created from strings',
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
break;
|
||||
function(aViolationType, aSourceFile, aScriptSample, aLineNum, violatedPolicyIndex) {
|
||||
for (let policyIndex=0; policyIndex < this._policies.length; policyIndex++) {
|
||||
let policy = this._policies[policyIndex];
|
||||
|
||||
// call-sites to the eval/inline checks recieve two return values: allows
|
||||
// and violates. Policies that are report-only allow the
|
||||
// loads/compilations but violations should still be reported. Not all
|
||||
// policies in this nsIContentSecurityPolicy instance will be violated,
|
||||
// which is why we must check again here.
|
||||
switch (aViolationType) {
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_STYLE:
|
||||
if (!policy.allowsInlineStyles) {
|
||||
var violatedDirective = this._buildViolatedDirectiveString('STYLE_SRC', policy);
|
||||
this._asyncReportViolation('self', null, violatedDirective, policyIndex,
|
||||
INLINE_STYLE_VIOLATION_OBSERVER_SUBJECT,
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
}
|
||||
break;
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT:
|
||||
if (!policy.allowsInlineScripts) {
|
||||
var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC', policy);
|
||||
this._asyncReportViolation('self', null, violatedDirective, policyIndex,
|
||||
INLINE_SCRIPT_VIOLATION_OBSERVER_SUBJECT,
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
}
|
||||
break;
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL:
|
||||
if (!policy.allowsEvalInScripts) {
|
||||
var violatedDirective = this._buildViolatedDirectiveString('SCRIPT_SRC', policy);
|
||||
this._asyncReportViolation('self', null, violatedDirective, policyIndex,
|
||||
EVAL_VIOLATION_OBSERVER_SUBJECT,
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
set reportOnlyMode(val) {
|
||||
this._reportOnlyMode = val;
|
||||
},
|
||||
|
||||
get reportOnlyMode () {
|
||||
return this._reportOnlyMode;
|
||||
},
|
||||
|
||||
/*
|
||||
// Having a setter is a bad idea... opens up the policy to "loosening"
|
||||
// Instead, use "refinePolicy."
|
||||
set policy (aStr) {
|
||||
this._policy = CSPRep.fromString(aStr);
|
||||
},
|
||||
*/
|
||||
|
||||
/**
|
||||
* Given an nsIHttpChannel, fill out the appropriate data.
|
||||
*/
|
||||
@ -250,26 +275,29 @@ ContentSecurityPolicy.prototype = {
|
||||
/* ........ Methods .............. */
|
||||
|
||||
/**
|
||||
* Given a new policy, intersects the currently enforced policy with the new
|
||||
* one and stores the result. The effect is a "tightening" or refinement of
|
||||
* an old policy. This is called any time a new policy is encountered and
|
||||
* the effective policy has to be refined.
|
||||
* Adds a new policy to our list of policies for this CSP context.
|
||||
* @returns the count of policies.
|
||||
*/
|
||||
refinePolicy:
|
||||
function csp_refinePolicy(aPolicy, selfURI, aSpecCompliant) {
|
||||
CSPdebug("REFINE POLICY: " + aPolicy);
|
||||
CSPdebug(" SELF: " + selfURI.asciiSpec);
|
||||
appendPolicy:
|
||||
function csp_appendPolicy(aPolicy, selfURI, aReportOnly, aSpecCompliant) {
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug("APPENDING POLICY: " + aPolicy);
|
||||
CSPdebug(" SELF: " + selfURI.asciiSpec);
|
||||
CSPdebug("CSP 1.0 COMPLIANT : " + aSpecCompliant);
|
||||
#endif
|
||||
|
||||
// For nested schemes such as view-source: make sure we are taking the
|
||||
// innermost URI to use as 'self' since that's where we will extract the
|
||||
// scheme, host and port from
|
||||
if (selfURI instanceof Ci.nsINestedURI) {
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug(" INNER: " + selfURI.innermostURI.asciiSpec);
|
||||
#endif
|
||||
selfURI = selfURI.innermostURI;
|
||||
}
|
||||
|
||||
// stay uninitialized until policy merging is done
|
||||
this._isInitialized = false;
|
||||
// stay uninitialized until policy setup is done
|
||||
var newpolicy;
|
||||
|
||||
// If there is a policy-uri, fetch the policy, then re-call this function.
|
||||
// (1) parse and create a CSPRep object
|
||||
@ -279,29 +307,37 @@ ContentSecurityPolicy.prototype = {
|
||||
// If we want to be CSP 1.0 spec compliant, use the new parser.
|
||||
// The old one will be deprecated in the future and will be
|
||||
// removed at that time.
|
||||
var newpolicy;
|
||||
if (aSpecCompliant) {
|
||||
newpolicy = CSPRep.fromStringSpecCompliant(aPolicy,
|
||||
selfURI,
|
||||
this._docRequest,
|
||||
this);
|
||||
this,
|
||||
aReportOnly);
|
||||
} else {
|
||||
newpolicy = CSPRep.fromString(aPolicy,
|
||||
selfURI,
|
||||
this._docRequest,
|
||||
this);
|
||||
this,
|
||||
aReportOnly);
|
||||
}
|
||||
|
||||
// (2) Intersect the currently installed CSPRep object with the new one
|
||||
var intersect = this._policy.intersectWith(newpolicy);
|
||||
newpolicy._specCompliant = !!aSpecCompliant;
|
||||
newpolicy._isInitialized = true;
|
||||
this._policies.push(newpolicy);
|
||||
this._cache = {}; // reset cache since effective policy changes
|
||||
},
|
||||
|
||||
// (3) Save the result
|
||||
this._policy = intersect;
|
||||
|
||||
this._policy._specCompliant = !!aSpecCompliant;
|
||||
|
||||
this._isInitialized = true;
|
||||
this._cache = {};
|
||||
/**
|
||||
* Removes a policy from the array of policies.
|
||||
*/
|
||||
removePolicy:
|
||||
function csp_removePolicy(index) {
|
||||
if (index < 0 || index >= this._policies.length) {
|
||||
CSPdebug("Cannot remove policy " + index + "; not enough policies.");
|
||||
return;
|
||||
}
|
||||
this._policies.splice(index, 1);
|
||||
this._cache = {}; // reset cache since effective policy changes
|
||||
},
|
||||
|
||||
/**
|
||||
@ -309,17 +345,29 @@ ContentSecurityPolicy.prototype = {
|
||||
*/
|
||||
sendReports:
|
||||
function(blockedUri, originalUri, violatedDirective,
|
||||
aSourceFile, aScriptSample, aLineNum) {
|
||||
var uriString = this._policy.getReportURIs();
|
||||
violatedPolicyIndex, aSourceFile,
|
||||
aScriptSample, aLineNum) {
|
||||
|
||||
let policy = this._getPolicyInternal(violatedPolicyIndex);
|
||||
if (!policy) {
|
||||
CSPdebug("ERROR in SendReports: policy " + violatedPolicyIndex + " is not defined.");
|
||||
return;
|
||||
}
|
||||
|
||||
var uriString = policy.getReportURIs();
|
||||
var uris = uriString.split(/\s+/);
|
||||
if (uris.length > 0) {
|
||||
// see if we need to sanitize the blocked-uri
|
||||
let blocked = '';
|
||||
if (originalUri) {
|
||||
// We've redirected, only report the blocked origin
|
||||
let clone = blockedUri.clone();
|
||||
clone.path = '';
|
||||
blocked = clone.asciiSpec;
|
||||
try {
|
||||
let clone = blockedUri.clone();
|
||||
clone.path = '';
|
||||
blocked = clone.asciiSpec;
|
||||
} catch(e) {
|
||||
CSPdebug(".... blockedUri can't be cloned: " + blockedUri);
|
||||
}
|
||||
}
|
||||
else if (blockedUri instanceof Ci.nsIURI) {
|
||||
blocked = blockedUri.cloneIgnoringRef().asciiSpec;
|
||||
@ -360,17 +408,6 @@ ContentSecurityPolicy.prototype = {
|
||||
var reportString = JSON.stringify(report);
|
||||
CSPdebug("Constructed violation report:\n" + reportString);
|
||||
|
||||
var violationMessage = null;
|
||||
if (blockedUri["asciiSpec"]) {
|
||||
violationMessage = CSPLocalizer.getFormatStr("CSPViolationWithURI", [violatedDirective, blockedUri.asciiSpec]);
|
||||
} else {
|
||||
violationMessage = CSPLocalizer.getFormatStr("CSPViolation", [violatedDirective]);
|
||||
}
|
||||
this._policy.log(WARN_FLAG, violationMessage,
|
||||
(aSourceFile) ? aSourceFile : null,
|
||||
(aScriptSample) ? decodeURIComponent(aScriptSample) : null,
|
||||
(aLineNum) ? aLineNum : null);
|
||||
|
||||
// For each URI in the report list, send out a report.
|
||||
// We make the assumption that all of the URIs are absolute URIs; this
|
||||
// should be taken care of in CSPRep.fromString (where it converts any
|
||||
@ -396,7 +433,7 @@ ContentSecurityPolicy.prototype = {
|
||||
|
||||
// we need to set an nsIChannelEventSink on the channel object
|
||||
// so we can tell it to not follow redirects when posting the reports
|
||||
chan.notificationCallbacks = new CSPReportRedirectSink(this._policy);
|
||||
chan.notificationCallbacks = new CSPReportRedirectSink(policy);
|
||||
if (this._docRequest) {
|
||||
chan.loadGroup = this._docRequest.loadGroup;
|
||||
}
|
||||
@ -431,25 +468,68 @@ ContentSecurityPolicy.prototype = {
|
||||
} catch(e) {
|
||||
// it's possible that the URI was invalid, just log a
|
||||
// warning and skip over that.
|
||||
this._policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]));
|
||||
this._policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("errorWas", [e.toString()]));
|
||||
policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]));
|
||||
policy.log(WARN_FLAG, CSPLocalizer.getFormatStr("errorWas", [e.toString()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Logs a meaningful CSP warning to the developer console.
|
||||
*/
|
||||
logToConsole:
|
||||
function(blockedUri, originalUri, violatedDirective, aViolatedPolicyIndex,
|
||||
aSourceFile, aScriptSample, aLineNum, aObserverSubject) {
|
||||
let policy = this._policies[aViolatedPolicyIndex];
|
||||
switch(aObserverSubject.data) {
|
||||
case INLINE_STYLE_VIOLATION_OBSERVER_SUBJECT:
|
||||
violatedDirective = CSPLocalizer.getStr("inlineStyleBlocked");
|
||||
break;
|
||||
case INLINE_SCRIPT_VIOLATION_OBSERVER_SUBJECT:
|
||||
violatedDirective = CSPLocalizer.getStr("inlineScriptBlocked");
|
||||
break;
|
||||
case EVAL_VIOLATION_OBSERVER_SUBJECT:
|
||||
violatedDirective = CSPLocalizer.getStr("scriptFromStringBlocked");
|
||||
break;
|
||||
}
|
||||
var violationMessage = null;
|
||||
if (blockedUri["asciiSpec"]) {
|
||||
violationMessage = CSPLocalizer.getFormatStr("CSPViolationWithURI", [violatedDirective, blockedUri.asciiSpec]);
|
||||
} else {
|
||||
violationMessage = CSPLocalizer.getFormatStr("CSPViolation", [violatedDirective]);
|
||||
}
|
||||
policy.log(WARN_FLAG, violationMessage,
|
||||
(aSourceFile) ? aSourceFile : null,
|
||||
(aScriptSample) ? decodeURIComponent(aScriptSample) : null,
|
||||
(aLineNum) ? aLineNum : null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Exposed Method to analyze docShell for approved frame ancestry.
|
||||
* Also sends violation reports if necessary.
|
||||
* NOTE: Also sends violation reports if necessary.
|
||||
* @param docShell
|
||||
* the docShell for this policy's resource.
|
||||
* @return
|
||||
* true if the frame ancestry is allowed by this policy.
|
||||
* true if the frame ancestry is allowed by this policy and the load
|
||||
* should progress.
|
||||
*/
|
||||
permitsAncestry:
|
||||
function(docShell) {
|
||||
// Cannot shortcut checking all the policies since violation reports have
|
||||
// to be triggered if any policy wants it.
|
||||
var permitted = true;
|
||||
for (let i = 0; i < this._policies.length; i++) {
|
||||
if (!this._permitsAncestryInternal(docShell, this._policies[i], i)) {
|
||||
permitted = false;
|
||||
}
|
||||
}
|
||||
return permitted;
|
||||
},
|
||||
|
||||
_permitsAncestryInternal:
|
||||
function(docShell, policy, policyIndex) {
|
||||
if (!docShell) { return false; }
|
||||
CSPdebug(" in permitsAncestry(), docShell = " + docShell);
|
||||
|
||||
// walk up this docShell tree until we hit chrome
|
||||
var dst = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
@ -471,7 +551,9 @@ ContentSecurityPolicy.prototype = {
|
||||
ancestor.userPass = '';
|
||||
} catch (ex) {}
|
||||
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug(" found frame ancestor " + ancestor.asciiSpec);
|
||||
#endif
|
||||
ancestors.push(ancestor);
|
||||
}
|
||||
}
|
||||
@ -482,17 +564,16 @@ ContentSecurityPolicy.prototype = {
|
||||
let cspContext = CSPRep.SRC_DIRECTIVES_NEW.FRAME_ANCESTORS;
|
||||
for (let i in ancestors) {
|
||||
let ancestor = ancestors[i];
|
||||
if (!this._policy.permits(ancestor, cspContext)) {
|
||||
if (!policy.permits(ancestor, cspContext)) {
|
||||
// report the frame-ancestor violation
|
||||
let directive = this._policy._directives[cspContext];
|
||||
let violatedPolicy = (directive._isImplicit
|
||||
? 'default-src' : 'frame-ancestors ')
|
||||
+ directive.toString();
|
||||
let directive = policy._directives[cspContext];
|
||||
let violatedPolicy = 'frame-ancestors ' + directive.toString();
|
||||
|
||||
this._asyncReportViolation(ancestors[i], null, violatedPolicy);
|
||||
this._asyncReportViolation(ancestors[i], null, violatedPolicy,
|
||||
policyIndex);
|
||||
|
||||
// need to lie if we are testing in report-only mode
|
||||
return this._reportOnlyMode;
|
||||
return policy._reportOnlyMode;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -548,63 +629,82 @@ ContentSecurityPolicy.prototype = {
|
||||
|
||||
let cp = Ci.nsIContentPolicy;
|
||||
|
||||
// iterate through all the _policies and send reports where a policy is
|
||||
// violated. After the check, determine the overall effect (blocked or
|
||||
// loaded?) and cache it.
|
||||
let policyAllowsLoadArray = [];
|
||||
for (let policyIndex=0; policyIndex < this._policies.length; policyIndex++) {
|
||||
let policy = this._policies[policyIndex];
|
||||
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug("policy is " + (this._policy._specCompliant ?
|
||||
"1.0 compliant" : "pre-1.0"));
|
||||
CSPdebug("policy is " + (policy._specCompliant ?
|
||||
"1.0 compliant" : "pre-1.0"));
|
||||
CSPdebug("policy is " + (policy._reportOnlyMode ?
|
||||
"report-only" : "blocking"));
|
||||
#endif
|
||||
|
||||
if (aContentType == cp.TYPE_XMLHTTPREQUEST && this._policy._specCompliant) {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPLIANT];
|
||||
} else if (aContentType == cp.TYPE_WEBSOCKET && this._policy._specCompliant) {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT];
|
||||
} else {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[aContentType];
|
||||
}
|
||||
|
||||
CSPdebug("shouldLoad cspContext = " + cspContext);
|
||||
|
||||
// if the mapping is null, there's no policy, let it through.
|
||||
if (!cspContext) {
|
||||
return Ci.nsIContentPolicy.ACCEPT;
|
||||
}
|
||||
|
||||
// otherwise, honor the translation
|
||||
// var source = aContentLocation.scheme + "://" + aContentLocation.hostPort;
|
||||
var res = this._policy.permits(aContentLocation, cspContext)
|
||||
? Ci.nsIContentPolicy.ACCEPT
|
||||
: Ci.nsIContentPolicy.REJECT_SERVER;
|
||||
|
||||
// frame-ancestors is taken care of early on (as this document is loaded)
|
||||
|
||||
// If the result is *NOT* ACCEPT, then send report
|
||||
if (res != Ci.nsIContentPolicy.ACCEPT) {
|
||||
CSPdebug("blocking request for " + aContentLocation.asciiSpec);
|
||||
try {
|
||||
let directive = "unknown directive",
|
||||
violatedPolicy = "unknown policy";
|
||||
|
||||
// The policy might not explicitly declare each source directive (so
|
||||
// the cspContext may be implicit). If so, we have to report
|
||||
// violations as appropriate: specific or the default-src directive.
|
||||
if (this._policy._directives.hasOwnProperty(cspContext)) {
|
||||
directive = this._policy._directives[cspContext];
|
||||
violatedPolicy = cspContext + ' ' + directive.toString();
|
||||
} else if (this._policy._directives.hasOwnProperty("default-src")) {
|
||||
directive = this._policy._directives["default-src"];
|
||||
violatedPolicy = "default-src " + directive.toString();
|
||||
} else {
|
||||
violatedPolicy = "unknown directive";
|
||||
CSPdebug('ERROR in blocking content: ' +
|
||||
'CSP is not sure which part of the policy caused this block');
|
||||
}
|
||||
|
||||
this._asyncReportViolation(aContentLocation, aOriginalUri, violatedPolicy);
|
||||
} catch(e) {
|
||||
CSPdebug('---------------- ERROR: ' + e);
|
||||
if (aContentType == cp.TYPE_XMLHTTPREQUEST && this._policies[policyIndex]._specCompliant) {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_XMLHTTPREQUEST_SPEC_COMPLIANT];
|
||||
} else if (aContentType == cp.TYPE_WEBSOCKET && this._policies[policyIndex]._specCompliant) {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[CSP_TYPE_WEBSOCKET_SPEC_COMPLIANT];
|
||||
} else {
|
||||
cspContext = ContentSecurityPolicy._MAPPINGS[aContentType];
|
||||
}
|
||||
}
|
||||
|
||||
let ret = (this._reportOnlyMode ? Ci.nsIContentPolicy.ACCEPT : res);
|
||||
#ifndef MOZ_B2G
|
||||
CSPdebug("shouldLoad cspContext = " + cspContext);
|
||||
#endif
|
||||
|
||||
// if the mapping is null, there's no policy, let it through.
|
||||
if (!cspContext) {
|
||||
return Ci.nsIContentPolicy.ACCEPT;
|
||||
}
|
||||
|
||||
// otherwise, honor the translation
|
||||
// var source = aContentLocation.scheme + "://" + aContentLocation.hostPort;
|
||||
var res = policy.permits(aContentLocation, cspContext)
|
||||
? cp.ACCEPT : cp.REJECT_SERVER;
|
||||
// record whether the thing should be blocked or just reported.
|
||||
policyAllowsLoadArray.push(res == cp.ACCEPT || policy._reportOnlyMode);
|
||||
|
||||
// frame-ancestors is taken care of early on (as this document is loaded)
|
||||
|
||||
// If the result is *NOT* ACCEPT, then send report
|
||||
if (res != Ci.nsIContentPolicy.ACCEPT) {
|
||||
CSPdebug("blocking request for " + aContentLocation.asciiSpec);
|
||||
try {
|
||||
let directive = "unknown directive",
|
||||
violatedPolicy = "unknown policy";
|
||||
|
||||
// The policy might not explicitly declare each source directive (so
|
||||
// the cspContext may be implicit). If so, we have to report
|
||||
// violations as appropriate: specific or the default-src directive.
|
||||
if (policy._directives.hasOwnProperty(cspContext)) {
|
||||
directive = policy._directives[cspContext];
|
||||
violatedPolicy = cspContext + ' ' + directive.toString();
|
||||
} else if (policy._directives.hasOwnProperty("default-src")) {
|
||||
directive = policy._directives["default-src"];
|
||||
violatedPolicy = "default-src " + directive.toString();
|
||||
} else {
|
||||
violatedPolicy = "unknown directive";
|
||||
CSPdebug('ERROR in blocking content: ' +
|
||||
'CSP is not sure which part of the policy caused this block');
|
||||
}
|
||||
|
||||
this._asyncReportViolation(aContentLocation, aOriginalUri,
|
||||
violatedPolicy, policyIndex);
|
||||
} catch(e) {
|
||||
CSPdebug('---------------- ERROR: ' + e);
|
||||
}
|
||||
}
|
||||
} // end for-each loop over policies
|
||||
|
||||
// the ultimate decision is based on whether any policies want to reject
|
||||
// the load. The array keeps track of whether the policies allowed the
|
||||
// loads. If any doesn't, we'll reject the load (and cache the result).
|
||||
let ret = (policyAllowsLoadArray.some(function(a,b) { return !a; }) ?
|
||||
cp.REJECT_SERVER : cp.ACCEPT);
|
||||
|
||||
if (key) {
|
||||
this._cache[key] = ret;
|
||||
}
|
||||
@ -629,14 +729,17 @@ ContentSecurityPolicy.prototype = {
|
||||
* topic that a violation occurred. Also triggers report sending. All
|
||||
* asynchronous on the main thread.
|
||||
*
|
||||
* @param blockedContentSource
|
||||
* @param aBlockedContentSource
|
||||
* Either a CSP Source (like 'self', as string) or nsIURI: the source
|
||||
* of the violation.
|
||||
* @param originalUri
|
||||
* @param aOriginalUri
|
||||
* The original URI if the blocked content is a redirect, else null
|
||||
* @param violatedDirective
|
||||
* @param aViolatedDirective
|
||||
* the directive that was violated (string).
|
||||
* @param observerSubject
|
||||
* @param aViolatedPolicyIndex
|
||||
* the index of the policy that was violated (so we know where to send
|
||||
* the reports).
|
||||
* @param aObserverSubject
|
||||
* optional, subject sent to the nsIObservers listening to the CSP
|
||||
* violation topic.
|
||||
* @param aSourceFile
|
||||
@ -647,32 +750,38 @@ ContentSecurityPolicy.prototype = {
|
||||
* source line number of the violation (if available)
|
||||
*/
|
||||
_asyncReportViolation:
|
||||
function(blockedContentSource, originalUri, violatedDirective, observerSubject,
|
||||
function(aBlockedContentSource, aOriginalUri, aViolatedDirective,
|
||||
aViolatedPolicyIndex, aObserverSubject,
|
||||
aSourceFile, aScriptSample, aLineNum) {
|
||||
// if optional observerSubject isn't specified, default to the source of
|
||||
// the violation.
|
||||
if (!observerSubject)
|
||||
observerSubject = blockedContentSource;
|
||||
if (!aObserverSubject)
|
||||
aObserverSubject = aBlockedContentSource;
|
||||
|
||||
// gotta wrap things that aren't nsISupports, since it's sent out to
|
||||
// observers as such. Objects that are not nsISupports are converted to
|
||||
// strings and then wrapped into a nsISupportsCString.
|
||||
if (!(observerSubject instanceof Ci.nsISupports)) {
|
||||
let d = observerSubject;
|
||||
observerSubject = Cc["@mozilla.org/supports-cstring;1"]
|
||||
if (!(aObserverSubject instanceof Ci.nsISupports)) {
|
||||
let d = aObserverSubject;
|
||||
aObserverSubject = Cc["@mozilla.org/supports-cstring;1"]
|
||||
.createInstance(Ci.nsISupportsCString);
|
||||
observerSubject.data = d;
|
||||
aObserverSubject.data = d;
|
||||
}
|
||||
|
||||
var reportSender = this;
|
||||
Services.tm.mainThread.dispatch(
|
||||
function() {
|
||||
Services.obs.notifyObservers(observerSubject,
|
||||
Services.obs.notifyObservers(aObserverSubject,
|
||||
CSP_VIOLATION_TOPIC,
|
||||
violatedDirective);
|
||||
reportSender.sendReports(blockedContentSource, originalUri,
|
||||
violatedDirective,
|
||||
aViolatedDirective);
|
||||
reportSender.sendReports(aBlockedContentSource, aOriginalUri,
|
||||
aViolatedDirective, aViolatedPolicyIndex,
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
reportSender.logToConsole(aBlockedContentSource, aOriginalUri,
|
||||
aViolatedDirective, aViolatedPolicyIndex,
|
||||
aSourceFile, aScriptSample,
|
||||
aLineNum, aObserverSubject);
|
||||
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
},
|
||||
};
|
||||
|
@ -60,88 +60,97 @@ CSPService::ShouldLoad(uint32_t aContentType,
|
||||
nsIPrincipal *aRequestPrincipal,
|
||||
int16_t *aDecision)
|
||||
{
|
||||
if (!aContentLocation)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!aContentLocation)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
nsAutoCString location;
|
||||
aContentLocation->GetSpec(location);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSPService::ShouldLoad called for %s", location.get()));
|
||||
}
|
||||
#endif
|
||||
// default decision, CSP can revise it if there's a policy to enforce
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
|
||||
// No need to continue processing if CSP is disabled
|
||||
if (!sCSPEnabled)
|
||||
return NS_OK;
|
||||
|
||||
// shortcut for about: chrome: and resource: and javascript: uris since
|
||||
// they're not subject to CSP content policy checks.
|
||||
bool schemeMatch = false;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("about", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("chrome", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("resource", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("javascript", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
|
||||
|
||||
// These content types are not subject to CSP content policy checks:
|
||||
// TYPE_CSP_REPORT, TYPE_REFRESH, TYPE_DOCUMENT
|
||||
// (their mappings are null in contentSecurityPolicy.js)
|
||||
if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
|
||||
aContentType == nsIContentPolicy::TYPE_REFRESH ||
|
||||
aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// find the principal of the document that initiated this request and see
|
||||
// if it has a CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (node) {
|
||||
principal = node->NodePrincipal();
|
||||
principal->GetCsp(getter_AddRefs(csp));
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoString policy;
|
||||
csp->GetPolicy(policy);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Document has CSP: %s",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
#endif
|
||||
// obtain the enforcement decision
|
||||
// (don't pass aExtra, we use that slot for redirects)
|
||||
csp->ShouldLoad(aContentType,
|
||||
aContentLocation,
|
||||
aRequestOrigin,
|
||||
aRequestContext,
|
||||
aMimeTypeGuess,
|
||||
nullptr,
|
||||
aDecision);
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else {
|
||||
nsAutoCString uriSpec;
|
||||
aContentLocation->GetSpec(uriSpec);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
||||
}
|
||||
{
|
||||
nsAutoCString location;
|
||||
aContentLocation->GetSpec(location);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSPService::ShouldLoad called for %s", location.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
// default decision, CSP can revise it if there's a policy to enforce
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
|
||||
// No need to continue processing if CSP is disabled
|
||||
if (!sCSPEnabled)
|
||||
return NS_OK;
|
||||
|
||||
// shortcut for about: chrome: and resource: and javascript: uris since
|
||||
// they're not subject to CSP content policy checks.
|
||||
bool schemeMatch = false;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("about", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("chrome", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("resource", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("javascript", &schemeMatch), NS_OK);
|
||||
if (schemeMatch)
|
||||
return NS_OK;
|
||||
|
||||
|
||||
// These content types are not subject to CSP content policy checks:
|
||||
// TYPE_CSP_REPORT, TYPE_REFRESH, TYPE_DOCUMENT
|
||||
// (their mappings are null in contentSecurityPolicy.js)
|
||||
if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
|
||||
aContentType == nsIContentPolicy::TYPE_REFRESH ||
|
||||
aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// find the principal of the document that initiated this request and see
|
||||
// if it has a CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (node) {
|
||||
principal = node->NodePrincipal();
|
||||
principal->GetCsp(getter_AddRefs(csp));
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
int numPolicies = 0;
|
||||
nsresult rv = csp->GetPolicyCount(&numPolicies);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (int i=0; i<numPolicies; i++) {
|
||||
nsAutoString policy;
|
||||
csp->GetPolicy(i, policy);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Document has CSP[%d]: %s", i,
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// obtain the enforcement decision
|
||||
// (don't pass aExtra, we use that slot for redirects)
|
||||
csp->ShouldLoad(aContentType,
|
||||
aContentLocation,
|
||||
aRequestOrigin,
|
||||
aRequestContext,
|
||||
aMimeTypeGuess,
|
||||
nullptr,
|
||||
aDecision);
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else {
|
||||
nsAutoCString uriSpec;
|
||||
aContentLocation->GetSpec(uriSpec);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -154,52 +163,60 @@ CSPService::ShouldProcess(uint32_t aContentType,
|
||||
nsIPrincipal *aRequestPrincipal,
|
||||
int16_t *aDecision)
|
||||
{
|
||||
if (!aContentLocation)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!aContentLocation)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// default decision is to accept the item
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
// default decision is to accept the item
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
|
||||
// No need to continue processing if CSP is disabled
|
||||
if (!sCSPEnabled)
|
||||
return NS_OK;
|
||||
|
||||
// find the nsDocument that initiated this request and see if it has a
|
||||
// CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (node) {
|
||||
principal = node->NodePrincipal();
|
||||
principal->GetCsp(getter_AddRefs(csp));
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
nsAutoString policy;
|
||||
csp->GetPolicy(policy);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("shouldProcess - document has policy: %s",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
#endif
|
||||
// obtain the enforcement decision
|
||||
csp->ShouldProcess(aContentType,
|
||||
aContentLocation,
|
||||
aRequestOrigin,
|
||||
aRequestContext,
|
||||
aMimeTypeGuess,
|
||||
aExtra,
|
||||
aDecision);
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else {
|
||||
nsAutoCString uriSpec;
|
||||
aContentLocation->GetSpec(uriSpec);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
||||
}
|
||||
#endif
|
||||
// No need to continue processing if CSP is disabled
|
||||
if (!sCSPEnabled)
|
||||
return NS_OK;
|
||||
|
||||
// find the nsDocument that initiated this request and see if it has a
|
||||
// CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (node) {
|
||||
principal = node->NodePrincipal();
|
||||
principal->GetCsp(getter_AddRefs(csp));
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
int numPolicies = 0;
|
||||
nsresult rv = csp->GetPolicyCount(&numPolicies);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (int i=0; i<numPolicies; i++) {
|
||||
nsAutoString policy;
|
||||
csp->GetPolicy(i, policy);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("shouldProcess - document has policy[%d]: %s", i,
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// obtain the enforcement decision
|
||||
csp->ShouldProcess(aContentType,
|
||||
aContentLocation,
|
||||
aRequestOrigin,
|
||||
aRequestContext,
|
||||
aMimeTypeGuess,
|
||||
aExtra,
|
||||
aDecision);
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else {
|
||||
nsAutoCString uriSpec;
|
||||
aContentLocation->GetSpec(uriSpec);
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIChannelEventSink implementation */
|
||||
|
@ -2458,6 +2458,30 @@ nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
|
||||
}
|
||||
}
|
||||
|
||||
static nsresult
|
||||
AppendCSPFromHeader(nsIContentSecurityPolicy* csp, const nsAString& aHeaderValue,
|
||||
nsIURI* aSelfURI, bool aReportOnly, bool aSpecCompliant)
|
||||
{
|
||||
// Need to tokenize the header value since multiple headers could be
|
||||
// concatenated into one comma-separated list of policies.
|
||||
// See RFC2616 section 4.2 (last paragraph)
|
||||
nsresult rv = NS_OK;
|
||||
nsCharSeparatedTokenizer tokenizer(aHeaderValue, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& policy = tokenizer.nextToken();
|
||||
rv = csp->AppendPolicy(policy, aSelfURI, aReportOnly, aSpecCompliant);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP refined with policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
{
|
||||
@ -2502,15 +2526,23 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
bool specCompliantEnabled =
|
||||
Preferences::GetBool("security.csp.speccompliant");
|
||||
|
||||
// If spec compliant pref isn't set, pretend we never got these headers.
|
||||
if ((!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) &&
|
||||
!specCompliantEnabled) {
|
||||
// If spec compliant pref isn't set, pretend we never got
|
||||
// these headers.
|
||||
if (!specCompliantEnabled) {
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Got spec compliant CSP headers but pref was not set"));
|
||||
cspHeaderValue.Truncate();
|
||||
cspROHeaderValue.Truncate();
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Got spec compliant CSP headers but pref was not set"));
|
||||
cspHeaderValue.Truncate();
|
||||
cspROHeaderValue.Truncate();
|
||||
}
|
||||
|
||||
// If the old header is present, warn that it will be deprecated.
|
||||
if (!cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty()) {
|
||||
mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated");
|
||||
|
||||
// Also, if the new headers AND the old headers were present, warn
|
||||
// that the old headers will be ignored.
|
||||
if (!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) {
|
||||
mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2571,19 +2603,13 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
}
|
||||
|
||||
// used as a "self" identifier for the CSP.
|
||||
nsCOMPtr<nsIURI> chanURI;
|
||||
aChannel->GetURI(getter_AddRefs(chanURI));
|
||||
nsCOMPtr<nsIURI> selfURI;
|
||||
aChannel->GetURI(getter_AddRefs(selfURI));
|
||||
|
||||
// Store the request context for violation reports
|
||||
csp->ScanRequestData(httpChannel);
|
||||
|
||||
// The CSP is refined in the following order:
|
||||
// 1. Default app CSP, if applicable
|
||||
// 2. App manifest CSP, if provided
|
||||
// 3. HTTP header CSP, if provided
|
||||
// Note that since each application of refinePolicy is a set intersection,
|
||||
// the order in which multiple CSP's are refined does not matter.
|
||||
|
||||
// ----- if the doc is an app and we want a default CSP, apply it.
|
||||
if (applyAppDefaultCSP) {
|
||||
nsAdoptingString appCSP;
|
||||
if (appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) {
|
||||
@ -2594,92 +2620,43 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default");
|
||||
}
|
||||
|
||||
if (appCSP)
|
||||
if (appCSP) {
|
||||
// Use the 1.0 CSP parser for apps if the pref to do so is set.
|
||||
csp->RefinePolicy(appCSP, chanURI, specCompliantEnabled);
|
||||
csp->AppendPolicy(appCSP, selfURI, false, specCompliantEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- if the doc is an app and specifies a CSP in its manifest, apply it.
|
||||
if (applyAppManifestCSP) {
|
||||
// Use the 1.0 CSP parser for apps if the pref to do so is set.
|
||||
csp->RefinePolicy(appManifestCSP, chanURI, specCompliantEnabled);
|
||||
csp->AppendPolicy(appManifestCSP, selfURI, false, specCompliantEnabled);
|
||||
}
|
||||
|
||||
// While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers
|
||||
// take priority. If any spec-compliant headers are present, the x- headers
|
||||
// are ignored, and the spec compliant parser is used.
|
||||
bool cspSpecCompliant = (!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty());
|
||||
|
||||
// If the old header is present, warn that it will be deprecated.
|
||||
if (!cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty()) {
|
||||
mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated");
|
||||
|
||||
// Also, if the new headers AND the old headers were present, warn
|
||||
// that the old headers will be ignored.
|
||||
if (cspSpecCompliant) {
|
||||
mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent");
|
||||
}
|
||||
}
|
||||
// can coexist with x- headers. If both exist, they're both enforced, but
|
||||
// there's a warning posted in the web console that the x-headers are going
|
||||
// away.
|
||||
|
||||
// ----- if there's a full-strength CSP header, apply it.
|
||||
bool applyCSPFromHeader =
|
||||
(( cspSpecCompliant && !cspHeaderValue.IsEmpty()) ||
|
||||
(!cspSpecCompliant && !cspOldHeaderValue.IsEmpty()));
|
||||
|
||||
if (applyCSPFromHeader) {
|
||||
// Need to tokenize the header value since multiple headers could be
|
||||
// concatenated into one comma-separated list of policies.
|
||||
// See RFC2616 section 4.2 (last paragraph)
|
||||
nsCharSeparatedTokenizer tokenizer(cspSpecCompliant ?
|
||||
cspHeaderValue :
|
||||
cspOldHeaderValue, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& policy = tokenizer.nextToken();
|
||||
csp->RefinePolicy(policy, chanURI, cspSpecCompliant);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP refined with policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!cspOldHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspOldHeaderValue, selfURI, false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- if there's a report-only CSP header, apply it
|
||||
if (( cspSpecCompliant && !cspROHeaderValue.IsEmpty()) ||
|
||||
(!cspSpecCompliant && !cspOldROHeaderValue.IsEmpty())) {
|
||||
// post a warning and skip report-only CSP when both read only and regular
|
||||
// CSP policies are present since CSP only allows one policy and it can't
|
||||
// be partially report-only.
|
||||
if (applyAppDefaultCSP || applyCSPFromHeader) {
|
||||
mCSPWebConsoleErrorQueue.Add("ReportOnlyCSPIgnored");
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Skipped report-only CSP init for document %p because another, enforced policy is set", this));
|
||||
#endif
|
||||
} else {
|
||||
// we can apply the report-only policy because there's no other CSP
|
||||
// applied.
|
||||
csp->SetReportOnlyMode(true);
|
||||
if (!cspHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspHeaderValue, selfURI, false, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Need to tokenize the header value since multiple headers could be
|
||||
// concatenated into one comma-separated list of policies.
|
||||
// See RFC2616 section 4.2 (last paragraph)
|
||||
nsCharSeparatedTokenizer tokenizer(cspSpecCompliant ?
|
||||
cspROHeaderValue :
|
||||
cspOldROHeaderValue, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& policy = tokenizer.nextToken();
|
||||
csp->RefinePolicy(policy, chanURI, cspSpecCompliant);
|
||||
#ifdef PR_LOGGING
|
||||
{
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSP (report-only) refined with policy: \"%s\"",
|
||||
NS_ConvertUTF16toUTF8(policy).get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// ----- if there's a report-only CSP header, apply it.
|
||||
if (!cspOldROHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspOldROHeaderValue, selfURI, true, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!cspROHeaderValue.IsEmpty()) {
|
||||
rv = AppendCSPFromHeader(csp, cspROHeaderValue, selfURI, true, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- Enforce frame-ancestor policy on any applied policies
|
||||
|
@ -92,6 +92,11 @@ MOCHITEST_FILES := \
|
||||
test_CSP_bug888172.html \
|
||||
file_CSP_bug888172.html \
|
||||
file_CSP_bug888172.sjs \
|
||||
test_bug836922_npolicies.html \
|
||||
file_bug836922_npolicies.html \
|
||||
file_bug836922_npolicies.html^headers^ \
|
||||
file_bug836922_npolicies_violation.sjs \
|
||||
file_bug836922_npolicies_ro_violation.sjs \
|
||||
$(NULL)
|
||||
|
||||
MOCHITEST_CHROME_FILES := \
|
||||
|
15
content/base/test/csp/file_bug836922_npolicies.html
Normal file
15
content/base/test/csp/file_bug836922_npolicies.html
Normal file
@ -0,0 +1,15 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel='stylesheet' type='text/css'
|
||||
href='/tests/content/base/test/csp/file_CSP.sjs?testid=css_self&type=text/css' />
|
||||
<link rel='stylesheet' type='text/css'
|
||||
href='http://example.com/tests/content/base/test/csp/file_CSP.sjs?testid=css_examplecom&type=text/css' />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img_self&type=img/png"> </img>
|
||||
<img src="http://example.com/tests/content/base/test/csp/file_CSP.sjs?testid=img_examplecom&type=img/png"> </img>
|
||||
<script src='/tests/content/base/test/csp/file_CSP.sjs?testid=script_self&type=text/javascript'></script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,2 @@
|
||||
content-security-policy: default-src 'self'; img-src 'none'; report-uri http://mochi.test:8888/tests/content/base/test/csp/file_bug836922_npolicies_violation.sjs
|
||||
content-security-policy-report-only: default-src *; img-src 'self'; script-src 'none'; report-uri http://mochi.test:8888/tests/content/base/test/csp/file_bug836922_npolicies_ro_violation.sjs
|
@ -0,0 +1,53 @@
|
||||
// SJS file that receives violation reports and then responds with nothing.
|
||||
|
||||
const CC = Components.Constructor;
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
"setInputStream");
|
||||
|
||||
const STATE_KEY = "bug836922_ro_violations";
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var query = {};
|
||||
request.queryString.split('&').forEach(function (val) {
|
||||
var [name, value] = val.split('=');
|
||||
query[name] = unescape(value);
|
||||
});
|
||||
|
||||
if ('results' in query) {
|
||||
// if asked for the received data, send it.
|
||||
response.setHeader("Content-Type", "text/javascript", false);
|
||||
if (getState(STATE_KEY)) {
|
||||
response.write(getState(STATE_KEY));
|
||||
} else {
|
||||
// no state has been recorded.
|
||||
response.write(JSON.stringify({}));
|
||||
}
|
||||
} else if ('reset' in query) {
|
||||
//clear state
|
||||
setState(STATE_KEY, JSON.stringify(null));
|
||||
} else {
|
||||
// ... otherwise, just respond "ok".
|
||||
response.write("null");
|
||||
|
||||
var bodystream = new BinaryInputStream(request.bodyInputStream);
|
||||
var avail;
|
||||
var bytes = [];
|
||||
while ((avail = bodystream.available()) > 0)
|
||||
Array.prototype.push.apply(bytes, bodystream.readByteArray(avail));
|
||||
|
||||
var data = String.fromCharCode.apply(null, bytes);
|
||||
|
||||
// figure out which test was violating a policy
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
var testid = testpat.exec(data)[1];
|
||||
|
||||
// store the violation in the persistent state
|
||||
var s = JSON.parse(getState(STATE_KEY) || "{}");
|
||||
s[testid] ? s[testid]++ : s[testid] = 1;
|
||||
setState(STATE_KEY, JSON.stringify(s));
|
||||
}
|
||||
}
|
||||
|
||||
|
59
content/base/test/csp/file_bug836922_npolicies_violation.sjs
Normal file
59
content/base/test/csp/file_bug836922_npolicies_violation.sjs
Normal file
@ -0,0 +1,59 @@
|
||||
// SJS file that receives violation reports and then responds with nothing.
|
||||
|
||||
const CC = Components.Constructor;
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
"setInputStream");
|
||||
|
||||
const STATE = "bug836922_violations";
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var query = {};
|
||||
request.queryString.split('&').forEach(function (val) {
|
||||
var [name, value] = val.split('=');
|
||||
query[name] = unescape(value);
|
||||
});
|
||||
|
||||
|
||||
if ('results' in query) {
|
||||
// if asked for the received data, send it.
|
||||
response.setHeader("Content-Type", "text/javascript", false);
|
||||
if (getState(STATE)) {
|
||||
response.write(getState(STATE));
|
||||
} else {
|
||||
// no state has been recorded.
|
||||
response.write(JSON.stringify({}));
|
||||
}
|
||||
} else if ('reset' in query) {
|
||||
//clear state
|
||||
setState(STATE, JSON.stringify(null));
|
||||
} else {
|
||||
// ... otherwise, just respond "ok".
|
||||
response.write("null");
|
||||
|
||||
var bodystream = new BinaryInputStream(request.bodyInputStream);
|
||||
var avail;
|
||||
var bytes = [];
|
||||
while ((avail = bodystream.available()) > 0)
|
||||
Array.prototype.push.apply(bytes, bodystream.readByteArray(avail));
|
||||
|
||||
var data = String.fromCharCode.apply(null, bytes);
|
||||
|
||||
// figure out which test was violating a policy
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
var testid = testpat.exec(data)[1];
|
||||
|
||||
// store the violation in the persistent state
|
||||
var s = getState(STATE);
|
||||
if (!s) s = "{}";
|
||||
s = JSON.parse(s);
|
||||
if (!s) s = {};
|
||||
|
||||
if (!s[testid]) s[testid] = 0;
|
||||
s[testid]++;
|
||||
setState(STATE, JSON.stringify(s));
|
||||
}
|
||||
}
|
||||
|
||||
|
255
content/base/test/csp/test_bug836922_npolicies.html
Normal file
255
content/base/test/csp/test_bug836922_npolicies.html
Normal file
@ -0,0 +1,255 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Content Security Policy multiple policy support (regular and Report-Only mode)</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var path = "/tests/content/base/test/csp/";
|
||||
|
||||
// These are test results: verified indicates whether or not the test has run.
|
||||
// true/false is the pass/fail result.
|
||||
window.loads = {
|
||||
css_self: {expected: true, verified: false},
|
||||
css_examplecom: {expected: false, verified: false},
|
||||
img_self: {expected: false, verified: false},
|
||||
img_examplecom: {expected: false, verified: false},
|
||||
script_self: {expected: true, verified: false},
|
||||
};
|
||||
|
||||
window.violation_reports = {
|
||||
css_self:
|
||||
{expected: 0, expected_ro: 0}, /* totally fine */
|
||||
css_examplecom:
|
||||
{expected: 1, expected_ro: 0}, /* violates enforced CSP */
|
||||
img_self:
|
||||
{expected: 1, expected_ro: 0}, /* violates enforced CSP */
|
||||
img_examplecom:
|
||||
{expected: 1, expected_ro: 1}, /* violates both CSPs */
|
||||
script_self:
|
||||
{expected: 0, expected_ro: 1}, /* violates report-only */
|
||||
};
|
||||
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire. This also watches for violation reports to go out.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if(!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(
|
||||
SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"),
|
||||
"URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
|
||||
// violation reports don't come through here, but the requested resources do
|
||||
// if the test has already finished, move on. Some things throw multiple
|
||||
// requests (preloads and such)
|
||||
try {
|
||||
if (window.loads[testid].verified) return;
|
||||
} catch(e) { return; }
|
||||
|
||||
// these are requests that were allowed by CSP
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
window.testResult(testid, 'allowed', asciiSpec + " allowed by csp");
|
||||
}
|
||||
|
||||
if(topic === "csp-on-violate-policy") {
|
||||
// if the violated policy was report-only, the resource will still be
|
||||
// loaded even if this topic is notified.
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(
|
||||
SpecialPowers.do_QueryInterface(subject, "nsIURI"),
|
||||
"asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
|
||||
// if the test has already finished, move on.
|
||||
try {
|
||||
if (window.loads[testid].verified) return;
|
||||
} catch(e) { return; }
|
||||
|
||||
// record the ones that were supposed to be blocked, but don't use this
|
||||
// as an indicator for tests that are not blocked but do generate reports.
|
||||
// We skip recording the result if the load is expected since a
|
||||
// report-only policy will generate a request *and* a violation note.
|
||||
if (!window.loads[testid].expected) {
|
||||
window.testResult(testid,
|
||||
'blocked',
|
||||
asciiSpec + " blocked by \"" + data + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// if any test is unverified, keep waiting
|
||||
for (var v in window.loads) {
|
||||
if(!window.loads[v].verified) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
window.bug836922examiner.remove();
|
||||
window.resultPoller.pollForFinish();
|
||||
},
|
||||
|
||||
// must eventually call this to remove the listener,
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
}
|
||||
}
|
||||
window.bug836922examiner = new examiner();
|
||||
|
||||
|
||||
// Poll for results and see if enough reports came in. Keep trying
|
||||
// for a few seconds before failing with lack of reports.
|
||||
// Have to do this because there's a race between the async reporting
|
||||
// and this test finishing, and we don't want to win the race.
|
||||
window.resultPoller = {
|
||||
|
||||
POLL_ATTEMPTS_LEFT: 14,
|
||||
|
||||
pollForFinish:
|
||||
function() {
|
||||
var vr = resultPoller.tallyReceivedReports();
|
||||
if (resultPoller.verifyReports(vr, resultPoller.POLL_ATTEMPTS_LEFT < 1)) {
|
||||
// report success condition.
|
||||
resultPoller.resetReportServer();
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
resultPoller.POLL_ATTEMPTS_LEFT--;
|
||||
// try again unless we reached the threshold.
|
||||
setTimeout(resultPoller.pollForFinish, 100);
|
||||
}
|
||||
},
|
||||
|
||||
resetReportServer:
|
||||
function() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr_ro = new XMLHttpRequest();
|
||||
xhr.open("GET", "file_bug836922_npolicies_violation.sjs?reset", false);
|
||||
xhr_ro.open("GET", "file_bug836922_npolicies_ro_violation.sjs?reset", false);
|
||||
xhr.send(null);
|
||||
xhr_ro.send(null);
|
||||
},
|
||||
|
||||
tallyReceivedReports:
|
||||
function() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr_ro = new XMLHttpRequest();
|
||||
xhr.open("GET", "file_bug836922_npolicies_violation.sjs?results", false);
|
||||
xhr_ro.open("GET", "file_bug836922_npolicies_ro_violation.sjs?results", false);
|
||||
xhr.send(null);
|
||||
xhr_ro.send(null);
|
||||
|
||||
var received = JSON.parse(xhr.responseText);
|
||||
var received_ro = JSON.parse(xhr_ro.responseText);
|
||||
|
||||
var results = {enforced: {}, reportonly: {}};
|
||||
for (var r in window.violation_reports) {
|
||||
results.enforced[r] = 0;
|
||||
results.reportonly[r] = 0;
|
||||
}
|
||||
|
||||
for (var r in received) {
|
||||
results.enforced[r] += received[r];
|
||||
}
|
||||
for (var r in received_ro) {
|
||||
results.reportonly[r] += received_ro[r];
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
verifyReports:
|
||||
function(receivedCounts, lastAttempt) {
|
||||
for (var r in window.violation_reports) {
|
||||
var exp = window.violation_reports[r].expected;
|
||||
var exp_ro = window.violation_reports[r].expected_ro;
|
||||
var rec = receivedCounts.enforced[r];
|
||||
var rec_ro = receivedCounts.reportonly[r];
|
||||
|
||||
// if this test breaks, these are helpful dumps:
|
||||
//dump(">>> Verifying " + r + "\n");
|
||||
//dump(" > Expected: " + exp + " / " + exp_ro + " (ro)\n");
|
||||
//dump(" > Received: " + rec + " / " + rec_ro + " (ro) \n");
|
||||
|
||||
// in all cases, we're looking for *at least* the expected number of
|
||||
// reports of each type (there could be more in some edge cases).
|
||||
// If there are not enough, we keep waiting and poll the server again
|
||||
// later. If there are enough, we can successfully finish.
|
||||
|
||||
if (exp == 0)
|
||||
is(rec, 0,
|
||||
"Expected zero enforced-policy violation " +
|
||||
"reports for " + r + ", got " + rec);
|
||||
else if (lastAttempt)
|
||||
ok(rec >= exp,
|
||||
"Received (" + rec + "/" + exp + ") " +
|
||||
"enforced-policy reports for " + r);
|
||||
else if (rec < exp)
|
||||
return false; // continue waiting for more
|
||||
|
||||
if(exp_ro == 0)
|
||||
is(rec_ro, 0,
|
||||
"Expected zero report-only-policy violation " +
|
||||
"reports for " + r + ", got " + rec_ro);
|
||||
else if (lastAttempt)
|
||||
ok(rec_ro >= exp_ro,
|
||||
"Received (" + rec_ro + "/" + exp_ro + ") " +
|
||||
"report-only-policy reports for " + r);
|
||||
else if (rec_ro < exp_ro)
|
||||
return false; // continue waiting for more
|
||||
}
|
||||
|
||||
// if we complete the loop, we've found all of the violation
|
||||
// reports we expect.
|
||||
if (lastAttempt) return true;
|
||||
|
||||
// Repeat successful tests once more to record successes via ok()
|
||||
return resultPoller.verifyReports(receivedCounts, true);
|
||||
}
|
||||
};
|
||||
|
||||
window.testResult = function(testname, result, msg) {
|
||||
// otherwise, make sure the allowed ones are expected and blocked ones are not.
|
||||
if (window.loads[testname].expected) {
|
||||
is(result, 'allowed', ">> " + msg);
|
||||
} else {
|
||||
is(result, 'blocked', ">> " + msg);
|
||||
}
|
||||
window.loads[testname].verified = true;
|
||||
}
|
||||
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{'set':[["security.csp.speccompliant", true]]},
|
||||
function() {
|
||||
// save this for last so that our listeners are registered.
|
||||
// ... this loads the testbed of good and bad requests.
|
||||
document.getElementById('cspframe').src = 'http://mochi.test:8888' + path + 'file_bug836922_npolicies.html';
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -67,7 +67,7 @@ listener.prototype = {
|
||||
// nsIContentSecurityPolicy instance. The problem is, this cspr_str is a
|
||||
// string and not a policy due to the way it's exposed from
|
||||
// nsIContentSecurityPolicy, so we have to re-parse it.
|
||||
let cspr_str = this._csp.policy;
|
||||
let cspr_str = this._csp.getPolicy(0);
|
||||
let cspr = CSPRep.fromString(cspr_str, mkuri(DOCUMENT_URI));
|
||||
|
||||
// and in reparsing it, we lose the 'self' relationships, so need to also
|
||||
|
@ -79,10 +79,8 @@ function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
|
||||
csp.scanRequestData(selfchan);
|
||||
|
||||
// Load up the policy
|
||||
csp.refinePolicy(policy, selfuri, false);
|
||||
|
||||
// set as report-only if that's the case
|
||||
if (useReportOnlyPolicy) csp.reportOnlyMode = true;
|
||||
csp.appendPolicy(policy, selfuri, useReportOnlyPolicy, false);
|
||||
|
||||
// prime the report server
|
||||
var handler = makeReportHandler("/test" + id, "Test " + id, expectedJSON);
|
||||
@ -100,7 +98,7 @@ function run_test() {
|
||||
// test that inline script violations cause a report.
|
||||
makeTest(0, {"blocked-uri": "self"}, false,
|
||||
function(csp) {
|
||||
let inlineOK = true, oReportViolation = {};
|
||||
let inlineOK = true, oReportViolation = {'value': false};
|
||||
inlineOK = csp.getAllowsInlineScript(oReportViolation);
|
||||
|
||||
// this is not a report only policy, so it better block inline scripts
|
||||
@ -108,17 +106,19 @@ function run_test() {
|
||||
// ... and cause reports to go out
|
||||
do_check_true(oReportViolation.value);
|
||||
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
0);
|
||||
if (oReportViolation.value) {
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
0);
|
||||
}
|
||||
});
|
||||
|
||||
// test that eval violations cause a report.
|
||||
makeTest(1, {"blocked-uri": "self"}, false,
|
||||
function(csp) {
|
||||
let evalOK = true, oReportViolation = {};
|
||||
let evalOK = true, oReportViolation = {'value': false};
|
||||
evalOK = csp.getAllowsEval(oReportViolation);
|
||||
|
||||
// this is not a report only policy, so it better block eval
|
||||
@ -126,11 +126,13 @@ function run_test() {
|
||||
// ... and cause reports to go out
|
||||
do_check_true(oReportViolation.value);
|
||||
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
1);
|
||||
if (oReportViolation.value) {
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
1);
|
||||
}
|
||||
});
|
||||
|
||||
makeTest(2, {"blocked-uri": "http://blocked.test/foo.js"}, false,
|
||||
@ -144,25 +146,28 @@ function run_test() {
|
||||
// test that inline script violations cause a report in report-only policy
|
||||
makeTest(3, {"blocked-uri": "self"}, true,
|
||||
function(csp) {
|
||||
let inlineOK = true, oReportViolation = {};
|
||||
let inlineOK = true, oReportViolation = {'value': false};
|
||||
inlineOK = csp.getAllowsInlineScript(oReportViolation);
|
||||
|
||||
// this is a report only policy, so it better allow inline scripts
|
||||
do_check_true(inlineOK);
|
||||
// ... but still cause reports to go out
|
||||
|
||||
// ... and cause reports to go out
|
||||
do_check_true(oReportViolation.value);
|
||||
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
3);
|
||||
if (oReportViolation.value) {
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
3);
|
||||
}
|
||||
});
|
||||
|
||||
// test that eval violations cause a report in report-only policy
|
||||
makeTest(4, {"blocked-uri": "self"}, true,
|
||||
function(csp) {
|
||||
let evalOK = true, oReportViolation = {};
|
||||
let evalOK = true, oReportViolation = {'value': false};
|
||||
evalOK = csp.getAllowsEval(oReportViolation);
|
||||
|
||||
// this is a report only policy, so it better allow eval
|
||||
@ -170,10 +175,12 @@ function run_test() {
|
||||
// ... but still cause reports to go out
|
||||
do_check_true(oReportViolation.value);
|
||||
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
4);
|
||||
if (oReportViolation.value) {
|
||||
// force the logging, since the getter doesn't.
|
||||
csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
|
||||
selfuri.asciiSpec,
|
||||
"script sample",
|
||||
4);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -141,15 +141,6 @@ test(
|
||||
do_check_true( h2.permits("a.b.c")); //"CSPHost a.b.c should allow string a.b.c"
|
||||
});
|
||||
|
||||
test(
|
||||
function test_CSPHost_intersectWith() {
|
||||
var h = CSPHost.fromString("*.b.c");
|
||||
//"*.a.b.c ^ *.b.c should be *.a.b.c"
|
||||
do_check_eq("*.a.b.c", h.intersectWith(CSPHost.fromString("*.a.b.c")).toString());
|
||||
|
||||
//"*.b.c ^ *.d.e should not work (null)"
|
||||
do_check_eq(null, h.intersectWith(CSPHost.fromString("*.d.e")));
|
||||
});
|
||||
|
||||
///////////////////// Test the Source object //////////////////////
|
||||
|
||||
@ -317,42 +308,6 @@ test(
|
||||
do_check_false(wildcardHostSourceList.permits("http://barbaz.com"));
|
||||
});
|
||||
|
||||
test(
|
||||
function test_CSPSourceList_intersect() {
|
||||
// for this test, 'self' values are irrelevant
|
||||
// policy a /\ policy b intersects policies, not context (where 'self'
|
||||
// values come into play)
|
||||
var nullSourceList = CSPSourceList.fromString("'none'");
|
||||
var simpleSourceList = CSPSourceList.fromString("http://a.com");
|
||||
var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88");
|
||||
var singleFooSourceList = CSPSourceList.fromString("https://foo.com");
|
||||
var allSourceList = CSPSourceList.fromString("*");
|
||||
|
||||
//"Intersection of one source with 'none' source list should be none.");
|
||||
do_check_true(nullSourceList.intersectWith(simpleSourceList).isNone());
|
||||
//"Intersection of two sources with 'none' source list should be none.");
|
||||
do_check_true(nullSourceList.intersectWith(doubleSourceList).isNone());
|
||||
//"Intersection of '*' with 'none' source list should be none.");
|
||||
do_check_true(nullSourceList.intersectWith(allSourceList).isNone());
|
||||
|
||||
//"Intersection of one source with '*' source list should be one source.");
|
||||
do_check_equivalent(allSourceList.intersectWith(simpleSourceList),
|
||||
simpleSourceList);
|
||||
//"Intersection of two sources with '*' source list should be two sources.");
|
||||
do_check_equivalent(allSourceList.intersectWith(doubleSourceList),
|
||||
doubleSourceList);
|
||||
|
||||
//"Non-overlapping source lists should intersect to 'none'");
|
||||
do_check_true(simpleSourceList.intersectWith(doubleSourceList).isNone());
|
||||
|
||||
//"subset and superset should intersect to subset.");
|
||||
do_check_equivalent(singleFooSourceList,
|
||||
doubleSourceList.intersectWith(singleFooSourceList));
|
||||
|
||||
//TODO: write more tests?
|
||||
|
||||
});
|
||||
|
||||
///////////////////// Test the Whole CSP rep object //////////////////////
|
||||
|
||||
test(
|
||||
@ -680,7 +635,7 @@ test(function test_FrameAncestor_ignores_userpass_bug779918() {
|
||||
function testPermits(aChildUri, aParentUri, aContentType) {
|
||||
let cspObj = Cc["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance(Ci.nsIContentSecurityPolicy);
|
||||
cspObj.refinePolicy(testPolicy, aChildUri, false);
|
||||
cspObj.appendPolicy(testPolicy, aChildUri, false, false);
|
||||
let docshellparent = Cc["@mozilla.org/docshell;1"]
|
||||
.createInstance(Ci.nsIDocShell);
|
||||
let docshellchild = Cc["@mozilla.org/docshell;1"]
|
||||
@ -909,56 +864,6 @@ test(
|
||||
do_check_false(p_none.permits("http://bar.com"));
|
||||
});
|
||||
|
||||
test(
|
||||
function test_bug783497_refinePolicyIssues() {
|
||||
|
||||
const firstPolicy = "allow 'self'; img-src 'self'; script-src 'self'; options 'bogus-option'";
|
||||
const secondPolicy = "default-src 'none'; script-src 'self'";
|
||||
var cspObj = Cc["@mozilla.org/contentsecuritypolicy;1"]
|
||||
.createInstance(Ci.nsIContentSecurityPolicy);
|
||||
var selfURI = URI("http://self.com/");
|
||||
|
||||
function testPermits(aUri, aContentType) {
|
||||
return cspObj.shouldLoad(aContentType, aUri, null, null, null, null)
|
||||
== Ci.nsIContentPolicy.ACCEPT;
|
||||
};
|
||||
|
||||
// everything is allowed by the default policy
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_true(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
|
||||
// fold in the first policy
|
||||
cspObj.refinePolicy(firstPolicy, selfURI, false);
|
||||
|
||||
// script-src and img-src are limited to self after the first policy
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_true(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
|
||||
// fold in the second policy
|
||||
cspObj.refinePolicy(secondPolicy, selfURI, false);
|
||||
|
||||
// script-src is self and img-src is none after the merge
|
||||
do_check_true(testPermits(URI("http://self.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
do_check_false(testPermits(URI("http://self.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
do_check_false(testPermits(URI("http://other.com/foo.png"),
|
||||
Ci.nsIContentPolicy.TYPE_IMAGE));
|
||||
});
|
||||
|
||||
test(
|
||||
function test_bug764937_defaultSrcMissing() {
|
||||
@ -974,7 +879,7 @@ test(
|
||||
};
|
||||
|
||||
const policy = "script-src 'self'";
|
||||
cspObjSpecCompliant.refinePolicy(policy, selfURI, true);
|
||||
cspObjSpecCompliant.appendPolicy(policy, selfURI, false, true);
|
||||
|
||||
// Spec-Compliant policy default-src defaults to *.
|
||||
// This means all images are allowed, and only 'self'
|
||||
@ -992,7 +897,7 @@ test(
|
||||
URI("http://bar.com/foo.js"),
|
||||
Ci.nsIContentPolicy.TYPE_SCRIPT));
|
||||
|
||||
cspObjOld.refinePolicy(policy, selfURI, false);
|
||||
cspObjOld.appendPolicy(policy, selfURI, false, false);
|
||||
|
||||
// non-Spec-Compliant policy default-src defaults to 'none'
|
||||
// This means all images are blocked, and so are all scripts (because the
|
||||
|
@ -6,9 +6,6 @@ const C_i = Components.interfaces;
|
||||
|
||||
const UNORDERED_TYPE = C_i.nsIDOMXPathResult.ANY_UNORDERED_NODE_TYPE;
|
||||
|
||||
// Instantiate nsIDOMScriptObjectFactory so that DOMException is usable in xpcshell
|
||||
Components.classesByID["{9eb760f0-4380-11d2-b328-00805f8a3859}"].getService(C_i.nsISupports);
|
||||
|
||||
/**
|
||||
* Determine if the data node has only ignorable white-space.
|
||||
*
|
||||
|
33
docshell/base/crashtests/914521.html
Normal file
33
docshell/base/crashtests/914521.html
Normal file
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
|
||||
function f()
|
||||
{
|
||||
function spin() {
|
||||
for (var i = 0; i < 8; ++i) {
|
||||
var x = new XMLHttpRequest();
|
||||
x.open('GET', 'data:text/html,' + i, false);
|
||||
x.send();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("popstate", spin, false);
|
||||
window.close();
|
||||
window.location = "#c";
|
||||
finish();
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
var html = "<script>" + f + "<\/script><body onload=f()>";
|
||||
var win = window.open("data:text/html," + encodeURIComponent(html), null, "width=300,height=300");
|
||||
win.finish = function() { document.documentElement.removeAttribute("class"); };
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="start();"></body>
|
||||
</html>
|
@ -11,3 +11,4 @@ load 500328-1.html
|
||||
load 514779-1.xhtml
|
||||
load 614499-1.html
|
||||
load 678872-1.html
|
||||
skip-if(Android||B2G) pref(dom.disable_open_during_load,false) load 914521.html
|
||||
|
@ -9214,18 +9214,23 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||
SetDocCurrentStateObj(mOSHE);
|
||||
|
||||
// Dispatch the popstate and hashchange events, as appropriate.
|
||||
if (mScriptGlobal) {
|
||||
//
|
||||
// The event dispatch below can cause us to re-enter script and
|
||||
// destroy the docshell, nulling out mScriptGlobal. Hold a stack
|
||||
// reference to avoid null derefs. See bug 914521.
|
||||
nsRefPtr<nsGlobalWindow> win = mScriptGlobal;
|
||||
if (win) {
|
||||
// Fire a hashchange event URIs differ, and only in their hashes.
|
||||
bool doHashchange = sameExceptHashes && !curHash.Equals(newHash);
|
||||
|
||||
if (historyNavBetweenSameDoc || doHashchange) {
|
||||
mScriptGlobal->DispatchSyncPopState();
|
||||
win->DispatchSyncPopState();
|
||||
}
|
||||
|
||||
if (doHashchange) {
|
||||
// Make sure to use oldURI here, not mCurrentURI, because by
|
||||
// now, mCurrentURI has changed!
|
||||
mScriptGlobal->DispatchAsyncHashchange(oldURI, aURI);
|
||||
win->DispatchAsyncHashchange(oldURI, aURI);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -551,6 +551,10 @@ ManifestHelper.prototype = {
|
||||
return this._localeProp("description");
|
||||
},
|
||||
|
||||
get type() {
|
||||
return this._localeProp("type");
|
||||
},
|
||||
|
||||
get version() {
|
||||
return this._localeProp("version");
|
||||
},
|
||||
|
25
dom/apps/src/InterAppComm.cpp
Normal file
25
dom/apps/src/InterAppComm.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
/* 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 "InterAppComm.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "AccessCheck.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
/* static */ bool
|
||||
InterAppComm::EnabledForScope(JSContext* /* unused */, JSObject* aObj)
|
||||
{
|
||||
// Disable the constructors if they're disabled by the preference for sure.
|
||||
if (!Preferences::GetBool("dom.inter-app-communication-api.enabled", false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only expose the constructors to the chrome codes for Gecko internal uses.
|
||||
// The content pages shouldn't be aware of the constructors.
|
||||
return xpc::AccessCheck::isChrome(aObj);
|
||||
}
|
24
dom/apps/src/InterAppComm.h
Normal file
24
dom/apps/src/InterAppComm.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* 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 mozilla_dom_apps_InterAppComm_h
|
||||
#define mozilla_dom_apps_InterAppComm_h
|
||||
|
||||
// Forward declarations.
|
||||
struct JSContext;
|
||||
class JSObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class InterAppComm
|
||||
{
|
||||
public:
|
||||
static bool EnabledForScope(JSContext* /* unused */, JSObject* aObj);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_apps_InterAppComm_h
|
18
dom/apps/src/InterAppComm.manifest
Normal file
18
dom/apps/src/InterAppComm.manifest
Normal file
@ -0,0 +1,18 @@
|
||||
component {9dbfa904-0718-11e3-8e77-0721a45514b8} InterAppConnection.js
|
||||
contract @mozilla.org/dom/inter-app-connection;1 {9dbfa904-0718-11e3-8e77-0721a45514b8}
|
||||
|
||||
component {6a77e9e0-0645-11e3-b90b-73bb7c78e06a} InterAppConnection.js
|
||||
contract @mozilla.org/dom/inter-app-connection-request;1 {6a77e9e0-0645-11e3-b90b-73bb7c78e06a}
|
||||
|
||||
component {c66e0f8c-e3cb-11e2-9e85-43ef6244b884} InterAppMessagePort.js
|
||||
contract @mozilla.org/dom/inter-app-message-port;1 {c66e0f8c-e3cb-11e2-9e85-43ef6244b884}
|
||||
|
||||
component {3dd15ce6-e7be-11e2-82bc-77967e7a63e6} InterAppCommService.js
|
||||
contract @mozilla.org/inter-app-communication-service;1 {3dd15ce6-e7be-11e2-82bc-77967e7a63e6}
|
||||
category profile-after-change InterAppCommService @mozilla.org/inter-app-communication-service;1
|
||||
|
||||
component {d7c7a466-f91d-11e2-812a-6fab12ece58e} InterAppConnection.js
|
||||
contract @mozilla.org/dom/system-messages/wrapper/connection;1 {d7c7a466-f91d-11e2-812a-6fab12ece58e}
|
||||
|
||||
component {33b4dff4-edf8-11e2-ae9c-77f99f99c3ad} InterAppMessagePort.js
|
||||
contract @mozilla.org/dom/inter-app-message-event;1 {33b4dff4-edf8-11e2-ae9c-77f99f99c3ad}
|
879
dom/apps/src/InterAppCommService.js
Normal file
879
dom/apps/src/InterAppCommService.js
Normal file
@ -0,0 +1,879 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(aMsg) {
|
||||
dump("-- InterAppCommService: " + Date.now() + ": " + aMsg + "\n");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageBroadcaster");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "messenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
const kMessages =["Webapps:Connect",
|
||||
"Webapps:GetConnections",
|
||||
"InterAppConnection:Cancel",
|
||||
"InterAppMessagePort:PostMessage",
|
||||
"InterAppMessagePort:Register",
|
||||
"InterAppMessagePort:Unregister",
|
||||
"child-process-shutdown"];
|
||||
|
||||
function InterAppCommService() {
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
Services.obs.addObserver(this, "inter-app-comm-select-app-result", false);
|
||||
|
||||
kMessages.forEach(function(aMsg) {
|
||||
ppmm.addMessageListener(aMsg, this);
|
||||
}, this);
|
||||
|
||||
// This matrix is used for saving the inter-app connection info registered in
|
||||
// the app manifest. The object literal is defined as below:
|
||||
//
|
||||
// {
|
||||
// "keyword1": {
|
||||
// "subAppManifestURL1": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// "subAppManifestURL2": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// "keyword2": {
|
||||
// "subAppManifestURL3": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// {
|
||||
// "foo": {
|
||||
// "app://subApp1.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp1.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// appStatus: Ci.nsIPrincipal.APP_STATUS_CERTIFIED,
|
||||
// rules: { ... }
|
||||
// },
|
||||
// "app://subApp2.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp2.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// appStatus: Ci.nsIPrincipal.APP_STATUS_PRIVILEGED,
|
||||
// rules: { ... }
|
||||
// }
|
||||
// },
|
||||
// "bar": {
|
||||
// "app://subApp3.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp3.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// appStatus: Ci.nsIPrincipal.APP_STATUS_INSTALLED,
|
||||
// rules: { ... }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TODO Bug 908999 - Update registered connections when app gets uninstalled.
|
||||
this._registeredConnections = {};
|
||||
|
||||
// This matrix is used for saving the permitted connections, which allows
|
||||
// the messaging between publishers and subscribers. The object literal is
|
||||
// defined as below:
|
||||
//
|
||||
// {
|
||||
// "keyword1": {
|
||||
// "pubAppManifestURL1": [
|
||||
// "subAppManifestURL1",
|
||||
// "subAppManifestURL2",
|
||||
// ...
|
||||
// ],
|
||||
// "pubAppManifestURL2": [
|
||||
// "subAppManifestURL3",
|
||||
// "subAppManifestURL4",
|
||||
// ...
|
||||
// ],
|
||||
// ...
|
||||
// },
|
||||
// "keyword2": {
|
||||
// "pubAppManifestURL3": [
|
||||
// "subAppManifestURL5",
|
||||
// ...
|
||||
// ],
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// {
|
||||
// "foo": {
|
||||
// "app://pubApp1.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp1.gaiamobile.org/manifest.webapp",
|
||||
// "app://subApp2.gaiamobile.org/manifest.webapp"
|
||||
// ],
|
||||
// "app://pubApp2.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp3.gaiamobile.org/manifest.webapp",
|
||||
// "app://subApp4.gaiamobile.org/manifest.webapp"
|
||||
// ]
|
||||
// },
|
||||
// "bar": {
|
||||
// "app://pubApp3.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp5.gaiamobile.org/manifest.webapp",
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TODO Bug 908999 - Update allowed connections when app gets uninstalled.
|
||||
this._allowedConnections = {};
|
||||
|
||||
// This matrix is used for saving the caller info from the content process,
|
||||
// which is indexed by a random UUID, to know where to return the promise
|
||||
// resolvser's callback when the prompt UI for allowing connections returns.
|
||||
// An example of the object literal is shown as below:
|
||||
//
|
||||
// {
|
||||
// "fooID": {
|
||||
// outerWindowID: 12,
|
||||
// requestID: 34,
|
||||
// target: pubAppTarget1
|
||||
// },
|
||||
// "barID": {
|
||||
// outerWindowID: 56,
|
||||
// requestID: 78,
|
||||
// target: pubAppTarget2
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// where |outerWindowID| is the ID of the window requesting the connection,
|
||||
// |requestID| is the ID specifying the promise resolver to return,
|
||||
// |target| is the target of the process requesting the connection.
|
||||
this._promptUICallers = {};
|
||||
|
||||
// This matrix is used for saving the pair of message ports, which is indexed
|
||||
// by a random UUID, so that each port can know whom it should talk to.
|
||||
// An example of the object literal is shown as below:
|
||||
//
|
||||
// {
|
||||
// "UUID1": {
|
||||
// keyword: "keyword1",
|
||||
// publisher: {
|
||||
// manifestURL: "app://pubApp1.gaiamobile.org/manifest.webapp",
|
||||
// target: pubAppTarget1,
|
||||
// pageURL: "app://pubApp1.gaiamobile.org/caller.html",
|
||||
// messageQueue: [...]
|
||||
// },
|
||||
// subscriber: {
|
||||
// manifestURL: "app://subApp1.gaiamobile.org/manifest.webapp",
|
||||
// target: subAppTarget1,
|
||||
// pageURL: "app://pubApp1.gaiamobile.org/handler.html",
|
||||
// messageQueue: [...]
|
||||
// }
|
||||
// },
|
||||
// "UUID2": {
|
||||
// keyword: "keyword2",
|
||||
// publisher: {
|
||||
// manifestURL: "app://pubApp2.gaiamobile.org/manifest.webapp",
|
||||
// target: pubAppTarget2,
|
||||
// pageURL: "app://pubApp2.gaiamobile.org/caller.html",
|
||||
// messageQueue: [...]
|
||||
// },
|
||||
// subscriber: {
|
||||
// manifestURL: "app://subApp2.gaiamobile.org/manifest.webapp",
|
||||
// target: subAppTarget2,
|
||||
// pageURL: "app://pubApp2.gaiamobile.org/handler.html",
|
||||
// messageQueue: [...]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
this._messagePortPairs = {};
|
||||
}
|
||||
|
||||
InterAppCommService.prototype = {
|
||||
registerConnection: function(aKeyword, aHandlerPageURI, aManifestURI,
|
||||
aDescription, aAppStatus, aRules) {
|
||||
let manifestURL = aManifestURI.spec;
|
||||
let pageURL = aHandlerPageURI.spec;
|
||||
|
||||
if (DEBUG) {
|
||||
debug("registerConnection: aKeyword: " + aKeyword +
|
||||
" manifestURL: " + manifestURL + " pageURL: " + pageURL +
|
||||
" aDescription: " + aDescription + " aAppStatus: " + aAppStatus +
|
||||
" aRules.minimumAccessLevel: " + aRules.minimumAccessLevel +
|
||||
" aRules.manifestURLs: " + aRules.manifestURLs +
|
||||
" aRules.installOrigins: " + aRules.installOrigins);
|
||||
}
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[aKeyword];
|
||||
if (!subAppManifestURLs) {
|
||||
subAppManifestURLs = this._registeredConnections[aKeyword] = {};
|
||||
}
|
||||
|
||||
subAppManifestURLs[manifestURL] = {
|
||||
pageURL: pageURL,
|
||||
description: aDescription,
|
||||
appStatus: aAppStatus,
|
||||
rules: aRules,
|
||||
manifestURL: manifestURL
|
||||
};
|
||||
},
|
||||
|
||||
_matchMinimumAccessLevel: function(aRules, aAppStatus) {
|
||||
if (!aRules || !aRules.minimumAccessLevel) {
|
||||
if (DEBUG) {
|
||||
debug("rules.minimumAccessLevel is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let minAccessLevel = aRules.minimumAccessLevel;
|
||||
switch (minAccessLevel) {
|
||||
case "web":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_INSTALLED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "privileged":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "certified":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.minimumAccessLevel is not matched!" +
|
||||
" minAccessLevel: " + minAccessLevel +
|
||||
" aAppStatus : " + aAppStatus);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchManifestURLs: function(aRules, aManifestURL) {
|
||||
if (!aRules || !Array.isArray(aRules.manifestURLs)) {
|
||||
if (DEBUG) {
|
||||
debug("rules.manifestURLs is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let manifestURLs = aRules.manifestURLs;
|
||||
if (manifestURLs.indexOf(aManifestURL) != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.manifestURLs is not matched!" +
|
||||
" manifestURLs: " + manifestURLs +
|
||||
" aManifestURL : " + aManifestURL);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchInstallOrigins: function(aRules, aManifestURL) {
|
||||
if (!aRules || !Array.isArray(aRules.installOrigins)) {
|
||||
if (DEBUG) {
|
||||
debug("rules.installOrigins is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let installOrigin =
|
||||
appsService.getAppByManifestURL(aManifestURL).installOrigin;
|
||||
|
||||
let installOrigins = aRules.installOrigins;
|
||||
if (installOrigins.indexOf(installOrigin) != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.installOrigins is not matched!" +
|
||||
" aManifestURL: " + aManifestURL +
|
||||
" installOrigins: " + installOrigins +
|
||||
" installOrigin : " + installOrigin);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchRules: function(aPubAppManifestURL, aPubAppStatus, aPubRules,
|
||||
aSubAppManifestURL, aSubAppStatus, aSubRules) {
|
||||
// TODO Bug 907068 In the initiative step, we only expose this API to
|
||||
// certified apps to meet the time line. Eventually, we need to make
|
||||
// it available for the non-certified apps as well. For now, only the
|
||||
// certified apps can match the rules.
|
||||
if (aPubAppStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED ||
|
||||
aSubAppStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
if (DEBUG) {
|
||||
debug("Only certified apps are allowed to do connections.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aPubRules && !aSubRules) {
|
||||
if (DEBUG) {
|
||||
debug("No rules for publisher and subscriber. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check minimumAccessLevel.
|
||||
if (!this._matchMinimumAccessLevel(aPubRules, aSubAppStatus) ||
|
||||
!this._matchMinimumAccessLevel(aSubRules, aPubAppStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check manifestURLs.
|
||||
if (!this._matchManifestURLs(aPubRules, aSubAppManifestURL) ||
|
||||
!this._matchManifestURLs(aSubRules, aPubAppManifestURL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check installOrigins.
|
||||
if (!this._matchInstallOrigins(aPubRules, aSubAppManifestURL) ||
|
||||
!this._matchInstallOrigins(aSubRules, aPubAppManifestURL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check developers.
|
||||
// TODO Do we really want to check this? This one seems naive.
|
||||
|
||||
if (DEBUG) debug("All rules are matched.");
|
||||
return true;
|
||||
},
|
||||
|
||||
_dispatchMessagePorts: function(aKeyword, aPubAppManifestURL,
|
||||
aAllowedSubAppManifestURLs,
|
||||
aTarget, aOuterWindowID, aRequestID) {
|
||||
if (DEBUG) {
|
||||
debug("_dispatchMessagePorts: aKeyword: " + aKeyword +
|
||||
" aPubAppManifestURL: " + aPubAppManifestURL +
|
||||
" aAllowedSubAppManifestURLs: " + aAllowedSubAppManifestURLs);
|
||||
}
|
||||
|
||||
if (aAllowedSubAppManifestURLs.length == 0) {
|
||||
if (DEBUG) debug("No apps are allowed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[aKeyword];
|
||||
if (!subAppManifestURLs) {
|
||||
if (DEBUG) debug("No apps are subscribed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
let messagePortIDs = [];
|
||||
aAllowedSubAppManifestURLs.forEach(function(aAllowedSubAppManifestURL) {
|
||||
let subscribedInfo = subAppManifestURLs[aAllowedSubAppManifestURL];
|
||||
if (!subscribedInfo) {
|
||||
if (DEBUG) {
|
||||
debug("The sunscribed info is not available. Skipping: " +
|
||||
aAllowedSubAppManifestURL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The message port ID is aimed for identifying the coupling targets
|
||||
// to deliver messages with each other. This ID is centrally generated
|
||||
// by the parent and dispatched to both the sender and receiver ends
|
||||
// for creating their own message ports respectively.
|
||||
let messagePortID = UUIDGenerator.generateUUID().toString();
|
||||
this._messagePortPairs[messagePortID] = {
|
||||
keyword: aKeyword,
|
||||
publisher: {
|
||||
manifestURL: aPubAppManifestURL
|
||||
},
|
||||
subscriber: {
|
||||
manifestURL: aAllowedSubAppManifestURL
|
||||
}
|
||||
};
|
||||
|
||||
// Fire system message to deliver the message port to the subscriber.
|
||||
messenger.sendMessage("connection",
|
||||
{ keyword: aKeyword,
|
||||
messagePortID: messagePortID },
|
||||
Services.io.newURI(subscribedInfo.pageURL, null, null),
|
||||
Services.io.newURI(subscribedInfo.manifestURL, null, null));
|
||||
|
||||
messagePortIDs.push(messagePortID);
|
||||
}, this);
|
||||
|
||||
if (messagePortIDs.length == 0) {
|
||||
if (DEBUG) debug("No apps are subscribed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
// Return the message port IDs to open the message ports for the publisher.
|
||||
if (DEBUG) debug("messagePortIDs: " + messagePortIDs);
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:OK",
|
||||
{ keyword: aKeyword,
|
||||
messagePortIDs: messagePortIDs,
|
||||
oid: aOuterWindowID, requestID: aRequestID });
|
||||
},
|
||||
|
||||
_connect: function(aMessage, aTarget) {
|
||||
let keyword = aMessage.keyword;
|
||||
let pubRules = aMessage.rules;
|
||||
let pubAppManifestURL = aMessage.manifestURL;
|
||||
let outerWindowID = aMessage.outerWindowID;
|
||||
let requestID = aMessage.requestID;
|
||||
let pubAppStatus = aMessage.appStatus;
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[keyword];
|
||||
if (!subAppManifestURLs) {
|
||||
if (DEBUG) {
|
||||
debug("No apps are subscribed for this connection. Returning.");
|
||||
}
|
||||
this._dispatchMessagePorts(keyword, pubAppManifestURL, [],
|
||||
aTarget, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the apps that used to be allowed to connect before, so that
|
||||
// users don't need to select/allow them again. That is, we only pop up
|
||||
// the prompt UI for the *new* connections.
|
||||
let allowedSubAppManifestURLs = [];
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (allowedPubAppManifestURLs &&
|
||||
allowedPubAppManifestURLs[pubAppManifestURL]) {
|
||||
allowedSubAppManifestURLs = allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
}
|
||||
|
||||
// Check rules to see if a subscribed app is allowed to connect.
|
||||
let appsToSelect = [];
|
||||
for (let subAppManifestURL in subAppManifestURLs) {
|
||||
if (allowedSubAppManifestURLs.indexOf(subAppManifestURL) != -1) {
|
||||
if (DEBUG) {
|
||||
debug("Don't need to select again. Skipping: " + subAppManifestURL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only rule-matched publishers/subscribers are allowed to connect.
|
||||
let subscribedInfo = subAppManifestURLs[subAppManifestURL];
|
||||
let subAppStatus = subscribedInfo.appStatus;
|
||||
let subRules = subscribedInfo.rules;
|
||||
|
||||
let matched =
|
||||
this._matchRules(pubAppManifestURL, pubAppStatus, pubRules,
|
||||
subAppManifestURL, subAppStatus, subRules);
|
||||
if (!matched) {
|
||||
if (DEBUG) {
|
||||
debug("Rules are not matched. Skipping: " + subAppManifestURL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
appsToSelect.push({
|
||||
manifestURL: subAppManifestURL,
|
||||
description: subscribedInfo.description
|
||||
});
|
||||
}
|
||||
|
||||
if (appsToSelect.length == 0) {
|
||||
if (DEBUG) {
|
||||
debug("No additional apps need to be selected for this connection. " +
|
||||
"Just dispatch message ports for the existing connections.");
|
||||
}
|
||||
|
||||
this._dispatchMessagePorts(keyword, pubAppManifestURL,
|
||||
allowedSubAppManifestURLs,
|
||||
aTarget, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remember the caller info with an UUID so that we can know where to
|
||||
// return the promise resolver's callback when the prompt UI returns.
|
||||
let callerID = UUIDGenerator.generateUUID().toString();
|
||||
this._promptUICallers[callerID] = {
|
||||
outerWindowID: outerWindowID,
|
||||
requestID: requestID,
|
||||
target: aTarget
|
||||
};
|
||||
|
||||
// TODO Bug 897169 Temporarily disable the notification for popping up
|
||||
// the prompt until the UX/UI for the prompt is confirmed.
|
||||
//
|
||||
// TODO Bug 908191 We need to change the way of interaction between API and
|
||||
// run-time prompt from observer notification to xpcom-interface caller.
|
||||
//
|
||||
/*
|
||||
if (DEBUG) debug("appsToSelect: " + appsToSelect);
|
||||
Services.obs.notifyObservers(null, "inter-app-comm-select-app",
|
||||
JSON.stringify({ callerID: callerID,
|
||||
manifestURL: pubAppManifestURL,
|
||||
keyword: keyword,
|
||||
appsToSelect: appsToSelect }));
|
||||
*/
|
||||
|
||||
// TODO Bug 897169 Simulate the return of the app-selected result by
|
||||
// the prompt, which always allows the connection. This dummy codes
|
||||
// will be removed when the UX/UI for the prompt is ready.
|
||||
if (DEBUG) debug("appsToSelect: " + appsToSelect);
|
||||
Services.obs.notifyObservers(null, 'inter-app-comm-select-app-result',
|
||||
JSON.stringify({ callerID: callerID,
|
||||
manifestURL: pubAppManifestURL,
|
||||
keyword: keyword,
|
||||
selectedApps: appsToSelect }));
|
||||
},
|
||||
|
||||
_getConnections: function(aMessage, aTarget) {
|
||||
let outerWindowID = aMessage.outerWindowID;
|
||||
let requestID = aMessage.requestID;
|
||||
|
||||
let connections = [];
|
||||
for (let keyword in this._allowedConnections) {
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
for (let allowedPubAppManifestURL in allowedPubAppManifestURLs) {
|
||||
let allowedSubAppManifestURLs =
|
||||
allowedPubAppManifestURLs[allowedPubAppManifestURL];
|
||||
allowedSubAppManifestURLs.forEach(function(allowedSubAppManifestURL) {
|
||||
connections.push({ keyword: keyword,
|
||||
pubAppManifestURL: allowedPubAppManifestURL,
|
||||
subAppManifestURL: allowedSubAppManifestURL });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
aTarget.sendAsyncMessage("Webapps:GetConnections:Return:OK",
|
||||
{ connections: connections,
|
||||
oid: outerWindowID, requestID: requestID });
|
||||
},
|
||||
|
||||
_cancelConnection: function(aMessage) {
|
||||
let keyword = aMessage.keyword;
|
||||
let pubAppManifestURL = aMessage.pubAppManifestURL;
|
||||
let subAppManifestURL = aMessage.subAppManifestURL;
|
||||
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (!allowedPubAppManifestURLs) {
|
||||
if (DEBUG) debug("keyword is not found: " + keyword);
|
||||
return;
|
||||
}
|
||||
|
||||
let allowedSubAppManifestURLs =
|
||||
allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
if (!allowedSubAppManifestURLs) {
|
||||
if (DEBUG) debug("publisher is not found: " + pubAppManifestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
let index = allowedSubAppManifestURLs.indexOf(subAppManifestURL);
|
||||
if (index == -1) {
|
||||
if (DEBUG) debug("subscriber is not found: " + subAppManifestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Cancelling the connection.");
|
||||
allowedSubAppManifestURLs.splice(index, 1);
|
||||
|
||||
// Clean up the parent entries if needed.
|
||||
if (allowedSubAppManifestURLs.length == 0) {
|
||||
delete allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
if (Object.keys(allowedPubAppManifestURLs).length == 0) {
|
||||
delete this._allowedConnections[keyword];
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Unregistering message ports based on this connection.");
|
||||
let messagePortIDs = [];
|
||||
for (let messagePortID in this._messagePortPairs) {
|
||||
let pair = this._messagePortPairs[messagePortID];
|
||||
if (pair.keyword == keyword &&
|
||||
pair.publisher.manifestURL == pubAppManifestURL &&
|
||||
pair.subscriber.manifestURL == subAppManifestURL) {
|
||||
messagePortIDs.push(messagePortID);
|
||||
}
|
||||
}
|
||||
messagePortIDs.forEach(function(aMessagePortID) {
|
||||
delete this._messagePortPairs[aMessagePortID];
|
||||
}, this);
|
||||
},
|
||||
|
||||
_identifyMessagePort: function(aMessagePortID, aManifestURL) {
|
||||
let pair = this._messagePortPairs[aMessagePortID];
|
||||
if (!pair) {
|
||||
if (DEBUG) {
|
||||
debug("Error! The message port ID is invalid: " + aMessagePortID +
|
||||
", which should have been generated by parent.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check it the message port is for publisher.
|
||||
if (pair.publisher.manifestURL == aManifestURL) {
|
||||
return { pair: pair, isPublisher: true };
|
||||
}
|
||||
|
||||
// Check it the message port is for subscriber.
|
||||
if (pair.subscriber.manifestURL == aManifestURL) {
|
||||
return { pair: pair, isPublisher: false };
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Error! The manifest URL is invalid: " + aManifestURL +
|
||||
", which might be a hacked app.");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_registerMessagePort: function(aMessage, aTarget) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
let pageURL = aMessage.pageURL;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) {
|
||||
debug("Cannot identify the message port. Failed to register.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Registering message port for " + manifestURL);
|
||||
let pair = identity.pair;
|
||||
let isPublisher = identity.isPublisher;
|
||||
|
||||
let sender = isPublisher ? pair.publisher : pair.subscriber;
|
||||
sender.target = aTarget;
|
||||
sender.pageURL = pageURL;
|
||||
sender.messageQueue = [];
|
||||
|
||||
// Check if the other port has queued messages. Deliver them if needed.
|
||||
if (DEBUG) {
|
||||
debug("Checking if the other port used to send messages but queued.");
|
||||
}
|
||||
let receiver = isPublisher ? pair.subscriber : pair.publisher;
|
||||
if (receiver.messageQueue) {
|
||||
while (receiver.messageQueue.length) {
|
||||
let message = receiver.messageQueue.shift();
|
||||
if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
|
||||
sender.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
|
||||
{ message: message,
|
||||
manifestURL: sender.manifestURL,
|
||||
pageURL: sender.pageURL,
|
||||
messagePortID: messagePortID });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_unregisterMessagePort: function(aMessage) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) {
|
||||
debug("Cannot identify the message port. Failed to unregister.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Unregistering message port for " + manifestURL);
|
||||
}
|
||||
delete this._messagePortPairs[messagePortID];
|
||||
},
|
||||
|
||||
_removeTarget: function(aTarget) {
|
||||
if (!aTarget) {
|
||||
if (DEBUG) debug("Error! aTarget cannot be null/undefined in any way.");
|
||||
return
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Unregistering message ports based on this target.");
|
||||
let messagePortIDs = [];
|
||||
for (let messagePortID in this._messagePortPairs) {
|
||||
let pair = this._messagePortPairs[messagePortID];
|
||||
if (pair.publisher.target === aTarget ||
|
||||
pair.subscriber.target === aTarget) {
|
||||
messagePortIDs.push(messagePortID);
|
||||
}
|
||||
}
|
||||
messagePortIDs.forEach(function(aMessagePortID) {
|
||||
delete this._messagePortPairs[aMessagePortID];
|
||||
}, this);
|
||||
},
|
||||
|
||||
_postMessage: function(aMessage) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
let message = aMessage.message;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) debug("Cannot identify the message port. Failed to post.");
|
||||
return;
|
||||
}
|
||||
|
||||
let pair = identity.pair;
|
||||
let isPublisher = identity.isPublisher;
|
||||
|
||||
let receiver = isPublisher ? pair.subscriber : pair.publisher;
|
||||
if (!receiver.target) {
|
||||
if (DEBUG) {
|
||||
debug("The receiver's target is not ready yet. Queuing the message.");
|
||||
}
|
||||
let sender = isPublisher ? pair.publisher : pair.subscriber;
|
||||
sender.messageQueue.push(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
|
||||
receiver.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
|
||||
{ manifestURL: receiver.manifestURL,
|
||||
pageURL: receiver.pageURL,
|
||||
messagePortID: messagePortID,
|
||||
message: message });
|
||||
},
|
||||
|
||||
_handleSelectcedApps: function(aData) {
|
||||
let callerID = aData.callerID;
|
||||
let caller = this._promptUICallers[callerID];
|
||||
if (!caller) {
|
||||
if (DEBUG) debug("Error! Cannot find the caller.");
|
||||
return;
|
||||
}
|
||||
|
||||
delete this._promptUICallers[callerID];
|
||||
|
||||
let outerWindowID = caller.outerWindowID;
|
||||
let requestID = caller.requestID;
|
||||
let target = caller.target;
|
||||
|
||||
let manifestURL = aData.manifestURL;
|
||||
let keyword = aData.keyword;
|
||||
let selectedApps = aData.selectedApps;
|
||||
|
||||
if (selectedApps.length == 0) {
|
||||
if (DEBUG) debug("No apps are selected to connect.")
|
||||
this._dispatchMessagePorts(keyword, manifestURL, [],
|
||||
target, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the entry of allowed connections to add the selected apps.
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (!allowedPubAppManifestURLs) {
|
||||
allowedPubAppManifestURLs = this._allowedConnections[keyword] = {};
|
||||
}
|
||||
let allowedSubAppManifestURLs = allowedPubAppManifestURLs[manifestURL];
|
||||
if (!allowedSubAppManifestURLs) {
|
||||
allowedSubAppManifestURLs = allowedPubAppManifestURLs[manifestURL] = [];
|
||||
}
|
||||
|
||||
// Add the selected app into the existing set of allowed connections.
|
||||
selectedApps.forEach(function(aSelectedApp) {
|
||||
let allowedSubAppManifestURL = aSelectedApp.manifestURL;
|
||||
if (allowedSubAppManifestURLs.indexOf(allowedSubAppManifestURL) == -1) {
|
||||
allowedSubAppManifestURLs.push(allowedSubAppManifestURL);
|
||||
}
|
||||
});
|
||||
|
||||
// Finally, dispatch the message ports for the allowed connections,
|
||||
// including the old connections and the newly selected connection.
|
||||
this._dispatchMessagePorts(keyword, manifestURL, allowedSubAppManifestURLs,
|
||||
target, outerWindowID, requestID);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
|
||||
let message = aMessage.json;
|
||||
let target = aMessage.target;
|
||||
|
||||
// To prevent the hacked child process from sending commands to parent
|
||||
// to do illegal connections, we need to check its manifest URL.
|
||||
if (aMessage.name !== "child-process-shutdown" &&
|
||||
kMessages.indexOf(aMessage.name) != -1) {
|
||||
if (!target.assertContainApp(message.manifestURL)) {
|
||||
if (DEBUG) {
|
||||
debug("Got message from a process carrying illegal manifest URL.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:Connect":
|
||||
this._connect(message, target);
|
||||
break;
|
||||
case "Webapps:GetConnections":
|
||||
this._getConnections(message, target);
|
||||
break;
|
||||
case "InterAppConnection:Cancel":
|
||||
this._cancelConnection(message);
|
||||
break;
|
||||
case "InterAppMessagePort:PostMessage":
|
||||
this._postMessage(message);
|
||||
break;
|
||||
case "InterAppMessagePort:Register":
|
||||
this._registerMessagePort(message, target);
|
||||
break;
|
||||
case "InterAppMessagePort:Unregister":
|
||||
this._unregisterMessagePort(message);
|
||||
break;
|
||||
case "child-process-shutdown":
|
||||
this._removeTarget(target);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "xpcom-shutdown":
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
Services.obs.removeObserver(this, "inter-app-comm-select-app-result");
|
||||
kMessages.forEach(function(aMsg) {
|
||||
ppmm.removeMessageListener(aMsg, this);
|
||||
}, this);
|
||||
ppmm = null;
|
||||
break;
|
||||
case "inter-app-comm-select-app-result":
|
||||
if (DEBUG) debug("inter-app-comm-select-app-result: " + aData);
|
||||
this._handleSelectcedApps(JSON.parse(aData));
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
classID: Components.ID("{3dd15ce6-e7be-11e2-82bc-77967e7a63e6}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterAppCommService,
|
||||
Ci.nsIObserver])
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppCommService]);
|
144
dom/apps/src/InterAppConnection.js
Normal file
144
dom/apps/src/InterAppConnection.js
Normal file
@ -0,0 +1,144 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(aMsg) {
|
||||
dump("-- InterAppConnection: " + Date.now() + ": " + aMsg + "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* MozInterAppConnection implementation.
|
||||
*/
|
||||
|
||||
function InterAppConnection() {
|
||||
if (DEBUG) debug("InterAppConnection()");
|
||||
this.keyword = null;
|
||||
this.publisher = null;
|
||||
this.subscriber = null;
|
||||
};
|
||||
|
||||
InterAppConnection.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
classDescription: "MozInterAppConnection",
|
||||
|
||||
classID: Components.ID("{9dbfa904-0718-11e3-8e77-0721a45514b8}"),
|
||||
|
||||
contractID: "@mozilla.org/dom/inter-app-connection;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
__init: function(aKeyword, aPublisher, aSubscriber) {
|
||||
if (DEBUG) {
|
||||
debug("__init: aKeyword: " + aKeyword +
|
||||
" aPublisher: " + aPublisher + " aSubscriber: " + aSubscriber);
|
||||
}
|
||||
this.keyword = aKeyword;
|
||||
this.publisher = aPublisher;
|
||||
this.subscriber = aSubscriber;
|
||||
},
|
||||
|
||||
// Ci.nsIDOMGlobalPropertyInitializer implementation.
|
||||
init: function(aWindow) {
|
||||
if (DEBUG) debug("init");
|
||||
|
||||
this.initDOMRequestHelper(aWindow, []);
|
||||
let principal = aWindow.document.nodePrincipal;
|
||||
this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (DEBUG) debug("cancel");
|
||||
|
||||
cpmm.sendAsyncMessage("InterAppConnection:Cancel",
|
||||
{ keyword: this.keyword,
|
||||
pubAppManifestURL: this.publisher,
|
||||
subAppManifestURL: this.subscriber,
|
||||
manifestURL: this._manifestURL });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* MozInterAppConnectionRequest implementation.
|
||||
*/
|
||||
|
||||
function InterAppConnectionRequest() {
|
||||
if (DEBUG) debug("InterAppConnectionRequest()");
|
||||
this.keyword = null;
|
||||
this.port = null;
|
||||
};
|
||||
|
||||
InterAppConnectionRequest.prototype = {
|
||||
classDescription: "MozInterAppConnectionRequest",
|
||||
|
||||
classID: Components.ID("{6a77e9e0-0645-11e3-b90b-73bb7c78e06a}"),
|
||||
|
||||
contractID: "@mozilla.org/dom/inter-app-connection-request;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
||||
|
||||
__init: function(aKeyword, aPort) {
|
||||
if (DEBUG) debug("__init: aKeyword: " + aKeyword + " aPort: " + aPort);
|
||||
this.keyword = aKeyword;
|
||||
this.port = aPort;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* InterAppConnectionRequestWrapper implementation.
|
||||
*
|
||||
* This implements nsISystemMessagesWrapper.wrapMessage(), which provides a
|
||||
* plugable way to wrap a "connection" type system message.
|
||||
*
|
||||
* Please see SystemMessageManager.js to know how it customizes the wrapper.
|
||||
*/
|
||||
|
||||
function InterAppConnectionRequestWrapper() {
|
||||
if (DEBUG) debug("InterAppConnectionRequestWrapper()");
|
||||
}
|
||||
|
||||
InterAppConnectionRequestWrapper.prototype = {
|
||||
// nsISystemMessagesWrapper implementation.
|
||||
wrapMessage: function(aMessage, aWindow) {
|
||||
if (DEBUG) debug("wrapMessage: " + JSON.stringify(aMessage));
|
||||
|
||||
let port = new aWindow.MozInterAppMessagePort(aMessage.messagePortID);
|
||||
let connectionRequest =
|
||||
new aWindow.MozInterAppConnectionRequest(aMessage.keyword, port);
|
||||
|
||||
return connectionRequest;
|
||||
},
|
||||
|
||||
classDescription: "InterAppConnectionRequestWrapper",
|
||||
|
||||
classID: Components.ID("{d7c7a466-f91d-11e2-812a-6fab12ece58e}"),
|
||||
|
||||
contractID: "@mozilla.org/dom/system-messages/wrapper/connection;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper])
|
||||
}
|
||||
|
||||
|
||||
this.NSGetFactory =
|
||||
XPCOMUtils.generateNSGetFactory([InterAppConnection,
|
||||
InterAppConnectionRequest,
|
||||
InterAppConnectionRequestWrapper]);
|
245
dom/apps/src/InterAppMessagePort.js
Normal file
245
dom/apps/src/InterAppMessagePort.js
Normal file
@ -0,0 +1,245 @@
|
||||
/* 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/. */
|
||||
|
||||
// TODO Bug 907060 Per off-line discussion, after the MessagePort is done
|
||||
// at Bug 643325, we will start to refactorize the common logic of both
|
||||
// Inter-App Communication and Shared Worker. For now, we hope to design an
|
||||
// MozInterAppMessagePort to meet the timeline, which still follows exactly
|
||||
// the same interface and semantic as the MessagePort is. In the future,
|
||||
// we can then align it back to MessagePort with backward compatibility.
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(aMsg) {
|
||||
dump("-- InterAppMessagePort: " + Date.now() + ": " + aMsg + "\n");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
const kMessages = ["InterAppMessagePort:OnMessage"];
|
||||
|
||||
|
||||
function InterAppMessageEvent() {
|
||||
this.type = this.data = null;
|
||||
};
|
||||
|
||||
InterAppMessageEvent.prototype = {
|
||||
classDescription: "MozInterAppMessageEvent",
|
||||
|
||||
classID: Components.ID("{33b4dff4-edf8-11e2-ae9c-77f99f99c3ad}"),
|
||||
|
||||
contractID: "@mozilla.org/dom/inter-app-message-event;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
||||
|
||||
__init: function(aType, aDict) {
|
||||
this.type = aType;
|
||||
this.__DOM_IMPL__.initEvent(aType, aDict.bubbles || false,
|
||||
aDict.cancelable || false);
|
||||
this.data = aDict.data;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function InterAppMessagePort() {
|
||||
if (DEBUG) debug("InterAppMessagePort()");
|
||||
};
|
||||
|
||||
InterAppMessagePort.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
classDescription: "MozInterAppMessagePort",
|
||||
|
||||
classID: Components.ID("{c66e0f8c-e3cb-11e2-9e85-43ef6244b884}"),
|
||||
|
||||
contractID: "@mozilla.org/dom/inter-app-message-port;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
// Ci.nsIDOMGlobalPropertyInitializer implementation.
|
||||
init: function(aWindow) {
|
||||
if (DEBUG) debug("Calling init().");
|
||||
|
||||
this.initDOMRequestHelper(aWindow, kMessages);
|
||||
|
||||
let principal = aWindow.document.nodePrincipal;
|
||||
this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
|
||||
this._pageURL = principal.URI.spec;
|
||||
|
||||
this._started = false;
|
||||
this._closed = false;
|
||||
this._messageQueue = [];
|
||||
},
|
||||
|
||||
// WebIDL implementation for constructor.
|
||||
__init: function(aMessagePortID) {
|
||||
if (DEBUG) {
|
||||
debug("Calling __init(): aMessagePortID: " + aMessagePortID);
|
||||
}
|
||||
|
||||
this._messagePortID = aMessagePortID;
|
||||
|
||||
cpmm.sendAsyncMessage("InterAppMessagePort:Register",
|
||||
{ messagePortID: this._messagePortID,
|
||||
manifestURL: this._manifestURL,
|
||||
pageURL: this._pageURL });
|
||||
},
|
||||
|
||||
// DOMRequestIpcHelper implementation.
|
||||
uninit: function() {
|
||||
if (DEBUG) debug("Calling uninit().");
|
||||
|
||||
// When the message port is uninitialized, we need to disentangle the
|
||||
// coupling ports, as if the close() method had been called.
|
||||
if (this._closed) {
|
||||
if (DEBUG) debug("close() has been called. Don't need to close again.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.close();
|
||||
},
|
||||
|
||||
postMessage: function(aMessage) {
|
||||
if (DEBUG) debug("Calling postMessage().");
|
||||
|
||||
if (this._closed) {
|
||||
if (DEBUG) debug("close() has been called. Cannot post message.");
|
||||
return;
|
||||
}
|
||||
|
||||
cpmm.sendAsyncMessage("InterAppMessagePort:PostMessage",
|
||||
{ messagePortID: this._messagePortID,
|
||||
manifestURL: this._manifestURL,
|
||||
message: aMessage });
|
||||
},
|
||||
|
||||
start: function() {
|
||||
// Begin dispatching messages received on the port.
|
||||
if (DEBUG) debug("Calling start().");
|
||||
|
||||
if (this._closed) {
|
||||
if (DEBUG) debug("close() has been called. Cannot call start().");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._started) {
|
||||
if (DEBUG) debug("start() has been called. Don't need to start again.");
|
||||
return;
|
||||
}
|
||||
|
||||
// When a port's port message queue is enabled, the event loop must use it
|
||||
// as one of its task sources.
|
||||
this._started = true;
|
||||
while (this._messageQueue.length) {
|
||||
let message = this._messageQueue.shift();
|
||||
this._dispatchMessage(message);
|
||||
}
|
||||
},
|
||||
|
||||
close: function() {
|
||||
// Disconnecting the port, so that it is no longer active.
|
||||
if (DEBUG) debug("Calling close().");
|
||||
|
||||
if (this._closed) {
|
||||
if (DEBUG) debug("close() has been called. Don't need to close again.");
|
||||
return;
|
||||
}
|
||||
|
||||
this._closed = true;
|
||||
this._messageQueue.length = 0;
|
||||
|
||||
// When this method called on a local port that is entangled with another
|
||||
// port, must cause the user agent to disentangle the coupling ports.
|
||||
cpmm.sendAsyncMessage("InterAppMessagePort:Unregister",
|
||||
{ messagePortID: this._messagePortID,
|
||||
manifestURL: this._manifestURL });
|
||||
},
|
||||
|
||||
get onmessage() {
|
||||
if (DEBUG) debug("Getting onmessage handler.");
|
||||
|
||||
return this.__DOM_IMPL__.getEventHandler("onmessage");
|
||||
},
|
||||
|
||||
set onmessage(aHandler) {
|
||||
if (DEBUG) debug("Setting onmessage handler.");
|
||||
|
||||
this.__DOM_IMPL__.setEventHandler("onmessage", aHandler);
|
||||
|
||||
// The first time a MessagePort object's onmessage IDL attribute is set,
|
||||
// the port's message queue must be enabled, as if the start() method had
|
||||
// been called.
|
||||
if (this._started) {
|
||||
if (DEBUG) debug("start() has been called. Don't need to start again.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.start();
|
||||
},
|
||||
|
||||
_dispatchMessage: function _dispatchMessage(aMessage) {
|
||||
let wrappedMessage = ObjectWrapper.wrap(aMessage, this._window);
|
||||
if (DEBUG) {
|
||||
debug("_dispatchMessage: wrappedMessage: " +
|
||||
JSON.stringify(wrappedMessage));
|
||||
}
|
||||
|
||||
let event = new this._window
|
||||
.MozInterAppMessageEvent("message",
|
||||
{ data: wrappedMessage });
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
|
||||
|
||||
let message = aMessage.json;
|
||||
if (message.manifestURL != this._manifestURL ||
|
||||
message.pageURL != this._pageURL ||
|
||||
message.messagePortID != this._messagePortID) {
|
||||
if (DEBUG) debug("The message doesn't belong to this page. Returning.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "InterAppMessagePort:OnMessage":
|
||||
if (this._closed) {
|
||||
if (DEBUG) debug("close() has been called. Drop the message.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._started) {
|
||||
if (DEBUG) debug("Not yet called start(). Queue up the message.");
|
||||
this._messageQueue.push(message.message);
|
||||
return;
|
||||
}
|
||||
|
||||
this._dispatchMessage(message.message);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) debug("Error! Shouldn't fall into this case.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppMessagePort,
|
||||
InterAppMessageEvent]);
|
||||
|
10
dom/apps/src/Makefile.in
Normal file
10
dom/apps/src/Makefile.in
Normal file
@ -0,0 +1,10 @@
|
||||
# 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/.
|
||||
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(topsrcdir)/js/xpconnect/wrappers \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
@ -309,6 +309,8 @@ WebappsApplication.prototype = {
|
||||
|
||||
init: function(aWindow, aApp) {
|
||||
this._window = aWindow;
|
||||
let principal = this._window.document.nodePrincipal;
|
||||
this._appStatus = principal.appStatus;
|
||||
this.origin = aApp.origin;
|
||||
this._manifest = aApp.manifest;
|
||||
this._updateManifest = aApp.updateManifest;
|
||||
@ -342,7 +344,10 @@ WebappsApplication.prototype = {
|
||||
"Webapps:Launch:Return:OK",
|
||||
"Webapps:Launch:Return:KO",
|
||||
"Webapps:PackageEvent",
|
||||
"Webapps:ClearBrowserData:Return"]);
|
||||
"Webapps:ClearBrowserData:Return",
|
||||
"Webapps:Connect:Return:OK",
|
||||
"Webapps:Connect:Return:KO",
|
||||
"Webapps:GetConnections:Return:OK"]);
|
||||
|
||||
cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
|
||||
["Webapps:OfflineCache",
|
||||
@ -460,6 +465,33 @@ WebappsApplication.prototype = {
|
||||
return request;
|
||||
},
|
||||
|
||||
connect: function(aKeyword, aRules) {
|
||||
return this.createPromise(function (aResolve, aReject) {
|
||||
cpmm.sendAsyncMessage("Webapps:Connect",
|
||||
{ keyword: aKeyword,
|
||||
rules: aRules,
|
||||
manifestURL: this.manifestURL,
|
||||
outerWindowID: this._id,
|
||||
appStatus: this._appStatus,
|
||||
requestID: this.getPromiseResolverId({
|
||||
resolve: aResolve,
|
||||
reject: aReject
|
||||
})});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getConnections: function() {
|
||||
return this.createPromise(function (aResolve, aReject) {
|
||||
cpmm.sendAsyncMessage("Webapps:GetConnections",
|
||||
{ manifestURL: this.manifestURL,
|
||||
outerWindowID: this._id,
|
||||
requestID: this.getPromiseResolverId({
|
||||
resolve: aResolve,
|
||||
reject: aReject
|
||||
})});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
this._onprogress = null;
|
||||
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
|
||||
@ -479,7 +511,14 @@ WebappsApplication.prototype = {
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let msg = aMessage.json;
|
||||
let req = this.takeRequest(msg.requestID);
|
||||
let req;
|
||||
if (aMessage.name == "Webapps:Connect:Return:OK" ||
|
||||
aMessage.name == "Webapps:Connect:Return:KO" ||
|
||||
aMessage.name == "Webapps:GetConnections:Return:OK") {
|
||||
req = this.takePromiseResolver(msg.requestID);
|
||||
} else {
|
||||
req = this.takeRequest(msg.requestID);
|
||||
}
|
||||
|
||||
// ondownload* callbacks should be triggered on all app instances
|
||||
if ((msg.oid != this._id || !req) &&
|
||||
@ -603,6 +642,28 @@ WebappsApplication.prototype = {
|
||||
case "Webapps:ClearBrowserData:Return":
|
||||
Services.DOMRequest.fireSuccess(req, null);
|
||||
break;
|
||||
case "Webapps:Connect:Return:OK":
|
||||
let messagePorts = [];
|
||||
msg.messagePortIDs.forEach(function(aPortID) {
|
||||
let port = new this._window.MozInterAppMessagePort(aPortID);
|
||||
messagePorts.push(port);
|
||||
}, this);
|
||||
req.resolve(messagePorts);
|
||||
break;
|
||||
case "Webapps:Connect:Return:KO":
|
||||
req.reject("No connections registered");
|
||||
break;
|
||||
case "Webapps:GetConnections:Return:OK":
|
||||
let connections = [];
|
||||
msg.connections.forEach(function(aConnection) {
|
||||
let connection =
|
||||
new this._window.MozInterAppConnection(aConnection.keyword,
|
||||
aConnection.pubAppManifestURL,
|
||||
aConnection.subAppManifestURL);
|
||||
connections.push(connection);
|
||||
}, this);
|
||||
req.resolve(connections);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -60,6 +60,11 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "interAppCommService", function() {
|
||||
return Cc["@mozilla.org/inter-app-communication-service;1"]
|
||||
.getService(Ci.nsIInterAppCommService);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "msgmgr", function() {
|
||||
return Cc["@mozilla.org/system-message-internal;1"]
|
||||
.getService(Ci.nsISystemMessagesInternal);
|
||||
@ -530,6 +535,8 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
// |aEntryPoint| is either the entry_point name or the null in which case we
|
||||
// use the root of the manifest.
|
||||
//
|
||||
// TODO Bug 908094 Refine _registerSystemMessagesForEntryPoint(...).
|
||||
_registerSystemMessagesForEntryPoint: function(aManifest, aApp, aEntryPoint) {
|
||||
let root = aManifest;
|
||||
if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
|
||||
@ -571,6 +578,69 @@ this.DOMApplicationRegistry = {
|
||||
});
|
||||
},
|
||||
|
||||
// |aEntryPoint| is either the entry_point name or the null in which case we
|
||||
// use the root of the manifest.
|
||||
//
|
||||
// TODO Bug 908094 Refine _registerInterAppConnectionsForEntryPoint(...).
|
||||
_registerInterAppConnectionsForEntryPoint: function(aManifest, aApp,
|
||||
aEntryPoint) {
|
||||
let root = aManifest;
|
||||
if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
|
||||
root = aManifest.entry_points[aEntryPoint];
|
||||
}
|
||||
|
||||
let connections = root.connections;
|
||||
if (!connections) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((typeof connections) !== "object") {
|
||||
debug("|connections| is not an object. Skipping: " + connections);
|
||||
return;
|
||||
}
|
||||
|
||||
let manifest = new ManifestHelper(aManifest, aApp.origin);
|
||||
let launchPathURI = Services.io.newURI(manifest.fullLaunchPath(aEntryPoint),
|
||||
null, null);
|
||||
let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
|
||||
|
||||
for (let keyword in connections) {
|
||||
let connection = connections[keyword];
|
||||
|
||||
// Resolve the handler path from origin. If |handler_path| is absent,
|
||||
// use |launch_path| as default.
|
||||
let fullHandlerPath;
|
||||
let handlerPath = connection.handler_path;
|
||||
if (handlerPath) {
|
||||
try {
|
||||
fullHandlerPath = manifest.resolveFromOrigin(handlerPath);
|
||||
} catch(e) {
|
||||
debug("Connection's handler path is invalid. Skipping: keyword: " +
|
||||
keyword + " handler_path: " + handlerPath);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let handlerPageURI = fullHandlerPath
|
||||
? Services.io.newURI(fullHandlerPath, null, null)
|
||||
: launchPathURI;
|
||||
|
||||
if (SystemMessagePermissionsChecker
|
||||
.isSystemMessagePermittedToRegister("connection",
|
||||
aApp.origin,
|
||||
aManifest)) {
|
||||
msgmgr.registerPage("connection", handlerPageURI, manifestURI);
|
||||
}
|
||||
|
||||
interAppCommService.
|
||||
registerConnection(keyword,
|
||||
handlerPageURI,
|
||||
manifestURI,
|
||||
connection.description,
|
||||
AppsUtils.getAppManifestStatus(manifest),
|
||||
connection.rules);
|
||||
}
|
||||
},
|
||||
|
||||
_registerSystemMessages: function(aManifest, aApp) {
|
||||
this._registerSystemMessagesForEntryPoint(aManifest, aApp, null);
|
||||
|
||||
@ -583,6 +653,19 @@ this.DOMApplicationRegistry = {
|
||||
}
|
||||
},
|
||||
|
||||
_registerInterAppConnections: function(aManifest, aApp) {
|
||||
this._registerInterAppConnectionsForEntryPoint(aManifest, aApp, null);
|
||||
|
||||
if (!aManifest.entry_points) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let entryPoint in aManifest.entry_points) {
|
||||
this._registerInterAppConnectionsForEntryPoint(aManifest, aApp,
|
||||
entryPoint);
|
||||
}
|
||||
},
|
||||
|
||||
// |aEntryPoint| is either the entry_point name or the null in which case we
|
||||
// use the root of the manifest.
|
||||
_createActivitiesToRegister: function(aManifest, aApp, aEntryPoint, aRunUpdate) {
|
||||
@ -745,6 +828,7 @@ this.DOMApplicationRegistry = {
|
||||
app.redirects = this.sanitizeRedirects(manifest.redirects);
|
||||
}
|
||||
this._registerSystemMessages(manifest, app);
|
||||
this._registerInterAppConnections(manifest, app);
|
||||
appsToRegister.push({ manifest: manifest, app: app });
|
||||
}, this);
|
||||
this._registerActivitiesForApps(appsToRegister, aRunUpdate);
|
||||
@ -1396,6 +1480,7 @@ this.DOMApplicationRegistry = {
|
||||
}
|
||||
this._registerSystemMessages(aNewManifest, aApp);
|
||||
this._registerActivities(aNewManifest, aApp, true);
|
||||
this._registerInterAppConnections(aNewManifest, aApp);
|
||||
} else {
|
||||
// Nothing else to do but notifying we're ready.
|
||||
this.notifyAppsRegistryReady();
|
||||
|
@ -4,9 +4,21 @@
|
||||
# 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/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'InterAppComm.h',
|
||||
]
|
||||
|
||||
CPP_SOURCES += [
|
||||
'InterAppComm.cpp',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'AppsService.js',
|
||||
'AppsService.manifest',
|
||||
'InterAppComm.manifest',
|
||||
'InterAppCommService.js',
|
||||
'InterAppConnection.js',
|
||||
'InterAppMessagePort.js',
|
||||
'Webapps.js',
|
||||
'Webapps.manifest',
|
||||
]
|
||||
@ -25,3 +37,8 @@ EXTRA_PP_JS_MODULES += [
|
||||
'Webapps.jsm',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
LIBRARY_NAME = 'dom_apps_s'
|
||||
|
157
dom/base/CompositionStringSynthesizer.cpp
Normal file
157
dom/base/CompositionStringSynthesizer.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CompositionStringSynthesizer.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsView.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS1(CompositionStringSynthesizer,
|
||||
nsICompositionStringSynthesizer)
|
||||
|
||||
CompositionStringSynthesizer::CompositionStringSynthesizer(
|
||||
nsPIDOMWindow* aWindow)
|
||||
{
|
||||
mWindow = do_GetWeakReference(aWindow);
|
||||
ClearInternal();
|
||||
}
|
||||
|
||||
CompositionStringSynthesizer::~CompositionStringSynthesizer()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CompositionStringSynthesizer::ClearInternal()
|
||||
{
|
||||
mString.Truncate();
|
||||
mClauses.Clear();
|
||||
mCaret.mRangeType = 0;
|
||||
}
|
||||
|
||||
nsIWidget*
|
||||
CompositionStringSynthesizer::GetWidget()
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
nsIDocShell *docShell = window->GetDocShell();
|
||||
if (!docShell) {
|
||||
return nullptr;
|
||||
}
|
||||
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
||||
if (!presShell) {
|
||||
return nullptr;
|
||||
}
|
||||
nsIFrame* frame = presShell->GetRootFrame();
|
||||
if (!frame) {
|
||||
return nullptr;
|
||||
}
|
||||
return frame->GetView()->GetNearestWidget(nullptr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CompositionStringSynthesizer::SetString(const nsAString& aString)
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
mString = aString;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CompositionStringSynthesizer::AppendClause(uint32_t aLength,
|
||||
uint32_t aAttribute)
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
switch (aAttribute) {
|
||||
case ATTR_RAWINPUT:
|
||||
case ATTR_SELECTEDRAWTEXT:
|
||||
case ATTR_CONVERTEDTEXT:
|
||||
case ATTR_SELECTEDCONVERTEDTEXT: {
|
||||
nsTextRange textRange;
|
||||
textRange.mStartOffset =
|
||||
mClauses.IsEmpty() ? 0 : mClauses[mClauses.Length() - 1].mEndOffset;
|
||||
textRange.mEndOffset = textRange.mStartOffset + aLength;
|
||||
textRange.mRangeType = aAttribute;
|
||||
mClauses.AppendElement(textRange);
|
||||
return NS_OK;
|
||||
}
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CompositionStringSynthesizer::SetCaret(uint32_t aOffset, uint32_t aLength)
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
mCaret.mStartOffset = aOffset;
|
||||
mCaret.mEndOffset = mCaret.mStartOffset + aLength;
|
||||
mCaret.mRangeType = NS_TEXTRANGE_CARETPOSITION;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CompositionStringSynthesizer::DispatchEvent(bool* aDefaultPrevented)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDefaultPrevented);
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
NS_ENSURE_TRUE(widget && !widget->Destroyed(), NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
if (!nsContentUtils::IsCallerChrome()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
if (!mClauses.IsEmpty() &&
|
||||
mClauses[mClauses.Length()-1].mEndOffset != mString.Length()) {
|
||||
NS_WARNING("Sum of length of the all clauses must be same as the string "
|
||||
"length");
|
||||
ClearInternal();
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
if (mCaret.mRangeType == NS_TEXTRANGE_CARETPOSITION) {
|
||||
if (mCaret.mEndOffset > mString.Length()) {
|
||||
NS_WARNING("Caret position is out of the composition string");
|
||||
ClearInternal();
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
mClauses.AppendElement(mCaret);
|
||||
}
|
||||
|
||||
nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
|
||||
textEvent.time = PR_IntervalNow();
|
||||
textEvent.theText = mString;
|
||||
textEvent.rangeCount = mClauses.Length();
|
||||
textEvent.rangeArray = mClauses.Elements();
|
||||
|
||||
// XXX How should we set false for this on b2g?
|
||||
textEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsresult rv = widget->DispatchEvent(&textEvent, status);
|
||||
*aDefaultPrevented = (status == nsEventStatus_eConsumeNoDefault);
|
||||
|
||||
ClearInternal();
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
45
dom/base/CompositionStringSynthesizer.h
Normal file
45
dom/base/CompositionStringSynthesizer.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_compositionstringsynthesizer_h__
|
||||
#define mozilla_dom_compositionstringsynthesizer_h__
|
||||
|
||||
#include "nsICompositionStringSynthesizer.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsIWidget;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class CompositionStringSynthesizer MOZ_FINAL :
|
||||
public nsICompositionStringSynthesizer
|
||||
{
|
||||
public:
|
||||
CompositionStringSynthesizer(nsPIDOMWindow* aWindow);
|
||||
~CompositionStringSynthesizer();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICOMPOSITIONSTRINGSYNTHESIZER
|
||||
|
||||
private:
|
||||
nsWeakPtr mWindow; // refers an instance of nsPIDOMWindow
|
||||
nsString mString;
|
||||
nsAutoTArray<nsTextRange, 10> mClauses;
|
||||
nsTextRange mCaret;
|
||||
|
||||
nsIWidget* GetWidget();
|
||||
void ClearInternal();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // #ifndef mozilla_dom_compositionstringsynthesizer_h__
|
@ -65,6 +65,7 @@ EXPORTS.mozilla.dom += [
|
||||
|
||||
CPP_SOURCES += [
|
||||
'BarProps.cpp',
|
||||
'CompositionStringSynthesizer.cpp',
|
||||
'Crypto.cpp',
|
||||
'DOMCursor.cpp',
|
||||
'DOMError.cpp',
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "nsDOMWindowUtils.h"
|
||||
#include "nsQueryContentEventResult.h"
|
||||
#include "CompositionStringSynthesizer.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsFocusManager.h"
|
||||
@ -1816,82 +1817,21 @@ nsDOMWindowUtils::SendCompositionEvent(const nsAString& aType,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
AppendClause(int32_t aClauseLength, uint32_t aClauseAttr,
|
||||
nsTArray<nsTextRange>* aRanges)
|
||||
{
|
||||
NS_PRECONDITION(aRanges, "aRange is null");
|
||||
if (aClauseLength == 0) {
|
||||
return;
|
||||
}
|
||||
nsTextRange range;
|
||||
range.mStartOffset = aRanges->Length() == 0 ? 0 :
|
||||
aRanges->ElementAt(aRanges->Length() - 1).mEndOffset + 1;
|
||||
range.mEndOffset = range.mStartOffset + aClauseLength;
|
||||
NS_ASSERTION(range.mStartOffset <= range.mEndOffset, "range is invalid");
|
||||
NS_PRECONDITION(aClauseAttr == NS_TEXTRANGE_RAWINPUT ||
|
||||
aClauseAttr == NS_TEXTRANGE_SELECTEDRAWTEXT ||
|
||||
aClauseAttr == NS_TEXTRANGE_CONVERTEDTEXT ||
|
||||
aClauseAttr == NS_TEXTRANGE_SELECTEDCONVERTEDTEXT,
|
||||
"aClauseAttr is invalid value");
|
||||
range.mRangeType = aClauseAttr;
|
||||
aRanges->AppendElement(range);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendTextEvent(const nsAString& aCompositionString,
|
||||
int32_t aFirstClauseLength,
|
||||
uint32_t aFirstClauseAttr,
|
||||
int32_t aSecondClauseLength,
|
||||
uint32_t aSecondClauseAttr,
|
||||
int32_t aThirdClauseLength,
|
||||
uint32_t aThirdClauseAttr,
|
||||
int32_t aCaretStart,
|
||||
int32_t aCaretLength)
|
||||
nsDOMWindowUtils::CreateCompositionStringSynthesizer(
|
||||
nsICompositionStringSynthesizer** aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
*aResult = nullptr;
|
||||
|
||||
if (!nsContentUtils::IsCallerChrome()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
|
||||
InitEvent(textEvent);
|
||||
|
||||
nsAutoTArray<nsTextRange, 4> textRanges;
|
||||
NS_ENSURE_TRUE(aFirstClauseLength >= 0, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aSecondClauseLength >= 0, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aThirdClauseLength >= 0, NS_ERROR_INVALID_ARG);
|
||||
AppendClause(aFirstClauseLength, aFirstClauseAttr, &textRanges);
|
||||
AppendClause(aSecondClauseLength, aSecondClauseAttr, &textRanges);
|
||||
AppendClause(aThirdClauseLength, aThirdClauseAttr, &textRanges);
|
||||
int32_t len = aFirstClauseLength + aSecondClauseLength + aThirdClauseLength;
|
||||
NS_ENSURE_TRUE(len == 0 || uint32_t(len) == aCompositionString.Length(),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
if (aCaretStart >= 0) {
|
||||
nsTextRange range;
|
||||
range.mStartOffset = aCaretStart;
|
||||
range.mEndOffset = range.mStartOffset + aCaretLength;
|
||||
range.mRangeType = NS_TEXTRANGE_CARETPOSITION;
|
||||
textRanges.AppendElement(range);
|
||||
}
|
||||
|
||||
textEvent.theText = aCompositionString;
|
||||
|
||||
textEvent.rangeCount = textRanges.Length();
|
||||
textEvent.rangeArray = textRanges.Elements();
|
||||
|
||||
textEvent.mFlags.mIsSynthesizedForTests = true;
|
||||
|
||||
nsEventStatus status;
|
||||
nsresult rv = widget->DispatchEvent(&textEvent, status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
NS_ADDREF(*aResult = new CompositionStringSynthesizer(window));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -254,7 +254,7 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (reportViolation) {
|
||||
// TODO : FIX DATA in violation report.
|
||||
// TODO : need actual script sample in violation report.
|
||||
NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
|
||||
|
||||
// Get the calling location.
|
||||
@ -268,9 +268,9 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
|
||||
}
|
||||
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
NS_ConvertUTF8toUTF16(aFileName),
|
||||
scriptSample,
|
||||
lineNum);
|
||||
NS_ConvertUTF8toUTF16(aFileName),
|
||||
scriptSample,
|
||||
lineNum);
|
||||
}
|
||||
|
||||
if (!allowsEval) {
|
||||
|
@ -5,9 +5,6 @@
|
||||
|
||||
const { 'classes': Cc, 'interfaces': Ci } = Components;
|
||||
|
||||
// Instantiate nsIDOMScriptObjectFactory so that DOMException is usable in xpcshell
|
||||
Components.classesByID["{9eb760f0-4380-11d2-b328-00805f8a3859}"].getService(Ci.nsISupports);
|
||||
|
||||
function assert_equals(a, b, msg) {
|
||||
dump("assert_equals(" + a + ", " + b + ", \"" + msg + "\")");
|
||||
do_check_eq(a, b, Components.stack.caller);
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIDiskSpaceWatcher.h"
|
||||
#include "nsIDOMScriptObjectFactory.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStorage.h"
|
||||
#include "nsIObserverService.h"
|
||||
@ -40,8 +39,6 @@ USING_INDEXEDDB_NAMESPACE
|
||||
using namespace mozilla::dom;
|
||||
USING_QUOTA_NAMESPACE
|
||||
|
||||
static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
|
||||
|
||||
BEGIN_INDEXEDDB_NAMESPACE
|
||||
|
||||
class FileManagerInfo
|
||||
@ -606,11 +603,6 @@ IndexedDatabaseManager::InitWindowless(const jsval& aObj, JSContext* aCx)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Instantiating this class will register exception providers so even
|
||||
// in xpcshell we will get typed (dom) exceptions, instead of general
|
||||
// exceptions.
|
||||
nsCOMPtr<nsIDOMScriptObjectFactory> sof(do_GetService(kDOMSOF_CID));
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
|
||||
NS_ASSERTION(global, "What?! No global!");
|
||||
|
||||
|
@ -11,6 +11,7 @@ XPIDL_SOURCES += [
|
||||
'nsIDOMApplicationRegistry.idl',
|
||||
'nsIDOMApplicationRegistry2.idl',
|
||||
'nsIDOMMozApplicationEvent.idl',
|
||||
'nsIInterAppCommService.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_apps'
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
interface nsIDOMDOMRequest;
|
||||
|
||||
[scriptable, uuid(8bdeef38-e9cd-46f8-b8de-ed9e6b4d01ea)]
|
||||
[scriptable, uuid(4081390c-08cf-11e3-9200-b3c0a8744b20)]
|
||||
interface mozIDOMApplication : nsISupports
|
||||
{
|
||||
readonly attribute jsval manifest;
|
||||
@ -90,6 +90,16 @@ interface mozIDOMApplication : nsISupports
|
||||
* onsuccess will be called once data is actually cleared.
|
||||
*/
|
||||
nsIDOMDOMRequest clearBrowserData();
|
||||
|
||||
/**
|
||||
* Inter-App Communication APIs.
|
||||
*
|
||||
* https://wiki.mozilla.org/WebAPI/Inter_App_Communication_Alt_proposal
|
||||
*/
|
||||
nsISupports connect(in DOMString keyword,
|
||||
[optional] in jsval rules); // nsISupports is a Promise.
|
||||
|
||||
nsISupports getConnections(); // nsISupports is a Promise.
|
||||
};
|
||||
|
||||
[scriptable, uuid(cf742022-5ba3-11e2-868f-03310341b006)]
|
||||
|
40
dom/interfaces/apps/nsIInterAppCommService.idl
Normal file
40
dom/interfaces/apps/nsIInterAppCommService.idl
Normal file
@ -0,0 +1,40 @@
|
||||
/* 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 "domstubs.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
/**
|
||||
* Implemented by the contract id @mozilla.org/inter-app-communication-service;1
|
||||
*
|
||||
* This interface contains helpers for Inter-App Communication API [1] related
|
||||
* purposes. A singleton service of this interface will be instantiated during
|
||||
* the system boot-up, which plays the role of the central service receiving
|
||||
* messages from and interacting with the content processes.
|
||||
*
|
||||
* [1] https://wiki.mozilla.org/WebAPI/Inter_App_Communication_Alt_proposal
|
||||
*/
|
||||
[scriptable, uuid(7fdd8b68-0b0a-11e3-9b4c-afbc236da250)]
|
||||
interface nsIInterAppCommService : nsISupports
|
||||
{
|
||||
/*
|
||||
* Registration of a page that wants to be connected to other apps through
|
||||
* the Inter-App Communication API.
|
||||
*
|
||||
* @param keyword The connection's keyword.
|
||||
* @param handlerPageURI The URI of the handler's page.
|
||||
* @param manifestURI The webapp's manifest URI.
|
||||
* @param description The connection's description.
|
||||
* @param appStatus The app status can be Ci.nsIPrincipal.APP_STATUS_[
|
||||
* NOT_INSTALLED, INSTALLED, PRIVILEGED, CERTIFIED].
|
||||
* @param rules The connection's rules.
|
||||
*/
|
||||
void registerConnection(in DOMString keyword,
|
||||
in nsIURI handlerPageURI,
|
||||
in nsIURI manifestURI,
|
||||
in DOMString description,
|
||||
in unsigned short appStatus,
|
||||
in jsval rules);
|
||||
};
|
@ -7,6 +7,7 @@
|
||||
XPIDL_SOURCES += [
|
||||
'domstubs.idl',
|
||||
'nsIBrowserDOMWindow.idl',
|
||||
'nsICompositionStringSynthesizer.idl',
|
||||
'nsIContentPermissionPrompt.idl',
|
||||
'nsIContentPrefService.idl',
|
||||
'nsIContentPrefService2.idl',
|
||||
|
53
dom/interfaces/base/nsICompositionStringSynthesizer.idl
Normal file
53
dom/interfaces/base/nsICompositionStringSynthesizer.idl
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* Stores composition clauses information and caret information for synthesizing
|
||||
* composition string.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(9a7d7851-8c0a-4061-9edc-60d6693f86c9)]
|
||||
interface nsICompositionStringSynthesizer : nsISupports
|
||||
{
|
||||
/**
|
||||
* Set composition string or committed string.
|
||||
*/
|
||||
void setString(in AString aString);
|
||||
|
||||
// NOTE: These values must be same to NS_TEXTRANGE_* in nsGUIEvent.h
|
||||
const unsigned long ATTR_RAWINPUT = 0x02;
|
||||
const unsigned long ATTR_SELECTEDRAWTEXT = 0x03;
|
||||
const unsigned long ATTR_CONVERTEDTEXT = 0x04;
|
||||
const unsigned long ATTR_SELECTEDCONVERTEDTEXT = 0x05;
|
||||
|
||||
/**
|
||||
* Append a clause.
|
||||
*
|
||||
* TODO: Should be able to specify custom clause style.
|
||||
*/
|
||||
void appendClause(in unsigned long aLength,
|
||||
in unsigned long aAttribute);
|
||||
|
||||
/**
|
||||
* Set caret information.
|
||||
*/
|
||||
void setCaret(in unsigned long aOffset,
|
||||
in unsigned long aLength);
|
||||
|
||||
/**
|
||||
* Synthesize composition string with given information by dispatching
|
||||
* a proper event.
|
||||
*
|
||||
* If clauses have never been set, this dispatches a commit event.
|
||||
* If clauses are not filled all over the composition string, this throw an
|
||||
* error.
|
||||
*
|
||||
* After dispatching event, this clears all the information about the
|
||||
* composition string. So, you can reuse this instance.
|
||||
*/
|
||||
bool dispatchEvent();
|
||||
};
|
@ -41,8 +41,9 @@ interface nsIDOMClientRect;
|
||||
interface nsIURI;
|
||||
interface nsIDOMEventTarget;
|
||||
interface nsIRunnable;
|
||||
interface nsICompositionStringSynthesizer;
|
||||
|
||||
[scriptable, uuid(d18a8d69-7609-4165-ae20-af8aead36833)]
|
||||
[scriptable, uuid(dd45c6ae-9d80-46ef-86d7-f2795a48a77b)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -787,44 +788,12 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
in AString aLocale);
|
||||
|
||||
/**
|
||||
* Synthesize a text event to the window.
|
||||
* Creating synthesizer of composition string on the window.
|
||||
*
|
||||
* Cannot be accessed from unprivileged context (not content-accessible)
|
||||
* Will throw a DOM security error if called without chrome privileges.
|
||||
*
|
||||
* Currently, this method doesn't support 4 or more clauses composition
|
||||
* string.
|
||||
*
|
||||
* @param aCompositionString composition string
|
||||
* @param a*ClauseLengh the length of nth clause, set 0 when you
|
||||
* don't need second or third clause.
|
||||
* @param a*ClauseAttr the attribute of nth clause, uese following
|
||||
* const values.
|
||||
* @param aCaretStart the caret position in the composition string,
|
||||
* if you set negative value, this method don't
|
||||
* set the caret position to the event.
|
||||
* @param aCaretLength the caret length, if this is one or more,
|
||||
* the caret will be wide caret, otherwise,
|
||||
* it's collapsed.
|
||||
* XXX nsEditor doesn't support wide caret yet.
|
||||
*/
|
||||
|
||||
// NOTE: These values must be same to NS_TEXTRANGE_* in nsGUIEvent.h
|
||||
|
||||
const unsigned long COMPOSITION_ATTR_RAWINPUT = 0x02;
|
||||
const unsigned long COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
|
||||
const unsigned long COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
|
||||
const unsigned long COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
|
||||
|
||||
void sendTextEvent(in AString aCompositionString,
|
||||
in long aFirstClauseLength,
|
||||
in unsigned long aFirstClauseAttr,
|
||||
in long aSecondClauseLength,
|
||||
in unsigned long aSecondClauseAttr,
|
||||
in long aThirdClauseLength,
|
||||
in unsigned long aThirdClauseAttr,
|
||||
in long aCaretStart,
|
||||
in long aCaretLength);
|
||||
nsICompositionStringSynthesizer createCompositionStringSynthesizer();
|
||||
|
||||
/**
|
||||
* Synthesize a query content event. Note that the result value returned here
|
||||
|
@ -55,6 +55,7 @@ this.SystemMessagePermissionsTable = {
|
||||
"bluetooth-opp-transfer-start": {
|
||||
"bluetooth": []
|
||||
},
|
||||
"connection": { },
|
||||
"headset-button": { },
|
||||
"icc-stkcommand": {
|
||||
"settings": ["read", "write"]
|
||||
|
@ -369,11 +369,14 @@ nsGeolocationRequest::Notify(nsITimer* aTimer)
|
||||
{
|
||||
MOZ_ASSERT(!mShutdown, "timeout after shutdown");
|
||||
|
||||
NotifyError(nsIDOMGeoPositionError::TIMEOUT);
|
||||
if (!mIsWatchPositionRequest) {
|
||||
Shutdown();
|
||||
mLocator->RemoveRequest(this);
|
||||
} else if (!mShutdown) {
|
||||
}
|
||||
|
||||
NotifyError(nsIDOMGeoPositionError::TIMEOUT);
|
||||
|
||||
if (!mShutdown) {
|
||||
SetTimeoutTimer();
|
||||
}
|
||||
|
||||
@ -534,6 +537,11 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
|
||||
}
|
||||
|
||||
mLocator->SetCachedPosition(wrapped);
|
||||
if (!mIsWatchPositionRequest) {
|
||||
// Cancel timer and position updates in case the position
|
||||
// callback spins the event loop
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
// Ensure that the proper context is on the stack (bug 452762)
|
||||
nsCxPusher pusher;
|
||||
@ -552,9 +560,10 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
|
||||
callback->HandleEvent(aPosition);
|
||||
}
|
||||
|
||||
if (!mIsWatchPositionRequest) {
|
||||
Shutdown();
|
||||
} else if (!mShutdown) { // The handler may have called clearWatch
|
||||
if (!mShutdown) {
|
||||
// For watch requests, the handler may have called clearWatch
|
||||
MOZ_ASSERT(mIsWatchPositionRequest,
|
||||
"non-shutdown getCurrentPosition request after callback!");
|
||||
SetTimeoutTimer();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Sntp.jsm");
|
||||
|
||||
var RIL = {};
|
||||
Cu.import("resource://gre/modules/ril_consts.js", RIL);
|
||||
@ -58,8 +59,10 @@ const kMozSettingsChangedObserverTopic = "mozsettings-changed";
|
||||
const kSysMsgListenerReadyObserverTopic = "system-message-listener-ready";
|
||||
const kSysClockChangeObserverTopic = "system-clock-change";
|
||||
const kScreenStateChangedTopic = "screen-state-changed";
|
||||
const kTimeNitzAutomaticUpdateEnabled = "time.nitz.automatic-update.enabled";
|
||||
const kTimeNitzAvailable = "time.nitz.available";
|
||||
const kClockAutoUpdateEnabled = "time.clock.automatic-update.enabled";
|
||||
const kClockAutoUpdateAvailable = "time.clock.automatic-update.available";
|
||||
const kTimezoneAutoUpdateEnabled = "time.timezone.automatic-update.enabled";
|
||||
const kTimezoneAutoUpdateAvailable = "time.timezone.automatic-update.available";
|
||||
const kCellBroadcastSearchList = "ril.cellbroadcast.searchlist";
|
||||
const kCellBroadcastDisabled = "ril.cellbroadcast.disabled";
|
||||
const kPrefenceChangedObserverTopic = "nsPref:changed";
|
||||
@ -709,12 +712,19 @@ function RadioInterface(options) {
|
||||
lock.get("ril.data.enabled", this);
|
||||
lock.get("ril.data.apnSettings", this);
|
||||
|
||||
// Read the 'time.nitz.automatic-update.enabled' setting to see if
|
||||
// we need to adjust the system clock time and time zone by NITZ.
|
||||
lock.get(kTimeNitzAutomaticUpdateEnabled, this);
|
||||
// Read the 'time.clock.automatic-update.enabled' setting to see if
|
||||
// we need to adjust the system clock time by NITZ or SNTP.
|
||||
lock.get(kClockAutoUpdateEnabled, this);
|
||||
|
||||
// Set "time.nitz.available" to false when starting up.
|
||||
this.setNitzAvailable(false);
|
||||
// Read the 'time.timezone.automatic-update.enabled' setting to see if
|
||||
// we need to adjust the system timezone by NITZ.
|
||||
lock.get(kTimezoneAutoUpdateEnabled, this);
|
||||
|
||||
// Set "time.clock.automatic-update.available" to false when starting up.
|
||||
this.setClockAutoUpdateAvailable(false);
|
||||
|
||||
// Set "time.timezone.automatic-update.available" to false when starting up.
|
||||
this.setTimezoneAutoUpdateAvailable(false);
|
||||
|
||||
// Read the Cell Broadcast Search List setting, string of integers or integer
|
||||
// ranges separated by comma, to set listening channels.
|
||||
@ -726,11 +736,20 @@ function RadioInterface(options) {
|
||||
Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
|
||||
Services.obs.addObserver(this, kScreenStateChangedTopic, false);
|
||||
|
||||
Services.obs.addObserver(this, kNetworkInterfaceStateChangedTopic, false);
|
||||
Services.prefs.addObserver(kCellBroadcastDisabled, this, false);
|
||||
|
||||
this.portAddressedSmsApps = {};
|
||||
this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
|
||||
|
||||
this._sntp = new Sntp(this.setClockBySntp.bind(this),
|
||||
Services.prefs.getIntPref('network.sntp.maxRetryCount'),
|
||||
Services.prefs.getIntPref('network.sntp.refreshPeriod'),
|
||||
Services.prefs.getIntPref('network.sntp.timeout'),
|
||||
Services.prefs.getCharPref('network.sntp.pools').split(';'),
|
||||
Services.prefs.getIntPref('network.sntp.port'));
|
||||
}
|
||||
|
||||
RadioInterface.prototype = {
|
||||
|
||||
classID: RADIOINTERFACE_CID,
|
||||
@ -1860,22 +1879,35 @@ RadioInterface.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the setting value of "time.nitz.available".
|
||||
* Set the setting value of "time.clock.automatic-update.available".
|
||||
*/
|
||||
setNitzAvailable: function setNitzAvailable(value) {
|
||||
gSettingsService.createLock().set(kTimeNitzAvailable, value, null,
|
||||
setClockAutoUpdateAvailable: function setClockAutoUpdateAvailable(value) {
|
||||
gSettingsService.createLock().set(kClockAutoUpdateAvailable, value, null,
|
||||
"fromInternalSetting");
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the NITZ message in our system time.
|
||||
* Set the setting value of "time.timezone.automatic-update.available".
|
||||
*/
|
||||
setNitzTime: function setNitzTime(message) {
|
||||
setTimezoneAutoUpdateAvailable: function setTimezoneAutoUpdateAvailable(value) {
|
||||
gSettingsService.createLock().set(kTimezoneAutoUpdateAvailable, value, null,
|
||||
"fromInternalSetting");
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the system clock by NITZ.
|
||||
*/
|
||||
setClockByNitz: function setClockByNitz(message) {
|
||||
// To set the system clock time. Note that there could be a time diff
|
||||
// between when the NITZ was received and when the time is actually set.
|
||||
gTimeService.set(
|
||||
message.networkTimeInMS + (Date.now() - message.receiveTimeInMS));
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the system time zone by NITZ.
|
||||
*/
|
||||
setTimezoneByNitz: function setTimezoneByNitz(message) {
|
||||
// To set the sytem timezone. Note that we need to convert the time zone
|
||||
// value to a UTC repesentation string in the format of "UTC(+/-)hh:mm".
|
||||
// Ex, time zone -480 is "UTC-08:00"; time zone 630 is "UTC+10:30".
|
||||
@ -1898,15 +1930,36 @@ RadioInterface.prototype = {
|
||||
*/
|
||||
handleNitzTime: function handleNitzTime(message) {
|
||||
// Got the NITZ info received from the ril_worker.
|
||||
this.setNitzAvailable(true);
|
||||
this.setClockAutoUpdateAvailable(true);
|
||||
this.setTimezoneAutoUpdateAvailable(true);
|
||||
|
||||
// Cache the latest NITZ message whenever receiving it.
|
||||
this._lastNitzMessage = message;
|
||||
|
||||
// Set the received NITZ time if the setting is enabled.
|
||||
if (this._nitzAutomaticUpdateEnabled) {
|
||||
this.setNitzTime(message);
|
||||
// Set the received NITZ clock if the setting is enabled.
|
||||
if (this._clockAutoUpdateEnabled) {
|
||||
this.setClockByNitz(message);
|
||||
}
|
||||
// Set the received NITZ timezone if the setting is enabled.
|
||||
if (this._timezoneAutoUpdateEnabled) {
|
||||
this.setTimezoneByNitz(message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the system clock by SNTP.
|
||||
*/
|
||||
setClockBySntp: function setClockBySntp(offset) {
|
||||
// Got the SNTP info.
|
||||
this.setClockAutoUpdateAvailable(true);
|
||||
if (!this._clockAutoUpdateEnabled) {
|
||||
return;
|
||||
}
|
||||
if (this._lastNitzMessage) {
|
||||
debug("SNTP: NITZ available, discard SNTP");
|
||||
return;
|
||||
}
|
||||
gTimeService.set(Date.now() + offset);
|
||||
},
|
||||
|
||||
handleIccMbdn: function handleIccMbdn(message) {
|
||||
@ -2023,11 +2076,24 @@ RadioInterface.prototype = {
|
||||
Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
|
||||
Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
|
||||
Services.obs.removeObserver(this, kScreenStateChangedTopic);
|
||||
Services.obs.removeObserver(this, kNetworkInterfaceStateChangedTopic);
|
||||
Services.prefs.removeObserver(kCellBroadcastDisabled, this);
|
||||
break;
|
||||
case kSysClockChangeObserverTopic:
|
||||
let offset = parseInt(data, 10);
|
||||
if (this._lastNitzMessage) {
|
||||
this._lastNitzMessage.receiveTimeInMS += parseInt(data, 10);
|
||||
this._lastNitzMessage.receiveTimeInMS += offset;
|
||||
}
|
||||
this._sntp.updateOffset(offset);
|
||||
break;
|
||||
case kNetworkInterfaceStateChangedTopic:
|
||||
let network = subject.QueryInterface(Ci.nsINetworkInterface);
|
||||
if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
|
||||
// Check SNTP when we have data connection, this may not take
|
||||
// effect immediately before the setting get enabled.
|
||||
if (this._sntp.isExpired()) {
|
||||
this._sntp.request();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kScreenStateChangedTopic:
|
||||
@ -2053,28 +2119,51 @@ RadioInterface.prototype = {
|
||||
|
||||
apnSettings: null,
|
||||
|
||||
// Flag to determine whether to use NITZ. It corresponds to the
|
||||
// 'time.nitz.automatic-update.enabled' setting from the UI.
|
||||
_nitzAutomaticUpdateEnabled: null,
|
||||
// Flag to determine whether to update system clock automatically. It
|
||||
// corresponds to the 'time.clock.automatic-update.enabled' setting.
|
||||
_clockAutoUpdateEnabled: null,
|
||||
|
||||
// Flag to determine whether to update system timezone automatically. It
|
||||
// corresponds to the 'time.clock.automatic-update.enabled' setting.
|
||||
_timezoneAutoUpdateEnabled: null,
|
||||
|
||||
// Remember the last NITZ message so that we can set the time based on
|
||||
// the network immediately when users enable network-based time.
|
||||
_lastNitzMessage: null,
|
||||
|
||||
// Object that handles SNTP.
|
||||
_sntp: null,
|
||||
|
||||
// Cell Broadcast settings values.
|
||||
_cellBroadcastSearchListStr: null,
|
||||
|
||||
handleSettingsChange: function handleSettingsChange(aName, aResult, aMessage) {
|
||||
// Don't allow any content processes to modify the setting
|
||||
// "time.nitz.available" except for the chrome process.
|
||||
let isNitzAvailable = (this._lastNitzMessage !== null);
|
||||
if (aName === kTimeNitzAvailable && aMessage !== "fromInternalSetting" &&
|
||||
aResult !== isNitzAvailable) {
|
||||
if (DEBUG) {
|
||||
this.debug("Content processes cannot modify 'time.nitz.available'. Restore!");
|
||||
// "time.clock.automatic-update.available" except for the chrome process.
|
||||
if (aName === kClockAutoUpdateAvailable &&
|
||||
aMessage !== "fromInternalSetting") {
|
||||
let isClockAutoUpdateAvailable = this._lastNitzMessage !== null ||
|
||||
this._sntp.isAvailable();
|
||||
if (aResult !== isClockAutoUpdateAvailable) {
|
||||
debug("Content processes cannot modify 'time.clock.automatic-update.available'. Restore!");
|
||||
// Restore the setting to the current value.
|
||||
this.setClockAutoUpdateAvailable(isClockAutoUpdateAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't allow any content processes to modify the setting
|
||||
// "time.timezone.automatic-update.available" except for the chrome
|
||||
// process.
|
||||
if (aName === kTimezoneAutoUpdateAvailable &&
|
||||
aMessage !== "fromInternalSetting") {
|
||||
let isTimezoneAutoUpdateAvailable = this._lastNitzMessage !== null;
|
||||
if (aResult !== isTimezoneAutoUpdateAvailable) {
|
||||
if (DEBUG) {
|
||||
this.debug("Content processes cannot modify 'time.timezone.automatic-update.available'. Restore!");
|
||||
}
|
||||
// Restore the setting to the current value.
|
||||
this.setTimezoneAutoUpdateAvailable(isTimezoneAutoUpdateAvailable);
|
||||
}
|
||||
// Restore the setting to the current value.
|
||||
this.setNitzAvailable(isNitzAvailable);
|
||||
}
|
||||
|
||||
this.handle(aName, aResult);
|
||||
@ -2117,12 +2206,34 @@ RadioInterface.prototype = {
|
||||
this.updateRILNetworkInterface();
|
||||
}
|
||||
break;
|
||||
case kTimeNitzAutomaticUpdateEnabled:
|
||||
this._nitzAutomaticUpdateEnabled = aResult;
|
||||
case kClockAutoUpdateEnabled:
|
||||
this._clockAutoUpdateEnabled = aResult;
|
||||
if (!this._clockAutoUpdateEnabled) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the latest cached NITZ time if the setting is enabled.
|
||||
if (this._nitzAutomaticUpdateEnabled && this._lastNitzMessage) {
|
||||
this.setNitzTime(this._lastNitzMessage);
|
||||
// Set the latest cached NITZ time if it's available.
|
||||
if (this._lastNitzMessage) {
|
||||
this.setClockByNitz(this._lastNitzMessage);
|
||||
} else if (gNetworkManager.active && gNetworkManager.active.state ==
|
||||
Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
|
||||
// Set the latest cached SNTP time if it's available.
|
||||
if (!this._sntp.isExpired()) {
|
||||
this.setClockBySntp(this._sntp.getOffset());
|
||||
} else {
|
||||
// Or refresh the SNTP.
|
||||
this._sntp.request();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kTimezoneAutoUpdateEnabled:
|
||||
this._timezoneAutoUpdateEnabled = aResult;
|
||||
|
||||
if (this._timezoneAutoUpdateEnabled) {
|
||||
// Apply the latest cached NITZ for timezone if it's available.
|
||||
if (this._timezoneAutoUpdateEnabled && this._lastNitzMessage) {
|
||||
this.setTimezoneByNitz(this._lastNitzMessage);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kCellBroadcastSearchList:
|
||||
|
@ -11,6 +11,7 @@ MOCHITEST_FILES = \
|
||||
test_clearWatch.html \
|
||||
test_clearWatch_invalid.html \
|
||||
test_geolocation_is_undefined_when_pref_is_off.html \
|
||||
test_handlerSpinsEventLoop.html \
|
||||
test_manyCurrentConcurrent.html \
|
||||
test_manyCurrentSerial.html \
|
||||
test_manyWatchConcurrent.html \
|
||||
|
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=911595
|
||||
-->
|
||||
<head>
|
||||
<title>Test for spinning the event loop inside position handlers</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="geolocation_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=911595 ">Mozilla Bug 911595</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/*
|
||||
* In bug 911595 , spinning the event loop from inside position
|
||||
* handlers could cause both success and error callbacks to be
|
||||
* fired for the same request if that request has a small timeout.
|
||||
*/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
resume_geolocationProvider(function() {
|
||||
force_prompt(true, test1);
|
||||
});
|
||||
|
||||
function spinEventLoopAndSetTimeout() {
|
||||
if (successCallbackCalled || errorCallbackCalled) {
|
||||
// this should only be called once from either callback
|
||||
return;
|
||||
}
|
||||
|
||||
window.showModalDialog("javascript:window.close()");
|
||||
|
||||
setTimeout(function() {
|
||||
ok(successCallbackCalled != errorCallbackCalled, "Ensure only one callback is called");
|
||||
SimpleTest.finish();
|
||||
}, 5);
|
||||
}
|
||||
|
||||
var successCallbackCalled = false;
|
||||
function successCallback(position) {
|
||||
spinEventLoopAndSetTimeout();
|
||||
successCallbackCalled = true;
|
||||
}
|
||||
|
||||
var errorCallbackCalled = false;
|
||||
function errorCallback(error) {
|
||||
spinEventLoopAndSetTimeout();
|
||||
errorCallbackCalled = true;
|
||||
}
|
||||
|
||||
function test1() {
|
||||
navigator.geolocation.getCurrentPosition(successCallback, errorCallback, {timeout: 1});
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
15
dom/webidl/InterAppConnection.webidl
Normal file
15
dom/webidl/InterAppConnection.webidl
Normal file
@ -0,0 +1,15 @@
|
||||
/* 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/. */
|
||||
|
||||
[HeaderFile="mozilla/dom/InterAppComm.h",
|
||||
Func="mozilla::dom::InterAppComm::EnabledForScope",
|
||||
Constructor(DOMString keyword, DOMString publisher, DOMString subsriber),
|
||||
JSImplementation="@mozilla.org/dom/inter-app-connection;1"]
|
||||
interface MozInterAppConnection {
|
||||
readonly attribute DOMString keyword;
|
||||
readonly attribute DOMString publisher;
|
||||
readonly attribute DOMString subscriber;
|
||||
|
||||
void cancel();
|
||||
};
|
13
dom/webidl/InterAppConnectionRequest.webidl
Normal file
13
dom/webidl/InterAppConnectionRequest.webidl
Normal file
@ -0,0 +1,13 @@
|
||||
/* 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/. */
|
||||
|
||||
[HeaderFile="mozilla/dom/InterAppComm.h",
|
||||
Func="mozilla::dom::InterAppComm::EnabledForScope",
|
||||
Constructor(DOMString keyword, MozInterAppMessagePort port),
|
||||
JSImplementation="@mozilla.org/dom/inter-app-connection-request;1"]
|
||||
interface MozInterAppConnectionRequest {
|
||||
readonly attribute DOMString keyword;
|
||||
|
||||
readonly attribute MozInterAppMessagePort port;
|
||||
};
|
16
dom/webidl/InterAppMessageEvent.webidl
Normal file
16
dom/webidl/InterAppMessageEvent.webidl
Normal file
@ -0,0 +1,16 @@
|
||||
/* 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/. */
|
||||
|
||||
dictionary MozInterAppMessageEventInit : EventInit {
|
||||
any data;
|
||||
};
|
||||
|
||||
[HeaderFile="mozilla/dom/InterAppComm.h",
|
||||
Func="mozilla::dom::InterAppComm::EnabledForScope",
|
||||
Constructor(DOMString type,
|
||||
optional MozInterAppMessageEventInit eventInitDict),
|
||||
JSImplementation="@mozilla.org/dom/inter-app-message-event;1"]
|
||||
interface MozInterAppMessageEvent : Event {
|
||||
readonly attribute any data;
|
||||
};
|
24
dom/webidl/InterAppMessagePort.webidl
Normal file
24
dom/webidl/InterAppMessagePort.webidl
Normal file
@ -0,0 +1,24 @@
|
||||
/* 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/. */
|
||||
|
||||
// TODO Bug 907060 Per off-line discussion, after the MessagePort is done
|
||||
// at Bug 643325, we will start to refactorize the common logic of both
|
||||
// Inter-App Communication and Shared Worker. For now, we hope to design an
|
||||
// MozInterAppMessagePort to meet the timeline, which still follows exactly
|
||||
// the same interface and semantic as the MessagePort is. In the future,
|
||||
// we can then align it back to MessagePort with backward compatibility.
|
||||
|
||||
[HeaderFile="mozilla/dom/InterAppComm.h",
|
||||
Func="mozilla::dom::InterAppComm::EnabledForScope",
|
||||
Constructor(DOMString messagePortID),
|
||||
JSImplementation="@mozilla.org/dom/inter-app-message-port;1"]
|
||||
interface MozInterAppMessagePort : EventTarget {
|
||||
void postMessage(any message);
|
||||
|
||||
void start();
|
||||
|
||||
void close();
|
||||
|
||||
attribute EventHandler onmessage;
|
||||
};
|
@ -186,6 +186,10 @@ WEBIDL_FILES = [
|
||||
'ImageData.webidl',
|
||||
'ImageDocument.webidl',
|
||||
'InspectorUtils.webidl',
|
||||
'InterAppConnection.webidl',
|
||||
'InterAppConnectionRequest.webidl',
|
||||
'InterAppMessageEvent.webidl',
|
||||
'InterAppMessagePort.webidl',
|
||||
'KeyboardEvent.webidl',
|
||||
'KeyEvent.webidl',
|
||||
'LinkStyle.webidl',
|
||||
|
@ -674,8 +674,10 @@ public:
|
||||
if (csp) {
|
||||
NS_NAMED_LITERAL_STRING(scriptSample,
|
||||
"Call to eval() or related function blocked by CSP.");
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
mFileName, scriptSample, mLineNum);
|
||||
if (mWorkerPrivate->GetReportCSPViolations()) {
|
||||
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
|
||||
mFileName, scriptSample, mLineNum);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<LogViolationDetailsResponseRunnable> response =
|
||||
|
@ -219,7 +219,6 @@ function runTests()
|
||||
}
|
||||
|
||||
// IME
|
||||
const nsIDOMWindowUtils = Components.interfaces.nsIDOMWindowUtils;
|
||||
// start composition
|
||||
synthesizeComposition({ type: "compositionstart" });
|
||||
// input first character
|
||||
@ -229,7 +228,7 @@ function runTests()
|
||||
{ "string": "\u3089",
|
||||
"clauses":
|
||||
[
|
||||
{ "length": 1, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
|
||||
{ "length": 1, "attr": COMPOSITION_ATTR_RAWINPUT }
|
||||
]
|
||||
},
|
||||
"caret": { "start": 1, "length": 0 }
|
||||
|
@ -53,7 +53,9 @@ GrallocImage::GrallocImage()
|
||||
|
||||
GrallocImage::~GrallocImage()
|
||||
{
|
||||
if (mGraphicBuffer.get()) {
|
||||
// If we have a texture client, the latter takes over the responsibility to
|
||||
// unlock the GraphicBufferLocked.
|
||||
if (mGraphicBuffer.get() && !mTextureClient) {
|
||||
mGraphicBuffer->Unlock();
|
||||
if (mBufferAllocated) {
|
||||
ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
|
||||
@ -294,6 +296,7 @@ GrallocImage::GetTextureClient()
|
||||
mTextureClient = new GrallocTextureClientOGL(actor,
|
||||
gfx::ToIntSize(mSize),
|
||||
flags);
|
||||
mTextureClient->SetGraphicBufferLocked(mGraphicBuffer);
|
||||
}
|
||||
return mTextureClient;
|
||||
}
|
||||
|
@ -177,21 +177,25 @@ ImageContainer::SetCurrentImageInternal(Image *aImage)
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::SetCurrentImage(Image *aImage)
|
||||
ImageContainer::ClearCurrentImage()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
if (IsAsync()) {
|
||||
if (aImage) {
|
||||
ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
|
||||
} else {
|
||||
// here we used to have a SetIdle() call on the image bridge to tell
|
||||
// the compositor that the video element is not going to be seen for
|
||||
// moment and that it can release its shared memory. It was causing
|
||||
// crashes so it has been removed.
|
||||
// This may be reimplemented after 858914 lands.
|
||||
}
|
||||
SetCurrentImageInternal(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::SetCurrentImage(Image *aImage)
|
||||
{
|
||||
if (IsAsync() && !aImage) {
|
||||
// Let ImageClient to release all TextureClients.
|
||||
ImageBridgeChild::FlushImage(mImageClient, this);
|
||||
return;
|
||||
}
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
if (IsAsync()) {
|
||||
ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
|
||||
}
|
||||
SetCurrentImageInternal(aImage);
|
||||
}
|
||||
|
||||
|
@ -333,6 +333,14 @@ public:
|
||||
*/
|
||||
void SetCurrentImage(Image* aImage);
|
||||
|
||||
/**
|
||||
* Clear the current image.
|
||||
* This function is expect to be called only from a CompositableClient
|
||||
* that belongs to ImageBridgeChild. Created to prevent dead lock.
|
||||
* See Bug 901224.
|
||||
*/
|
||||
void ClearCurrentImage();
|
||||
|
||||
/**
|
||||
* Set an Image as the current image to display. The Image must have
|
||||
* been created by this ImageContainer.
|
||||
|
@ -1173,9 +1173,13 @@ Layer::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml)
|
||||
fprintf(aFile, ">");
|
||||
}
|
||||
DumpSelf(aFile, aPrefix);
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
if (AsLayerComposite() && AsLayerComposite()->GetCompositableHost()) {
|
||||
AsLayerComposite()->GetCompositableHost()->Dump(aFile, aPrefix, aDumpHtml);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aDumpHtml) {
|
||||
fprintf(aFile, "</a>");
|
||||
}
|
||||
|
@ -337,6 +337,10 @@ ClientLayerManager::ForwardTransaction()
|
||||
// glue code here to find the TextureClient and invoke a callback to
|
||||
// let the camera know that the gralloc buffer is not used anymore on
|
||||
// the compositor side and that it can reuse it.
|
||||
const ReplyTextureRemoved& rep = reply.get_ReplyTextureRemoved();
|
||||
CompositableClient* compositable
|
||||
= static_cast<CompositableChild*>(rep.compositableChild())->GetCompositableClient();
|
||||
compositable->OnReplyTextureRemoved(rep.textureId());
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -32,9 +32,26 @@ CompositableClient::~CompositableClient()
|
||||
{
|
||||
MOZ_COUNT_DTOR(CompositableClient);
|
||||
Destroy();
|
||||
|
||||
FlushTexturesToRemoveCallbacks();
|
||||
|
||||
MOZ_ASSERT(mTexturesToRemove.Length() == 0, "would leak textures pending for deletion");
|
||||
}
|
||||
|
||||
void
|
||||
CompositableClient::FlushTexturesToRemoveCallbacks()
|
||||
{
|
||||
std::map<uint64_t,TextureClientData*>::iterator it
|
||||
= mTexturesToRemoveCallbacks.begin();
|
||||
std::map<uint64_t,TextureClientData*>::iterator stop
|
||||
= mTexturesToRemoveCallbacks.end();
|
||||
for (; it != stop; ++it) {
|
||||
it->second->DeallocateSharedData(GetForwarder());
|
||||
delete it->second;
|
||||
}
|
||||
mTexturesToRemoveCallbacks.clear();
|
||||
}
|
||||
|
||||
LayersBackend
|
||||
CompositableClient::GetCompositorBackendType() const
|
||||
{
|
||||
@ -206,16 +223,36 @@ void
|
||||
CompositableClient::RemoveTextureClient(TextureClient* aClient)
|
||||
{
|
||||
MOZ_ASSERT(aClient);
|
||||
mTexturesToRemove.AppendElement(aClient->GetID());
|
||||
mTexturesToRemove.AppendElement(TextureIDAndFlags(aClient->GetID(),
|
||||
aClient->GetFlags()));
|
||||
if (!(aClient->GetFlags() & TEXTURE_DEALLOCATE_HOST)) {
|
||||
TextureClientData* data = aClient->DropTextureData();
|
||||
if (data) {
|
||||
mTexturesToRemoveCallbacks[aClient->GetID()] = data;
|
||||
}
|
||||
}
|
||||
aClient->ClearID();
|
||||
aClient->MarkInvalid();
|
||||
}
|
||||
|
||||
void
|
||||
CompositableClient::OnReplyTextureRemoved(uint64_t aTextureID)
|
||||
{
|
||||
std::map<uint64_t,TextureClientData*>::iterator it
|
||||
= mTexturesToRemoveCallbacks.find(aTextureID);
|
||||
if (it != mTexturesToRemoveCallbacks.end()) {
|
||||
it->second->DeallocateSharedData(GetForwarder());
|
||||
delete it->second;
|
||||
mTexturesToRemoveCallbacks.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositableClient::OnTransaction()
|
||||
{
|
||||
for (unsigned i = 0; i < mTexturesToRemove.Length(); ++i) {
|
||||
mForwarder->RemoveTexture(this, mTexturesToRemove[i]);
|
||||
const TextureIDAndFlags& texture = mTexturesToRemove[i];
|
||||
mForwarder->RemoveTexture(this, texture.mID, texture.mFlags);
|
||||
}
|
||||
mTexturesToRemove.Clear();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdint.h> // for uint64_t
|
||||
#include <vector> // for vector
|
||||
#include <map> // for map
|
||||
#include "mozilla/Assertions.h" // for MOZ_CRASH
|
||||
#include "mozilla/RefPtr.h" // for TemporaryRef, RefCounted
|
||||
#include "mozilla/gfx/Types.h" // for SurfaceFormat
|
||||
@ -27,6 +28,7 @@ class ImageBridgeChild;
|
||||
class CompositableForwarder;
|
||||
class CompositableChild;
|
||||
class SurfaceDescriptor;
|
||||
class TextureClientData;
|
||||
|
||||
/**
|
||||
* CompositableClient manages the texture-specific logic for composite layers,
|
||||
@ -137,9 +139,19 @@ public:
|
||||
*/
|
||||
virtual void OnDetach() {}
|
||||
|
||||
void OnReplyTextureRemoved(uint64_t aTextureID);
|
||||
|
||||
void FlushTexturesToRemoveCallbacks();
|
||||
protected:
|
||||
struct TextureIDAndFlags {
|
||||
TextureIDAndFlags(uint64_t aID, TextureFlags aFlags)
|
||||
: mID(aID), mFlags(aFlags) {}
|
||||
uint64_t mID;
|
||||
TextureFlags mFlags;
|
||||
};
|
||||
// The textures to destroy in the next transaction;
|
||||
nsTArray<uint64_t> mTexturesToRemove;
|
||||
nsTArray<TextureIDAndFlags> mTexturesToRemove;
|
||||
std::map<uint64_t, TextureClientData*> mTexturesToRemoveCallbacks;
|
||||
uint64_t mNextTextureID;
|
||||
CompositableChild* mCompositableChild;
|
||||
CompositableForwarder* mForwarder;
|
||||
|
@ -97,6 +97,28 @@ TextureInfo ImageClientSingle::GetTextureInfo() const
|
||||
return TextureInfo(COMPOSITABLE_IMAGE);
|
||||
}
|
||||
|
||||
void
|
||||
ImageClientSingle::FlushImage()
|
||||
{
|
||||
if (mFrontBuffer) {
|
||||
RemoveTextureClient(mFrontBuffer);
|
||||
mFrontBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageClientBuffered::FlushImage()
|
||||
{
|
||||
if (mFrontBuffer) {
|
||||
RemoveTextureClient(mFrontBuffer);
|
||||
mFrontBuffer = nullptr;
|
||||
}
|
||||
if (mBackBuffer) {
|
||||
RemoveTextureClient(mBackBuffer);
|
||||
mBackBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ImageClientSingle::UpdateImage(ImageContainer* aContainer,
|
||||
uint32_t aContentFlags)
|
||||
|
@ -62,6 +62,11 @@ public:
|
||||
virtual already_AddRefed<Image> CreateImage(const uint32_t *aFormats,
|
||||
uint32_t aNumFormats) = 0;
|
||||
|
||||
/**
|
||||
* Synchronously remove all the textures used by the image client.
|
||||
*/
|
||||
virtual void FlushImage() {}
|
||||
|
||||
protected:
|
||||
ImageClient(CompositableForwarder* aFwd, CompositableType aType);
|
||||
|
||||
@ -96,6 +101,9 @@ public:
|
||||
|
||||
virtual already_AddRefed<Image> CreateImage(const uint32_t *aFormats,
|
||||
uint32_t aNumFormats) MOZ_OVERRIDE;
|
||||
|
||||
virtual void FlushImage() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
RefPtr<TextureClient> mFrontBuffer;
|
||||
// Some layers may want to enforce some flags to all their textures
|
||||
@ -117,6 +125,8 @@ public:
|
||||
|
||||
virtual void OnDetach() MOZ_OVERRIDE;
|
||||
|
||||
virtual void FlushImage() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
RefPtr<TextureClient> mBackBuffer;
|
||||
};
|
||||
|
@ -35,6 +35,78 @@ using namespace mozilla::gl;
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class ShmemTextureClientData : public TextureClientData
|
||||
{
|
||||
public:
|
||||
ShmemTextureClientData(ipc::Shmem& aShmem)
|
||||
: mShmem(aShmem)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ShmemTextureClientData);
|
||||
}
|
||||
|
||||
~ShmemTextureClientData()
|
||||
{
|
||||
MOZ_COUNT_CTOR(ShmemTextureClientData);
|
||||
}
|
||||
|
||||
virtual void DeallocateSharedData(ISurfaceAllocator* allocator)
|
||||
{
|
||||
allocator->DeallocShmem(mShmem);
|
||||
mShmem = ipc::Shmem();
|
||||
}
|
||||
|
||||
private:
|
||||
ipc::Shmem mShmem;
|
||||
};
|
||||
|
||||
class MemoryTextureClientData : public TextureClientData
|
||||
{
|
||||
public:
|
||||
MemoryTextureClientData(uint8_t* aBuffer)
|
||||
: mBuffer(aBuffer)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MemoryTextureClientData);
|
||||
}
|
||||
|
||||
~MemoryTextureClientData()
|
||||
{
|
||||
MOZ_ASSERT(!mBuffer, "Forgot to deallocate the shared texture data?");
|
||||
MOZ_COUNT_CTOR(MemoryTextureClientData);
|
||||
}
|
||||
|
||||
virtual void DeallocateSharedData(ISurfaceAllocator*)
|
||||
{
|
||||
delete[] mBuffer;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t* mBuffer;
|
||||
};
|
||||
|
||||
TextureClientData*
|
||||
MemoryTextureClient::DropTextureData()
|
||||
{
|
||||
if (!mBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
TextureClientData* result = new MemoryTextureClientData(mBuffer);
|
||||
MarkInvalid();
|
||||
mBuffer = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
TextureClientData*
|
||||
ShmemTextureClient::DropTextureData()
|
||||
{
|
||||
if (!mShmem.IsReadable()) {
|
||||
return nullptr;
|
||||
}
|
||||
TextureClientData* result = new ShmemTextureClientData(mShmem);
|
||||
MarkInvalid();
|
||||
mShmem = ipc::Shmem();
|
||||
return result;
|
||||
}
|
||||
|
||||
TextureClient::TextureClient(TextureFlags aFlags)
|
||||
: mID(0)
|
||||
, mFlags(aFlags)
|
||||
@ -51,15 +123,11 @@ TextureClient::ShouldDeallocateInDestructor() const
|
||||
if (!IsAllocated()) {
|
||||
return false;
|
||||
}
|
||||
if (GetFlags() & TEXTURE_DEALLOCATE_CLIENT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're meant to be deallocated by the host,
|
||||
// but we haven't been shared yet, then we should
|
||||
// deallocate on the client instead.
|
||||
return (GetFlags() & TEXTURE_DEALLOCATE_HOST) &&
|
||||
!IsSharedWithCompositor();
|
||||
return !IsSharedWithCompositor();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -69,6 +69,28 @@ public:
|
||||
StereoMode aStereoMode) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds the shared data of a TextureClient, to be destroyed later.
|
||||
*
|
||||
* TextureClient's destructor initiates the destruction sequence of the
|
||||
* texture client/host pair. If the shared data is to be deallocated on the
|
||||
* host side, there is nothing to do.
|
||||
* On the other hand, if the client data must be deallocated on the client
|
||||
* side, the CompositableClient will ask the TextureClient to drop its shared
|
||||
* data in the form of a TextureClientData object. The compositable will keep
|
||||
* this object until it has received from the host side the confirmation that
|
||||
* the compositor is not using the texture and that it is completely safe to
|
||||
* deallocate the shared data.
|
||||
*
|
||||
* See:
|
||||
* - CompositableClient::RemoveTextureClient
|
||||
* - CompositableClient::OnReplyTextureRemoved
|
||||
*/
|
||||
class TextureClientData {
|
||||
public:
|
||||
virtual void DeallocateSharedData(ISurfaceAllocator* allocator) = 0;
|
||||
virtual ~TextureClientData() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* TextureClient is a thin abstraction over texture data that need to be shared
|
||||
@ -144,6 +166,8 @@ public:
|
||||
|
||||
virtual gfx::IntSize GetSize() const = 0;
|
||||
|
||||
virtual TextureClientData* DropTextureData() = 0;
|
||||
|
||||
TextureFlags GetFlags() const { return mFlags; }
|
||||
|
||||
/**
|
||||
@ -268,6 +292,8 @@ public:
|
||||
|
||||
virtual bool IsAllocated() const MOZ_OVERRIDE { return mAllocated; }
|
||||
|
||||
virtual TextureClientData* DropTextureData() MOZ_OVERRIDE;
|
||||
|
||||
ISurfaceAllocator* GetAllocator() const;
|
||||
|
||||
ipc::Shmem& GetShmem() { return mShmem; }
|
||||
@ -301,6 +327,8 @@ public:
|
||||
|
||||
virtual bool IsAllocated() const MOZ_OVERRIDE { return mBuffer != nullptr; }
|
||||
|
||||
virtual TextureClientData* DropTextureData() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
uint8_t* mBuffer;
|
||||
size_t mBufSize;
|
||||
|
@ -106,7 +106,7 @@ CanvasLayerComposite::RenderLayer(const nsIntPoint& aOffset,
|
||||
CompositableHost*
|
||||
CanvasLayerComposite::GetCompositableHost()
|
||||
{
|
||||
if (mImageHost->IsAttached()) {
|
||||
if ( mImageHost && mImageHost->IsAttached()) {
|
||||
return mImageHost.get();
|
||||
}
|
||||
|
||||
|
@ -261,7 +261,7 @@ public:
|
||||
|
||||
void AddTextureHost(TextureHost* aTexture);
|
||||
virtual void UseTextureHost(TextureHost* aTexture) {}
|
||||
void RemoveTextureHost(uint64_t aTextureID);
|
||||
virtual void RemoveTextureHost(uint64_t aTextureID);
|
||||
TextureHost* GetTextureHost(uint64_t aTextureID);
|
||||
|
||||
protected:
|
||||
|
@ -42,6 +42,15 @@ ImageHost::UseTextureHost(TextureHost* aTexture)
|
||||
mFrontBuffer = aTexture;
|
||||
}
|
||||
|
||||
void
|
||||
ImageHost::RemoveTextureHost(uint64_t aTextureID)
|
||||
{
|
||||
CompositableHost::RemoveTextureHost(aTextureID);
|
||||
if (mFrontBuffer && mFrontBuffer->GetID() == aTextureID) {
|
||||
mFrontBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TextureHost*
|
||||
ImageHost::GetTextureHost()
|
||||
{
|
||||
|
@ -57,6 +57,8 @@ public:
|
||||
|
||||
virtual void UseTextureHost(TextureHost* aTexture) MOZ_OVERRIDE;
|
||||
|
||||
virtual void RemoveTextureHost(uint64_t aTextureID) MOZ_OVERRIDE;
|
||||
|
||||
virtual TextureHost* GetTextureHost() MOZ_OVERRIDE;
|
||||
|
||||
virtual void SetPictureRect(const nsIntRect& aPictureRect) MOZ_OVERRIDE
|
||||
|
@ -158,7 +158,7 @@ ThebesLayerComposite::RenderLayer(const nsIntPoint& aOffset,
|
||||
CompositableHost*
|
||||
ThebesLayerComposite::GetCompositableHost()
|
||||
{
|
||||
if (mBuffer->IsAttached()) {
|
||||
if ( mBuffer && mBuffer->IsAttached()) {
|
||||
return mBuffer.get();
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ public:
|
||||
*/
|
||||
virtual void RemoveTexture(CompositableClient* aCompositable,
|
||||
uint64_t aTextureID,
|
||||
TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT) = 0;
|
||||
TextureFlags aFlags) = 0;
|
||||
|
||||
/**
|
||||
* Tell the CompositableHost on the compositor side what texture to use for
|
||||
|
@ -122,9 +122,17 @@ ImageBridgeChild::RemoveTexture(CompositableClient* aCompositable,
|
||||
uint64_t aTexture,
|
||||
TextureFlags aFlags)
|
||||
{
|
||||
mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
|
||||
aTexture,
|
||||
aFlags));
|
||||
if (aFlags & TEXTURE_DEALLOCATE_HOST) {
|
||||
// if deallocation happens on the host side, we don't need the transaction
|
||||
// to be synchronous.
|
||||
mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
|
||||
aTexture,
|
||||
aFlags));
|
||||
} else {
|
||||
mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
|
||||
aTexture,
|
||||
aFlags));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -393,6 +401,52 @@ void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
|
||||
nsRefPtr<ImageContainer> >(&UpdateImageClientNow, aClient, aContainer));
|
||||
}
|
||||
|
||||
static void FlushImageSync(ImageClient* aClient, ImageContainer* aContainer, ReentrantMonitor* aBarrier, bool* aDone)
|
||||
{
|
||||
ImageBridgeChild::FlushImageNow(aClient, aContainer);
|
||||
|
||||
ReentrantMonitorAutoEnter autoMon(*aBarrier);
|
||||
*aDone = true;
|
||||
aBarrier->NotifyAll();
|
||||
}
|
||||
|
||||
//static
|
||||
void ImageBridgeChild::FlushImage(ImageClient* aClient, ImageContainer* aContainer)
|
||||
{
|
||||
if (InImageBridgeChildThread()) {
|
||||
FlushImageNow(aClient, aContainer);
|
||||
return;
|
||||
}
|
||||
|
||||
ReentrantMonitor barrier("CreateImageClient Lock");
|
||||
ReentrantMonitorAutoEnter autoMon(barrier);
|
||||
bool done = false;
|
||||
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(&FlushImageSync, aClient, aContainer, &barrier, &done));
|
||||
|
||||
// should stop the thread until the ImageClient has been created on
|
||||
// the other thread
|
||||
while (!done) {
|
||||
barrier.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void ImageBridgeChild::FlushImageNow(ImageClient* aClient, ImageContainer* aContainer)
|
||||
{
|
||||
MOZ_ASSERT(aClient);
|
||||
sImageBridgeChildSingleton->BeginTransaction();
|
||||
if (aContainer) {
|
||||
aContainer->ClearCurrentImage();
|
||||
}
|
||||
aClient->FlushImage();
|
||||
aClient->OnTransaction();
|
||||
sImageBridgeChildSingleton->EndTransaction();
|
||||
aClient->FlushTexturesToRemoveCallbacks();
|
||||
}
|
||||
|
||||
void
|
||||
ImageBridgeChild::BeginTransaction()
|
||||
{
|
||||
@ -454,6 +508,10 @@ ImageBridgeChild::EndTransaction()
|
||||
// This would be, for instance, the place to implement a mechanism to
|
||||
// notify the B2G camera that the gralloc buffer is not used by the
|
||||
// compositor anymore and that it can be recycled.
|
||||
const ReplyTextureRemoved& rep = reply.get_ReplyTextureRemoved();
|
||||
CompositableClient* compositable
|
||||
= static_cast<CompositableChild*>(rep.compositableChild())->GetCompositableClient();
|
||||
compositable->OnReplyTextureRemoved(rep.textureId());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -239,6 +239,15 @@ public:
|
||||
static void DispatchReleaseImageClient(ImageClient* aClient);
|
||||
static void DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer);
|
||||
|
||||
/**
|
||||
* Flush all Images sent to CompositableHost.
|
||||
*/
|
||||
static void FlushImage(ImageClient* aClient, ImageContainer* aContainer);
|
||||
|
||||
/**
|
||||
* Must be called on the ImageBridgeChild's thread.
|
||||
*/
|
||||
static void FlushImageNow(ImageClient* aClient, ImageContainer* aContainer);
|
||||
|
||||
// CompositableForwarder
|
||||
|
||||
|
@ -79,7 +79,10 @@ public:
|
||||
mClientBounds = aClientBounds;
|
||||
mTargetOrientation = aOrientation;
|
||||
}
|
||||
|
||||
void MarkSyncTransaction()
|
||||
{
|
||||
mSwapRequired = true;
|
||||
}
|
||||
void AddEdit(const Edit& aEdit)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!Finished(), "forgot BeginTransaction?");
|
||||
@ -405,10 +408,12 @@ ShadowLayerForwarder::RemoveTexture(CompositableClient* aCompositable,
|
||||
uint64_t aTexture,
|
||||
TextureFlags aFlags)
|
||||
{
|
||||
mTxn->AddEdit(OpRemoveTexture(nullptr,
|
||||
aCompositable->GetIPDLActor(),
|
||||
aTexture,
|
||||
aFlags));
|
||||
mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
|
||||
aTexture,
|
||||
aFlags));
|
||||
if (!(aFlags & TEXTURE_DEALLOCATE_HOST)) {
|
||||
mTxn->MarkSyncTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -426,7 +431,6 @@ ShadowLayerForwarder::UpdatedTexture(CompositableClient* aCompositable,
|
||||
mTxn->AddNoSwapPaint(OpUpdateTexture(nullptr, aCompositable->GetIPDLActor(),
|
||||
aTexture->GetID(),
|
||||
region));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "mozilla/layers/CompositableForwarder.h"
|
||||
#include "mozilla/layers/ISurfaceAllocator.h"
|
||||
#include "mozilla/layers/ShadowLayerUtilsGralloc.h"
|
||||
#include "GrallocImages.h"
|
||||
#include "gfx2DGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -17,6 +18,75 @@ namespace layers {
|
||||
|
||||
using namespace android;
|
||||
|
||||
class GraphicBufferLockedTextureClientData : public TextureClientData {
|
||||
public:
|
||||
GraphicBufferLockedTextureClientData(GraphicBufferLocked* aBufferLocked)
|
||||
: mBufferLocked(aBufferLocked)
|
||||
{
|
||||
MOZ_COUNT_CTOR(GrallocTextureClientData);
|
||||
}
|
||||
|
||||
~GraphicBufferLockedTextureClientData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GrallocTextureClientData);
|
||||
MOZ_ASSERT(!mBufferLocked, "Forgot to unlock the GraphicBufferLocked?");
|
||||
}
|
||||
|
||||
virtual void DeallocateSharedData(ISurfaceAllocator*) MOZ_OVERRIDE
|
||||
{
|
||||
mBufferLocked->Unlock();
|
||||
mBufferLocked = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<GraphicBufferLocked> mBufferLocked;
|
||||
};
|
||||
|
||||
class GrallocTextureClientData : public TextureClientData {
|
||||
public:
|
||||
GrallocTextureClientData(GrallocBufferActor* aActor)
|
||||
: mGrallocActor(aActor)
|
||||
{
|
||||
MOZ_COUNT_CTOR(GrallocTextureClientData);
|
||||
}
|
||||
|
||||
~GrallocTextureClientData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GrallocTextureClientData);
|
||||
MOZ_ASSERT(!mGrallocActor, "Forgot to unlock the GraphicBufferLocked?");
|
||||
}
|
||||
|
||||
virtual void DeallocateSharedData(ISurfaceAllocator* allocator) MOZ_OVERRIDE
|
||||
{
|
||||
// We just need to wrap the actor in a SurfaceDescriptor because that's what
|
||||
// ISurfaceAllocator uses as input, we don't care about the other parameters.
|
||||
SurfaceDescriptor sd = SurfaceDescriptorGralloc(nullptr, mGrallocActor,
|
||||
nsIntSize(0,0), false, false);
|
||||
allocator->DestroySharedSurface(&sd);
|
||||
mGrallocActor = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
GrallocBufferActor* mGrallocActor;
|
||||
};
|
||||
|
||||
TextureClientData*
|
||||
GrallocTextureClientOGL::DropTextureData()
|
||||
{
|
||||
if (mBufferLocked) {
|
||||
TextureClientData* result = new GraphicBufferLockedTextureClientData(mBufferLocked);
|
||||
mBufferLocked = nullptr;
|
||||
mGrallocActor = nullptr;
|
||||
mGraphicBuffer = nullptr;
|
||||
return result;
|
||||
} else {
|
||||
TextureClientData* result = new GrallocTextureClientData(mGrallocActor);
|
||||
mGrallocActor = nullptr;
|
||||
mGraphicBuffer = nullptr;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
GrallocTextureClientOGL::GrallocTextureClientOGL(GrallocBufferActor* aActor,
|
||||
gfx::IntSize aSize,
|
||||
TextureFlags aFlags)
|
||||
@ -41,6 +111,20 @@ GrallocTextureClientOGL::GrallocTextureClientOGL(CompositableClient* aCompositab
|
||||
GrallocTextureClientOGL::~GrallocTextureClientOGL()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GrallocTextureClientOGL);
|
||||
if (ShouldDeallocateInDestructor()) {
|
||||
// If the buffer has never been shared we must deallocate it or it would
|
||||
// leak.
|
||||
if (mBufferLocked) {
|
||||
mBufferLocked->Unlock();
|
||||
} else {
|
||||
MOZ_ASSERT(mCompositable);
|
||||
// We just need to wrap the actor in a SurfaceDescriptor because that's what
|
||||
// ISurfaceAllocator uses as input, we don't care about the other parameters.
|
||||
SurfaceDescriptor sd = SurfaceDescriptorGralloc(nullptr, mGrallocActor,
|
||||
nsIntSize(0,0), false, false);
|
||||
mCompositable->GetForwarder()->DestroySharedSurface(&sd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -54,6 +138,12 @@ GrallocTextureClientOGL::InitWith(GrallocBufferActor* aActor, gfx::IntSize aSize
|
||||
mSize = aSize;
|
||||
}
|
||||
|
||||
void
|
||||
GrallocTextureClientOGL::SetGraphicBufferLocked(GraphicBufferLocked* aBufferLocked)
|
||||
{
|
||||
mBufferLocked = aBufferLocked;
|
||||
}
|
||||
|
||||
bool
|
||||
GrallocTextureClientOGL::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
|
||||
{
|
||||
|
@ -15,6 +15,8 @@
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class GraphicBufferLocked;
|
||||
|
||||
/**
|
||||
* A TextureClient implementation based on android::GraphicBuffer (also referred to
|
||||
* as "gralloc").
|
||||
@ -50,6 +52,8 @@ public:
|
||||
|
||||
virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
|
||||
|
||||
virtual TextureClientData* DropTextureData() MOZ_OVERRIDE;
|
||||
|
||||
void InitWith(GrallocBufferActor* aActor, gfx::IntSize aSize);
|
||||
|
||||
gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
|
||||
@ -88,6 +92,8 @@ public:
|
||||
|
||||
virtual size_t GetBufferSize() const MOZ_OVERRIDE;
|
||||
|
||||
void SetGraphicBufferLocked(GraphicBufferLocked* aBufferLocked);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
@ -95,6 +101,8 @@ protected:
|
||||
*/
|
||||
GrallocBufferActor* mGrallocActor;
|
||||
|
||||
RefPtr<GraphicBufferLocked> mBufferLocked;
|
||||
|
||||
android::sp<android::GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
/**
|
||||
|
@ -41,6 +41,15 @@ public:
|
||||
|
||||
virtual gfx::IntSize GetSize() const { return mSize; }
|
||||
|
||||
virtual TextureClientData* DropTextureData() MOZ_OVERRIDE
|
||||
{
|
||||
// XXX - right now the code paths using this are managing the shared texture
|
||||
// data, although they should use a TextureClientData for this to ensure that
|
||||
// the destruction sequence is race-free.
|
||||
MarkInvalid();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
gl::SharedTextureHandle mHandle;
|
||||
gfx::IntSize mSize;
|
||||
|
@ -78,10 +78,12 @@ ifeq ($(OS_TARGET),Android) # {
|
||||
LOCAL_INCLUDES += -I$(srcdir)/src/third_party/libevent/android
|
||||
else # } else {
|
||||
LOCAL_INCLUDES += -I$(srcdir)/src/third_party/libevent/linux
|
||||
CSRCS += \
|
||||
epoll_sub.c \
|
||||
$(NULL)
|
||||
endif # }
|
||||
CSRCS += \
|
||||
epoll.c \
|
||||
epoll_sub.c \
|
||||
$(NULL)
|
||||
|
||||
else # } else (OS_BSD) {
|
||||
|
@ -51,6 +51,7 @@ RPCChannel::~RPCChannel()
|
||||
{
|
||||
MOZ_COUNT_DTOR(RPCChannel);
|
||||
RPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
|
||||
Clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -151,11 +151,9 @@ EXTRA_DSO_LDOPTS += $(NSPR_LIBS)
|
||||
|
||||
# Define keyword generator before rules.mk, see bug 323979 comment 50
|
||||
|
||||
HOST_CPPSRCS += jskwgen.cpp
|
||||
HOST_SIMPLE_PROGRAMS += host_jskwgen$(HOST_BIN_SUFFIX)
|
||||
GARBAGE += jsautokw.h host_jskwgen$(HOST_BIN_SUFFIX)
|
||||
|
||||
HOST_CPPSRCS += jsoplengen.cpp
|
||||
HOST_SIMPLE_PROGRAMS += host_jsoplengen$(HOST_BIN_SUFFIX)
|
||||
GARBAGE += jsautooplen.h host_jsoplengen$(HOST_BIN_SUFFIX)
|
||||
|
||||
|
@ -2249,7 +2249,6 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned st
|
||||
return null();
|
||||
}
|
||||
|
||||
|
||||
if (fun->isNamedLambda()) {
|
||||
if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
|
||||
Definition *dn = p.value().get<FullParseHandler>();
|
||||
@ -2263,6 +2262,9 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned st
|
||||
if (!pc->generateFunctionBindings(context, alloc, bindings))
|
||||
return null();
|
||||
|
||||
if (!FoldConstants(context, &pn, this))
|
||||
return null();
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
@ -9,13 +9,6 @@
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
|
||||
#include "jscompartment.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
#include "gc/StoreBuffer.h"
|
||||
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
inline const Value &
|
||||
|
14
js/src/jit-test/tests/ion/bug911369.js
Normal file
14
js/src/jit-test/tests/ion/bug911369.js
Normal file
@ -0,0 +1,14 @@
|
||||
var a = [];
|
||||
var count = 0;
|
||||
a.valueOf = function() {
|
||||
++count;
|
||||
}
|
||||
function f(a) {
|
||||
6 - a;
|
||||
}
|
||||
|
||||
f(3);
|
||||
for (var i=0; i<10; i++)
|
||||
f(a);
|
||||
|
||||
assertEq(count, 10);
|
5
js/src/jit-test/tests/ion/bug914341.js
Normal file
5
js/src/jit-test/tests/ion/bug914341.js
Normal file
@ -0,0 +1,5 @@
|
||||
function f() {
|
||||
assertEq(typeof eval("this"), "object");
|
||||
}
|
||||
for (var i=0; i<5; i++)
|
||||
f();
|
@ -5289,7 +5289,7 @@ static const RegisterSet NonVolatileRegs =
|
||||
static void
|
||||
LoadAsmJSActivationIntoRegister(MacroAssembler &masm, Register reg)
|
||||
{
|
||||
masm.movePtr(ImmWord(GetIonContext()->runtime), reg);
|
||||
masm.movePtr(ImmPtr(GetIonContext()->runtime), reg);
|
||||
size_t offset = offsetof(JSRuntime, mainThread) +
|
||||
PerThreadData::offsetOfAsmJSActivationStackReadOnly();
|
||||
masm.loadPtr(Address(reg, offset), reg);
|
||||
@ -5671,16 +5671,16 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
|
||||
AssertStackAlignment(masm);
|
||||
switch (exit.sig().retType().which()) {
|
||||
case RetType::Void:
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_Ignore)));
|
||||
masm.call(ImmPtr(InvokeFromAsmJS_Ignore));
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
break;
|
||||
case RetType::Signed:
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_ToInt32)));
|
||||
masm.call(ImmPtr(InvokeFromAsmJS_ToInt32));
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
masm.unboxInt32(argv, ReturnReg);
|
||||
break;
|
||||
case RetType::Double:
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_ToNumber)));
|
||||
masm.call(ImmPtr(InvokeFromAsmJS_ToNumber));
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
masm.loadDouble(argv, ReturnFloatReg);
|
||||
break;
|
||||
@ -5722,16 +5722,16 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
|
||||
AssertStackAlignment(masm);
|
||||
switch (exit.sig().retType().which()) {
|
||||
case RetType::Void:
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_Ignore)));
|
||||
masm.call(ImmPtr(InvokeFromAsmJS_Ignore));
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
break;
|
||||
case RetType::Signed:
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_ToInt32)));
|
||||
masm.call(ImmPtr(InvokeFromAsmJS_ToInt32));
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
masm.unboxInt32(argv, ReturnReg);
|
||||
break;
|
||||
case RetType::Double:
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, &InvokeFromAsmJS_ToNumber)));
|
||||
masm.call(ImmPtr(InvokeFromAsmJS_ToNumber));
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
#if defined(JS_CPU_ARM) && !defined(JS_CPU_ARM_HARDFP)
|
||||
masm.loadValue(argv, softfpReturnOperand);
|
||||
@ -5818,12 +5818,12 @@ GenerateOOLConvert(ModuleCompiler &m, RetType retType, Label *throwLabel)
|
||||
// Call
|
||||
switch (retType.which()) {
|
||||
case RetType::Signed:
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void *, &ValueToInt32)));
|
||||
masm.call(ImmPtr(ValueToInt32));
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
masm.unboxInt32(Address(StackPointer, offsetToArgv), ReturnReg);
|
||||
break;
|
||||
case RetType::Double:
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void *, &ValueToNumber)));
|
||||
masm.call(ImmPtr(ValueToNumber));
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
#if defined(JS_CPU_ARM) && !defined(JS_CPU_ARM_HARDFP)
|
||||
masm.loadValue(Address(StackPointer, offsetToArgv), softfpReturnOperand);
|
||||
@ -6051,8 +6051,8 @@ GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
|
||||
LoadAsmJSActivationIntoRegister(masm, IntArgReg0);
|
||||
LoadJSContextFromActivation(masm, IntArgReg0, IntArgReg0);
|
||||
#endif
|
||||
void (*pf)(JSContext*) = js_ReportOverRecursed;
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, pf)));
|
||||
void (*reportOverRecursed)(JSContext*) = js_ReportOverRecursed;
|
||||
masm.call(ImmPtr(reportOverRecursed));
|
||||
masm.jump(throwLabel);
|
||||
|
||||
return !masm.oom();
|
||||
@ -6109,8 +6109,7 @@ GenerateOperationCallbackExit(ModuleCompiler &m, Label *throwLabel)
|
||||
LoadJSContextFromActivation(masm, activation, IntArgReg0);
|
||||
#endif
|
||||
|
||||
bool (*pf)(JSContext*) = js_HandleExecutionInterrupt;
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, pf)));
|
||||
masm.call(ImmPtr(js_HandleExecutionInterrupt));
|
||||
masm.branchIfFalseBool(ReturnReg, throwLabel);
|
||||
|
||||
// Restore the StackPointer to it's position before the call.
|
||||
@ -6141,8 +6140,7 @@ GenerateOperationCallbackExit(ModuleCompiler &m, Label *throwLabel)
|
||||
masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfContext()), IntArgReg0);
|
||||
|
||||
masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllMask))); // save all FP registers
|
||||
bool (*pf)(JSContext*) = js_HandleExecutionInterrupt;
|
||||
masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, pf)));
|
||||
masm.call(ImmPtr(js_HandleExecutionInterrupt));
|
||||
masm.branchIfFalseBool(ReturnReg, throwLabel);
|
||||
|
||||
// Restore the machine state to before the interrupt. this will set the pc!
|
||||
|
@ -190,8 +190,8 @@ BaselineCompiler::compile()
|
||||
size_t icEntry = icLoadLabels_[i].icEntry;
|
||||
ICEntry *entryAddr = &(baselineScript->icEntry(icEntry));
|
||||
Assembler::patchDataWithValueCheck(CodeLocationLabel(code, label),
|
||||
ImmWord(uintptr_t(entryAddr)),
|
||||
ImmWord(uintptr_t(-1)));
|
||||
ImmPtr(entryAddr),
|
||||
ImmPtr((void*)-1));
|
||||
}
|
||||
|
||||
if (modifiesArguments_)
|
||||
@ -236,7 +236,7 @@ BaselineCompiler::emitPrologue()
|
||||
// the callee, NULL is stored for now so that GC doesn't choke on
|
||||
// a bogus ScopeChain value in the frame.
|
||||
if (function())
|
||||
masm.storePtr(ImmWord((uintptr_t)0), frame.addressOfScopeChain());
|
||||
masm.storePtr(ImmPtr(NULL), frame.addressOfScopeChain());
|
||||
else
|
||||
masm.storePtr(R1.scratchReg(), frame.addressOfScopeChain());
|
||||
|
||||
@ -345,7 +345,7 @@ BaselineCompiler::emitOutOfLinePostBarrierSlot()
|
||||
#endif
|
||||
|
||||
masm.setupUnalignedABICall(2, scratch);
|
||||
masm.movePtr(ImmWord(cx->runtime()), scratch);
|
||||
masm.movePtr(ImmPtr(cx->runtime()), scratch);
|
||||
masm.passABIArg(scratch);
|
||||
masm.passABIArg(objReg);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PostWriteBarrier));
|
||||
@ -527,7 +527,7 @@ BaselineCompiler::emitUseCountIncrement()
|
||||
|
||||
masm.branchPtr(Assembler::Equal,
|
||||
Address(scriptReg, JSScript::offsetOfIonScript()),
|
||||
ImmWord(ION_COMPILING_SCRIPT), &skipCall);
|
||||
ImmPtr(ION_COMPILING_SCRIPT), &skipCall);
|
||||
|
||||
// Call IC.
|
||||
ICUseCount_Fallback::Compiler stubCompiler(cx);
|
||||
@ -969,7 +969,7 @@ BaselineCompiler::emit_JSOP_THIS()
|
||||
frame.pushThis();
|
||||
|
||||
// In strict mode function or self-hosted function, |this| is left alone.
|
||||
if (!function() || function()->strict() || function()->isSelfHostedBuiltin())
|
||||
if (function() && (function()->strict() || function()->isSelfHostedBuiltin()))
|
||||
return true;
|
||||
|
||||
Label skipIC;
|
||||
@ -2067,7 +2067,7 @@ BaselineCompiler::emitInitPropGetterSetter()
|
||||
pushArg(R0.scratchReg());
|
||||
pushArg(ImmGCPtr(script->getName(pc)));
|
||||
pushArg(R1.scratchReg());
|
||||
pushArg(ImmWord(pc));
|
||||
pushArg(ImmPtr(pc));
|
||||
|
||||
if (!callVM(InitPropGetterSetterInfo))
|
||||
return false;
|
||||
@ -2111,7 +2111,7 @@ BaselineCompiler::emitInitElemGetterSetter()
|
||||
pushArg(R0);
|
||||
masm.extractObject(frame.addressOfStackValue(frame.peek(-3)), R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
pushArg(ImmWord(pc));
|
||||
pushArg(ImmPtr(pc));
|
||||
|
||||
if (!callVM(InitElemGetterSetterInfo))
|
||||
return false;
|
||||
@ -2538,7 +2538,7 @@ bool
|
||||
BaselineCompiler::emit_JSOP_DEBUGGER()
|
||||
{
|
||||
prepareVMCall();
|
||||
pushArg(ImmWord(pc));
|
||||
pushArg(ImmPtr(pc));
|
||||
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
@ -2645,7 +2645,7 @@ BaselineCompiler::emit_JSOP_TOID()
|
||||
|
||||
pushArg(R0);
|
||||
pushArg(R1);
|
||||
pushArg(ImmWord(pc));
|
||||
pushArg(ImmPtr(pc));
|
||||
pushArg(ImmGCPtr(script));
|
||||
|
||||
if (!callVM(ToIdInfo))
|
||||
|
@ -685,7 +685,7 @@ ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, Reg
|
||||
saveRegs = GeneralRegisterSet::Intersect(saveRegs, GeneralRegisterSet::Volatile());
|
||||
masm.PushRegsInMask(saveRegs);
|
||||
masm.setupUnalignedABICall(2, scratch);
|
||||
masm.movePtr(ImmWord(cx->runtime()), scratch);
|
||||
masm.movePtr(ImmPtr(cx->runtime()), scratch);
|
||||
masm.passABIArg(scratch);
|
||||
masm.passABIArg(obj);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PostWriteBarrier));
|
||||
@ -971,7 +971,7 @@ ICUseCount_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
leaveStubFrame(masm);
|
||||
|
||||
// If no IonCode was found, then skip just exit the IC.
|
||||
masm.branchPtr(Assembler::Equal, R0.scratchReg(), ImmWord((void*) NULL), &noCompiledCode);
|
||||
masm.branchPtr(Assembler::Equal, R0.scratchReg(), ImmPtr(NULL), &noCompiledCode);
|
||||
}
|
||||
|
||||
// Get a scratch register.
|
||||
@ -3198,7 +3198,7 @@ GenerateDOMProxyChecks(JSContext *cx, MacroAssembler &masm, Register object,
|
||||
// expando object at all, in which case the presence of a non-undefined
|
||||
// expando value in the incoming object is automatically a failure.
|
||||
masm.loadPtr(*checkExpandoShapeAddr, scratch);
|
||||
masm.branchPtr(Assembler::Equal, scratch, ImmWord((void*)NULL), &failDOMProxyCheck);
|
||||
masm.branchPtr(Assembler::Equal, scratch, ImmPtr(NULL), &failDOMProxyCheck);
|
||||
|
||||
// Otherwise, ensure that the incoming object has an object for its expando value and that
|
||||
// the shape matches.
|
||||
@ -4285,7 +4285,7 @@ ICGetElem_String::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
&failure);
|
||||
|
||||
// Load static string.
|
||||
masm.movePtr(ImmWord(&cx->runtime()->staticStrings.unitStaticTable), str);
|
||||
masm.movePtr(ImmPtr(&cx->runtime()->staticStrings.unitStaticTable), str);
|
||||
masm.loadPtr(BaseIndex(str, scratchReg, ScalePointer), str);
|
||||
|
||||
// Return.
|
||||
@ -4484,7 +4484,7 @@ ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
masm.loadPtr(BaseIndex(scratchReg, tempReg, ScaleFromElemWidth(sizeof(size_t))), scratchReg);
|
||||
|
||||
// Don't bother testing specific bit, if any bit is set in the word, fail.
|
||||
masm.branchPtr(Assembler::NotEqual, scratchReg, ImmWord((size_t)0), &failureReconstructInputs);
|
||||
masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(NULL), &failureReconstructInputs);
|
||||
|
||||
// Load the value. use scratchReg and tempReg to form a ValueOperand to load into.
|
||||
masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), argData);
|
||||
@ -6098,9 +6098,11 @@ ICGetProp_TypedArrayLength::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
|
||||
// Implement the negated version of JSObject::isTypedArray predicate.
|
||||
masm.loadObjClass(obj, scratch);
|
||||
masm.branchPtr(Assembler::Below, scratch, ImmWord(&TypedArrayObject::classes[0]), &failure);
|
||||
masm.branchPtr(Assembler::Below, scratch, ImmPtr(&TypedArrayObject::classes[0]),
|
||||
&failure);
|
||||
masm.branchPtr(Assembler::AboveOrEqual, scratch,
|
||||
ImmWord(&TypedArrayObject::classes[ScalarTypeRepresentation::TYPE_MAX]), &failure);
|
||||
ImmPtr(&TypedArrayObject::classes[ScalarTypeRepresentation::TYPE_MAX]),
|
||||
&failure);
|
||||
|
||||
// Load length from fixed slot.
|
||||
masm.loadValue(Address(obj, TypedArrayObject::lengthOffset()), R0);
|
||||
@ -7696,7 +7698,7 @@ ICCallStubCompiler::guardFunApply(MacroAssembler &masm, GeneralRegisterSet regs,
|
||||
failure);
|
||||
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
|
||||
|
||||
masm.branchPtr(Assembler::NotEqual, callee, ImmWord((void*) js_fun_apply), failure);
|
||||
masm.branchPtr(Assembler::NotEqual, callee, ImmPtr(js_fun_apply), failure);
|
||||
|
||||
// Load the |thisv|, ensure that it's a scripted function with a valid baseline or ion
|
||||
// script, or a native function.
|
||||
@ -7916,7 +7918,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler &masm)
|
||||
masm.loadBaselineOrIonRaw(callee, code, SequentialExecution, &failure);
|
||||
} else {
|
||||
Address scriptCode(callee, JSScript::offsetOfBaselineOrIonRaw());
|
||||
masm.branchPtr(Assembler::Equal, scriptCode, ImmWord((void *)NULL), &failure);
|
||||
masm.branchPtr(Assembler::Equal, scriptCode, ImmPtr(NULL), &failure);
|
||||
}
|
||||
|
||||
// We no longer need R1.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user