diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js index 4a429ae9331..112b1c482fb 100644 --- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -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); } diff --git a/accessible/tests/mochitest/states/test_doc_busy.html b/accessible/tests/mochitest/states/test_doc_busy.html index 4bcb3a954ea..9b76a00954a 100644 --- a/accessible/tests/mochitest/states/test_doc_busy.html +++ b/accessible/tests/mochitest/states/test_doc_busy.html @@ -20,7 +20,7 @@ src="../events.js"> + + + diff --git a/content/base/test/csp/file_bug836922_npolicies.html^headers^ b/content/base/test/csp/file_bug836922_npolicies.html^headers^ new file mode 100644 index 00000000000..b328f447fda --- /dev/null +++ b/content/base/test/csp/file_bug836922_npolicies.html^headers^ @@ -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 diff --git a/content/base/test/csp/file_bug836922_npolicies_ro_violation.sjs b/content/base/test/csp/file_bug836922_npolicies_ro_violation.sjs new file mode 100644 index 00000000000..3e7603421e9 --- /dev/null +++ b/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)); + } +} + + diff --git a/content/base/test/csp/file_bug836922_npolicies_violation.sjs b/content/base/test/csp/file_bug836922_npolicies_violation.sjs new file mode 100644 index 00000000000..15e4958afce --- /dev/null +++ b/content/base/test/csp/file_bug836922_npolicies_violation.sjs @@ -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)); + } +} + + diff --git a/content/base/test/csp/test_bug836922_npolicies.html b/content/base/test/csp/test_bug836922_npolicies.html new file mode 100644 index 00000000000..374ad895e96 --- /dev/null +++ b/content/base/test/csp/test_bug836922_npolicies.html @@ -0,0 +1,255 @@ + + + + Test for Content Security Policy multiple policy support (regular and Report-Only mode) + + + + + +

+ + + + + + + diff --git a/content/base/test/unit/test_bug558431.js b/content/base/test/unit/test_bug558431.js index fd66f6acf04..0fbaa71e8e6 100644 --- a/content/base/test/unit/test_bug558431.js +++ b/content/base/test/unit/test_bug558431.js @@ -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 diff --git a/content/base/test/unit/test_cspreports.js b/content/base/test/unit/test_cspreports.js index e7c5f0d89b6..0bf7f9681a2 100644 --- a/content/base/test/unit/test_cspreports.js +++ b/content/base/test/unit/test_cspreports.js @@ -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); + } }); } diff --git a/content/base/test/unit/test_csputils.js b/content/base/test/unit/test_csputils.js index a9bccf12579..c4bfc3aef35 100644 --- a/content/base/test/unit/test_csputils.js +++ b/content/base/test/unit/test_csputils.js @@ -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 diff --git a/content/test/unit/test_range.js b/content/test/unit/test_range.js index f5c5a669e9f..4ea380c46ef 100644 --- a/content/test/unit/test_range.js +++ b/content/test/unit/test_range.js @@ -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. * diff --git a/docshell/base/crashtests/914521.html b/docshell/base/crashtests/914521.html new file mode 100644 index 00000000000..9ae18b86068 --- /dev/null +++ b/docshell/base/crashtests/914521.html @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/docshell/base/crashtests/crashtests.list b/docshell/base/crashtests/crashtests.list index eddb1e67a83..5318d671d26 100644 --- a/docshell/base/crashtests/crashtests.list +++ b/docshell/base/crashtests/crashtests.list @@ -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 diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 79d088f7013..794fa4b2749 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -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 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); } } diff --git a/dom/apps/src/AppsUtils.jsm b/dom/apps/src/AppsUtils.jsm index 4882cb8d5d2..04f2f2012a0 100644 --- a/dom/apps/src/AppsUtils.jsm +++ b/dom/apps/src/AppsUtils.jsm @@ -551,6 +551,10 @@ ManifestHelper.prototype = { return this._localeProp("description"); }, + get type() { + return this._localeProp("type"); + }, + get version() { return this._localeProp("version"); }, diff --git a/dom/apps/src/InterAppComm.cpp b/dom/apps/src/InterAppComm.cpp new file mode 100644 index 00000000000..622f05b0cb7 --- /dev/null +++ b/dom/apps/src/InterAppComm.cpp @@ -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); +} diff --git a/dom/apps/src/InterAppComm.h b/dom/apps/src/InterAppComm.h new file mode 100644 index 00000000000..13eab86af1f --- /dev/null +++ b/dom/apps/src/InterAppComm.h @@ -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 diff --git a/dom/apps/src/InterAppComm.manifest b/dom/apps/src/InterAppComm.manifest new file mode 100644 index 00000000000..aff530ead46 --- /dev/null +++ b/dom/apps/src/InterAppComm.manifest @@ -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} diff --git a/dom/apps/src/InterAppCommService.js b/dom/apps/src/InterAppCommService.js new file mode 100644 index 00000000000..62f68bdb748 --- /dev/null +++ b/dom/apps/src/InterAppCommService.js @@ -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]); diff --git a/dom/apps/src/InterAppConnection.js b/dom/apps/src/InterAppConnection.js new file mode 100644 index 00000000000..6edb9102968 --- /dev/null +++ b/dom/apps/src/InterAppConnection.js @@ -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]); diff --git a/dom/apps/src/InterAppMessagePort.js b/dom/apps/src/InterAppMessagePort.js new file mode 100644 index 00000000000..121303e77cf --- /dev/null +++ b/dom/apps/src/InterAppMessagePort.js @@ -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]); + diff --git a/dom/apps/src/Makefile.in b/dom/apps/src/Makefile.in new file mode 100644 index 00000000000..8493b51088b --- /dev/null +++ b/dom/apps/src/Makefile.in @@ -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 diff --git a/dom/apps/src/Webapps.js b/dom/apps/src/Webapps.js index 56233bee2da..8c4ce5533ff 100644 --- a/dom/apps/src/Webapps.js +++ b/dom/apps/src/Webapps.js @@ -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; } }, diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index ce2eb0b1047..1dd92474d08 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -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(); diff --git a/dom/apps/src/moz.build b/dom/apps/src/moz.build index 0ea5fd41c2b..27dab8e547d 100644 --- a/dom/apps/src/moz.build +++ b/dom/apps/src/moz.build @@ -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' diff --git a/dom/base/CompositionStringSynthesizer.cpp b/dom/base/CompositionStringSynthesizer.cpp new file mode 100644 index 00000000000..7158a545afc --- /dev/null +++ b/dom/base/CompositionStringSynthesizer.cpp @@ -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 window = do_QueryReferent(mWindow); + if (!window) { + return nullptr; + } + nsIDocShell *docShell = window->GetDocShell(); + if (!docShell) { + return nullptr; + } + nsCOMPtr 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 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 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 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 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 diff --git a/dom/base/CompositionStringSynthesizer.h b/dom/base/CompositionStringSynthesizer.h new file mode 100644 index 00000000000..c38525d3e4d --- /dev/null +++ b/dom/base/CompositionStringSynthesizer.h @@ -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 mClauses; + nsTextRange mCaret; + + nsIWidget* GetWidget(); + void ClearInternal(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // #ifndef mozilla_dom_compositionstringsynthesizer_h__ diff --git a/dom/base/moz.build b/dom/base/moz.build index 90bfaa75df2..fc3fbeb9ddb 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -65,6 +65,7 @@ EXPORTS.mozilla.dom += [ CPP_SOURCES += [ 'BarProps.cpp', + 'CompositionStringSynthesizer.cpp', 'Crypto.cpp', 'DOMCursor.cpp', 'DOMError.cpp', diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 5f500833ecb..31283a5ff02 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.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* 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 widget = GetWidget(); - if (!widget) { - return NS_ERROR_FAILURE; - } - - nsTextEvent textEvent(true, NS_TEXT_TEXT, widget); - InitEvent(textEvent); - - nsAutoTArray 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 window = do_QueryReferent(mWindow); + NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE); + NS_ADDREF(*aResult = new CompositionStringSynthesizer(window)); return NS_OK; } diff --git a/dom/base/nsJSTimeoutHandler.cpp b/dom/base/nsJSTimeoutHandler.cpp index ffabe99bec9..d0061ef2113 100644 --- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -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) { diff --git a/dom/encoding/test/unit/head.js b/dom/encoding/test/unit/head.js index 885146c0ed0..073c121f1be 100644 --- a/dom/encoding/test/unit/head.js +++ b/dom/encoding/test/unit/head.js @@ -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); diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index 51c433892e6..037fdf0c74c 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -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 sof(do_GetService(kDOMSOF_CID)); - JS::Rooted global(aCx, JS_GetGlobalForObject(aCx, obj)); NS_ASSERTION(global, "What?! No global!"); diff --git a/dom/interfaces/apps/moz.build b/dom/interfaces/apps/moz.build index 3d69090e73b..e44db282129 100644 --- a/dom/interfaces/apps/moz.build +++ b/dom/interfaces/apps/moz.build @@ -11,6 +11,7 @@ XPIDL_SOURCES += [ 'nsIDOMApplicationRegistry.idl', 'nsIDOMApplicationRegistry2.idl', 'nsIDOMMozApplicationEvent.idl', + 'nsIInterAppCommService.idl', ] XPIDL_MODULE = 'dom_apps' diff --git a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl index d1ef258087e..f53dba271cf 100644 --- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl +++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl @@ -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)] diff --git a/dom/interfaces/apps/nsIInterAppCommService.idl b/dom/interfaces/apps/nsIInterAppCommService.idl new file mode 100644 index 00000000000..4cf88b49551 --- /dev/null +++ b/dom/interfaces/apps/nsIInterAppCommService.idl @@ -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); +}; diff --git a/dom/interfaces/base/moz.build b/dom/interfaces/base/moz.build index d7fbec8bc07..8fee6e76d1e 100644 --- a/dom/interfaces/base/moz.build +++ b/dom/interfaces/base/moz.build @@ -7,6 +7,7 @@ XPIDL_SOURCES += [ 'domstubs.idl', 'nsIBrowserDOMWindow.idl', + 'nsICompositionStringSynthesizer.idl', 'nsIContentPermissionPrompt.idl', 'nsIContentPrefService.idl', 'nsIContentPrefService2.idl', diff --git a/dom/interfaces/base/nsICompositionStringSynthesizer.idl b/dom/interfaces/base/nsICompositionStringSynthesizer.idl new file mode 100644 index 00000000000..fe7acaf67e7 --- /dev/null +++ b/dom/interfaces/base/nsICompositionStringSynthesizer.idl @@ -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(); +}; diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 5ebc26559db..95d0e8c9e0a 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -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 diff --git a/dom/messages/SystemMessagePermissionsChecker.jsm b/dom/messages/SystemMessagePermissionsChecker.jsm index 66c6825dde7..5ad00e1a8a2 100644 --- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -55,6 +55,7 @@ this.SystemMessagePermissionsTable = { "bluetooth-opp-transfer-start": { "bluetooth": [] }, + "connection": { }, "headset-button": { }, "icc-stkcommand": { "settings": ["read", "write"] diff --git a/dom/src/geolocation/nsGeolocation.cpp b/dom/src/geolocation/nsGeolocation.cpp index 22726afb8f6..8f8649459fc 100644 --- a/dom/src/geolocation/nsGeolocation.cpp +++ b/dom/src/geolocation/nsGeolocation.cpp @@ -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(); } } diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index 34ce2807249..1e405a741e1 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -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: diff --git a/dom/tests/mochitest/geolocation/Makefile.in b/dom/tests/mochitest/geolocation/Makefile.in index 3fa7bc435f2..108cd45fa5c 100644 --- a/dom/tests/mochitest/geolocation/Makefile.in +++ b/dom/tests/mochitest/geolocation/Makefile.in @@ -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 \ diff --git a/dom/tests/mochitest/geolocation/test_handlerSpinsEventLoop.html b/dom/tests/mochitest/geolocation/test_handlerSpinsEventLoop.html new file mode 100644 index 00000000000..0125f3944b7 --- /dev/null +++ b/dom/tests/mochitest/geolocation/test_handlerSpinsEventLoop.html @@ -0,0 +1,66 @@ + + + + + Test for spinning the event loop inside position handlers + + + + + + +Mozilla Bug 911595 +

+ +
+
+
+ + diff --git a/dom/webidl/InterAppConnection.webidl b/dom/webidl/InterAppConnection.webidl new file mode 100644 index 00000000000..710f9978da3 --- /dev/null +++ b/dom/webidl/InterAppConnection.webidl @@ -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(); +}; diff --git a/dom/webidl/InterAppConnectionRequest.webidl b/dom/webidl/InterAppConnectionRequest.webidl new file mode 100644 index 00000000000..d90d1c00fff --- /dev/null +++ b/dom/webidl/InterAppConnectionRequest.webidl @@ -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; +}; diff --git a/dom/webidl/InterAppMessageEvent.webidl b/dom/webidl/InterAppMessageEvent.webidl new file mode 100644 index 00000000000..33734a5fa03 --- /dev/null +++ b/dom/webidl/InterAppMessageEvent.webidl @@ -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; +}; diff --git a/dom/webidl/InterAppMessagePort.webidl b/dom/webidl/InterAppMessagePort.webidl new file mode 100644 index 00000000000..9f23f9b89e2 --- /dev/null +++ b/dom/webidl/InterAppMessagePort.webidl @@ -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; +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 11f34a1fe6f..d0aa0c1d1b7 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -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', diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 777727707a5..c6e89fa66a7 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -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 response = diff --git a/editor/libeditor/html/tests/test_contenteditable_text_input_handling.html b/editor/libeditor/html/tests/test_contenteditable_text_input_handling.html index 36c65afe6e9..79f9553cc9b 100644 --- a/editor/libeditor/html/tests/test_contenteditable_text_input_handling.html +++ b/editor/libeditor/html/tests/test_contenteditable_text_input_handling.html @@ -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 } diff --git a/gfx/layers/GrallocImages.cpp b/gfx/layers/GrallocImages.cpp index f8f487372b9..59d02dc4c26 100644 --- a/gfx/layers/GrallocImages.cpp +++ b/gfx/layers/GrallocImages.cpp @@ -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; } diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index 56eb611b84e..42a09d00110 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -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); } diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h index b9c6a280616..e36bec1f852 100644 --- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -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. diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index e6ca8b23d3d..c064f427b01 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -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, ""); } diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index eb310fc9b23..bb4de37daad 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -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(rep.compositableChild())->GetCompositableClient(); + compositable->OnReplyTextureRemoved(rep.textureId()); break; } diff --git a/gfx/layers/client/CompositableClient.cpp b/gfx/layers/client/CompositableClient.cpp index 5608dc70f0d..1cbfa46c815 100644 --- a/gfx/layers/client/CompositableClient.cpp +++ b/gfx/layers/client/CompositableClient.cpp @@ -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::iterator it + = mTexturesToRemoveCallbacks.begin(); + std::map::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::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(); } diff --git a/gfx/layers/client/CompositableClient.h b/gfx/layers/client/CompositableClient.h index 774e94e4783..9b3020e9525 100644 --- a/gfx/layers/client/CompositableClient.h +++ b/gfx/layers/client/CompositableClient.h @@ -8,6 +8,7 @@ #include // for uint64_t #include // for vector +#include // 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 mTexturesToRemove; + nsTArray mTexturesToRemove; + std::map mTexturesToRemoveCallbacks; uint64_t mNextTextureID; CompositableChild* mCompositableChild; CompositableForwarder* mForwarder; diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp index deddd369a4f..dadf2eb2cec 100644 --- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -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) diff --git a/gfx/layers/client/ImageClient.h b/gfx/layers/client/ImageClient.h index 5ae01c567cb..cc3ef0495f4 100644 --- a/gfx/layers/client/ImageClient.h +++ b/gfx/layers/client/ImageClient.h @@ -62,6 +62,11 @@ public: virtual already_AddRefed 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 CreateImage(const uint32_t *aFormats, uint32_t aNumFormats) MOZ_OVERRIDE; + + virtual void FlushImage() MOZ_OVERRIDE; + protected: RefPtr 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 mBackBuffer; }; diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index f3814239629..c93d4c1de04 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -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 diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h index e49f8506f87..94ce0ff5d0b 100644 --- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -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; diff --git a/gfx/layers/composite/CanvasLayerComposite.cpp b/gfx/layers/composite/CanvasLayerComposite.cpp index 17b85e1725c..c599d20b3af 100644 --- a/gfx/layers/composite/CanvasLayerComposite.cpp +++ b/gfx/layers/composite/CanvasLayerComposite.cpp @@ -106,7 +106,7 @@ CanvasLayerComposite::RenderLayer(const nsIntPoint& aOffset, CompositableHost* CanvasLayerComposite::GetCompositableHost() { - if (mImageHost->IsAttached()) { + if ( mImageHost && mImageHost->IsAttached()) { return mImageHost.get(); } diff --git a/gfx/layers/composite/CompositableHost.h b/gfx/layers/composite/CompositableHost.h index 7c668d701be..ab8973feffa 100644 --- a/gfx/layers/composite/CompositableHost.h +++ b/gfx/layers/composite/CompositableHost.h @@ -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: diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index 0da1722781f..88651561892 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -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() { diff --git a/gfx/layers/composite/ImageHost.h b/gfx/layers/composite/ImageHost.h index 63cdf11a223..30e22d9fe81 100644 --- a/gfx/layers/composite/ImageHost.h +++ b/gfx/layers/composite/ImageHost.h @@ -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 diff --git a/gfx/layers/composite/ThebesLayerComposite.cpp b/gfx/layers/composite/ThebesLayerComposite.cpp index c74eb87778a..199fbf544ee 100644 --- a/gfx/layers/composite/ThebesLayerComposite.cpp +++ b/gfx/layers/composite/ThebesLayerComposite.cpp @@ -158,7 +158,7 @@ ThebesLayerComposite::RenderLayer(const nsIntPoint& aOffset, CompositableHost* ThebesLayerComposite::GetCompositableHost() { - if (mBuffer->IsAttached()) { + if ( mBuffer && mBuffer->IsAttached()) { return mBuffer.get(); } diff --git a/gfx/layers/ipc/CompositableForwarder.h b/gfx/layers/ipc/CompositableForwarder.h index 7b0f79d0934..f0b42eeb7a4 100644 --- a/gfx/layers/ipc/CompositableForwarder.h +++ b/gfx/layers/ipc/CompositableForwarder.h @@ -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 diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp index e471361be10..fbb4c37109c 100644 --- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -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 >(&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(rep.compositableChild())->GetCompositableClient(); + compositable->OnReplyTextureRemoved(rep.textureId()); break; } default: diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h index 9130603c555..b040fd7950e 100644 --- a/gfx/layers/ipc/ImageBridgeChild.h +++ b/gfx/layers/ipc/ImageBridgeChild.h @@ -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 diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 3fce6427565..521b36a0e1d 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -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)); - } } diff --git a/gfx/layers/opengl/GrallocTextureClient.cpp b/gfx/layers/opengl/GrallocTextureClient.cpp index 72f7266dbec..df6622b6e06 100644 --- a/gfx/layers/opengl/GrallocTextureClient.cpp +++ b/gfx/layers/opengl/GrallocTextureClient.cpp @@ -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 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) { diff --git a/gfx/layers/opengl/GrallocTextureClient.h b/gfx/layers/opengl/GrallocTextureClient.h index 71ca038077d..bf7b35ebff3 100644 --- a/gfx/layers/opengl/GrallocTextureClient.h +++ b/gfx/layers/opengl/GrallocTextureClient.h @@ -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 mBufferLocked; + android::sp mGraphicBuffer; /** diff --git a/gfx/layers/opengl/TextureClientOGL.h b/gfx/layers/opengl/TextureClientOGL.h index 5a639ce201b..c9cd0425648 100644 --- a/gfx/layers/opengl/TextureClientOGL.h +++ b/gfx/layers/opengl/TextureClientOGL.h @@ -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; diff --git a/ipc/chromium/Makefile.in b/ipc/chromium/Makefile.in index f6c272c86f6..4e8c54ba6d4 100644 --- a/ipc/chromium/Makefile.in +++ b/ipc/chromium/Makefile.in @@ -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) { diff --git a/ipc/glue/RPCChannel.cpp b/ipc/glue/RPCChannel.cpp index a0cb0ebc5e6..b0c4729d99d 100644 --- a/ipc/glue/RPCChannel.cpp +++ b/ipc/glue/RPCChannel.cpp @@ -51,6 +51,7 @@ RPCChannel::~RPCChannel() { MOZ_COUNT_DTOR(RPCChannel); RPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors"); + Clear(); } void diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 0e3d35cbde5..961bb3d29e2 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -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) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 210d56f2ec8..d6054b04d89 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2249,7 +2249,6 @@ Parser::standaloneLazyFunction(HandleFunction fun, unsigned st return null(); } - if (fun->isNamedLambda()) { if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) { Definition *dn = p.value().get(); @@ -2263,6 +2262,9 @@ Parser::standaloneLazyFunction(HandleFunction fun, unsigned st if (!pc->generateFunctionBindings(context, alloc, bindings)) return null(); + if (!FoldConstants(context, &pn, this)) + return null(); + return pn; } diff --git a/js/src/gc/Barrier-inl.h b/js/src/gc/Barrier-inl.h index 5c601b481e8..3b221e28113 100644 --- a/js/src/gc/Barrier-inl.h +++ b/js/src/gc/Barrier-inl.h @@ -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 & diff --git a/js/src/jit-test/tests/ion/bug911369.js b/js/src/jit-test/tests/ion/bug911369.js new file mode 100644 index 00000000000..90240b29cb5 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug911369.js @@ -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); diff --git a/js/src/jit-test/tests/ion/bug914341.js b/js/src/jit-test/tests/ion/bug914341.js new file mode 100644 index 00000000000..2f0bb3174d0 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug914341.js @@ -0,0 +1,5 @@ +function f() { + assertEq(typeof eval("this"), "object"); +} +for (var i=0; i<5; i++) + f(); diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index 9a87e4721d2..e303c7e3fe1 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -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! diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 2472443850b..7826ceeb730 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -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)) diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 54ca2050882..f932ae2add4 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -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. diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index bfb1ef33df9..bda533259fa 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -652,7 +652,7 @@ CodeGenerator::visitIntToString(LIntToString *lir) masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), ool->entry()); - masm.movePtr(ImmWord(&GetIonContext()->runtime->staticStrings.intStaticTable), output); + masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings.intStaticTable), output); masm.loadPtr(BaseIndex(output, input, ScalePointer), output); masm.bind(ool->rejoin()); @@ -681,7 +681,7 @@ CodeGenerator::visitDoubleToString(LDoubleToString *lir) masm.branch32(Assembler::AboveOrEqual, temp, Imm32(StaticStrings::INT_STATIC_LIMIT), ool->entry()); - masm.movePtr(ImmWord(&GetIonContext()->runtime->staticStrings.intStaticTable), output); + masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings.intStaticTable), output); masm.loadPtr(BaseIndex(output, temp, ScalePointer), output); masm.bind(ool->rejoin()); @@ -1104,7 +1104,7 @@ CodeGenerator::visitPointer(LPointer *lir) if (lir->kind() == LPointer::GC_THING) masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output())); else - masm.movePtr(ImmWord(lir->ptr()), ToRegister(lir->output())); + masm.movePtr(ImmPtr(lir->ptr()), ToRegister(lir->output())); return true; } @@ -1441,7 +1441,7 @@ CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier } Register runtimereg = regs.takeAny(); - masm.mov(ImmWord(GetIonContext()->runtime), runtimereg); + masm.mov(ImmPtr(GetIonContext()->runtime), runtimereg); masm.setupUnalignedABICall(2, regs.takeAny()); masm.passABIArg(runtimereg); @@ -1776,7 +1776,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call) // Guard that calleereg is actually a function object. masm.loadObjClass(calleereg, nargsreg); - masm.branchPtr(Assembler::NotEqual, nargsreg, ImmWord(&JSFunction::class_), &invoke); + masm.branchPtr(Assembler::NotEqual, nargsreg, ImmPtr(&JSFunction::class_), &invoke); // Guard that calleereg is an interpreted function with a JSScript. // If we are constructing, also ensure the callee is a constructor. @@ -2104,7 +2104,7 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply) // Unless already known, guard that calleereg is actually a function object. if (!apply->hasSingleTarget()) { masm.loadObjClass(calleereg, objreg); - masm.cmpPtr(objreg, ImmWord(&JSFunction::class_)); + masm.cmpPtr(objreg, ImmPtr(&JSFunction::class_)); if (!bailoutIf(Assembler::NotEqual, apply->snapshot())) return false; } @@ -2281,7 +2281,7 @@ CodeGenerator::visitCallDirectEval(LCallDirectEval *lir) Register scopeChain = ToRegister(lir->getScopeChain()); Register string = ToRegister(lir->getString()); - pushArg(ImmWord(lir->mir()->pc())); + pushArg(ImmPtr(lir->mir()->pc())); pushArg(string); pushArg(ToValue(lir, LCallDirectEval::ThisValueInput)); pushArg(ImmGCPtr(gen->info().script())); @@ -2879,7 +2879,7 @@ CodeGenerator::visitNewSlots(LNewSlots *lir) Register temp3 = ToRegister(lir->temp3()); Register output = ToRegister(lir->output()); - masm.mov(ImmWord(GetIonContext()->runtime), temp1); + masm.mov(ImmPtr(GetIonContext()->runtime), temp1); masm.mov(Imm32(lir->mir()->nslots()), temp2); masm.setupUnalignedABICall(2, temp3); @@ -3104,7 +3104,7 @@ CodeGenerator::visitNewCallObject(LNewCallObject *lir) (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()), ImmGCPtr(templateObj->lastProperty()), ImmGCPtr(templateObj->hasLazyType() ? NULL : templateObj->type()), - ImmWord((void *)NULL)), + ImmPtr(NULL)), StoreRegisterTo(obj)); } if (!ool) @@ -3339,7 +3339,7 @@ CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter *lir) pushArg(value); pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex)); pushArg(obj); - pushArg(ImmWord(lir->mir()->resumePoint()->pc())); + pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); return callVM(InitElemGetterSetterInfo, lir); } @@ -3375,7 +3375,7 @@ CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter *lir) pushArg(value); pushArg(ImmGCPtr(lir->mir()->name())); pushArg(obj); - pushArg(ImmWord(lir->mir()->resumePoint()->pc())); + pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); return callVM(InitPropGetterSetterInfo, lir); } @@ -3705,7 +3705,7 @@ CodeGenerator::visitMathFunctionD(LMathFunctionD *ins) MathCache *mathCache = ins->mir()->cache(); masm.setupUnalignedABICall(2, temp); - masm.movePtr(ImmWord(mathCache), temp); + masm.movePtr(ImmPtr(mathCache), temp); masm.passABIArg(temp); masm.passABIArg(input); @@ -3823,7 +3823,7 @@ CodeGenerator::visitBinaryV(LBinaryV *lir) pushArg(ToValue(lir, LBinaryV::RhsInput)); pushArg(ToValue(lir, LBinaryV::LhsInput)); if (gen->info().executionMode() == SequentialExecution) { - pushArg(ImmWord(lir->mirRaw()->toInstruction()->resumePoint()->pc())); + pushArg(ImmPtr(lir->mirRaw()->toInstruction()->resumePoint()->pc())); pushArg(ImmGCPtr(current->mir()->info().script())); } @@ -4423,7 +4423,7 @@ IonCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode) masm.pop(temp1); masm.bind(&failure); - masm.movePtr(ImmWord((void *)NULL), output); + masm.movePtr(ImmPtr(NULL), output); masm.ret(); Linker linker(masm); @@ -4481,7 +4481,7 @@ CodeGenerator::visitFromCharCode(LFromCharCode *lir) masm.branch32(Assembler::AboveOrEqual, code, Imm32(StaticStrings::UNIT_STATIC_LIMIT), ool->entry()); - masm.movePtr(ImmWord(&GetIonContext()->runtime->staticStrings.unitStaticTable), output); + masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings.unitStaticTable), output); masm.loadPtr(BaseIndex(output, code, ScalePointer), output); masm.bind(ool->rejoin()); @@ -5150,7 +5150,7 @@ CodeGenerator::visitArrayConcat(LArrayConcat *lir) masm.jump(&call); { masm.bind(&fail); - masm.movePtr(ImmWord((void *)NULL), temp1); + masm.movePtr(ImmPtr(NULL), temp1); } masm.bind(&call); @@ -5225,7 +5225,7 @@ CodeGenerator::visitIteratorStart(LIteratorStart *lir) // elements is not captured by the shape tests above. masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfElements()), - ImmWord(js::emptyObjectElements), + ImmPtr(js::emptyObjectElements), ool->entry()); // Write barrier for stores to the iterator. We only need to take a write @@ -5253,7 +5253,7 @@ CodeGenerator::visitIteratorStart(LIteratorStart *lir) masm.or32(Imm32(JSITER_ACTIVE), Address(niTemp, offsetof(NativeIterator, flags))); // Chain onto the active iterator stack. - masm.movePtr(ImmWord(gen->compartment), temp1); + masm.movePtr(ImmPtr(gen->compartment), temp1); masm.loadPtr(Address(temp1, offsetof(JSCompartment, enumerators)), temp1); // ni->next = list @@ -5380,8 +5380,8 @@ CodeGenerator::visitIteratorEnd(LIteratorEnd *lir) masm.storePtr(prev, Address(next, NativeIterator::offsetOfPrev())); masm.storePtr(next, Address(prev, NativeIterator::offsetOfNext())); #ifdef DEBUG - masm.storePtr(ImmWord(uintptr_t(0)), Address(temp1, NativeIterator::offsetOfNext())); - masm.storePtr(ImmWord(uintptr_t(0)), Address(temp1, NativeIterator::offsetOfPrev())); + masm.storePtr(ImmPtr(NULL), Address(temp1, NativeIterator::offsetOfNext())); + masm.storePtr(ImmPtr(NULL), Address(temp1, NativeIterator::offsetOfPrev())); #endif masm.bind(ool->rejoin()); @@ -5485,7 +5485,7 @@ CodeGenerator::visitRest(LRest *lir) masm.jump(&joinAlloc); { masm.bind(&failAlloc); - masm.movePtr(ImmWord((void *)NULL), temp2); + masm.movePtr(ImmPtr(NULL), temp2); } masm.bind(&joinAlloc); @@ -5684,8 +5684,8 @@ CodeGenerator::link() invalidateEpilogueData_.fixup(&masm); Assembler::patchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_), - ImmWord(uintptr_t(ionScript)), - ImmWord(uintptr_t(-1))); + ImmPtr(ionScript), + ImmPtr((void*)-1)); IonSpew(IonSpew_Codegen, "Created IonScript %p (raw %p)", (void *) ionScript, (void *) code->raw()); @@ -5851,7 +5851,7 @@ CodeGenerator::visitCallInitElementArray(LCallInitElementArray *lir) pushArg(ToValue(lir, LCallInitElementArray::Value)); pushArg(Imm32(lir->mir()->index())); pushArg(ToRegister(lir->getOperand(0))); - pushArg(ImmWord(lir->mir()->resumePoint()->pc())); + pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); return callVM(InitElementArrayInfo, lir); } @@ -6259,7 +6259,7 @@ CodeGenerator::visitCallSetProperty(LCallSetProperty *ins) const Register objReg = ToRegister(ins->getOperand(0)); - pushArg(ImmWord(ins->mir()->resumePoint()->pc())); + pushArg(ImmPtr(ins->mir()->resumePoint()->pc())); pushArg(Imm32(ins->mir()->strict())); pushArg(value); @@ -6511,7 +6511,7 @@ CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool) saveVolatile(output); masm.setupUnalignedABICall(2, output); masm.passABIArg(obj); - masm.movePtr(ImmWord(GetIonContext()->runtime), output); + masm.movePtr(ImmPtr(GetIonContext()->runtime), output); masm.passABIArg(output); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::TypeOfObjectOperation)); masm.storeCallResult(output); @@ -6536,7 +6536,7 @@ CodeGenerator::visitToIdV(LToIdV *lir) OutOfLineCode *ool = oolCallVM(ToIdInfo, lir, (ArgList(), ImmGCPtr(current->mir()->info().script()), - ImmWord(lir->mir()->resumePoint()->pc()), + ImmPtr(lir->mir()->resumePoint()->pc()), ToValue(lir, LToIdV::Object), ToValue(lir, LToIdV::Index)), StoreValueTo(out)); @@ -7227,7 +7227,7 @@ CodeGenerator::visitOutOfLineAbortPar(OutOfLineAbortPar *ool) masm.move32(Imm32(cause), CallTempReg0); loadOutermostJSScript(CallTempReg1); loadJSScriptForBlock(ool->basicBlock(), CallTempReg2); - masm.movePtr(ImmWord((void *) bytecode), CallTempReg3); + masm.movePtr(ImmPtr(bytecode), CallTempReg3); masm.setupUnalignedABICall(4, CallTempReg4); masm.passABIArg(CallTempReg0); @@ -7251,12 +7251,12 @@ CodeGenerator::visitIsCallable(LIsCallable *ins) // An object is callable iff (is() || getClass()->call). Label notFunction, done; - masm.branchPtr(Assembler::NotEqual, output, ImmWord(&JSFunction::class_), ¬Function); + masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), ¬Function); masm.move32(Imm32(1), output); masm.jump(&done); masm.bind(¬Function); - masm.cmpPtr(Address(output, offsetof(js::Class, call)), ImmWord((void *)NULL)); + masm.cmpPtr(Address(output, offsetof(js::Class, call)), ImmPtr(NULL)); masm.emitSet(Assembler::NonZero, output); masm.bind(&done); @@ -7354,7 +7354,7 @@ CodeGenerator::visitAsmJSCall(LAsmJSCall *ins) masm.call(ToRegister(ins->getOperand(mir->dynamicCalleeOperandIndex()))); break; case MAsmJSCall::Callee::Builtin: - masm.call(ImmWord(callee.builtin())); + masm.call(ImmPtr(callee.builtin())); break; } diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 4050372e78b..71cc2927f12 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1304,7 +1304,7 @@ OptimizeMIR(MIRGenerator *mir) if (js_IonOptions.rangeAnalysis) { RangeAnalysis r(mir, graph); - if (!r.addBetaNobes()) + if (!r.addBetaNodes()) return false; IonSpewPass("Beta"); AssertExtendedGraphCoherency(graph); @@ -1320,7 +1320,7 @@ OptimizeMIR(MIRGenerator *mir) if (mir->shouldCancel("Range Analysis")) return false; - if (!r.removeBetaNobes()) + if (!r.removeBetaNodes()) return false; IonSpewPass("De-Beta"); AssertExtendedGraphCoherency(graph); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index d2d67a9677d..ca1bab37b03 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -3629,10 +3629,8 @@ IonBuilder::jsop_binary(JSOp op, MDefinition *left, MDefinition *right) MOZ_ASSUME_UNREACHABLE("unexpected binary opcode"); } - bool overflowed = types::HasOperationOverflowed(script(), pc); - current->add(ins); - ins->infer(inspector, pc, overflowed); + ins->infer(inspector, pc); current->push(ins); if (ins->isEffectful()) diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index d3b2de05b0e..d10975d6a93 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -177,7 +177,7 @@ class IonCache::StubAttacher // value would be replaced by the attachStub function after the allocation // of the IonCode. The self-reference is used to keep the stub path alive // even if the IonScript is invalidated or if the IC is flushed. - static const ImmWord STUB_ADDR; + static const ImmPtr STUB_ADDR; template void branchNextStub(MacroAssembler &masm, Assembler::Condition cond, T1 op1, T2 op2) { @@ -238,14 +238,14 @@ class IonCache::StubAttacher if (hasStubCodePatchOffset_) { stubCodePatchOffset_.fixup(&masm); Assembler::patchDataWithValueCheck(CodeLocationLabel(code, stubCodePatchOffset_), - ImmWord(uintptr_t(code)), STUB_ADDR); + ImmPtr(code), STUB_ADDR); } } virtual void patchNextStubJump(MacroAssembler &masm, IonCode *code) = 0; }; -const ImmWord IonCache::StubAttacher::STUB_ADDR = ImmWord(uintptr_t(0xdeadc0de)); +const ImmPtr IonCache::StubAttacher::STUB_ADDR = ImmPtr((void*)0xdeadc0de); class RepatchIonCache::RepatchStubAppender : public IonCache::StubAttacher { @@ -344,7 +344,7 @@ void DispatchIonCache::emitInitialJump(MacroAssembler &masm, AddCacheState &addState) { Register scratch = addState.dispatchScratch; - dispatchLabel_ = masm.movWithPatch(ImmWord(uintptr_t(-1)), scratch); + dispatchLabel_ = masm.movWithPatch(ImmPtr((void*)-1), scratch); masm.loadPtr(Address(scratch, 0), scratch); masm.jump(scratch); rejoinLabel_ = masm.labelForPatch(); @@ -365,8 +365,8 @@ DispatchIonCache::updateBaseAddress(IonCode *code, MacroAssembler &masm) IonCache::updateBaseAddress(code, masm); dispatchLabel_.fixup(&masm); Assembler::patchDataWithValueCheck(CodeLocationLabel(code, dispatchLabel_), - ImmWord(uintptr_t(&firstStub_)), - ImmWord(uintptr_t(-1))); + ImmPtr(&firstStub_), + ImmPtr((void*)-1)); firstStub_ = fallbackLabel_.raw(); rejoinLabel_.repoint(code, &masm); } @@ -651,7 +651,7 @@ GenerateDOMProxyChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj, // Check that object is a DOMProxy. masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, - ImmWord(obj->as().handler()), stubFailure); + ImmPtr(obj->as().handler()), stubFailure); if (skipExpandoCheck) return; @@ -673,7 +673,7 @@ GenerateDOMProxyChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj, masm.branchTestValue(Assembler::NotEqual, tempVal, expandoVal, &failDOMProxyCheck); ExpandoAndGeneration *expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate(); - masm.movePtr(ImmWord(expandoAndGeneration), tempVal.scratchReg()); + masm.movePtr(ImmPtr(expandoAndGeneration), tempVal.scratchReg()); masm.branch32(Assembler::NotEqual, Address(tempVal.scratchReg(), sizeof(Value)), Imm32(expandoAndGeneration->generation), @@ -1078,8 +1078,11 @@ GenerateTypedArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAtta // Implement the negated version of JSObject::isTypedArray predicate. masm.loadObjClass(object, tmpReg); - masm.branchPtr(Assembler::Below, tmpReg, ImmWord(&TypedArrayObject::classes[0]), &failures); - masm.branchPtr(Assembler::AboveOrEqual, tmpReg, ImmWord(&TypedArrayObject::classes[ScalarTypeRepresentation::TYPE_MAX]), &failures); + masm.branchPtr(Assembler::Below, tmpReg, ImmPtr(&TypedArrayObject::classes[0]), + &failures); + masm.branchPtr(Assembler::AboveOrEqual, tmpReg, + ImmPtr(&TypedArrayObject::classes[ScalarTypeRepresentation::TYPE_MAX]), + &failures); // Load length. masm.loadTypedOrValue(Address(object, TypedArrayObject::lengthOffset()), output); @@ -3112,7 +3115,7 @@ GetElementIC::attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *ob masm.loadPtr(BaseIndex(tmpReg, indexReg, ScaleFromElemWidth(sizeof(size_t))), tmpReg); // Don't bother testing specific bit, if any bit is set in the word, fail. - masm.branchPtr(Assembler::NotEqual, tmpReg, ImmWord((size_t)0), &failurePopIndex); + masm.branchPtr(Assembler::NotEqual, tmpReg, ImmPtr(NULL), &failurePopIndex); // Get the address to load from into tmpReg masm.loadPrivate(Address(object(), ArgumentsObject::getDataSlotOffset()), tmpReg); diff --git a/js/src/jit/IonMacroAssembler.cpp b/js/src/jit/IonMacroAssembler.cpp index fb17f5957c7..bdb0d9723b1 100644 --- a/js/src/jit/IonMacroAssembler.cpp +++ b/js/src/jit/IonMacroAssembler.cpp @@ -560,7 +560,7 @@ MacroAssembler::newGCThing(const Register &result, gc::AllocKind allocKind, Labe #ifdef JS_GC_ZEAL // Don't execute the inline path if gcZeal is active. - movePtr(ImmWord(GetIonContext()->runtime), result); + movePtr(ImmPtr(GetIonContext()->runtime), result); loadPtr(Address(result, offsetof(JSRuntime, gcZeal_)), result); branch32(Assembler::NotEqual, result, Imm32(0), fail); #endif @@ -704,7 +704,7 @@ MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject) storePtr(ImmGCPtr(templateObject->lastProperty()), Address(obj, JSObject::offsetOfShape())); storePtr(ImmGCPtr(templateObject->type()), Address(obj, JSObject::offsetOfType())); - storePtr(ImmWord((void *)NULL), Address(obj, JSObject::offsetOfSlots())); + storePtr(ImmPtr(NULL), Address(obj, JSObject::offsetOfSlots())); if (templateObject->is()) { JS_ASSERT(!templateObject->getDenseInitializedLength()); @@ -727,7 +727,7 @@ MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject) : 0), Address(obj, elementsOffset + ObjectElements::offsetOfFlags())); } else { - storePtr(ImmWord(emptyObjectElements), Address(obj, JSObject::offsetOfElements())); + storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements())); // Fixed slots of non-array objects are required to be initialized. // Use the values currently in the template object. @@ -740,7 +740,7 @@ MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject) if (templateObject->hasPrivate()) { uint32_t nfixed = templateObject->numFixedSlots(); - storePtr(ImmWord(templateObject->getPrivate()), + storePtr(ImmPtr(templateObject->getPrivate()), Address(obj, JSObject::getPrivateDataOffset(nfixed))); } } @@ -786,8 +786,7 @@ void MacroAssembler::checkInterruptFlagsPar(const Register &tempReg, Label *fail) { - void *interrupt = (void*)&GetIonContext()->runtime->interrupt; - movePtr(ImmWord(interrupt), tempReg); + movePtr(ImmPtr(&GetIonContext()->runtime->interrupt), tempReg); load32(Address(tempReg, 0), tempReg); branchTest32(Assembler::NonZero, tempReg, tempReg, fail); } @@ -946,7 +945,7 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo) Label done; branchPtr(Assembler::Equal, Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub)), - ImmWord((void*) 0), + ImmPtr(NULL), &noMonitor); // @@ -1041,7 +1040,7 @@ MacroAssembler::loadBaselineOrIonRaw(Register script, Register dest, ExecutionMo } else { loadPtr(Address(script, JSScript::offsetOfParallelIonScript()), dest); if (failure) - branchPtr(Assembler::BelowOrEqual, dest, ImmWord(ION_COMPILING_SCRIPT), failure); + branchPtr(Assembler::BelowOrEqual, dest, ImmPtr(ION_COMPILING_SCRIPT), failure); loadPtr(Address(dest, IonScript::offsetOfMethod()), dest); loadPtr(Address(dest, IonCode::offsetOfCode()), dest); } @@ -1066,7 +1065,7 @@ MacroAssembler::loadBaselineOrIonNoArgCheck(Register script, Register dest, Exec loadPtr(Address(script, JSScript::offsetOfParallelIonScript()), dest); if (failure) - branchPtr(Assembler::BelowOrEqual, dest, ImmWord(ION_COMPILING_SCRIPT), failure); + branchPtr(Assembler::BelowOrEqual, dest, ImmPtr(ION_COMPILING_SCRIPT), failure); Push(offset); load32(Address(script, IonScript::offsetOfSkipArgCheckEntryOffset()), offset); @@ -1126,7 +1125,7 @@ MacroAssembler::enterParallelExitFrameAndLoadSlice(const VMFunction *f, Register // Push the ioncode. exitCodePatch_ = PushWithPatch(ImmWord(-1)); // Push the VMFunction pointer, to mark arguments. - Push(ImmWord(f)); + Push(ImmPtr(f)); } void @@ -1136,8 +1135,8 @@ MacroAssembler::enterFakeParallelExitFrame(Register slice, Register scratch, // Load the PerThreadData from from the slice. loadPtr(Address(slice, offsetof(ForkJoinSlice, perThreadData)), scratch); linkParallelExitFrame(scratch); - Push(ImmWord(uintptr_t(codeVal))); - Push(ImmWord(uintptr_t(NULL))); + Push(ImmPtr(codeVal)); + Push(ImmPtr(NULL)); } void @@ -1265,7 +1264,7 @@ MacroAssembler::printf(const char *output) Register temp = regs.takeGeneral(); setupUnalignedABICall(1, temp); - movePtr(ImmWord(output), temp); + movePtr(ImmPtr(output), temp); passABIArg(temp); callWithABI(JS_FUNC_TO_DATA_PTR(void *, printf0_)); @@ -1289,7 +1288,7 @@ MacroAssembler::printf(const char *output, Register value) Register temp = regs.takeGeneral(); setupUnalignedABICall(2, temp); - movePtr(ImmWord(output), temp); + movePtr(ImmPtr(output), temp); passABIArg(temp); passABIArg(value); callWithABI(JS_FUNC_TO_DATA_PTR(void *, printf1_)); @@ -1310,7 +1309,7 @@ MacroAssembler::tracelogStart(JSScript *script) Register rscript = regs.takeGeneral(); setupUnalignedABICall(3, temp); - movePtr(ImmWord((void *)TraceLogging::defaultLogger()), temp); + movePtr(ImmPtr(TraceLogging::defaultLogger()), temp); passABIArg(temp); move32(Imm32(TraceLogging::SCRIPT_START), type); passABIArg(type); @@ -1333,7 +1332,7 @@ MacroAssembler::tracelogStop() Register type = regs.takeGeneral(); setupUnalignedABICall(2, temp); - movePtr(ImmWord((void *)TraceLogging::defaultLogger()), logger); + movePtr(ImmPtr(TraceLogging::defaultLogger()), logger); passABIArg(logger); move32(Imm32(TraceLogging::SCRIPT_STOP), type); passABIArg(type); @@ -1354,7 +1353,7 @@ MacroAssembler::tracelogLog(TraceLogging::Type type) Register rtype = regs.takeGeneral(); setupUnalignedABICall(2, temp); - movePtr(ImmWord((void *)TraceLogging::defaultLogger()), logger); + movePtr(ImmPtr(TraceLogging::defaultLogger()), logger); passABIArg(logger); move32(Imm32(type), rtype); passABIArg(rtype); @@ -1462,7 +1461,7 @@ MacroAssembler::PushEmptyRooted(VMFunction::RootType rootType) case VMFunction::RootPropertyName: case VMFunction::RootFunction: case VMFunction::RootCell: - Push(ImmWord((void *)NULL)); + Push(ImmPtr(NULL)); break; case VMFunction::RootValue: Push(UndefinedValue()); diff --git a/js/src/jit/IonMacroAssembler.h b/js/src/jit/IonMacroAssembler.h index 5fce99b1fa6..f10aa1d45fe 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -184,7 +184,7 @@ class MacroAssembler : public MacroAssemblerSpecific void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp, Label *label) { loadPtr(Address(obj, JSObject::offsetOfType()), scratch); - branchPtr(cond, Address(scratch, offsetof(types::TypeObject, clasp)), ImmWord(clasp), label); + branchPtr(cond, Address(scratch, offsetof(types::TypeObject, clasp)), ImmPtr(clasp), label); } void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) { branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label); @@ -197,7 +197,7 @@ class MacroAssembler : public MacroAssemblerSpecific Address handlerAddr(proxy, ProxyObject::offsetOfHandler()); loadPrivate(handlerAddr, scratch); Address familyAddr(scratch, BaseProxyHandler::offsetOfFamily()); - branchPtr(cond, familyAddr, ImmWord(handlerp), label); + branchPtr(cond, familyAddr, ImmPtr(handlerp), label); } template @@ -252,11 +252,11 @@ class MacroAssembler : public MacroAssemblerSpecific } void loadJSContext(const Register &dest) { - movePtr(ImmWord(GetIonContext()->runtime), dest); + movePtr(ImmPtr(GetIonContext()->runtime), dest); loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionJSContext)), dest); } void loadJitActivation(const Register &dest) { - movePtr(ImmWord(GetIonContext()->runtime), dest); + movePtr(ImmPtr(GetIonContext()->runtime), dest); size_t offset = offsetof(JSRuntime, mainThread) + PerThreadData::offsetOfActivation(); loadPtr(Address(dest, offset), dest); } @@ -422,8 +422,7 @@ class MacroAssembler : public MacroAssemblerSpecific Push(ImmGCPtr(str)); } } else { - size_t idbits = JSID_BITS(id); - Push(ImmWord(idbits)); + Push(ImmWord(JSID_BITS(id))); } } @@ -499,7 +498,7 @@ class MacroAssembler : public MacroAssemblerSpecific void branchTestNeedsBarrier(Condition cond, const Register &scratch, Label *label) { JS_ASSERT(cond == Zero || cond == NonZero); JS::Zone *zone = GetIonContext()->compartment->zone(); - movePtr(ImmWord(zone), scratch); + movePtr(ImmPtr(zone), scratch); Address needsBarrierAddr(scratch, JS::Zone::OffsetOfNeedsBarrier()); branchTest32(cond, needsBarrierAddr, Imm32(0x1), label); } @@ -691,12 +690,12 @@ class MacroAssembler : public MacroAssemblerSpecific // Push the ioncode. (Bailout or VM wrapper) exitCodePatch_ = PushWithPatch(ImmWord(-1)); // Push VMFunction pointer, to mark arguments. - Push(ImmWord(f)); + Push(ImmPtr(f)); } void enterFakeExitFrame(IonCode *codeVal = NULL) { linkExitFrame(); - Push(ImmWord(uintptr_t(codeVal))); - Push(ImmWord(uintptr_t(NULL))); + Push(ImmPtr(codeVal)); + Push(ImmPtr(NULL)); } void loadForkJoinSlice(Register slice, Register scratch); @@ -730,8 +729,8 @@ class MacroAssembler : public MacroAssemblerSpecific // be unset if the code never needed to push its IonCode*. if (hasEnteredExitFrame()) { patchDataWithValueCheck(CodeLocationLabel(code, exitCodePatch_), - ImmWord(uintptr_t(code)), - ImmWord(uintptr_t(-1))); + ImmPtr(code), + ImmPtr((void*)-1)); } } @@ -794,9 +793,9 @@ class MacroAssembler : public MacroAssemblerSpecific // of the JSObject::isWrapper test performed in EmulatesUndefined. If none // of the branches are taken, we can check class flags directly. loadObjClass(objReg, scratch); - branchPtr(Assembler::Equal, scratch, ImmWord(&ObjectProxyObject::class_), slowCheck); - branchPtr(Assembler::Equal, scratch, ImmWord(&OuterWindowProxyObject::class_), slowCheck); - branchPtr(Assembler::Equal, scratch, ImmWord(&FunctionProxyObject::class_), slowCheck); + branchPtr(Assembler::Equal, scratch, ImmPtr(&ObjectProxyObject::class_), slowCheck); + branchPtr(Assembler::Equal, scratch, ImmPtr(&OuterWindowProxyObject::class_), slowCheck); + branchPtr(Assembler::Equal, scratch, ImmPtr(&FunctionProxyObject::class_), slowCheck); test32(Address(scratch, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED)); return truthy ? Assembler::Zero : Assembler::NonZero; @@ -841,7 +840,7 @@ class MacroAssembler : public MacroAssemblerSpecific void spsProfileEntryAddress(SPSProfiler *p, int offset, Register temp, Label *full) { - movePtr(ImmWord(p->sizePointer()), temp); + movePtr(ImmPtr(p->sizePointer()), temp); load32(Address(temp, 0), temp); if (offset != 0) add32(Imm32(offset), temp); @@ -850,7 +849,7 @@ class MacroAssembler : public MacroAssemblerSpecific // 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*))) JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*)); lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp); - addPtr(ImmWord(p->stack()), temp); + addPtr(ImmPtr(p->stack()), temp); } // The safe version of the above method refrains from assuming that the fields @@ -862,7 +861,7 @@ class MacroAssembler : public MacroAssemblerSpecific void spsProfileEntryAddressSafe(SPSProfiler *p, int offset, Register temp, Label *full) { - movePtr(ImmWord(p->addressOfSizePointer()), temp); + movePtr(ImmPtr(p->addressOfSizePointer()), temp); // Load size pointer loadPtr(Address(temp, 0), temp); @@ -879,7 +878,7 @@ class MacroAssembler : public MacroAssemblerSpecific JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*)); lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp); push(temp); - movePtr(ImmWord(p->addressOfStack()), temp); + movePtr(ImmPtr(p->addressOfStack()), temp); loadPtr(Address(temp, 0), temp); addPtr(Address(StackPointer, 0), temp); addPtr(Imm32(sizeof(size_t)), StackPointer); @@ -909,14 +908,14 @@ class MacroAssembler : public MacroAssemblerSpecific Label stackFull; spsProfileEntryAddress(p, 0, temp, &stackFull); - storePtr(ImmWord(str), Address(temp, ProfileEntry::offsetOfString())); - storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfScript())); - storePtr(ImmWord((void*) NULL), Address(temp, ProfileEntry::offsetOfStackAddress())); + storePtr(ImmPtr(str), Address(temp, ProfileEntry::offsetOfString())); + storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfScript())); + storePtr(ImmPtr(NULL), Address(temp, ProfileEntry::offsetOfStackAddress())); store32(Imm32(ProfileEntry::NullPCIndex), Address(temp, ProfileEntry::offsetOfPCIdx())); /* Always increment the stack size, whether or not we actually pushed. */ bind(&stackFull); - movePtr(ImmWord(p->sizePointer()), temp); + movePtr(ImmPtr(p->sizePointer()), temp); add32(Imm32(1), Address(temp, 0)); } @@ -932,7 +931,7 @@ class MacroAssembler : public MacroAssemblerSpecific loadPtr(script, temp2); storePtr(temp2, Address(temp, ProfileEntry::offsetOfScript())); - storePtr(ImmWord((void*) 0), Address(temp, ProfileEntry::offsetOfStackAddress())); + storePtr(ImmPtr(NULL), Address(temp, ProfileEntry::offsetOfStackAddress())); // Store 0 for PCIdx because that's what interpreter does. // (See Probes::enterScript, which calls spsProfiler.enter, which pushes an entry @@ -941,19 +940,19 @@ class MacroAssembler : public MacroAssemblerSpecific /* Always increment the stack size, whether or not we actually pushed. */ bind(&stackFull); - movePtr(ImmWord(p->addressOfSizePointer()), temp); + movePtr(ImmPtr(p->addressOfSizePointer()), temp); loadPtr(Address(temp, 0), temp); add32(Imm32(1), Address(temp, 0)); } void spsPopFrame(SPSProfiler *p, Register temp) { - movePtr(ImmWord(p->sizePointer()), temp); + movePtr(ImmPtr(p->sizePointer()), temp); add32(Imm32(-1), Address(temp, 0)); } // spsPropFrameSafe does not assume |profiler->sizePointer()| will stay constant. void spsPopFrameSafe(SPSProfiler *p, Register temp) { - movePtr(ImmWord(p->addressOfSizePointer()), temp); + movePtr(ImmPtr(p->addressOfSizePointer()), temp); loadPtr(Address(temp, 0), temp); add32(Imm32(-1), Address(temp, 0)); } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index c3a0e735100..7dff4a99f69 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1465,23 +1465,27 @@ KnownNonStringPrimitive(MDefinition *op) } void -MBinaryArithInstruction::infer(BaselineInspector *inspector, - jsbytecode *pc, - bool overflowed) +MBinaryArithInstruction::infer(BaselineInspector *inspector, jsbytecode *pc) { JS_ASSERT(this->type() == MIRType_Value); specialization_ = MIRType_None; - // Retrieve type information of lhs and rhs. - MIRType lhs = getOperand(0)->type(); - MIRType rhs = getOperand(1)->type(); + // Don't specialize if one operand could be an object. If we specialize + // as int32 or double based on baseline feedback, we could DCE this + // instruction and fail to invoke any valueOf methods. + if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) + return; // Anything complex - strings and objects - are not specialized // unless baseline type hints suggest it might be profitable if (!KnownNonStringPrimitive(getOperand(0)) || !KnownNonStringPrimitive(getOperand(1))) return inferFallback(inspector, pc); + // Retrieve type information of lhs and rhs. + MIRType lhs = getOperand(0)->type(); + MIRType rhs = getOperand(1)->type(); + // Guess a result type based on the inputs. // Don't specialize for neither-integer-nor-double results. if (lhs == MIRType_Int32 && rhs == MIRType_Int32) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 518a6d53309..d2b8d3fd6c1 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3216,8 +3216,7 @@ class MBinaryArithInstruction virtual double getIdentity() = 0; - void infer(BaselineInspector *inspector, - jsbytecode *pc, bool overflowed); + void infer(BaselineInspector *inspector, jsbytecode *pc); void setInt32() { specialization_ = MIRType_Int32; diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 94e440b9f1a..688b4ad16eb 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -124,9 +124,9 @@ RangeAnalysis::replaceDominatedUsesWith(MDefinition *orig, MDefinition *dom, } bool -RangeAnalysis::addBetaNobes() +RangeAnalysis::addBetaNodes() { - IonSpew(IonSpew_Range, "Adding beta nobes"); + IonSpew(IonSpew_Range, "Adding beta nodes"); for (PostorderIterator i(graph_.poBegin()); i != graph_.poEnd(); i++) { MBasicBlock *block = *i; @@ -230,9 +230,9 @@ RangeAnalysis::addBetaNobes() } bool -RangeAnalysis::removeBetaNobes() +RangeAnalysis::removeBetaNodes() { - IonSpew(IonSpew_Range, "Removing beta nobes"); + IonSpew(IonSpew_Range, "Removing beta nodes"); for (PostorderIterator i(graph_.poBegin()); i != graph_.poEnd(); i++) { MBasicBlock *block = *i; diff --git a/js/src/jit/RangeAnalysis.h b/js/src/jit/RangeAnalysis.h index 2e08a0cab56..c6ae0a51480 100644 --- a/js/src/jit/RangeAnalysis.h +++ b/js/src/jit/RangeAnalysis.h @@ -80,10 +80,10 @@ class RangeAnalysis public: MOZ_CONSTEXPR RangeAnalysis(MIRGenerator *mir, MIRGraph &graph) : mir(mir), graph_(graph) {} - bool addBetaNobes(); + bool addBetaNodes(); bool analyze(); bool addRangeAssertions(); - bool removeBetaNobes(); + bool removeBetaNodes(); bool truncate(); private: diff --git a/js/src/jit/arm/Assembler-arm.cpp b/js/src/jit/arm/Assembler-arm.cpp index 92a1fad66b8..be6294cd729 100644 --- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -2531,15 +2531,16 @@ Assembler::patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall } void -Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmWord newValue, ImmWord expectedValue) +Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue) { Instruction *ptr = (Instruction *) label.raw(); InstructionIterator iter(ptr); Register dest; Assembler::RelocStyle rs; DebugOnly val = getPtr32Target(&iter, &dest, &rs); - JS_ASSERT((uint32_t)(const uint32_t *)val == expectedValue.value); - reinterpret_cast(dummy)->ma_movPatchable(Imm32(newValue.value), dest, Always, rs, ptr); + JS_ASSERT((uint32_t)(const uint32_t *)val == uint32_t(expectedValue.value)); + reinterpret_cast(dummy)->ma_movPatchable(Imm32(int32_t(newValue.value)), + dest, Always, rs, ptr); // L_LDR won't cause any instructions to be updated. if (rs != L_LDR) { AutoFlushCache::updateTop(uintptr_t(ptr), 4); diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index ca5e8480f7a..3fafd37d03d 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1779,8 +1779,8 @@ class Assembler static uint32_t patchWrite_NearCallSize(); static uint32_t nopSize() { return 4; } static void patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall); - static void patchDataWithValueCheck(CodeLocationLabel label, ImmWord newValue, - ImmWord expectedValue); + static void patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, + ImmPtr expectedValue); static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm); static uint32_t alignDoubleArg(uint32_t offset) { return (offset+1)&~1; diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 817e1168c4d..10e0a7541db 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -377,6 +377,13 @@ MacroAssemblerARM::ma_movPatchable(Imm32 imm_, Register dest, } } +void +MacroAssemblerARM::ma_movPatchable(ImmPtr imm, Register dest, + Assembler::Condition c, RelocStyle rs, Instruction *i) +{ + return ma_movPatchable(Imm32(int32_t(imm.value)), dest, c, rs, i); +} + void MacroAssemblerARM::ma_mov(Register src, Register dest, SetCond_ sc, Assembler::Condition c) @@ -1546,7 +1553,7 @@ MacroAssemblerARMCompat::buildOOLFakeExitFrame(void *fakeReturnAddr) Push(Imm32(descriptor)); // descriptor_ enterNoPool(); - Push(Imm32((uint32_t) fakeReturnAddr)); + Push(ImmPtr(fakeReturnAddr)); leaveNoPool(); return true; @@ -1565,7 +1572,7 @@ MacroAssemblerARMCompat::callWithExitFrame(IonCode *target) else rs = L_LDR; - ma_movPatchable(Imm32((int) target->raw()), ScratchRegister, Always, rs); + ma_movPatchable(ImmPtr(target->raw()), ScratchRegister, Always, rs); ma_callIonHalfPush(ScratchRegister); } @@ -1583,7 +1590,7 @@ MacroAssemblerARMCompat::callWithExitFrame(IonCode *target, Register dynStack) else rs = L_LDR; - ma_movPatchable(Imm32((int) target->raw()), ScratchRegister, Always, rs); + ma_movPatchable(ImmPtr(target->raw()), ScratchRegister, Always, rs); ma_callIonHalfPush(ScratchRegister); } @@ -1756,6 +1763,11 @@ MacroAssemblerARMCompat::movePtr(const ImmGCPtr &imm, const Register &dest) ma_mov(imm, dest); } void +MacroAssemblerARMCompat::movePtr(const ImmPtr &imm, const Register &dest) +{ + movePtr(ImmWord(uintptr_t(imm.value)), dest); +} +void MacroAssemblerARMCompat::load8ZeroExtend(const Address &address, const Register &dest) { ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest); @@ -1896,7 +1908,7 @@ MacroAssemblerARMCompat::loadPtr(const BaseIndex &src, const Register &dest) void MacroAssemblerARMCompat::loadPtr(const AbsoluteAddress &address, const Register &dest) { - movePtr(ImmWord(address.addr), ScratchRegister); + movePtr(ImmWord(uintptr_t(address.addr)), ScratchRegister); loadPtr(Address(ScratchRegister, 0x0), dest); } @@ -2072,6 +2084,12 @@ MacroAssemblerARMCompat::storePtr(ImmWord imm, const Address &address) storePtr(ScratchRegister, address); } +void +MacroAssemblerARMCompat::storePtr(ImmPtr imm, const Address &address) +{ + storePtr(ImmWord(uintptr_t(imm.value)), address); +} + void MacroAssemblerARMCompat::storePtr(ImmGCPtr imm, const Address &address) { @@ -2088,7 +2106,7 @@ MacroAssemblerARMCompat::storePtr(Register src, const Address &address) void MacroAssemblerARMCompat::storePtr(const Register &src, const AbsoluteAddress &dest) { - movePtr(ImmWord(dest.addr), ScratchRegister); + movePtr(ImmWord(uintptr_t(dest.addr)), ScratchRegister); storePtr(src, Address(ScratchRegister, 0x0)); } @@ -2125,6 +2143,12 @@ MacroAssemblerARMCompat::cmpPtr(const Register &lhs, const ImmWord &rhs) ma_cmp(lhs, Imm32(rhs.value)); } +void +MacroAssemblerARMCompat::cmpPtr(const Register &lhs, const ImmPtr &rhs) +{ + return cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); +} + void MacroAssemblerARMCompat::cmpPtr(const Register &lhs, const Register &rhs) { @@ -2151,6 +2175,12 @@ MacroAssemblerARMCompat::cmpPtr(const Address &lhs, const ImmWord &rhs) ma_cmp(secondScratchReg_, Imm32(rhs.value)); } +void +MacroAssemblerARMCompat::cmpPtr(const Address &lhs, const ImmPtr &rhs) +{ + cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); +} + void MacroAssemblerARMCompat::setStackArg(const Register ®, uint32_t arg) { @@ -3005,7 +3035,7 @@ MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, Register base, Register index, void MacroAssemblerARMCompat::linkExitFrame() { uint8_t *dest = ((uint8_t*)GetIonContext()->runtime) + offsetof(JSRuntime, mainThread.ionTop); - movePtr(ImmWord(dest), ScratchRegister); + movePtr(ImmPtr(dest), ScratchRegister); ma_str(StackPointer, Operand(ScratchRegister, 0)); } @@ -3521,7 +3551,7 @@ MacroAssemblerARMCompat::toggledCall(IonCode *target, bool enabled) BufferOffset bo = nextOffset(); CodeOffsetLabel offset(bo.getOffset()); addPendingJump(bo, target->raw(), Relocation::IONCODE); - ma_movPatchable(Imm32(uint32_t(target->raw())), ScratchRegister, Always, hasMOVWT() ? L_MOVWT : L_LDR); + ma_movPatchable(ImmPtr(target->raw()), ScratchRegister, Always, hasMOVWT() ? L_MOVWT : L_LDR); if (enabled) ma_blx(ScratchRegister); else diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index aa55f5e0d21..fc3e455c149 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -95,6 +95,8 @@ class MacroAssemblerARM : public Assembler void ma_nop(); void ma_movPatchable(Imm32 imm, Register dest, Assembler::Condition c, RelocStyle rs, Instruction *i = NULL); + void ma_movPatchable(ImmPtr imm, Register dest, Assembler::Condition c, + RelocStyle rs, Instruction *i = NULL); // These should likely be wrapped up as a set of macros // or something like that. I cannot think of a good reason // to explicitly have all of this code. @@ -507,6 +509,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void mov(ImmWord imm, Register dest) { ma_mov(Imm32(imm.value), dest); } + void mov(ImmPtr imm, Register dest) { + mov(ImmWord(uintptr_t(imm.value)), dest); + } void mov(Register src, Address dest) { MOZ_ASSUME_UNREACHABLE("NYI-IC"); } @@ -527,6 +532,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM addPendingJump(bo, (void*)word.value, Relocation::HARDCODED); ma_call((void *) word.value); } + void call(ImmPtr imm) { + call(ImmWord(uintptr_t(imm.value))); + } void call(IonCode *c) { BufferOffset bo = m_buffer.nextOffset(); addPendingJump(bo, c->raw(), Relocation::IONCODE); @@ -626,6 +634,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM ma_movPatchable(Imm32(imm.value), dest, Always, hasMOVWT() ? L_MOVWT : L_LDR); return label; } + CodeOffsetLabel movWithPatch(ImmPtr imm, Register dest) { + return movWithPatch(ImmWord(uintptr_t(imm.value)), dest); + } void jump(Label *label) { as_b(label); @@ -820,7 +831,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM branch32(cond, lhs, rhs, label); } - void branchPrivatePtr(Condition cond, const Address &lhs, ImmWord ptr, Label *label) { + void branchPrivatePtr(Condition cond, const Address &lhs, ImmPtr ptr, Label *label) { branchPtr(cond, lhs, ptr, label); } @@ -919,6 +930,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void branchPtr(Condition cond, Register lhs, ImmWord imm, Label *label) { branch32(cond, lhs, Imm32(imm.value), label); } + void branchPtr(Condition cond, Register lhs, ImmPtr imm, Label *label) { + branchPtr(cond, lhs, ImmWord(uintptr_t(imm.value)), label); + } void decBranchPtr(Condition cond, const Register &lhs, Imm32 imm, Label *label) { subPtr(imm, lhs); branch32(cond, lhs, Imm32(0), label); @@ -947,6 +961,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM ma_cmp(secondScratchReg_, ptr); ma_b(label, cond); } + void branchPtr(Condition cond, Address addr, ImmPtr ptr, Label *label) { + branchPtr(cond, addr, ImmWord(uintptr_t(ptr.value)), label); + } void branchPtr(Condition cond, const AbsoluteAddress &addr, const Register &ptr, Label *label) { loadPtr(addr, secondScratchReg_); // ma_cmp will use the scratch register. ma_cmp(secondScratchReg_, ptr); @@ -1103,6 +1120,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM push(imm); adjustFrame(STACK_SLOT_SIZE); } + void Push(const ImmPtr imm) { + Push(ImmWord(uintptr_t(imm.value))); + } void Push(const ImmGCPtr ptr) { push(ptr); adjustFrame(STACK_SLOT_SIZE); @@ -1117,7 +1137,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM framePushed_ += sizeof(word.value); return pushWithPatch(word); } - + CodeOffsetLabel PushWithPatch(const ImmPtr &imm) { + return PushWithPatch(ImmWord(uintptr_t(imm.value))); + } void PushWithPadding(const Register ®, const Imm32 extraSpace) { pushWithPadding(reg, extraSpace); @@ -1183,6 +1205,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void movePtr(const Register &src, const Register &dest); void movePtr(const ImmWord &imm, const Register &dest); + void movePtr(const ImmPtr &imm, const Register &dest); void movePtr(const ImmGCPtr &imm, const Register &dest); void load8SignExtend(const Address &address, const Register &dest); @@ -1238,6 +1261,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void store32(const Imm32 &src, const BaseIndex &address); void storePtr(ImmWord imm, const Address &address); + void storePtr(ImmPtr imm, const Address &address); void storePtr(ImmGCPtr imm, const Address &address); void storePtr(Register src, const Address &address); void storePtr(const Register &src, const AbsoluteAddress &dest); @@ -1278,10 +1302,12 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void cmp32(const Operand &lhs, const Register &rhs); void cmpPtr(const Register &lhs, const ImmWord &rhs); + void cmpPtr(const Register &lhs, const ImmPtr &rhs); void cmpPtr(const Register &lhs, const Register &rhs); void cmpPtr(const Register &lhs, const ImmGCPtr &rhs); void cmpPtr(const Address &lhs, const Register &rhs); void cmpPtr(const Address &lhs, const ImmWord &rhs); + void cmpPtr(const Address &lhs, const ImmPtr &rhs); void subPtr(Imm32 imm, const Register dest); void subPtr(const Address &addr, const Register dest); @@ -1291,6 +1317,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void addPtr(ImmWord imm, const Register dest) { addPtr(Imm32(imm.value), dest); } + void addPtr(ImmPtr imm, const Register dest) { + addPtr(ImmWord(uintptr_t(imm.value)), dest); + } void setStackArg(const Register ®, uint32_t arg); diff --git a/js/src/jit/arm/Trampoline-arm.cpp b/js/src/jit/arm/Trampoline-arm.cpp index 808c89ab214..48d60600dc4 100644 --- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -849,7 +849,7 @@ IonRuntime::generatePreBarrier(JSContext *cx, MIRType type) masm.PushRegsInMask(save); JS_ASSERT(PreBarrierReg == r1); - masm.movePtr(ImmWord(cx->runtime()), r0); + masm.movePtr(ImmPtr(cx->runtime()), r0); masm.setupUnalignedABICall(2, r2); masm.passABIArg(r0); @@ -892,7 +892,7 @@ IonRuntime::generateDebugTrapHandler(JSContext *cx) // Enter a stub frame and call the HandleDebugTrap VM function. Ensure // the stub frame has a NULL ICStub pointer, since this pointer is marked // during GC. - masm.movePtr(ImmWord((void *)NULL), BaselineStubReg); + masm.movePtr(ImmPtr(NULL), BaselineStubReg); EmitEnterStubFrame(masm, scratch2); IonCode *code = cx->runtime()->ionRuntime()->getVMWrapper(HandleDebugTrapInfo); diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 82dd82d1c79..4c5772c9f98 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -95,22 +95,48 @@ struct Imm32 } }; -// Pointer-sized immediate. +// Pointer-sized integer to be embedded as an immediate in an instruction. struct ImmWord { uintptr_t value; explicit ImmWord(uintptr_t value) : value(value) { } - explicit ImmWord(const void *ptr) : value(reinterpret_cast(ptr)) +}; + +// Pointer to be embedded as an immediate in an instruction. +struct ImmPtr +{ + void *value; + + explicit ImmPtr(const void *value) : value(const_cast(value)) { } - // Note - this constructor is not implemented as it should not be used. - explicit ImmWord(gc::Cell *cell); + template + explicit ImmPtr(R (*pf)()) + : value(JS_FUNC_TO_DATA_PTR(void *, pf)) + { } + + template + explicit ImmPtr(R (*pf)(A1)) + : value(JS_FUNC_TO_DATA_PTR(void *, pf)) + { } + + template + explicit ImmPtr(R (*pf)(A1, A2)) + : value(JS_FUNC_TO_DATA_PTR(void *, pf)) + { } + + template + explicit ImmPtr(R (*pf)(A1, A2, A3)) + : value(JS_FUNC_TO_DATA_PTR(void *, pf)) + { } + + template + explicit ImmPtr(R (*pf)(A1, A2, A3, A4)) + : value(JS_FUNC_TO_DATA_PTR(void *, pf)) + { } - void *asPointer() { - return reinterpret_cast(value); - } }; // Used for immediates which require relocation. @@ -138,7 +164,8 @@ struct ImmMaybeNurseryPtr : public ImmGCPtr } }; -// Specifies a hardcoded, absolute address. +// Pointer to be embedded as an immediate that is loaded/stored from by an +// instruction. struct AbsoluteAddress { void *addr; diff --git a/js/src/jit/shared/Assembler-x86-shared.h b/js/src/jit/shared/Assembler-x86-shared.h index 104e5c55a73..1cb8b015d08 100644 --- a/js/src/jit/shared/Assembler-x86-shared.h +++ b/js/src/jit/shared/Assembler-x86-shared.h @@ -780,6 +780,9 @@ class AssemblerX86Shared MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); } } + void cmpl(const Operand &op, ImmPtr imm) { + cmpl(op, ImmWord(uintptr_t(imm.value))); + } void setCC(Condition cond, const Register &r) { masm.setCC_r(static_cast(cond), r.code()); } @@ -1477,12 +1480,12 @@ class AssemblerX86Shared *((int32_t *) dataLabel.raw() - 1) = toWrite.value; } - static void patchDataWithValueCheck(CodeLocationLabel data, ImmWord newData, - ImmWord expectedData) { + static void patchDataWithValueCheck(CodeLocationLabel data, ImmPtr newData, + ImmPtr expectedData) { // The pointer given is a pointer to *after* the data. uintptr_t *ptr = ((uintptr_t *) data.raw()) - 1; - JS_ASSERT(*ptr == expectedData.value); - *ptr = newData.value; + JS_ASSERT(*ptr == (uintptr_t)expectedData.value); + *ptr = (uintptr_t)newData.value; } static uint32_t nopSize() { return 1; diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index d9bc4386cca..b7a3aa7ee73 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -829,24 +829,24 @@ CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir, masm.move32(Imm32(blockIndex), blockIndexReg); masm.move32(Imm32(lir->id()), lirIndexReg); masm.move32(Imm32(emi), emiReg); - masm.movePtr(ImmWord(lir->opName()), lirOpNameReg); + masm.movePtr(ImmPtr(lir->opName()), lirOpNameReg); if (MDefinition *mir = lir->mirRaw()) { - masm.movePtr(ImmWord(mir->opName()), mirOpNameReg); - masm.movePtr(ImmWord((void *)mir->block()->info().script()), scriptReg); - masm.movePtr(ImmWord(mir->trackedPc()), pcReg); + masm.movePtr(ImmPtr(mir->opName()), mirOpNameReg); + masm.movePtr(ImmPtr(mir->block()->info().script()), scriptReg); + masm.movePtr(ImmPtr(mir->trackedPc()), pcReg); } else { - masm.movePtr(ImmWord((void *)NULL), mirOpNameReg); - masm.movePtr(ImmWord((void *)NULL), scriptReg); - masm.movePtr(ImmWord((void *)NULL), pcReg); + masm.movePtr(ImmPtr(NULL), mirOpNameReg); + masm.movePtr(ImmPtr(NULL), scriptReg); + masm.movePtr(ImmPtr(NULL), pcReg); } } else { masm.move32(Imm32(0xDEADBEEF), blockIndexReg); masm.move32(Imm32(0xDEADBEEF), lirIndexReg); masm.move32(Imm32(emi), emiReg); - masm.movePtr(ImmWord(bailoutName), lirOpNameReg); - masm.movePtr(ImmWord(bailoutName), mirOpNameReg); - masm.movePtr(ImmWord((void *)NULL), scriptReg); - masm.movePtr(ImmWord((void *)NULL), pcReg); + masm.movePtr(ImmPtr(bailoutName), lirOpNameReg); + masm.movePtr(ImmPtr(bailoutName), mirOpNameReg); + masm.movePtr(ImmPtr(NULL), scriptReg); + masm.movePtr(ImmPtr(NULL), pcReg); } masm.setupUnalignedABICall(7, CallTempReg4); diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index fabbb663ca0..6c090894a03 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -1495,7 +1495,7 @@ CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard) Register tmp = ToRegister(guard->tempInt()); masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp); - masm.cmpPtr(Operand(tmp, offsetof(types::TypeObject, clasp)), ImmWord(guard->mir()->getClass())); + masm.cmpPtr(Operand(tmp, offsetof(types::TypeObject, clasp)), ImmPtr(guard->mir()->getClass())); if (!bailoutIf(Assembler::NotEqual, guard->snapshot())) return false; return true; diff --git a/js/src/jit/shared/MacroAssembler-x86-shared.h b/js/src/jit/shared/MacroAssembler-x86-shared.h index 9f07bf7fd8f..2abff48ccc9 100644 --- a/js/src/jit/shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/shared/MacroAssembler-x86-shared.h @@ -205,6 +205,9 @@ class MacroAssemblerX86Shared : public Assembler framePushed_ += sizeof(word.value); return pushWithPatch(word); } + CodeOffsetLabel PushWithPatch(const ImmPtr &imm) { + return PushWithPatch(ImmWord(uintptr_t(imm.value))); + } template void Pop(const T &t) { @@ -528,7 +531,7 @@ class MacroAssemblerX86Shared : public Assembler bool buildOOLFakeExitFrame(void *fakeReturnAddr) { uint32_t descriptor = MakeFrameDescriptor(framePushed(), IonFrame_OptimizedJS); Push(Imm32(descriptor)); - Push(ImmWord(fakeReturnAddr)); + Push(ImmPtr(fakeReturnAddr)); return true; } diff --git a/js/src/jit/x64/Assembler-x64.h b/js/src/jit/x64/Assembler-x64.h index 9bbae3a5f59..7c386fd619c 100644 --- a/js/src/jit/x64/Assembler-x64.h +++ b/js/src/jit/x64/Assembler-x64.h @@ -347,6 +347,9 @@ class Assembler : public AssemblerX86Shared push(ScratchReg); } } + void push(const ImmPtr &imm) { + push(ImmWord(uintptr_t(imm.value))); + } void push(const FloatRegister &src) { subq(Imm32(sizeof(double)), StackPointer); movsd(src, Operand(StackPointer, 0)); @@ -366,6 +369,9 @@ class Assembler : public AssemblerX86Shared masm.movq_i64r(word.value, dest.code()); return masm.currentOffset(); } + CodeOffsetLabel movWithPatch(const ImmPtr &imm, const Register &dest) { + return movWithPatch(ImmWord(uintptr_t(imm.value)), dest); + } // Load an ImmWord value into a register. Note that this instruction will // attempt to optimize its immediate field size. When a full 64-bit @@ -385,6 +391,9 @@ class Assembler : public AssemblerX86Shared masm.movq_i64r(word.value, dest.code()); } } + void movq(ImmPtr imm, const Register &dest) { + movq(ImmWord(uintptr_t(imm.value)), dest); + } void movq(ImmGCPtr ptr, const Register &dest) { masm.movq_i64r(ptr.value, dest.code()); writeDataRelocation(ptr); @@ -541,6 +550,9 @@ class Assembler : public AssemblerX86Shared void mov(ImmWord word, const Register &dest) { movq(word, dest); } + void mov(ImmPtr imm, const Register &dest) { + movq(imm, dest); + } void mov(const Imm32 &imm32, const Register &dest) { movl(imm32, dest); } diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 1bd45b9b583..5bc9dc89a3c 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -287,8 +287,7 @@ CodeGeneratorX64::visitInterruptCheck(LInterruptCheck *lir) if (!ool) return false; - void *interrupt = (void*)&GetIonContext()->runtime->interrupt; - masm.movq(ImmWord(interrupt), ScratchReg); + masm.movq(ImmPtr(&GetIonContext()->runtime->interrupt), ScratchReg); masm.cmpl(Operand(ScratchReg, 0), Imm32(0)); masm.j(Assembler::NonZero, ool->entry()); masm.bind(ool->rejoin()); diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 2689021c479..09bd3e23244 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -196,7 +196,7 @@ MacroAssemblerX64::callWithABI(void *fun, Result result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); - call(ImmWord(fun)); + call(ImmPtr(fun)); callWithABIPost(stackAdjust, result); } diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index 9ca70cbdf48..1cf14075b08 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -98,6 +98,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared mov(target, rax); call(rax); } + void call(ImmPtr target) { + call(ImmWord(uintptr_t(target.value))); + } // Refers to the upper 32 bits of a 64-bit Value operand. // On x86_64, the upper 32 bits do not necessarily only contain the type. @@ -215,7 +218,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared void moveValue(const Value &val, const Register &dest) { jsval_layout jv = JSVAL_TO_IMPL(val); - movWithPatch(ImmWord(jv.asPtr), dest); + movWithPatch(ImmWord(jv.asBits), dest); writeDataRelocation(val); } void moveValue(const Value &src, const ValueOperand &dest) { @@ -400,6 +403,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared mov(rhs, ScratchReg); cmpq(lhs, ScratchReg); } + void cmpPtr(const Register &lhs, const ImmPtr rhs) { + cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); + } void cmpPtr(const Register &lhs, const ImmGCPtr rhs) { JS_ASSERT(lhs != ScratchReg); movq(rhs, ScratchReg); @@ -417,12 +423,18 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared cmpq(lhs, ScratchReg); } } + void cmpPtr(const Operand &lhs, const ImmPtr rhs) { + cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); + } void cmpPtr(const Address &lhs, const ImmGCPtr rhs) { cmpPtr(Operand(lhs), rhs); } void cmpPtr(const Address &lhs, const ImmWord rhs) { cmpPtr(Operand(lhs), rhs); } + void cmpPtr(const Address &lhs, const ImmPtr rhs) { + cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); + } void cmpPtr(const Operand &lhs, const Register &rhs) { cmpq(lhs, rhs); } @@ -480,6 +492,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared addq(ScratchReg, dest); } } + void addPtr(ImmPtr imm, const Register &dest) { + addPtr(ImmWord(uintptr_t(imm.value)), dest); + } void addPtr(const Address &src, const Register &dest) { addq(Operand(src), dest); } @@ -494,24 +509,23 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared } void branch32(Condition cond, const AbsoluteAddress &lhs, Imm32 rhs, Label *label) { - mov(ImmWord(lhs.addr), ScratchReg); + mov(ImmPtr(lhs.addr), ScratchReg); branch32(cond, Address(ScratchReg, 0), rhs, label); } void branch32(Condition cond, const AbsoluteAddress &lhs, Register rhs, Label *label) { - mov(ImmWord(lhs.addr), ScratchReg); + mov(ImmPtr(lhs.addr), ScratchReg); branch32(cond, Address(ScratchReg, 0), rhs, label); } // Specialization for AbsoluteAddress. void branchPtr(Condition cond, const AbsoluteAddress &addr, const Register &ptr, Label *label) { JS_ASSERT(ptr != ScratchReg); - mov(ImmWord(addr.addr), ScratchReg); + mov(ImmPtr(addr.addr), ScratchReg); branchPtr(cond, Operand(ScratchReg, 0x0), ptr, label); } - template - void branchPrivatePtr(Condition cond, T lhs, ImmWord ptr, Label *label) { - branchPtr(cond, lhs, ImmWord(ptr.value >> 1), label); + void branchPrivatePtr(Condition cond, Address lhs, ImmPtr ptr, Label *label) { + branchPtr(cond, lhs, ImmWord(uintptr_t(ptr.value) >> 1), label); } void branchPrivatePtr(Condition cond, Address lhs, Register ptr, Label *label) { @@ -572,11 +586,14 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared void movePtr(ImmWord imm, Register dest) { mov(imm, dest); } + void movePtr(ImmPtr imm, Register dest) { + mov(imm, dest); + } void movePtr(ImmGCPtr imm, Register dest) { movq(imm, dest); } void loadPtr(const AbsoluteAddress &address, Register dest) { - mov(ImmWord(address.addr), ScratchReg); + mov(ImmPtr(address.addr), ScratchReg); movq(Operand(ScratchReg, 0x0), dest); } void loadPtr(const Address &address, Register dest) { @@ -600,6 +617,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared movq(ScratchReg, Operand(address)); } } + void storePtr(ImmPtr imm, const Address &address) { + storePtr(ImmWord(uintptr_t(imm.value)), address); + } void storePtr(ImmGCPtr imm, const Address &address) { movq(imm, ScratchReg); movq(ScratchReg, Operand(address)); @@ -611,7 +631,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared movq(src, dest); } void storePtr(const Register &src, const AbsoluteAddress &address) { - mov(ImmWord(address.addr), ScratchReg); + mov(ImmPtr(address.addr), ScratchReg); movq(src, Operand(ScratchReg, 0x0)); } void rshiftPtr(Imm32 imm, Register dest) { @@ -1045,7 +1065,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared } void inc64(AbsoluteAddress dest) { - mov(ImmWord(dest.addr), ScratchReg); + mov(ImmPtr(dest.addr), ScratchReg); addPtr(Imm32(1), Address(ScratchReg, 0)); } @@ -1110,7 +1130,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared // Save an exit frame (which must be aligned to the stack pointer) to // ThreadData::ionTop of the main thread. void linkExitFrame() { - mov(ImmWord(GetIonContext()->runtime), ScratchReg); + mov(ImmPtr(GetIonContext()->runtime), ScratchReg); mov(StackPointer, Operand(ScratchReg, offsetof(JSRuntime, mainThread.ionTop))); } diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index 137042e96f5..0c6c7878ddf 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -703,7 +703,7 @@ IonRuntime::generatePreBarrier(JSContext *cx, MIRType type) masm.PushRegsInMask(regs); JS_ASSERT(PreBarrierReg == rdx); - masm.mov(ImmWord(cx->runtime()), rcx); + masm.mov(ImmPtr(cx->runtime()), rcx); masm.setupUnalignedABICall(2, rax); masm.passABIArg(rcx); @@ -750,7 +750,7 @@ IonRuntime::generateDebugTrapHandler(JSContext *cx) // Enter a stub frame and call the HandleDebugTrap VM function. Ensure // the stub frame has a NULL ICStub pointer, since this pointer is marked // during GC. - masm.movePtr(ImmWord((void *)NULL), BaselineStubReg); + masm.movePtr(ImmPtr(NULL), BaselineStubReg); EmitEnterStubFrame(masm, scratch3); IonCode *code = cx->runtime()->ionRuntime()->getVMWrapper(HandleDebugTrapInfo); diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 03c8169f5d2..71123c4eb67 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -273,6 +273,9 @@ class Assembler : public AssemblerX86Shared void push(const ImmWord imm) { push(Imm32(imm.value)); } + void push(const ImmPtr imm) { + push(ImmWord(uintptr_t(imm.value))); + } void push(const FloatRegister &src) { subl(Imm32(sizeof(double)), StackPointer); movsd(src, Operand(StackPointer, 0)); @@ -292,6 +295,9 @@ class Assembler : public AssemblerX86Shared movl(Imm32(word.value), dest); return masm.currentOffset(); } + CodeOffsetLabel movWithPatch(const ImmPtr &imm, const Register &dest) { + return movWithPatch(ImmWord(uintptr_t(imm.value)), dest); + } void movl(const ImmGCPtr &ptr, const Register &dest) { masm.movl_i32r(ptr.value, dest.code()); @@ -318,9 +324,15 @@ class Assembler : public AssemblerX86Shared void movl(ImmWord imm, Register dest) { masm.movl_i32r(imm.value, dest.code()); } + void movl(ImmPtr imm, Register dest) { + movl(ImmWord(uintptr_t(imm.value)), dest); + } void mov(ImmWord imm, Register dest) { movl(imm, dest); } + void mov(ImmPtr imm, Register dest) { + movl(imm, dest); + } void mov(Imm32 imm, Register dest) { movl(imm, dest); } @@ -353,6 +365,9 @@ class Assembler : public AssemblerX86Shared void cmpl(const Register src, ImmWord ptr) { masm.cmpl_ir(ptr.value, src.code()); } + void cmpl(const Register src, ImmPtr imm) { + cmpl(src, ImmWord(uintptr_t(imm.value))); + } void cmpl(const Register src, ImmGCPtr ptr) { masm.cmpl_ir(ptr.value, src.code()); writeDataRelocation(ptr); @@ -405,7 +420,10 @@ class Assembler : public AssemblerX86Shared } void call(ImmWord target) { JmpSrc src = masm.call(); - addPendingJump(src, target.asPointer(), Relocation::HARDCODED); + addPendingJump(src, (void*)target.value, Relocation::HARDCODED); + } + void call(ImmPtr target) { + call(ImmWord(uintptr_t(target.value))); } // Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index ccb96c20341..fbdad449b2e 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -231,7 +231,7 @@ MacroAssemblerX86::callWithABI(void *fun, Result result) { uint32_t stackAdjust; callWithABIPre(&stackAdjust); - call(ImmWord(fun)); + call(ImmPtr(fun)); callWithABIPost(stackAdjust, result); } diff --git a/js/src/jit/x86/MacroAssembler-x86.h b/js/src/jit/x86/MacroAssembler-x86.h index 3efa79cc157..c707f9b743b 100644 --- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -461,12 +461,18 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void cmpPtr(Register lhs, const ImmWord rhs) { cmpl(lhs, Imm32(rhs.value)); } + void cmpPtr(Register lhs, const ImmPtr imm) { + cmpPtr(lhs, ImmWord(uintptr_t(imm.value))); + } void cmpPtr(Register lhs, const ImmGCPtr rhs) { cmpl(lhs, rhs); } void cmpPtr(const Operand &lhs, const ImmWord rhs) { cmpl(lhs, rhs); } + void cmpPtr(const Operand &lhs, const ImmPtr imm) { + cmpPtr(lhs, ImmWord(uintptr_t(imm.value))); + } void cmpPtr(const Operand &lhs, const ImmGCPtr rhs) { cmpl(lhs, rhs); } @@ -479,6 +485,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void cmpPtr(const Address &lhs, const ImmWord rhs) { cmpl(Operand(lhs), rhs); } + void cmpPtr(const Address &lhs, const ImmPtr rhs) { + cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); + } void cmpPtr(Register lhs, Register rhs) { cmpl(lhs, rhs); } @@ -515,6 +524,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void addPtr(ImmWord imm, const Register &dest) { addl(Imm32(imm.value), dest); } + void addPtr(ImmPtr imm, const Register &dest) { + addPtr(ImmWord(uintptr_t(imm.value)), dest); + } void addPtr(Imm32 imm, const Address &dest) { addl(imm, Operand(dest)); } @@ -549,8 +561,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared j(cond, label); } - template - void branchPrivatePtr(Condition cond, T lhs, ImmWord ptr, Label *label) { + void branchPrivatePtr(Condition cond, const Address &lhs, ImmPtr ptr, Label *label) { branchPtr(cond, lhs, ptr, label); } @@ -607,6 +618,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void movePtr(ImmWord imm, Register dest) { movl(Imm32(imm.value), dest); } + void movePtr(ImmPtr imm, Register dest) { + movl(imm, dest); + } void movePtr(ImmGCPtr imm, Register dest) { movl(imm, dest); } @@ -628,6 +642,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared void storePtr(ImmWord imm, const Address &address) { movl(Imm32(imm.value), Operand(address)); } + void storePtr(ImmPtr imm, const Address &address) { + storePtr(ImmWord(uintptr_t(imm.value)), address); + } void storePtr(ImmGCPtr imm, const Address &address) { movl(imm, Operand(address)); } diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index 8c9a2f14287..0b3d724b81d 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -738,7 +738,7 @@ IonRuntime::generatePreBarrier(JSContext *cx, MIRType type) masm.PushRegsInMask(save); JS_ASSERT(PreBarrierReg == edx); - masm.movl(ImmWord(cx->runtime()), ecx); + masm.movl(ImmPtr(cx->runtime()), ecx); masm.setupUnalignedABICall(2, eax); masm.passABIArg(ecx); @@ -786,7 +786,7 @@ IonRuntime::generateDebugTrapHandler(JSContext *cx) // Enter a stub frame and call the HandleDebugTrap VM function. Ensure // the stub frame has a NULL ICStub pointer, since this pointer is marked // during GC. - masm.movePtr(ImmWord((void *)NULL), BaselineStubReg); + masm.movePtr(ImmPtr(NULL), BaselineStubReg); EmitEnterStubFrame(masm, scratch3); IonCode *code = cx->runtime()->ionRuntime()->getVMWrapper(HandleDebugTrapInfo); diff --git a/js/src/jsapi-tests/testGCFinalizeCallback.cpp b/js/src/jsapi-tests/testGCFinalizeCallback.cpp index e796e424b4e..d4ad33c7fe7 100644 --- a/js/src/jsapi-tests/testGCFinalizeCallback.cpp +++ b/js/src/jsapi-tests/testGCFinalizeCallback.cpp @@ -9,8 +9,6 @@ #include "jsapi-tests/tests.h" -#include "vm/ObjectImpl-inl.h" - const unsigned BufferSize = 20; static unsigned FinalizeCalls = 0; static JSFinalizeStatus StatusBuffer[BufferSize]; diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index e7b7b3cd779..a55c3536dfc 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -8,6 +8,7 @@ #define jscntxtinlines_h #include "jscntxt.h" +#include "jscompartment.h" #include "jsiter.h" #include "jsworkers.h" @@ -18,8 +19,6 @@ #include "vm/Interpreter.h" #include "vm/ProxyObject.h" -#include "gc/Barrier-inl.h" - namespace js { #ifdef JS_CRASH_DIAGNOSTICS diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 707cce5e844..1a2053a9faf 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -29,8 +29,6 @@ #include "jsgcinlines.h" #include "jsinferinlines.h" -#include "gc/Barrier-inl.h" - using namespace js; using namespace js::gc; diff --git a/js/src/jscompartmentinlines.h b/js/src/jscompartmentinlines.h index c371be006c8..c2a6c5ca118 100644 --- a/js/src/jscompartmentinlines.h +++ b/js/src/jscompartmentinlines.h @@ -9,6 +9,8 @@ #include "jscompartment.h" +#include "gc/Barrier-inl.h" + inline void JSCompartment::initGlobal(js::GlobalObject &global) { diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index fac8afe1676..57d13462656 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3556,7 +3556,7 @@ JSObject::makeLazyType(JSContext *cx, HandleObject obj) if (!type) { if (cx->typeInferenceEnabled()) cx->compartment()->types.setPendingNukeTypes(cx); - return obj->type_; + return NULL; } if (!cx->typeInferenceEnabled()) { diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index 07f0dbfcd8d..947437858dd 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -1614,20 +1614,6 @@ Property::Property(const Property &o) { } -inline bool -HasOperationOverflowed(JSScript *script, jsbytecode *pc) -{ - types::TypeResult *result = script->types->dynamicList; - while (result) { - if (result->offset == uint32_t(pc - script->code)) { - if (result->type == types::Type::DoubleType()) - return true; - } - result = result->next; - } - return false; -} - inline bool IterationValuesMustBeStrings(JSScript *script) { diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 850d9f747e5..5882ccbb23d 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -18,6 +18,7 @@ #include "jsatominlines.h" #include "jscompartmentinlines.h" +#include "jsgcinlines.h" #include "jsinferinlines.h" #include "vm/Shape-inl.h" diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp index 2a83ae3ea61..2a1111d6f59 100644 --- a/js/src/jswatchpoint.cpp +++ b/js/src/jswatchpoint.cpp @@ -14,8 +14,6 @@ #include "jsgcinlines.h" -#include "gc/Barrier-inl.h" - using namespace js; using namespace js::gc; diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index f1f6095b32b..9271ddf767d 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -19,8 +19,6 @@ #include "jsobjinlines.h" -#include "gc/Barrier-inl.h" - using namespace js; WeakMapBase::WeakMapBase(JSObject *memOf, JSCompartment *c) diff --git a/js/src/moz.build b/js/src/moz.build index 9e8e4b60881..6008e8a5526 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -349,3 +349,7 @@ else: MSVC_ENABLE_PGO = True +HOST_CPPSRCS += [ + 'jskwgen.cpp', + 'jsoplengen.cpp', +] diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index c051f9464b7..287c0fa2d40 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -24,6 +24,7 @@ #include "jsobjinlines.h" #include "vm/Stack-inl.h" +#include "vm/String-inl.h" namespace js { diff --git a/js/src/vm/ObjectImpl-inl.h b/js/src/vm/ObjectImpl-inl.h index 3323b96521d..60cd7348138 100644 --- a/js/src/vm/ObjectImpl-inl.h +++ b/js/src/vm/ObjectImpl-inl.h @@ -9,12 +9,11 @@ #include "vm/ObjectImpl.h" +#include "jscntxt.h" #include "jsproxy.h" #include "vm/ProxyObject.h" -#include "gc/Barrier-inl.h" - /* static */ inline bool js::ObjectImpl::isExtensible(ExclusiveContext *cx, js::Handle obj, bool *extensible) { diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 7c0f508d838..57c83934113 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -21,7 +21,6 @@ #include "jsobjinlines.h" #include "jsscriptinlines.h" -#include "gc/Barrier-inl.h" #include "vm/Stack-inl.h" using namespace js; diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in index 51b8f933f90..5b10f925316 100644 --- a/layout/build/Makefile.in +++ b/layout/build/Makefile.in @@ -38,6 +38,7 @@ SHARED_LIBRARY_LIBS = \ $(DEPTH)/content/xul/document/src/$(LIB_PREFIX)gkconxuldoc_s.$(LIB_SUFFIX) \ $(DEPTH)/view/src/$(LIB_PREFIX)gkview_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/activities/src/$(LIB_PREFIX)dom_activities_s.$(LIB_SUFFIX) \ + $(DEPTH)/dom/apps/src/$(LIB_PREFIX)dom_apps_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/base/$(LIB_PREFIX)jsdombase_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/battery/$(LIB_PREFIX)dom_battery_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/alarm/$(LIB_PREFIX)domalarm_s.$(LIB_SUFFIX) \ diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 586eefc43a8..4ae04248577 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -485,7 +485,7 @@ typedef uint32_t nsReflowStatus; // Construct a line-break-before status. Note that there is no // completion status for a line-break before because we *know* that -// the frame will be reflowed later and hence it's current completion +// the frame will be reflowed later and hence its current completion // status doesn't matter. #define NS_INLINE_LINE_BREAK_BEFORE() \ (NS_INLINE_BREAK | NS_INLINE_BREAK_BEFORE | \ @@ -1002,7 +1002,7 @@ public: * @param aReflowState An optional reflow state parameter, which is used if * ApplySkipSides() is being called in the middle of reflow. * - * @note (See also bug 743402, comment 11) GetSkipSides() and it's sister + * @note (See also bug 743402, comment 11) GetSkipSides() and its sister * method, ApplySkipSides() checks to see if this frame has a previous * or next continuation to determine if a side should be skipped. * Unfortunately, this only works after reflow has been completed. In @@ -2420,7 +2420,7 @@ public: * Determine whether borders should not be painted on certain sides of the * frame. * - * @note (See also bug 743402, comment 11) GetSkipSides() and it's sister + * @note (See also bug 743402, comment 11) GetSkipSides() and its sister * method, ApplySkipSides() checks to see if this frame has a previous * or next continuation to determine if a side should be skipped. * Unfortunately, this only works after reflow has been completed. In diff --git a/layout/reftests/position-sticky/reftest.list b/layout/reftests/position-sticky/reftest.list index 77d8bfc2ec7..e016b8d8b21 100644 --- a/layout/reftests/position-sticky/reftest.list +++ b/layout/reftests/position-sticky/reftest.list @@ -4,7 +4,7 @@ default-preferences pref(layout.css.sticky.enabled,true) == top-1.html top-1-ref.html fuzzy-if(Android,6,914) == top-2.html top-2-ref.html -fuzzy-if(Android,5,2729) == top-3.html top-3-ref.html +fuzzy-if(Android,6,2729) == top-3.html top-3-ref.html == top-4.html top-4-ref.html == top-5.html top-5-ref.html == top-6.html top-6-ref.html diff --git a/layout/style/nsStyleUtil.cpp b/layout/style/nsStyleUtil.cpp index 82cc4408c97..4f67fa855cd 100644 --- a/layout/style/nsStyleUtil.cpp +++ b/layout/style/nsStyleUtil.cpp @@ -464,7 +464,7 @@ nsStyleUtil::CSPAllowsInlineStyle(nsIPrincipal* aPrincipal, if (csp) { bool inlineOK = true; - bool reportViolation = false; + bool reportViolation; rv = csp->GetAllowsInlineStyle(&reportViolation, &inlineOK); if (NS_FAILED(rv)) { if (aRv) @@ -485,9 +485,9 @@ nsStyleUtil::CSPAllowsInlineStyle(nsIPrincipal* aPrincipal, } csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE, - NS_ConvertUTF8toUTF16(asciiSpec), - aStyleText, - aLineNumber); + NS_ConvertUTF8toUTF16(asciiSpec), + aStyleText, + aLineNumber); } if (!inlineOK) { diff --git a/layout/style/test/Makefile.in b/layout/style/test/Makefile.in index ebc9aab2cd5..fb3d6cd0d81 100644 --- a/layout/style/test/Makefile.in +++ b/layout/style/test/Makefile.in @@ -3,11 +3,12 @@ # 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/. -HOST_CPPSRCS = \ +# Preserve until HOST_SIMPLE_PROGRAMS has been converted. +host_cppsrcs = \ ListCSSProperties.cpp \ $(NULL) -HOST_SIMPLE_PROGRAMS = $(addprefix host_, $(HOST_CPPSRCS:.cpp=$(HOST_BIN_SUFFIX))) +HOST_SIMPLE_PROGRAMS = $(addprefix host_, $(host_cppsrcs:.cpp=$(HOST_BIN_SUFFIX))) # ParseCSS.cpp used to be built as a test program, but it was not # being used for anything, and recent changes to the CSS loader have diff --git a/layout/style/test/moz.build b/layout/style/test/moz.build index 77fedb23874..b0baeb83d31 100644 --- a/layout/style/test/moz.build +++ b/layout/style/test/moz.build @@ -8,3 +8,6 @@ TEST_DIRS += ['chrome'] MODULE = 'layout' +HOST_CPPSRCS += [ + 'ListCSSProperties.cpp', +] diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 2e430a66751..74ec99e81e4 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -4396,3 +4396,6 @@ pref("dom.forms.inputmode", false); #else pref("dom.forms.inputmode", true); #endif + +// DOM Inter-App Communication API. +pref("dom.inter-app-communication-api.enabled", false); diff --git a/mozglue/linker/Makefile.in b/mozglue/linker/Makefile.in index 7016ef70b42..70b239d7b8a 100644 --- a/mozglue/linker/Makefile.in +++ b/mozglue/linker/Makefile.in @@ -7,11 +7,6 @@ STL_FLAGS = HOST_PROGRAM = szip -HOST_CPPSRCS = \ - szip.cpp \ - SeekableZStream.cpp \ - $(NULL) - HOST_LIBS = -lz DEFINES += -DIMPL_MFBT diff --git a/mozglue/linker/moz.build b/mozglue/linker/moz.build index adf40f9f555..75c1df0907f 100644 --- a/mozglue/linker/moz.build +++ b/mozglue/linker/moz.build @@ -16,3 +16,7 @@ CPP_SOURCES += [ LIBRARY_NAME = 'linker' +HOST_CPPSRCS += [ + 'SeekableZStream.cpp', + 'szip.cpp', +] diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 4b3f6bc951f..7db779763b0 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals +import itertools import logging import operator import os @@ -160,11 +161,12 @@ class BuildProgressFooter(object): ' ', '(', ]) - for d in active_dirs: - parts.extend([ - ('magenta', d), ' ,' - ]) - parts[-1] = ')' + if active_dirs: + commas = [', '] * (len(active_dirs) - 1) + magenta_dirs = [('magenta', d) for d in active_dirs] + parts.extend(i.next() for i in itertools.cycle((iter(magenta_dirs), + iter(commas)))) + parts.append(')') if not have_dirs: parts = parts[0:-2] diff --git a/testing/marionette/client/marionette/marionette.py b/testing/marionette/client/marionette/marionette.py index 5d5da9fe763..08f5260c0c3 100644 --- a/testing/marionette/client/marionette/marionette.py +++ b/testing/marionette/client/marionette/marionette.py @@ -421,7 +421,8 @@ class Marionette(object): profile=None, emulator=None, sdcard=None, emulatorBinary=None, emulatorImg=None, emulator_res=None, gecko_path=None, connectToRunningEmulator=False, homedir=None, baseurl=None, - noWindow=False, logcat_dir=None, busybox=None, symbols_path=None, timeout=None): + noWindow=False, logcat_dir=None, busybox=None, symbols_path=None, + timeout=None, device_serial=None): self.host = host self.port = self.local_port = port self.bin = bin @@ -438,6 +439,7 @@ class Marionette(object): self._test_name = None self.symbols_path = symbols_path self.timeout = timeout + self.device_serial = device_serial if bin: port = int(self.port) diff --git a/testing/marionette/client/marionette/runtests.py b/testing/marionette/client/marionette/runtests.py index 4375b4ba1a6..7f8f678e486 100644 --- a/testing/marionette/client/marionette/runtests.py +++ b/testing/marionette/client/marionette/runtests.py @@ -330,7 +330,8 @@ class MarionetteTestRunner(object): bin=self.bin, profile=self.profile, baseurl=self.baseurl, - timeout=self.timeout) + timeout=self.timeout, + device_serial=self.device_serial) elif self.address: host, port = self.address.split(':') try: diff --git a/testing/mochitest/android.json b/testing/mochitest/android.json index c5dc3a1633f..961c297f29d 100644 --- a/testing/mochitest/android.json +++ b/testing/mochitest/android.json @@ -184,6 +184,7 @@ "dom/tests/mochitest/geolocation/test_allowWatch.html": "TIMED_OUT", "dom/tests/mochitest/geolocation/test_cachedPosition.html": "TIMED_OUT", "dom/tests/mochitest/geolocation/test_clearWatch.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_handlerSpinsEventLoop.html": "Don't run modal tests on Android", "dom/tests/mochitest/geolocation/test_manyCurrentConcurrent.html": "TIMED_OUT", "dom/tests/mochitest/geolocation/test_manyCurrentSerial.html": "TIMED_OUT", "dom/tests/mochitest/geolocation/test_manyWatchConcurrent.html": "TIMED_OUT", diff --git a/testing/mochitest/androidx86.json b/testing/mochitest/androidx86.json index 2de089c68e4..dfbb50b8af8 100644 --- a/testing/mochitest/androidx86.json +++ b/testing/mochitest/androidx86.json @@ -248,6 +248,7 @@ "dom/tests/mochitest/geolocation/test_allowWatch.html": "TIMED_OUT", "dom/tests/mochitest/geolocation/test_cachedPosition.html": "TIMED_OUT", "dom/tests/mochitest/geolocation/test_clearWatch.html": "TIMED_OUT", + "dom/tests/mochitest/geolocation/test_handlerSpinsEventLoop.html": "Don't run modal tests on Android", "dom/tests/mochitest/geolocation/test_manyCurrentConcurrent.html": "TIMED_OUT", "dom/tests/mochitest/geolocation/test_manyCurrentSerial.html": "TIMED_OUT", "dom/tests/mochitest/geolocation/test_manyWatchConcurrent.html": "TIMED_OUT", diff --git a/testing/mochitest/b2g.json b/testing/mochitest/b2g.json index 5301102ce96..b537a173701 100644 --- a/testing/mochitest/b2g.json +++ b/testing/mochitest/b2g.json @@ -202,6 +202,7 @@ "content/base/test/csp/test_CSP_evalscript_getCRMFRequest.html":"observer not working", "content/base/test/csp/test_CSP_frameancestors.html":"observer not working", "content/base/test/csp/test_CSP.html":"observer not working", + "content/base/test/csp/test_bug836922_npolicies.html":"observer not working", "content/base/test/test_CrossSiteXHR_origin.html":"https not working, bug 907770", "content/base/test/test_bug326337.html":"popup windows don't have specialpowers installed, could be solved with sendmessage/receivemessage", @@ -347,6 +348,7 @@ "dom/tests/mochitest/geolocation/test_cancelWatch.html":"", "dom/tests/mochitest/geolocation/test_clearWatch.html":"", "dom/tests/mochitest/geolocation/test_clearWatch_invalid.html":"", + "dom/tests/mochitest/geolocation/test_handlerSpinsEventLoop.html": "showmodaldialog", "dom/tests/mochitest/geolocation/test_manyCurrentConcurrent.html":"", "dom/tests/mochitest/geolocation/test_manyCurrentSerial.html":"", "dom/tests/mochitest/geolocation/test_manyWatchConcurrent.html":"", diff --git a/testing/mochitest/runtestsremote.py b/testing/mochitest/runtestsremote.py index 4858b999f20..bab93c84b98 100644 --- a/testing/mochitest/runtestsremote.py +++ b/testing/mochitest/runtestsremote.py @@ -118,16 +118,13 @@ class RemoteOptions(MochitestOptions): defaults["closeWhenDone"] = True defaults["testPath"] = "" defaults["app"] = None + defaults["utilityPath"] = None self.set_defaults(**defaults) def verifyRemoteOptions(self, options, automation): if not options.remoteTestRoot: options.remoteTestRoot = automation._devicemanager.getDeviceRoot() - productRoot = options.remoteTestRoot + "/" + automation._product - - if (options.utilityPath == self._automation.DIST_BIN): - options.utilityPath = productRoot + "/bin" if options.remoteWebServer == None: if os.name != "nt": @@ -161,10 +158,7 @@ class RemoteOptions(MochitestOptions): # Only reset the xrePath if it wasn't provided if (options.xrePath == None): - if (automation._product == "fennec"): - options.xrePath = productRoot + "/xulrunner" - else: - options.xrePath = options.utilityPath + options.xrePath = options.utilityPath if (options.pidFile != ""): f = open(options.pidFile, 'w') @@ -292,15 +286,15 @@ class MochiRemote(Mochitest): if options.xrePath == None: log.error("unable to find xulrunner path for %s, please specify with --xre-path", os.name) sys.exit(1) - paths.append("bin") - paths.append(os.path.join("..", "bin")) xpcshell = "xpcshell" if (os.name == "nt"): xpcshell += ".exe" - if (options.utilityPath): - paths.insert(0, options.utilityPath) + if options.utilityPath: + paths = [options.utilityPath, options.xrePath] + else: + paths = [options.xrePath] options.utilityPath = self.findPath(paths, xpcshell) if options.utilityPath == None: log.error("unable to find utility path for %s, please specify with --utility-path", os.name) diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js index fe091c16fc7..a2d6c76f924 100644 --- a/testing/mochitest/tests/SimpleTest/EventUtils.js +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js @@ -686,7 +686,7 @@ function _getDOMWindowUtils(aWindow) getInterface(_EU_Ci.nsIDOMWindowUtils); } -// Must be synchronized with nsIDOMWindowUtils. +// Must be synchronized with nsICompositionStringSynthesizer. const COMPOSITION_ATTR_RAWINPUT = 0x02; const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03; const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04; @@ -741,7 +741,7 @@ function synthesizeComposition(aEvent, aWindow) * When it's composing, set the each clauses' length to the * |composition.clauses[n].length|. The sum of the all length * values must be same as the length of |composition.string|. - * Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the + * Set nsICompositionStringSynthesizer.ATTR_* to the * |composition.clauses[n].attr|. * * When it's not composing, set 0 to the @@ -768,33 +768,20 @@ function synthesizeText(aEvent, aWindow) return; } - var firstClauseLength = aEvent.composition.clauses[0].length; - var firstClauseAttr = aEvent.composition.clauses[0].attr; - var secondClauseLength = 0; - var secondClauseAttr = 0; - var thirdClauseLength = 0; - var thirdClauseAttr = 0; - if (aEvent.composition.clauses[1]) { - secondClauseLength = aEvent.composition.clauses[1].length; - secondClauseAttr = aEvent.composition.clauses[1].attr; - if (aEvent.composition.clauses[2]) { - thirdClauseLength = aEvent.composition.clauses[2].length; - thirdClauseAttr = aEvent.composition.clauses[2].attr; + var compositionString = utils.createCompositionStringSynthesizer(); + compositionString.setString(aEvent.composition.string); + if (aEvent.composition.clauses[0].length) { + for (var i = 0; i < aEvent.composition.clauses.length; i++) { + compositionString.appendClause(aEvent.composition.clauses[i].length, + aEvent.composition.clauses[i].attr); } } - var caretStart = -1; - var caretLength = 0; if (aEvent.caret) { - caretStart = aEvent.caret.start; - caretLength = aEvent.caret.length; + compositionString.setCaret(aEvent.caret.start, aEvent.caret.length); } - utils.sendTextEvent(aEvent.composition.string, - firstClauseLength, firstClauseAttr, - secondClauseLength, secondClauseAttr, - thirdClauseLength, thirdClauseAttr, - caretStart, caretLength); + compositionString.dispatchEvent(); } /** diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 512e477b8de..cfff8e6c27f 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -273,8 +273,7 @@ function appendElementWithText(aP, aTagName, aClassName, aText) //--------------------------------------------------------------------------- -const kTreeDescriptions = { - 'explicit' : +const explicitTreeDescription = "This tree covers explicit memory allocations by the application. It includes \ \n\n\ * allocations made at the operating system level (via calls to functions such as \ @@ -291,69 +290,7 @@ and thread stacks. \ \n\n\ 'explicit' is not guaranteed to cover every explicit allocation, but it does cover \ most (including the entire heap), and therefore it is the single best number to \ -focus on when trying to reduce memory usage.", - - 'rss': -"This tree shows how much space in physical memory each of the process's \ -mappings is currently using (the mapping's 'resident set size', or 'RSS'). \ -This is a good measure of the 'cost' of the mapping, although it does not \ -take into account the fact that shared libraries may be mapped by multiple \ -processes but appear only once in physical memory. \ -\n\n\ -Note that the 'rss' value here might not equal the value for 'resident' \ -under 'Other Measurements' because the two measurements are not taken at \ -exactly the same time.", - - 'pss': -"This tree shows how much space in physical memory can be 'blamed' on this \ -process. For each mapping, its 'proportional set size' (PSS) is the \ -mapping's resident size divided by the number of processes which use the \ -mapping. So if a mapping is private to this process, its PSS should equal \ -its RSS. But if a mapping is shared between three processes, its PSS in each \ -of the processes would be 1/3 its RSS.", - - 'size': -"This tree shows how much virtual addres space each of the process's mappings \ -takes up (a.k.a. the mapping's 'vsize'). A mapping may have a large size but use \ -only a small amount of physical memory; the resident set size of a mapping is \ -a better measure of the mapping's 'cost'. \ -\n\n\ -Note that the 'size' value here might not equal the value for 'vsize' under \ -'Other Measurements' because the two measurements are not taken at exactly \ -the same time.", - - 'swap': -"This tree shows how much space in the swap file each of the process's \ -mappings is currently using. Mappings which are not in the swap file (i.e., \ -nodes which would have a value of 0 in this tree) are omitted." -}; - -const kSectionNames = { - 'explicit': 'Explicit Allocations', - 'rss': 'Resident Set Size (RSS) Breakdown', - 'pss': 'Proportional Set Size (PSS) Breakdown', - 'size': 'Virtual Size Breakdown', - 'swap': 'Swap Breakdown', - 'other': 'Other Measurements' -}; - -const kSmapsTreeNames = ['rss', 'pss', 'size', 'swap' ]; -const kSmapsTreePrefixes = ['rss/', 'pss/', 'size/', 'swap/']; - -function isExplicitPath(aUnsafePath) -{ - return aUnsafePath.startsWith("explicit/"); -} - -function isSmapsPath(aUnsafePath) -{ - for (let i = 0; i < kSmapsTreePrefixes.length; i++) { - if (aUnsafePath.startsWith(kSmapsTreePrefixes[i])) { - return true; - } - } - return false; -} +focus on when trying to reduce memory usage."; //--------------------------------------------------------------------------- @@ -546,8 +483,7 @@ function updateAboutMemoryFromReporters() try { // Process the reports from the memory reporters. - appendAboutMemoryMain(processMemoryReporters, gMgr.hasMozMallocUsableSize, - /* forceShowSmaps = */ false); + appendAboutMemoryMain(processMemoryReporters, gMgr.hasMozMallocUsableSize); } catch (ex) { handleException(ex); @@ -576,8 +512,7 @@ function updateAboutMemoryFromJSONObject(aObj) let process = function(aIgnoreReporter, aIgnoreReport, aHandleReport) { processMemoryReportsFromFile(aObj.reports, aIgnoreReport, aHandleReport); } - appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize, - /* forceShowSmaps = */ true); + appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize); } catch (ex) { handleException(ex); } @@ -765,6 +700,8 @@ DReport.prototype = { // // In those cases, we just use the description from the first-encountered // one, which is what about:memory also does. + // (Note: reports with those paths are no longer generated, but allowing + // the descriptions to differ seems reasonable.) }, merge: function(aJr) { @@ -930,14 +867,10 @@ function PColl() * file. * @param aHasMozMallocUsableSize * Boolean indicating if moz_malloc_usable_size works. - * @param aForceShowSmaps - * True if we should show the smaps memory reporters even if we're not - * in verbose mode. */ -function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize, - aForceShowSmaps) +function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize) { - let pcollsByProcess = getPCollsByProcess(aProcessReports, aForceShowSmaps); + let pcollsByProcess = getPCollsByProcess(aProcessReports); // Sort the processes. let processes = Object.keys(pcollsByProcess); @@ -998,12 +931,9 @@ function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize, * @param aProcessReports * Function that extracts the memory reports from the reporters or from * file. - * @param aForceShowSmaps - * True if we should show the smaps memory reporters even if we're not - * in verbose mode. * @return The table of PColls by process. */ -function getPCollsByProcess(aProcessReports, aForceShowSmaps) +function getPCollsByProcess(aProcessReports) { let pcollsByProcess = {}; @@ -1012,41 +942,29 @@ function getPCollsByProcess(aProcessReports, aForceShowSmaps) // be in parentheses, so a ')' might appear after the '.'.) const gSentenceRegExp = /^[A-Z].*\.\)?$/m; - // Ignore the "smaps" reporter in non-verbose mode unless we're reading from - // a file or the clipboard. (Note that reports from these reporters can - // reach here via a "content-child" reporter if they were in a child - // process.) - // // Ignore any "redundant/"-prefixed reporters and reports, which are only // used by telemetry. function ignoreReporter(aName) { - return (aName === "smaps" && !gVerbose.checked && !aForceShowSmaps) || - aName.startsWith("redundant/"); + return aName.startsWith("redundant/"); } function ignoreReport(aUnsafePath) { - return (isSmapsPath(aUnsafePath) && !gVerbose.checked && !aForceShowSmaps) || - aUnsafePath.startsWith("redundant/"); + return aUnsafePath.startsWith("redundant/"); } function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount, aDescription, aPresence) { - if (isExplicitPath(aUnsafePath)) { + if (aUnsafePath.startsWith("explicit/")) { assertInput(aKind === KIND_HEAP || aKind === KIND_NONHEAP, "bad explicit kind"); assertInput(aUnits === UNITS_BYTES, "bad explicit units"); assertInput(gSentenceRegExp.test(aDescription), "non-sentence explicit description"); - } else if (isSmapsPath(aUnsafePath)) { - assertInput(aKind === KIND_NONHEAP, "bad smaps kind"); - assertInput(aUnits === UNITS_BYTES, "bad smaps units"); - assertInput(aDescription !== "", "empty smaps description"); - } else { assertInput(gSentenceRegExp.test(aDescription), "non-sentence other description"); @@ -1434,7 +1352,7 @@ function appendProcessAboutMemoryElements(aP, aProcess, aTrees, aDegenerates, let hasKnownHeapAllocated; { let treeName = "explicit"; - let pre = appendSectionHeader(aP, kSectionNames[treeName]); + let pre = appendSectionHeader(aP, "Explicit Allocations"); let t = aTrees[treeName]; if (t) { fillInTree(t); @@ -1442,31 +1360,13 @@ function appendProcessAboutMemoryElements(aP, aProcess, aTrees, aDegenerates, aDegenerates && addHeapUnclassifiedNode(t, aDegenerates["heap-allocated"], aHeapTotal); sortTreeAndInsertAggregateNodes(t._amount, t); - t._description = kTreeDescriptions[treeName]; + t._description = explicitTreeDescription; appendTreeElements(pre, t, aProcess, ""); delete aTrees[treeName]; } appendTextNode(aP, "\n"); // gives nice spacing when we cut and paste } - // The smaps trees, which are only present in aTrees in verbose mode or when - // we're reading from a file or the clipboard. - kSmapsTreeNames.forEach(function(aTreeName) { - // |t| will be undefined if we don't have any reports for the given - // unsafePath. - let t = aTrees[aTreeName]; - if (t) { - let pre = appendSectionHeader(aP, kSectionNames[aTreeName]); - fillInTree(t); - sortTreeAndInsertAggregateNodes(t._amount, t); - t._description = kTreeDescriptions[aTreeName]; - t._hideKids = true; // smaps trees are always initially collapsed - appendTreeElements(pre, t, aProcess, ""); - delete aTrees[aTreeName]; - appendTextNode(aP, "\n"); // gives nice spacing when we cut and paste - } - }); - // Fill in and sort all the non-degenerate other trees. let otherTrees = []; for (let unsafeName in aTrees) { @@ -1494,7 +1394,7 @@ function appendProcessAboutMemoryElements(aP, aProcess, aTrees, aDegenerates, otherDegenerates.sort(TreeNode.compareUnsafeNames); // Now generate the elements, putting non-degenerate trees first. - let pre = appendSectionHeader(aP, kSectionNames['other']); + let pre = appendSectionHeader(aP, "Other Measurements"); for (let i = 0; i < otherTrees.length; i++) { let t = otherTrees[i]; appendTreeElements(pre, t, aProcess, ""); diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul index 646fb8ee8f4..0f7de2cea4e 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -116,21 +116,6 @@ f("compartments/user/https:\\\\very-long-url.com\\very-long\\oh-so-long\\really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789", OTHER, COUNT, 1); } }, - { name: "smaps", - collectReports: function(aCbObj, aClosure) { - // The amounts are given in pages, so multiply here by 4kb. - function f(aP, aA) { - aCbObj.callback("", aP, NONHEAP, BYTES, aA * 4 * KB, "Desc.", aClosure); - } - f("size/a", 24); - f("swap/a", 1); - f("swap/a", 2); - f("size/a", 19); - f("swap/b/c", 10); - f("rss/a", 42); - f("pss/a", 43); - } - }, { name: "compartments", collectReports: function(aCbObj, aClosure) { function f(aP) { @@ -189,12 +174,6 @@ HEAP, BYTES,200 * MB); f("2nd", "other0", OTHER, BYTES,666 * MB); f("2nd", "other1", OTHER, BYTES,111 * MB); - // If the "smaps" reporter is in a child process it'll be passed to - // the main process under a different name. The fact that we skip - // the "smaps" reporter in the main process won't cause these - // to be skipped; the report-level skipping will be hit instead. - f("2nd", "size/e", NONHEAP, BYTES,24*4*KB); - f("2nd", "size/f", NONHEAP, BYTES,24*4*KB); f("2nd", "redundant/blah", NONHEAP, BYTES,24*4*KB); // ignored! // Check that we can handle "heap-allocated" not being present. @@ -437,22 +416,6 @@ Explicit Allocations\n\ ├──────510,976 B (00.08%) ── d\n\ └──────102,400 B (00.02%) ── e\n\ \n\ -Resident Set Size (RSS) Breakdown\n\ -\n\ -172,032 B (100.0%) ++ rss\n\ -\n\ -Proportional Set Size (PSS) Breakdown\n\ -\n\ -176,128 B (100.0%) ++ pss\n\ -\n\ -Virtual Size Breakdown\n\ -\n\ -176,128 B (100.0%) ++ size\n\ -\n\ -Swap Breakdown\n\ -\n\ -53,248 B (100.0%) ++ swap\n\ -\n\ Other Measurements\n\ \n\ 5 (100.0%) -- compartments\n\ @@ -549,10 +512,6 @@ Explicit Allocations\n\ ├────209,715,200 B (20.00%) ── flip/the/backslashes\n\ └────105,906,176 B (10.10%) ── heap-unclassified\n\ \n\ -Virtual Size Breakdown\n\ -\n\ -196,608 B (100.0%) ++ size\n\ -\n\ Other Measurements\n\ \n\ 1,048,576,000 B ── heap-allocated\n\ diff --git a/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js b/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js index 19619432cbb..fd88aee9b1f 100644 --- a/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js +++ b/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js @@ -9,6 +9,21 @@ function test() const expectScrollBoth = 3; var allTests = [ + {dataUri: 'data:text/html,\ +
\ +
\ +
\ +
\ + \ + \ +
\ +
\ + \ + '}, {elem: 'a', expected: expectScrollNone}, {elem: 'b', expected: expectScrollBoth}, {elem: 'c', expected: expectScrollHori}, @@ -16,7 +31,16 @@ function test() {elem: 'e', expected: expectScrollVert}, {elem: 'f', expected: expectScrollNone}, {elem: 'g', expected: expectScrollBoth}, - {elem: 'h', expected: expectScrollNone} + {elem: 'h', expected: expectScrollNone}, + {dataUri: 'data:text/html,
\ + \ + '}, + {elem: 'i', expected: expectScrollVert}, // bug 695121 + {dataUri: 'data:text/html,\ +
\ + \ + '}, + {elem: 'j', expected: expectScrollVert} // bug 914251 ]; var doc; @@ -28,6 +52,11 @@ function test() return; } + if (test.dataUri) { + startLoad(test.dataUri); + return; + } + var elem = doc.getElementById(test.elem); // Skip the first callback as it's the same callback that the browser // uses to kick off the scrolling. @@ -69,23 +98,13 @@ function test() } waitForExplicitFinish(); - var dataUri = 'data:text/html,\ -
\ -
\ -
\ -
\ - \ - \ -
\ -
\ - \ - '; - gBrowser.selectedBrowser.addEventListener("pageshow", onLoad, false); - gBrowser.loadURI(dataUri); + + nextTest(); + + function startLoad(dataUri) { + gBrowser.selectedBrowser.addEventListener("pageshow", onLoad, false); + gBrowser.loadURI(dataUri); + } function onLoad() { gBrowser.selectedBrowser.removeEventListener("pageshow", onLoad, false); diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml index d8f2d10ca33..cc0083861d3 100644 --- a/toolkit/content/widgets/browser.xml +++ b/toolkit/content/widgets/browser.xml @@ -840,8 +840,6 @@ // Elements or non-html elements such as svg or Document nodes // also make sure to skip select elements that are not multiline if (!(this._scrollable instanceof HTMLElement) || - (this._scrollable instanceof HTMLHtmlElement) || - (this._scrollable instanceof HTMLBodyElement) || ((this._scrollable instanceof HTMLSelectElement) && !this._scrollable.multiple)) { continue; } @@ -855,16 +853,14 @@ // we already discarded non-multiline selects so allow vertical // scroll for multiline ones directly without checking for a // overflow property - var scrollVert = this._scrollable.clientHeight > 0 && - this._scrollable.scrollHeight > this._scrollable.clientHeight && + var scrollVert = this._scrollable.scrollTopMax && (this._scrollable instanceof HTMLSelectElement || scrollingAllowed.indexOf(overflowy) >= 0); // do not allow horizontal scrolling for select elements, it leads // to visual artifacts and is not the expected behavior anyway if (!(this._scrollable instanceof HTMLSelectElement) && - this._scrollable.clientWidth > 0 && - this._scrollable.scrollWidth > this._scrollable.clientWidth && + this._scrollable.scrollLeftMax && scrollingAllowed.indexOf(overflowx) >= 0) { this._autoScrollPopup.setAttribute("scrolldir", scrollVert ? "NSEW" : "EW"); break; diff --git a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in index 72994734173..24540c27531 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in @@ -9,27 +9,6 @@ endif LOCAL_INCLUDES = -I$(srcdir)/.. -ifneq (WINNT,$(OS_TARGET)) -ifdef MOZ_CRASHREPORTER -HOST_CPPSRCS = \ - string_conversion.cc \ - module.cc \ - unique_string.cc \ - md5.cc \ - dwarf_cfi_to_module.cc \ - dwarf_cu_to_module.cc \ - language.cc \ - stabs_to_module.cc \ - stabs_reader.cc \ - dwarf_line_to_module.cc \ - arm_ex_reader.cc \ - arm_ex_to_module.cc \ - pathname_stripper.cc \ - logging.cc \ - $(NULL) -endif -endif - ifeq ($(OS_TARGET),Android) VPATH += $(srcdir)/android TARGET_LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src/common/android/include/ diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in index 29029e05375..81dd9f55b69 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in @@ -6,12 +6,6 @@ ifdef MOZ_CRASHREPORTER LOCAL_INCLUDES = -I$(srcdir)/../.. -HOST_CPPSRCS = \ - bytereader.cc \ - dwarf2diehandler.cc \ - dwarf2reader.cc \ - functioninfo.cc \ - $(NULL) endif # This code is only compiled for build-time tools, diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/moz.build b/toolkit/crashreporter/google-breakpad/src/common/dwarf/moz.build index fead12efcaf..7040dd735b3 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/moz.build @@ -8,3 +8,9 @@ MODULE = 'breakpad_dwarf' if CONFIG['MOZ_CRASHREPORTER']: HOST_LIBRARY_NAME = 'host_breakpad_dwarf_s' + HOST_CPPSRCS += [ + 'bytereader.cc', + 'dwarf2diehandler.cc', + 'dwarf2reader.cc', + 'functioninfo.cc', + ] diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in index 908f3d7bec9..15feade8a94 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in @@ -2,9 +2,6 @@ # 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/. -ifdef MOZ_CRASHREPORTER -endif - LOCAL_INCLUDES = \ -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src \ $(NULL) @@ -20,18 +17,6 @@ endif DEFINES += -DNO_STABS_SUPPORT -ifdef MOZ_CRASHREPORTER -HOST_CPPSRCS = \ - dump_symbols.cc \ - elf_symbols_to_module.cc \ - elfutils.cc \ - file_id.cc \ - guid_creator.cc \ - linux_libc_support.cc \ - memory_mapped_file.cc \ - $(NULL) -endif - # need static lib FORCE_STATIC_LIB = 1 diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build b/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build index 3f9908794d8..87bd4c7e5a5 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build @@ -22,6 +22,15 @@ if CONFIG['OS_TARGET'] != 'Android': if CONFIG['MOZ_CRASHREPORTER']: HOST_LIBRARY_NAME = 'host_breakpad_linux_common_s' + HOST_CPPSRCS += [ + 'dump_symbols.cc', + 'elf_symbols_to_module.cc', + 'elfutils.cc', + 'file_id.cc', + 'guid_creator.cc', + 'linux_libc_support.cc', + 'memory_mapped_file.cc', + ] LIBRARY_NAME = 'breakpad_linux_common_s' diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in index e4f6fbc4f43..e7e90fcc197 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in @@ -11,9 +11,6 @@ CMSRCS = \ HTTPMultipartUpload.m \ $(NULL) - -HOST_CPPSRCS = $(CPPSRCS) - HOST_CMMSRCS = \ dump_syms.mm \ $(NULL) diff --git a/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build b/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build index b872a0c1a92..66d1eff704c 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/mac/moz.build @@ -17,6 +17,8 @@ CPP_SOURCES += [ 'string_utilities.cc', ] +HOST_CPPSRCS += CPP_SOURCES + HOST_LIBRARY_NAME = 'host_breakpad_mac_common_s' CMMSRCS += [ 'MachIPC.mm', diff --git a/toolkit/crashreporter/google-breakpad/src/common/moz.build b/toolkit/crashreporter/google-breakpad/src/common/moz.build index 836203cf7f7..a5cbdf7d983 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/moz.build @@ -52,9 +52,23 @@ else: if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_CRASHREPORTER']: HOST_CSRCS += CSRCS - -if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_CRASHREPORTER']: HOST_LIBRARY_NAME = 'host_breakpad_common_s' + HOST_CPPSRCS += [ + 'arm_ex_reader.cc', + 'arm_ex_to_module.cc', + 'dwarf_cfi_to_module.cc', + 'dwarf_cu_to_module.cc', + 'dwarf_line_to_module.cc', + 'language.cc', + 'logging.cc', + 'md5.cc', + 'module.cc', + 'pathname_stripper.cc', + 'stabs_reader.cc', + 'stabs_to_module.cc', + 'string_conversion.cc', + 'unique_string.cc', + ] if CONFIG['OS_ARCH'] == 'Darwin': CMMSRCS += [ diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/solaris/Makefile.in index 589f0b95848..1010d79a725 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/solaris/Makefile.in +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/Makefile.in @@ -4,13 +4,5 @@ LOCAL_INCLUDES = -I$(srcdir)/../.. -# not compiling http_upload.cc currently -# since it depends on libcurl -HOST_CPPSRCS = \ - dump_symbols.cc \ - file_id.cc \ - guid_creator.cc \ - $(NULL) - # need static lib FORCE_STATIC_LIB = 1 diff --git a/toolkit/crashreporter/google-breakpad/src/common/solaris/moz.build b/toolkit/crashreporter/google-breakpad/src/common/solaris/moz.build index 2ace46fe6f3..0d260e3f2fd 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/solaris/moz.build +++ b/toolkit/crashreporter/google-breakpad/src/common/solaris/moz.build @@ -16,3 +16,11 @@ HOST_LIBRARY_NAME = 'host_breakpad_solaris_common_s' LIBRARY_NAME = 'breakpad_solaris_common_s' +# not compiling http_upload.cc currently +# since it depends on libcurl +HOST_CPPSRCS += [ + 'dump_symbols.cc', + 'file_id.cc', + 'guid_creator.cc', +] + diff --git a/toolkit/modules/Sntp.jsm b/toolkit/modules/Sntp.jsm new file mode 100644 index 00000000000..672354b16db --- /dev/null +++ b/toolkit/modules/Sntp.jsm @@ -0,0 +1,334 @@ +/* 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"; + +this.EXPORTED_SYMBOLS = [ + "Sntp", +]; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +// Set to true to see debug messages. +let DEBUG = false; + +/** + * Constructor of Sntp. + * + * @param dataAvailableCb + * Callback function gets called when SNTP offset available. Signature + * is function dataAvailableCb(offsetInMS). + * @param maxRetryCount + * Maximum retry count when SNTP failed to connect to server; set to + * zero to disable the retry. + * @param refreshPeriodInSecs + * Refresh period; set to zero to disable refresh. + * @param timeoutInSecs + * Timeout value used for connection. + * @param pools + * SNTP server lists separated by ';'. + * @param port + * SNTP port. + */ +this.Sntp = function Sntp(dataAvailableCb, maxRetryCount, refreshPeriodInSecs, + timeoutInSecs, pools, port) { + if (dataAvailableCb != null) { + this._dataAvailableCb = dataAvailableCb; + } + if (maxRetryCount != null) { + this._maxRetryCount = maxRetryCount; + } + if (refreshPeriodInSecs != null) { + this._refreshPeriodInMS = refreshPeriodInSecs * 1000; + } + if (timeoutInSecs != null) { + this._timeoutInMS = timeoutInSecs * 1000; + } + if (pools != null && Array.isArray(pools) && pools.length > 0) { + this._pools = pools; + } + if (port != null) { + this._port = port; + } +} + +Sntp.prototype = { + isAvailable: function isAvailable() { + return this._cachedOffset != null; + }, + + isExpired: function isExpired() { + let valid = this._cachedOffset != null && this._cachedTimeInMS != null; + if (this._refreshPeriodInMS > 0) { + valid = valid && Date.now() < this._cachedTimeInMS + + this._refreshPeriodInMS; + } + return !valid; + }, + + request: function request() { + this._request(); + }, + + getOffset: function getOffset() { + return this._cachedOffset; + }, + + /** + * Indicates the system clock has been changed by [offset]ms so we need to + * adjust the stored value. + */ + updateOffset: function updateOffset(offset) { + if (this._cachedOffset != null) { + this._cachedOffset -= offset; + } + }, + + /** + * Used to schedule a retry or periodic updates. + */ + _schedule: function _schedule(timeInMS) { + if (this._updateTimer == null) { + this._updateTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + } + + this._updateTimer.initWithCallback(this._request.bind(this), + timeInMS, + Ci.nsITimer.TYPE_ONE_SHOT); + debug("Scheduled SNTP request in " + timeInMS + "ms"); + }, + + /** + * Handle the SNTP response. + */ + _handleSntp: function _handleSntp(originateTimeInMS, receiveTimeInMS, + transmitTimeInMS, respondTimeInMS) { + let clockOffset = Math.floor(((receiveTimeInMS - originateTimeInMS) + + (transmitTimeInMS - respondTimeInMS)) / 2); + debug("Clock offset: " + clockOffset); + + // We've succeeded so clear the retry status. + this._retryCount = 0; + this._retryPeriodInMS = 0; + + // Cache the latest SNTP offset whenever receiving it. + this._cachedOffset = clockOffset; + this._cachedTimeInMS = respondTimeInMS; + + if (this._dataAvailableCb != null) { + this._dataAvailableCb(clockOffset); + } + + this._schedule(this._refreshPeriodInMS); + }, + + /** + * Used for retry SNTP requests. + */ + _retry: function _retry() { + this._retryCount++; + if (this._retryCount > this._maxRetryCount) { + debug ("stop retrying SNTP"); + // Clear so we can start with clean status next time we have network. + this._retryCount = 0; + this._retryPeriodInMS = 0; + return; + } + this._retryPeriodInMS = Math.max(1000, this._retryPeriodInMS * 2); + + this._schedule(this._retryPeriodInMS); + }, + + /** + * Request SNTP. + */ + _request: function _request() { + function GetRequest() { + let NTP_PACKET_SIZE = 48; + let NTP_MODE_CLIENT = 3; + let NTP_VERSION = 3; + let TRANSMIT_TIME_OFFSET = 40; + + // Send the NTP request. + let requestTimeInMS = Date.now(); + let s = requestTimeInMS / 1000; + let ms = requestTimeInMS % 1000; + // NTP time is relative to 1900. + s += OFFSET_1900_TO_1970; + let f = ms * 0x100000000 / 1000; + s = Math.floor(s); + f = Math.floor(f); + + let buffer = new ArrayBuffer(NTP_PACKET_SIZE); + let data = new DataView(buffer); + data.setUint8(0, NTP_MODE_CLIENT | (NTP_VERSION << 3)); + data.setUint32(TRANSMIT_TIME_OFFSET, s, false); + data.setUint32(TRANSMIT_TIME_OFFSET + 4, f, false); + + return String.fromCharCode.apply(null, new Uint8Array(buffer)); + } + + function SNTPListener() {}; + SNTPListener.prototype = { + onStartRequest: function onStartRequest(request, context) { + }, + + onStopRequest: function onStopRequest(request, context, status) { + if (!Components.isSuccessCode(status)) { + debug ("Connection failed"); + this._requesting = false; + this._retry(); + } + }.bind(this), + + onDataAvailable: function onDataAvailable(request, context, inputStream, + offset, count) { + function GetTimeStamp(binaryInputStream) { + let s = binaryInputStream.read32(); + let f = binaryInputStream.read32(); + return Math.floor( + ((s - OFFSET_1900_TO_1970) * 1000) + ((f * 1000) / 0x100000000) + ); + } + debug ("Data available: " + count + " bytes"); + + try { + let binaryInputStream = Cc["@mozilla.org/binaryinputstream;1"] + .createInstance(Ci.nsIBinaryInputStream); + binaryInputStream.setInputStream(inputStream); + // We don't need first 24 bytes. + for (let i = 0; i < 6; i++) { + binaryInputStream.read32(); + } + // Offset 24: originate time. + let originateTimeInMS = GetTimeStamp(binaryInputStream); + // Offset 32: receive time. + let receiveTimeInMS = GetTimeStamp(binaryInputStream); + // Offset 40: transmit time. + let transmitTimeInMS = GetTimeStamp(binaryInputStream); + let respondTimeInMS = Date.now(); + + this._handleSntp(originateTimeInMS, receiveTimeInMS, + transmitTimeInMS, respondTimeInMS); + this._requesting = false; + } catch (e) { + debug ("SNTPListener Error: " + e.message); + this._requesting = false; + this._retry(); + } + inputStream.close(); + }.bind(this) + }; + + function SNTPRequester() {} + SNTPRequester.prototype = { + onOutputStreamReady: function(stream) { + try { + let data = GetRequest(); + let bytes_write = stream.write(data, data.length); + debug ("SNTP: sent " + bytes_write + " bytes"); + stream.close(); + } catch (e) { + debug ("SNTPRequester Error: " + e.message); + this._requesting = false; + this._retry(); + } + }.bind(this) + }; + + // Number of seconds between Jan 1, 1900 and Jan 1, 1970. + // 70 years plus 17 leap days. + let OFFSET_1900_TO_1970 = ((365 * 70) + 17) * 24 * 60 * 60; + + if (this._requesting) { + return; + } + if (this._pools.length < 1) { + debug("No server defined"); + return; + } + if (this._updateTimer) { + this._updateTimer.cancel(); + } + + debug ("Making request"); + this._requesting = true; + + let currentThread = Cc["@mozilla.org/thread-manager;1"] + .getService().currentThread; + let socketTransportService = + Cc["@mozilla.org/network/socket-transport-service;1"] + .getService(Ci.nsISocketTransportService); + let pump = Cc["@mozilla.org/network/input-stream-pump;1"] + .createInstance(Ci.nsIInputStreamPump); + let transport = socketTransportService + .createTransport(["udp"], + 1, + this._pools[Math.floor(this._pools.length * Math.random())], + this._port, + null); + + transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, this._timeoutInMS); + transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, this._timeoutInMS); + + let outStream = transport.openOutputStream(0, 0, 0) + .QueryInterface(Ci.nsIAsyncOutputStream); + let inStream = transport.openInputStream(0, 0, 0); + + pump.init(inStream, -1, -1, 0, 0, false); + pump.asyncRead(new SNTPListener(), null); + + outStream.asyncWait(new SNTPRequester(), 0, 0, currentThread); + }, + + // Callback function. + _dataAvailableCb: null, + + // Sntp servers. + _pools: [ + "0.pool.ntp.org", + "1.pool.ntp.org", + "2.pool.ntp.org", + "3.pool.ntp.org" + ], + + // The SNTP port. + _port: 123, + + // Maximum retry count allowed when request failed. + _maxRetryCount: 0, + + // Refresh period. + _refreshPeriodInMS: 0, + + // Timeout value used for connecting. + _timeoutInMS: 30 * 1000, + + // Cached SNTP offset. + _cachedOffset: null, + + // The time point when we cache the offset. + _cachedTimeInMS: null, + + // Flag to avoid redundant requests. + _requesting: false, + + // Retry counter. + _retryCount: 0, + + // Retry time offset (in seconds). + _retryPeriodInMS: 0, + + // Timer used for retries & daily updates. + _updateTimer: null +}; + +let debug; +if (DEBUG) { + debug = function (s) { + dump("-*- Sntp: " + s + "\n"); + }; +} else { + debug = function (s) {}; +} diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index 62237c0bf09..d2d6fd3d44e 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -29,6 +29,7 @@ EXTRA_JS_MODULES += [ 'RemoteWebProgress.jsm', 'SelectContentHelper.jsm', 'SelectParentHelper.jsm', + 'Sntp.jsm', 'Sqlite.jsm', 'Task.jsm', 'TelemetryTimestamps.jsm', diff --git a/widget/tests/test_composition_text_querycontent.xul b/widget/tests/test_composition_text_querycontent.xul index da0c2926634..ef4fdee17ec 100644 --- a/widget/tests/test_composition_text_querycontent.xul +++ b/widget/tests/test_composition_text_querycontent.xul @@ -19,7 +19,7 @@