From 28c7a264947fe7d8e8a6ae8aa7e123b02992b191 Mon Sep 17 00:00:00 2001 From: Matt Joras Date: Thu, 1 Nov 2012 18:09:15 -0400 Subject: [PATCH 01/83] Bug 798033 - Removes 'using namespace' from widget headers - r=roc --- widget/qt/mozqorientationsensorfilter.h | 1 - 1 file changed, 1 deletion(-) diff --git a/widget/qt/mozqorientationsensorfilter.h b/widget/qt/mozqorientationsensorfilter.h index 424d03b1c66c..856e9aee90aa 100644 --- a/widget/qt/mozqorientationsensorfilter.h +++ b/widget/qt/mozqorientationsensorfilter.h @@ -12,7 +12,6 @@ #include #include -using namespace QtMobility; class MozQOrientationSensorFilter : public QObject, public QOrientationFilter { From 613e229e93d65758a44984b0a7c7cbb47c836009 Mon Sep 17 00:00:00 2001 From: Matt Joras Date: Thu, 1 Nov 2012 18:09:22 -0400 Subject: [PATCH 02/83] Bug 798033 - Removes 'using namespace' from toolkit headers - r=bsmedberg --- .../places/tests/cpp/places_test_harness.h | 5 ++--- .../components/places/tests/cpp/test_IHistory.cpp | 1 + .../url-classifier/nsUrlClassifierProxies.cpp | 2 ++ .../url-classifier/nsUrlClassifierProxies.h | 13 ++++++------- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/toolkit/components/places/tests/cpp/places_test_harness.h b/toolkit/components/places/tests/cpp/places_test_harness.h index 7dc0eae81c57..5e86cd98af48 100644 --- a/toolkit/components/places/tests/cpp/places_test_harness.h +++ b/toolkit/components/places/tests/cpp/places_test_harness.h @@ -24,7 +24,6 @@ #define TOPIC_FRECENCY_UPDATED "places-frecency-updated" #define WAITFORTOPIC_TIMEOUT_SECONDS 5 -using namespace mozilla; static size_t gTotalTests = 0; static size_t gPassedTests = 0; @@ -183,10 +182,10 @@ struct VisitRecord int32_t transitionType; }; -already_AddRefed +already_AddRefed do_get_IHistory() { - nsCOMPtr history = do_GetService(NS_IHISTORY_CONTRACTID); + nsCOMPtr history = do_GetService(NS_IHISTORY_CONTRACTID); do_check_true(history); return history.forget(); } diff --git a/toolkit/components/places/tests/cpp/test_IHistory.cpp b/toolkit/components/places/tests/cpp/test_IHistory.cpp index daf2a915170c..9f9a160ad15d 100644 --- a/toolkit/components/places/tests/cpp/test_IHistory.cpp +++ b/toolkit/components/places/tests/cpp/test_IHistory.cpp @@ -11,6 +11,7 @@ #include "mozilla/Attributes.h" #include "mock_Link.h" +using namespace mozilla; using namespace mozilla::dom; /** diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp index 69f5f8ef4f01..89bde0cd9c06 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp @@ -6,6 +6,8 @@ #include "nsUrlClassifierProxies.h" #include "nsUrlClassifierDBService.h" +using namespace mozilla::safebrowsing; + static nsresult DispatchToWorkerThread(nsIRunnable* r) { diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.h b/toolkit/components/url-classifier/nsUrlClassifierProxies.h index 053f4eec7826..2b5c7547f5bf 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierProxies.h +++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.h @@ -13,7 +13,6 @@ #include "nsIPrincipal.h" #include "LookupCache.h" -using namespace mozilla::safebrowsing; /** * Thread proxy from the main thread to the worker thread. @@ -124,7 +123,7 @@ public: { public: CacheCompletionsRunnable(nsIUrlClassifierDBServiceWorker* aTarget, - CacheResultArray *aEntries) + mozilla::safebrowsing::CacheResultArray *aEntries) : mTarget(aTarget) , mEntries(aEntries) { } @@ -133,14 +132,14 @@ public: private: nsCOMPtr mTarget; - CacheResultArray *mEntries; + mozilla::safebrowsing::CacheResultArray *mEntries; }; class CacheMissesRunnable : public nsRunnable { public: CacheMissesRunnable(nsIUrlClassifierDBServiceWorker* aTarget, - PrefixArray *aEntries) + mozilla::safebrowsing::PrefixArray *aEntries) : mTarget(aTarget) , mEntries(aEntries) { } @@ -149,7 +148,7 @@ public: private: nsCOMPtr mTarget; - PrefixArray *mEntries; + mozilla::safebrowsing::PrefixArray *mEntries; }; private: @@ -173,7 +172,7 @@ public: { public: LookupCompleteRunnable(nsMainThreadPtrHolder* aTarget, - LookupResultArray *aResults) + mozilla::safebrowsing::LookupResultArray *aResults) : mTarget(aTarget) , mResults(aResults) { } @@ -182,7 +181,7 @@ public: private: nsMainThreadPtrHandle mTarget; - LookupResultArray * mResults; + mozilla::safebrowsing::LookupResultArray * mResults; }; private: From 2b5af3f221b785d086d811e6623cc47f296c4687 Mon Sep 17 00:00:00 2001 From: Oonishi Atsushi Date: Thu, 1 Nov 2012 19:23:13 -0400 Subject: [PATCH 03/83] Bug 282432 - Calling asyncOpen on a file channel should notify about file not found errors asynchronously instead of throwing from asyncOpen. r=bz --- netwerk/protocol/file/nsFileChannel.cpp | 28 +++++++++++++++++++------ netwerk/protocol/file/nsFileChannel.h | 2 +- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/netwerk/protocol/file/nsFileChannel.cpp b/netwerk/protocol/file/nsFileChannel.cpp index 6385f3584563..fcfceef87036 100644 --- a/netwerk/protocol/file/nsFileChannel.cpp +++ b/netwerk/protocol/file/nsFileChannel.cpp @@ -273,7 +273,8 @@ nsFileChannel::nsFileChannel(nsIURI *uri) nsresult nsFileChannel::MakeFileInputStream(nsIFile *file, nsCOMPtr &stream, - nsCString &contentType) + nsCString &contentType, + bool async) { // we accept that this might result in a disk hit to stat the file bool isDir; @@ -282,7 +283,14 @@ nsFileChannel::MakeFileInputStream(nsIFile *file, // canonicalize error message if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) rv = NS_ERROR_FILE_NOT_FOUND; - return rv; + + if (async && (NS_ERROR_FILE_NOT_FOUND == rv)) { + // We don't return "Not Found" errors here. Since we could not find + // the file, it's not a directory anyway. + isDir = false; + } else { + return rv; + } } if (isDir) { @@ -290,7 +298,8 @@ nsFileChannel::MakeFileInputStream(nsIFile *file, if (NS_SUCCEEDED(rv) && !HasContentTypeHint()) contentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT); } else { - rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file); + rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file, -1, -1, + async? nsIFileInputStream::DEFER_OPEN : 0); if (NS_SUCCEEDED(rv) && !HasContentTypeHint()) { // Use file extension to infer content type nsCOMPtr mime = do_GetService("@mozilla.org/mime;1", &rv); @@ -364,7 +373,7 @@ nsFileChannel::OpenContentStream(bool async, nsIInputStream **result, SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM)); } else { nsAutoCString contentType; - rv = MakeFileInputStream(file, stream, contentType); + rv = MakeFileInputStream(file, stream, contentType, async); if (NS_FAILED(rv)) return rv; @@ -374,8 +383,15 @@ nsFileChannel::OpenContentStream(bool async, nsIInputStream **result, if (mContentLength < 0) { int64_t size; rv = file->GetFileSize(&size); - if (NS_FAILED(rv)) - return rv; + if (NS_FAILED(rv)) { + if (async && + (NS_ERROR_FILE_NOT_FOUND == rv || + NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv)) { + size = 0; + } else { + return rv; + } + } mContentLength = size; } if (!contentType.IsEmpty()) diff --git a/netwerk/protocol/file/nsFileChannel.h b/netwerk/protocol/file/nsFileChannel.h index e424199d7fe6..17d8a2021880 100644 --- a/netwerk/protocol/file/nsFileChannel.h +++ b/netwerk/protocol/file/nsFileChannel.h @@ -28,7 +28,7 @@ protected: // NOTE: If the channel has a type hint set, contentType will be left // untouched. The caller should not use it in that case. nsresult MakeFileInputStream(nsIFile *file, nsCOMPtr &stream, - nsCString &contentType); + nsCString &contentType, bool async); virtual nsresult OpenContentStream(bool async, nsIInputStream **result, nsIChannel** channel); From 0638bc0fda9f94b5dc47fe5faccb9b2d4be5100e Mon Sep 17 00:00:00 2001 From: Oonishi Atsushi Date: Thu, 1 Nov 2012 19:23:13 -0400 Subject: [PATCH 04/83] Bug 282432 - XPCShell test. r=bz --- netwerk/test/unit/test_bug282432.js | 38 +++++++++++++++++++++++++++++ netwerk/test/unit/xpcshell.ini | 1 + 2 files changed, 39 insertions(+) create mode 100644 netwerk/test/unit/test_bug282432.js diff --git a/netwerk/test/unit/test_bug282432.js b/netwerk/test/unit/test_bug282432.js new file mode 100644 index 000000000000..1f53a1c0a476 --- /dev/null +++ b/netwerk/test/unit/test_bug282432.js @@ -0,0 +1,38 @@ +function run_test() { + do_test_pending(); + + function StreamListener() {} + + StreamListener.prototype = { + QueryInterface: function(aIID) { + if (aIID.equals(Components.interfaces.nsIStreamListener) || + aIID.equals(Components.interfaces.nsIRequestObserver) || + aIID.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_NOINTERFACE; + }, + + onStartRequest: function(aRequest, aContext) {}, + + onStopRequest: function(aRequest, aContext, aStatusCode) { + // Make sure we can catch the error NS_ERROR_FILE_NOT_FOUND here. + do_check_eq(aStatusCode, Components.results.NS_ERROR_FILE_NOT_FOUND); + do_test_finished(); + }, + + onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) { + do_throw("The channel must not call onDataAvailable()."); + } + }; + + let listener = new StreamListener(); + let ios = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + + // This file does not exist. + let file = do_get_file("_NOT_EXIST_.txt", true); + do_check_false(file.exists()); + + let channel = ios.newChannelFromURI(ios.newFileURI(file)); + channel.asyncOpen(listener, null); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 5dc3dde5e412..290fa4296b21 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -19,6 +19,7 @@ skip-if = os == "android" [test_bug248970_cookie.js] [test_bug261425.js] [test_bug263127.js] +[test_bug282432.js] [test_bug321706.js] [test_bug331825.js] [test_bug336501.js] From 1c17e4d3267322f7374d16e0427618ecb533300d Mon Sep 17 00:00:00 2001 From: Abhishek Rajput Date: Thu, 1 Nov 2012 19:23:14 -0400 Subject: [PATCH 05/83] Bug 693808 - Make number keywords in locationbar be searched as normal terms. r=jst --- docshell/base/nsDefaultURIFixup.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp index 83b7d9e335cb..a89429a006b4 100644 --- a/docshell/base/nsDefaultURIFixup.cpp +++ b/docshell/base/nsDefaultURIFixup.cpp @@ -806,7 +806,10 @@ nsresult nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString, if (((spaceLoc < dotLoc || quoteLoc < dotLoc) && (spaceLoc < colonLoc || quoteLoc < colonLoc) && (spaceLoc < qMarkLoc || quoteLoc < qMarkLoc)) || - qMarkLoc == 0) + qMarkLoc == 0 || + (dotLoc == uint32_t(kNotFound) && + colonLoc == uint32_t(kNotFound) && + qMarkLoc == uint32_t(kNotFound) ) ) { KeywordToURI(aURIString, aURI); } From 5bda15e690872051a31169a8d3b107672cb1d471 Mon Sep 17 00:00:00 2001 From: Al Tsai Date: Thu, 1 Nov 2012 19:23:14 -0400 Subject: [PATCH 06/83] Bug 805539 - Prevent effect from default values and network geolocation provider. r=dougt --- dom/system/gonk/tests/marionette/manifest.ini | 6 +- .../gonk/tests/marionette/test_geolocation.js | 83 +++++++++++-------- .../client/marionette/tests/unit-tests.ini | 2 +- 3 files changed, 52 insertions(+), 39 deletions(-) diff --git a/dom/system/gonk/tests/marionette/manifest.ini b/dom/system/gonk/tests/marionette/manifest.ini index ef8cce0c5c80..777bec13ea39 100644 --- a/dom/system/gonk/tests/marionette/manifest.ini +++ b/dom/system/gonk/tests/marionette/manifest.ini @@ -3,6 +3,6 @@ b2g = true browser = false qemu = true -; Bug 805539 -;[test_geolocation.js] -; gps = true + +[test_geolocation.js] + diff --git a/dom/system/gonk/tests/marionette/test_geolocation.js b/dom/system/gonk/tests/marionette/test_geolocation.js index ea3f98af912f..d1de0196c0eb 100644 --- a/dom/system/gonk/tests/marionette/test_geolocation.js +++ b/dom/system/gonk/tests/marionette/test_geolocation.js @@ -6,7 +6,8 @@ MARIONETTE_TIMEOUT = 10000; let geolocation = window.navigator.geolocation; ok(geolocation); -var target = Object(); +var sample = []; +var result = []; var wpid; /** @@ -14,17 +15,25 @@ var wpid; */ SpecialPowers.addPermission("geolocation", true, document); +/** + * Disable wifi geolocation provider + */ +wifiUri = SpecialPowers.getCharPref("geo.wifi.uri"); +SpecialPowers.setCharPref("geo.wifi.uri", "http://mochi.test:8888/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs?action=stop-responding"); + /** * Helper that compares the geolocation against the web API. */ -function verifyLocation(callback, expectedLocation) { - geolocation.getCurrentPosition(function(position) { - log("Expected location: " + expectedLocation.latitude + " " + expectedLocation.longitude); - log("Current location: " + position.coords.latitude + " " + position.coords.longitude); - is(expectedLocation.latitude, position.coords.latitude); - is(expectedLocation.longitude, position.coords.longitude); - }); - window.setTimeout(callback, 0); +function verifyLocation() { + + log("Sample:" + sample.join(',')); + log("Result:" + result.join(',')); + + for (i in sample) { + is(sample.pop(), result.pop()); + } + + window.setTimeout(cleanup, 0); } /** @@ -33,71 +42,75 @@ function verifyLocation(callback, expectedLocation) { function setup() { log("Providing initial setup: set geographic position watcher."); + wpid = geolocation.watchPosition(function(position) { - log("Position changes has found."); - log("Watch: Target location: " + target.latitude + " " + target.longitude); - log("Watch: Current location: " + position.coords.latitude + " " + position.coords.longitude); - is(target.latitude, position.coords.latitude, "Latitude isn't match!"); - is(target.longitude, position.coords.longitude, "Longitude isn't match!"); + log("Position changes: (" + position.coords.latitude + "/" + position.coords.longitude + ")"); + result.push(""+position.coords.latitude + "/" + position.coords.longitude); }); - target.latitude = 0; - target.longitude = 0; + lat = 0; + lon = 0; - cmd = "geo fix " + target.longitude + " " + target.latitude; + cmd = "geo fix " + lon + " " + lat; + sample.push(lat+"/"+lon); runEmulatorCmd(cmd, function(result) { - log("Geolocation setting status: " + result); - verifyLocation(movePosition_1, target); + window.setTimeout(movePosition_1, 0); }); - } function movePosition_1() { log("Geolocation changes. Move to Position 1."); - target.latitude = 25; - target.longitude = 121.56499833333334; + lat = 25; + lon = 121.56499833333334; - cmd = "geo fix " + target.longitude + " " + target.latitude; + cmd = "geo fix " + lon + " " + lat; + sample.push(lat+"/"+lon); runEmulatorCmd(cmd, function(result) { - log("Geolocation setting status: " + result); - verifyLocation(movePosition_2, target); + window.setTimeout(movePosition_2, 0); }); } function movePosition_2() { log("Geolocation changes to a negative longitude. Move to Position 2."); - target.latitude = 37.393; - target.longitude = -122.08199833333335; + lat = 37.393; + lon = -122.08199833333335; - cmd = "geo fix " + target.longitude + " " + target.latitude; + cmd = "geo fix " + lon + " " + lat; + sample.push(lat+"/"+lon); runEmulatorCmd(cmd, function(result) { - log("Geolocation setting status: " + result); - verifyLocation(movePosition_3, target); + window.setTimeout(movePosition_3, 0); }); } function movePosition_3() { log("Geolocation changes with WatchPosition. Move to Position 3."); - target.latitude = -22; - target.longitude = -43; + lat = -22; + lon = -43; - cmd = "geo fix " + target.longitude + " " + target.latitude; + cmd = "geo fix " + lon + " " + lat; + sample.push(lat+"/"+lon); + + geolocation.getCurrentPosition(function(position) { + log("getCurrentPosition: Expected location: ("+lat+"/"+lon+"); Current location: (" + position.coords.latitude + "/" + position.coords.longitude + ")"); + is(lat, position.coords.latitude); + is(lon, position.coords.longitude); + }); runEmulatorCmd(cmd, function(result) { - log("Geolocation setting status: " + result); - verifyLocation(cleanup, target); + window.setTimeout(verifyLocation, 0); }); } function cleanup() { geolocation.clearWatch(wpid); SpecialPowers.removePermission("geolocation", document); + SpecialPowers.setCharPref("geo.wifi.uri", wifiUri); finish(); } diff --git a/testing/marionette/client/marionette/tests/unit-tests.ini b/testing/marionette/client/marionette/tests/unit-tests.ini index 8284816a6956..cd9841287be6 100644 --- a/testing/marionette/client/marionette/tests/unit-tests.ini +++ b/testing/marionette/client/marionette/tests/unit-tests.ini @@ -16,7 +16,7 @@ skip = false [include:../../../../../dom/battery/test/marionette/manifest.ini] [include:../../../../../dom/sms/tests/marionette/manifest.ini] [include:../../../../../dom/network/tests/marionette/manifest.ini] -; [include:../../../../../dom/system/gonk/tests/marionette/manifest.ini] +[include:../../../../../dom/system/gonk/tests/marionette/manifest.ini] ; marionette unit tests [include:unit/unit-tests.ini] From 409a98df733bec0da8aa1554d4b1cd73b5335dda Mon Sep 17 00:00:00 2001 From: Jed Parsons Date: Thu, 1 Nov 2012 19:23:14 -0400 Subject: [PATCH 07/83] Bug 806605 - Replace loggedInEmail parameter with loggedInUser. r=benadida --- b2g/components/SignInToWebsite.jsm | 2 +- b2g/components/test/unit/head_identity.js | 2 +- dom/identity/DOMIdentity.jsm | 8 ++-- dom/identity/nsDOMIdentity.js | 29 +++++++----- toolkit/identity/Identity.jsm | 2 +- toolkit/identity/IdentityUtils.jsm | 46 ++++++++++++++++++++ toolkit/identity/Makefile.in | 1 + toolkit/identity/MinimalIdentity.jsm | 7 ++- toolkit/identity/RelyingParty.jsm | 22 +++++----- toolkit/identity/tests/unit/head_identity.js | 2 +- 10 files changed, 87 insertions(+), 34 deletions(-) create mode 100644 toolkit/identity/IdentityUtils.jsm diff --git a/b2g/components/SignInToWebsite.jsm b/b2g/components/SignInToWebsite.jsm index 3d8e59efe97b..a3cd8127a89f 100644 --- a/b2g/components/SignInToWebsite.jsm +++ b/b2g/components/SignInToWebsite.jsm @@ -143,7 +143,7 @@ let Pipe = { * provide a callback for handling messages. * * @param aRpOptions options describing the Relying Party's - * (dicitonary) call, such as origin and loggedInEmail. + * (dictionary) call, such as origin and loggedInUser. * * @param aGaiaOptions showUI: boolean * (dictionary) message: name of the message to emit diff --git a/b2g/components/test/unit/head_identity.js b/b2g/components/test/unit/head_identity.js index 534cf36197c4..81561698101b 100644 --- a/b2g/components/test/unit/head_identity.js +++ b/b2g/components/test/unit/head_identity.js @@ -50,7 +50,7 @@ function uuid() { function mockDoc(aIdentity, aOrigin, aDoFunc) { let mockedDoc = {}; mockedDoc.id = uuid(); - mockedDoc.loggedInEmail = aIdentity; + mockedDoc.loggedInUser = aIdentity; mockedDoc.origin = aOrigin; mockedDoc['do'] = aDoFunc; mockedDoc.doReady = partial(aDoFunc, 'ready'); diff --git a/dom/identity/DOMIdentity.jsm b/dom/identity/DOMIdentity.jsm index b2a1282fc037..2a6494f9e0d3 100644 --- a/dom/identity/DOMIdentity.jsm +++ b/dom/identity/DOMIdentity.jsm @@ -87,17 +87,17 @@ IDPAuthenticationContext.prototype = { }, }; -function RPWatchContext(aID, aOrigin, aLoggedInEmail, aTargetMM) { +function RPWatchContext(aID, aOrigin, aLoggedInUser, aTargetMM) { this._id = aID; this._origin = aOrigin; - this._loggedInEmail = aLoggedInEmail; + this._loggedInUser = aLoggedInUser; this._mm = aTargetMM; } RPWatchContext.prototype = { get id() this._id, get origin() this._origin, - get loggedInEmail() this._loggedInEmail, + get loggedInUser() this._loggedInUser, doLogin: function RPWatchContext_onlogin(aAssertion) { log("doLogin: " + this.id); @@ -221,7 +221,7 @@ this.DOMIdentity = { // Pass an object with the watch members to Identity.jsm so it can call the // callbacks. let context = new RPWatchContext(message.id, message.origin, - message.loggedInEmail, targetMM); + message.loggedInUser, targetMM); IdentityService.RP.watch(context); }, diff --git a/dom/identity/nsDOMIdentity.js b/dom/identity/nsDOMIdentity.js index 6bee460a1416..b8d3a3cd4cf4 100644 --- a/dom/identity/nsDOMIdentity.js +++ b/dom/identity/nsDOMIdentity.js @@ -16,6 +16,7 @@ const MAX_RP_CALLS = 100; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/IdentityUtils.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", @@ -78,24 +79,30 @@ nsDOMIdentity.prototype = { let message = this.DOMIdentityMessage(); - // loggedInEmail - message.loggedInEmail = null; - let emailType = typeof(aOptions["loggedInEmail"]); - if (aOptions["loggedInEmail"] && aOptions["loggedInEmail"] !== "undefined") { + // loggedInUser vs loggedInEmail + // https://developer.mozilla.org/en-US/docs/DOM/navigator.id.watch + // This parameter, loggedInUser, was renamed from loggedInEmail in early + // September, 2012. Both names will continue to work for the time being, + // but code should be changed to use loggedInUser instead. + checkRenamed(aOptions, "loggedInEmail", "loggedInUser"); + message["loggedInUser"] = aOptions["loggedInUser"]; + + let emailType = typeof(aOptions["loggedInUser"]); + if (aOptions["loggedInUser"] && aOptions["loggedInUser"] !== "undefined") { if (emailType !== "string") { - throw new Error("loggedInEmail must be a String or null"); + throw new Error("loggedInUser must be a String or null"); } // TODO: Bug 767610 - check email format. // See nsHTMLInputElement::IsValidEmailAddress - if (aOptions["loggedInEmail"].indexOf("@") == -1 - || aOptions["loggedInEmail"].length > MAX_STRING_LENGTH) { - throw new Error("loggedInEmail is not valid"); + if (aOptions["loggedInUser"].indexOf("@") == -1 + || aOptions["loggedInUser"].length > MAX_STRING_LENGTH) { + throw new Error("loggedInUser is not valid"); } - // Set loggedInEmail in this block that "undefined" doesn't get through. - message.loggedInEmail = aOptions.loggedInEmail; + // Set loggedInUser in this block that "undefined" doesn't get through. + message.loggedInUser = aOptions.loggedInUser; } - this._log("loggedInEmail: " + message.loggedInEmail); + this._log("loggedInUser: " + message.loggedInUser); this._rpWatcher = aOptions; this._identityInternal._mm.sendAsyncMessage("Identity:RP:Watch", message); diff --git a/toolkit/identity/Identity.jsm b/toolkit/identity/Identity.jsm index a3d5e37d739b..47c4e5399615 100644 --- a/toolkit/identity/Identity.jsm +++ b/toolkit/identity/Identity.jsm @@ -131,7 +131,7 @@ IDService.prototype = { let provId = rp.provId; let rpLoginOptions = { - loggedInEmail: aIdentity, + loggedInUser: aIdentity, origin: rp.origin }; log("selectIdentity: provId:", provId, "origin:", rp.origin); diff --git a/toolkit/identity/IdentityUtils.jsm b/toolkit/identity/IdentityUtils.jsm new file mode 100644 index 000000000000..db7304263d44 --- /dev/null +++ b/toolkit/identity/IdentityUtils.jsm @@ -0,0 +1,46 @@ +/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* 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/. */ + +// functions common to Identity.jsm and MinimalIdentity.jsm + +"use strict"; + +const EXPORTED_SYMBOLS = ["checkRenamed"]; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Logger", + "resource://gre/modules/identity/LogUtils.jsm"); + +function log(...aMessageArgs) { + Logger.log.apply(Logger, ["Identity"].concat(aMessageArgs)); +} + +function defined(item) { + return typeof item !== 'undefined'; +} + +function checkDeprecated(aOptions, aField) { + if (defined(aOptions[aField])) { + log("WARNING: field is deprecated:", aField); + return true; + } + return false; +} + +let checkRenamed = function checkRenamed(aOptions, aOldName, aNewName) { + if (defined(aOptions[aOldName]) && + defined(aOptions[aNewName])) { + let err = "You cannot provide both " + aOldName + " and " + aNewName; + Logger.reportError(err); + throw new Error(err); + } + + if (checkDeprecated(aOptions, aOldName)) { + aOptions[aNewName] = aOptions[aOldName]; + delete(aOptions[aOldName]); + } +}; \ No newline at end of file diff --git a/toolkit/identity/Makefile.in b/toolkit/identity/Makefile.in index bfe47e459c06..45812e14b2f8 100644 --- a/toolkit/identity/Makefile.in +++ b/toolkit/identity/Makefile.in @@ -30,6 +30,7 @@ CPPSRCS = \ $(NULL) EXTRA_JS_MODULES = \ + IdentityUtils.jsm \ Identity.jsm \ IdentityProvider.jsm \ IdentityStore.jsm \ diff --git a/toolkit/identity/MinimalIdentity.jsm b/toolkit/identity/MinimalIdentity.jsm index 1f30d1641133..65a6d1e9861a 100644 --- a/toolkit/identity/MinimalIdentity.jsm +++ b/toolkit/identity/MinimalIdentity.jsm @@ -85,7 +85,7 @@ IDService.prototype = { * (Object) an object that represents the caller document, and * is expected to have properties: * - id (unique, e.g. uuid) - * - loggedInEmail (string or null) + * - loggedInUser (string or null) * - origin (string) * * and a bunch of callbacks @@ -101,12 +101,11 @@ IDService.prototype = { log("watch: rpcaller:", aRpCaller); // store the caller structure and notify the UI observers - // note that, unfortunately, what here is loggedInEmail is called - // loggedInUser in the native API. this._rpFlows[aRpCaller.id] = aRpCaller; + let options = {rpId: aRpCaller.id, origin: aRpCaller.origin, - loggedInUser: aRpCaller.loggedInEmail || null}; + loggedInUser: aRpCaller.loggedInUser}; log("sending identity-controller-watch:", options); Services.obs.notifyObservers({wrappedJSObject: options},"identity-controller-watch", null); }, diff --git a/toolkit/identity/RelyingParty.jsm b/toolkit/identity/RelyingParty.jsm index 6a6dfaf461df..e7d0b05bef66 100644 --- a/toolkit/identity/RelyingParty.jsm +++ b/toolkit/identity/RelyingParty.jsm @@ -70,7 +70,7 @@ IdentityRelyingParty.prototype = { * (Object) an object that represents the caller document, and * is expected to have properties: * - id (unique, e.g. uuid) - * - loggedInEmail (string or null) + * - loggedInUser (string or null) * - origin (string) * * and a bunch of callbacks @@ -88,7 +88,7 @@ IdentityRelyingParty.prototype = { log("watch: rpId:", aRpCaller.id, "origin:", origin, - "loggedInEmail:", aRpCaller.loggedInEmail, + "loggedInUser:", aRpCaller.loggedInUser, "loggedIn:", state.isLoggedIn, "email:", state.email); @@ -99,20 +99,20 @@ IdentityRelyingParty.prototype = { // 2. the email is null: 'login'; 'ready' // 3. the email has changed: 'login'; 'ready' if (state.isLoggedIn) { - if (state.email && aRpCaller.loggedInEmail === state.email) { + if (state.email && aRpCaller.loggedInUser === state.email) { this._notifyLoginStateChanged(aRpCaller.id, state.email); return aRpCaller.doReady(); - } else if (aRpCaller.loggedInEmail === null) { + } else if (aRpCaller.loggedInUser === null) { // Generate assertion for existing login - let options = {loggedInEmail: state.email, origin: origin}; + let options = {loggedInUser: state.email, origin: origin}; return this._doLogin(aRpCaller, options); } else { - // A loggedInEmail different from state.email has been specified. + // A loggedInUser different from state.email has been specified. // Change login identity. - let options = {loggedInEmail: state.email, origin: origin}; + let options = {loggedInUser: state.email, origin: origin}; return this._doLogin(aRpCaller, options); } @@ -122,7 +122,7 @@ IdentityRelyingParty.prototype = { // 2. not logged in, no email given: 'ready'; } else { - if (aRpCaller.loggedInEmail) { + if (aRpCaller.loggedInUser) { return this._doLogout(aRpCaller, {origin: origin}); } else { @@ -141,8 +141,8 @@ IdentityRelyingParty.prototype = { log("_doLogin: rpId:", aRpCaller.id, "origin:", aOptions.origin); let loginWithAssertion = function loginWithAssertion(assertion) { - this._store.setLoginState(aOptions.origin, true, aOptions.loggedInEmail); - this._notifyLoginStateChanged(aRpCaller.id, aOptions.loggedInEmail); + this._store.setLoginState(aOptions.origin, true, aOptions.loggedInUser); + this._notifyLoginStateChanged(aRpCaller.id, aOptions.loggedInUser); aRpCaller.doLogin(assertion); aRpCaller.doReady(); }.bind(this); @@ -290,7 +290,7 @@ IdentityRelyingParty.prototype = { */ _getAssertion: function _getAssertion(aOptions, aCallback) { let audience = aOptions.origin; - let email = aOptions.loggedInEmail || this.getDefaultEmailForOrigin(audience); + let email = aOptions.loggedInUser || this.getDefaultEmailForOrigin(audience); log("_getAssertion: audience:", audience, "email:", email); if (!audience) { throw "audience required for _getAssertion"; diff --git a/toolkit/identity/tests/unit/head_identity.js b/toolkit/identity/tests/unit/head_identity.js index 306f1d4a9fae..c226a85ddc89 100644 --- a/toolkit/identity/tests/unit/head_identity.js +++ b/toolkit/identity/tests/unit/head_identity.js @@ -100,7 +100,7 @@ function uuid() { function mock_doc(aIdentity, aOrigin, aDoFunc) { let mockedDoc = {}; mockedDoc.id = uuid(); - mockedDoc.loggedInEmail = aIdentity; + mockedDoc.loggedInUser = aIdentity; mockedDoc.origin = aOrigin; mockedDoc['do'] = aDoFunc; mockedDoc.doReady = partial(aDoFunc, 'ready'); From ffb42aab238b8d58b9694c1ea1a46ffb10790834 Mon Sep 17 00:00:00 2001 From: Tobias Schneider Date: Tue, 30 Oct 2012 10:19:16 +0100 Subject: [PATCH 08/83] Bug 806780 - PathSkia::ContainsPoint should use SkPath::contains(x, y). r=jmuizelaar --- gfx/2d/PathSkia.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/gfx/2d/PathSkia.cpp b/gfx/2d/PathSkia.cpp index 4d605a427694..918d258477d8 100644 --- a/gfx/2d/PathSkia.cpp +++ b/gfx/2d/PathSkia.cpp @@ -136,15 +136,8 @@ PathSkia::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const return false; } - SkRegion pointRect; - pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1)), - int32_t(SkFloatToScalar(transformed.y - 1)), - int32_t(SkFloatToScalar(transformed.x + 1)), - int32_t(SkFloatToScalar(transformed.y + 1))); - - SkRegion pathRegion; - - return pathRegion.setPath(mPath, pointRect); + return mPath.contains(SkFloatToScalar(transformed.x), + SkFloatToScalar(transformed.y)); } static Rect SkRectToRect(const SkRect& aBounds) From cf6ad32dc62cdb025d8811ef1c06145782aa1090 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Thu, 1 Nov 2012 19:23:14 -0400 Subject: [PATCH 09/83] Bug 807544 - Add #ifdef to make XUL Fennec compile. r=gbrown --- widget/android/nsWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 7fc5f216107c..af00d466df9a 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -771,12 +771,14 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) if (ae->Type() == AndroidGeckoEvent::FORCED_RESIZE || nw != gAndroidBounds.width || nh != gAndroidBounds.height) { +#ifdef MOZ_ANDROID_OMTC if (sCompositorParent != 0 && gAndroidBounds.width == 0) { // Propagate size change to compositor. This is sometimes essential // on startup, because the window size may not have been available // when the compositor was created. ScheduleResumeComposition(nw, nh); } +#endif gAndroidBounds.width = nw; gAndroidBounds.height = nh; From 64e99f11c4b9f9e9a4bbdca61445e18bb4ab3981 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Thu, 1 Nov 2012 14:08:16 -0700 Subject: [PATCH 10/83] Bug 807805 - Remove app.reportCrashes from b2g.js now that we have crash reporter settings in the UI. r=fabrice --- b2g/app/b2g.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index ba897a4b085c..5b0a4d0726fb 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -190,9 +190,6 @@ pref("app.privacyURL", "http://www.mozilla.com/%LOCALE%/m/privacy.html"); pref("app.creditsURL", "http://www.mozilla.org/credits/"); pref("app.featuresURL", "http://www.mozilla.com/%LOCALE%/b2g/features/"); pref("app.faqURL", "http://www.mozilla.com/%LOCALE%/b2g/faq/"); -// Whether we want to report crashes (headless) -//XXX Remove this pref when bug 801932 is fixed -pref("app.reportCrashes", true); // Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror) pref("security.alternate_certificate_error_page", "certerror"); From d5fff20ef4835f39c21eb196c22f14f2dfe771bb Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 1 Nov 2012 12:30:21 -0400 Subject: [PATCH 11/83] Bug 807697 - Enable integer to float type coercion in the Web IDL parser; r=bzbarsky --HG-- extra : rebase_source : 93907e32c1111773af0b207274aa32323aabfa03 --- dom/bindings/parser/WebIDL.py | 8 ++++++++ dom/bindings/parser/tests/test_method.py | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 3d0498a09e83..a22c52106fbf 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1897,6 +1897,14 @@ class IDLValue(IDLObject): else: raise WebIDLError("Value %s is out of range for type %s." % (self.value, type), [location]) + elif self.type.isInteger() and type.isFloat(): + # Convert an integer literal into float + if -2**24 <= self.value <= 2**24: + floatType = BuiltinTypes[IDLBuiltinType.Types.float] + return IDLValue(self.location, floatType, float(self.value)) + else: + raise WebIDLError("Converting value %s to %s will lose precision." % + (self.value, type), [location]) elif self.type.isString() and type.isEnum(): # Just keep our string, but make sure it's a valid value for this enum if self.value not in type.inner.values(): diff --git a/dom/bindings/parser/tests/test_method.py b/dom/bindings/parser/tests/test_method.py index 40b2d2cf8b98..6e57ca336d3d 100644 --- a/dom/bindings/parser/tests/test_method.py +++ b/dom/bindings/parser/tests/test_method.py @@ -118,6 +118,19 @@ def WebIDLTest(parser, harness): [("Float", [("::TestMethods::doFloats::arg1", "arg1", "Float", False, False)])]) + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + void foo(optional float bar = 1); + }; + """) + results = parser.finish() + except Exception, x: + threw = True + harness.ok(not threw, "Should allow integer to float type corecion") + parser = parser.reset() threw = False try: From 4c01419332763838ce38710d0e6367383f9e965a Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 31 Oct 2012 20:26:03 -0400 Subject: [PATCH 12/83] Bug 807533 - Implement DelayNode; r=bzbarsky --HG-- extra : rebase_source : 36bb70c9f4b9c86fc1564447a34153d92fe7f347 --- content/media/webaudio/AudioContext.cpp | 8 +++ content/media/webaudio/AudioContext.h | 4 ++ content/media/webaudio/DelayNode.cpp | 42 +++++++++++ content/media/webaudio/DelayNode.h | 51 ++++++++++++++ content/media/webaudio/Makefile.in | 2 + content/media/webaudio/test/Makefile.in | 1 + .../media/webaudio/test/test_delayNode.html | 69 +++++++++++++++++++ dom/bindings/Bindings.conf | 5 ++ dom/webidl/AudioContext.webidl | 2 + dom/webidl/DelayNode.webidl | 19 +++++ dom/webidl/WebIDL.mk | 1 + 11 files changed, 204 insertions(+) create mode 100644 content/media/webaudio/DelayNode.cpp create mode 100644 content/media/webaudio/DelayNode.h create mode 100644 content/media/webaudio/test/test_delayNode.html create mode 100644 dom/webidl/DelayNode.webidl diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp index c5e6f4ca80ff..0aa510ec7940 100644 --- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -13,6 +13,7 @@ #include "AudioBufferSourceNode.h" #include "AudioBuffer.h" #include "GainNode.h" +#include "DelayNode.h" namespace mozilla { namespace dom { @@ -94,6 +95,13 @@ AudioContext::CreateGain() return gainNode.forget(); } +already_AddRefed +AudioContext::CreateDelay(float aMaxDelayTime) +{ + nsRefPtr delayNode = new DelayNode(this, aMaxDelayTime); + return delayNode.forget(); +} + } } diff --git a/content/media/webaudio/AudioContext.h b/content/media/webaudio/AudioContext.h index 6deaccafe679..c10161ff36f4 100644 --- a/content/media/webaudio/AudioContext.h +++ b/content/media/webaudio/AudioContext.h @@ -27,6 +27,7 @@ class AudioDestinationNode; class AudioBufferSourceNode; class AudioBuffer; class GainNode; +class DelayNode; class AudioContext MOZ_FINAL : public nsWrapperCache, public EnableWebAudioCheck @@ -65,6 +66,9 @@ public: already_AddRefed CreateGain(); + already_AddRefed + CreateDelay(float aMaxDelayTime); + private: nsCOMPtr mWindow; nsRefPtr mDestination; diff --git a/content/media/webaudio/DelayNode.cpp b/content/media/webaudio/DelayNode.cpp new file mode 100644 index 000000000000..be8e5d779b32 --- /dev/null +++ b/content/media/webaudio/DelayNode.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DelayNode.h" +#include "mozilla/dom/DelayNodeBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_CLASS(DelayNode) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DelayNode, AudioNode) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDelay) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DelayNode, AudioNode) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(tmp->mDelay, AudioParam, "delay value") +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode) +NS_INTERFACE_MAP_END_INHERITING(AudioNode) + +NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode) +NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode) + +DelayNode::DelayNode(AudioContext* aContext, float aMaxDelay) + : AudioNode(aContext) + , mDelay(new AudioParam(aContext, 0.0f, 0.0f, aMaxDelay)) +{ +} + +JSObject* +DelayNode::WrapObject(JSContext* aCx, JSObject* aScope, + bool* aTriedToWrap) +{ + return DelayNodeBinding::Wrap(aCx, aScope, this, aTriedToWrap); +} + +} +} + diff --git a/content/media/webaudio/DelayNode.h b/content/media/webaudio/DelayNode.h new file mode 100644 index 000000000000..2bc4d90610c7 --- /dev/null +++ b/content/media/webaudio/DelayNode.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DelayNode_h_ +#define DelayNode_h_ + +#include "AudioNode.h" +#include "AudioParam.h" + +namespace mozilla { +namespace dom { + +class AudioContext; + +class DelayNode : public AudioNode +{ +public: + DelayNode(AudioContext* aContext, float aMaxDelay); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DelayNode, AudioNode) + + virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope, + bool* aTriedToWrap); + + virtual uint32_t MaxNumberOfInputs() const MOZ_FINAL MOZ_OVERRIDE + { + return 1; + } + virtual uint32_t MaxNumberOfOutputs() const MOZ_FINAL MOZ_OVERRIDE + { + return 1; + } + + AudioParam* DelayTime() const + { + return mDelay; + } + +private: + nsRefPtr mDelay; +}; + +} +} + +#endif + diff --git a/content/media/webaudio/Makefile.in b/content/media/webaudio/Makefile.in index 777d9fe17372..0307b4ea34ba 100644 --- a/content/media/webaudio/Makefile.in +++ b/content/media/webaudio/Makefile.in @@ -22,6 +22,7 @@ CPPSRCS := \ AudioNode.cpp \ AudioParam.cpp \ AudioSourceNode.cpp \ + DelayNode.cpp \ EnableWebAudioCheck.cpp \ GainNode.cpp \ $(NULL) @@ -34,6 +35,7 @@ EXPORTS_mozilla/dom := \ AudioNode.h \ AudioParam.h \ AudioSourceNode.h \ + DelayNode.h \ GainNode.h \ $(NULL) diff --git a/content/media/webaudio/test/Makefile.in b/content/media/webaudio/test/Makefile.in index 7111a27bdea8..2b48a89d6906 100644 --- a/content/media/webaudio/test/Makefile.in +++ b/content/media/webaudio/test/Makefile.in @@ -14,6 +14,7 @@ MOCHITEST_FILES := \ test_AudioBuffer.html \ test_AudioContext.html \ test_badConnect.html \ + test_delayNode.html \ test_gainNode.html \ test_singleSourceDest.html \ $(NULL) diff --git a/content/media/webaudio/test/test_delayNode.html b/content/media/webaudio/test/test_delayNode.html new file mode 100644 index 000000000000..7bdfb1a50c8d --- /dev/null +++ b/content/media/webaudio/test/test_delayNode.html @@ -0,0 +1,69 @@ + + + + Test DelayNode + + + + +
+
+
+ + diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 34f118cf32d4..5f33918b8ffe 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -140,6 +140,11 @@ DOMInterfaces = { 'nativeType': 'nsICSSDeclaration' }, +'DelayNode': [ +{ + 'resultNotAddRefed': [ 'delayTime' ], +}], + 'Document': [ { 'nativeType': 'nsIDocument', diff --git a/dom/webidl/AudioContext.webidl b/dom/webidl/AudioContext.webidl index f92733690f9b..f286721ed241 100644 --- a/dom/webidl/AudioContext.webidl +++ b/dom/webidl/AudioContext.webidl @@ -27,6 +27,8 @@ interface mozAudioContext { [Creator] GainNode createGain(); + [Creator] + DelayNode createDelay(optional float maxDelayTime = 1); }; diff --git a/dom/webidl/DelayNode.webidl b/dom/webidl/DelayNode.webidl new file mode 100644 index 000000000000..37a348ce95cb --- /dev/null +++ b/dom/webidl/DelayNode.webidl @@ -0,0 +1,19 @@ +/* -*- 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/. + * + * The origin of this IDL file is + * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +[PrefControlled] +interface DelayNode : AudioNode { + + readonly attribute AudioParam delayTime; + +}; + diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 225c8aaa74c1..bca78af590ed 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -20,6 +20,7 @@ webidl_files = \ CanvasRenderingContext2D.webidl \ ClientRectList.webidl \ CSSStyleDeclaration.webidl \ + DelayNode.webidl \ DOMImplementation.webidl \ DOMTokenList.webidl \ DOMSettableTokenList.webidl \ From b428f34b9f982eee190a6ea2ee253b9e1e265809 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 1 Nov 2012 19:31:02 -0400 Subject: [PATCH 13/83] Backout e35f252ca573 for mochitest-other orange. CLOSED TREE --HG-- extra : rebase_source : 62e15f7306c4443d91daf6af66be9b7012ada9cc --- browser/base/content/browser.js | 82 +++++++++++++++---------------- testing/mochitest/browser-test.js | 7 +-- 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index adbd51f2bf57..5075e6719dfe 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1008,6 +1008,7 @@ var gBrowserInit = { if ("arguments" in window && window.arguments[0]) var uriToLoad = window.arguments[0]; + var isLoadingBlank = isBlankPageURL(uriToLoad); var mustLoadSidebar = false; gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false); @@ -1094,6 +1095,44 @@ var gBrowserInit = { // setup simple gestures support gGestureSupport.init(true); + + if (uriToLoad && uriToLoad != "about:blank") { + if (uriToLoad instanceof Ci.nsISupportsArray) { + let count = uriToLoad.Count(); + let specs = []; + for (let i = 0; i < count; i++) { + let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString); + specs.push(urisstring.data); + } + + // This function throws for certain malformed URIs, so use exception handling + // so that we don't disrupt startup + try { + gBrowser.loadTabs(specs, false, true); + } catch (e) {} + } + else if (uriToLoad instanceof XULElement) { + // swap the given tab with the default about:blank tab and then close + // the original tab in the other window. + + // Stop the about:blank load + gBrowser.stop(); + // make sure it has a docshell + gBrowser.docShell; + + gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad); + } + else if (window.arguments.length >= 3) { + loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null, + window.arguments[4] || false); + window.focus(); + } + // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3. + // Such callers expect that window.arguments[0] is handled as a single URI. + else + loadOneOrMoreURIs(uriToLoad); + } + if (window.opener && !window.opener.closed) { let openerSidebarBox = window.opener.document.getElementById("sidebar-box"); // If the opener had a sidebar, open the same sidebar in our window. @@ -1203,7 +1242,7 @@ var gBrowserInit = { retrieveToolbarIconsizesFromTheme(); // Wait until chrome is painted before executing code not critical to making the window visible - this._boundDelayedStartup = this._delayedStartup.bind(this, uriToLoad, mustLoadSidebar); + this._boundDelayedStartup = this._delayedStartup.bind(this, isLoadingBlank, mustLoadSidebar); window.addEventListener("MozAfterPaint", this._boundDelayedStartup); gStartupRan = true; @@ -1214,7 +1253,7 @@ var gBrowserInit = { this._boundDelayedStartup = null; }, - _delayedStartup: function(uriToLoad, mustLoadSidebar) { + _delayedStartup: function(isLoadingBlank, mustLoadSidebar) { let tmp = {}; Cu.import("resource:///modules/TelemetryTimestamps.jsm", tmp); let TelemetryTimestamps = tmp.TelemetryTimestamps; @@ -1222,45 +1261,6 @@ var gBrowserInit = { this._cancelDelayedStartup(); - var isLoadingBlank = isBlankPageURL(uriToLoad); - - if (uriToLoad && uriToLoad != "about:blank") { - if (uriToLoad instanceof Ci.nsISupportsArray) { - let count = uriToLoad.Count(); - let specs = []; - for (let i = 0; i < count; i++) { - let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString); - specs.push(urisstring.data); - } - - // This function throws for certain malformed URIs, so use exception handling - // so that we don't disrupt startup - try { - gBrowser.loadTabs(specs, false, true); - } catch (e) {} - } - else if (uriToLoad instanceof XULElement) { - // swap the given tab with the default about:blank tab and then close - // the original tab in the other window. - - // Stop the about:blank load - gBrowser.stop(); - // make sure it has a docshell - gBrowser.docShell; - - gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad); - } - else if (window.arguments.length >= 3) { - loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null, - window.arguments[4] || false); - window.focus(); - } - // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3. - // Such callers expect that window.arguments[0] is handled as a single URI. - else - loadOneOrMoreURIs(uriToLoad); - } - #ifdef MOZ_SAFE_BROWSING // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008. setTimeout(function() { SafeBrowsing.init(); }, 2000); diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index 2d8cf22580df..69e4c55e4df7 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -96,11 +96,8 @@ Tester.prototype = { Services.obs.addObserver(this, "chrome-document-global-created", false); Services.obs.addObserver(this, "content-document-global-created", false); this._globalProperties = Object.keys(window); - this._globalPropertyWhitelist = [ - "navigator", "constructor", "top", - "Application", - "__SS_tabsToRestore", "__SSi", - "webConsoleCommandController", + this._globalPropertyWhitelist = ["navigator", "constructor", "Application", + "__SS_tabsToRestore", "__SSi", "webConsoleCommandController", ]; if (this.tests.length) From d83c5899ef578a909adee04972cf78636955764a Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Nov 2012 00:36:50 +0100 Subject: [PATCH 14/83] Bug 806793: disable hoisting shape guards after bailing because of a shape guard, r=jandem --- js/src/ion/Bailouts.cpp | 12 ++++--- js/src/ion/Bailouts.h | 4 +-- js/src/ion/IonBuilder.cpp | 38 ++++++++++++++--------- js/src/ion/IonBuilder.h | 5 +++ js/src/ion/IonMacroAssembler.cpp | 4 +-- js/src/ion/IonTypes.h | 4 +-- js/src/ion/MIR.h | 1 + js/src/ion/arm/Lowering-arm.cpp | 8 ++++- js/src/ion/shared/Lowering-x86-shared.cpp | 8 ++++- js/src/jsscript.h | 3 ++ 10 files changed, 61 insertions(+), 26 deletions(-) diff --git a/js/src/ion/Bailouts.cpp b/js/src/ion/Bailouts.cpp index 700515325442..2cd225bbfbe1 100644 --- a/js/src/ion/Bailouts.cpp +++ b/js/src/ion/Bailouts.cpp @@ -313,8 +313,8 @@ ConvertFrames(JSContext *cx, IonActivation *activation, IonBailoutIterator &it) return BAILOUT_RETURN_RECOMPILE_CHECK; case Bailout_BoundsCheck: return BAILOUT_RETURN_BOUNDS_CHECK; - case Bailout_Invalidate: - return BAILOUT_RETURN_INVALIDATE; + case Bailout_ShapeGuard: + return BAILOUT_RETURN_SHAPE_GUARD; case Bailout_CachedShapeGuard: return BAILOUT_RETURN_CACHED_SHAPE_GUARD; @@ -552,7 +552,7 @@ ion::BoundsCheckFailure() } uint32 -ion::ForceInvalidation() +ion::ShapeGuardFailure() { JSContext *cx = GetIonContext()->cx; JSScript *script = GetBailedJSScript(cx); @@ -560,7 +560,9 @@ ion::ForceInvalidation() JS_ASSERT(script->hasIonScript()); JS_ASSERT(!script->ion->invalidated()); - IonSpew(IonSpew_Invalidate, "Forced invalidation bailout"); + script->failedShapeGuard = true; + + IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure"); return Invalidate(cx, script); } @@ -574,6 +576,8 @@ ion::CachedShapeGuardFailure() JS_ASSERT(script->hasIonScript()); JS_ASSERT(!script->ion->invalidated()); + script->failedShapeGuard = true; + // Purge JM caches in the script and all inlined script, to avoid baking in // the same shape guard next time. for (size_t i = 0; i < script->ion->scriptEntries(); i++) diff --git a/js/src/ion/Bailouts.h b/js/src/ion/Bailouts.h index cd1bf297c575..509816580e14 100644 --- a/js/src/ion/Bailouts.h +++ b/js/src/ion/Bailouts.h @@ -106,7 +106,7 @@ static const uint32 BAILOUT_RETURN_TYPE_BARRIER = 3; static const uint32 BAILOUT_RETURN_MONITOR = 4; static const uint32 BAILOUT_RETURN_RECOMPILE_CHECK = 5; static const uint32 BAILOUT_RETURN_BOUNDS_CHECK = 6; -static const uint32 BAILOUT_RETURN_INVALIDATE = 7; +static const uint32 BAILOUT_RETURN_SHAPE_GUARD = 7; static const uint32 BAILOUT_RETURN_OVERRECURSED = 8; static const uint32 BAILOUT_RETURN_CACHED_SHAPE_GUARD = 9; @@ -222,7 +222,7 @@ uint32 RecompileForInlining(); uint32 BoundsCheckFailure(); -uint32 ForceInvalidation(); +uint32 ShapeGuardFailure(); uint32 CachedShapeGuardFailure(); diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index db60fa414578..c743820b3da6 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -35,6 +35,7 @@ IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph, oracle(oracle), inliningDepth(inliningDepth), failedBoundsCheck_(info->script()->failedBoundsCheck), + failedShapeGuard_(info->script()->failedShapeGuard), lazyArguments_(NULL) { script_.init(info->script()); @@ -406,6 +407,9 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi if (callerBuilder->failedBoundsCheck_) failedBoundsCheck_ = true; + if (callerBuilder->failedShapeGuard_) + failedShapeGuard_ = true; + // Generate single entrance block. current = newBlock(pc); if (!current) @@ -4617,10 +4621,8 @@ IonBuilder::jsop_getgname(HandlePropertyName name) // If we have a property typeset, the isOwnProperty call will trigger recompilation if // the property is deleted or reconfigured. - if (!propertyTypes && shape->configurable()) { - MGuardShape *guard = MGuardShape::New(global, globalObj->lastProperty(), Bailout_Invalidate); - current->add(guard); - } + if (!propertyTypes && shape->configurable()) + global = addShapeGuard(global, globalObj->lastProperty(), Bailout_ShapeGuard); JS_ASSERT(shape->slot() >= globalObj->numFixedSlots()); @@ -4670,10 +4672,8 @@ IonBuilder::jsop_setgname(HandlePropertyName name) // If we have a property type set, the isOwnProperty call will trigger recompilation // if the property is deleted or reconfigured. Without TI, we always need a shape guard // to guard against the property being reconfigured as non-writable. - if (!propertyTypes) { - MGuardShape *guard = MGuardShape::New(global, globalObj->lastProperty(), Bailout_Invalidate); - current->add(guard); - } + if (!propertyTypes) + global = addShapeGuard(global, globalObj->lastProperty(), Bailout_ShapeGuard); JS_ASSERT(shape->slot() >= globalObj->numFixedSlots()); @@ -5436,8 +5436,7 @@ IonBuilder::TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types, Handle // are no lookup hooks for this property. MInstruction *wrapper = MConstant::New(ObjectValue(*foundProto)); current->add(wrapper); - MGuardShape *guard = MGuardShape::New(wrapper, foundProto->lastProperty(), Bailout_Invalidate); - current->add(guard); + wrapper = addShapeGuard(wrapper, foundProto->lastProperty(), Bailout_ShapeGuard); // Now we have to freeze all the property typesets to ensure there isn't a // lower shadowing getter or setter installed in the future. @@ -5961,8 +5960,7 @@ IonBuilder::getPropTryMonomorphic(bool *emitted, HandleId id, types::StackTypeSe // the shape is not in dictionary made. We cannot be sure that the shape is // still a lastProperty, and calling Shape::search() on dictionary mode // shapes that aren't lastProperty is invalid. - MGuardShape *guard = MGuardShape::New(obj, objShape, Bailout_CachedShapeGuard); - current->add(guard); + obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard); spew("Inlining monomorphic GETPROP"); Shape *shape = objShape->search(cx, id); @@ -6112,8 +6110,7 @@ IonBuilder::jsop_setprop(HandlePropertyName name) // long as the shape is not in dictionary mode. We cannot be sure // that the shape is still a lastProperty, and calling Shape::search // on dictionary mode shapes that aren't lastProperty is invalid. - MGuardShape *guard = MGuardShape::New(obj, objShape, Bailout_CachedShapeGuard); - current->add(guard); + obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard); Shape *shape = objShape->search(cx, NameToId(name)); JS_ASSERT(shape); @@ -6441,3 +6438,16 @@ IonBuilder::addBoundsCheck(MDefinition *index, MDefinition *length) return check; } + +MInstruction * +IonBuilder::addShapeGuard(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind) +{ + MGuardShape *guard = MGuardShape::New(obj, shape, bailoutKind); + current->add(guard); + + // If a shape guard failed in the past, don't optimize shape guard. + if (failedShapeGuard_) + guard->setNotMovable(); + + return guard; +} diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index 6be1155a9533..7797e5a838b1 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -280,6 +280,7 @@ class IonBuilder : public MIRGenerator MDefinition *walkScopeChain(unsigned hops); MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length); + MInstruction *addShapeGuard(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind); JSObject *getNewArrayTemplateObject(uint32 count); @@ -473,6 +474,10 @@ class IonBuilder : public MIRGenerator // an outer script. bool failedBoundsCheck_; + // True if script->failedShapeGuard is set for the current script or + // an outer script. + bool failedShapeGuard_; + // If this script can use a lazy arguments object, it wil be pre-created // here. MInstruction *lazyArguments_; diff --git a/js/src/ion/IonMacroAssembler.cpp b/js/src/ion/IonMacroAssembler.cpp index 2fb0b04f74aa..27f290801c99 100644 --- a/js/src/ion/IonMacroAssembler.cpp +++ b/js/src/ion/IonMacroAssembler.cpp @@ -524,7 +524,7 @@ MacroAssembler::generateBailoutTail(Register scratch) branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_BOUNDS_CHECK), &boundscheck); branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_OVERRECURSED), &overrecursed); - branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_INVALIDATE), &invalidate); + branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_SHAPE_GUARD), &invalidate); // Fall-through: cached shape guard failure. { @@ -539,7 +539,7 @@ MacroAssembler::generateBailoutTail(Register scratch) bind(&invalidate); { setupUnalignedABICall(0, scratch); - callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForceInvalidation)); + callWithABI(JS_FUNC_TO_DATA_PTR(void *, ShapeGuardFailure)); branchTest32(Zero, ReturnReg, ReturnReg, &exception); jump(&interpret); diff --git a/js/src/ion/IonTypes.h b/js/src/ion/IonTypes.h index ea324dfe4d99..3beda5b2e14f 100644 --- a/js/src/ion/IonTypes.h +++ b/js/src/ion/IonTypes.h @@ -52,8 +52,8 @@ enum BailoutKind // A bailout triggered by a bounds-check failure. Bailout_BoundsCheck, - // Like Bailout_Normal, but invalidate the current IonScript. - Bailout_Invalidate, + // A shape guard based on TI information failed. + Bailout_ShapeGuard, // A shape guard based on JM ICs failed. Bailout_CachedShapeGuard diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 5964d36d6f45..f235c2c5039c 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -4484,6 +4484,7 @@ class MGuardShape { setGuard(); setMovable(); + setResultType(MIRType_Object); } public: diff --git a/js/src/ion/arm/Lowering-arm.cpp b/js/src/ion/arm/Lowering-arm.cpp index 835b57efb9c7..a166358df640 100644 --- a/js/src/ion/arm/Lowering-arm.cpp +++ b/js/src/ion/arm/Lowering-arm.cpp @@ -297,9 +297,15 @@ LIRGeneratorARM::newLTableSwitchV(MTableSwitch *tableswitch) bool LIRGeneratorARM::visitGuardShape(MGuardShape *ins) { + JS_ASSERT(ins->obj()->type() == MIRType_Object); + LDefinition tempObj = temp(LDefinition::OBJECT); LGuardShape *guard = new LGuardShape(useRegister(ins->obj()), tempObj); - return assignSnapshot(guard, ins->bailoutKind()) && add(guard, ins); + if (!assignSnapshot(guard, ins->bailoutKind())) + return false; + if (!add(guard, ins)) + return false; + return redefine(ins, ins->obj()); } bool diff --git a/js/src/ion/shared/Lowering-x86-shared.cpp b/js/src/ion/shared/Lowering-x86-shared.cpp index df173eb21e37..7c4fcfd2cdd6 100644 --- a/js/src/ion/shared/Lowering-x86-shared.cpp +++ b/js/src/ion/shared/Lowering-x86-shared.cpp @@ -46,8 +46,14 @@ LIRGeneratorX86Shared::visitInterruptCheck(MInterruptCheck *ins) bool LIRGeneratorX86Shared::visitGuardShape(MGuardShape *ins) { + JS_ASSERT(ins->obj()->type() == MIRType_Object); + LGuardShape *guard = new LGuardShape(useRegister(ins->obj())); - return assignSnapshot(guard, ins->bailoutKind()) && add(guard, ins); + if (!assignSnapshot(guard, ins->bailoutKind())) + return false; + if (!add(guard, ins)) + return false; + return redefine(ins, ins->obj()); } bool diff --git a/js/src/jsscript.h b/js/src/jsscript.h index af2fe623f62c..21586a9ca82f 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -472,6 +472,9 @@ struct JSScript : public js::gc::Cell #ifdef JS_METHODJIT bool debugMode:1; /* script was compiled in debug mode */ bool failedBoundsCheck:1; /* script has had hoisted bounds checks fail */ +#endif +#ifdef JS_ION + bool failedShapeGuard:1; /* script has had hoisted shape guard fail */ #endif bool invalidatedIdempotentCache:1; /* idempotent cache has triggered invalidation */ bool isGenerator:1; /* is a generator */ From 3bdf0ebcd3c39e23b5948b107ecd62e965138147 Mon Sep 17 00:00:00 2001 From: Sam Garrett Date: Sat, 27 Oct 2012 17:42:25 -0400 Subject: [PATCH 15/83] Bug 771554 - Removing autolog and having tps write-out results to JSON so that coversheet can handle them, r=jgriffin, DONTBUILD(NPOTB) --- testing/tps/setup.py | 4 +- testing/tps/tps/cli.py | 44 +-------- testing/tps/tps/emailtemplate.py | 158 ------------------------------- testing/tps/tps/sendemail.py | 50 ---------- testing/tps/tps/testrunner.py | 148 ++++++----------------------- testing/tps/tps/thread.py | 19 +--- 6 files changed, 42 insertions(+), 381 deletions(-) delete mode 100644 testing/tps/tps/emailtemplate.py delete mode 100644 testing/tps/tps/sendemail.py diff --git a/testing/tps/setup.py b/testing/tps/setup.py index bbc90e64e684..b62d78696e67 100644 --- a/testing/tps/setup.py +++ b/testing/tps/setup.py @@ -5,11 +5,11 @@ import sys from setuptools import setup, find_packages -version = '0.3' +version = '0.4' deps = ['mozinfo >= 0.3.3', 'mozprofile >= 0.4', 'mozprocess >= 0.4', 'mozrunner >= 5.8', 'mozinstall >= 1.4', - 'mozautolog >= 0.2.4', 'mozautoeslib >= 0.1.1', 'httplib2 >= 0.7.3'] + 'httplib2 >= 0.7.3'] # we only support python 2.6+ right now assert sys.version_info[0] == 2 diff --git a/testing/tps/tps/cli.py b/testing/tps/tps/cli.py index d4b584cf7c1c..c8ea9f828c25 100644 --- a/testing/tps/tps/cli.py +++ b/testing/tps/tps/cli.py @@ -8,7 +8,6 @@ import optparse import os import sys import time -import traceback from threading import RLock @@ -16,19 +15,10 @@ from tps import TPSFirefoxRunner, TPSTestRunner def main(): parser = optparse.OptionParser() - parser.add_option("--email-results", - action = "store_true", dest = "emailresults", - default = False, - help = "email the test results to the recipients defined " - "in the config file") parser.add_option("--mobile", action = "store_true", dest = "mobile", default = False, help = "run with mobile settings") - parser.add_option("--autolog", - action = "store_true", dest = "autolog", - default = False, - help = "post results to Autolog") parser.add_option("--testfile", action = "store", type = "string", dest = "testfile", default = '../../services/sync/tests/tps/test_sync.js', @@ -38,6 +28,10 @@ def main(): action = "store", type = "string", dest = "logfile", default = 'tps.log', help = "path to the log file [default: %default]") + parser.add_option("--resultfile", + action = "store", type = "string", dest = "resultfile", + default = 'tps_result.json', + help = "path to the result file [default: %default]") parser.add_option("--binary", action = "store", type = "string", dest = "binary", default = None, @@ -90,43 +84,13 @@ def main(): extensionDir = "%s:/%s" % (m.group(0)[1:2], extensionDir[3:]) extensionDir = extensionDir.replace("/", "\\") - if options.binary is None: - while True: - try: - # If no binary is specified, start the pulse build monitor, and wait - # until we receive build notifications before running tests. - monitor = TPSPulseMonitor(extensionDir, - config=config, - autolog=options.autolog, - emailresults=options.emailresults, - testfile=options.testfile, - logfile=options.logfile, - rlock=rlock) - print "waiting for pulse build notifications" - - if options.pulsefile: - # For testing purposes, inject a pulse message directly into - # the monitor. - builddata = json.loads(open(options.pulsefile, 'r').read()) - monitor.onBuildComplete(builddata) - - monitor.listen() - except KeyboardInterrupt: - sys.exit() - except: - traceback.print_exc() - print 'sleeping 5 minutes' - time.sleep(300) - TPS = TPSTestRunner(extensionDir, - emailresults=options.emailresults, testfile=options.testfile, logfile=options.logfile, binary=options.binary, config=config, rlock=rlock, mobile=options.mobile, - autolog=options.autolog, ignore_unused_engines=options.ignore_unused_engines) TPS.run_tests() diff --git a/testing/tps/tps/emailtemplate.py b/testing/tps/tps/emailtemplate.py deleted file mode 100644 index 7f3556f24214..000000000000 --- a/testing/tps/tps/emailtemplate.py +++ /dev/null @@ -1,158 +0,0 @@ -# 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/. - -import datetime - -def GenerateEmailBody(data, numpassed, numfailed, serverUrl, buildUrl): - - now = datetime.datetime.now() - builddate = datetime.datetime.strptime(data['productversion']['buildid'], - '%Y%m%d%H%M%S') - tree = data['productversion']['repository'] - - row = """ - - {name} - {state} - {message} - -""" - - rowWithLog = """ - - {name} - {state} - {message} [view log] - -""" - - rows = "" - for test in data['tests']: - if test.get('logurl'): - rows += rowWithLog.format(name=test['name'], - state=test['state'], - message=test['message'] if test['message'] else 'None', - logurl=test['logurl']) - else: - rows += row.format(name=test['name'], - state=test['state'], - message=test['message'] if test['message'] else 'None') - - firefox_version = data['productversion']['version'] - if buildUrl is not None: - firefox_version = "%s" % (buildUrl, firefox_version) - body = """ - - - TPS - - - - -
- -

TPS Testrun Details

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Testrun Date{date}
Firefox Version{firefox_version}
Firefox Build Date{firefox_date}
Firefox Sync Version / Type{sync_version} / {sync_type} -
Firefox Sync Changeset - - - - {changeset} / {sync_tree} - -
Sync Server{server}
OS{os}
Passed Tests - {numpassed} -
Failed Tests - - {numfailed} -
- - - - - - - - - - - -{rows} - -
TestcaseResultMessage
- -
- - - -""".format(date=now.ctime(), - firefox_version=firefox_version, - firefox_date=builddate.ctime(), - sync_version=data['addonversion']['version'], - sync_type=data['synctype'], - sync_tree=tree[tree.rfind("/") + 1:], - repository=data['productversion']['repository'], - changeset=data['productversion']['changeset'], - os=data['os'], - rows=rows, - numpassed=numpassed, - numfailed=numfailed, - passclass="pass" if numpassed > 0 else "light", - failclass="fail" if numfailed > 0 else "light", - server=serverUrl if serverUrl != "" else "default" - ) - - return body diff --git a/testing/tps/tps/sendemail.py b/testing/tps/tps/sendemail.py deleted file mode 100644 index b4273541bd6b..000000000000 --- a/testing/tps/tps/sendemail.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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/. - -import smtplib -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText - -def SendEmail(From=None, To=None, Subject='No Subject', - TextData=None, HtmlData=None, - Server='mail.mozilla.com', Port=465, - Username=None, Password=None): - """Sends an e-mail. - - From is an e-mail address, To is a list of e-mail adresses. - - TextData and HtmlData are both strings. You can specify one or both. - If you specify both, the e-mail will be sent as a MIME multipart - alternative; i.e., the recipient will see the HTML content if his - viewer supports it, otherwise he'll see the text content. - """ - - if From is None or To is None: - raise Exception("Both From and To must be specified") - if TextData is None and HtmlData is None: - raise Exception("Must specify either TextData or HtmlData") - - server = smtplib.SMTP_SSL(Server, Port) - - if Username is not None and Password is not None: - server.login(Username, Password) - - if HtmlData is None: - msg = MIMEText(TextData) - elif TextData is None: - msg = MIMEMultipart() - msg.preamble = Subject - msg.attach(MIMEText(HtmlData, 'html')) - else: - msg = MIMEMultipart('alternative') - msg.attach(MIMEText(TextData, 'plain')) - msg.attach(MIMEText(HtmlData, 'html')) - - msg['Subject'] = Subject - msg['From'] = From - msg['To'] = ', '.join(To) - - server.sendmail(From, To, msg.as_string()) - - server.quit() diff --git a/testing/tps/tps/testrunner.py b/testing/tps/tps/testrunner.py index 57723c9b915a..1a9fe337fb72 100644 --- a/testing/tps/tps/testrunner.py +++ b/testing/tps/tps/testrunner.py @@ -46,7 +46,6 @@ class TempFile(object): __del__ = cleanup - class TPSTestRunner(object): default_env = { 'MOZ_CRASHREPORTER_DISABLE': '1', @@ -86,14 +85,15 @@ class TPSTestRunner(object): ffDateRe = re.compile( r"Firefox builddate: (?P.*)\n") - def __init__(self, extensionDir, emailresults=False, testfile="sync.test", + def __init__(self, extensionDir, + testfile="sync.test", binary=None, config=None, rlock=None, mobile=False, - autolog=False, logfile="tps.log", + logfile="tps.log", resultfile="tps_result.json", ignore_unused_engines=False): self.extensions = [] - self.emailresults = emailresults self.testfile = testfile self.logfile = os.path.abspath(logfile) + self.resultfile = resultfile self.binary = binary self.ignore_unused_engines = ignore_unused_engines self.config = config if config else {} @@ -105,7 +105,6 @@ class TPSTestRunner(object): self.nightly = False self.rlock = rlock self.mobile = mobile - self.autolog = autolog self.tpsxpi = None self.firefoxRunner = None self.extensionDir = extensionDir @@ -132,6 +131,25 @@ class TPSTestRunner(object): if printToConsole: print msg + def writeToResultFile(self, postdata, body=None, + sendTo='crossweave@mozilla.com'): + """Writes results to test file""" + f = open(self.resultfile, 'a') + if body is not None: + postdata['body'] = body + if self.numpassed is not None: + postdata['numpassed'] = self.numpassed + if self.numfailed is not None: + postdata['numfailed'] = self.numfailed + if self.firefoxRunner and self.firefoxRunner.url: + postdata['firefoxrunnerurl'] = self.firefoxRunner.url + + postdata['sendTo'] = sendTo + results = {} + results['results'] = postdata + f.write(json.dumps(results, indent=2)) + f.close() + def _zip_add_file(self, zip, file, rootDir): zip.write(os.path.join(rootDir, file), file) @@ -314,26 +332,19 @@ class TPSTestRunner(object): traceback.print_exc() self.numpassed = 0 self.numfailed = 1 - if self.emailresults: - try: - self.sendEmail('
%s
' % traceback.format_exc(), - sendTo='crossweave@mozilla.com') - except: - traceback.print_exc() - else: - raise - + try: + self.writeToResultFile(self.postdata, + '
%s
' % traceback.format_exc()) + except: + traceback.print_exc() else: try: - if self.autolog: - self.postToAutolog() - if self.emailresults: - self.sendEmail() + self.writeToResultFile(self.postdata) except: traceback.print_exc() try: - self.sendEmail('
%s
' % traceback.format_exc(), - sendTo='crossweave@mozilla.com') + self.writeToResultFile(self.postdata, + '
%s
' % traceback.format_exc()) except: traceback.print_exc() @@ -409,100 +420,3 @@ class TPSTestRunner(object): 'addonversion': self.addonversion, 'synctype': self.synctype, } - - def sendEmail(self, body=None, sendTo=None): - # send the result e-mail - if self.config.get('email') and self.config['email'].get('username') \ - and self.config['email'].get('password'): - - from tps.sendemail import SendEmail - from tps.emailtemplate import GenerateEmailBody - - if body is None: - buildUrl = None - if self.firefoxRunner and self.firefoxRunner.url: - buildUrl = self.firefoxRunner.url - body = GenerateEmailBody(self.postdata, - self.numpassed, - self.numfailed, - self.config['account']['serverURL'], - buildUrl) - - subj = "TPS Report: " - if self.numfailed == 0 and self.numpassed > 0: - subj += "YEEEAAAHHH" - else: - subj += "PC LOAD LETTER" - - changeset = self.postdata['productversion']['changeset'] if \ - self.postdata and self.postdata.get('productversion') and \ - self.postdata['productversion'].get('changeset') \ - else 'unknown' - subj +=", changeset " + changeset + "; " + str(self.numfailed) + \ - " failed, " + str(self.numpassed) + " passed" - - To = [sendTo] if sendTo else None - if not To: - if self.numfailed > 0 or self.numpassed == 0: - To = self.config['email'].get('notificationlist') - else: - To = self.config['email'].get('passednotificationlist') - - if To: - SendEmail(From=self.config['email']['username'], - To=To, - Subject=subj, - HtmlData=body, - Username=self.config['email']['username'], - Password=self.config['email']['password']) - - def postToAutolog(self): - from mozautolog import RESTfulAutologTestGroup as AutologTestGroup - - group = AutologTestGroup( - harness='crossweave', - testgroup='crossweave-%s' % self.synctype, - server=self.config.get('es'), - restserver=self.config.get('restserver'), - machine=socket.gethostname(), - platform=self.config.get('platform', None), - os=self.config.get('os', None), - ) - tree = self.postdata['productversion']['repository'] - group.set_primary_product( - tree=tree[tree.rfind("/")+1:], - version=self.postdata['productversion']['version'], - buildid=self.postdata['productversion']['buildid'], - buildtype='opt', - revision=self.postdata['productversion']['changeset'], - ) - group.add_test_suite( - passed=self.numpassed, - failed=self.numfailed, - todo=0, - ) - for test in self.results: - if test['state'] != "TEST-PASS": - errorlog = self.errorlogs.get(test['name']) - errorlog_filename = errorlog.filename if errorlog else None - group.add_test_failure( - test = test['name'], - status = test['state'], - text = test['message'], - logfile = errorlog_filename - ) - try: - group.submit() - except: - self.sendEmail('
%s
' % traceback.format_exc(), - sendTo='crossweave@mozilla.com') - return - - # Iterate through all testfailure objects, and update the postdata - # dict with the testfailure logurl's, if any. - for tf in group.testsuites[-1].testfailures: - result = [x for x in self.results if x.get('name') == tf.test] - if not result: - continue - result[0]['logurl'] = tf.logurl - diff --git a/testing/tps/tps/thread.py b/testing/tps/tps/thread.py index fee30ff8ac2f..cab326ae135c 100644 --- a/testing/tps/tps/thread.py +++ b/testing/tps/tps/thread.py @@ -8,32 +8,27 @@ from testrunner import TPSTestRunner class TPSTestThread(Thread): - def __init__(self, extensionDir, builddata=None, emailresults=False, - testfile=None, logfile=None, rlock=None, config=None, - autolog=False): + def __init__(self, extensionDir, builddata=None, + testfile=None, logfile=None, rlock=None, config=None): assert(builddata) assert(config) self.extensionDir = extensionDir self.builddata = builddata - self.emailresults = emailresults self.testfile = testfile self.logfile = logfile self.rlock = rlock self.config = config - self.autolog = autolog Thread.__init__(self) def run(self): # run the tests in normal mode ... TPS = TPSTestRunner(self.extensionDir, - emailresults=self.emailresults, testfile=self.testfile, logfile=self.logfile, binary=self.builddata['buildurl'], config=self.config, rlock=self.rlock, - mobile=False, - autolog=self.autolog) + mobile=False) TPS.run_tests() # Get the binary used by this TPS instance, and use it in subsequent @@ -42,14 +37,12 @@ class TPSTestThread(Thread): # ... and then again in mobile mode TPS_mobile = TPSTestRunner(self.extensionDir, - emailresults=self.emailresults, testfile=self.testfile, logfile=self.logfile, binary=binary, config=self.config, rlock=self.rlock, - mobile=True, - autolog=self.autolog) + mobile=True) TPS_mobile.run_tests() # ... and again via the staging server, if credentials are present @@ -62,12 +55,10 @@ class TPSTestThread(Thread): stageconfig = self.config.copy() stageconfig['account'] = stageaccount.copy() TPS_stage = TPSTestRunner(self.extensionDir, - emailresults=self.emailresults, testfile=self.testfile, logfile=self.logfile, binary=binary, config=stageconfig, rlock=self.rlock, - mobile=False, - autolog=self.autolog) + mobile=False)#, autolog=self.autolog) TPS_stage.run_tests() From 0a726cdb19a949529398105420322be1bbf5d95f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Nov 2012 16:51:04 -0700 Subject: [PATCH 16/83] Bug 806283 - Split up and add native iterators to "object/misc" memory reports. r=jorendorff. --HG-- extra : rebase_source : 867e178d0c921de9e144fb274e1c0d77c8845b81 --- js/public/MemoryMetrics.h | 40 ++++++++++++++++++------------- js/src/builtin/Iterator-inl.h | 7 ++++++ js/src/jsiter.cpp | 6 +++++ js/src/jsiter.h | 2 ++ js/src/jsmemorymetrics.cpp | 18 ++++++++------ js/src/jsobj.h | 8 ++++--- js/src/jsobjinlines.h | 17 ++++++++----- js/xpconnect/src/XPCJSRuntime.cpp | 26 +++++++++++++------- 8 files changed, 83 insertions(+), 41 deletions(-) diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index f5be90335174..d43a3a6a9276 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -146,10 +146,12 @@ struct CompartmentStats #if JS_HAS_XML_SUPPORT , gcHeapXML(0) #endif - , objectSlots(0) - , objectElements(0) - , objectMisc(0) - , objectPrivate(0) + , objectsExtraSlots(0) + , objectsExtraElements(0) + , objectsExtraArgumentsData(0) + , objectsExtraRegExpStatics(0) + , objectsExtraPropertyIteratorData(0) + , objectsExtraPrivate(0) , stringCharsNonHuge(0) , shapesExtraTreeTables(0) , shapesExtraDictTables(0) @@ -186,10 +188,12 @@ struct CompartmentStats #if JS_HAS_XML_SUPPORT , gcHeapXML(other.gcHeapXML) #endif - , objectSlots(other.objectSlots) - , objectElements(other.objectElements) - , objectMisc(other.objectMisc) - , objectPrivate(other.objectPrivate) + , objectsExtraSlots(other.objectsExtraSlots) + , objectsExtraElements(other.objectsExtraElements) + , objectsExtraArgumentsData(other.objectsExtraArgumentsData) + , objectsExtraRegExpStatics(other.objectsExtraRegExpStatics) + , objectsExtraPropertyIteratorData(other.objectsExtraPropertyIteratorData) + , objectsExtraPrivate(other.objectsExtraPrivate) , stringCharsNonHuge(other.stringCharsNonHuge) , shapesExtraTreeTables(other.shapesExtraTreeTables) , shapesExtraDictTables(other.shapesExtraDictTables) @@ -234,10 +238,12 @@ struct CompartmentStats size_t gcHeapXML; #endif - size_t objectSlots; - size_t objectElements; - size_t objectMisc; - size_t objectPrivate; + size_t objectsExtraSlots; + size_t objectsExtraElements; + size_t objectsExtraArgumentsData; + size_t objectsExtraRegExpStatics; + size_t objectsExtraPropertyIteratorData; + size_t objectsExtraPrivate; size_t stringCharsNonHuge; size_t shapesExtraTreeTables; size_t shapesExtraDictTables; @@ -280,10 +286,12 @@ struct CompartmentStats ADD(gcHeapXML); #endif - ADD(objectSlots); - ADD(objectElements); - ADD(objectMisc); - ADD(objectPrivate); + ADD(objectsExtraSlots); + ADD(objectsExtraElements); + ADD(objectsExtraArgumentsData); + ADD(objectsExtraRegExpStatics); + ADD(objectsExtraPropertyIteratorData); + ADD(objectsExtraPrivate); ADD(stringCharsNonHuge); ADD(shapesExtraTreeTables); ADD(shapesExtraDictTables); diff --git a/js/src/builtin/Iterator-inl.h b/js/src/builtin/Iterator-inl.h index ff9ac8757709..7f93219af814 100644 --- a/js/src/builtin/Iterator-inl.h +++ b/js/src/builtin/Iterator-inl.h @@ -22,6 +22,13 @@ JSObject::asPropertyIterator() return *static_cast(this); } +inline const js::PropertyIteratorObject & +JSObject::asPropertyIterator() const +{ + JS_ASSERT(isPropertyIterator()); + return *static_cast(this); +} + js::NativeIterator * js::PropertyIteratorObject::getNativeIterator() const { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 9c9c71eb70e9..8df12c81816e 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -821,6 +821,12 @@ iterator_iteratorObject(JSContext *cx, HandleObject obj, JSBool keysonly) return obj; } +size_t +PropertyIteratorObject::sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const +{ + return mallocSizeOf(getPrivate()); +} + void PropertyIteratorObject::trace(JSTracer *trc, RawObject obj) { diff --git a/js/src/jsiter.h b/js/src/jsiter.h index b39f802f0b90..d9a44d24d0e4 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -78,6 +78,8 @@ class PropertyIteratorObject : public JSObject inline NativeIterator *getNativeIterator() const; inline void setNativeIterator(js::NativeIterator *ni); + size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const; + private: static void trace(JSTracer *trc, RawObject obj); static void finalize(FreeOp *fop, RawObject obj); diff --git a/js/src/jsmemorymetrics.cpp b/js/src/jsmemorymetrics.cpp index 0441db9049af..7e315a230a7c 100644 --- a/js/src/jsmemorymetrics.cpp +++ b/js/src/jsmemorymetrics.cpp @@ -155,19 +155,23 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin } else { cStats->gcHeapObjectsOrdinary += thingSize; } - size_t slotsSize, elementsSize, miscSize; - obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize, - &elementsSize, &miscSize); - cStats->objectSlots += slotsSize; - cStats->objectElements += elementsSize; - cStats->objectMisc += miscSize; + size_t slotsSize, elementsSize, argumentsDataSize, regExpStaticsSize, + propertyIteratorDataSize; + obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize, &elementsSize, + &argumentsDataSize, ®ExpStaticsSize, + &propertyIteratorDataSize); + cStats->objectsExtraSlots += slotsSize; + cStats->objectsExtraElements += elementsSize; + cStats->objectsExtraArgumentsData += argumentsDataSize; + cStats->objectsExtraRegExpStatics += regExpStaticsSize; + cStats->objectsExtraPropertyIteratorData += propertyIteratorDataSize; if (ObjectPrivateVisitor *opv = closure->opv) { js::Class *clazz = js::GetObjectClass(obj); if (clazz->flags & JSCLASS_HAS_PRIVATE && clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { - cStats->objectPrivate += opv->sizeOfIncludingThis(GetObjectPrivate(obj)); + cStats->objectsExtraPrivate += opv->sizeOfIncludingThis(GetObjectPrivate(obj)); } } break; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index b1618a3e17eb..c48f0daa4ed0 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -368,9 +368,10 @@ struct JSObject : public js::ObjectImpl inline size_t computedSizeOfThisSlotsElements() const; - inline void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, - size_t *slotsSize, size_t *elementsSize, - size_t *miscSize) const; + inline void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *slotsSize, + size_t *elementsSize, size_t *argumentsDataSize, + size_t *regExpStaticsSize, + size_t *propertyIteratorDataSize) const; bool hasIdempotentProtoChain() const; @@ -1020,6 +1021,7 @@ struct JSObject : public js::ObjectImpl inline js::NormalArgumentsObject &asNormalArguments(); inline js::NumberObject &asNumber(); inline js::PropertyIteratorObject &asPropertyIterator(); + inline const js::PropertyIteratorObject &asPropertyIterator() const; inline js::RegExpObject &asRegExp(); inline js::ScopeObject &asScope(); inline js::SetObject &asSet(); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 48032ae606c4..fae2a4c8f1da 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -27,6 +27,7 @@ #include "jswrapper.h" #include "builtin/MapObject.h" +#include "builtin/Iterator-inl.h" #include "gc/Barrier.h" #include "gc/Marking.h" #include "gc/Root.h" @@ -989,9 +990,9 @@ JSObject::computedSizeOfThisSlotsElements() const } inline void -JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, - size_t *slotsSize, size_t *elementsSize, - size_t *miscSize) const +JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *slotsSize, + size_t *elementsSize, size_t *argumentsDataSize, + size_t *regExpStaticsSize, size_t *propertyIteratorDataSize) const { *slotsSize = 0; if (hasDynamicSlots()) { @@ -1004,11 +1005,15 @@ JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, } /* Other things may be measured in the future if DMD indicates it is worthwhile. */ - *miscSize = 0; + *argumentsDataSize = 0; + *regExpStaticsSize = 0; + *propertyIteratorDataSize = 0; if (isArguments()) { - *miscSize += asArguments().sizeOfMisc(mallocSizeOf); + *argumentsDataSize += asArguments().sizeOfMisc(mallocSizeOf); } else if (isRegExpStatics()) { - *miscSize += js::SizeOfRegExpStaticsData(this, mallocSizeOf); + *regExpStaticsSize += js::SizeOfRegExpStaticsData(this, mallocSizeOf); + } else if (isPropertyIterator()) { + *propertyIteratorDataSize += asPropertyIterator().sizeOfMisc(mallocSizeOf); } } diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 4f20015b3240..840cf8d59bd3 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1559,30 +1559,38 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats, "heap that holds E4X XML objects."); #endif - CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/slots"), - cStats.objectSlots, + CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects-extra/slots"), + cStats.objectsExtraSlots, "Memory allocated for the non-fixed object " "slot arrays, which are used to represent object properties. " "Some objects also contain a fixed number of slots which are " "stored on the JavaScript heap; those slots " "are not counted here, but in 'gc-heap/objects' instead."); - CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/elements"), - cStats.objectElements, + CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects-extra/elements"), + cStats.objectsExtraElements, "Memory allocated for object element " "arrays, which are used to represent indexed object " "properties."); - CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/misc"), - cStats.objectMisc, - "Memory allocated for various small, miscellaneous " - "structures that hang off certain kinds of objects."); + CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects-extra/arguments-data"), + cStats.objectsExtraArgumentsData, + "Memory allocated for data belonging to arguments objects."); + + CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects-extra/regexp-statics"), + cStats.objectsExtraRegExpStatics, + "Memory allocated for data belonging to the RegExpStatics object."); + + CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects-extra/property-iterator-data"), + cStats.objectsExtraPropertyIteratorData, + "Memory allocated for data belonging to property iterator " + "objects."); // Note that we use cDOMPathPrefix here. This is because we measure orphan // DOM nodes in the JS multi-reporter, but we want to report them in a // "dom" sub-tree rather than a "js" sub-tree. CREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"), - cStats.objectPrivate, + cStats.objectsExtraPrivate, "Memory used by orphan DOM nodes that are only reachable " "from JavaScript objects."); From 7eb78c7eb59185e83588419e6bf1f7ad402cff20 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Fri, 2 Nov 2012 01:52:59 +0100 Subject: [PATCH 17/83] Bug 806056 - Make nsContainerFrame destroy abs/fixed pos. child frames unless a derived class already did so. Assert that there are no abs/fixed pos. child frames in nsFrame::DestroyFrom(). r=roc --- layout/forms/nsFieldSetFrame.cpp | 8 -------- layout/forms/nsHTMLButtonControlFrame.cpp | 1 - layout/generic/nsCanvasFrame.cpp | 1 - layout/generic/nsColumnSetFrame.cpp | 8 -------- layout/generic/nsContainerFrame.cpp | 11 +++++++++++ layout/generic/nsContainerFrame.h | 8 ++++++++ layout/generic/nsFlexContainerFrame.cpp | 8 -------- layout/generic/nsFlexContainerFrame.h | 3 --- layout/generic/nsFrame.cpp | 9 +-------- layout/generic/nsFrame.h | 1 - layout/generic/nsGfxScrollFrame.cpp | 2 +- layout/generic/nsInlineFrame.cpp | 7 ------- layout/generic/nsInlineFrame.h | 2 -- layout/generic/nsViewportFrame.cpp | 7 ------- layout/generic/nsViewportFrame.h | 2 -- layout/xul/base/src/nsBoxFrame.cpp | 2 -- 16 files changed, 21 insertions(+), 59 deletions(-) diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index f7ee48086b4d..28b274e7815f 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -53,7 +53,6 @@ public: nsSize aMargin, nsSize aBorder, nsSize aPadding, uint32_t aFlags) MOZ_OVERRIDE; virtual nscoord GetBaseline() const; - virtual void DestroyFrom(nsIFrame* aDestructRoot); NS_IMETHOD Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, @@ -116,13 +115,6 @@ nsFieldSetFrame::nsFieldSetFrame(nsStyleContext* aContext) mLegendSpace = 0; } -void -nsFieldSetFrame::DestroyFrom(nsIFrame* aDestructRoot) -{ - DestroyAbsoluteFrames(aDestructRoot); - nsContainerFrame::DestroyFrom(aDestructRoot); -} - nsIAtom* nsFieldSetFrame::GetType() const { diff --git a/layout/forms/nsHTMLButtonControlFrame.cpp b/layout/forms/nsHTMLButtonControlFrame.cpp index 3b67ad73b773..ae8687470e46 100644 --- a/layout/forms/nsHTMLButtonControlFrame.cpp +++ b/layout/forms/nsHTMLButtonControlFrame.cpp @@ -54,7 +54,6 @@ void nsHTMLButtonControlFrame::DestroyFrom(nsIFrame* aDestructRoot) { nsFormControlFrame::RegUnRegAccessKey(static_cast(this), false); - DestroyAbsoluteFrames(aDestructRoot); nsContainerFrame::DestroyFrom(aDestructRoot); } diff --git a/layout/generic/nsCanvasFrame.cpp b/layout/generic/nsCanvasFrame.cpp index ff290042c0da..da148d09e902 100644 --- a/layout/generic/nsCanvasFrame.cpp +++ b/layout/generic/nsCanvasFrame.cpp @@ -47,7 +47,6 @@ NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) void nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot) { - DestroyAbsoluteFrames(aDestructRoot); nsIScrollableFrame* sf = PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable(); if (sf) { diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index dec2767ac006..9a23356dd4d0 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -43,7 +43,6 @@ public: NS_IMETHOD RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame); - virtual void DestroyFrom(nsIFrame* aDestructRoot); virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext); virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext); @@ -176,13 +175,6 @@ nsColumnSetFrame::nsColumnSetFrame(nsStyleContext* aContext) { } -void -nsColumnSetFrame::DestroyFrom(nsIFrame* aDestructRoot) -{ - DestroyAbsoluteFrames(aDestructRoot); - nsContainerFrame::DestroyFrom(aDestructRoot); -} - nsIAtom* nsColumnSetFrame::GetType() const { diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index d83c6ae0f902..8745400ad2da 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -205,6 +205,15 @@ nsContainerFrame::RemoveFrame(ChildListID aListID, return NS_OK; } +void +nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot) +{ + if (IsAbsoluteContainer()) { + GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot); + MarkAsNotAbsoluteContainingBlock(); + } +} + void nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot) { @@ -213,6 +222,8 @@ nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot) GetView()->SetFrame(nullptr); } + DestroyAbsoluteFrames(aDestructRoot); + // Delete the primary child list mFrames.DestroyFramesFrom(aDestructRoot); diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h index a5812ed389ab..82da836bef0e 100644 --- a/layout/generic/nsContainerFrame.h +++ b/layout/generic/nsContainerFrame.h @@ -371,6 +371,14 @@ protected: nsContainerFrame(nsStyleContext* aContext) : nsSplittableFrame(aContext) {} ~nsContainerFrame(); + /** + * Helper for DestroyFrom. DestroyAbsoluteFrames is called before + * destroying frames on lists that can contain placeholders. + * Derived classes must do that too, if they destroy such frame lists. + * See nsBlockFrame::DestroyFrom for an example. + */ + void DestroyAbsoluteFrames(nsIFrame* aDestructRoot); + /** * Builds a display list for non-block children that behave like * inlines. This puts the background of each child into the diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index 19204b3eb626..9469937df508 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -929,14 +929,6 @@ nsFlexContainerFrame::~nsFlexContainerFrame() { } -/* virtual */ -void -nsFlexContainerFrame::DestroyFrom(nsIFrame* aDestructRoot) -{ - DestroyAbsoluteFrames(aDestructRoot); - nsFlexContainerFrameSuper::DestroyFrom(aDestructRoot); -} - /* virtual */ nsIAtom* nsFlexContainerFrame::GetType() const diff --git a/layout/generic/nsFlexContainerFrame.h b/layout/generic/nsFlexContainerFrame.h index 4cf2f9f2316e..e7153f3218f1 100644 --- a/layout/generic/nsFlexContainerFrame.h +++ b/layout/generic/nsFlexContainerFrame.h @@ -65,9 +65,6 @@ protected: {} virtual ~nsFlexContainerFrame(); - // Protected nsIFrame overrides: - virtual void DestroyFrom(nsIFrame* aDestructRoot); - // Protected flex-container-specific methods / member-vars #ifdef DEBUG void SanityCheckAnonymousFlexItems() const; diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 3b688a0f9cb9..6f4e5fd574fc 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -605,6 +605,7 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot) NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(), "Frames should be removed before destruction."); NS_ASSERTION(aDestructRoot, "Must specify destruct root"); + MOZ_ASSERT(!HasAbsolutelyPositionedChildren()); nsSVGEffects::InvalidateDirectRenderingObservers(this); @@ -4224,14 +4225,6 @@ nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext, FinishAndStoreOverflow(&aDesiredSize); } -void -nsFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot) -{ - if (IsAbsoluteContainer()) { - GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot); - } -} - void nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index 4474ee264c2a..a81d9e04c4e4 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -335,7 +335,6 @@ public: nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); - void DestroyAbsoluteFrames(nsIFrame* aDestructRoot); virtual bool CanContinueTextRun() const; virtual bool UpdateOverflow(); diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 55fbac079620..24ed41533a41 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -91,8 +91,8 @@ nsHTMLScrollFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, void nsHTMLScrollFrame::DestroyFrom(nsIFrame* aDestructRoot) { - mInner.Destroy(); DestroyAbsoluteFrames(aDestructRoot); + mInner.Destroy(); nsContainerFrame::DestroyFrom(aDestructRoot); } diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index 329053bd1ac1..0653fb4549bd 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -901,13 +901,6 @@ nsInlineFrame::GetBaseline() const return mBaseline; } -void -nsInlineFrame::DestroyFrom(nsIFrame* aDestructRoot) -{ - DestroyAbsoluteFrames(aDestructRoot); - nsContainerFrame::DestroyFrom(aDestructRoot); -} - #ifdef ACCESSIBILITY a11y::AccType nsInlineFrame::AccessibleType() diff --git a/layout/generic/nsInlineFrame.h b/layout/generic/nsInlineFrame.h index 87fd2f272bd6..7e3378fd565d 100644 --- a/layout/generic/nsInlineFrame.h +++ b/layout/generic/nsInlineFrame.h @@ -68,8 +68,6 @@ public: virtual bool IsEmpty() MOZ_OVERRIDE; virtual bool IsSelfEmpty() MOZ_OVERRIDE; - virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; - virtual bool PeekOffsetCharacter(bool aForward, int32_t* aOffset, bool aRespectClusters = true) MOZ_OVERRIDE; diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp index c74543127759..ee6e9f42dc74 100644 --- a/layout/generic/nsViewportFrame.cpp +++ b/layout/generic/nsViewportFrame.cpp @@ -46,13 +46,6 @@ ViewportFrame::Init(nsIContent* aContent, return rv; } -void -ViewportFrame::DestroyFrom(nsIFrame* aDestructRoot) -{ - DestroyAbsoluteFrames(aDestructRoot); - nsContainerFrame::DestroyFrom(aDestructRoot); -} - NS_IMETHODIMP ViewportFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) diff --git a/layout/generic/nsViewportFrame.h b/layout/generic/nsViewportFrame.h index 01693b12b61c..d74106e0a962 100644 --- a/layout/generic/nsViewportFrame.h +++ b/layout/generic/nsViewportFrame.h @@ -33,8 +33,6 @@ public: {} virtual ~ViewportFrame() { } // useful for debugging - virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; - NS_IMETHOD Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* asPrevInFlow) MOZ_OVERRIDE; diff --git a/layout/xul/base/src/nsBoxFrame.cpp b/layout/xul/base/src/nsBoxFrame.cpp index b352a79666c0..5e803c7736c4 100644 --- a/layout/xul/base/src/nsBoxFrame.cpp +++ b/layout/xul/base/src/nsBoxFrame.cpp @@ -943,8 +943,6 @@ nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot) // clean up the container box's layout manager and child boxes SetLayoutManager(nullptr); - DestroyAbsoluteFrames(aDestructRoot); - nsContainerFrame::DestroyFrom(aDestructRoot); } From 1a123c0b7d748027fe2d88a926be2c55203904dd Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Fri, 2 Nov 2012 01:52:59 +0100 Subject: [PATCH 18/83] Bug 806056 - Add a frame bit that says if a frame is allowed to have abs/fixed pos. children when the style so indicates (position/transform usually). Copy the bit to next-in-flows. Don't call MarkAsAbsoluteContainingBlock for style changes on an existing frame unless that bit is set (style changes that recreates the frame resets it of course). Assert in MarkAs[Not]AbsoluteContainingBlock() that the bit is set. r=bz --- layout/base/nsCSSFrameConstructor.cpp | 25 +++++++++++++++++-------- layout/generic/nsFrame.cpp | 5 ++++- layout/generic/nsIFrame.h | 6 ++++-- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index c1a835fb537c..d09096e4cf03 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1942,6 +1942,7 @@ nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState, const nsStyleDisplay* display = outerStyleContext->GetStyleDisplay(); // Mark the table frame as an absolute container if needed + newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); if (display->IsPositioned(aParentFrame)) { aState.PushAbsoluteContainingBlock(newFrame, absoluteSaveState); } @@ -2387,6 +2388,7 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle if (mHasRootAbsPosContainingBlock) { // Push the absolute containing block now so we can absolutely position // the root element + mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); state.PushAbsoluteContainingBlock(mDocElementContainingBlock, absoluteSaveState); } @@ -2575,6 +2577,7 @@ nsCSSFrameConstructor::ConstructRootFrame(nsIFrame** aNewFrame) // The viewport is the containing block for 'fixed' elements mFixedContainingBlock = viewportFrame; // Make it an absolute container for fixed-pos elements + mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); mFixedContainingBlock->MarkAsAbsoluteContainingBlock(); *aNewFrame = viewportFrame; @@ -2844,6 +2847,7 @@ nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell* aPresShell, SetInitialSingleChild(aPageFrame, pageContentFrame); mFixedContainingBlock = pageContentFrame; // Make it an absolute container for fixed-pos elements + mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); mFixedContainingBlock->MarkAsAbsoluteContainingBlock(); nsRefPtr canvasPseudoStyle; @@ -3150,6 +3154,7 @@ nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState, nsFrameConstructorSaveState absoluteSaveState; nsFrameItems childItems; + newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); if (newFrame->IsPositioned()) { aState.PushAbsoluteContainingBlock(newFrame, absoluteSaveState); } @@ -3726,11 +3731,12 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) { aState.PushAbsoluteContainingBlock(nullptr, absoluteSaveState); - } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH) && - maybeAbsoluteContainingBlockDisplay->IsPositioned - (maybeAbsoluteContainingBlock)) { - aState.PushAbsoluteContainingBlock(maybeAbsoluteContainingBlock, - absoluteSaveState); + } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) { + nsIFrame* cb = maybeAbsoluteContainingBlock; + cb->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); + if (maybeAbsoluteContainingBlockDisplay->IsPositioned(cb)) { + aState.PushAbsoluteContainingBlock(cb, absoluteSaveState); + } } if (bits & FCDATA_USE_CHILD_ITEMS) { @@ -8160,7 +8166,8 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList) // but we may be taking this path even if a transform has been // removed. It's OK to add the bit even if it's not needed. frame->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED); - if (!frame->IsAbsoluteContainer()) { + if (!frame->IsAbsoluteContainer() && + (frame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) { frame->MarkAsAbsoluteContainingBlock(); } } else { @@ -11122,6 +11129,7 @@ nsCSSFrameConstructor::ConstructBlock(nsFrameConstructorState& aState, // children. So use the block and try to compensate with hacks // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes. nsFrameConstructorSaveState absoluteSaveState; + (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); if (aAbsPosContainer) { // NS_ASSERTION(aRelPos, "should have made area frame for this"); aState.PushAbsoluteContainingBlock(*aNewFrame, absoluteSaveState); @@ -11226,6 +11234,7 @@ nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState, // because the object's destructor is significant // this is part of the fix for bug 42372 + newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); if (positioned) { // Relatively positioned frames becomes a container for child // frames that are positioned @@ -11339,8 +11348,8 @@ nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState, InitAndRestoreFrame(aState, content, parentFrame, nullptr, inlineFrame, false); - inlineFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT); - + inlineFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT | + NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); if (aIsPositioned) { inlineFrame->MarkAsAbsoluteContainingBlock(); } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 6f4e5fd574fc..c2ba1ce54258 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -251,6 +251,7 @@ nsIFrame::GetAbsoluteContainingBlock() const { void nsIFrame::MarkAsAbsoluteContainingBlock() { + MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()), "Already has an abs-pos containing block property?"); NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN), @@ -267,6 +268,7 @@ nsIFrame::MarkAsNotAbsoluteContainingBlock() "Should have an abs-pos containing block property"); NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN), "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit"); + MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)); RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN); Properties().Delete(AbsoluteContainingBlockProperty()); } @@ -512,7 +514,8 @@ nsFrame::Init(nsIContent* aContent, mState |= state & (NS_FRAME_INDEPENDENT_SELECTION | NS_FRAME_IS_SPECIAL | NS_FRAME_MAY_BE_TRANSFORMED | - NS_FRAME_MAY_HAVE_GENERATED_CONTENT); + NS_FRAME_MAY_HAVE_GENERATED_CONTENT | + NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); } if (mParent) { nsFrameState state = mParent->GetStateBits(); diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 2179d5c059ec..69cb8d08a81f 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -164,8 +164,10 @@ typedef uint64_t nsFrameState; // e.g., it is absolutely positioned or floated #define NS_FRAME_OUT_OF_FLOW NS_FRAME_STATE_BIT(8) -// This bit is available for re-use. -//#define NS_FRAME_SELECTED_CONTENT NS_FRAME_STATE_BIT(9) +// Frame can be an abs/fixed pos. container, if its style says so. +// MarkAs[Not]AbsoluteContainingBlock will assert that this bit is set. +// NS_FRAME_HAS_ABSPOS_CHILDREN must not be set when this bit is unset. +#define NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN NS_FRAME_STATE_BIT(9) // If this bit is set, then the frame and _all_ of its descendant frames need // to be reflowed. From dac41839af2fc4f7c1b6e24524489551d64f82d7 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Fri, 2 Nov 2012 01:52:59 +0100 Subject: [PATCH 19/83] Bug 805153 - Check with the focus manager if our *content* is already focused. r=roc --- layout/forms/nsComboboxControlFrame.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index e24fa506699a..e8a96f950866 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -6,8 +6,9 @@ #include "nsReadableUtils.h" #include "nsComboboxControlFrame.h" #include "nsIDOMEventTarget.h" -#include "nsFrameManager.h" +#include "nsFocusManager.h" #include "nsFormControlFrame.h" +#include "nsFrameManager.h" #include "nsGfxButtonControlFrame.h" #include "nsGkAtoms.h" #include "nsCSSAnonBoxes.h" @@ -974,7 +975,8 @@ nsComboboxControlFrame::ShowDropDown(bool aDoDropDown) } if (!mDroppedDown && aDoDropDown) { - if (sFocused == this) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (!fm || fm->GetFocusedContent() == GetContent()) { DropDownPositionState state = AbsolutelyPositionDropDown(); if (state == eDropDownPositionFinal) { ShowList(aDoDropDown); // might destroy us From d21a5d80044d6d41c3480fd76aff34196ef9a603 Mon Sep 17 00:00:00 2001 From: Andrew Quartey Date: Thu, 1 Nov 2012 20:57:25 -0400 Subject: [PATCH 20/83] Bug 792581 - part 23: Replace LL_INIT and LL_UDIVMOD macros. r=ehsan --- dom/base/nsJSEnvironment.cpp | 2 +- gfx/thebes/gfxUserFontSet.cpp | 2 +- rdf/base/src/nsRDFService.cpp | 8 +-- toolkit/components/places/nsNavHistory.cpp | 3 +- tools/trace-malloc/spacetrace.c | 80 ++++++++++------------ tools/trace-malloc/stoptions.h | 8 +-- widget/xpwidgets/nsTransferable.cpp | 2 +- xpcom/glue/nsTextFormatter.cpp | 7 +- xpcom/tests/TestStrings.cpp | 2 +- 9 files changed, 51 insertions(+), 63 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 8c844cb8a1e1..829633f4c2a6 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3666,7 +3666,7 @@ MaxScriptRunTimePrefChangedCallback(const char *aPrefName, void *aClosure) PRTime t; if (time <= 0) { // Let scripts run for a really, really long time. - t = LL_INIT(0x40000000, 0); + t = 0x40000000LL << 32; } else { t = time * PR_USEC_PER_SEC; } diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index 88cb47b27fd6..f6586a9eb6f1 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -37,7 +37,7 @@ gfxUserFontSet::GetUserFontsLog() #define LOG(args) PR_LOG(GetUserFontsLog(), PR_LOG_DEBUG, args) #define LOG_ENABLED() PR_LOG_TEST(GetUserFontsLog(), PR_LOG_DEBUG) -static uint64_t sFontSetGeneration = LL_INIT(0, 0); +static uint64_t sFontSetGeneration = 0; // TODO: support for unicode ranges not yet implemented diff --git a/rdf/base/src/nsRDFService.cpp b/rdf/base/src/nsRDFService.cpp index 2a9b91dacbda..8c0d8131d346 100644 --- a/rdf/base/src/nsRDFService.cpp +++ b/rdf/base/src/nsRDFService.cpp @@ -240,12 +240,8 @@ struct DateHashEntry : public PLDHashEntryHdr { { // xor the low 32 bits with the high 32 bits. PRTime t = *static_cast(key); - int64_t h64, l64; - h64 = t >> 32; - l64 = LL_INIT(0, 0xffffffff); - l64 &= t; - int32_t h32 = int32_t(h64); - int32_t l32 = int32_t(l64); + int32_t h32 = int32_t(t >> 32); + int32_t l32 = int32_t(0xffffffff & t); return PLDHashNumber(l32 ^ h32); } diff --git a/toolkit/components/places/nsNavHistory.cpp b/toolkit/components/places/nsNavHistory.cpp index 82fd274d3f2a..1e975ec82f77 100644 --- a/toolkit/components/places/nsNavHistory.cpp +++ b/toolkit/components/places/nsNavHistory.cpp @@ -100,8 +100,7 @@ using namespace mozilla::places; // for repeating stuff. These are milliseconds between "now" cache refreshes. #define RENEW_CACHED_NOW_TIMEOUT ((int32_t)3 * PR_MSEC_PER_SEC) -// USECS_PER_DAY == PR_USEC_PER_SEC * 60 * 60 * 24; -static const int64_t USECS_PER_DAY = LL_INIT(20, 500654080); +static const int64_t USECS_PER_DAY = PR_USEC_PER_SEC * 60 * 60 * 24; // character-set annotation #define CHARSET_ANNO NS_LITERAL_CSTRING("URIProperties/characterSet") diff --git a/tools/trace-malloc/spacetrace.c b/tools/trace-malloc/spacetrace.c index d28b0502079f..3647f4a3555d 100644 --- a/tools/trace-malloc/spacetrace.c +++ b/tools/trace-malloc/spacetrace.c @@ -650,14 +650,10 @@ recalculateAllocationCost(STOptions * inOptions, STContext * inContext, uint32_t timeval = aAllocation->mMaxTimeval - aAllocation->mMinTimeval; uint32_t size = byteSize(inOptions, aAllocation); - uint64_t weight64 = LL_INIT(0, 0); uint32_t heapCost = aAllocation->mHeapRuntimeCost; - uint64_t timeval64 = LL_INIT(0, 0); - uint64_t size64 = LL_INIT(0, 0); - - timeval64 = timeval; - size64 = size; - weight64 = timeval64 * size64; + uint64_t timeval64 = timeval; + uint64_t size64 = size; + uint64_t weight64 = timeval64 * size64; /* ** First, update this run. @@ -888,9 +884,9 @@ harvestRun(const STRun * aInRun, STRun * aOutRun, if (NULL != current) { uint32_t lifetime = 0; uint32_t bytesize = 0; - uint64_t weight64 = LL_INIT(0, 0); - uint64_t bytesize64 = LL_INIT(0, 0); - uint64_t lifetime64 = LL_INIT(0, 0); + uint64_t weight64 = 0; + uint64_t bytesize64 = 0; + uint64_t lifetime64 = 0; int appendRes = 0; int looper = 0; PRBool matched = PR_FALSE; @@ -1077,12 +1073,12 @@ compareAllocations(const void *aAlloc1, const void *aAlloc2, void *aContext) */ case ST_WEIGHT: { - uint64_t weight164 = LL_INIT(0, 0); - uint64_t weight264 = LL_INIT(0, 0); - uint64_t bytesize164 = LL_INIT(0, 0); - uint64_t bytesize264 = LL_INIT(0, 0); - uint64_t timeval164 = LL_INIT(0, 0); - uint64_t timeval264 = LL_INIT(0, 0); + uint64_t weight164 = 0; + uint64_t weight264 = 0; + uint64_t bytesize164 = 0; + uint64_t bytesize264 = 0; + uint64_t timeval164 = 0; + uint64_t timeval264 = 0; bytesize164 = byteSize(inOptions, alloc1); timeval164 = alloc1->mMaxTimeval - alloc1->mMinTimeval; @@ -2601,7 +2597,7 @@ getDataPRUint64(const FormData * aGetData, const char *aCheckFor, int inIndex, uint64_t * aStoreResult64, uint64_t aConversion64) { int retval = 0; - uint64_t value64 = LL_INIT(0, 0); + uint64_t value64 = 0; retval = getDataPRUint32Base(aGetData, aCheckFor, inIndex, &value64, 64); *aStoreResult64 = value64 * aConversion64; @@ -2703,7 +2699,7 @@ displayTopAllocations(STRequest * inRequest, STRun * aRun, current->mMaxTimeval - current->mMinTimeval; uint32_t size = byteSize(&inRequest->mOptions, current); uint32_t heapCost = current->mHeapRuntimeCost; - uint64_t weight64 = LL_INIT(0, 0); + uint64_t weight64 = 0; char buffer[32]; weight64 =(uint64_t)(size * lifespan); @@ -2823,7 +2819,7 @@ displayMemoryLeaks(STRequest * inRequest, STRun * aRun) current->mMaxTimeval - current->mMinTimeval; uint32_t size = byteSize(&inRequest->mOptions, current); uint32_t heapCost = current->mHeapRuntimeCost; - uint64_t weight64 = LL_INIT(0, 0); + uint64_t weight64 = 0; char buffer[32]; weight64 =(uint64_t)(size * lifespan); @@ -3077,7 +3073,7 @@ displayAllocationDetails(STRequest * inRequest, STAllocation * aAllocation) uint32_t timeval = aAllocation->mMaxTimeval - aAllocation->mMinTimeval; uint32_t heapCost = aAllocation->mHeapRuntimeCost; - uint64_t weight64 = LL_INIT(0, 0); + uint64_t weight64 = 0; uint32_t cacheval = 0; int displayRes = 0; @@ -3829,10 +3825,10 @@ graphFootprint(STRequest * inRequest, STRun * aRun) legends); if (maxMemory != minMemory) { - int64_t in64 = LL_INIT(0, 0); - int64_t ydata64 = LL_INIT(0, 0); - int64_t spacey64 = LL_INIT(0, 0); - int64_t mem64 = LL_INIT(0, 0); + int64_t in64 = 0; + int64_t ydata64 = 0; + int64_t spacey64 = 0; + int64_t mem64 = 0; int32_t in32 = 0; /* @@ -4045,10 +4041,10 @@ graphTimeval(STRequest * inRequest, STRun * aRun) legends); if (maxMemory != minMemory) { - int64_t in64 = LL_INIT(0, 0); - int64_t ydata64 = LL_INIT(0, 0); - int64_t spacey64 = LL_INIT(0, 0); - int64_t mem64 = LL_INIT(0, 0); + int64_t in64 = 0; + int64_t ydata64 = 0; + int64_t spacey64 = 0; + int64_t mem64 = 0; int32_t in32 = 0; /* @@ -4263,10 +4259,10 @@ graphLifespan(STRequest * inRequest, STRun * aRun) legends); if (maxMemory != minMemory) { - int64_t in64 = LL_INIT(0, 0); - int64_t ydata64 = LL_INIT(0, 0); - int64_t spacey64 = LL_INIT(0, 0); - int64_t mem64 = LL_INIT(0, 0); + int64_t in64 = 0; + int64_t ydata64 = 0; + int64_t spacey64 = 0; + int64_t mem64 = 0; int32_t in32 = 0; /* @@ -4392,9 +4388,9 @@ graphWeight(STRequest * inRequest, STRun * aRun) for (loop = 0; loop < aRun->mAllocationCount; loop++) { if (prevTimeval < aRun->mAllocations[loop]->mMinTimeval && timeval >= aRun->mAllocations[loop]->mMinTimeval) { - uint64_t size64 = LL_INIT(0, 0); - uint64_t lifespan64 = LL_INIT(0, 0); - uint64_t weight64 = LL_INIT(0, 0); + uint64_t size64 = 0; + uint64_t lifespan64 = 0; + uint64_t weight64 = 0; size64 = byteSize(&inRequest->mOptions, aRun->mAllocations[loop]); @@ -4423,8 +4419,8 @@ graphWeight(STRequest * inRequest, STRun * aRun) } if (0 == retval) { - uint64_t minWeight64 = LL_INIT(0xFFFFFFFF, 0xFFFFFFFF); - uint64_t maxWeight64 = LL_INIT(0, 0); + uint64_t minWeight64 = (0xFFFFFFFFLL << 32) + 0xFFFFFFFFLL; + uint64_t maxWeight64 = 0; int transparent = 0; gdImagePtr graph = NULL; @@ -4459,8 +4455,8 @@ graphWeight(STRequest * inRequest, STRun * aRun) char byteSpace[11][32]; int legendColors[1]; const char *legends[1] = { "Memory Weight" }; - uint64_t percent64 = LL_INIT(0, 0); - uint64_t result64 = LL_INIT(0, 0); + uint64_t percent64 = 0; + uint64_t result64 = 0; uint32_t cached = 0; uint64_t hundred64 = 100; @@ -4492,9 +4488,9 @@ graphWeight(STRequest * inRequest, STRun * aRun) legendColors, legends); if (maxWeight64 != minWeight64) { - int64_t in64 = LL_INIT(0, 0); - int64_t spacey64 = LL_INIT(0, 0); - int64_t weight64 = LL_INIT(0, 0); + int64_t in64 = 0; + int64_t spacey64 = 0; + int64_t weight64 = 0; int32_t in32 = 0; /* diff --git a/tools/trace-malloc/stoptions.h b/tools/trace-malloc/stoptions.h index a8fd1ea63e11..f750ecba79b6 100644 --- a/tools/trace-malloc/stoptions.h +++ b/tools/trace-malloc/stoptions.h @@ -222,14 +222,14 @@ ST_ALL_OPTION_UINT32(ListItemMax, ST_ALL_OPTION_UINT64(WeightMin, DataSetGenre, - LL_INIT(0, 0), - LL_INIT(0, 1), + 0, + 1, "Exclude allocations that are below this weight (lifespan * size).\n") ST_ALL_OPTION_UINT64(WeightMax, DataSetGenre, - LL_INIT(0xFFFFFFFF, 0xFFFFFFFF), - LL_INIT(0, 1), + (0xFFFFFFFFLL << 32) + 0xFFFFFFFFLL, + 1, "Exclude allocations that are above this weight (lifespan * size).\n") ST_CMD_OPTION_STRING(FileName, diff --git a/widget/xpwidgets/nsTransferable.cpp b/widget/xpwidgets/nsTransferable.cpp index ccf34f685bc1..32b88b06d5b5 100644 --- a/widget/xpwidgets/nsTransferable.cpp +++ b/widget/xpwidgets/nsTransferable.cpp @@ -176,7 +176,7 @@ DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen) if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) { // get the size of the file int64_t fileSize; - int64_t max32(LL_INIT(0, 0xFFFFFFFF)); + int64_t max32 = 0xFFFFFFFF; cacheFile->GetFileSize(&fileSize); if (fileSize > max32) return NS_ERROR_OUT_OF_MEMORY; diff --git a/xpcom/glue/nsTextFormatter.cpp b/xpcom/glue/nsTextFormatter.cpp index 3789ce352655..49f03a339a49 100644 --- a/xpcom/glue/nsTextFormatter.cpp +++ b/xpcom/glue/nsTextFormatter.cpp @@ -291,12 +291,9 @@ static int cvt_ll(SprintfState *ss, int64_t num, int width, int prec, cvt = &cvtbuf[0] + ELEMENTS_OF(cvtbuf); digits = 0; while (num != 0) { - int64_t quot, rem; - LL_UDIVMOD(", &rem, num, rad); - int32_t digit = int32_t(rem); - *--cvt = hexp[digit & 0xf]; + *--cvt = hexp[int32_t(num % rad) & 0xf]; digits++; - num = quot; + num /= rad; } if (digits == 0) { *--cvt = '0'; diff --git a/xpcom/tests/TestStrings.cpp b/xpcom/tests/TestStrings.cpp index 0a11e07eef03..2427f45e7b48 100644 --- a/xpcom/tests/TestStrings.cpp +++ b/xpcom/tests/TestStrings.cpp @@ -691,7 +691,7 @@ bool test_appendint64() int64_t min = INT64_MIN; static const char min_expected[] = "-9223372036854775808"; static const char min_expected_oct[] = "1000000000000000000000"; - int64_t maxint_plus1 = LL_INIT(1, 0); + int64_t maxint_plus1 = 1LL << 32; static const char maxint_plus1_expected[] = "4294967296"; static const char maxint_plus1_expected_x[] = "100000000"; From d9992f0b5dde2f4341868a29024846c1ca9a4952 Mon Sep 17 00:00:00 2001 From: Anthony Jones Date: Thu, 1 Nov 2012 22:41:03 -0400 Subject: [PATCH 21/83] Bug 802787 - Work around misreported stride. r=cpeterson --- media/omx-plugin/OmxPlugin.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/media/omx-plugin/OmxPlugin.cpp b/media/omx-plugin/OmxPlugin.cpp index 376a4dcfa624..d90e08934994 100644 --- a/media/omx-plugin/OmxPlugin.cpp +++ b/media/omx-plugin/OmxPlugin.cpp @@ -601,24 +601,26 @@ void OmxDecoder::ToVideoFrame_CbYCrY(VideoFrame *aFrame, int64_t aTimeUs, void * } void OmxDecoder::ToVideoFrame_YUV420SemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { - // There is a bug in the OMX.SEC.avcdec where it returns an implausibly high - // value for mVideoSliceHeight. To work around this issue we calculate the - // maximum slice height for the data buffer size and limit to that. - // - // For example if we've got 396 lines of data then we must have at most 264 - // lines of Y data and 132 lines of U data. There isn't enough data for a - // slice height of 272 so we limit the slice height to 264. + int32_t videoStride = mVideoStride; + int32_t videoSliceHeight = mVideoSliceHeight; - int32_t maxVideoSliceHeight = (aSize / mVideoStride) * 2 / 3; - int32_t videoSliceHeight = std::min(mVideoSliceHeight, maxVideoSliceHeight); + // OMX.SEC.avcdec rounds mVideoStride and mVideoSliceHeight up to the nearest + // multiple of 16 but the data itself is too small to fit. What we do is check + // to see if the video size patches the raw width and height. If so we can + // use those figures instead. + + if (aSize == mVideoWidth * mVideoHeight * 3 / 2) { + videoStride = mVideoWidth; + videoSliceHeight = mVideoHeight; + } void *y = aData; - void *uv = static_cast(y) + (mVideoStride * videoSliceHeight); + void *uv = static_cast(y) + (videoStride * videoSliceHeight); aFrame->Set(aTimeUs, aKeyFrame, - aData, aSize, mVideoStride, videoSliceHeight, mVideoRotation, - y, mVideoStride, mVideoWidth, mVideoHeight, 0, 0, - uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 0, 1, - uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 1, 1); + aData, aSize, videoStride, videoSliceHeight, mVideoRotation, + y, videoStride, mVideoWidth, mVideoHeight, 0, 0, + uv, videoStride, mVideoWidth/2, mVideoHeight/2, 0, 1, + uv, videoStride, mVideoWidth/2, mVideoHeight/2, 1, 1); } void OmxDecoder::ToVideoFrame_YVU420SemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { From b51b4ca7f710be759c69973502b421350a89d94d Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 2 Nov 2012 15:04:41 +1300 Subject: [PATCH 22/83] b=807728 Finish() only Xlib old surfaces r=roc --HG-- extra : transplant_source : %DB%1B%92%BA%27%25%05%DE%B2-%E5j%EB%A8%06T%C8%24_%F5 --- dom/plugins/ipc/PluginInstanceParent.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 65a66df06d33..c1f6fdf1ff25 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -599,21 +599,19 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect, } #endif - if (mFrontSurface) { +#ifdef MOZ_X11 + if (mFrontSurface && + mFrontSurface->GetType() == gfxASurface::SurfaceTypeXlib) { // This is the "old front buffer" we're about to hand back to // the plugin. We might still have drawing operations // referencing it. mFrontSurface->Finish(); - -#ifdef MOZ_X11 - if (mFrontSurface->GetType() == gfxASurface::SurfaceTypeXlib) { - // XSync here to ensure the server has finished operations on the - // surface before the plugin starts scribbling on it again, or - // worse, destroys it. - FinishX(DefaultXDisplay()); - } -#endif + // XSync here to ensure the server has finished operations on the + // surface before the plugin starts scribbling on it again, or + // worse, destroys it. + FinishX(DefaultXDisplay()); } +#endif if (mFrontSurface && gfxSharedImageSurface::IsSharedImage(mFrontSurface)) *prevSurface = static_cast(mFrontSurface.get())->GetShmem(); From 038e0fa5f452b28c118af5bda9bdcb4eb4dabe13 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Thu, 1 Nov 2012 20:37:55 -0700 Subject: [PATCH 23/83] Bug 753981 - 'XHR in Web Workers bypasses Offline AppCache'. r=smaug. --- content/base/src/nsXMLHttpRequest.cpp | 2 ++ dom/workers/XMLHttpRequest.cpp | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index e17ac124a696..2ed2adb405c9 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -465,6 +465,8 @@ nsXMLHttpRequest::Init(nsIPrincipal* aPrincipal, nsPIDOMWindow* aOwnerWindow, nsIURI* aBaseURI) { + NS_ASSERTION(!aOwnerWindow || aOwnerWindow->IsOuterWindow(), + "Expecting an outer window here!"); NS_ENSURE_ARG_POINTER(aPrincipal); Construct(aPrincipal, aOwnerWindow ? aOwnerWindow->GetCurrentInnerWindow() : nullptr, diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp index 07fa9bbfab55..9f99c33b86f2 100644 --- a/dom/workers/XMLHttpRequest.cpp +++ b/dom/workers/XMLHttpRequest.cpp @@ -154,12 +154,26 @@ public: NS_ASSERTION(mWorkerPrivate, "Must have a worker here!"); if (!mXHR) { + nsPIDOMWindow* ownerWindow = mWorkerPrivate->GetWindow(); + if (ownerWindow) { + ownerWindow = ownerWindow->GetOuterWindow(); + if (!ownerWindow) { + NS_ERROR("No outer window?!"); + return false; + } + + nsPIDOMWindow* innerWindow = ownerWindow->GetCurrentInnerWindow(); + if (mWorkerPrivate->GetWindow() != innerWindow) { + NS_WARNING("Window has navigated, cannot create XHR here."); + return false; + } + } + mXHR = new nsXMLHttpRequest(); if (NS_FAILED(mXHR->Init(mWorkerPrivate->GetPrincipal(), mWorkerPrivate->GetScriptContext(), - mWorkerPrivate->GetWindow(), - mWorkerPrivate->GetBaseURI()))) { + ownerWindow, mWorkerPrivate->GetBaseURI()))) { mXHR = nullptr; return false; } From 555fb4ae35a549e50b89fead7c1db93c39a0f718 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 1 Nov 2012 21:27:07 -0700 Subject: [PATCH 24/83] Bug 805294 - Don't use the RegExpShared cache to track all live RegExpShareds (r=billm) --- js/src/jscompartment.cpp | 6 +++++- js/src/vm/RegExpObject.cpp | 26 ++++++++++++++++++++------ js/src/vm/RegExpObject.h | 25 ++++++++++++++++++++++--- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b7e173c4b976..4d57f5d9f8fc 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -561,7 +561,11 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes) ionCompartment_->sweep(fop); #endif - /* JIT code can hold references on RegExpShared, so sweep regexps after clearing code. */ + /* + * JIT code increments activeUseCount for any RegExpShared used by jit + * code for the lifetime of the JIT script. Thus, we must perform + * sweeping after clearing jit code. + */ regExps.sweep(rt); } diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 5c78ce881883..059aff088320 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -519,18 +519,19 @@ RegExpShared::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t /* RegExpCompartment */ RegExpCompartment::RegExpCompartment(JSRuntime *rt) - : map_(rt) + : map_(rt), inUse_(rt) {} RegExpCompartment::~RegExpCompartment() { - map_.empty(); + JS_ASSERT(map_.empty()); + JS_ASSERT(inUse_.empty()); } bool RegExpCompartment::init(JSContext *cx) { - if (!map_.init()) { + if (!map_.init() || !inUse_.init()) { js_ReportOutOfMemory(cx); return false; } @@ -538,12 +539,19 @@ RegExpCompartment::init(JSContext *cx) return true; } +/* See the comment on RegExpShared lifetime in RegExpObject.h. */ void RegExpCompartment::sweep(JSRuntime *rt) { - for (Map::Enum e(map_); !e.empty(); e.popFront()) { - /* See the comment on RegExpShared lifetime in RegExpObject.h. */ - RegExpShared *shared = e.front().value; +#ifdef DEBUG + for (Map::Range r = map_.all(); !r.empty(); r.popFront()) + JS_ASSERT(inUse_.has(r.front().value)); +#endif + + map_.clear(); + + for (PendingSet::Enum e(inUse_); !e.empty(); e.popFront()) { + RegExpShared *shared = e.front(); if (shared->activeUseCount == 0 && shared->gcNumberWhenUsed < rt->gcStartNumber) { js_delete(shared); e.removeFront(); @@ -575,6 +583,12 @@ RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFla return false; } + if (!inUse_.put(shared)) { + map_.remove(key); + js_ReportOutOfMemory(cx); + return false; + } + /* * Since 'error' deletes 'shared', only guard 'shared' on success. This is * safe since 'shared' cannot be deleted by GC until after the call to diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index af5b3bd45f0e..f605d5dd4459 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -140,9 +140,9 @@ class RegExpCode /* * A RegExpShared is the compiled representation of a regexp. A RegExpShared is - * pointed to by potentially multiple RegExpObjects. Additionally, C++ code may - * have pointers to RegExpShareds on the stack. The RegExpShareds are tracked in - * a RegExpCompartment hashtable, and most are destroyed on every GC. + * potentially pointed to by multiple RegExpObjects. Additionally, C++ code may + * have pointers to RegExpShareds on the stack. The RegExpShareds are kept in a + * cache so that they can be reused when compiling the same regex string. * * During a GC, the trace hook for RegExpObject clears any pointers to * RegExpShareds so that there will be no dangling pointers when they are @@ -160,6 +160,13 @@ class RegExpCode * * The activeUseCount and gcNumberWhenUsed fields are used to track these * conditions. + * + * There are two tables used to track RegExpShareds. map_ implements the cache + * and is cleared on every GC. inUse_ logically owns all RegExpShareds in the + * compartment and attempts to delete all RegExpShareds that aren't kept alive + * by the above conditions on every GC sweep phase. It is necessary to use two + * separate tables since map_ *must* be fully cleared on each GC since the Key + * points to a JSAtom that can become garbage. */ class RegExpShared { @@ -251,9 +258,21 @@ class RegExpCompartment } }; + /* + * Cache to reuse RegExpShareds with the same source/flags/etc. The cache + * is entirely cleared on each GC. + */ typedef HashMap Map; Map map_; + /* + * The set of all RegExpShareds in the compartment. On every GC, every + * RegExpShared that is not actively being used is deleted and removed from + * the set. + */ + typedef HashSet, RuntimeAllocPolicy> PendingSet; + PendingSet inUse_; + bool get(JSContext *cx, JSAtom *key, JSAtom *source, RegExpFlag flags, Type type, RegExpGuard *g); From 6b1639d99962a609838126c32ac171fbf8d5e87a Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 1 Nov 2012 21:35:25 -0700 Subject: [PATCH 25/83] Update Yarr to WebKit rev 130234 (bug 740015, r=dmandelin). --- js/public/Vector.h | 35 +- js/src/Makefile.in | 1 + js/src/assembler/assembler/ARMAssembler.h | 5 + js/src/assembler/assembler/ARMv7Assembler.h | 4 + .../assembler/AbstractMacroAssembler.h | 2 + .../assembler/assembler/MacroAssemblerARM.h | 10 + .../assembler/MacroAssemblerCodeRef.h | 7 + .../assembler/MacroAssemblerX86Common.h | 10 + js/src/assembler/assembler/X86Assembler.h | 4 + js/src/assembler/wtf/Platform.h | 5 + js/src/vm/RegExpObject.cpp | 23 +- js/src/yarr/CheckedArithmetic.h | 714 ++++++++++++++++++ js/src/yarr/MatchResult.h | 73 ++ js/src/yarr/TypeTraits.h | 261 +++++++ js/src/yarr/Yarr.h | 16 +- js/src/yarr/YarrCanonicalizeUCS2.cpp | 463 ++++++++++++ js/src/yarr/YarrCanonicalizeUCS2.h | 139 ++++ js/src/yarr/YarrCanonicalizeUCS2.js | 220 ++++++ js/src/yarr/YarrInterpreter.cpp | 497 +++++++----- js/src/yarr/YarrInterpreter.h | 60 +- js/src/yarr/YarrJIT.cpp | 613 ++++++++++----- js/src/yarr/YarrJIT.h | 139 +++- js/src/yarr/YarrParser.h | 39 +- js/src/yarr/YarrPattern.cpp | 310 +++++--- js/src/yarr/YarrPattern.h | 89 ++- js/src/yarr/YarrSyntaxChecker.cpp | 10 +- js/src/yarr/YarrSyntaxChecker.h | 10 +- js/src/yarr/wtfbridge.h | 39 +- 28 files changed, 3192 insertions(+), 606 deletions(-) create mode 100644 js/src/yarr/CheckedArithmetic.h create mode 100644 js/src/yarr/MatchResult.h create mode 100644 js/src/yarr/TypeTraits.h create mode 100644 js/src/yarr/YarrCanonicalizeUCS2.cpp create mode 100644 js/src/yarr/YarrCanonicalizeUCS2.h create mode 100644 js/src/yarr/YarrCanonicalizeUCS2.js diff --git a/js/public/Vector.h b/js/public/Vector.h index 05480632beff..c8ac4406b42b 100644 --- a/js/public/Vector.h +++ b/js/public/Vector.h @@ -255,7 +255,11 @@ class Vector : private AllocPolicy /* private accessors */ bool usingInlineStorage() const { - return mBegin == (T *)storage.addr(); + return mBegin == inlineStorage(); + } + + T *inlineStorage() const { + return (T *)storage.addr(); } T *beginNoCheck() const { @@ -479,6 +483,8 @@ class Vector : private AllocPolicy * object (which must be heap-allocated) itself. */ size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const; + + void swap(Vector &other); }; /* This does the re-entrancy check plus several other sanity checks. */ @@ -995,6 +1001,33 @@ Vector::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); } +template +inline void +Vector::swap(Vector &other) +{ + // TODO Implement N != 0 + JS_STATIC_ASSERT(N == 0); + + // This only works when inline storage is always empty. + if (!usingInlineStorage() && other.usingInlineStorage()) { + other.mBegin = mBegin; + mBegin = inlineStorage(); + } else if (usingInlineStorage() && !other.usingInlineStorage()) { + mBegin = other.mBegin; + other.mBegin = other.inlineStorage(); + } else if (!usingInlineStorage() && !other.usingInlineStorage()) { + Swap(mBegin, other.mBegin); + } else { + // This case is a no-op, since we'd set both to use their inline storage. + } + + Swap(mLength, other.mLength); + Swap(mCapacity, other.mCapacity); +#ifdef DEBUG + Swap(mReserved, other.mReserved); +#endif +} + } /* namespace js */ #ifdef _MSC_VER diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 31dc5fb4416f..4efe1ca3f6ed 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -384,6 +384,7 @@ CPPSRCS += ExecutableAllocator.cpp \ YarrInterpreter.cpp \ YarrPattern.cpp \ YarrSyntaxChecker.cpp \ + YarrCanonicalizeUCS2.cpp \ $(NONE) ifdef ENABLE_METHODJIT_SPEW diff --git a/js/src/assembler/assembler/ARMAssembler.h b/js/src/assembler/assembler/ARMAssembler.h index a6bea31494ec..f807eebf37e0 100644 --- a/js/src/assembler/assembler/ARMAssembler.h +++ b/js/src/assembler/assembler/ARMAssembler.h @@ -291,6 +291,11 @@ namespace JSC { { } int offset() {return m_offset;} + + bool isSet() const { + return m_offset != -1; + } + private: JmpSrc(int offset) : m_offset(offset) diff --git a/js/src/assembler/assembler/ARMv7Assembler.h b/js/src/assembler/assembler/ARMv7Assembler.h index 54c866f21399..1f96585ee16c 100644 --- a/js/src/assembler/assembler/ARMv7Assembler.h +++ b/js/src/assembler/assembler/ARMv7Assembler.h @@ -457,6 +457,10 @@ public: { } + bool isSet() const { + return m_offset != -1; + } + private: JmpSrc(int offset) : m_offset(offset) diff --git a/js/src/assembler/assembler/AbstractMacroAssembler.h b/js/src/assembler/assembler/AbstractMacroAssembler.h index 42276896a11e..286fad954742 100644 --- a/js/src/assembler/assembler/AbstractMacroAssembler.h +++ b/js/src/assembler/assembler/AbstractMacroAssembler.h @@ -418,6 +418,8 @@ public: masm->m_assembler.linkJump(m_jmp, label.m_label); } + bool isSet() const { return m_jmp.isSet(); } + private: JmpSrc m_jmp; }; diff --git a/js/src/assembler/assembler/MacroAssemblerARM.h b/js/src/assembler/assembler/MacroAssemblerARM.h index 4fcc3c6197a5..19049c08c52b 100644 --- a/js/src/assembler/assembler/MacroAssemblerARM.h +++ b/js/src/assembler/assembler/MacroAssemblerARM.h @@ -250,6 +250,11 @@ public: m_assembler.eors_r(dest, dest, ARMRegisters::S1); } + void load8(BaseIndex address, RegisterID dest) + { + load8ZeroExtend(address, dest); + } + void load8SignExtend(ImplicitAddress address, RegisterID dest) { m_assembler.dataTransferN(true, true, 8, dest, address.base, address.offset); @@ -277,6 +282,11 @@ public: { load8ZeroExtend(address, dest); } + + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } void load16SignExtend(ImplicitAddress address, RegisterID dest) { diff --git a/js/src/assembler/assembler/MacroAssemblerCodeRef.h b/js/src/assembler/assembler/MacroAssemblerCodeRef.h index 7103c3847f4d..df279d585082 100644 --- a/js/src/assembler/assembler/MacroAssemblerCodeRef.h +++ b/js/src/assembler/assembler/MacroAssemblerCodeRef.h @@ -206,6 +206,13 @@ public: m_executablePool = NULL; } + MacroAssemblerCodePtr code() { + return m_code; + } + size_t size() { + return m_size; + } + MacroAssemblerCodePtr m_code; ExecutablePool* m_executablePool; size_t m_size; diff --git a/js/src/assembler/assembler/MacroAssemblerX86Common.h b/js/src/assembler/assembler/MacroAssemblerX86Common.h index d3e84cc57823..d1c2b08c2523 100644 --- a/js/src/assembler/assembler/MacroAssemblerX86Common.h +++ b/js/src/assembler/assembler/MacroAssemblerX86Common.h @@ -414,6 +414,11 @@ public: m_assembler.movw_rm(src, address.offset, address.base, address.index, address.scale); } + void load8(BaseIndex address, RegisterID dest) + { + load8ZeroExtend(address, dest); + } + void load8ZeroExtend(BaseIndex address, RegisterID dest) { m_assembler.movzbl_mr(address.offset, address.base, address.index, address.scale, dest); @@ -454,6 +459,11 @@ public: m_assembler.movzwl_mr(address.offset, address.base, dest); } + void load16Unaligned(BaseIndex address, RegisterID dest) + { + load16(address, dest); + } + DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address) { m_assembler.movl_rm_disp32(src, address.offset, address.base); diff --git a/js/src/assembler/assembler/X86Assembler.h b/js/src/assembler/assembler/X86Assembler.h index 039686616691..3914993a2773 100644 --- a/js/src/assembler/assembler/X86Assembler.h +++ b/js/src/assembler/assembler/X86Assembler.h @@ -378,6 +378,10 @@ public: return m_offset; } + bool isSet() const { + return m_offset != -1; + } + private: int m_offset; }; diff --git a/js/src/assembler/wtf/Platform.h b/js/src/assembler/wtf/Platform.h index b80d3b4ea426..fd2f974160f2 100644 --- a/js/src/assembler/wtf/Platform.h +++ b/js/src/assembler/wtf/Platform.h @@ -1177,6 +1177,11 @@ #define WARN_UNUSED_RETURN #endif +/* COMPILER(CLANG) - Clang */ +#if defined(__clang__) +#define WTF_COMPILER_CLANG 1 +#endif + #if !ENABLE_NETSCAPE_PLUGIN_API || (ENABLE_NETSCAPE_PLUGIN_API && ((WTF_OS_UNIX && (WTF_PLATFORM_QT || WTF_PLATFORM_WX)) || WTF_PLATFORM_GTK)) #define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1 #endif diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 059aff088320..48bbe06071c5 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -195,7 +195,10 @@ RegExpCode::compile(JSContext *cx, JSLinearString &pattern, unsigned *parenCount return false; JSGlobalData globalData(execAlloc); - jitCompile(yarrPattern, &globalData, codeBlock); + jitCompile(yarrPattern, + JSC::Yarr::Char16, + &globalData, + codeBlock); if (!codeBlock.isFallBack()) return true; } @@ -218,21 +221,23 @@ RegExpRunStatus RegExpCode::execute(JSContext *cx, StableCharPtr chars, size_t length, size_t start, int *output, size_t outputCount) { - int result; + unsigned result; #if ENABLE_YARR_JIT (void) cx; /* Unused. */ - if (codeBlock.isFallBack()) - result = JSC::Yarr::interpret(byteCode, chars.get(), start, length, output); - else - result = JSC::Yarr::execute(codeBlock, chars.get(), start, length, output); + if (codeBlock.isFallBack()) { + result = JSC::Yarr::interpret(byteCode, chars.get(), length, start, + reinterpret_cast(output)); + } else { + result = codeBlock.execute(chars.get(), start, length, output).start; + } #else - result = JSC::Yarr::interpret(byteCode, chars.get(), start, length, output); + result = JSC::Yarr::interpret(byteCode, chars.get(), length, start, + reinterpret_cast(output)); #endif - if (result == -1) + if (result == JSC::Yarr::offsetNoMatch) return RegExpRunStatus_Success_NotFound; - JS_ASSERT(result >= 0); return RegExpRunStatus_Success; } diff --git a/js/src/yarr/CheckedArithmetic.h b/js/src/yarr/CheckedArithmetic.h new file mode 100644 index 000000000000..8e4632c871f9 --- /dev/null +++ b/js/src/yarr/CheckedArithmetic.h @@ -0,0 +1,714 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CheckedArithmetic_h +#define CheckedArithmetic_h + +#include "assembler/wtf/Assertions.h" +#include "TypeTraits.h" + +#include +#include + +#ifdef _MSC_VER +# undef min +# undef max +#endif + +/* Checked + * + * This class provides a mechanism to perform overflow-safe integer arithmetic + * without having to manually ensure that you have all the required bounds checks + * directly in your code. + * + * There are two modes of operation: + * - The default is Checked, and crashes at the point + * and overflow has occurred. + * - The alternative is Checked, which uses an additional + * byte of storage to track whether an overflow has occurred, subsequent + * unchecked operations will crash if an overflow has occured + * + * It is possible to provide a custom overflow handler, in which case you need + * to support these functions: + * - void overflowed(); + * This function is called when an operation has produced an overflow. + * - bool hasOverflowed(); + * This function must return true if overflowed() has been called on an + * instance and false if it has not. + * - void clearOverflow(); + * Used to reset overflow tracking when a value is being overwritten with + * a new value. + * + * Checked works for all integer types, with the following caveats: + * - Mixing signedness of operands is only supported for types narrower than + * 64bits. + * - It does have a performance impact, so tight loops may want to be careful + * when using it. + * + */ + +namespace WTF { + +class CrashOnOverflow { +protected: + void overflowed() + { + CRASH(); + } + + void clearOverflow() { } + +public: + bool hasOverflowed() const { return false; } +}; + +class RecordOverflow { +protected: + RecordOverflow() + : m_overflowed(false) + { + } + + void overflowed() + { + m_overflowed = true; + } + + void clearOverflow() + { + m_overflowed = false; + } + +public: + bool hasOverflowed() const { return m_overflowed; } + +private: + unsigned char m_overflowed; +}; + +template class Checked; +template struct RemoveChecked; +template struct RemoveChecked >; + +template ::is_signed, bool sourceSigned = ::std::numeric_limits::is_signed> struct BoundsChecker; +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return value <= ::std::numeric_limits::max(); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Same signedness so implicit type conversion will always increase precision + // to widest type + return ::std::numeric_limits::min() <= value && value <= ::std::numeric_limits::max(); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Target is unsigned so any value less than zero is clearly unsafe + if (value < 0) + return false; + // If our (unsigned) Target is the same or greater width we can + // convert value to type Target without losing precision + if (sizeof(Target) >= sizeof(Source)) + return static_cast(value) <= ::std::numeric_limits::max(); + // The signed Source type has greater precision than the target so + // max(Target) -> Source will widen. + return value <= static_cast(::std::numeric_limits::max()); + } +}; + +template struct BoundsChecker { + static bool inBounds(Source value) + { + // Signed target with an unsigned source + if (sizeof(Target) <= sizeof(Source)) + return value <= static_cast(::std::numeric_limits::max()); + // Target is Wider than Source so we're guaranteed to fit any value in + // unsigned Source + return true; + } +}; + +template ::value> struct BoundsCheckElider; +template struct BoundsCheckElider { + static bool inBounds(Source) { return true; } +}; +template struct BoundsCheckElider : public BoundsChecker { +}; + +template static inline bool isInBounds(Source value) +{ + return BoundsCheckElider::inBounds(value); +} + +template struct RemoveChecked { + typedef T CleanType; + static const CleanType DefaultValue = 0; +}; + +template struct RemoveChecked > { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +template struct RemoveChecked > { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; +}; + +// The ResultBase and SignednessSelector are used to workaround typeof not being +// available in MSVC +template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; +template struct ResultBase { + typedef U ResultType; +}; + +template struct ResultBase { + typedef V ResultType; +}; + +template struct ResultBase { + typedef U ResultType; +}; + +template ::is_signed, bool vIsSigned = ::std::numeric_limits::is_signed> struct SignednessSelector; +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct SignednessSelector { + typedef V ResultType; +}; + +template struct SignednessSelector { + typedef U ResultType; +}; + +template struct ResultBase { + typedef typename SignednessSelector::ResultType ResultType; +}; + +template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { +}; + +template ::ResultType, + bool lhsSigned = ::std::numeric_limits::is_signed, bool rhsSigned = ::std::numeric_limits::is_signed> struct ArithmeticOperations; + +template struct ArithmeticOperations { + // LHS and RHS are signed types + + // Helper function + static inline bool signsMatch(LHS lhs, RHS rhs) + { + return (lhs ^ rhs) >= 0; + } + + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if ((::std::numeric_limits::max() - rhs) < lhs) + return false; + } else { + ResultType temp = lhs - ::std::numeric_limits::min(); + if (rhs < -temp) + return false; + } + } // if the signs do not match this operation can't overflow + result = lhs + rhs; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (!signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs > ::std::numeric_limits::max() + rhs) + return false; + } else { + if (rhs > ::std::numeric_limits::max() + lhs) + return false; + } + } // if the signs match this operation can't overflow + result = lhs - rhs; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs && (::std::numeric_limits::max() / lhs) < rhs) + return false; + } else { + if (lhs == ::std::numeric_limits::min() || rhs == ::std::numeric_limits::min()) + return false; + if ((::std::numeric_limits::max() / -lhs) < -rhs) + return false; + } + } else { + if (lhs < 0) { + if (rhs && lhs < (::std::numeric_limits::min() / rhs)) + return false; + } else { + if (lhs && rhs < (::std::numeric_limits::min() / lhs)) + return false; + } + } + result = lhs * rhs; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template struct ArithmeticOperations { + // LHS and RHS are unsigned types so bounds checks are nice and easy + static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs + rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs - rhs; + if (temp > lhs) + return false; + result = temp; + return true; + } + + static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN + { + ResultType temp = lhs * rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } + +}; + +template struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs + rhs; + if (temp < ::std::numeric_limits::min()) + return false; + if (temp > ::std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs - rhs; + if (temp < ::std::numeric_limits::min()) + return false; + if (temp > ::std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + int64_t temp = lhs * rhs; + if (temp < ::std::numeric_limits::min()) + return false; + if (temp > ::std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool equals(int lhs, unsigned rhs) + { + return static_cast(lhs) == static_cast(rhs); + } +}; + +template struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::add(rhs, lhs, result); + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::sub(lhs, rhs, result); + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) + { + return ArithmeticOperations::multiply(rhs, lhs, result); + } + + static inline bool equals(unsigned lhs, int rhs) + { + return ArithmeticOperations::equals(rhs, lhs); + } +}; + +template static inline bool safeAdd(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::add(lhs, rhs, result); +} + +template static inline bool safeSub(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::sub(lhs, rhs, result); +} + +template static inline bool safeMultiply(U lhs, V rhs, R& result) +{ + return ArithmeticOperations::multiply(lhs, rhs, result); +} + +template static inline bool safeEquals(U lhs, V rhs) +{ + return ArithmeticOperations::equals(lhs, rhs); +} + +enum ResultOverflowedTag { ResultOverflowed }; + +// FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 +static inline bool workAroundClangBug() { return true; } + +template class Checked : public OverflowHandler { +public: + template friend class Checked; + Checked() + : m_value(0) + { + } + + Checked(ResultOverflowedTag) + : m_value(0) + { + // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 + if (workAroundClangBug()) + this->overflowed(); + } + + template Checked(U value) + { + if (!isInBounds(value)) + this->overflowed(); + m_value = static_cast(value); + } + + template Checked(const Checked& rhs) + : m_value(rhs.m_value) + { + if (rhs.hasOverflowed()) + this->overflowed(); + } + + template Checked(const Checked& rhs) + : OverflowHandler(rhs) + { + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + template Checked(const Checked& rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + const Checked& operator=(Checked rhs) + { + this->clearOverflow(); + if (rhs.hasOverflowed()) + this->overflowed(); + m_value = static_cast(rhs.m_value); + return *this; + } + + template const Checked& operator=(U value) + { + return *this = Checked(value); + } + + template const Checked& operator=(const Checked& rhs) + { + return *this = Checked(rhs); + } + + // prefix + const Checked& operator++() + { + if (m_value == ::std::numeric_limits::max()) + this->overflowed(); + m_value++; + return *this; + } + + const Checked& operator--() + { + if (m_value == ::std::numeric_limits::min()) + this->overflowed(); + m_value--; + return *this; + } + + // postfix operators + const Checked operator++(int) + { + if (m_value == ::std::numeric_limits::max()) + this->overflowed(); + return Checked(m_value++); + } + + const Checked operator--(int) + { + if (m_value == ::std::numeric_limits::min()) + this->overflowed(); + return Checked(m_value--); + } + + // Boolean operators + bool operator!() const + { + if (this->hasOverflowed()) + CRASH(); + return !m_value; + } + + typedef void* (Checked::*UnspecifiedBoolType); + operator UnspecifiedBoolType*() const + { + if (this->hasOverflowed()) + CRASH(); + return (m_value) ? reinterpret_cast(1) : 0; + } + + // Value accessors. unsafeGet() will crash if there's been an overflow. + T unsafeGet() const + { + if (this->hasOverflowed()) + CRASH(); + return m_value; + } + + bool safeGet(T& value) const WARN_UNUSED_RETURN + { + value = m_value; + return this->hasOverflowed(); + } + + // Mutating assignment + template const Checked operator+=(U rhs) + { + if (!safeAdd(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template const Checked operator-=(U rhs) + { + if (!safeSub(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template const Checked operator*=(U rhs) + { + if (!safeMultiply(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + const Checked operator*=(double rhs) + { + double result = rhs * m_value; + // Handle +/- infinity and NaN + if (!(::std::numeric_limits::min() <= result && ::std::numeric_limits::max() >= result)) + this->overflowed(); + m_value = (T)result; + return *this; + } + + const Checked operator*=(float rhs) + { + return *this *= (double)rhs; + } + + template const Checked operator+=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this += rhs.m_value; + } + + template const Checked operator-=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this -= rhs.m_value; + } + + template const Checked operator*=(Checked rhs) + { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this *= rhs.m_value; + } + + // Equality comparisons + template bool operator==(Checked rhs) + { + return unsafeGet() == rhs.unsafeGet(); + } + + template bool operator==(U rhs) + { + if (this->hasOverflowed()) + this->overflowed(); + return safeEquals(m_value, rhs); + } + + template const Checked operator==(Checked rhs) + { + return unsafeGet() == Checked(rhs.unsafeGet()); + } + + template bool operator!=(U rhs) + { + return !(*this == rhs); + } + +private: + // Disallow implicit conversion of floating point to integer types + Checked(float); + Checked(double); + void operator=(float); + void operator=(double); + void operator+=(float); + void operator+=(double); + void operator-=(float); + void operator-=(double); + T m_value; +}; + +template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeAdd(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeSub(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) +{ + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); + typename Result::ResultType result = 0; + overflowed |= !safeMultiply(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; +} + +template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) +{ + return lhs + Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) +{ + return lhs - Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) +{ + return lhs * Checked(rhs); +} + +template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) +{ + return Checked(lhs) + rhs; +} + +template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) +{ + return Checked(lhs) - rhs; +} + +template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) +{ + return Checked(lhs) * rhs; +} + +} + +using WTF::Checked; +using WTF::RecordOverflow; + +#endif diff --git a/js/src/yarr/MatchResult.h b/js/src/yarr/MatchResult.h new file mode 100644 index 000000000000..632332cac6c4 --- /dev/null +++ b/js/src/yarr/MatchResult.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MatchResult_h +#define MatchResult_h + +#include "wtfbridge.h" + +typedef uint64_t EncodedMatchResult; + +struct MatchResult { + MatchResult(size_t start, size_t end) + : start(start) + , end(end) + { + } + + explicit MatchResult(EncodedMatchResult encoded) + { + union u { + uint64_t encoded; + struct s { + size_t start; + size_t end; + } split; + } value; + value.encoded = encoded; + start = value.split.start; + end = value.split.end; + } + + static MatchResult failed() + { + return MatchResult(WTF::notFound, 0); + } + + operator bool() + { + return start != WTF::notFound; + } + + bool empty() + { + return start == end; + } + + size_t start; + size_t end; +}; + +#endif diff --git a/js/src/yarr/TypeTraits.h b/js/src/yarr/TypeTraits.h new file mode 100644 index 000000000000..f5ed5ad361f4 --- /dev/null +++ b/js/src/yarr/TypeTraits.h @@ -0,0 +1,261 @@ + /* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef TypeTraits_h +#define TypeTraits_h + +#include "assembler/wtf/Platform.h" + +#if (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) +#include +#if defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__) +#include +#endif +#endif + +namespace WTF { + + // The following are provided in this file: + // + // Conditional::Type + // + // IsInteger::value + // IsPod::value, see the definition for a note about its limitations + // IsConvertibleToInteger::value + // + // IsArray::value + // + // IsSameType::value + // + // RemovePointer::Type + // RemoveReference::Type + // RemoveConst::Type + // RemoveVolatile::Type + // RemoveConstVolatile::Type + // RemoveExtent::Type + // + // DecayArray::Type + // + // COMPILE_ASSERT's in TypeTraits.cpp illustrate their usage and what they do. + + template struct Conditional { typedef If Type; }; + template struct Conditional { typedef Then Type; }; + + template struct IsInteger { static const bool value = false; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; + template<> struct IsInteger { static const bool value = true; }; +#if WTF_COMPILER_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + template<> struct IsInteger { static const bool value = true; }; +#endif + + template struct IsFloatingPoint { static const bool value = false; }; + template<> struct IsFloatingPoint { static const bool value = true; }; + template<> struct IsFloatingPoint { static const bool value = true; }; + template<> struct IsFloatingPoint { static const bool value = true; }; + + template struct IsArithmetic { static const bool value = IsInteger::value || IsFloatingPoint::value; }; + + // IsPod is misnamed as it doesn't cover all plain old data (pod) types. + // Specifically, it doesn't allow for enums or for structs. + template struct IsPod { static const bool value = IsArithmetic::value; }; + template struct IsPod { static const bool value = true; }; + + template class IsConvertibleToInteger { + // Avoid "possible loss of data" warning when using Microsoft's C++ compiler + // by not converting int's to doubles. + template class IsConvertibleToDouble; + template class IsConvertibleToDouble { + public: + static const bool value = false; + }; + + template class IsConvertibleToDouble { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + static YesType floatCheck(long double); + static NoType floatCheck(...); + static T& t; + public: + static const bool value = sizeof(floatCheck(t)) == sizeof(YesType); + }; + + public: + static const bool value = IsInteger::value || IsConvertibleToDouble::value, T>::value; + }; + + + template struct IsArray { + static const bool value = false; + }; + + template struct IsArray { + static const bool value = true; + }; + + template struct IsArray { + static const bool value = true; + }; + + + template struct IsSameType { + static const bool value = false; + }; + + template struct IsSameType { + static const bool value = true; + }; + + template class IsSubclass { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); + }; + + template class U> class IsSubclassOfTemplate { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + template static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); + }; + + template class OuterTemplate> struct RemoveTemplate { + typedef T Type; + }; + + template class OuterTemplate> struct RemoveTemplate, OuterTemplate> { + typedef T Type; + }; + + template struct RemoveConst { + typedef T Type; + }; + + template struct RemoveConst { + typedef T Type; + }; + + template struct RemoveVolatile { + typedef T Type; + }; + + template struct RemoveVolatile { + typedef T Type; + }; + + template struct RemoveConstVolatile { + typedef typename RemoveVolatile::Type>::Type Type; + }; + + template struct RemovePointer { + typedef T Type; + }; + + template struct RemovePointer { + typedef T Type; + }; + + template struct RemoveReference { + typedef T Type; + }; + + template struct RemoveReference { + typedef T Type; + }; + + template struct RemoveExtent { + typedef T Type; + }; + + template struct RemoveExtent { + typedef T Type; + }; + + template struct RemoveExtent { + typedef T Type; + }; + + template struct DecayArray { + typedef typename RemoveReference::Type U; + public: + typedef typename Conditional< + IsArray::value, + typename RemoveExtent::Type*, + typename RemoveConstVolatile::Type + >::Type Type; + }; + +#if WTF_COMPILER_CLANG || GCC_VERSION_AT_LEAST(4, 6, 0) || (defined(_MSC_VER) && (_MSC_VER >= 1400) && (_MSC_VER < 1600) && !defined(__INTEL_COMPILER)) + // VC8 (VS2005) and later has __has_trivial_constructor and __has_trivial_destructor, + // but the implementation returns false for built-in types. We add the extra IsPod condition to + // work around this. + template struct HasTrivialConstructor { + static const bool value = __has_trivial_constructor(T) || IsPod >::value; + }; + template struct HasTrivialDestructor { + static const bool value = __has_trivial_destructor(T) || IsPod >::value; + }; +#elif (defined(__GLIBCXX__) && (__GLIBCXX__ >= 20070724) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) + // GCC's libstdc++ 20070724 and later supports C++ TR1 type_traits in the std namespace. + // VC10 (VS2010) and later support C++ TR1 type_traits in the std::tr1 namespace. + template struct HasTrivialConstructor : public std::tr1::has_trivial_constructor { }; + template struct HasTrivialDestructor : public std::tr1::has_trivial_destructor { }; +#else + // For compilers that don't support detection of trivial constructors and destructors in classes, + // we use a template that returns true for any POD type that IsPod can detect (see IsPod caveats above), + // but false for all other types (which includes all classes). This will give false negatives, which can hurt + // performance, but avoids false positives, which would result in incorrect behavior. + template struct HasTrivialConstructor { + static const bool value = IsPod >::value; + }; + template struct HasTrivialDestructor { + static const bool value = IsPod >::value; + }; +#endif + +} // namespace WTF + +#endif // TypeTraits_h diff --git a/js/src/yarr/Yarr.h b/js/src/yarr/Yarr.h index 40ebcca096af..110ae4124bed 100644 --- a/js/src/yarr/Yarr.h +++ b/js/src/yarr/Yarr.h @@ -1,7 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** +/* * Copyright (C) 2009 Apple Inc. All rights reserved. * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged * All rights reserved. @@ -26,14 +23,12 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #ifndef Yarr_h #define Yarr_h #include - #include "YarrInterpreter.h" #include "YarrPattern.h" @@ -49,6 +44,7 @@ namespace JSC { namespace Yarr { #define YarrStackSpaceForBackTrackInfoParentheses 2 static const unsigned quantifyInfinite = UINT_MAX; +static const unsigned offsetNoMatch = (unsigned)-1; // The below limit restricts the number of "recursive" match calls in order to // avoid spending exponential time on complex regular expressions. @@ -63,8 +59,10 @@ enum JSRegExpResult { JSRegExpErrorInternal = -4 }; -PassOwnPtr byteCompile(YarrPattern&, BumpPointerAllocator*); -int interpret(BytecodePattern*, const UChar* input, unsigned start, unsigned length, int* output); +enum YarrCharSize { + Char8, + Char16 +}; } } // namespace JSC::Yarr diff --git a/js/src/yarr/YarrCanonicalizeUCS2.cpp b/js/src/yarr/YarrCanonicalizeUCS2.cpp new file mode 100644 index 000000000000..17f6ae81da4c --- /dev/null +++ b/js/src/yarr/YarrCanonicalizeUCS2.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js + +#include "YarrCanonicalizeUCS2.h" + +namespace JSC { namespace Yarr { + +#include +#include + +uint16_t ucs2CharacterSet0[] = { 0x01c4u, 0x01c5u, 0x01c6u, 0 }; +uint16_t ucs2CharacterSet1[] = { 0x01c7u, 0x01c8u, 0x01c9u, 0 }; +uint16_t ucs2CharacterSet2[] = { 0x01cau, 0x01cbu, 0x01ccu, 0 }; +uint16_t ucs2CharacterSet3[] = { 0x01f1u, 0x01f2u, 0x01f3u, 0 }; +uint16_t ucs2CharacterSet4[] = { 0x0392u, 0x03b2u, 0x03d0u, 0 }; +uint16_t ucs2CharacterSet5[] = { 0x0395u, 0x03b5u, 0x03f5u, 0 }; +uint16_t ucs2CharacterSet6[] = { 0x0398u, 0x03b8u, 0x03d1u, 0 }; +uint16_t ucs2CharacterSet7[] = { 0x0345u, 0x0399u, 0x03b9u, 0x1fbeu, 0 }; +uint16_t ucs2CharacterSet8[] = { 0x039au, 0x03bau, 0x03f0u, 0 }; +uint16_t ucs2CharacterSet9[] = { 0x00b5u, 0x039cu, 0x03bcu, 0 }; +uint16_t ucs2CharacterSet10[] = { 0x03a0u, 0x03c0u, 0x03d6u, 0 }; +uint16_t ucs2CharacterSet11[] = { 0x03a1u, 0x03c1u, 0x03f1u, 0 }; +uint16_t ucs2CharacterSet12[] = { 0x03a3u, 0x03c2u, 0x03c3u, 0 }; +uint16_t ucs2CharacterSet13[] = { 0x03a6u, 0x03c6u, 0x03d5u, 0 }; +uint16_t ucs2CharacterSet14[] = { 0x1e60u, 0x1e61u, 0x1e9bu, 0 }; + +static const size_t UCS2_CANONICALIZATION_SETS = 15; +uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = { + ucs2CharacterSet0, + ucs2CharacterSet1, + ucs2CharacterSet2, + ucs2CharacterSet3, + ucs2CharacterSet4, + ucs2CharacterSet5, + ucs2CharacterSet6, + ucs2CharacterSet7, + ucs2CharacterSet8, + ucs2CharacterSet9, + ucs2CharacterSet10, + ucs2CharacterSet11, + ucs2CharacterSet12, + ucs2CharacterSet13, + ucs2CharacterSet14, +}; + +const size_t UCS2_CANONICALIZATION_RANGES = 364; +UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = { + { 0x0000u, 0x0040u, 0x0000u, CanonicalizeUnique }, + { 0x0041u, 0x005au, 0x0020u, CanonicalizeRangeLo }, + { 0x005bu, 0x0060u, 0x0000u, CanonicalizeUnique }, + { 0x0061u, 0x007au, 0x0020u, CanonicalizeRangeHi }, + { 0x007bu, 0x00b4u, 0x0000u, CanonicalizeUnique }, + { 0x00b5u, 0x00b5u, 0x0009u, CanonicalizeSet }, + { 0x00b6u, 0x00bfu, 0x0000u, CanonicalizeUnique }, + { 0x00c0u, 0x00d6u, 0x0020u, CanonicalizeRangeLo }, + { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeUnique }, + { 0x00d8u, 0x00deu, 0x0020u, CanonicalizeRangeLo }, + { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeUnique }, + { 0x00e0u, 0x00f6u, 0x0020u, CanonicalizeRangeHi }, + { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeUnique }, + { 0x00f8u, 0x00feu, 0x0020u, CanonicalizeRangeHi }, + { 0x00ffu, 0x00ffu, 0x0079u, CanonicalizeRangeLo }, + { 0x0100u, 0x012fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0130u, 0x0131u, 0x0000u, CanonicalizeUnique }, + { 0x0132u, 0x0137u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0138u, 0x0138u, 0x0000u, CanonicalizeUnique }, + { 0x0139u, 0x0148u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0149u, 0x0149u, 0x0000u, CanonicalizeUnique }, + { 0x014au, 0x0177u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0178u, 0x0178u, 0x0079u, CanonicalizeRangeHi }, + { 0x0179u, 0x017eu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x017fu, 0x017fu, 0x0000u, CanonicalizeUnique }, + { 0x0180u, 0x0180u, 0x00c3u, CanonicalizeRangeLo }, + { 0x0181u, 0x0181u, 0x00d2u, CanonicalizeRangeLo }, + { 0x0182u, 0x0185u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0186u, 0x0186u, 0x00ceu, CanonicalizeRangeLo }, + { 0x0187u, 0x0188u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0189u, 0x018au, 0x00cdu, CanonicalizeRangeLo }, + { 0x018bu, 0x018cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x018du, 0x018du, 0x0000u, CanonicalizeUnique }, + { 0x018eu, 0x018eu, 0x004fu, CanonicalizeRangeLo }, + { 0x018fu, 0x018fu, 0x00cau, CanonicalizeRangeLo }, + { 0x0190u, 0x0190u, 0x00cbu, CanonicalizeRangeLo }, + { 0x0191u, 0x0192u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0193u, 0x0193u, 0x00cdu, CanonicalizeRangeLo }, + { 0x0194u, 0x0194u, 0x00cfu, CanonicalizeRangeLo }, + { 0x0195u, 0x0195u, 0x0061u, CanonicalizeRangeLo }, + { 0x0196u, 0x0196u, 0x00d3u, CanonicalizeRangeLo }, + { 0x0197u, 0x0197u, 0x00d1u, CanonicalizeRangeLo }, + { 0x0198u, 0x0199u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x019au, 0x019au, 0x00a3u, CanonicalizeRangeLo }, + { 0x019bu, 0x019bu, 0x0000u, CanonicalizeUnique }, + { 0x019cu, 0x019cu, 0x00d3u, CanonicalizeRangeLo }, + { 0x019du, 0x019du, 0x00d5u, CanonicalizeRangeLo }, + { 0x019eu, 0x019eu, 0x0082u, CanonicalizeRangeLo }, + { 0x019fu, 0x019fu, 0x00d6u, CanonicalizeRangeLo }, + { 0x01a0u, 0x01a5u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01a6u, 0x01a6u, 0x00dau, CanonicalizeRangeLo }, + { 0x01a7u, 0x01a8u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01a9u, 0x01a9u, 0x00dau, CanonicalizeRangeLo }, + { 0x01aau, 0x01abu, 0x0000u, CanonicalizeUnique }, + { 0x01acu, 0x01adu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01aeu, 0x01aeu, 0x00dau, CanonicalizeRangeLo }, + { 0x01afu, 0x01b0u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01b1u, 0x01b2u, 0x00d9u, CanonicalizeRangeLo }, + { 0x01b3u, 0x01b6u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01b7u, 0x01b7u, 0x00dbu, CanonicalizeRangeLo }, + { 0x01b8u, 0x01b9u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01bau, 0x01bbu, 0x0000u, CanonicalizeUnique }, + { 0x01bcu, 0x01bdu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01beu, 0x01beu, 0x0000u, CanonicalizeUnique }, + { 0x01bfu, 0x01bfu, 0x0038u, CanonicalizeRangeLo }, + { 0x01c0u, 0x01c3u, 0x0000u, CanonicalizeUnique }, + { 0x01c4u, 0x01c6u, 0x0000u, CanonicalizeSet }, + { 0x01c7u, 0x01c9u, 0x0001u, CanonicalizeSet }, + { 0x01cau, 0x01ccu, 0x0002u, CanonicalizeSet }, + { 0x01cdu, 0x01dcu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x01ddu, 0x01ddu, 0x004fu, CanonicalizeRangeHi }, + { 0x01deu, 0x01efu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01f0u, 0x01f0u, 0x0000u, CanonicalizeUnique }, + { 0x01f1u, 0x01f3u, 0x0003u, CanonicalizeSet }, + { 0x01f4u, 0x01f5u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x01f6u, 0x01f6u, 0x0061u, CanonicalizeRangeHi }, + { 0x01f7u, 0x01f7u, 0x0038u, CanonicalizeRangeHi }, + { 0x01f8u, 0x021fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0220u, 0x0220u, 0x0082u, CanonicalizeRangeHi }, + { 0x0221u, 0x0221u, 0x0000u, CanonicalizeUnique }, + { 0x0222u, 0x0233u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0234u, 0x0239u, 0x0000u, CanonicalizeUnique }, + { 0x023au, 0x023au, 0x2a2bu, CanonicalizeRangeLo }, + { 0x023bu, 0x023cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x023du, 0x023du, 0x00a3u, CanonicalizeRangeHi }, + { 0x023eu, 0x023eu, 0x2a28u, CanonicalizeRangeLo }, + { 0x023fu, 0x0240u, 0x2a3fu, CanonicalizeRangeLo }, + { 0x0241u, 0x0242u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x0243u, 0x0243u, 0x00c3u, CanonicalizeRangeHi }, + { 0x0244u, 0x0244u, 0x0045u, CanonicalizeRangeLo }, + { 0x0245u, 0x0245u, 0x0047u, CanonicalizeRangeLo }, + { 0x0246u, 0x024fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0250u, 0x0250u, 0x2a1fu, CanonicalizeRangeLo }, + { 0x0251u, 0x0251u, 0x2a1cu, CanonicalizeRangeLo }, + { 0x0252u, 0x0252u, 0x2a1eu, CanonicalizeRangeLo }, + { 0x0253u, 0x0253u, 0x00d2u, CanonicalizeRangeHi }, + { 0x0254u, 0x0254u, 0x00ceu, CanonicalizeRangeHi }, + { 0x0255u, 0x0255u, 0x0000u, CanonicalizeUnique }, + { 0x0256u, 0x0257u, 0x00cdu, CanonicalizeRangeHi }, + { 0x0258u, 0x0258u, 0x0000u, CanonicalizeUnique }, + { 0x0259u, 0x0259u, 0x00cau, CanonicalizeRangeHi }, + { 0x025au, 0x025au, 0x0000u, CanonicalizeUnique }, + { 0x025bu, 0x025bu, 0x00cbu, CanonicalizeRangeHi }, + { 0x025cu, 0x025fu, 0x0000u, CanonicalizeUnique }, + { 0x0260u, 0x0260u, 0x00cdu, CanonicalizeRangeHi }, + { 0x0261u, 0x0262u, 0x0000u, CanonicalizeUnique }, + { 0x0263u, 0x0263u, 0x00cfu, CanonicalizeRangeHi }, + { 0x0264u, 0x0264u, 0x0000u, CanonicalizeUnique }, + { 0x0265u, 0x0265u, 0xa528u, CanonicalizeRangeLo }, + { 0x0266u, 0x0267u, 0x0000u, CanonicalizeUnique }, + { 0x0268u, 0x0268u, 0x00d1u, CanonicalizeRangeHi }, + { 0x0269u, 0x0269u, 0x00d3u, CanonicalizeRangeHi }, + { 0x026au, 0x026au, 0x0000u, CanonicalizeUnique }, + { 0x026bu, 0x026bu, 0x29f7u, CanonicalizeRangeLo }, + { 0x026cu, 0x026eu, 0x0000u, CanonicalizeUnique }, + { 0x026fu, 0x026fu, 0x00d3u, CanonicalizeRangeHi }, + { 0x0270u, 0x0270u, 0x0000u, CanonicalizeUnique }, + { 0x0271u, 0x0271u, 0x29fdu, CanonicalizeRangeLo }, + { 0x0272u, 0x0272u, 0x00d5u, CanonicalizeRangeHi }, + { 0x0273u, 0x0274u, 0x0000u, CanonicalizeUnique }, + { 0x0275u, 0x0275u, 0x00d6u, CanonicalizeRangeHi }, + { 0x0276u, 0x027cu, 0x0000u, CanonicalizeUnique }, + { 0x027du, 0x027du, 0x29e7u, CanonicalizeRangeLo }, + { 0x027eu, 0x027fu, 0x0000u, CanonicalizeUnique }, + { 0x0280u, 0x0280u, 0x00dau, CanonicalizeRangeHi }, + { 0x0281u, 0x0282u, 0x0000u, CanonicalizeUnique }, + { 0x0283u, 0x0283u, 0x00dau, CanonicalizeRangeHi }, + { 0x0284u, 0x0287u, 0x0000u, CanonicalizeUnique }, + { 0x0288u, 0x0288u, 0x00dau, CanonicalizeRangeHi }, + { 0x0289u, 0x0289u, 0x0045u, CanonicalizeRangeHi }, + { 0x028au, 0x028bu, 0x00d9u, CanonicalizeRangeHi }, + { 0x028cu, 0x028cu, 0x0047u, CanonicalizeRangeHi }, + { 0x028du, 0x0291u, 0x0000u, CanonicalizeUnique }, + { 0x0292u, 0x0292u, 0x00dbu, CanonicalizeRangeHi }, + { 0x0293u, 0x0344u, 0x0000u, CanonicalizeUnique }, + { 0x0345u, 0x0345u, 0x0007u, CanonicalizeSet }, + { 0x0346u, 0x036fu, 0x0000u, CanonicalizeUnique }, + { 0x0370u, 0x0373u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0374u, 0x0375u, 0x0000u, CanonicalizeUnique }, + { 0x0376u, 0x0377u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0378u, 0x037au, 0x0000u, CanonicalizeUnique }, + { 0x037bu, 0x037du, 0x0082u, CanonicalizeRangeLo }, + { 0x037eu, 0x0385u, 0x0000u, CanonicalizeUnique }, + { 0x0386u, 0x0386u, 0x0026u, CanonicalizeRangeLo }, + { 0x0387u, 0x0387u, 0x0000u, CanonicalizeUnique }, + { 0x0388u, 0x038au, 0x0025u, CanonicalizeRangeLo }, + { 0x038bu, 0x038bu, 0x0000u, CanonicalizeUnique }, + { 0x038cu, 0x038cu, 0x0040u, CanonicalizeRangeLo }, + { 0x038du, 0x038du, 0x0000u, CanonicalizeUnique }, + { 0x038eu, 0x038fu, 0x003fu, CanonicalizeRangeLo }, + { 0x0390u, 0x0390u, 0x0000u, CanonicalizeUnique }, + { 0x0391u, 0x0391u, 0x0020u, CanonicalizeRangeLo }, + { 0x0392u, 0x0392u, 0x0004u, CanonicalizeSet }, + { 0x0393u, 0x0394u, 0x0020u, CanonicalizeRangeLo }, + { 0x0395u, 0x0395u, 0x0005u, CanonicalizeSet }, + { 0x0396u, 0x0397u, 0x0020u, CanonicalizeRangeLo }, + { 0x0398u, 0x0398u, 0x0006u, CanonicalizeSet }, + { 0x0399u, 0x0399u, 0x0007u, CanonicalizeSet }, + { 0x039au, 0x039au, 0x0008u, CanonicalizeSet }, + { 0x039bu, 0x039bu, 0x0020u, CanonicalizeRangeLo }, + { 0x039cu, 0x039cu, 0x0009u, CanonicalizeSet }, + { 0x039du, 0x039fu, 0x0020u, CanonicalizeRangeLo }, + { 0x03a0u, 0x03a0u, 0x000au, CanonicalizeSet }, + { 0x03a1u, 0x03a1u, 0x000bu, CanonicalizeSet }, + { 0x03a2u, 0x03a2u, 0x0000u, CanonicalizeUnique }, + { 0x03a3u, 0x03a3u, 0x000cu, CanonicalizeSet }, + { 0x03a4u, 0x03a5u, 0x0020u, CanonicalizeRangeLo }, + { 0x03a6u, 0x03a6u, 0x000du, CanonicalizeSet }, + { 0x03a7u, 0x03abu, 0x0020u, CanonicalizeRangeLo }, + { 0x03acu, 0x03acu, 0x0026u, CanonicalizeRangeHi }, + { 0x03adu, 0x03afu, 0x0025u, CanonicalizeRangeHi }, + { 0x03b0u, 0x03b0u, 0x0000u, CanonicalizeUnique }, + { 0x03b1u, 0x03b1u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b2u, 0x03b2u, 0x0004u, CanonicalizeSet }, + { 0x03b3u, 0x03b4u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b5u, 0x03b5u, 0x0005u, CanonicalizeSet }, + { 0x03b6u, 0x03b7u, 0x0020u, CanonicalizeRangeHi }, + { 0x03b8u, 0x03b8u, 0x0006u, CanonicalizeSet }, + { 0x03b9u, 0x03b9u, 0x0007u, CanonicalizeSet }, + { 0x03bau, 0x03bau, 0x0008u, CanonicalizeSet }, + { 0x03bbu, 0x03bbu, 0x0020u, CanonicalizeRangeHi }, + { 0x03bcu, 0x03bcu, 0x0009u, CanonicalizeSet }, + { 0x03bdu, 0x03bfu, 0x0020u, CanonicalizeRangeHi }, + { 0x03c0u, 0x03c0u, 0x000au, CanonicalizeSet }, + { 0x03c1u, 0x03c1u, 0x000bu, CanonicalizeSet }, + { 0x03c2u, 0x03c3u, 0x000cu, CanonicalizeSet }, + { 0x03c4u, 0x03c5u, 0x0020u, CanonicalizeRangeHi }, + { 0x03c6u, 0x03c6u, 0x000du, CanonicalizeSet }, + { 0x03c7u, 0x03cbu, 0x0020u, CanonicalizeRangeHi }, + { 0x03ccu, 0x03ccu, 0x0040u, CanonicalizeRangeHi }, + { 0x03cdu, 0x03ceu, 0x003fu, CanonicalizeRangeHi }, + { 0x03cfu, 0x03cfu, 0x0008u, CanonicalizeRangeLo }, + { 0x03d0u, 0x03d0u, 0x0004u, CanonicalizeSet }, + { 0x03d1u, 0x03d1u, 0x0006u, CanonicalizeSet }, + { 0x03d2u, 0x03d4u, 0x0000u, CanonicalizeUnique }, + { 0x03d5u, 0x03d5u, 0x000du, CanonicalizeSet }, + { 0x03d6u, 0x03d6u, 0x000au, CanonicalizeSet }, + { 0x03d7u, 0x03d7u, 0x0008u, CanonicalizeRangeHi }, + { 0x03d8u, 0x03efu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x03f0u, 0x03f0u, 0x0008u, CanonicalizeSet }, + { 0x03f1u, 0x03f1u, 0x000bu, CanonicalizeSet }, + { 0x03f2u, 0x03f2u, 0x0007u, CanonicalizeRangeLo }, + { 0x03f3u, 0x03f4u, 0x0000u, CanonicalizeUnique }, + { 0x03f5u, 0x03f5u, 0x0005u, CanonicalizeSet }, + { 0x03f6u, 0x03f6u, 0x0000u, CanonicalizeUnique }, + { 0x03f7u, 0x03f8u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x03f9u, 0x03f9u, 0x0007u, CanonicalizeRangeHi }, + { 0x03fau, 0x03fbu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x03fcu, 0x03fcu, 0x0000u, CanonicalizeUnique }, + { 0x03fdu, 0x03ffu, 0x0082u, CanonicalizeRangeHi }, + { 0x0400u, 0x040fu, 0x0050u, CanonicalizeRangeLo }, + { 0x0410u, 0x042fu, 0x0020u, CanonicalizeRangeLo }, + { 0x0430u, 0x044fu, 0x0020u, CanonicalizeRangeHi }, + { 0x0450u, 0x045fu, 0x0050u, CanonicalizeRangeHi }, + { 0x0460u, 0x0481u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0482u, 0x0489u, 0x0000u, CanonicalizeUnique }, + { 0x048au, 0x04bfu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x04c0u, 0x04c0u, 0x000fu, CanonicalizeRangeLo }, + { 0x04c1u, 0x04ceu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x04cfu, 0x04cfu, 0x000fu, CanonicalizeRangeHi }, + { 0x04d0u, 0x0527u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x0528u, 0x0530u, 0x0000u, CanonicalizeUnique }, + { 0x0531u, 0x0556u, 0x0030u, CanonicalizeRangeLo }, + { 0x0557u, 0x0560u, 0x0000u, CanonicalizeUnique }, + { 0x0561u, 0x0586u, 0x0030u, CanonicalizeRangeHi }, + { 0x0587u, 0x109fu, 0x0000u, CanonicalizeUnique }, + { 0x10a0u, 0x10c5u, 0x1c60u, CanonicalizeRangeLo }, + { 0x10c6u, 0x1d78u, 0x0000u, CanonicalizeUnique }, + { 0x1d79u, 0x1d79u, 0x8a04u, CanonicalizeRangeLo }, + { 0x1d7au, 0x1d7cu, 0x0000u, CanonicalizeUnique }, + { 0x1d7du, 0x1d7du, 0x0ee6u, CanonicalizeRangeLo }, + { 0x1d7eu, 0x1dffu, 0x0000u, CanonicalizeUnique }, + { 0x1e00u, 0x1e5fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1e60u, 0x1e61u, 0x000eu, CanonicalizeSet }, + { 0x1e62u, 0x1e95u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1e96u, 0x1e9au, 0x0000u, CanonicalizeUnique }, + { 0x1e9bu, 0x1e9bu, 0x000eu, CanonicalizeSet }, + { 0x1e9cu, 0x1e9fu, 0x0000u, CanonicalizeUnique }, + { 0x1ea0u, 0x1effu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x1f00u, 0x1f07u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f08u, 0x1f0fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f10u, 0x1f15u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f16u, 0x1f17u, 0x0000u, CanonicalizeUnique }, + { 0x1f18u, 0x1f1du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f1eu, 0x1f1fu, 0x0000u, CanonicalizeUnique }, + { 0x1f20u, 0x1f27u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f28u, 0x1f2fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f30u, 0x1f37u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f38u, 0x1f3fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f40u, 0x1f45u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f46u, 0x1f47u, 0x0000u, CanonicalizeUnique }, + { 0x1f48u, 0x1f4du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f4eu, 0x1f50u, 0x0000u, CanonicalizeUnique }, + { 0x1f51u, 0x1f51u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f52u, 0x1f52u, 0x0000u, CanonicalizeUnique }, + { 0x1f53u, 0x1f53u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f54u, 0x1f54u, 0x0000u, CanonicalizeUnique }, + { 0x1f55u, 0x1f55u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f56u, 0x1f56u, 0x0000u, CanonicalizeUnique }, + { 0x1f57u, 0x1f57u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f58u, 0x1f58u, 0x0000u, CanonicalizeUnique }, + { 0x1f59u, 0x1f59u, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5au, 0x1f5au, 0x0000u, CanonicalizeUnique }, + { 0x1f5bu, 0x1f5bu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5cu, 0x1f5cu, 0x0000u, CanonicalizeUnique }, + { 0x1f5du, 0x1f5du, 0x0008u, CanonicalizeRangeHi }, + { 0x1f5eu, 0x1f5eu, 0x0000u, CanonicalizeUnique }, + { 0x1f5fu, 0x1f5fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f60u, 0x1f67u, 0x0008u, CanonicalizeRangeLo }, + { 0x1f68u, 0x1f6fu, 0x0008u, CanonicalizeRangeHi }, + { 0x1f70u, 0x1f71u, 0x004au, CanonicalizeRangeLo }, + { 0x1f72u, 0x1f75u, 0x0056u, CanonicalizeRangeLo }, + { 0x1f76u, 0x1f77u, 0x0064u, CanonicalizeRangeLo }, + { 0x1f78u, 0x1f79u, 0x0080u, CanonicalizeRangeLo }, + { 0x1f7au, 0x1f7bu, 0x0070u, CanonicalizeRangeLo }, + { 0x1f7cu, 0x1f7du, 0x007eu, CanonicalizeRangeLo }, + { 0x1f7eu, 0x1fafu, 0x0000u, CanonicalizeUnique }, + { 0x1fb0u, 0x1fb1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fb2u, 0x1fb7u, 0x0000u, CanonicalizeUnique }, + { 0x1fb8u, 0x1fb9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1fbau, 0x1fbbu, 0x004au, CanonicalizeRangeHi }, + { 0x1fbcu, 0x1fbdu, 0x0000u, CanonicalizeUnique }, + { 0x1fbeu, 0x1fbeu, 0x0007u, CanonicalizeSet }, + { 0x1fbfu, 0x1fc7u, 0x0000u, CanonicalizeUnique }, + { 0x1fc8u, 0x1fcbu, 0x0056u, CanonicalizeRangeHi }, + { 0x1fccu, 0x1fcfu, 0x0000u, CanonicalizeUnique }, + { 0x1fd0u, 0x1fd1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fd2u, 0x1fd7u, 0x0000u, CanonicalizeUnique }, + { 0x1fd8u, 0x1fd9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1fdau, 0x1fdbu, 0x0064u, CanonicalizeRangeHi }, + { 0x1fdcu, 0x1fdfu, 0x0000u, CanonicalizeUnique }, + { 0x1fe0u, 0x1fe1u, 0x0008u, CanonicalizeRangeLo }, + { 0x1fe2u, 0x1fe4u, 0x0000u, CanonicalizeUnique }, + { 0x1fe5u, 0x1fe5u, 0x0007u, CanonicalizeRangeLo }, + { 0x1fe6u, 0x1fe7u, 0x0000u, CanonicalizeUnique }, + { 0x1fe8u, 0x1fe9u, 0x0008u, CanonicalizeRangeHi }, + { 0x1feau, 0x1febu, 0x0070u, CanonicalizeRangeHi }, + { 0x1fecu, 0x1fecu, 0x0007u, CanonicalizeRangeHi }, + { 0x1fedu, 0x1ff7u, 0x0000u, CanonicalizeUnique }, + { 0x1ff8u, 0x1ff9u, 0x0080u, CanonicalizeRangeHi }, + { 0x1ffau, 0x1ffbu, 0x007eu, CanonicalizeRangeHi }, + { 0x1ffcu, 0x2131u, 0x0000u, CanonicalizeUnique }, + { 0x2132u, 0x2132u, 0x001cu, CanonicalizeRangeLo }, + { 0x2133u, 0x214du, 0x0000u, CanonicalizeUnique }, + { 0x214eu, 0x214eu, 0x001cu, CanonicalizeRangeHi }, + { 0x214fu, 0x215fu, 0x0000u, CanonicalizeUnique }, + { 0x2160u, 0x216fu, 0x0010u, CanonicalizeRangeLo }, + { 0x2170u, 0x217fu, 0x0010u, CanonicalizeRangeHi }, + { 0x2180u, 0x2182u, 0x0000u, CanonicalizeUnique }, + { 0x2183u, 0x2184u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2185u, 0x24b5u, 0x0000u, CanonicalizeUnique }, + { 0x24b6u, 0x24cfu, 0x001au, CanonicalizeRangeLo }, + { 0x24d0u, 0x24e9u, 0x001au, CanonicalizeRangeHi }, + { 0x24eau, 0x2bffu, 0x0000u, CanonicalizeUnique }, + { 0x2c00u, 0x2c2eu, 0x0030u, CanonicalizeRangeLo }, + { 0x2c2fu, 0x2c2fu, 0x0000u, CanonicalizeUnique }, + { 0x2c30u, 0x2c5eu, 0x0030u, CanonicalizeRangeHi }, + { 0x2c5fu, 0x2c5fu, 0x0000u, CanonicalizeUnique }, + { 0x2c60u, 0x2c61u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2c62u, 0x2c62u, 0x29f7u, CanonicalizeRangeHi }, + { 0x2c63u, 0x2c63u, 0x0ee6u, CanonicalizeRangeHi }, + { 0x2c64u, 0x2c64u, 0x29e7u, CanonicalizeRangeHi }, + { 0x2c65u, 0x2c65u, 0x2a2bu, CanonicalizeRangeHi }, + { 0x2c66u, 0x2c66u, 0x2a28u, CanonicalizeRangeHi }, + { 0x2c67u, 0x2c6cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2c6du, 0x2c6du, 0x2a1cu, CanonicalizeRangeHi }, + { 0x2c6eu, 0x2c6eu, 0x29fdu, CanonicalizeRangeHi }, + { 0x2c6fu, 0x2c6fu, 0x2a1fu, CanonicalizeRangeHi }, + { 0x2c70u, 0x2c70u, 0x2a1eu, CanonicalizeRangeHi }, + { 0x2c71u, 0x2c71u, 0x0000u, CanonicalizeUnique }, + { 0x2c72u, 0x2c73u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2c74u, 0x2c74u, 0x0000u, CanonicalizeUnique }, + { 0x2c75u, 0x2c76u, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2c77u, 0x2c7du, 0x0000u, CanonicalizeUnique }, + { 0x2c7eu, 0x2c7fu, 0x2a3fu, CanonicalizeRangeHi }, + { 0x2c80u, 0x2ce3u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0x2ce4u, 0x2ceau, 0x0000u, CanonicalizeUnique }, + { 0x2cebu, 0x2ceeu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0x2cefu, 0x2cffu, 0x0000u, CanonicalizeUnique }, + { 0x2d00u, 0x2d25u, 0x1c60u, CanonicalizeRangeHi }, + { 0x2d26u, 0xa63fu, 0x0000u, CanonicalizeUnique }, + { 0xa640u, 0xa66du, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa66eu, 0xa67fu, 0x0000u, CanonicalizeUnique }, + { 0xa680u, 0xa697u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa698u, 0xa721u, 0x0000u, CanonicalizeUnique }, + { 0xa722u, 0xa72fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa730u, 0xa731u, 0x0000u, CanonicalizeUnique }, + { 0xa732u, 0xa76fu, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa770u, 0xa778u, 0x0000u, CanonicalizeUnique }, + { 0xa779u, 0xa77cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0xa77du, 0xa77du, 0x8a04u, CanonicalizeRangeHi }, + { 0xa77eu, 0xa787u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa788u, 0xa78au, 0x0000u, CanonicalizeUnique }, + { 0xa78bu, 0xa78cu, 0x0000u, CanonicalizeAlternatingUnaligned }, + { 0xa78du, 0xa78du, 0xa528u, CanonicalizeRangeHi }, + { 0xa78eu, 0xa78fu, 0x0000u, CanonicalizeUnique }, + { 0xa790u, 0xa791u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa792u, 0xa79fu, 0x0000u, CanonicalizeUnique }, + { 0xa7a0u, 0xa7a9u, 0x0000u, CanonicalizeAlternatingAligned }, + { 0xa7aau, 0xff20u, 0x0000u, CanonicalizeUnique }, + { 0xff21u, 0xff3au, 0x0020u, CanonicalizeRangeLo }, + { 0xff3bu, 0xff40u, 0x0000u, CanonicalizeUnique }, + { 0xff41u, 0xff5au, 0x0020u, CanonicalizeRangeHi }, + { 0xff5bu, 0xffffu, 0x0000u, CanonicalizeUnique }, +}; + +const size_t LATIN_CANONICALIZATION_RANGES = 20; +LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = { + { 0x0000u, 0x0040u, 0x0000u, CanonicalizeLatinSelf }, + { 0x0041u, 0x005au, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x005bu, 0x0060u, 0x0000u, CanonicalizeLatinSelf }, + { 0x0061u, 0x007au, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x007bu, 0x00bfu, 0x0000u, CanonicalizeLatinSelf }, + { 0x00c0u, 0x00d6u, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeLatinSelf }, + { 0x00d8u, 0x00deu, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeLatinSelf }, + { 0x00e0u, 0x00f6u, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeLatinSelf }, + { 0x00f8u, 0x00feu, 0x0000u, CanonicalizeLatinMask0x20 }, + { 0x00ffu, 0x00ffu, 0x0000u, CanonicalizeLatinSelf }, + { 0x0100u, 0x0177u, 0x0000u, CanonicalizeLatinInvalid }, + { 0x0178u, 0x0178u, 0x00ffu, CanonicalizeLatinOther }, + { 0x0179u, 0x039bu, 0x0000u, CanonicalizeLatinInvalid }, + { 0x039cu, 0x039cu, 0x00b5u, CanonicalizeLatinOther }, + { 0x039du, 0x03bbu, 0x0000u, CanonicalizeLatinInvalid }, + { 0x03bcu, 0x03bcu, 0x00b5u, CanonicalizeLatinOther }, + { 0x03bdu, 0xffffu, 0x0000u, CanonicalizeLatinInvalid }, +}; + +} } // JSC::Yarr + diff --git a/js/src/yarr/YarrCanonicalizeUCS2.h b/js/src/yarr/YarrCanonicalizeUCS2.h new file mode 100644 index 000000000000..18005354d73c --- /dev/null +++ b/js/src/yarr/YarrCanonicalizeUCS2.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef YarrCanonicalizeUCS2_H +#define YarrCanonicalizeUCS2_H + +#include +#include "wtfbridge.h" +#include "assembler/wtf/Assertions.h" + +namespace JSC { namespace Yarr { + +// This set of data (autogenerated using YarrCanonicalizeUCS2.js into YarrCanonicalizeUCS2.cpp) +// provides information for each UCS2 code point as to the set of code points that it should +// match under the ES5.1 case insensitive RegExp matching rules, specified in 15.10.2.8. +enum UCS2CanonicalizationType { + CanonicalizeUnique, // No canonically equal values, e.g. 0x0. + CanonicalizeSet, // Value indicates a set in characterSetInfo. + CanonicalizeRangeLo, // Value is positive delta to pair, E.g. 0x41 has value 0x20, -> 0x61. + CanonicalizeRangeHi, // Value is positive delta to pair, E.g. 0x61 has value 0x20, -> 0x41. + CanonicalizeAlternatingAligned, // Aligned consequtive pair, e.g. 0x1f4,0x1f5. + CanonicalizeAlternatingUnaligned // Unaligned consequtive pair, e.g. 0x241,0x242. +}; +struct UCS2CanonicalizationRange { uint16_t begin, end, value, type; }; +extern const size_t UCS2_CANONICALIZATION_RANGES; +extern uint16_t* characterSetInfo[]; +extern UCS2CanonicalizationRange rangeInfo[]; + +// This table is similar to the full rangeInfo table, however this maps from UCS2 codepoints to +// the set of Latin1 codepoints that could match. +enum LatinCanonicalizationType { + CanonicalizeLatinSelf, // This character is in the Latin1 range, but has no canonical equivalent in the range. + CanonicalizeLatinMask0x20, // One of a pair of characters, under the mask 0x20. + CanonicalizeLatinOther, // This character is not in the Latin1 range, but canonicalizes to another that is. + CanonicalizeLatinInvalid // Cannot match against Latin1 input. +}; +struct LatinCanonicalizationRange { uint16_t begin, end, value, type; }; +extern const size_t LATIN_CANONICALIZATION_RANGES; +extern LatinCanonicalizationRange latinRangeInfo[]; + +// This searches in log2 time over ~364 entries, so should typically result in 8 compares. +inline UCS2CanonicalizationRange* rangeInfoFor(UChar ch) +{ + UCS2CanonicalizationRange* info = rangeInfo; + size_t entries = UCS2_CANONICALIZATION_RANGES; + + while (true) { + size_t candidate = entries >> 1; + UCS2CanonicalizationRange* candidateInfo = info + candidate; + if (ch < candidateInfo->begin) + entries = candidate; + else if (ch <= candidateInfo->end) + return candidateInfo; + else { + info = candidateInfo + 1; + entries -= (candidate + 1); + } + } +} + +// Should only be called for characters that have one canonically matching value. +inline UChar getCanonicalPair(UCS2CanonicalizationRange* info, UChar ch) +{ + ASSERT(ch >= info->begin && ch <= info->end); + switch (info->type) { + case CanonicalizeRangeLo: + return ch + info->value; + case CanonicalizeRangeHi: + return ch - info->value; + case CanonicalizeAlternatingAligned: + return ch ^ 1; + case CanonicalizeAlternatingUnaligned: + return ((ch - 1) ^ 1) + 1; + default: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +// Returns true if no other UCS2 codepoint can match this value. +inline bool isCanonicallyUnique(UChar ch) +{ + return rangeInfoFor(ch)->type == CanonicalizeUnique; +} + +// Returns true if values are equal, under the canonicalization rules. +inline bool areCanonicallyEquivalent(UChar a, UChar b) +{ + UCS2CanonicalizationRange* info = rangeInfoFor(a); + switch (info->type) { + case CanonicalizeUnique: + return a == b; + case CanonicalizeSet: { + for (uint16_t* set = characterSetInfo[info->value]; (a = *set); ++set) { + if (a == b) + return true; + } + return false; + } + case CanonicalizeRangeLo: + return (a == b) || (a + info->value == b); + case CanonicalizeRangeHi: + return (a == b) || (a - info->value == b); + case CanonicalizeAlternatingAligned: + return (a | 1) == (b | 1); + case CanonicalizeAlternatingUnaligned: + return ((a - 1) | 1) == ((b - 1) | 1); + } + + ASSERT_NOT_REACHED(); + return false; +} + +} } // JSC::Yarr + +#endif diff --git a/js/src/yarr/YarrCanonicalizeUCS2.js b/js/src/yarr/YarrCanonicalizeUCS2.js new file mode 100644 index 000000000000..5cadd53b306d --- /dev/null +++ b/js/src/yarr/YarrCanonicalizeUCS2.js @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// See ES 5.1, 15.10.2.8 +function canonicalize(ch) +{ + var u = String.fromCharCode(ch).toUpperCase(); + if (u.length > 1) + return ch; + var cu = u.charCodeAt(0); + if (ch >= 128 && cu < 128) + return ch; + return cu; +} + +var MAX_UCS2 = 0xFFFF; +var MAX_LATIN = 0xFF; + +var groupedCanonically = []; +// Pass 1: populate groupedCanonically - this is mapping from canonicalized +// values back to the set of character code that canonicalize to them. +for (var i = 0; i <= MAX_UCS2; ++i) { + var ch = canonicalize(i); + if (!groupedCanonically[ch]) + groupedCanonically[ch] = []; + groupedCanonically[ch].push(i); +} + +var typeInfo = []; +var latinTypeInfo = []; +var characterSetInfo = []; +// Pass 2: populate typeInfo & characterSetInfo. For every character calculate +// a typeInfo value, described by the types above, and a value payload. +for (cu in groupedCanonically) { + // The set of characters that canonicalize to cu + var characters = groupedCanonically[cu]; + + // If there is only one, it is unique. + if (characters.length == 1) { + typeInfo[characters[0]] = "CanonicalizeUnique:0"; + latinTypeInfo[characters[0]] = characters[0] <= MAX_LATIN ? "CanonicalizeLatinSelf:0" : "CanonicalizeLatinInvalid:0"; + continue; + } + + // Sort the array. + characters.sort(function(x,y){return x-y;}); + + // If there are more than two characters, create an entry in characterSetInfo. + if (characters.length > 2) { + for (i in characters) + typeInfo[characters[i]] = "CanonicalizeSet:" + characterSetInfo.length; + characterSetInfo.push(characters); + + if (characters[1] <= MAX_LATIN) + throw new Error("sets with more than one latin character not supported!"); + if (characters[0] <= MAX_LATIN) { + for (i in characters) + latinTypeInfo[characters[i]] = "CanonicalizeLatinOther:" + characters[0]; + latinTypeInfo[characters[0]] = "CanonicalizeLatinSelf:0"; + } else { + for (i in characters) + latinTypeInfo[characters[i]] = "CanonicalizeLatinInvalid:0"; + } + + continue; + } + + // We have a pair, mark alternating ranges, otherwise track whether this is the low or high partner. + var lo = characters[0]; + var hi = characters[1]; + var delta = hi - lo; + if (delta == 1) { + var type = lo & 1 ? "CanonicalizeAlternatingUnaligned:0" : "CanonicalizeAlternatingAligned:0"; + typeInfo[lo] = type; + typeInfo[hi] = type; + } else { + typeInfo[lo] = "CanonicalizeRangeLo:" + delta; + typeInfo[hi] = "CanonicalizeRangeHi:" + delta; + } + + if (lo > MAX_LATIN) { + latinTypeInfo[lo] = "CanonicalizeLatinInvalid:0"; + latinTypeInfo[hi] = "CanonicalizeLatinInvalid:0"; + } else if (hi > MAX_LATIN) { + latinTypeInfo[lo] = "CanonicalizeLatinSelf:0"; + latinTypeInfo[hi] = "CanonicalizeLatinOther:" + lo; + } else { + if (delta != 0x20 || lo & 0x20) + throw new Error("pairs of latin characters that don't mask with 0x20 not supported!"); + latinTypeInfo[lo] = "CanonicalizeLatinMask0x20:0"; + latinTypeInfo[hi] = "CanonicalizeLatinMask0x20:0"; + } +} + +var rangeInfo = []; +// Pass 3: coallesce types into ranges. +for (var end = 0; end <= MAX_UCS2; ++end) { + var begin = end; + var type = typeInfo[end]; + while (end < MAX_UCS2 && typeInfo[end + 1] == type) + ++end; + rangeInfo.push({begin:begin, end:end, type:type}); +} + +var latinRangeInfo = []; +// Pass 4: coallesce latin-1 types into ranges. +for (var end = 0; end <= MAX_UCS2; ++end) { + var begin = end; + var type = latinTypeInfo[end]; + while (end < MAX_UCS2 && latinTypeInfo[end + 1] == type) + ++end; + latinRangeInfo.push({begin:begin, end:end, type:type}); +} + + +// Helper function to convert a number to a fixed width hex representation of a C uint16_t. +function hex(x) +{ + var s = Number(x).toString(16); + while (s.length < 4) + s = 0 + s; + return "0x" + s + "u"; +} + +var copyright = ( + "/*" + "\n" + + " * Copyright (C) 2012 Apple Inc. All rights reserved." + "\n" + + " *" + "\n" + + " * Redistribution and use in source and binary forms, with or without" + "\n" + + " * modification, are permitted provided that the following conditions" + "\n" + + " * are met:" + "\n" + + " * 1. Redistributions of source code must retain the above copyright" + "\n" + + " * notice, this list of conditions and the following disclaimer." + "\n" + + " * 2. Redistributions in binary form must reproduce the above copyright" + "\n" + + " * notice, this list of conditions and the following disclaimer in the" + "\n" + + " * documentation and/or other materials provided with the distribution." + "\n" + + " *" + "\n" + + " * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY" + "\n" + + " * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" + "\n" + + " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR" + "\n" + + " * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR" + "\n" + + " * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL," + "\n" + + " * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO," + "\n" + + " * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR" + "\n" + + " * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY" + "\n" + + " * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "\n" + + " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "\n" + + " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " + "\n" + + " */"); + +print(copyright); +print(); +print("// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js"); +print(); +print('#include "wtfbridge.h"'); +print('#include "YarrCanonicalizeUCS2.h"'); +print('#include "assembler/wtf/Assertions.h"'); +print(); +print("namespace JSC { namespace Yarr {"); +print(); +print("#include "); +print(); + +for (i in characterSetInfo) { + var characters = "" + var set = characterSetInfo[i]; + for (var j in set) + characters += hex(set[j]) + ", "; + print("uint16_t ucs2CharacterSet" + i + "[] = { " + characters + "0 };"); +} +print(); +print("static const size_t UCS2_CANONICALIZATION_SETS = " + characterSetInfo.length + ";"); +print("uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {"); +for (i in characterSetInfo) +print(" ucs2CharacterSet" + i + ","); +print("};"); +print(); +print("const size_t UCS2_CANONICALIZATION_RANGES = " + rangeInfo.length + ";"); +print("UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = {"); +for (i in rangeInfo) { + var info = rangeInfo[i]; + var typeAndValue = info.type.split(':'); + print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); +} +print("};"); +print(); +print("const size_t LATIN_CANONICALIZATION_RANGES = " + latinRangeInfo.length + ";"); +print("LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = {"); +for (i in latinRangeInfo) { + var info = latinRangeInfo[i]; + var typeAndValue = info.type.split(':'); + print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },"); +} +print("};"); +print(); +print("} } // JSC::Yarr"); +print(); + diff --git a/js/src/yarr/YarrInterpreter.cpp b/js/src/yarr/YarrInterpreter.cpp index 06895b4b7493..3ce3d0bcee0d 100644 --- a/js/src/yarr/YarrInterpreter.cpp +++ b/js/src/yarr/YarrInterpreter.cpp @@ -1,7 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** +/* * Copyright (C) 2009 Apple Inc. All rights reserved. * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged * @@ -25,12 +22,12 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #include "YarrInterpreter.h" #include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" #include "BumpPointerAllocator.h" #ifndef NDEBUG @@ -41,6 +38,7 @@ using namespace WTF; namespace JSC { namespace Yarr { +template class Interpreter { public: struct ParenthesesDisjunctionContext; @@ -111,7 +109,7 @@ public: allocatorPool = allocatorPool->ensureCapacity(size); if (!allocatorPool) CRASH(); - return new(allocatorPool->alloc(size)) DisjunctionContext(); + return new (allocatorPool->alloc(size)) DisjunctionContext(); } void freeDisjunctionContext(DisjunctionContext* context) @@ -121,7 +119,7 @@ public: struct ParenthesesDisjunctionContext { - ParenthesesDisjunctionContext(int* output, ByteTerm& term) + ParenthesesDisjunctionContext(unsigned* output, ByteTerm& term) : next(0) { unsigned firstSubpatternId = term.atom.subpatternId; @@ -129,10 +127,10 @@ public: for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) { subpatternBackup[i] = output[(firstSubpatternId << 1) + i]; - output[(firstSubpatternId << 1) + i] = -1; + output[(firstSubpatternId << 1) + i] = offsetNoMatch; } - new(getDisjunctionContext(term)) DisjunctionContext(); + new (getDisjunctionContext(term)) DisjunctionContext(); } void* operator new(size_t, void* where) @@ -140,7 +138,7 @@ public: return where; } - void restoreOutput(int* output, unsigned firstSubpatternId, unsigned numNestedSubpatterns) + void restoreOutput(unsigned* output, unsigned firstSubpatternId, unsigned numNestedSubpatterns) { for (unsigned i = 0; i < (numNestedSubpatterns << 1); ++i) output[(firstSubpatternId << 1) + i] = subpatternBackup[i]; @@ -152,16 +150,16 @@ public: } ParenthesesDisjunctionContext* next; - int subpatternBackup[1]; + unsigned subpatternBackup[1]; }; - ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, int* output, ByteTerm& term) + ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, unsigned* output, ByteTerm& term) { - size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(int) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(int) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); + size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t); allocatorPool = allocatorPool->ensureCapacity(size); if (!allocatorPool) CRASH(); - return new(allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term); + return new (allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term); } void freeParenthesesDisjunctionContext(ParenthesesDisjunctionContext* context) @@ -171,7 +169,7 @@ public: class InputStream { public: - InputStream(const UChar* input, unsigned start, unsigned length) + InputStream(const CharType* input, unsigned start, unsigned length) : input(input) , pos(start) , length(length) @@ -203,11 +201,11 @@ public: return input[pos] | input[pos + 1] << 16; } - int readChecked(int position) + int readChecked(unsigned negativePositionOffest) { - ASSERT(position < 0); - ASSERT(static_cast(-position) <= pos); - unsigned p = pos + position; + if (pos < negativePositionOffest) + CRASH(); + unsigned p = pos - negativePositionOffest; ASSERT(p < length); return input[p]; } @@ -246,37 +244,46 @@ public: return pos == length; } - bool checkInput(int count) + unsigned end() { - if ((pos + count) <= length) { + return length; + } + + bool checkInput(unsigned count) + { + if (((pos + count) <= length) && ((pos + count) >= pos)) { pos += count; return true; } return false; } - void uncheckInput(int count) + void uncheckInput(unsigned count) { + if (pos < count) + CRASH(); pos -= count; } - bool atStart(int position) + bool atStart(unsigned negativePositionOffest) { - return (pos + position) == 0; + return pos == negativePositionOffest; } - bool atEnd(int position) + bool atEnd(unsigned negativePositionOffest) { - return (pos + position) == length; + if (pos < negativePositionOffest) + CRASH(); + return (pos - negativePositionOffest) == length; } - bool isNotAvailableInput(int position) + bool isAvailableInput(unsigned offset) { - return (pos + position) > length; + return (((pos + offset) <= length) && ((pos + offset) >= pos)); } private: - const UChar* input; + const CharType* input; unsigned pos; unsigned length; }; @@ -302,45 +309,52 @@ public: return false; } - bool checkCharacter(int testChar, int inputPosition) + bool checkCharacter(int testChar, unsigned negativeInputOffset) { - return testChar == input.readChecked(inputPosition); + return testChar == input.readChecked(negativeInputOffset); } - bool checkCasedCharacter(int loChar, int hiChar, int inputPosition) + bool checkCasedCharacter(int loChar, int hiChar, unsigned negativeInputOffset) { - int ch = input.readChecked(inputPosition); + int ch = input.readChecked(negativeInputOffset); return (loChar == ch) || (hiChar == ch); } - bool checkCharacterClass(CharacterClass* characterClass, bool invert, int inputPosition) + bool checkCharacterClass(CharacterClass* characterClass, bool invert, unsigned negativeInputOffset) { - bool match = testCharacterClass(characterClass, input.readChecked(inputPosition)); + bool match = testCharacterClass(characterClass, input.readChecked(negativeInputOffset)); return invert ? !match : match; } - bool tryConsumeBackReference(int matchBegin, int matchEnd, int inputOffset) + bool tryConsumeBackReference(int matchBegin, int matchEnd, unsigned negativeInputOffset) { - int matchSize = matchEnd - matchBegin; + unsigned matchSize = (unsigned)(matchEnd - matchBegin); if (!input.checkInput(matchSize)) return false; if (pattern->m_ignoreCase) { - for (int i = 0; i < matchSize; ++i) { - int ch = input.reread(matchBegin + i); + for (unsigned i = 0; i < matchSize; ++i) { + int oldCh = input.reread(matchBegin + i); + int ch = input.readChecked(negativeInputOffset + matchSize - i); - int lo = Unicode::toLower(ch); - int hi = Unicode::toUpper(ch); + if (oldCh == ch) + continue; - if ((lo != hi) ? (!checkCasedCharacter(lo, hi, inputOffset - matchSize + i)) : (!checkCharacter(ch, inputOffset - matchSize + i))) { - input.uncheckInput(matchSize); - return false; - } + // The definition for canonicalize (see ES 5.1, 15.10.2.8) means that + // unicode values are never allowed to match against ascii ones. + if (isASCII(oldCh) || isASCII(ch)) { + if (toASCIIUpper(oldCh) == toASCIIUpper(ch)) + continue; + } else if (areCanonicallyEquivalent(oldCh, ch)) + continue; + + input.uncheckInput(matchSize); + return false; } } else { - for (int i = 0; i < matchSize; ++i) { - if (!checkCharacter(input.reread(matchBegin + i), inputOffset - matchSize + i)) { + for (unsigned i = 0; i < matchSize; ++i) { + if (!checkCharacter(input.reread(matchBegin + i), negativeInputOffset + matchSize - i)) { input.uncheckInput(matchSize); return false; } @@ -352,7 +366,7 @@ public: bool matchAssertionBOL(ByteTerm& term) { - return (input.atStart(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition - 1))); + return (input.atStart(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition + 1))); } bool matchAssertionEOL(ByteTerm& term) @@ -365,7 +379,7 @@ public: bool matchAssertionWordBoundary(ByteTerm& term) { - bool prevIsWordchar = !input.atStart(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition - 1)); + bool prevIsWordchar = !input.atStart(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition + 1)); bool readIsWordchar; if (term.inputPosition) readIsWordchar = !input.atEnd(term.inputPosition) && testCharacterClass(pattern->wordcharCharacterClass, input.readChecked(term.inputPosition)); @@ -395,7 +409,7 @@ public: case QuantifierNonGreedy: if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { ++backTrack->matchAmount; - if (checkCharacter(term.atom.patternCharacter, term.inputPosition - 1)) + if (checkCharacter(term.atom.patternCharacter, term.inputPosition + 1)) return true; } input.uncheckInput(backTrack->matchAmount); @@ -424,7 +438,7 @@ public: case QuantifierNonGreedy: if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { ++backTrack->matchAmount; - if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition - 1)) + if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition + 1)) return true; } input.uncheckInput(backTrack->matchAmount); @@ -442,7 +456,7 @@ public: switch (term.atom.quantityType) { case QuantifierFixedCount: { for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) { - if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + matchAmount)) + if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount)) return false; } return true; @@ -451,7 +465,7 @@ public: case QuantifierGreedy: { unsigned matchAmount = 0; while ((matchAmount < term.atom.quantityCount) && input.checkInput(1)) { - if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - 1)) { + if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) { input.uncheckInput(1); break; } @@ -491,7 +505,7 @@ public: case QuantifierNonGreedy: if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) { ++backTrack->matchAmount; - if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - 1)) + if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) return true; } input.uncheckInput(backTrack->matchAmount); @@ -506,16 +520,19 @@ public: ASSERT(term.type == ByteTerm::TypeBackReference); BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); - int matchBegin = output[(term.atom.subpatternId << 1)]; - int matchEnd = output[(term.atom.subpatternId << 1) + 1]; + unsigned matchBegin = output[(term.atom.subpatternId << 1)]; + unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; // If the end position of the referenced match hasn't set yet then the backreference in the same parentheses where it references to that. // In this case the result of match is empty string like when it references to a parentheses with zero-width match. // Eg.: /(a\1)/ - if (matchEnd == -1) + if (matchEnd == offsetNoMatch) return true; - ASSERT((matchBegin == -1) || (matchBegin <= matchEnd)); + if (matchBegin == offsetNoMatch) + return true; + + ASSERT(matchBegin <= matchEnd); if (matchBegin == matchEnd) return true; @@ -555,9 +572,13 @@ public: ASSERT(term.type == ByteTerm::TypeBackReference); BackTrackInfoBackReference* backTrack = reinterpret_cast(context->frame + term.frameLocation); - int matchBegin = output[(term.atom.subpatternId << 1)]; - int matchEnd = output[(term.atom.subpatternId << 1) + 1]; - ASSERT((matchBegin == -1) || (matchBegin <= matchEnd)); + unsigned matchBegin = output[(term.atom.subpatternId << 1)]; + unsigned matchEnd = output[(term.atom.subpatternId << 1) + 1]; + + if (matchBegin == offsetNoMatch) + return false; + + ASSERT(matchBegin <= matchEnd); if (matchBegin == matchEnd) return false; @@ -646,7 +667,7 @@ public: if (term.capture()) { unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1)] = input.getPos() + term.inputPosition; + output[(subpatternId << 1)] = input.getPos() - term.inputPosition; } return true; @@ -678,8 +699,8 @@ public: if (term.capture()) { unsigned subpatternId = term.atom.subpatternId; - output[(subpatternId << 1)] = -1; - output[(subpatternId << 1) + 1] = -1; + output[(subpatternId << 1)] = offsetNoMatch; + output[(subpatternId << 1) + 1] = offsetNoMatch; } switch (term.atom.quantityType) { @@ -1046,6 +1067,38 @@ public: return JSRegExpErrorNoMatch; } + bool matchDotStarEnclosure(ByteTerm& term, DisjunctionContext* context) + { + UNUSED_PARAM(term); + unsigned matchBegin = context->matchBegin; + + if (matchBegin) { + for (matchBegin--; true; matchBegin--) { + if (testCharacterClass(pattern->newlineCharacterClass, input.reread(matchBegin))) { + ++matchBegin; + break; + } + + if (!matchBegin) + break; + } + } + + unsigned matchEnd = input.getPos(); + + for (; (matchEnd != input.end()) + && (!testCharacterClass(pattern->newlineCharacterClass, input.reread(matchEnd))); matchEnd++) { } + + if (((matchBegin && term.anchors.m_bol) + || ((matchEnd != input.end()) && term.anchors.m_eol)) + && !pattern->m_multiline) + return false; + + context->matchBegin = matchBegin; + context->matchEnd = matchEnd; + return true; + } + #define MATCH_NEXT() { ++context->term; goto matchAgain; } #define BACKTRACK() { --context->term; goto backtrack; } #define currentTerm() (disjunction->terms[context->term]) @@ -1104,7 +1157,7 @@ public: case ByteTerm::TypePatternCharacterOnce: case ByteTerm::TypePatternCharacterFixed: { for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { - if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + matchAmount)) + if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount)) BACKTRACK(); } MATCH_NEXT(); @@ -1113,7 +1166,7 @@ public: BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); unsigned matchAmount = 0; while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { - if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - 1)) { + if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + 1)) { input.uncheckInput(1); break; } @@ -1132,7 +1185,7 @@ public: case ByteTerm::TypePatternCasedCharacterOnce: case ByteTerm::TypePatternCasedCharacterFixed: { for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) { - if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + matchAmount)) + if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount)) BACKTRACK(); } MATCH_NEXT(); @@ -1141,7 +1194,7 @@ public: BackTrackInfoPatternCharacter* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); unsigned matchAmount = 0; while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) { - if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - 1)) { + if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + 1)) { input.uncheckInput(1); break; } @@ -1205,9 +1258,14 @@ public: MATCH_NEXT(); BACKTRACK(); - case ByteTerm::TypeUncheckInput: - input.uncheckInput(currentTerm().checkInputCount); - MATCH_NEXT(); + case ByteTerm::TypeUncheckInput: + input.uncheckInput(currentTerm().checkInputCount); + MATCH_NEXT(); + + case ByteTerm::TypeDotStarEnclosure: + if (matchDotStarEnclosure(currentTerm(), context)) + return JSRegExpMatch; + BACKTRACK(); } // We should never fall-through to here. @@ -1244,91 +1302,94 @@ public: case ByteTerm::TypeBodyAlternativeEnd: ASSERT_NOT_REACHED(); - case ByteTerm::TypeAlternativeBegin: - case ByteTerm::TypeAlternativeDisjunction: { - int offset = currentTerm().alternative.next; - context->term += offset; - if (offset > 0) - MATCH_NEXT(); - BACKTRACK(); - } - case ByteTerm::TypeAlternativeEnd: { - // We should never backtrack back into an alternative of the main body of the regex. - BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); - unsigned offset = backTrack->offset; - context->term -= offset; - BACKTRACK(); - } + case ByteTerm::TypeAlternativeBegin: + case ByteTerm::TypeAlternativeDisjunction: { + int offset = currentTerm().alternative.next; + context->term += offset; + if (offset > 0) + MATCH_NEXT(); + BACKTRACK(); + } + case ByteTerm::TypeAlternativeEnd: { + // We should never backtrack back into an alternative of the main body of the regex. + BackTrackInfoAlternative* backTrack = reinterpret_cast(context->frame + currentTerm().frameLocation); + unsigned offset = backTrack->offset; + context->term -= offset; + BACKTRACK(); + } - case ByteTerm::TypeAssertionBOL: - case ByteTerm::TypeAssertionEOL: - case ByteTerm::TypeAssertionWordBoundary: - BACKTRACK(); + case ByteTerm::TypeAssertionBOL: + case ByteTerm::TypeAssertionEOL: + case ByteTerm::TypeAssertionWordBoundary: + BACKTRACK(); - case ByteTerm::TypePatternCharacterOnce: - case ByteTerm::TypePatternCharacterFixed: - case ByteTerm::TypePatternCharacterGreedy: - case ByteTerm::TypePatternCharacterNonGreedy: - if (backtrackPatternCharacter(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypePatternCasedCharacterOnce: - case ByteTerm::TypePatternCasedCharacterFixed: - case ByteTerm::TypePatternCasedCharacterGreedy: - case ByteTerm::TypePatternCasedCharacterNonGreedy: - if (backtrackPatternCasedCharacter(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeCharacterClass: - if (backtrackCharacterClass(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeBackReference: - if (backtrackBackReference(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpattern: { - JSRegExpResult result = backtrackParentheses(currentTerm(), context); + case ByteTerm::TypePatternCharacterOnce: + case ByteTerm::TypePatternCharacterFixed: + case ByteTerm::TypePatternCharacterGreedy: + case ByteTerm::TypePatternCharacterNonGreedy: + if (backtrackPatternCharacter(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypePatternCasedCharacterOnce: + case ByteTerm::TypePatternCasedCharacterFixed: + case ByteTerm::TypePatternCasedCharacterGreedy: + case ByteTerm::TypePatternCasedCharacterNonGreedy: + if (backtrackPatternCasedCharacter(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeCharacterClass: + if (backtrackCharacterClass(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeBackReference: + if (backtrackBackReference(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpattern: { + JSRegExpResult result = backtrackParentheses(currentTerm(), context); - if (result == JSRegExpMatch) { - MATCH_NEXT(); - } else if (result != JSRegExpNoMatch) - return result; + if (result == JSRegExpMatch) { + MATCH_NEXT(); + } else if (result != JSRegExpNoMatch) + return result; - BACKTRACK(); - } - case ByteTerm::TypeParenthesesSubpatternOnceBegin: - if (backtrackParenthesesOnceBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternOnceEnd: - if (backtrackParenthesesOnceEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalBegin: - if (backtrackParenthesesTerminalBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParenthesesSubpatternTerminalEnd: - if (backtrackParenthesesTerminalEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionBegin: - if (backtrackParentheticalAssertionBegin(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); - case ByteTerm::TypeParentheticalAssertionEnd: - if (backtrackParentheticalAssertionEnd(currentTerm(), context)) - MATCH_NEXT(); - BACKTRACK(); + BACKTRACK(); + } + case ByteTerm::TypeParenthesesSubpatternOnceBegin: + if (backtrackParenthesesOnceBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternOnceEnd: + if (backtrackParenthesesOnceEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalBegin: + if (backtrackParenthesesTerminalBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParenthesesSubpatternTerminalEnd: + if (backtrackParenthesesTerminalEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionBegin: + if (backtrackParentheticalAssertionBegin(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); + case ByteTerm::TypeParentheticalAssertionEnd: + if (backtrackParentheticalAssertionEnd(currentTerm(), context)) + MATCH_NEXT(); + BACKTRACK(); - case ByteTerm::TypeCheckInput: - input.uncheckInput(currentTerm().checkInputCount); - BACKTRACK(); + case ByteTerm::TypeCheckInput: + input.uncheckInput(currentTerm().checkInputCount); + BACKTRACK(); - case ByteTerm::TypeUncheckInput: - input.checkInput(currentTerm().checkInputCount); - BACKTRACK(); + case ByteTerm::TypeUncheckInput: + input.checkInput(currentTerm().checkInputCount); + BACKTRACK(); + + case ByteTerm::TypeDotStarEnclosure: + ASSERT_NOT_REACHED(); } ASSERT_NOT_REACHED(); @@ -1351,15 +1412,18 @@ public: return result; } - int interpret() + unsigned interpret() { + if (!input.isAvailableInput(0)) + return offsetNoMatch; + + for (unsigned i = 0; i < pattern->m_body->m_numSubpatterns + 1; ++i) + output[i << 1] = offsetNoMatch; + allocatorPool = pattern->m_allocator->startAllocator(); if (!allocatorPool) CRASH(); - for (unsigned i = 0; i < ((pattern->m_body->m_numSubpatterns + 1) << 1); ++i) - output[i] = -1; - DisjunctionContext* context = allocDisjunctionContext(pattern->m_body.get()); JSRegExpResult result = matchDisjunction(pattern->m_body.get(), context, false); @@ -1372,15 +1436,14 @@ public: pattern->m_allocator->stopAllocator(); - // RegExp.cpp currently expects all error to be converted to -1. - ASSERT((result == JSRegExpMatch) == (output[0] != -1)); + ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch)); return output[0]; } - Interpreter(BytecodePattern* pattern, int* output, const UChar* inputChar, unsigned start, unsigned length) + Interpreter(BytecodePattern* pattern, unsigned* output, const CharType* input, unsigned length, unsigned start) : pattern(pattern) , output(output) - , input(inputChar, start, length) + , input(input, start, length) , allocatorPool(0) , remainingMatchCount(matchLimit) { @@ -1388,7 +1451,7 @@ public: private: BytecodePattern* pattern; - int* output; + unsigned* output; InputStream input; BumpPointerPool* allocatorPool; unsigned remainingMatchCount; @@ -1400,8 +1463,10 @@ class ByteCompiler { struct ParenthesesStackEntry { unsigned beginTerm; unsigned savedAlternativeIndex; + // For js::Vector. Does not create a valid object. - ParenthesesStackEntry() {} + ParenthesesStackEntry() { } + ParenthesesStackEntry(unsigned beginTerm, unsigned savedAlternativeIndex/*, unsigned subpatternId, bool capture = false*/) : beginTerm(beginTerm) , savedAlternativeIndex(savedAlternativeIndex) @@ -1435,22 +1500,22 @@ public: m_bodyDisjunction->terms.append(ByteTerm::UncheckInput(count)); } - void assertionBOL(int inputPosition) + void assertionBOL(unsigned inputPosition) { m_bodyDisjunction->terms.append(ByteTerm::BOL(inputPosition)); } - void assertionEOL(int inputPosition) + void assertionEOL(unsigned inputPosition) { m_bodyDisjunction->terms.append(ByteTerm::EOL(inputPosition)); } - void assertionWordBoundary(bool invert, int inputPosition) + void assertionWordBoundary(bool invert, unsigned inputPosition) { m_bodyDisjunction->terms.append(ByteTerm::WordBoundary(invert, inputPosition)); } - void atomPatternCharacter(UChar ch, int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType) + void atomPatternCharacter(UChar ch, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) { if (m_pattern.m_ignoreCase) { UChar lo = Unicode::toLower(ch); @@ -1465,27 +1530,27 @@ public: m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType)); } - void atomCharacterClass(CharacterClass* characterClass, bool invert, int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType) + void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) { m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; } - void atomBackReference(unsigned subpatternId, int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType) + void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) { ASSERT(subpatternId); m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition)); - m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType; m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation; } - void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, int inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) { int beginTerm = m_bodyDisjunction->terms.size(); @@ -1498,7 +1563,7 @@ public: m_currentAlternativeIndex = beginTerm + 1; } - void atomParenthesesTerminalBegin(unsigned subpatternId, bool capture, int inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + void atomParenthesesTerminalBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) { int beginTerm = m_bodyDisjunction->terms.size(); @@ -1511,7 +1576,7 @@ public: m_currentAlternativeIndex = beginTerm + 1; } - void atomParenthesesSubpatternBegin(unsigned subpatternId, bool capture, int inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) + void atomParenthesesSubpatternBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation) { // Errrk! - this is a little crazy, we initially generate as a TypeParenthesesSubpatternOnceBegin, // then fix this up at the end! - simplifying this should make it much clearer. @@ -1541,7 +1606,7 @@ public: m_currentAlternativeIndex = beginTerm + 1; } - void atomParentheticalAssertionEnd(int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType) + void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) { unsigned beginTerm = popParenthesesStack(); closeAlternative(beginTerm + 1); @@ -1557,12 +1622,17 @@ public: m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; } + void assertionDotStarEnclosure(bool bolAnchored, bool eolAnchored) + { + m_bodyDisjunction->terms.append(ByteTerm::DotStarEnclosure(bolAnchored, eolAnchored)); + } + unsigned popParenthesesStack() { ASSERT(m_parenthesesStack.size()); @@ -1580,10 +1650,10 @@ public: #ifndef NDEBUG void dumpDisjunction(ByteDisjunction* disjunction) { - printf("ByteDisjunction(%p):\n\t", (void *) disjunction); + dataLog("ByteDisjunction(%p):\n\t", (void *)disjunction); for (unsigned i = 0; i < disjunction->terms.size(); ++i) - printf("{ %d } ", disjunction->terms[i].type); - printf("\n"); + dataLog("{ %d } ", disjunction->terms[i].type); + dataLog("\n"); } #endif @@ -1634,7 +1704,7 @@ public: m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation; } - void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0) + void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0) { unsigned beginTerm = popParenthesesStack(); closeAlternative(beginTerm + 1); @@ -1660,12 +1730,12 @@ public: m_allParenthesesInfo.append(parenthesesDisjunction); m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, inputPosition)); - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation; } - void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType) + void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) { unsigned beginTerm = popParenthesesStack(); closeAlternative(beginTerm + 1); @@ -1681,13 +1751,13 @@ public: m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; } - void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType) + void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) { unsigned beginTerm = popParenthesesStack(); closeAlternative(beginTerm + 1); @@ -1703,9 +1773,9 @@ public: m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm; m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation; - m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType; - m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount; + m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet(); m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType; } @@ -1768,27 +1838,27 @@ public: switch (term.type) { case PatternTerm::TypeAssertionBOL: - assertionBOL(term.inputPosition - currentCountAlreadyChecked); + assertionBOL(currentCountAlreadyChecked - term.inputPosition); break; case PatternTerm::TypeAssertionEOL: - assertionEOL(term.inputPosition - currentCountAlreadyChecked); + assertionEOL(currentCountAlreadyChecked - term.inputPosition); break; case PatternTerm::TypeAssertionWordBoundary: - assertionWordBoundary(term.invert(), term.inputPosition - currentCountAlreadyChecked); + assertionWordBoundary(term.invert(), currentCountAlreadyChecked - term.inputPosition); break; case PatternTerm::TypePatternCharacter: - atomPatternCharacter(term.patternCharacter, term.inputPosition - currentCountAlreadyChecked, term.frameLocation, term.quantityCount, term.quantityType); + atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); break; case PatternTerm::TypeCharacterClass: - atomCharacterClass(term.characterClass, term.invert(), term.inputPosition - currentCountAlreadyChecked, term.frameLocation, term.quantityCount, term.quantityType); + atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); break; case PatternTerm::TypeBackReference: - atomBackReference(term.backReferenceSubpatternId, term.inputPosition - currentCountAlreadyChecked, term.frameLocation, term.quantityCount, term.quantityType); + atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType); break; case PatternTerm::TypeForwardReference: @@ -1804,17 +1874,17 @@ public: else alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce; unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), delegateEndInputOffset - disjunctionAlreadyCheckedCount, term.frameLocation, alternativeFrameLocation); + atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, alternativeFrameLocation); emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); } else if (term.parentheses.isTerminal) { unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), delegateEndInputOffset - disjunctionAlreadyCheckedCount, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce); + atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce); emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount); atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType); } else { unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked; - atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), delegateEndInputOffset - disjunctionAlreadyCheckedCount, term.frameLocation, 0); + atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, 0); emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, 0); atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize); } @@ -1842,6 +1912,10 @@ public: } break; } + + case PatternTerm::TypeDotStarEnclosure: + assertionDotStarEnclosure(term.anchors.bolAnchor, term.anchors.eolAnchor); + break; } } } @@ -1860,18 +1934,33 @@ PassOwnPtr byteCompile(YarrPattern& pattern, BumpPointerAllocat return ByteCompiler(pattern).compile(allocator); } -int interpret(BytecodePattern* bytecode, const UChar* input, unsigned start, unsigned length, int* output) +unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned start, unsigned* output) { - return Interpreter(bytecode, output, input, start, length).interpret(); +#if YARR_8BIT_CHAR_SUPPORT + if (input.is8Bit()) + return Interpreter(bytecode, output, input.characters8(), input.length(), start).interpret(); +#endif + return Interpreter(bytecode, output, input.chars(), input.length(), start).interpret(); } -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce); -COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheses) == (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses); +unsigned interpret(BytecodePattern* bytecode, const LChar* input, unsigned length, unsigned start, unsigned* output) +{ + return Interpreter(bytecode, output, input, length, start).interpret(); +} + +unsigned interpret(BytecodePattern* bytecode, const UChar* input, unsigned length, unsigned start, unsigned* output) +{ + return Interpreter(bytecode, output, input, length, start).interpret(); +} + +// These should be the same for both UChar & LChar. +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce); +COMPILE_ASSERT(sizeof(Interpreter::BackTrackInfoParentheses) == (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses); } } diff --git a/js/src/yarr/YarrInterpreter.h b/js/src/yarr/YarrInterpreter.h index 37ad8d60a9ce..79d8a12cbf51 100644 --- a/js/src/yarr/YarrInterpreter.h +++ b/js/src/yarr/YarrInterpreter.h @@ -1,7 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** +/* * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,8 +21,7 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #ifndef YarrInterpreter_h #define YarrInterpreter_h @@ -72,7 +68,8 @@ struct ByteTerm { TypeParentheticalAssertionBegin, TypeParentheticalAssertionEnd, TypeCheckInput, - TypeUncheckInput + TypeUncheckInput, + TypeDotStarEnclosure } type; union { struct { @@ -97,19 +94,18 @@ struct ByteTerm { int end; bool onceThrough; } alternative; + struct { + bool m_bol : 1; + bool m_eol : 1; + } anchors; unsigned checkInputCount; }; unsigned frameLocation; bool m_capture : 1; bool m_invert : 1; - int inputPosition; + unsigned inputPosition; - // For js::Vector. Does not create a valid object. - ByteTerm() - { - } - - ByteTerm(UChar ch, int inputPos, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType) + ByteTerm(UChar ch, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) : frameLocation(frameLocation) , m_capture(false) , m_invert(false) @@ -128,11 +124,11 @@ struct ByteTerm { atom.patternCharacter = ch; atom.quantityType = quantityType; - atom.quantityCount = quantityCount; + atom.quantityCount = quantityCount.unsafeGet(); inputPosition = inputPos; } - ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, unsigned quantityCount, QuantifierType quantityType) + ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, Checked quantityCount, QuantifierType quantityType) : frameLocation(frameLocation) , m_capture(false) , m_invert(false) @@ -152,7 +148,7 @@ struct ByteTerm { atom.casedCharacter.lo = lo; atom.casedCharacter.hi = hi; atom.quantityType = quantityType; - atom.quantityCount = quantityCount; + atom.quantityCount = quantityCount.unsafeGet(); inputPosition = inputPos; } @@ -199,6 +195,11 @@ struct ByteTerm { inputPosition = inputPos; } + // For js::Vector. Does not create a valid object. + ByteTerm() + { + } + static ByteTerm BOL(int inputPos) { ByteTerm term(TypeAssertionBOL); @@ -206,17 +207,17 @@ struct ByteTerm { return term; } - static ByteTerm CheckInput(unsigned count) + static ByteTerm CheckInput(Checked count) { ByteTerm term(TypeCheckInput); - term.checkInputCount = count; + term.checkInputCount = count.unsafeGet(); return term; } - static ByteTerm UncheckInput(unsigned count) + static ByteTerm UncheckInput(Checked count) { ByteTerm term(TypeUncheckInput); - term.checkInputCount = count; + term.checkInputCount = count.unsafeGet(); return term; } @@ -302,6 +303,14 @@ struct ByteTerm { { return ByteTerm(TypeSubpatternEnd); } + + static ByteTerm DotStarEnclosure(bool bolAnchor, bool eolAnchor) + { + ByteTerm term(TypeDotStarEnclosure); + term.anchors.m_bol = bolAnchor; + term.anchors.m_eol = eolAnchor; + return term; + } bool invert() { @@ -315,7 +324,7 @@ struct ByteTerm { }; class ByteDisjunction { - WTF_MAKE_FAST_ALLOCATED + WTF_MAKE_FAST_ALLOCATED; public: ByteDisjunction(unsigned numSubpatterns, unsigned frameSize) : m_numSubpatterns(numSubpatterns) @@ -329,7 +338,7 @@ public: }; struct BytecodePattern { - WTF_MAKE_FAST_ALLOCATED + WTF_MAKE_FAST_ALLOCATED; public: BytecodePattern(PassOwnPtr body, const Vector &allParenthesesInfo, YarrPattern& pattern, BumpPointerAllocator* allocator) : m_body(body) @@ -369,6 +378,11 @@ private: Vector m_userCharacterClasses; }; +JS_EXPORT_PRIVATE PassOwnPtr byteCompile(YarrPattern&, BumpPointerAllocator*); +JS_EXPORT_PRIVATE unsigned interpret(BytecodePattern*, const String& input, unsigned start, unsigned* output); +unsigned interpret(BytecodePattern*, const LChar* input, unsigned length, unsigned start, unsigned* output); +unsigned interpret(BytecodePattern*, const UChar* input, unsigned length, unsigned start, unsigned* output); + } } // namespace JSC::Yarr #endif // YarrInterpreter_h diff --git a/js/src/yarr/YarrJIT.cpp b/js/src/yarr/YarrJIT.cpp index aae6a1c47081..1c7c0309de48 100644 --- a/js/src/yarr/YarrJIT.cpp +++ b/js/src/yarr/YarrJIT.cpp @@ -1,7 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: +/* vim: set ts=4 sw=4 tw=99 et: * - * ***** BEGIN LICENSE BLOCK ***** * Copyright (C) 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,13 +22,13 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #include "YarrJIT.h" #include "assembler/assembler/LinkBuffer.h" #include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" #if ENABLE_YARR_JIT @@ -38,8 +36,9 @@ using namespace WTF; namespace JSC { namespace Yarr { +template class YarrGenerator : private MacroAssembler { - friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const UString& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); + friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); #if WTF_CPU_ARM static const RegisterID input = ARMRegisters::r0; @@ -51,6 +50,7 @@ class YarrGenerator : private MacroAssembler { static const RegisterID regT1 = ARMRegisters::r6; static const RegisterID returnRegister = ARMRegisters::r0; + static const RegisterID returnRegister2 = ARMRegisters::r1; #elif WTF_CPU_MIPS static const RegisterID input = MIPSRegisters::a0; static const RegisterID index = MIPSRegisters::a1; @@ -61,6 +61,7 @@ class YarrGenerator : private MacroAssembler { static const RegisterID regT1 = MIPSRegisters::t5; static const RegisterID returnRegister = MIPSRegisters::v0; + static const RegisterID returnRegister2 = MIPSRegisters::v1; #elif WTF_CPU_SH4 static const RegisterID input = SH4Registers::r4; static const RegisterID index = SH4Registers::r5; @@ -71,6 +72,7 @@ class YarrGenerator : private MacroAssembler { static const RegisterID regT1 = SH4Registers::r1; static const RegisterID returnRegister = SH4Registers::r0; + static const RegisterID returnRegister2 = SH4Registers::r1; #elif WTF_CPU_SPARC static const RegisterID input = SparcRegisters::i0; static const RegisterID index = SparcRegisters::i1; @@ -91,23 +93,25 @@ class YarrGenerator : private MacroAssembler { static const RegisterID regT1 = X86Registers::esi; static const RegisterID returnRegister = X86Registers::eax; + static const RegisterID returnRegister2 = X86Registers::edx; #elif WTF_CPU_X86_64 -#if WTF_PLATFORM_WIN +# if WTF_PLATFORM_WIN static const RegisterID input = X86Registers::ecx; static const RegisterID index = X86Registers::edx; static const RegisterID length = X86Registers::r8; static const RegisterID output = X86Registers::r9; -#else +# else static const RegisterID input = X86Registers::edi; static const RegisterID index = X86Registers::esi; static const RegisterID length = X86Registers::edx; static const RegisterID output = X86Registers::ecx; -#endif +# endif static const RegisterID regT0 = X86Registers::eax; static const RegisterID regT1 = X86Registers::ebx; static const RegisterID returnRegister = X86Registers::eax; + static const RegisterID returnRegister2 = X86Registers::edx; #endif void optimizeAlternative(PatternAlternative* alternative) @@ -275,19 +279,27 @@ class YarrGenerator : private MacroAssembler { return branch32(NotEqual, index, length); } - Jump jumpIfCharEquals(UChar ch, int inputPosition) + Jump jumpIfCharNotEquals(UChar ch, int inputPosition, RegisterID character) { - return branch16(Equal, BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), Imm32(ch)); - } + readCharacter(inputPosition, character); - Jump jumpIfCharNotEquals(UChar ch, int inputPosition) - { - return branch16(NotEqual, BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), Imm32(ch)); + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + or32(TrustedImm32(0x20), character); + ch |= 0x20; + } + + return branch32(NotEqual, character, Imm32(ch)); } void readCharacter(int inputPosition, RegisterID reg) { - load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg); + if (m_charSize == Char8) + load8(BaseIndex(input, index, TimesOne, inputPosition * sizeof(char)), reg); + else + load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg); } void storeToFrame(RegisterID reg, unsigned frameLocation) @@ -315,6 +327,65 @@ class YarrGenerator : private MacroAssembler { jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); } + void initCallFrame() + { + unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; + if (callFrameSize) + subPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + } + void removeCallFrame() + { + unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; + if (callFrameSize) + addPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + } + + // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns. + void setSubpatternStart(RegisterID reg, unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(reg, Address(output, (subpattern << 1) * sizeof(int))); + } + void setSubpatternEnd(RegisterID reg, unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(reg, Address(output, ((subpattern << 1) + 1) * sizeof(int))); + } + void clearSubpatternStart(unsigned subpattern) + { + ASSERT(subpattern); + // FIXME: should be able to ASSERT(compileMode == IncludeSubpatterns), but then this function is conditionally NORETURN. :-( + store32(TrustedImm32(-1), Address(output, (subpattern << 1) * sizeof(int))); + } + + // We use one of three different strategies to track the start of the current match, + // while matching. + // 1) If the pattern has a fixed size, do nothing! - we calculate the value lazily + // at the end of matching. This is irrespective of compileMode, and in this case + // these methods should never be called. + // 2) If we're compiling IncludeSubpatterns, 'output' contains a pointer to an output + // vector, store the match start in the output vector. + // 3) If we're compiling MatchOnly, 'output' is unused, store the match start directly + // in this register. + void setMatchStart(RegisterID reg) + { + ASSERT(!m_pattern.m_body->m_hasFixedSize); + if (compileMode == IncludeSubpatterns) + store32(reg, output); + else + move(reg, output); + } + void getMatchStart(RegisterID reg) + { + ASSERT(!m_pattern.m_body->m_hasFixedSize); + if (compileMode == IncludeSubpatterns) + load32(output, reg); + else + move(output, reg); + } + enum YarrOpCode { // These nodes wrap body alternatives - those in the main disjunction, // rather than subpatterns or assertions. These are chained together in @@ -390,6 +461,10 @@ class YarrGenerator : private MacroAssembler { Label m_reentry; JumpList m_jumps; + // Used for backtracking when the prior alternative did not consume any + // characters but matched. + Jump m_zeroLengthMatch; + // This flag is used to null out the second pattern character, when // two are fused to match a pair together. bool m_isDeadCode; @@ -579,7 +654,7 @@ class YarrGenerator : private MacroAssembler { if (term->inputPosition == m_checked) matchDest.append(atEndOfInput()); - readCharacter((term->inputPosition - m_checked), character); + readCharacter(term->inputPosition - m_checked, character); matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass()); op.m_jumps.append(jump()); @@ -666,70 +741,109 @@ class YarrGenerator : private MacroAssembler { { YarrOp& op = m_ops[opIndex]; + if (op.m_isDeadCode) + return; + // m_ops always ends with a OpBodyAlternativeEnd or OpMatchFailed // node, so there must always be at least one more node. ASSERT(opIndex + 1 < m_ops.size()); - YarrOp& nextOp = m_ops[opIndex + 1]; - - if (op.m_isDeadCode) - return; + YarrOp* nextOp = &m_ops[opIndex + 1]; PatternTerm* term = op.m_term; UChar ch = term->patternCharacter; + if ((ch > 0xff) && (m_charSize == Char8)) { + // Have a 16 bit pattern character and an 8 bit string - short circuit + op.m_jumps.append(jump()); + return; + } + const RegisterID character = regT0; + int maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2; + unsigned ignoreCaseMask = 0; + int allCharacters = ch; + int numberCharacters; + int startTermPosition = term->inputPosition; - if (nextOp.m_op == OpTerm) { - PatternTerm* nextTerm = nextOp.m_term; - if (nextTerm->type == PatternTerm::TypePatternCharacter - && nextTerm->quantityType == QuantifierFixedCount - && nextTerm->quantityCount == 1 - && nextTerm->inputPosition == (term->inputPosition + 1)) { + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); - UChar ch2 = nextTerm->patternCharacter; + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) + ignoreCaseMask |= 32; - int mask = 0; -#if WTF_CPU_BIG_ENDIAN - int chPair = ch2 | (ch << 16); -#else - int chPair = ch | (ch2 << 16); -#endif + for (numberCharacters = 1; numberCharacters < maxCharactersAtOnce && nextOp->m_op == OpTerm; ++numberCharacters, nextOp = &m_ops[opIndex + numberCharacters]) { + PatternTerm* nextTerm = nextOp->m_term; + + if (nextTerm->type != PatternTerm::TypePatternCharacter + || nextTerm->quantityType != QuantifierFixedCount + || nextTerm->quantityCount != 1 + || nextTerm->inputPosition != (startTermPosition + numberCharacters)) + break; - if (m_pattern.m_ignoreCase) { -#if WTF_CPU_BIG_ENDIAN - if (isASCIIAlpha(ch)) - mask |= 32 << 16; - if (isASCIIAlpha(ch2)) - mask |= 32; -#else - if (isASCIIAlpha(ch)) - mask |= 32; - if (isASCIIAlpha(ch2)) - mask |= 32 << 16; -#endif - } + nextOp->m_isDeadCode = true; - BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar)); - if (mask) { - load32WithUnalignedHalfWords(address, character); - or32(Imm32(mask), character); - op.m_jumps.append(branch32(NotEqual, character, Imm32(chPair | mask))); - } else - op.m_jumps.append(branch32WithUnalignedHalfWords(NotEqual, address, Imm32(chPair))); + int shiftAmount = (m_charSize == Char8 ? 8 : 16) * numberCharacters; - nextOp.m_isDeadCode = true; + UChar currentCharacter = nextTerm->patternCharacter; + + if ((currentCharacter > 0xff) && (m_charSize == Char8)) { + // Have a 16 bit pattern character and an 8 bit string - short circuit + op.m_jumps.append(jump()); return; } + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter)); + + allCharacters |= (currentCharacter << shiftAmount); + + if ((m_pattern.m_ignoreCase) && (isASCIIAlpha(currentCharacter))) + ignoreCaseMask |= 32 << shiftAmount; + } + + if (m_charSize == Char8) { + switch (numberCharacters) { + case 1: + op.m_jumps.append(jumpIfCharNotEquals(ch, startTermPosition - m_checked, character)); + return; + case 2: { + BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load16Unaligned(address, character); + break; + } + case 3: { + BaseIndex highAddress(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load16Unaligned(highAddress, character); + if (ignoreCaseMask) + or32(Imm32(ignoreCaseMask), character); + op.m_jumps.append(branch32(NotEqual, character, Imm32((allCharacters & 0xffff) | ignoreCaseMask))); + op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, startTermPosition + 2 - m_checked, character)); + return; + } + case 4: { + BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar)); + load32WithUnalignedHalfWords(address, character); + break; + } + } + } else { + switch (numberCharacters) { + case 1: + op.m_jumps.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + return; + case 2: + BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar)); + load32WithUnalignedHalfWords(address, character); + break; + } } - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { - readCharacter(term->inputPosition - m_checked, character); - or32(TrustedImm32(32), character); - op.m_jumps.append(branch32(NotEqual, character, Imm32(Unicode::toLower(ch)))); - } else { - ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch))); - op.m_jumps.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked)); - } + if (ignoreCaseMask) + or32(Imm32(ignoreCaseMask), character); + op.m_jumps.append(branch32(NotEqual, character, Imm32(allCharacters | ignoreCaseMask))); + return; } void backtrackPatternCharacterOnce(size_t opIndex) { @@ -746,19 +860,25 @@ class YarrGenerator : private MacroAssembler { const RegisterID countRegister = regT1; move(index, countRegister); - sub32(Imm32(term->quantityCount), countRegister); + sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); Label loop(this); - BaseIndex address(input, countRegister, TimesTwo, (term->inputPosition - m_checked + term->quantityCount) * sizeof(UChar)); + BaseIndex address(input, countRegister, m_charScale, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(m_charSize == Char8 ? sizeof(char) : sizeof(UChar))).unsafeGet()); - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + if (m_charSize == Char8) + load8(address, character); + else load16(address, character); - or32(TrustedImm32(32), character); - op.m_jumps.append(branch32(NotEqual, character, Imm32(Unicode::toLower(ch)))); - } else { - ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch))); - op.m_jumps.append(branch16(NotEqual, address, Imm32(ch))); + + // For case-insesitive compares, non-ascii characters that have different + // upper & lower case representations are converted to a character class. + ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch)); + if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { + or32(TrustedImm32(0x20), character); + ch |= 0x20; } + + op.m_jumps.append(branch32(NotEqual, character, Imm32(ch))); add32(TrustedImm32(1), countRegister); branch32(NotEqual, countRegister, index).linkTo(loop, this); } @@ -778,30 +898,25 @@ class YarrGenerator : private MacroAssembler { move(TrustedImm32(0), countRegister); - JumpList failures; - Label loop(this); - failures.append(atEndOfInput()); - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { - readCharacter(term->inputPosition - m_checked, character); - or32(TrustedImm32(32), character); - failures.append(branch32(NotEqual, character, Imm32(Unicode::toLower(ch)))); - } else { - ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch))); - failures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked)); + // Unless have a 16 bit pattern character and an 8 bit string - short circuit + if (!((ch > 0xff) && (m_charSize == Char8))) { + JumpList failures; + Label loop(this); + failures.append(atEndOfInput()); + failures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + if (term->quantityCount == quantifyInfinite) + jump(loop); + else + branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); + + failures.link(this); } - - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - if (term->quantityCount == quantifyInfinite) - jump(loop); - else - branch32(NotEqual, countRegister, Imm32(term->quantityCount)).linkTo(loop, this); - - failures.link(this); op.m_reentry = label(); storeToFrame(countRegister, term->frameLocation); - } void backtrackPatternCharacterGreedy(size_t opIndex) { @@ -839,30 +954,25 @@ class YarrGenerator : private MacroAssembler { const RegisterID character = regT0; const RegisterID countRegister = regT1; - JumpList nonGreedyFailures; - m_backtrackingState.link(this); loadFromFrame(term->frameLocation, countRegister); - nonGreedyFailures.append(atEndOfInput()); - if (term->quantityCount != quantifyInfinite) - nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount))); - if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) { - readCharacter(term->inputPosition - m_checked, character); - or32(TrustedImm32(32), character); - nonGreedyFailures.append(branch32(NotEqual, character, Imm32(Unicode::toLower(ch)))); - } else { - ASSERT(!m_pattern.m_ignoreCase || (Unicode::toLower(ch) == Unicode::toUpper(ch))); - nonGreedyFailures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked)); + // Unless have a 16 bit pattern character and an 8 bit string - short circuit + if (!((ch > 0xff) && (m_charSize == Char8))) { + JumpList nonGreedyFailures; + nonGreedyFailures.append(atEndOfInput()); + if (term->quantityCount != quantifyInfinite) + nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); + nonGreedyFailures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character)); + + add32(TrustedImm32(1), countRegister); + add32(TrustedImm32(1), index); + + jump(op.m_reentry); + nonGreedyFailures.link(this); } - add32(TrustedImm32(1), countRegister); - add32(TrustedImm32(1), index); - - jump(op.m_reentry); - - nonGreedyFailures.link(this); sub32(countRegister, index); m_backtrackingState.fallthrough(); } @@ -875,7 +985,7 @@ class YarrGenerator : private MacroAssembler { const RegisterID character = regT0; JumpList matchDest; - readCharacter((term->inputPosition - m_checked), character); + readCharacter(term->inputPosition - m_checked, character); matchCharacterClass(character, matchDest, term->characterClass); if (term->invert()) @@ -899,11 +1009,14 @@ class YarrGenerator : private MacroAssembler { const RegisterID countRegister = regT1; move(index, countRegister); - sub32(Imm32(term->quantityCount), countRegister); + sub32(Imm32(term->quantityCount.unsafeGet()), countRegister); Label loop(this); JumpList matchDest; - load16(BaseIndex(input, countRegister, TimesTwo, (term->inputPosition - m_checked + term->quantityCount) * sizeof(UChar)), character); + if (m_charSize == Char8) + load8(BaseIndex(input, countRegister, TimesOne, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(char))).unsafeGet()), character); + else + load16(BaseIndex(input, countRegister, TimesTwo, (Checked(term->inputPosition - m_checked + Checked(term->quantityCount)) * static_cast(sizeof(UChar))).unsafeGet()), character); matchCharacterClass(character, matchDest, term->characterClass); if (term->invert()) @@ -949,7 +1062,7 @@ class YarrGenerator : private MacroAssembler { add32(TrustedImm32(1), countRegister); add32(TrustedImm32(1), index); if (term->quantityCount != quantifyInfinite) { - branch32(NotEqual, countRegister, Imm32(term->quantityCount)).linkTo(loop, this); + branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this); failures.append(jump()); } else jump(loop); @@ -998,11 +1111,10 @@ class YarrGenerator : private MacroAssembler { m_backtrackingState.link(this); - Label backtrackBegin(this); loadFromFrame(term->frameLocation, countRegister); nonGreedyFailures.append(atEndOfInput()); - nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount))); + nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet()))); JumpList matchDest; readCharacter(term->inputPosition - m_checked, character); @@ -1025,6 +1137,67 @@ class YarrGenerator : private MacroAssembler { m_backtrackingState.fallthrough(); } + void generateDotStarEnclosure(size_t opIndex) + { + YarrOp& op = m_ops[opIndex]; + PatternTerm* term = op.m_term; + + const RegisterID character = regT0; + const RegisterID matchPos = regT1; + + JumpList foundBeginningNewLine; + JumpList saveStartIndex; + JumpList foundEndingNewLine; + + ASSERT(!m_pattern.m_body->m_hasFixedSize); + getMatchStart(matchPos); + + saveStartIndex.append(branchTest32(Zero, matchPos)); + Label findBOLLoop(this); + sub32(TrustedImm32(1), matchPos); + if (m_charSize == Char8) + load8(BaseIndex(input, matchPos, TimesOne, 0), character); + else + load16(BaseIndex(input, matchPos, TimesTwo, 0), character); + matchCharacterClass(character, foundBeginningNewLine, m_pattern.newlineCharacterClass()); + branchTest32(NonZero, matchPos).linkTo(findBOLLoop, this); + saveStartIndex.append(jump()); + + foundBeginningNewLine.link(this); + add32(TrustedImm32(1), matchPos); // Advance past newline + saveStartIndex.link(this); + + if (!m_pattern.m_multiline && term->anchors.bolAnchor) + op.m_jumps.append(branchTest32(NonZero, matchPos)); + + ASSERT(!m_pattern.m_body->m_hasFixedSize); + setMatchStart(matchPos); + + move(index, matchPos); + + Label findEOLLoop(this); + foundEndingNewLine.append(branch32(Equal, matchPos, length)); + if (m_charSize == Char8) + load8(BaseIndex(input, matchPos, TimesOne, 0), character); + else + load16(BaseIndex(input, matchPos, TimesTwo, 0), character); + matchCharacterClass(character, foundEndingNewLine, m_pattern.newlineCharacterClass()); + add32(TrustedImm32(1), matchPos); + jump(findEOLLoop); + + foundEndingNewLine.link(this); + + if (!m_pattern.m_multiline && term->anchors.eolAnchor) + op.m_jumps.append(branch32(NotEqual, matchPos, length)); + + move(matchPos, index); + } + + void backtrackDotStarEnclosure(size_t opIndex) + { + backtrackTermDefault(opIndex); + } + // Code generation/backtracking for simple terms // (pattern characters, character classes, and assertions). // These methods farm out work to the set of functions above. @@ -1089,6 +1262,9 @@ class YarrGenerator : private MacroAssembler { case PatternTerm::TypeBackReference: m_shouldFallBack = true; break; + case PatternTerm::TypeDotStarEnclosure: + generateDotStarEnclosure(opIndex); + break; } } void backtrackTerm(size_t opIndex) @@ -1149,6 +1325,11 @@ class YarrGenerator : private MacroAssembler { case PatternTerm::TypeParenthesesSubpattern: case PatternTerm::TypeParentheticalAssertion: ASSERT_NOT_REACHED(); + + case PatternTerm::TypeDotStarEnclosure: + backtrackDotStarEnclosure(opIndex); + break; + case PatternTerm::TypeBackReference: m_shouldFallBack = true; break; @@ -1213,8 +1394,7 @@ class YarrGenerator : private MacroAssembler { // Adjust the stack pointer to remove the pattern's frame. #if !WTF_CPU_SPARC - if (m_pattern.m_body->m_callFrameSize) - addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); + removeCallFrame(); #endif // Load appropriate values into the return register and the first output @@ -1225,10 +1405,14 @@ class YarrGenerator : private MacroAssembler { move(index, returnRegister); if (priorAlternative->m_minimumSize) sub32(Imm32(priorAlternative->m_minimumSize), returnRegister); - store32(returnRegister, output); + if (compileMode == IncludeSubpatterns) + store32(returnRegister, output); } else - load32(Address(output), returnRegister); - store32(index, Address(output, 4)); + getMatchStart(returnRegister); + if (compileMode == IncludeSubpatterns) + store32(index, Address(output, 4)); + move(index, returnRegister2); + generateReturn(); // This is the divide between the tail of the prior alternative, above, and @@ -1247,7 +1431,7 @@ class YarrGenerator : private MacroAssembler { sub32(Imm32(priorAlternative->m_minimumSize - alternative->m_minimumSize), index); } else if (op.m_nextOp == notFound) { // This is the reentry point for the End of 'once through' alternatives, - // jumped to when the las alternative fails to match. + // jumped to when the last alternative fails to match. op.m_reentry = label(); sub32(Imm32(priorAlternative->m_minimumSize), index); } @@ -1286,7 +1470,7 @@ class YarrGenerator : private MacroAssembler { op.m_checkAdjust -= disjunction->m_minimumSize; if (op.m_checkAdjust) op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust)); - + m_checked += op.m_checkAdjust; break; } @@ -1305,6 +1489,12 @@ class YarrGenerator : private MacroAssembler { op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); } + if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { + // If the previous alternative matched without consuming characters then + // backtrack to try to match while consumming some input. + op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + } + // If we reach here then the last alternative has matched - jump to the // End node, to skip over any further alternatives. // @@ -1349,6 +1539,12 @@ class YarrGenerator : private MacroAssembler { op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation); } + if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) { + // If the previous alternative matched without consuming characters then + // backtrack to try to match while consumming some input. + op.m_zeroLengthMatch = branch32(Equal, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + } + // If this set of alternatives contains more than one alternative, // then the Next nodes will have planted jumps to the End, and added // them to this node's m_jumps list. @@ -1399,30 +1595,34 @@ class YarrGenerator : private MacroAssembler { // FIXME: could avoid offsetting this value in JIT code, apply // offsets only afterwards, at the point the results array is // being accessed. - if (term->capture()) { - int offsetId = term->parentheses.subpatternId << 1; + if (term->capture() && compileMode == IncludeSubpatterns) { int inputOffset = term->inputPosition - m_checked; if (term->quantityType == QuantifierFixedCount) inputOffset -= term->parentheses.disjunction->m_minimumSize; if (inputOffset) { move(index, indexTemporary); add32(Imm32(inputOffset), indexTemporary); - store32(indexTemporary, Address(output, offsetId * sizeof(int))); + setSubpatternStart(indexTemporary, term->parentheses.subpatternId); } else - store32(index, Address(output, offsetId * sizeof(int))); + setSubpatternStart(index, term->parentheses.subpatternId); } break; } case OpParenthesesSubpatternOnceEnd: { PatternTerm* term = op.m_term; - unsigned parenthesesFrameLocation = term->frameLocation; const RegisterID indexTemporary = regT0; ASSERT(term->quantityCount == 1); - // For Greedy/NonGreedy quantified parentheses, we must reject zero length - // matches. If the minimum size is know to be non-zero we need not check. - if (term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) - op.m_jumps.append(branch32(Equal, index, Address(stackPointerRegister, parenthesesFrameLocation * sizeof(void*)))); +#ifndef NDEBUG + // Runtime ASSERT to make sure that the nested alternative handled the + // "no input consumed" check. + if (term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) { + Jump pastBreakpoint; + pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + breakpoint(); + pastBreakpoint.link(this); + } +#endif // If the parenthese are capturing, store the ending index value to the // captures array, offsetting as necessary. @@ -1430,15 +1630,14 @@ class YarrGenerator : private MacroAssembler { // FIXME: could avoid offsetting this value in JIT code, apply // offsets only afterwards, at the point the results array is // being accessed. - if (term->capture()) { - int offsetId = (term->parentheses.subpatternId << 1) + 1; + if (term->capture() && compileMode == IncludeSubpatterns) { int inputOffset = term->inputPosition - m_checked; if (inputOffset) { move(index, indexTemporary); add32(Imm32(inputOffset), indexTemporary); - store32(indexTemporary, Address(output, offsetId * sizeof(int))); + setSubpatternEnd(indexTemporary, term->parentheses.subpatternId); } else - store32(index, Address(output, offsetId * sizeof(int))); + setSubpatternEnd(index, term->parentheses.subpatternId); } // If the parentheses are quantified Greedy then add a label to jump back @@ -1469,15 +1668,21 @@ class YarrGenerator : private MacroAssembler { break; } case OpParenthesesSubpatternTerminalEnd: { + YarrOp& beginOp = m_ops[op.m_previousOp]; +#ifndef NDEBUG PatternTerm* term = op.m_term; - // Check for zero length matches - if the match is non-zero, then we - // can accept it & loop back up to the head of the subpattern. - YarrOp& beginOp = m_ops[op.m_previousOp]; - branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*)), beginOp.m_reentry); + // Runtime ASSERT to make sure that the nested alternative handled the + // "no input consumed" check. + Jump pastBreakpoint; + pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*))); + breakpoint(); + pastBreakpoint.link(this); +#endif - // Reject the match - backtrack back into the subpattern. - op.m_jumps.append(jump()); + // We know that the match is non-zero, we can accept it and + // loop back up to the head of the subpattern. + jump(beginOp.m_reentry); // This is the entry point to jump to when we stop matching - we will // do so once the subpattern cannot match any more. @@ -1523,10 +1728,10 @@ class YarrGenerator : private MacroAssembler { case OpMatchFailed: #if !WTF_CPU_SPARC - if (m_pattern.m_body->m_callFrameSize) - addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); + removeCallFrame(); #endif - move(TrustedImm32(-1), returnRegister); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); generateReturn(); break; } @@ -1621,14 +1826,14 @@ class YarrGenerator : private MacroAssembler { // If the pattern size is not fixed, then store the start index, for use if we match. if (!m_pattern.m_body->m_hasFixedSize) { if (alternative->m_minimumSize == 1) - store32(index, Address(output)); + setMatchStart(index); else { move(index, regT0); if (alternative->m_minimumSize) sub32(Imm32(alternative->m_minimumSize - 1), regT0); else - add32(Imm32(1), regT0); - store32(regT0, Address(output)); + add32(TrustedImm32(1), regT0); + setMatchStart(regT0); } } @@ -1714,7 +1919,7 @@ class YarrGenerator : private MacroAssembler { // disjunction is 0, e.g. /a*|b/). if (needsToUpdateMatchStart && alternative->m_minimumSize == 1) { // index is already incremented by 1, so just store it now! - store32(index, Address(output)); + setMatchStart(index); needsToUpdateMatchStart = false; } @@ -1726,7 +1931,7 @@ class YarrGenerator : private MacroAssembler { if (alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) { // If the last alternative had the same minimum size as the disjunction, // just simply increment input pos by 1, no adjustment based on minimum size. - add32(Imm32(1), index); + add32(TrustedImm32(1), index); } else { // If the minumum for the last alternative was one greater than than that // for the disjunction, we're already progressed by 1, nothing to do! @@ -1738,11 +1943,11 @@ class YarrGenerator : private MacroAssembler { if (needsToUpdateMatchStart) { if (!m_pattern.m_body->m_minimumSize) - store32(index, Address(output)); + setMatchStart(index); else { move(index, regT0); sub32(Imm32(m_pattern.m_body->m_minimumSize), regT0); - store32(regT0, Address(output)); + setMatchStart(regT0); } } @@ -1765,10 +1970,10 @@ class YarrGenerator : private MacroAssembler { matchFailed.link(this); #if !WTF_CPU_SPARC - if (m_pattern.m_body->m_callFrameSize) - addPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); + removeCallFrame(); #endif - move(TrustedImm32(-1), returnRegister); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); generateReturn(); break; } @@ -1833,7 +2038,7 @@ class YarrGenerator : private MacroAssembler { // An alternative that is not the last should jump to its successor. jump(nextOp.m_reentry); } else if (!isBegin) { - // The last of more than one alternatives must jump back to the begnning. + // The last of more than one alternatives must jump back to the beginning. nextOp.m_jumps.append(jump()); } else { // A single alternative on its own can fall through. @@ -1845,12 +2050,16 @@ class YarrGenerator : private MacroAssembler { // An alternative that is not the last should jump to its successor. m_backtrackingState.linkTo(nextOp.m_reentry, this); } else if (!isBegin) { - // The last of more than one alternatives must jump back to the begnning. + // The last of more than one alternatives must jump back to the beginning. m_backtrackingState.takeBacktracksToJumpList(nextOp.m_jumps, this); } // In the case of a single alternative on its own do nothing - it can fall through. } + // If there is a backtrack jump from a zero length match link it here. + if (op.m_zeroLengthMatch.isSet()) + m_backtrackingState.append(op.m_zeroLengthMatch); + // At this point we've handled the backtracking back into this node. // Now link any backtracks that need to jump to here. @@ -1883,6 +2092,10 @@ class YarrGenerator : private MacroAssembler { case OpNestedAlternativeEnd: { PatternTerm* term = op.m_term; + // If there is a backtrack jump from a zero length match link it here. + if (op.m_zeroLengthMatch.isSet()) + m_backtrackingState.append(op.m_zeroLengthMatch); + // If we backtrack into the end of a simple subpattern do nothing; // just continue through into the last alternative. If we backtrack // into the end of a non-simple set of alterntives we need to jump @@ -1927,12 +2140,12 @@ class YarrGenerator : private MacroAssembler { ASSERT(term->quantityCount == 1); // We only need to backtrack to thispoint if capturing or greedy. - if (term->capture() || term->quantityType == QuantifierGreedy) { + if ((term->capture() && compileMode == IncludeSubpatterns) || term->quantityType == QuantifierGreedy) { m_backtrackingState.link(this); // If capturing, clear the capture (we only need to reset start). - if (term->capture()) - store32(TrustedImm32(-1), Address(output, (term->parentheses.subpatternId << 1) * sizeof(int))); + if (term->capture() && compileMode == IncludeSubpatterns) + clearSubpatternStart(term->parentheses.subpatternId); // If Greedy, jump to the end. if (term->quantityType == QuantifierGreedy) { @@ -2111,7 +2324,7 @@ class YarrGenerator : private MacroAssembler { } } } - + // Select the 'Terminal' nodes. parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin; parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd; @@ -2334,22 +2547,25 @@ class YarrGenerator : private MacroAssembler { push(X86Registers::edi); push(X86Registers::esi); // load output into edi (2 = saved ebp + return address). - #if WTF_COMPILER_MSVC || WTF_COMPILER_SUNCC +# if WTF_COMPILER_MSVC || WTF_COMPILER_SUNCC loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), input); loadPtr(Address(X86Registers::ebp, 3 * sizeof(void*)), index); loadPtr(Address(X86Registers::ebp, 4 * sizeof(void*)), length); - loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output); - #else - loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); - #endif + if (compileMode == IncludeSubpatterns) + loadPtr(Address(X86Registers::ebp, 5 * sizeof(void*)), output); +# else + if (compileMode == IncludeSubpatterns) + loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); +# endif #elif WTF_CPU_ARM push(ARMRegisters::r4); push(ARMRegisters::r5); push(ARMRegisters::r6); -#if WTF_CPU_ARM_TRADITIONAL +# if WTF_CPU_ARM_TRADITIONAL push(ARMRegisters::r8); // scratch register -#endif - move(ARMRegisters::r3, output); +# endif + if (compileMode == IncludeSubpatterns) + move(ARMRegisters::r3, output); #elif WTF_CPU_SH4 push(SH4Registers::r11); push(SH4Registers::r13); @@ -2371,9 +2587,9 @@ class YarrGenerator : private MacroAssembler { pop(X86Registers::ebx); pop(X86Registers::ebp); #elif WTF_CPU_ARM -#if WTF_CPU_ARM_TRADITIONAL +# if WTF_CPU_ARM_TRADITIONAL pop(ARMRegisters::r8); // scratch register -#endif +# endif pop(ARMRegisters::r6); pop(ARMRegisters::r5); pop(ARMRegisters::r4); @@ -2390,8 +2606,10 @@ class YarrGenerator : private MacroAssembler { } public: - YarrGenerator(YarrPattern& pattern) + YarrGenerator(YarrPattern& pattern, YarrCharSize charSize) : m_pattern(pattern) + , m_charSize(charSize) + , m_charScale(m_charSize == Char8 ? TimesOne: TimesTwo) , m_shouldFallBack(false) , m_checked(0) { @@ -2401,13 +2619,21 @@ public: { generateEnter(); - if (!m_pattern.m_body->m_hasFixedSize) - store32(index, Address(output)); + Jump hasInput = checkInput(); + move(TrustedImmPtr((void*)WTF::notFound), returnRegister); + move(TrustedImm32(0), returnRegister2); + generateReturn(); + hasInput.link(this); -#if !WTF_CPU_SPARC - if (m_pattern.m_body->m_callFrameSize) - subPtr(Imm32(m_pattern.m_body->m_callFrameSize * sizeof(void*)), stackPointerRegister); -#endif + if (compileMode == IncludeSubpatterns) { + for (unsigned i = 0; i < m_pattern.m_numSubpatterns + 1; ++i) + store32(TrustedImm32(-1), Address(output, (i << 1) * sizeof(int))); + } + + if (!m_pattern.m_body->m_hasFixedSize) + setMatchStart(index); + + initCallFrame(); // Compile the pattern to the internal 'YarrOp' representation. opCompileBody(m_pattern.m_body); @@ -2428,13 +2654,32 @@ public: bool ok; LinkBuffer linkBuffer(this, globalData->regexAllocator, &pool, &ok, REGEXP_CODE); m_backtrackingState.linkDataLabels(linkBuffer); - jitObject.set(linkBuffer.finalizeCode()); + + if (compileMode == MatchOnly) { +#if YARR_8BIT_CHAR_SUPPORT + if (m_charSize == Char8) + jitObject.set8BitCodeMatchOnly(linkBuffer.finalizeCode()); + else +#endif + jitObject.set16BitCodeMatchOnly(linkBuffer.finalizeCode()); + } else { +#if YARR_8BIT_CHAR_SUPPORT + if (m_charSize == Char8) + jitObject.set8BitCode(linkBuffer.finalizeCode()); + else +#endif + jitObject.set16BitCode(linkBuffer.finalizeCode()); + } jitObject.setFallBack(m_shouldFallBack); } private: YarrPattern& m_pattern; + YarrCharSize m_charSize; + + Scale m_charScale; + // Used to detect regular expression constructs that are not currently // supported in the JIT; fall back to the interpreter when this is detected. bool m_shouldFallBack; @@ -2458,14 +2703,12 @@ private: BacktrackingState m_backtrackingState; }; -void jitCompile(YarrPattern& pattern, JSGlobalData* globalData, YarrCodeBlock& jitObject) +void jitCompile(YarrPattern& pattern, YarrCharSize charSize, JSGlobalData* globalData, YarrCodeBlock& jitObject, YarrJITCompileMode mode) { - YarrGenerator(pattern).compile(globalData, jitObject); -} - -int execute(YarrCodeBlock& jitObject, const UChar* input, unsigned start, unsigned length, int* output) -{ - return jitObject.execute(input, start, length, output); + if (mode == MatchOnly) + YarrGenerator(pattern, charSize).compile(globalData, jitObject); + else + YarrGenerator(pattern, charSize).compile(globalData, jitObject); } }} diff --git a/js/src/yarr/YarrJIT.h b/js/src/yarr/YarrJIT.h index 2039105e934b..f73dec023522 100644 --- a/js/src/yarr/YarrJIT.h +++ b/js/src/yarr/YarrJIT.h @@ -1,7 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: +/* vim: set ts=4 sw=4 tw=99 et: * - * ***** BEGIN LICENSE BLOCK ***** * Copyright (C) 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,8 +22,7 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #ifndef YarrJIT_h #define YarrJIT_h @@ -35,16 +32,19 @@ #if ENABLE_YARR_JIT #include "assembler/assembler/MacroAssembler.h" + +#include "MatchResult.h" +#include "Yarr.h" #include "YarrPattern.h" #if WTF_CPU_X86 && !WTF_COMPILER_MSVC && !WTF_COMPILER_SUNCC #define YARR_CALL __attribute__ ((regparm (3))) #else #define YARR_CALL -#endif -#if JS_TRACE_LOGGING -#include "TraceLogging.h" +# if JS_TRACE_LOGGING +# include "TraceLogging.h" +# endif #endif namespace JSC { @@ -55,7 +55,16 @@ class ExecutablePool; namespace Yarr { class YarrCodeBlock { - typedef int (*YarrJITCode)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; +#if defined(WTF_CPU_X86_64) && !defined(_WIN64) + typedef MatchResult JITMatchResult; +#else + typedef EncodedMatchResult JITMatchResult; +#endif + + typedef JITMatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef JITMatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL; + typedef JITMatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL; + typedef JITMatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL; public: YarrCodeBlock() @@ -69,32 +78,118 @@ public: void setFallBack(bool fallback) { m_needFallBack = fallback; } bool isFallBack() { return m_needFallBack; } - void set(MacroAssembler::CodeRef ref) { m_ref = ref; } - int execute(const UChar* input, unsigned start, unsigned length, int* output) - { -#if JS_TRACE_LOGGING - js::AutoTraceLog logger(js::TraceLogging::defaultLogger(), - js::TraceLogging::YARR_YIT_START, - js::TraceLogging::YARR_YIT_STOP); +#ifdef YARR_8BIT_CHAR_SUPPORT + bool has8BitCode() { return m_ref8.size(); } + void set8BitCode(MacroAssemblerCodeRef ref) { m_ref8 = ref; } + bool has8BitCodeMatchOnly() { return m_matchOnly8.size(); } + void set8BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly8 = matchOnly; } #endif - return JS_EXTENSION((reinterpret_cast(m_ref.m_code.executableAddress()))(input, start, length, output)); + bool has16BitCode() { return m_ref16.size(); } + void set16BitCode(MacroAssemblerCodeRef ref) { m_ref16 = ref; } + + bool has16BitCodeMatchOnly() { return m_matchOnly16.size(); } + void set16BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly16 = matchOnly; } + +#if YARR_8BIT_CHAR_SUPPORT + MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output) + { + ASSERT(has8BitCode()); + +#if JS_TRACE_LOGGING + js::AutoTraceLog logger(js::TraceLogging::defaultLogger(), + js::TraceLogging::YARR_JIT_START, + js::TraceLogging::YARR_JIT_STOP); +#endif + + return MatchResult(reinterpret_cast(m_ref8.code().executableAddress())(input, start, length, output)); + } + + MatchResult execute(const LChar* input, unsigned start, unsigned length) + { + ASSERT(has8BitCodeMatchOnly()); + +#if JS_TRACE_LOGGING + js::AutoTraceLog logger(js::TraceLogging::defaultLogger(), + js::TraceLogging::YARR_JIT_START, + js::TraceLogging::YARR_JIT_STOP); +#endif + + return MatchResult(reinterpret_cast(m_matchOnly8.code().executableAddress())(input, start, length)); + } +#endif + + MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output) + { + ASSERT(has16BitCode()); + +#if JS_TRACE_LOGGING + js::AutoTraceLog logger(js::TraceLogging::defaultLogger(), + js::TraceLogging::YARR_JIT_START, + js::TraceLogging::YARR_JIT_STOP); +#endif + + YarrJITCode16 fn = JS_FUNC_TO_DATA_PTR(YarrJITCode16, m_ref16.code().executableAddress()); + return MatchResult(fn(input, start, length, output)); + } + + MatchResult execute(const UChar* input, unsigned start, unsigned length) + { + ASSERT(has16BitCodeMatchOnly()); + +#if JS_TRACE_LOGGING + js::AutoTraceLog logger(js::TraceLogging::defaultLogger(), + js::TraceLogging::YARR_JIT_START, + js::TraceLogging::YARR_JIT_STOP); +#endif + + YarrJITCodeMatchOnly16 fn = JS_FUNC_TO_DATA_PTR(YarrJITCodeMatchOnly16, m_matchOnly16.code().executableAddress()); + return MatchResult(fn(input, start, length)); } #if ENABLE_REGEXP_TRACING - void *getAddr() { return m_ref.m_code.executableAddress(); } + void *getAddr() { return m_ref.code().executableAddress(); } #endif - void release() { m_ref.release(); } + void clear() + { +#ifdef YARR_8BIT_CHAR_SUPPORT + m_ref8 = MacroAssemblerCodeRef(); + m_matchOnly8 = MacroAssemblerCodeRef(); +#endif + + m_ref16 = MacroAssemblerCodeRef(); + m_matchOnly16 = MacroAssemblerCodeRef(); + m_needFallBack = false; + } + + void release() { +#ifdef YARR_8BIT_CHAR_SUPPORT + m_ref8.release(); + m_matchOnly8.release(); +#endif + + m_ref16.release(); + m_matchOnly16.release(); + } private: - MacroAssembler::CodeRef m_ref; +#ifdef YARR_8BIT_CHAR_SUPPORT + MacroAssemblerCodeRef m_ref8; + MacroAssemblerCodeRef m_matchOnly8; +#endif + + MacroAssemblerCodeRef m_ref16; + MacroAssemblerCodeRef m_matchOnly16; bool m_needFallBack; }; -void jitCompile(YarrPattern&, JSGlobalData*, YarrCodeBlock& jitObject); -int execute(YarrCodeBlock& jitObject, const UChar* input, unsigned start, unsigned length, int* output); +enum YarrJITCompileMode { + MatchOnly, + IncludeSubpatterns +}; +void jitCompile(YarrPattern&, YarrCharSize, JSGlobalData*, YarrCodeBlock& jitObject, YarrJITCompileMode = IncludeSubpatterns); } } // namespace JSC::Yarr diff --git a/js/src/yarr/YarrParser.h b/js/src/yarr/YarrParser.h index 2d499c92468c..f5d1aca6095c 100644 --- a/js/src/yarr/YarrParser.h +++ b/js/src/yarr/YarrParser.h @@ -1,7 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: +/* vim: set ts=4 sw=4 tw=99 et: * - * ***** BEGIN LICENSE BLOCK ***** * Copyright (C) 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,8 +22,7 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #ifndef YarrParser_h #define YarrParser_h @@ -34,8 +31,6 @@ namespace JSC { namespace Yarr { -#define REGEXP_ERROR_PREFIX "Invalid regular expression: " - enum BuiltInCharacterClassID { DigitClassID, SpaceClassID, @@ -44,11 +39,11 @@ enum BuiltInCharacterClassID { }; // The Parser class should not be used directly - only via the Yarr::parse() method. -template +template class Parser { private: template - friend ErrorCode parse(FriendDelegate& delegate, const UString& pattern, unsigned backReferenceLimit); + friend ErrorCode parse(FriendDelegate&, const String& pattern, unsigned backReferenceLimit); /* * CharacterClassParserDelegate: @@ -159,7 +154,6 @@ private: // the end of the range to be a single character. m_err = CharacterClassInvalidRange; return; - case AfterCharacterClassHyphen: m_delegate.atomCharacterClassBuiltIn(classID, invert); m_state = Empty; @@ -185,8 +179,8 @@ private: // parseEscape() should never call these delegate methods when // invoked with inCharacterClass set. - void assertionWordBoundary(bool) { ASSERT_NOT_REACHED(); } - void atomBackReference(unsigned) { ASSERT_NOT_REACHED(); } + NO_RETURN_DUE_TO_ASSERT void assertionWordBoundary(bool) { ASSERT_NOT_REACHED(); } + NO_RETURN_DUE_TO_ASSERT void atomBackReference(unsigned) { ASSERT_NOT_REACHED(); } private: Delegate& m_delegate; @@ -201,7 +195,7 @@ private: UChar m_character; }; - Parser(Delegate& delegate, const UString& pattern, unsigned backReferenceLimit) + Parser(Delegate& delegate, const String& pattern, unsigned backReferenceLimit) : m_delegate(delegate) , m_backReferenceLimit(backReferenceLimit) , m_err(NoError) @@ -211,7 +205,7 @@ private: , m_parenthesesNestingDepth(0) { } - + /* * parseEscape(): * @@ -308,7 +302,7 @@ private: unsigned backReference; if (!consumeNumber(backReference)) - break; + break; if (backReference <= m_backReferenceLimit) { delegate.atomBackReference(backReference); break; @@ -522,7 +516,7 @@ private: ASSERT(!m_err); ASSERT(min <= max); - if (min == unsigned(-1)) { + if (min == UINT_MAX) { m_err = QuantifierTooLarge; return; } @@ -617,8 +611,8 @@ private: unsigned min; if (!consumeNumber(min)) break; + unsigned max = min; - if (tryConsume(',')) { if (peekIsDigit()) { if (!consumeNumber(max)) @@ -671,7 +665,6 @@ private: return m_err; } - // Misc helper functions: typedef unsigned ParseState; @@ -772,7 +765,7 @@ private: Delegate& m_delegate; unsigned m_backReferenceLimit; ErrorCode m_err; - const UChar* m_data; + const CharType* m_data; unsigned m_size; unsigned m_index; unsigned m_parenthesesNestingDepth; @@ -841,9 +834,13 @@ private: */ template -ErrorCode parse(Delegate& delegate, const UString& pattern, unsigned backReferenceLimit = quantifyInfinite) +ErrorCode parse(Delegate& delegate, const String& pattern, unsigned backReferenceLimit = quantifyInfinite) { - return Parser(delegate, pattern, backReferenceLimit).parse(); +#ifdef YARR_8BIT_CHAR_SUPPORT + if (pattern.is8Bit()) + return Parser(delegate, pattern, backReferenceLimit).parse(); +#endif + return Parser(delegate, pattern, backReferenceLimit).parse(); } } } // namespace JSC::Yarr diff --git a/js/src/yarr/YarrPattern.cpp b/js/src/yarr/YarrPattern.cpp index 0fae75b05f0c..50f110ee4e1c 100644 --- a/js/src/yarr/YarrPattern.cpp +++ b/js/src/yarr/YarrPattern.cpp @@ -1,7 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: +/* vim: set ts=4 sw=4 tw=99 et: * - * ***** BEGIN LICENSE BLOCK ***** * Copyright (C) 2009 Apple Inc. All rights reserved. * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged * @@ -25,12 +23,12 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #include "YarrPattern.h" #include "Yarr.h" +#include "YarrCanonicalizeUCS2.h" #include "YarrParser.h" using namespace WTF; @@ -40,9 +38,9 @@ namespace JSC { namespace Yarr { #include "RegExpJitTables.h" #if WTF_CPU_SPARC -#define BASE_FRAME_SIZE 24 +# define BASE_FRAME_SIZE 24 #else -#define BASE_FRAME_SIZE 0 +# define BASE_FRAME_SIZE 0 #endif class CharacterClassConstructor { @@ -74,32 +72,43 @@ public: void putChar(UChar ch) { + // Handle ascii cases. if (ch <= 0x7f) { if (m_isCaseInsensitive && isASCIIAlpha(ch)) { addSorted(m_matches, toASCIIUpper(ch)); addSorted(m_matches, toASCIILower(ch)); } else addSorted(m_matches, ch); - } else { - UChar upper, lower; - if (m_isCaseInsensitive && ((upper = Unicode::toUpper(ch)) != (lower = Unicode::toLower(ch)))) { - addSorted(m_matchesUnicode, upper); - addSorted(m_matchesUnicode, lower); - } else - addSorted(m_matchesUnicode, ch); + return; } + + // Simple case, not a case-insensitive match. + if (!m_isCaseInsensitive) { + addSorted(m_matchesUnicode, ch); + return; + } + + // Add multiple matches, if necessary. + UCS2CanonicalizationRange* info = rangeInfoFor(ch); + if (info->type == CanonicalizeUnique) + addSorted(m_matchesUnicode, ch); + else + putUnicodeIgnoreCase(ch, info); } - // returns true if this character has another case, and 'ch' is the upper case form. - static inline bool isUnicodeUpper(UChar ch) + void putUnicodeIgnoreCase(UChar ch, UCS2CanonicalizationRange* info) { - return ch != Unicode::toLower(ch); - } - - // returns true if this character has another case, and 'ch' is the lower case form. - static inline bool isUnicodeLower(UChar ch) - { - return ch != Unicode::toUpper(ch); + ASSERT(m_isCaseInsensitive); + ASSERT(ch > 0x7f); + ASSERT(ch >= info->begin && ch <= info->end); + ASSERT(info->type != CanonicalizeUnique); + if (info->type == CanonicalizeSet) { + for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) + addSorted(m_matchesUnicode, ch); + } else { + addSorted(m_matchesUnicode, ch); + addSorted(m_matchesUnicode, getCanonicalPair(info, ch)); + } } void putRange(UChar lo, UChar hi) @@ -116,48 +125,69 @@ public: addSortedRange(m_ranges, std::max(asciiLo, 'a')+('A'-'a'), std::min(asciiHi, 'z')+('A'-'a')); } } - if (hi >= 0x80) { - uint32_t unicodeCurr = std::max(lo, (UChar)0x80); - addSortedRange(m_rangesUnicode, unicodeCurr, hi); - - if (m_isCaseInsensitive) { - while (unicodeCurr <= hi) { - // If the upper bound of the range (hi) is 0xffff, the increments to - // unicodeCurr in this loop may take it to 0x10000. This is fine - // (if so we won't re-enter the loop, since the loop condition above - // will definitely fail) - but this does mean we cannot use a UChar - // to represent unicodeCurr, we must use a 32-bit value instead. - ASSERT(unicodeCurr <= 0xffff); + if (hi <= 0x7f) + return; - if (isUnicodeUpper(unicodeCurr)) { - UChar lowerCaseRangeBegin = Unicode::toLower(unicodeCurr); - UChar lowerCaseRangeEnd = lowerCaseRangeBegin; - while ((++unicodeCurr <= hi) && isUnicodeUpper(unicodeCurr) && (Unicode::toLower(unicodeCurr) == (lowerCaseRangeEnd + 1))) - lowerCaseRangeEnd++; - addSortedRange(m_rangesUnicode, lowerCaseRangeBegin, lowerCaseRangeEnd); - } else if (isUnicodeLower(unicodeCurr)) { - UChar upperCaseRangeBegin = Unicode::toUpper(unicodeCurr); - UChar upperCaseRangeEnd = upperCaseRangeBegin; - while ((++unicodeCurr <= hi) && isUnicodeLower(unicodeCurr) && (Unicode::toUpper(unicodeCurr) == (upperCaseRangeEnd + 1))) - upperCaseRangeEnd++; - addSortedRange(m_rangesUnicode, upperCaseRangeBegin, upperCaseRangeEnd); - } else - ++unicodeCurr; - } + lo = std::max(lo, (UChar)0x80); + addSortedRange(m_rangesUnicode, lo, hi); + + if (!m_isCaseInsensitive) + return; + + UCS2CanonicalizationRange* info = rangeInfoFor(lo); + while (true) { + // Handle the range [lo .. end] + UChar end = std::min(info->end, hi); + + switch (info->type) { + case CanonicalizeUnique: + // Nothing to do - no canonical equivalents. + break; + case CanonicalizeSet: { + UChar ch; + for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set) + addSorted(m_matchesUnicode, ch); + break; } - } + case CanonicalizeRangeLo: + addSortedRange(m_rangesUnicode, lo + info->value, end + info->value); + break; + case CanonicalizeRangeHi: + addSortedRange(m_rangesUnicode, lo - info->value, end - info->value); + break; + case CanonicalizeAlternatingAligned: + // Use addSortedRange since there is likely an abutting range to combine with. + if (lo & 1) + addSortedRange(m_rangesUnicode, lo - 1, lo - 1); + if (!(end & 1)) + addSortedRange(m_rangesUnicode, end + 1, end + 1); + break; + case CanonicalizeAlternatingUnaligned: + // Use addSortedRange since there is likely an abutting range to combine with. + if (!(lo & 1)) + addSortedRange(m_rangesUnicode, lo - 1, lo - 1); + if (end & 1) + addSortedRange(m_rangesUnicode, end + 1, end + 1); + break; + } + + if (hi == end) + return; + + ++info; + lo = info->begin; + }; + } CharacterClass* charClass() { CharacterClass* characterClass = js_new(PassRefPtr(0)); - characterClass->m_matches.append(m_matches); - characterClass->m_ranges.append(m_ranges); - characterClass->m_matchesUnicode.append(m_matchesUnicode); - characterClass->m_rangesUnicode.append(m_rangesUnicode); - - reset(); + characterClass->m_matches.swap(m_matches); + characterClass->m_ranges.swap(m_ranges); + characterClass->m_matchesUnicode.swap(m_matchesUnicode); + characterClass->m_rangesUnicode.swap(m_rangesUnicode); return characterClass; } @@ -290,12 +320,21 @@ public: { // We handle case-insensitive checking of unicode characters which do have both // cases by handling them as if they were defined using a CharacterClass. - if (m_pattern.m_ignoreCase && !isASCII(ch) && (Unicode::toUpper(ch) != Unicode::toLower(ch))) { - atomCharacterClassBegin(); - atomCharacterClassAtom(ch); - atomCharacterClassEnd(); - } else + if (!m_pattern.m_ignoreCase || isASCII(ch)) { m_alternative->m_terms.append(PatternTerm(ch)); + return; + } + + UCS2CanonicalizationRange* info = rangeInfoFor(ch); + if (info->type == CanonicalizeUnique) { + m_alternative->m_terms.append(PatternTerm(ch)); + return; + } + + m_characterClassConstructor.putUnicodeIgnoreCase(ch, info); + CharacterClass* newCharacterClass = m_characterClassConstructor.charClass(); + m_pattern.m_userCharacterClasses.append(newCharacterClass); + m_alternative->m_terms.append(PatternTerm(newCharacterClass, false)); } void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert) @@ -486,14 +525,23 @@ public: ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary); ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount)); - // For any assertion with a zero minimum, not matching is valid and has no effect, - // remove it. Otherwise, we need to match as least once, but there is no point - // matching more than once, so remove the quantifier. It is not entirely clear - // from the spec whether or not this behavior is correct, but I believe this - // matches Firefox. :-/ if (term.type == PatternTerm::TypeParentheticalAssertion) { + // If an assertion is quantified with a minimum count of zero, it can simply be removed. + // This arises from the RepeatMatcher behaviour in the spec. Matching an assertion never + // results in any input being consumed, however the continuation passed to the assertion + // (called in steps, 8c and 9 of the RepeatMatcher definition, ES5.1 15.10.2.5) will + // reject all zero length matches (see step 2.1). A match from the continuation of the + // expression will still be accepted regardless (via steps 8a and 11) - the upshot of all + // this is that matches from the assertion are not required, and won't be accepted anyway, + // so no need to ever run it. if (!min) m_alternative->removeLastTerm(); + // We never need to run an assertion more than once. Subsequent interations will be run + // with the same start index (since assertions are non-capturing) and the same captures + // (per step 4 of RepeatMatcher in ES5.1 15.10.2.5), and as such will always produce the + // same result and captures. If the first match succeeds then the subsequent (min - 1) + // matches will too. Any additional optional matches will fail (on the same basis as the + // minimum zero quantified assertions, above), but this will still result in a match. return; } @@ -516,10 +564,11 @@ public: m_alternative = m_alternative->m_parent->addNewAlternative(); } - ErrorCode setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition, unsigned *callFrameSizeOut) + ErrorCode setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition, + unsigned *callFrameSizeOut) { alternative->m_hasFixedSize = true; - unsigned currentInputPosition = initialInputPosition; + Checked currentInputPosition = initialInputPosition; for (unsigned i = 0; i < alternative->m_terms.size(); ++i) { PatternTerm& term = alternative->m_terms[i]; @@ -528,11 +577,11 @@ public: case PatternTerm::TypeAssertionBOL: case PatternTerm::TypeAssertionEOL: case PatternTerm::TypeAssertionWordBoundary: - term.inputPosition = currentInputPosition; + term.inputPosition = currentInputPosition.unsafeGet(); break; case PatternTerm::TypeBackReference: - term.inputPosition = currentInputPosition; + term.inputPosition = currentInputPosition.unsafeGet(); term.frameLocation = currentCallFrameSize; currentCallFrameSize += YarrStackSpaceForBackTrackInfoBackReference; alternative->m_hasFixedSize = false; @@ -542,7 +591,7 @@ public: break; case PatternTerm::TypePatternCharacter: - term.inputPosition = currentInputPosition; + term.inputPosition = currentInputPosition.unsafeGet(); if (term.quantityType != QuantifierFixedCount) { term.frameLocation = currentCallFrameSize; currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter; @@ -552,7 +601,7 @@ public: break; case PatternTerm::TypeCharacterClass: - term.inputPosition = currentInputPosition; + term.inputPosition = currentInputPosition.unsafeGet(); if (term.quantityType != QuantifierFixedCount) { term.frameLocation = currentCallFrameSize; currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass; @@ -567,21 +616,21 @@ public: if (term.quantityCount == 1 && !term.parentheses.isCopy) { if (term.quantityType != QuantifierFixedCount) currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce; - if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition, ¤tCallFrameSize)) + if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), ¤tCallFrameSize)) return error; // If quantity is fixed, then pre-check its minimum size. if (term.quantityType == QuantifierFixedCount) currentInputPosition += term.parentheses.disjunction->m_minimumSize; - term.inputPosition = currentInputPosition; + term.inputPosition = currentInputPosition.unsafeGet(); } else if (term.parentheses.isTerminal) { currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal; - if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition, ¤tCallFrameSize)) + if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), ¤tCallFrameSize)) return error; - term.inputPosition = currentInputPosition; + term.inputPosition = currentInputPosition.unsafeGet(); } else { - term.inputPosition = currentInputPosition; + term.inputPosition = currentInputPosition.unsafeGet(); unsigned dummy; - if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, BASE_FRAME_SIZE, currentInputPosition, &dummy)) + if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, BASE_FRAME_SIZE, currentInputPosition.unsafeGet(), &dummy)) return error; currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses; } @@ -590,15 +639,20 @@ public: break; case PatternTerm::TypeParentheticalAssertion: - term.inputPosition = currentInputPosition; + term.inputPosition = currentInputPosition.unsafeGet(); term.frameLocation = currentCallFrameSize; - if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition, ¤tCallFrameSize)) + if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet(), ¤tCallFrameSize)) return error; break; + + case PatternTerm::TypeDotStarEnclosure: + alternative->m_hasFixedSize = false; + term.inputPosition = initialInputPosition; + break; } } - alternative->m_minimumSize = currentInputPosition - initialInputPosition; + alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet(); *callFrameSizeOut = currentCallFrameSize; return NoError; } @@ -621,10 +675,12 @@ public: maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize); hasFixedSize &= alternative->m_hasFixedSize; } - + + ASSERT(minimumInputSize != UINT_MAX); if (minimumInputSize == UINT_MAX) return PatternTooLarge; - + + ASSERT(minimumInputSize != UINT_MAX); ASSERT(maximumCallFrameSize >= initialCallFrameSize); disjunction->m_hasFixedSize = hasFixedSize; @@ -695,6 +751,87 @@ public: } } + bool containsCapturingTerms(PatternAlternative* alternative, size_t firstTermIndex, size_t lastTermIndex) + { + Vector& terms = alternative->m_terms; + + for (size_t termIndex = firstTermIndex; termIndex <= lastTermIndex; ++termIndex) { + PatternTerm& term = terms[termIndex]; + + if (term.m_capture) + return true; + + if (term.type == PatternTerm::TypeParenthesesSubpattern) { + PatternDisjunction* nestedDisjunction = term.parentheses.disjunction; + for (unsigned alt = 0; alt < nestedDisjunction->m_alternatives.size(); ++alt) { + if (containsCapturingTerms(nestedDisjunction->m_alternatives[alt], 0, nestedDisjunction->m_alternatives[alt]->m_terms.size() - 1)) + return true; + } + } + } + + return false; + } + + // This optimization identifies alternatives in the form of + // [^].*[?].*[$] for expressions that don't have any + // capturing terms. The alternative is changed to + // followed by processing of the dot stars to find and adjust the + // beginning and the end of the match. + void optimizeDotStarWrappedExpressions() + { + Vector& alternatives = m_pattern.m_body->m_alternatives; + if (alternatives.size() != 1) + return; + + PatternAlternative* alternative = alternatives[0]; + Vector& terms = alternative->m_terms; + if (terms.size() >= 3) { + bool startsWithBOL = false; + bool endsWithEOL = false; + size_t termIndex, firstExpressionTerm, lastExpressionTerm; + + termIndex = 0; + if (terms[termIndex].type == PatternTerm::TypeAssertionBOL) { + startsWithBOL = true; + ++termIndex; + } + + PatternTerm& firstNonAnchorTerm = terms[termIndex]; + if ((firstNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (firstNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || !((firstNonAnchorTerm.quantityType == QuantifierGreedy) || (firstNonAnchorTerm.quantityType == QuantifierNonGreedy))) + return; + + firstExpressionTerm = termIndex + 1; + + termIndex = terms.size() - 1; + if (terms[termIndex].type == PatternTerm::TypeAssertionEOL) { + endsWithEOL = true; + --termIndex; + } + + PatternTerm& lastNonAnchorTerm = terms[termIndex]; + if ((lastNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (lastNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || (lastNonAnchorTerm.quantityType != QuantifierGreedy)) + return; + + lastExpressionTerm = termIndex - 1; + + if (firstExpressionTerm > lastExpressionTerm) + return; + + if (!containsCapturingTerms(alternative, firstExpressionTerm, lastExpressionTerm)) { + for (termIndex = terms.size() - 1; termIndex > lastExpressionTerm; --termIndex) + terms.remove(termIndex); + + for (termIndex = firstExpressionTerm; termIndex > 0; --termIndex) + terms.remove(termIndex - 1); + + terms.append(PatternTerm(startsWithBOL, endsWithEOL)); + + m_pattern.m_containsBOL = false; + } + } + } + private: YarrPattern& m_pattern; PatternAlternative* m_alternative; @@ -703,7 +840,7 @@ private: bool m_invertParentheticalAssertion; }; -ErrorCode YarrPattern::compile(const UString& patternString) +ErrorCode YarrPattern::compile(const String& patternString) { YarrPatternConstructor constructor(*this); @@ -728,6 +865,7 @@ ErrorCode YarrPattern::compile(const UString& patternString) } constructor.checkForTerminalParentheses(); + constructor.optimizeDotStarWrappedExpressions(); constructor.optimizeBOL(); if (ErrorCode error = constructor.setupOffsets()) @@ -736,7 +874,7 @@ ErrorCode YarrPattern::compile(const UString& patternString) return NoError; } -YarrPattern::YarrPattern(const UString& pattern, bool ignoreCase, bool multiline, ErrorCode* error) +YarrPattern::YarrPattern(const String& pattern, bool ignoreCase, bool multiline, ErrorCode* error) : m_ignoreCase(ignoreCase) , m_multiline(multiline) , m_containsBackreferences(false) diff --git a/js/src/yarr/YarrPattern.h b/js/src/yarr/YarrPattern.h index 8c862323169b..737de608199f 100644 --- a/js/src/yarr/YarrPattern.h +++ b/js/src/yarr/YarrPattern.h @@ -1,7 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: +/* vim: set ts=4 sw=4 tw=99 et: * - * ***** BEGIN LICENSE BLOCK ***** * Copyright (C) 2009 Apple Inc. All rights reserved. * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged * @@ -25,8 +23,7 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #ifndef YarrPattern_h #define YarrPattern_h @@ -36,23 +33,47 @@ namespace JSC { namespace Yarr { +struct PatternDisjunction; + enum ErrorCode { NoError, PatternTooLarge, QuantifierOutOfOrder, QuantifierWithoutAtom, + QuantifierTooLarge, MissingParentheses, ParenthesesUnmatched, ParenthesesTypeInvalid, CharacterClassUnmatched, - CharacterClassInvalidRange, CharacterClassOutOfOrder, + CharacterClassInvalidRange, EscapeUnterminated, - QuantifierTooLarge, NumberOfErrorCodes }; -struct PatternDisjunction; +static inline const char* errorMessage(ErrorCode code) +{ + +#define REGEXP_ERROR_PREFIX "Invalid regular expression: " + // The order of this array must match the ErrorCode enum. + static const char* errorMessages[NumberOfErrorCodes] = { + 0, // NoError + REGEXP_ERROR_PREFIX "regular expression too large", + REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier", + REGEXP_ERROR_PREFIX "nothing to repeat", + REGEXP_ERROR_PREFIX "number too large in {} quantifier", + REGEXP_ERROR_PREFIX "missing )", + REGEXP_ERROR_PREFIX "unmatched parentheses", + REGEXP_ERROR_PREFIX "unrecognized character after (?", + REGEXP_ERROR_PREFIX "missing terminating ] for character class", + REGEXP_ERROR_PREFIX "character class out of order" + REGEXP_ERROR_PREFIX "range out of order in character class", + REGEXP_ERROR_PREFIX "\\ at end of pattern" + }; +#undef REGEXP_ERROR_PREFIX + + return errorMessages[code]; +} struct CharacterRange { UChar begin; @@ -81,7 +102,7 @@ struct CharacterClassTable : RefCounted { }; struct CharacterClass { - WTF_MAKE_FAST_ALLOCATED + WTF_MAKE_FAST_ALLOCATED; public: // All CharacterClass instances have to have the full set of matches and ranges, // they may have an optional table for faster lookups (which must match the @@ -117,7 +138,8 @@ struct PatternTerm { TypeBackReference, TypeForwardReference, TypeParenthesesSubpattern, - TypeParentheticalAssertion + TypeParentheticalAssertion, + TypeDotStarEnclosure } type; bool m_capture :1; bool m_invert :1; @@ -132,23 +154,16 @@ struct PatternTerm { bool isCopy; bool isTerminal; } parentheses; + struct { + bool bolAnchor : 1; + bool eolAnchor : 1; + } anchors; }; QuantifierType quantityType; - unsigned quantityCount; + Checked quantityCount; int inputPosition; unsigned frameLocation; - // No-argument constructor for js::Vector. - PatternTerm() - : type(PatternTerm::TypePatternCharacter) - , m_capture(false) - , m_invert(false) - { - patternCharacter = 0; - quantityType = QuantifierFixedCount; - quantityCount = 1; - } - PatternTerm(UChar ch) : type(PatternTerm::TypePatternCharacter) , m_capture(false) @@ -201,6 +216,28 @@ struct PatternTerm { quantityCount = 1; } + PatternTerm(bool bolAnchor, bool eolAnchor) + : type(TypeDotStarEnclosure) + , m_capture(false) + , m_invert(false) + { + anchors.bolAnchor = bolAnchor; + anchors.eolAnchor = eolAnchor; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + + // No-argument constructor for js::Vector. + PatternTerm() + : type(PatternTerm::TypePatternCharacter) + , m_capture(false) + , m_invert(false) + { + patternCharacter = 0; + quantityType = QuantifierFixedCount; + quantityCount = 1; + } + static PatternTerm ForwardReference() { return PatternTerm(TypeForwardReference); @@ -239,7 +276,7 @@ struct PatternTerm { }; struct PatternAlternative { - WTF_MAKE_FAST_ALLOCATED + WTF_MAKE_FAST_ALLOCATED; public: PatternAlternative(PatternDisjunction* disjunction) : m_parent(disjunction) @@ -282,7 +319,7 @@ public: }; struct PatternDisjunction { - WTF_MAKE_FAST_ALLOCATED + WTF_MAKE_FAST_ALLOCATED; public: PatternDisjunction(PatternAlternative* parent = 0) : m_parent(parent) @@ -331,7 +368,7 @@ struct TermChain { }; struct YarrPattern { - YarrPattern(const UString& pattern, bool ignoreCase, bool multiline, ErrorCode* error); + YarrPattern(const String& pattern, bool ignoreCase, bool multiline, ErrorCode* error); ~YarrPattern() { @@ -420,7 +457,7 @@ struct YarrPattern { Vector m_userCharacterClasses; private: - ErrorCode compile(const UString& patternString); + ErrorCode compile(const String& patternString); CharacterClass* newlineCached; CharacterClass* digitsCached; diff --git a/js/src/yarr/YarrSyntaxChecker.cpp b/js/src/yarr/YarrSyntaxChecker.cpp index f36ac5a3f5bc..34fb5010cfc9 100644 --- a/js/src/yarr/YarrSyntaxChecker.cpp +++ b/js/src/yarr/YarrSyntaxChecker.cpp @@ -1,7 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** +/* * Copyright (C) 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,8 +21,7 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #include "YarrSyntaxChecker.h" @@ -53,7 +49,7 @@ public: void disjunction() {} }; -ErrorCode checkSyntax(const UString& pattern) +ErrorCode checkSyntax(const String& pattern) { SyntaxChecker syntaxChecker; return parse(syntaxChecker, pattern); diff --git a/js/src/yarr/YarrSyntaxChecker.h b/js/src/yarr/YarrSyntaxChecker.h index 87f2ed5093b0..b6848e752681 100644 --- a/js/src/yarr/YarrSyntaxChecker.h +++ b/js/src/yarr/YarrSyntaxChecker.h @@ -1,7 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** +/* * Copyright (C) 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,8 +21,7 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ***** END LICENSE BLOCK ***** */ + */ #ifndef YarrSyntaxChecker_h #define YarrSyntaxChecker_h @@ -35,7 +31,7 @@ namespace JSC { namespace Yarr { -ErrorCode checkSyntax(const UString& pattern); +ErrorCode checkSyntax(const String& pattern); }} // JSC::YARR diff --git a/js/src/yarr/wtfbridge.h b/js/src/yarr/wtfbridge.h index a3856696b452..15d4285fb97b 100644 --- a/js/src/yarr/wtfbridge.h +++ b/js/src/yarr/wtfbridge.h @@ -12,11 +12,14 @@ * definitions for use by Yarr. */ +#include +#include #include "jsstr.h" #include "jsprvtd.h" #include "vm/String.h" #include "assembler/wtf/Platform.h" #include "assembler/jit/ExecutableAllocator.h" +#include "CheckedArithmetic.h" namespace JSC { namespace Yarr { @@ -24,8 +27,10 @@ namespace JSC { namespace Yarr { * Basic type definitions. */ +typedef char LChar; typedef jschar UChar; typedef JSLinearString UString; +typedef JSLinearString String; using namespace js::unicode; @@ -105,7 +110,8 @@ PassRefPtr adoptRef(T *p) { return PassRefPtr(p); } template PassOwnPtr adoptPtr(T *p) { return PassOwnPtr(p); } -#define WTF_MAKE_FAST_ALLOCATED +// Dummy wrapper. +#define WTF_MAKE_FAST_ALLOCATED void make_fast_allocated_() template class Ref { @@ -190,6 +196,10 @@ class Vector { (void) impl.resize(newLength); } + void swap(Vector &other) { + impl.swap(other.impl); + } + void deleteAllValues() { for (T *p = impl.begin(); p != impl.end(); ++p) js_delete(*p); @@ -229,6 +239,15 @@ deleteAllValues(Vector &v) { v.deleteAllValues(); } +static inline void +dataLog(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + #if ENABLE_YARR_JIT /* @@ -244,11 +263,6 @@ class JSGlobalData { #endif -/* - * Sentinel value used in Yarr. - */ -const size_t notFound = size_t(-1); - /* * Do-nothing version of a macro used by WTF to avoid unused * parameter warnings. @@ -273,6 +287,8 @@ namespace std { # undef max #endif +#define NO_RETURN_DUE_TO_ASSERT + template inline T min(T t1, T t2) @@ -299,4 +315,15 @@ swap(T &t1, T &t2) } /* namespace JSC */ +namespace WTF { + +/* + * Sentinel value used in Yarr. + */ +const size_t notFound = size_t(-1); + +} + +#define JS_EXPORT_PRIVATE + #endif From f70dbc02504d4710095c104ec9868cbdbc6c9db5 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 1 Nov 2012 23:40:38 -0700 Subject: [PATCH 26/83] Bug 807525 - Have embedjs.py append newlines when not present. (r=till) --- js/src/builtin/embedjs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/builtin/embedjs.py b/js/src/builtin/embedjs.py index c28eea4b0632..f762e0cb6fd4 100644 --- a/js/src/builtin/embedjs.py +++ b/js/src/builtin/embedjs.py @@ -17,7 +17,7 @@ def replaceErrorMsgs(source_files, messages_file, output_file): if len(source_files) == 0: return for line in fileinput.input(source_files): - output.write(replaceMessages(line, messages)) + output.write(replaceMessages(line if line[-1] == '\n' else line + '\n', messages)) def buildMessagesTable(messages_file): table = {} @@ -48,4 +48,4 @@ def main(): js2c.JS2C([combined_file, macros_file], [output_file], { 'TYPE': 'CORE', 'COMPRESSION': 'off', 'DEBUG':debug }) if __name__ == "__main__": - main() \ No newline at end of file + main() From 061dec3e69315439b04ca6cdc014581cd5f580de Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 2 Nov 2012 00:13:08 -0700 Subject: [PATCH 27/83] Bug 807142: Make containers for DisplayRemote behave like leaf layers when we're resolution-scaling. r=mattwoodrow --- gfx/layers/Layers.h | 6 ++++++ layout/ipc/RenderFrameParent.cpp | 13 ++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 67ed7c474198..2b188bc1c9b0 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -727,6 +727,9 @@ public: void SetPostScale(float aXScale, float aYScale) { + if (mPostXScale == aXScale && mPostYScale == aYScale) { + return; + } mPostXScale = aXScale; mPostYScale = aYScale; Mutated(); @@ -1232,6 +1235,9 @@ public: void SetPreScale(float aXScale, float aYScale) { + if (mPreXScale == aXScale && mPreYScale == aYScale) { + return; + } mPreXScale = aXScale; mPreYScale = aYScale; Mutated(); diff --git a/layout/ipc/RenderFrameParent.cpp b/layout/ipc/RenderFrameParent.cpp index 9fbff4f416e9..930ae3284af0 100644 --- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -661,9 +661,16 @@ RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder, static_cast(layer.get())->SetReferentId(id); layer->SetVisibleRegion(aVisibleRect); nsIntPoint rootFrameOffset = GetRootFrameOffset(aFrame, aBuilder); - layer->SetBaseTransform( - gfx3DMatrix::Translation(rootFrameOffset.x + aContainerParameters.mOffset.x, - rootFrameOffset.y + aContainerParameters.mOffset.y, 0.0)); + // We can only have an offset if we're a child of an inactive + // container, but our display item is LAYER_ACTIVE_FORCE which + // forces all layers above to be active. + MOZ_ASSERT(aContainerParameters.mOffset == nsIntPoint()); + gfx3DMatrix m = + gfx3DMatrix::Translation(rootFrameOffset.x, rootFrameOffset.y, 0.0); + // Remote content can't be repainted by us, so we multiply down + // the resolution that our container expects onto our container. + m.Scale(aContainerParameters.mXScale, aContainerParameters.mYScale, 1.0); + layer->SetBaseTransform(m); return layer.forget(); } From 6c37dfc53651fc611cebbe4c8d16dfbc3359b007 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 2 Nov 2012 08:35:32 +0000 Subject: [PATCH 28/83] Bug 797021 - Fix border clipping with multiple background layers. r=roc Bug 786502 broke border clipping with multiple background layers as it stopped iterating from the bottom-most layer when drawing, and thus skipped background clip setting. --- layout/base/nsCSSRendering.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index fb38de801911..9a87f2e4adda 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -2552,7 +2552,9 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, if (drawBackgroundImage) { bool clipSet = false; - NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, bg, startLayer, nLayers) { + NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, bg, bg->mImageCount - 1, + nLayers + (bg->mImageCount - + startLayer - 1)) { const nsStyleBackground::Layer &layer = bg->mLayers[i]; if (!aBGClipRect) { uint8_t newBackgroundClip = layer.mClip; @@ -2575,7 +2577,8 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, clipSet = true; } } - if (!clipState.mDirtyRectGfx.IsEmpty()) { + if ((aLayer < 0 || i == (uint32_t)startLayer) && + !clipState.mDirtyRectGfx.IsEmpty()) { nsBackgroundLayerState state = PrepareBackgroundLayer(aPresContext, aForFrame, aFlags, aBorderArea, clipState.mBGClipArea, *bg, layer); if (!state.mFillArea.IsEmpty()) { From f6f6bc2090ee0e408b9391cc77256a950c1aefca Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 2 Nov 2012 08:35:39 +0000 Subject: [PATCH 29/83] Bug 797021 - Reftest for multiple background content/border box clipping. r=roc --- .../multi-background-clip-content-border-ref.html | 10 ++++++++++ .../multi-background-clip-content-border.html | 10 ++++++++++ layout/reftests/backgrounds/reftest.list | 2 ++ 3 files changed, 22 insertions(+) create mode 100644 layout/reftests/backgrounds/multi-background-clip-content-border-ref.html create mode 100644 layout/reftests/backgrounds/multi-background-clip-content-border.html diff --git a/layout/reftests/backgrounds/multi-background-clip-content-border-ref.html b/layout/reftests/backgrounds/multi-background-clip-content-border-ref.html new file mode 100644 index 000000000000..290b0b1996b7 --- /dev/null +++ b/layout/reftests/backgrounds/multi-background-clip-content-border-ref.html @@ -0,0 +1,10 @@ + +multi-background-clip-content-border-ref + +
diff --git a/layout/reftests/backgrounds/multi-background-clip-content-border.html b/layout/reftests/backgrounds/multi-background-clip-content-border.html new file mode 100644 index 000000000000..d13352f6f616 --- /dev/null +++ b/layout/reftests/backgrounds/multi-background-clip-content-border.html @@ -0,0 +1,10 @@ + +multi-background-clip-content-border + +
diff --git a/layout/reftests/backgrounds/reftest.list b/layout/reftests/backgrounds/reftest.list index 9e2f43a1baee..588ce6c1bd20 100644 --- a/layout/reftests/backgrounds/reftest.list +++ b/layout/reftests/backgrounds/reftest.list @@ -136,3 +136,5 @@ random-if(bug685516) == table-background.html table-background-ref.html random-if(bug685516) != div-background.html div-background-ref.html random-if(bug685516) == background-repeat-1-ref.html background-repeat-1.html + +random-if(bug685516) == multi-background-clip-content-border.html multi-background-clip-content-border-ref.html From f3f7e1b0206a743a6f13c39c5d4c8a1b8be1bce8 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Fri, 2 Nov 2012 09:28:03 +0000 Subject: [PATCH 30/83] Bug 797021 - Allow unsigned start parameter in NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE. r=me This was causing build failures on debug builds, as unsigned compare >= 0 is always true. --- layout/style/nsStyleStruct.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 4fc8d3a769e9..55816697ff50 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -489,7 +489,7 @@ struct nsStyleBackground { #define NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(var_, stylebg_) \ for (uint32_t var_ = (stylebg_) ? (stylebg_)->mImageCount : 1; var_-- != 0; ) #define NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE(var_, stylebg_, start_, count_) \ - NS_ASSERTION((start_) >= 0 && (uint32_t)(start_) < ((stylebg_) ? (stylebg_)->mImageCount : 1), "Invalid layer start!"); \ + NS_ASSERTION((int32_t)(start_) >= 0 && (uint32_t)(start_) < ((stylebg_) ? (stylebg_)->mImageCount : 1), "Invalid layer start!"); \ NS_ASSERTION((count_) > 0 && (count_) <= (start_) + 1, "Invalid layer range!"); \ for (uint32_t var_ = (start_) + 1; var_-- != (uint32_t)((start_) + 1 - (count_)); ) From bb8f6f0e44879ede93373321d4422848d47e64ba Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Thu, 18 Oct 2012 11:38:26 +0100 Subject: [PATCH 31/83] bug 803030 - convert display to device pixels before creating the popup child view. r=smichaud --- widget/cocoa/nsCocoaWindow.mm | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 7461f0230825..140a205e484e 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -195,7 +195,7 @@ static NSScreen *FindTargetScreenForRect(const nsIntRect& aRect) // fits the rect to the screen that contains the largest area of it, // or to aScreen if a screen is passed in -// NB: this operates with aRect in global CSS pixels +// NB: this operates with aRect in global display pixels static void FitRectToVisibleAreaForScreen(nsIntRect &aRect, NSScreen *aScreen) { if (!aScreen) { @@ -239,7 +239,7 @@ static bool UseNativePopupWindows() #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */ } -// aRect here is specified in CSS pixels +// aRect here is specified in global display pixels nsresult nsCocoaWindow::Create(nsIWidget *aParent, nsNativeWidget aNativeParent, const nsIntRect &aRect, @@ -262,6 +262,10 @@ nsresult nsCocoaWindow::Create(nsIWidget *aParent, // Ensure that the toolkit is created. nsToolkit::GetToolkit(); + // newBounds is still display (global screen) pixels at this point; + // fortunately, BaseCreate doesn't actually use it so we don't + // need to worry about trying to convert it to device pixels + // when we don't have a window (or dev context, perhaps) yet Inherited::BaseCreate(aParent, newBounds, aContext, aInitData); mParent = aParent; @@ -270,14 +274,22 @@ nsresult nsCocoaWindow::Create(nsIWidget *aParent, if ((mWindowType == eWindowType_popup) && UseNativePopupWindows()) return NS_OK; - nsresult rv = CreateNativeWindow(nsCocoaUtils::GeckoRectToCocoaRect(newBounds), - mBorderStyle, false); + nsresult rv = + CreateNativeWindow(nsCocoaUtils::GeckoRectToCocoaRect(newBounds), + mBorderStyle, false); NS_ENSURE_SUCCESS(rv, rv); if (mWindowType == eWindowType_popup) { if (aInitData->mIsDragPopup) { [mWindow setIgnoresMouseEvents:YES]; } + // now we can convert newBounds to device pixels for the window we created, + // as the child view expects a rect expressed in the dev pix of its parent + double scale = BackingScaleFactor(); + newBounds.x *= scale; + newBounds.y *= scale; + newBounds.width *= scale; + newBounds.height *= scale; return CreatePopupContentView(newBounds, aContext); } From 87a6dd92fba18d4f13c05ce9504531d97b7a1574 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Nov 2012 11:45:25 +0100 Subject: [PATCH 32/83] Bug #807380: Report to the compiler the function is constructing when entering at a branch, r=dvander --- js/src/ion/Ion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index e8da55e50801..6cd9ed34eebf 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -1270,7 +1270,7 @@ ion::CanEnterAtBranch(JSContext *cx, HandleScript script, StackFrame *fp, jsbyte // Attempt compilation. Returns Method_Compiled if already compiled. JSFunction *fun = fp->isFunctionFrame() ? fp->fun() : NULL; - MethodStatus status = Compile(cx, script, fun, pc, false); + MethodStatus status = Compile(cx, script, fun, pc, fp->isConstructing()); if (status != Method_Compiled) { if (status == Method_CantCompile) ForbidCompilation(cx, script); From 75b6269d535b34841e1c1dbb36122635df5702cc Mon Sep 17 00:00:00 2001 From: Graeme McCutcheon Date: Thu, 11 Oct 2012 09:17:15 +0100 Subject: [PATCH 33/83] Bug 724513 - Part 1 - Add StartupCache method for disregarding disk file. r=mwu --- startupcache/StartupCache.cpp | 34 +++++++- startupcache/StartupCache.h | 10 +++ startupcache/nsIStartupCache.idl | 4 +- startupcache/test/TestStartupCache.cpp | 112 ++++++++++++++++++++++++- 4 files changed, 152 insertions(+), 8 deletions(-) diff --git a/startupcache/StartupCache.cpp b/startupcache/StartupCache.cpp index 8789f3b90a5b..1895f68027ac 100644 --- a/startupcache/StartupCache.cpp +++ b/startupcache/StartupCache.cpp @@ -122,6 +122,7 @@ StartupCache::InitSingleton() StartupCache* StartupCache::gStartupCache; bool StartupCache::gShutdownInitiated; +bool StartupCache::gIgnoreDiskCache; enum StartupCache::TelemetrifyAge StartupCache::gPostFlushAgeAction = StartupCache::IGNORE_AGE; StartupCache::StartupCache() @@ -203,12 +204,12 @@ StartupCache::Init() rv = mObserverService->AddObserver(mListener, "startupcache-invalidate", false); NS_ENSURE_SUCCESS(rv, rv); - + rv = LoadArchive(RECORD_AGE); // Sometimes we don't have a cache yet, that's ok. // If it's corrupted, just remove it and start over. - if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { + if (gIgnoreDiskCache || (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)) { NS_WARNING("Failed to load startupcache file correctly, removing!"); InvalidateCache(); } @@ -227,6 +228,9 @@ StartupCache::Init() nsresult StartupCache::LoadArchive(enum TelemetrifyAge flag) { + if (gIgnoreDiskCache) + return NS_ERROR_FAILURE; + bool exists; mArchive = NULL; nsresult rv = mFile->Exists(&exists); @@ -458,6 +462,9 @@ StartupCache::WriteToDisk() mArchive = NULL; zipW->Close(); + // We succesfully wrote the archive to disk; mark the disk file as trusted + gIgnoreDiskCache = false; + // Our reader's view of the archive is outdated now, reload it. LoadArchive(gPostFlushAgeAction); @@ -470,10 +477,24 @@ StartupCache::InvalidateCache() WaitOnWriteThread(); mTable.Clear(); mArchive = NULL; - mFile->Remove(false); + nsresult rv = mFile->Remove(false); + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && + rv != NS_ERROR_FILE_NOT_FOUND) { + gIgnoreDiskCache = true; + return; + } + gIgnoreDiskCache = false; LoadArchive(gPostFlushAgeAction); } +void +StartupCache::IgnoreDiskCache() +{ + gIgnoreDiskCache = true; + if (gStartupCache) + gStartupCache->InvalidateCache(); +} + /* * WaitOnWriteThread() is called from a main thread to wait for the worker * thread to finish. However since the same code is used in the worker thread and @@ -713,6 +734,13 @@ StartupCacheWrapper::InvalidateCache() return NS_OK; } +nsresult +StartupCacheWrapper::IgnoreDiskCache() +{ + StartupCache::IgnoreDiskCache(); + return NS_OK; +} + nsresult StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream, nsIObjectOutputStream** outStream) diff --git a/startupcache/StartupCache.h b/startupcache/StartupCache.h index 7440d71b74c5..f3e111e7bf1e 100644 --- a/startupcache/StartupCache.h +++ b/startupcache/StartupCache.h @@ -44,6 +44,12 @@ * * InvalidateCache() may be called if a client suspects data corruption * or wishes to invalidate for any other reason. This will remove all existing cache data. + * Additionally, the static method IgnoreDiskCache() can be called if it is + * believed that the on-disk cache file is itself corrupt. This call implicitly + * calls InvalidateCache (if the singleton has been initialized) to ensure any + * data already read from disk is discarded. The cache will not load data from + * the disk file until a successful write occurs. + * * Finally, getDebugObjectOutputStream() allows debug code to wrap an objectstream * with a debug objectstream, to check for multiply-referenced objects. These will * generally fail to deserialize correctly, unless they are stateless singletons or the @@ -114,6 +120,9 @@ public: // Removes the cache file. void InvalidateCache(); + // Signal that data should not be loaded from the cache file + static void IgnoreDiskCache(); + // In DEBUG builds, returns a stream that will attempt to check for // and disallow multiple writes of the same object. nsresult GetDebugObjectOutputStream(nsIObjectOutputStream* aStream, @@ -167,6 +176,7 @@ private: static StartupCache *gStartupCache; static bool gShutdownInitiated; + static bool gIgnoreDiskCache; PRThread *mWriteThread; #ifdef DEBUG nsTHashtable mWriteObjectMap; diff --git a/startupcache/nsIStartupCache.idl b/startupcache/nsIStartupCache.idl index ca989340fe8b..ece38b4edb65 100644 --- a/startupcache/nsIStartupCache.idl +++ b/startupcache/nsIStartupCache.idl @@ -9,7 +9,7 @@ #include "nsIObserver.idl" #include "nsIObjectOutputStream.idl" -[uuid(c1b3796b-33af-4ff0-b83d-8eb0ca2c080f)] +[uuid(25957820-90a1-428c-8739-b0845d3cc534)] interface nsIStartupCache : nsISupports { @@ -24,6 +24,8 @@ interface nsIStartupCache : nsISupports void invalidateCache(); + void ignoreDiskCache(); + /** In debug builds, wraps this object output stream with a stream that will * detect and prevent the write of a multiply-referenced non-singleton object * during serialization. In non-debug, returns an add-ref'd pointer to diff --git a/startupcache/test/TestStartupCache.cpp b/startupcache/test/TestStartupCache.cpp index a5e389fc302f..8955f311c484 100644 --- a/startupcache/test/TestStartupCache.cpp +++ b/startupcache/test/TestStartupCache.cpp @@ -22,6 +22,7 @@ #include "nsIPrefService.h" #include "nsITelemetry.h" #include "jsapi.h" +#include "prio.h" namespace mozilla { namespace scache { @@ -56,7 +57,7 @@ PR_END_MACRO nsresult WaitForStartupTimer() { nsresult rv; - nsCOMPtr sc + nsCOMPtr sc = do_GetService("@mozilla.org/startupcache/cache;1"); PR_Sleep(10 * PR_TicksPerSecond()); @@ -75,7 +76,7 @@ WaitForStartupTimer() { nsresult TestStartupWriteRead() { nsresult rv; - nsCOMPtr sc + nsCOMPtr sc = do_GetService("@mozilla.org/startupcache/cache;1", &rv); if (!sc) { fail("didn't get a pointer..."); @@ -118,7 +119,7 @@ TestWriteInvalidateRead() { const char* id = "id"; char* outbuf = NULL; uint32_t len; - nsCOMPtr sc + nsCOMPtr sc = do_GetService("@mozilla.org/startupcache/cache;1", &rv); sc->InvalidateCache(); @@ -247,10 +248,110 @@ TestWriteObject() { return NS_OK; } +nsresult +LockCacheFile(bool protect, nsIFile* profileDir) { + NS_ENSURE_ARG(profileDir); + + nsCOMPtr startupCache; + profileDir->Clone(getter_AddRefs(startupCache)); + NS_ENSURE_STATE(startupCache); + startupCache->AppendNative(NS_LITERAL_CSTRING("startupCache")); + + nsresult rv; +#ifndef XP_WIN + static uint32_t oldPermissions; +#else + static PRFileDesc* fd = nullptr; +#endif + + // To prevent deletion of the startupcache file, we change the containing + // directory's permissions on Linux/Mac, and hold the file open on Windows + if (protect) { +#ifndef XP_WIN + rv = startupCache->GetPermissions(&oldPermissions); + NS_ENSURE_SUCCESS(rv, rv); + rv = startupCache->SetPermissions(0555); + NS_ENSURE_SUCCESS(rv, rv); +#else + // Filename logic from StartupCache.cpp + #ifdef IS_BIG_ENDIAN + #define SC_ENDIAN "big" + #else + #define SC_ENDIAN "little" + #endif + + #if PR_BYTES_PER_WORD == 4 + #define SC_WORDSIZE "4" + #else + #define SC_WORDSIZE "8" + #endif + char sStartupCacheName[] = "startupCache." SC_WORDSIZE "." SC_ENDIAN; + startupCache->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName)); + + rv = startupCache->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); + NS_ENSURE_SUCCESS(rv, rv); +#endif + } else { +#ifndef XP_WIN + rv = startupCache->SetPermissions(oldPermissions); + NS_ENSURE_SUCCESS(rv, rv); +#else + PR_Close(fd); +#endif + } + + return NS_OK; +} + +nsresult +TestIgnoreDiskCache(nsIFile* profileDir) { + nsresult rv; + nsCOMPtr sc + = do_GetService("@mozilla.org/startupcache/cache;1", &rv); + sc->InvalidateCache(); + + const char* buf = "Get a Beardbook app for your smartphone"; + const char* id = "id"; + char* outbuf = NULL; + PRUint32 len; + + rv = sc->PutBuffer(id, buf, strlen(buf) + 1); + NS_ENSURE_SUCCESS(rv, rv); + rv = sc->ResetStartupWriteTimer(); + rv = WaitForStartupTimer(); + NS_ENSURE_SUCCESS(rv, rv); + + // Prevent StartupCache::InvalidateCache from deleting the disk file + rv = LockCacheFile(true, profileDir); + NS_ENSURE_SUCCESS(rv, rv); + + sc->IgnoreDiskCache(); + + rv = sc->GetBuffer(id, &outbuf, &len); + + nsresult r = LockCacheFile(false, profileDir); + NS_ENSURE_SUCCESS(r, r); + + delete[] outbuf; + + if (rv == NS_ERROR_NOT_AVAILABLE) { + passed("buffer not available after ignoring disk cache"); + } else if (NS_SUCCEEDED(rv)) { + fail("GetBuffer succeeded unexpectedly after ignoring disk cache"); + return NS_ERROR_UNEXPECTED; + } else { + fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE"); + return rv; + } + + sc->InvalidateCache(); + return NS_OK; +} + nsresult TestEarlyShutdown() { nsresult rv; - nsCOMPtr sc + nsCOMPtr sc = do_GetService("@mozilla.org/startupcache/cache;1", &rv); sc->InvalidateCache(); @@ -416,6 +517,9 @@ int main(int argc, char** argv) rv = 1; if (NS_FAILED(TestWriteObject())) rv = 1; + nsCOMPtr profileDir = xpcom.GetProfileDirectory(); + if (NS_FAILED(TestIgnoreDiskCache(profileDir))) + rv = 1; if (NS_FAILED(TestEarlyShutdown())) rv = 1; From 4007709169282667a304c5117d21eb1dae0d6703 Mon Sep 17 00:00:00 2001 From: Graeme McCutcheon Date: Tue, 23 Oct 2012 17:46:51 +0100 Subject: [PATCH 34/83] Bug 724513 - Part 2 - nsAppRunner shouldn't assume the deletion of invalid startup cache file succeeded. r=mwu --- toolkit/xre/nsAppRunner.cpp | 40 +++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 61f184205722..423166e016c5 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -91,11 +91,13 @@ #include "nsIWidget.h" #include "nsIDocShell.h" #include "nsAppShellCID.h" +#include "mozilla/scache/StartupCache.h" #include "mozilla/unused.h" using namespace mozilla; using mozilla::unused; +using mozilla::scache::StartupCache; #ifdef XP_WIN #include "nsIWinAppHelper.h" @@ -2406,7 +2408,7 @@ static void BuildVersion(nsCString &aBuf) static void WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion, const nsCString& aOSABI, nsIFile* aXULRunnerDir, - nsIFile* aAppDir) + nsIFile* aAppDir, bool invalidateCache) { nsCOMPtr file; aProfileDir->Clone(getter_AddRefs(file)); @@ -2449,19 +2451,30 @@ WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion, PR_Write(fd, appDir.get(), appDir.Length()); } + static const char kInvalidationHeader[] = "InvalidateCaches=1" NS_LINEBREAK; + if (invalidateCache) + PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1); + static const char kNL[] = NS_LINEBREAK; PR_Write(fd, kNL, sizeof(kNL) - 1); PR_Close(fd); } -static void RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfileDir, +/** + * Returns true if the startup cache file was successfully removed. + * Returns false if file->Clone fails at any point (OOM) or if unable + * to remove the startup cache file. Note in particular the return value + * is unaffected by a failure to remove extensions.ini + */ +static bool +RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfileDir, bool aRemoveEMFiles) { nsCOMPtr file; aProfileDir->Clone(getter_AddRefs(file)); if (!file) - return; + return false; if (aRemoveEMFiles) { file->SetNativeLeafName(NS_LITERAL_CSTRING("extensions.ini")); @@ -2470,7 +2483,7 @@ static void RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfi aLocalProfileDir->Clone(getter_AddRefs(file)); if (!file) - return; + return false; #if defined(XP_UNIX) || defined(XP_BEOS) #define PLATFORM_FASL_SUFFIX ".mfasl" @@ -2485,7 +2498,8 @@ static void RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfi file->Remove(false); file->SetNativeLeafName(NS_LITERAL_CSTRING("startupCache")); - file->Remove(true); + nsresult rv = file->Remove(true); + return NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; } // To support application initiated restart via nsIAppStartup.quit, we @@ -3533,21 +3547,22 @@ XREMain::XRE_mainStartup(bool* aExitFlag) // profile in different builds the component registry must be // re-generated to prevent mysterious component loading failures. // + bool startupCacheValid = true; if (gSafeMode) { - RemoveComponentRegistries(mProfD, mProfLD, false); + startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false); WriteVersion(mProfD, NS_LITERAL_CSTRING("Safe Mode"), osABI, - mDirProvider.GetGREDir(), mAppData->directory); + mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); } else if (versionOK) { if (!cachesOK) { // Remove caches, forcing component re-registration. // The new list of additional components directories is derived from // information in "extensions.ini". - RemoveComponentRegistries(mProfD, mProfLD, false); + startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false); // Rewrite compatibility.ini to remove the flag WriteVersion(mProfD, version, osABI, - mDirProvider.GetGREDir(), mAppData->directory); + mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); } // Nothing need be done for the normal startup case. } @@ -3555,13 +3570,16 @@ XREMain::XRE_mainStartup(bool* aExitFlag) // Remove caches, forcing component re-registration // with the default set of components (this disables any potentially // troublesome incompatible XPCOM components). - RemoveComponentRegistries(mProfD, mProfLD, true); + startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, true); // Write out version WriteVersion(mProfD, version, osABI, - mDirProvider.GetGREDir(), mAppData->directory); + mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid); } + if (!startupCacheValid) + StartupCache::IgnoreDiskCache(); + if (flagFile) { flagFile->Remove(true); } From 7c6e0f8608ce442b0a5fb131cec47b296a776f53 Mon Sep 17 00:00:00 2001 From: Graeme McCutcheon Date: Thu, 11 Oct 2012 09:50:41 +0100 Subject: [PATCH 35/83] Bug 724513 - Part 3 - Add telemetry for the invalid disk cache situation. r=taras --- startupcache/StartupCache.cpp | 1 + startupcache/test/TestStartupCache.cpp | 75 +++++++++++++------- toolkit/components/telemetry/Histograms.json | 4 ++ 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/startupcache/StartupCache.cpp b/startupcache/StartupCache.cpp index 1895f68027ac..13a6982a4078 100644 --- a/startupcache/StartupCache.cpp +++ b/startupcache/StartupCache.cpp @@ -481,6 +481,7 @@ StartupCache::InvalidateCache() if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && rv != NS_ERROR_FILE_NOT_FOUND) { gIgnoreDiskCache = true; + mozilla::Telemetry::Accumulate(Telemetry::STARTUP_CACHE_INVALID, true); return; } gIgnoreDiskCache = false; diff --git a/startupcache/test/TestStartupCache.cpp b/startupcache/test/TestStartupCache.cpp index 8955f311c484..4cbf30044b66 100644 --- a/startupcache/test/TestStartupCache.cpp +++ b/startupcache/test/TestStartupCache.cpp @@ -400,14 +400,14 @@ SetupJS(JSContext **cxp) } bool -GetHistogramCounts(const char *testmsg, JSContext *cx, jsval *counts) +GetHistogramCounts(const char *testmsg, const nsACString &histogram_id, + JSContext *cx, jsval *counts) { nsCOMPtr telemetry = do_GetService("@mozilla.org/base/telemetry;1"); - NS_NAMED_LITERAL_CSTRING(histogram_id, "STARTUP_CACHE_AGE_HOURS"); JS::AutoValueRooter h(cx); nsresult trv = telemetry->GetHistogramById(histogram_id, cx, h.addr()); if (NS_FAILED(trv)) { - fail("%s: couldn't get histogram", testmsg); + fail("%s: couldn't get histogram %s", testmsg, ToNewCString(histogram_id)); return false; } passed(testmsg); @@ -459,11 +459,32 @@ CompareCountArrays(JSContext *cx, JSObject *before, JSObject *after) } } - // All of the elements of the histograms's count arrays differed. + // None of the elements of the histograms's count arrays differed. // Not good, we should have recorded something. return NS_ERROR_FAILURE; } +nsresult +TestHistogramValues(const char* type, bool use_js, JSContext *cx, + JSObject *before, JSObject *after) +{ + if (!use_js) { + fail("couldn't check histogram recording"); + return NS_ERROR_FAILURE; + } + nsresult compare = CompareCountArrays(cx, before, after); + if (compare == NS_ERROR_UNEXPECTED) { + fail("count comparison error"); + return compare; + } + if (compare == NS_ERROR_FAILURE) { + fail("histogram didn't record %s", type); + return compare; + } + passed("histogram records %s", type); + return NS_OK; +} + int main(int argc, char** argv) { ScopedXPCOM xpcom("Startup Cache"); @@ -499,9 +520,17 @@ int main(int argc, char** argv) if (use_js && !JS_InitStandardClasses(cx, glob)) use_js = false; - JS::AutoValueRooter before_counts(cx); + NS_NAMED_LITERAL_CSTRING(age_histogram_id, "STARTUP_CACHE_AGE_HOURS"); + NS_NAMED_LITERAL_CSTRING(invalid_histogram_id, "STARTUP_CACHE_INVALID"); + + JS::AutoValueRooter age_before_counts(cx); if (use_js && !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram before test", - cx, before_counts.addr())) + age_histogram_id, cx, age_before_counts.addr())) + use_js = false; + + JS::AutoValueRooter invalid_before_counts(cx); + if (use_js && !GetHistogramCounts("STARTUP_CACHE_INVALID histogram before test", + invalid_histogram_id, cx, invalid_before_counts.addr())) use_js = false; nsresult scrv; @@ -523,28 +552,26 @@ int main(int argc, char** argv) if (NS_FAILED(TestEarlyShutdown())) rv = 1; - JS::AutoValueRooter after_counts(cx); + JS::AutoValueRooter age_after_counts(cx); if (use_js && !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram after test", - cx, after_counts.addr())) + age_histogram_id, cx, age_after_counts.addr())) use_js = false; - if (!use_js) { - fail("couldn't check histogram recording"); + if (NS_FAILED(TestHistogramValues("age samples", use_js, cx, + JSVAL_TO_OBJECT(age_before_counts.value()), + JSVAL_TO_OBJECT(age_after_counts.value())))) + rv = 1; + + JS::AutoValueRooter invalid_after_counts(cx); + if (use_js && !GetHistogramCounts("STARTUP_CACHE_INVALID histogram after test", + invalid_histogram_id, cx, invalid_after_counts.addr())) + use_js = false; + + // STARTUP_CACHE_INVALID should have been triggered by TestIgnoreDiskCache() + if (NS_FAILED(TestHistogramValues("invalid disk cache", use_js, cx, + JSVAL_TO_OBJECT(invalid_before_counts.value()), + JSVAL_TO_OBJECT(invalid_after_counts.value())))) rv = 1; - } else { - nsresult compare = CompareCountArrays(cx, - JSVAL_TO_OBJECT(before_counts.value()), - JSVAL_TO_OBJECT(after_counts.value())); - if (compare == NS_ERROR_UNEXPECTED) { - fail("count comparison error"); - rv = 1; - } else if (compare == NS_ERROR_FAILURE) { - fail("histogram didn't record samples"); - rv = 1; - } else { - passed("histogram records samples"); - } - } return rv; } diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 23d955519aae..11fe8e38c8a2 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -429,6 +429,10 @@ "n_buckets": 20, "description": "Startup cache age (hours)" }, + "STARTUP_CACHE_INVALID": { + "kind": "flag", + "description": "Was the disk startup cache file detected as invalid" + }, "WORD_CACHE_HITS": { "kind": "exponential", "high": "256", From e21debf331ee30a57b1fecbc60f307e1126edade Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Fri, 2 Nov 2012 06:54:44 -0500 Subject: [PATCH 36/83] Bug 750901 - Elm to mc migration work - metro related changes to Windows widget src to support building metro winrt bits (nsWidgetFactory, nsWindowGfx, nsToolkit, WinUtils). Also landing nsIWinMetroUtils.idl. r=bbondy --- widget/Makefile.in | 4 + widget/nsIWinMetroUtils.idl | 122 +++++++++++++++++++++ widget/nsWidgetsCID.h | 4 + widget/windows/AudioSession.h | 3 + widget/windows/Makefile.in | 4 + widget/windows/WinTaskbar.cpp | 5 + widget/windows/WinUtils.cpp | 39 +++++++ widget/windows/WinUtils.h | 18 ++++ widget/windows/nsAppShell.cpp | 11 +- widget/windows/nsAppShell.h | 4 + widget/windows/nsClipboard.cpp | 10 +- widget/windows/nsLookAndFeel.cpp | 1 - widget/windows/nsNativeThemeWin.h | 3 - widget/windows/nsToolkit.cpp | 22 ++-- widget/windows/nsWidgetFactory.cpp | 142 +++++++++++++++++++------ widget/windows/nsWindow.cpp | 2 +- widget/windows/nsWindowGfx.cpp | 36 +------ widget/windows/nsWindowGfx.h | 8 -- widget/xpwidgets/nsAppShellSingleton.h | 26 ++++- 19 files changed, 370 insertions(+), 94 deletions(-) create mode 100644 widget/nsIWinMetroUtils.idl diff --git a/widget/Makefile.in b/widget/Makefile.in index ec3baa494c60..199f42fc443b 100644 --- a/widget/Makefile.in +++ b/widget/Makefile.in @@ -141,6 +141,10 @@ XPIDLSRCS += nsIPrintSettingsWin.idl \ $(NULL) endif +ifdef MOZ_METRO +XPIDLSRCS += nsIWinMetroUtils.idl +endif + ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) XPIDLSRCS += nsIMacDockSupport.idl \ nsIStandaloneNativeMenu.idl \ diff --git a/widget/nsIWinMetroUtils.idl b/widget/nsIWinMetroUtils.idl new file mode 100644 index 000000000000..7b81e5dafbb6 --- /dev/null +++ b/widget/nsIWinMetroUtils.idl @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; 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" + +/** + * Integration with the "Metro"/"Modern" UI environment in Windows 8. + * + * Note: browser/metro/base/content/browser-scripts.js contains a stub + * implementation of this interface for non-Windows systems, for testing and + * development purposes only. + */ +[scriptable, uuid(45656788-B111-4317-B054-FFE881A0737E)] +interface nsIWinMetroUtils : nsISupports +{ + /* Fullscreen landscape orientation */ + const long fullScreenLandscape = 0; + /* Larger snapped state */ + const long filled = 1; + /* Smaller snapped state */ + const long snapped = 2; + /* Fullscreen portrait orientation */ + const long fullScreenPortrait = 3; + + /* return constants for the handPreference property */ + const long handPreferenceLeft = 0; + const long handPreferenceRight = 1; + + /** + * Determines the current snapped state. + */ + readonly attribute long snappedState; + + /** + * Determine if the current browser is running in the metro immersive + * environment. + */ + readonly attribute boolean immersive; + + /** + * Determine if the user prefers left handed or right handed input. + */ + readonly attribute long handPreference; + + /** + * Attempts to unsnap the application from snapped state to filled state + */ + void unsnap(); + + /** + * Launches the specified application with the specified arguments and + * switches to Desktop mode if in metro mode. + */ + void launchInDesktop(in AString aPath, in AString aArguments); + + /** + * Secondary tiles are a Windows 8 specific feature for pinning new tiles + * to the start screen. Tiles can later be activated whether the browser is + * already opened or not. + */ + + /** + * Pins a new tile to the Windows 8 start screen. + * + * @param aTileID An ID which can later be used to remove the tile + * @param aShortName A short name for the tile + * @param aDiplayName The name that will be displayed on the tile + * @param aActivationArgs The arguments to pass to the browser upon + * activation of the tile + * @param aTileImage An image for the normal tile view + * @param aSmallTileImage An image for the small tile view + */ + void pinTileAsync(in AString aTileID, + in AString aShortName, + in AString aDisplayName, + in AString aActivationArgs, + in AString aTileImage, + in AString aSmallTileImage); + + /** + * Unpins a tile from the Windows 8 start screen. + * + * @param aTileID An existing ID which was previously pinned + */ + void unpinTileAsync(in AString aTileID); + + /** + * Determines if a tile is pinned to the Windows 8 start screen. + * + * @param aTileID An ID which may have been pinned with pinTileAsync + * @return true if the tile is pinned + */ + bool isTilePinned(in AString aTileID); + + /** + * Soft keyboard attributes. Used in unison with shown/hidden observer + * events sent via FrameworkView. + * + * keyboardVisible - returns true if the soft keyboard is currently + * displayed, false otherwise. + * keyboardX, keyboardY, keyboardWidth, keyboardHeight - occlude rect + * of the keyboard when displayed in device independent pixels. + */ + readonly attribute boolean keyboardVisible; + readonly attribute unsigned long keyboardX; + readonly attribute unsigned long keyboardY; + readonly attribute unsigned long keyboardWidth; + readonly attribute unsigned long keyboardHeight; + + /** + * Settings panel links. addSettingsPanelEntry adds an entry to + * the settings flyout panel that the user can invoke. + * + * @param aChromePanelId panel id invoked via nsIBrowserDOMWindow's + * ShowPanel api. Example: 'prefs-container' + * @param aLabel Localized string label displayed in the settings + * flyout panel for this option. + */ + void addSettingsPanelEntry(in AString aChromePanelId, in AString aLabel); +}; diff --git a/widget/nsWidgetsCID.h b/widget/nsWidgetsCID.h index 961447f90873..dc814949960f 100644 --- a/widget/nsWidgetsCID.h +++ b/widget/nsWidgetsCID.h @@ -113,6 +113,10 @@ #define NS_WIN_JUMPLISTBUILDER_CID \ { 0x73a5946f, 0x608d, 0x454f, { 0x9d, 0x33, 0xb, 0x8f, 0x8c, 0x72, 0x94, 0xb6 } } +// {DE95B8C9-F0E8-4AD5-95A2-06C12F53EC2E} +#define NS_WIN_METROUTILS_CID \ +{ 0xde95b8c9, 0xf0e8, 0x4ad5, { 0x95, 0xa2, 0x06, 0xc1, 0x2f, 0x53, 0xec, 0x2e }} + // {2B9A1F2C-27CE-45b6-8D4E-755D0E34F8DB} #define NS_WIN_JUMPLISTITEM_CID \ { 0x2b9a1f2c, 0x27ce, 0x45b6, { 0x8d, 0x4e, 0x75, 0x5d, 0x0e, 0x34, 0xf8, 0xdb } } diff --git a/widget/windows/AudioSession.h b/widget/windows/AudioSession.h index aca49661e104..21296cbda06c 100644 --- a/widget/windows/AudioSession.h +++ b/widget/windows/AudioSession.h @@ -4,6 +4,9 @@ * 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 "nsAutoPtr.h" +#include "nsString.h" + namespace mozilla { namespace widget { diff --git a/widget/windows/Makefile.in b/widget/windows/Makefile.in index 3c7d051d7309..a6b2f216c38a 100644 --- a/widget/windows/Makefile.in +++ b/widget/windows/Makefile.in @@ -18,6 +18,10 @@ RESFILE = widget.res MODULE_NAME = nsWidgetWinModule LIBXUL_LIBRARY = 1 +ifdef MOZ_METRO +DIRS = winrt +endif + CPPSRCS = \ nsWindow.cpp \ nsWindowGfx.cpp \ diff --git a/widget/windows/WinTaskbar.cpp b/widget/windows/WinTaskbar.cpp index 07107f532c6e..c70b24239303 100644 --- a/widget/windows/WinTaskbar.cpp +++ b/widget/windows/WinTaskbar.cpp @@ -240,6 +240,11 @@ WinTaskbar::~WinTaskbar() { // static bool WinTaskbar::GetAppUserModelID(nsAString & aDefaultGroupId) { + // For win8 metro builds, we can't set this. The value is static + // for the app. + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { + return false; + } // If marked as such in prefs, use a hash of the profile path for the id // instead of the install path hash setup by the installer. bool useProfile = diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 2428eb076086..99b80e735c57 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -900,5 +900,44 @@ WinUtils::GetShellItemPath(IShellItem* aItem, return !aResultString.IsEmpty(); } +/* static */ +nsIntRegion +WinUtils::ConvertHRGNToRegion(HRGN aRgn) +{ + NS_ASSERTION(aRgn, "Don't pass NULL region here"); + + nsIntRegion rgn; + + DWORD size = ::GetRegionData(aRgn, 0, NULL); + nsAutoTArray buffer; + if (!buffer.SetLength(size)) + return rgn; + + RGNDATA* data = reinterpret_cast(buffer.Elements()); + if (!::GetRegionData(aRgn, size, data)) + return rgn; + + if (data->rdh.nCount > MAX_RECTS_IN_REGION) { + rgn = ToIntRect(data->rdh.rcBound); + return rgn; + } + + RECT* rects = reinterpret_cast(data->Buffer); + for (PRUint32 i = 0; i < data->rdh.nCount; ++i) { + RECT* r = rects + i; + rgn.Or(rgn, ToIntRect(*r)); + } + + return rgn; +} + +nsIntRect +WinUtils::ToIntRect(const RECT& aRect) +{ + return nsIntRect(aRect.left, aRect.top, + aRect.right - aRect.left, + aRect.bottom - aRect.top); +} + } // namespace widget } // namespace mozilla diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h index 7dac0bceadaa..e2f0dbdee49b 100644 --- a/widget/windows/WinUtils.h +++ b/widget/windows/WinUtils.h @@ -11,6 +11,8 @@ #include #include "nsAutoPtr.h" #include "nsString.h" +#include "nsRegion.h" +#include "nsRect.h" #include "nsThreadUtils.h" #include "nsICryptoHash.h" @@ -202,6 +204,22 @@ public: static bool GetShellItemPath(IShellItem* aItem, nsString& aResultString); + /** + * ConvertHRGNToRegion converts a Windows HRGN to an nsIntRegion. + * + * aRgn the HRGN to convert. + * returns the nsIntRegion. + */ + static nsIntRegion ConvertHRGNToRegion(HRGN aRgn); + + /** + * ToIntRect converts a Windows RECT to a nsIntRect. + * + * aRect the RECT to convert. + * returns the nsIntRect. + */ + static nsIntRect ToIntRect(const RECT& aRect); + private: typedef HRESULT (WINAPI * SHCreateItemFromParsingNamePtr)(PCWSTR pszPath, IBindCtx *pbc, diff --git a/widget/windows/nsAppShell.cpp b/widget/windows/nsAppShell.cpp index f12dc866f53d..de9d721bd14a 100644 --- a/widget/windows/nsAppShell.cpp +++ b/widget/windows/nsAppShell.cpp @@ -22,12 +22,9 @@ const PRUnichar* kAppShellEventId = L"nsAppShell:EventID"; const PRUnichar* kTaskbarButtonEventId = L"TaskbarButtonCreated"; -// The maximum time we allow before forcing a native event callback -#define NATIVE_EVENT_STARVATION_LIMIT mozilla::TimeDuration::FromSeconds(1) - static UINT sMsgId; -static UINT sTaskbarButtonCreatedMsg; +UINT sTaskbarButtonCreatedMsg; /* static */ UINT nsAppShell::GetTaskbarButtonCreatedMessage() { @@ -241,6 +238,12 @@ nsAppShell::Run(void) #endif +NS_IMETHODIMP +nsAppShell::Exit(void) +{ + return nsBaseAppShell::Exit(); +} + void nsAppShell::DoProcessMoreGeckoEvents() { diff --git a/widget/windows/nsAppShell.h b/widget/windows/nsAppShell.h index fc9b126eb521..feafe166b18e 100644 --- a/widget/windows/nsAppShell.h +++ b/widget/windows/nsAppShell.h @@ -10,6 +10,9 @@ #include #include "mozilla/TimeStamp.h" +// The maximum time we allow before forcing a native event callback +#define NATIVE_EVENT_STARVATION_LIMIT mozilla::TimeDuration::FromSeconds(1) + /** * Native Win32 Application shell wrapper */ @@ -31,6 +34,7 @@ protected: #if defined(_MSC_VER) && defined(_M_IX86) NS_IMETHOD Run(); #endif + NS_IMETHOD Exit(); virtual void ScheduleNativeEventCallback(); virtual bool ProcessNextNativeEvent(bool mayWait); virtual ~nsAppShell(); diff --git a/widget/windows/nsClipboard.cpp b/widget/windows/nsClipboard.cpp index 8f27a33f2c85..ff51ed904531 100644 --- a/widget/windows/nsClipboard.cpp +++ b/widget/windows/nsClipboard.cpp @@ -272,8 +272,10 @@ nsresult nsClipboard::GetGlobalData(HGLOBAL aHGBL, void ** aData, uint32_t * aLe result = NS_OK; } - } - else { + } else { +#ifdef MOZ_METRO + return result; +#endif // We really shouldn't ever get here // but just in case *aData = nullptr; @@ -301,12 +303,12 @@ nsresult nsClipboard::GetGlobalData(HGLOBAL aHGBL, void ** aData, uint32_t * aLe } //------------------------------------------------------------------------- -nsresult nsClipboard::GetNativeDataOffClipboard(nsIWidget * aWindow, UINT /*aIndex*/, UINT aFormat, void ** aData, uint32_t * aLen) +nsresult nsClipboard::GetNativeDataOffClipboard(nsIWidget * aWidget, UINT /*aIndex*/, UINT aFormat, void ** aData, uint32_t * aLen) { HGLOBAL hglb; nsresult result = NS_ERROR_FAILURE; - HWND nativeWin = nullptr;//(HWND)aWindow->GetNativeData(NS_NATIVE_WINDOW); + HWND nativeWin = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW); if (::OpenClipboard(nativeWin)) { hglb = ::GetClipboardData(aFormat); result = GetGlobalData(hglb, aData, aLen); diff --git a/widget/windows/nsLookAndFeel.cpp b/widget/windows/nsLookAndFeel.cpp index a3210764884b..c1110f624058 100644 --- a/widget/windows/nsLookAndFeel.cpp +++ b/widget/windows/nsLookAndFeel.cpp @@ -6,7 +6,6 @@ #include "nsLookAndFeel.h" #include #include -#include "nsWindow.h" #include "nsStyleConsts.h" #include "nsUXThemeData.h" #include "nsUXThemeConstants.h" diff --git a/widget/windows/nsNativeThemeWin.h b/widget/windows/nsNativeThemeWin.h index f178e3e1c4e8..28edc72c9eb8 100644 --- a/widget/windows/nsNativeThemeWin.h +++ b/widget/windows/nsNativeThemeWin.h @@ -104,6 +104,3 @@ protected: bool IsMenuActive(nsIFrame* aFrame, uint8_t aWidgetType); }; - -// Creator function -extern NS_METHOD NS_NewNativeThemeWin(nsISupports *aOuter, REFNSIID aIID, void **aResult); diff --git a/widget/windows/nsToolkit.cpp b/widget/windows/nsToolkit.cpp index c5830556e23a..a66913084f66 100644 --- a/widget/windows/nsToolkit.cpp +++ b/widget/windows/nsToolkit.cpp @@ -27,7 +27,9 @@ static const unsigned long kD3DUsageDelay = 5000; static void StartAllowingD3D9(nsITimer *aTimer, void *aClosure) { - nsWindow::StartAllowingD3D9(true); + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) { + nsWindow::StartAllowingD3D9(true); + } } MouseTrailer* nsToolkit::gMouseTrailer; @@ -47,11 +49,13 @@ nsToolkit::nsToolkit() gMouseTrailer = &mMouseTrailer; - mD3D9Timer = do_CreateInstance("@mozilla.org/timer;1"); - mD3D9Timer->InitWithFuncCallback(::StartAllowingD3D9, - NULL, - kD3DUsageDelay, - nsITimer::TYPE_ONE_SHOT); + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) { + mD3D9Timer = do_CreateInstance("@mozilla.org/timer;1"); + mD3D9Timer->InitWithFuncCallback(::StartAllowingD3D9, + NULL, + kD3DUsageDelay, + nsITimer::TYPE_ONE_SHOT); + } } @@ -83,8 +87,10 @@ nsToolkit::Shutdown() void nsToolkit::StartAllowingD3D9() { - nsToolkit::GetToolkit()->mD3D9Timer->Cancel(); - nsWindow::StartAllowingD3D9(false); + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) { + nsToolkit::GetToolkit()->mD3D9Timer->Cancel(); + nsWindow::StartAllowingD3D9(false); + } } //------------------------------------------------------------------------- diff --git a/widget/windows/nsWidgetFactory.cpp b/widget/windows/nsWidgetFactory.cpp index 8c3906e5de37..6cb6c81a9a14 100644 --- a/widget/windows/nsWidgetFactory.cpp +++ b/widget/windows/nsWidgetFactory.cpp @@ -8,35 +8,46 @@ #include "nsISupports.h" #include "nsdefs.h" #include "nsWidgetsCID.h" - #include "nsAppShell.h" #include "nsAppShellSingleton.h" -#include "nsFilePicker.h" #include "mozilla/ModuleUtils.h" #include "nsIServiceManager.h" #include "nsIdleServiceWin.h" #include "nsLookAndFeel.h" -#include "nsNativeThemeWin.h" #include "nsScreenManagerWin.h" #include "nsSound.h" -#include "nsWindow.h" #include "WinMouseScrollHandler.h" -#include "WinTaskbar.h" -#include "JumpListBuilder.h" -#include "JumpListItem.h" #include "GfxInfo.h" #include "nsToolkit.h" +// Modules that switch out based on the environment +#include "nsXULAppAPI.h" +// Desktop +#include "nsFilePicker.h" // needs to be included before other shobjidl.h includes +#include "nsNativeThemeWin.h" +#include "nsWindow.h" +// Content processes +#include "nsFilePickerProxy.h" +// Metro +#ifdef MOZ_METRO +#include "winrt/MetroAppShell.h" +#include "winrt/MetroWidget.h" +#include "winrt/nsMetroFilePicker.h" +#include "winrt/nsWinMetroUtils.h" +#endif + // Drag & Drop, Clipboard - #include "nsClipboardHelper.h" - #include "nsClipboard.h" #include "nsBidiKeyboard.h" #include "nsDragService.h" #include "nsTransferable.h" #include "nsHTMLFormatConverter.h" +#include "WinTaskbar.h" +#include "JumpListBuilder.h" +#include "JumpListItem.h" + #ifdef NS_PRINTING #include "nsDeviceContextSpecWin.h" #include "nsPrintOptionsWin.h" @@ -45,21 +56,98 @@ using namespace mozilla::widget; -NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) -NS_GENERIC_FACTORY_CONSTRUCTOR(ChildWindow) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePicker) +static nsresult +WindowConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + *aResult = nullptr; + if (aOuter != nullptr) { + return NS_ERROR_NO_AGGREGATION; + } + nsCOMPtr widget; + + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { +#ifdef MOZ_METRO + widget = new MetroWidget; +#else + NS_RUNTIMEABORT("build does not support metro."); +#endif + } else { + widget = new nsWindow; + } + + return widget->QueryInterface(aIID, aResult); +} + +static nsresult +ChildWindowConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + *aResult = nullptr; + if (aOuter != nullptr) { + return NS_ERROR_NO_AGGREGATION; + } + nsCOMPtr widget; + + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { + return NS_NOINTERFACE; + } else { + widget = new ChildWindow; + } + + return widget->QueryInterface(aIID, aResult); +} + +static nsresult +FilePickerConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + *aResult = nullptr; + if (aOuter != nullptr) { + return NS_ERROR_NO_AGGREGATION; + } + nsCOMPtr picker; + + if (XRE_GetProcessType() == GeckoProcessType_Content) { + picker = new nsFilePickerProxy(); + } else { + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { +#ifdef MOZ_METRO + picker = new nsMetroFilePicker; +#else + NS_RUNTIMEABORT("build does not support metro."); +#endif + } else { + picker = new nsFilePicker; + } + } + return picker->QueryInterface(aIID, aResult); +} + NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerWin) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceWin, nsIdleServiceWin::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard) NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper) NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound) - NS_GENERIC_FACTORY_CONSTRUCTOR(WinTaskbar) NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListBuilder) NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListItem) NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListSeparator) NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListLink) NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListShortcut) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard) +#ifdef MOZ_METRO +NS_GENERIC_FACTORY_CONSTRUCTOR(nsWinMetroUtils) +#endif +#ifdef NS_PRINTING +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsWin, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsPrinterEnumeratorWin) +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecWin) +#endif namespace mozilla { namespace widget { @@ -68,18 +156,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init); } } -NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard) - -#ifdef NS_PRINTING -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsWin, Init) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsPrinterEnumeratorWin) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecWin) -#endif - NS_DEFINE_NAMED_CID(NS_WINDOW_CID); NS_DEFINE_NAMED_CID(NS_CHILD_CID); NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID); @@ -93,17 +169,17 @@ NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID); NS_DEFINE_NAMED_CID(NS_SOUND_CID); NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID); NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID); - NS_DEFINE_NAMED_CID(NS_WIN_TASKBAR_CID); NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTBUILDER_CID); NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTITEM_CID); NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTSEPARATOR_CID); NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTLINK_CID); NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTSHORTCUT_CID); - +#ifdef MOZ_METRO +NS_DEFINE_NAMED_CID(NS_WIN_METROUTILS_CID); +#endif NS_DEFINE_NAMED_CID(NS_DRAGSERVICE_CID); NS_DEFINE_NAMED_CID(NS_BIDIKEYBOARD_CID); - #ifdef NS_PRINTING NS_DEFINE_NAMED_CID(NS_PRINTSETTINGSSERVICE_CID); NS_DEFINE_NAMED_CID(NS_PRINTER_ENUMERATOR_CID); @@ -113,9 +189,9 @@ NS_DEFINE_NAMED_CID(NS_DEVICE_CONTEXT_SPEC_CID); static const mozilla::Module::CIDEntry kWidgetCIDs[] = { - { &kNS_WINDOW_CID, false, NULL, nsWindowConstructor }, + { &kNS_WINDOW_CID, false, NULL, WindowConstructor }, { &kNS_CHILD_CID, false, NULL, ChildWindowConstructor }, - { &kNS_FILEPICKER_CID, false, NULL, nsFilePickerConstructor }, + { &kNS_FILEPICKER_CID, false, NULL, FilePickerConstructor }, { &kNS_APPSHELL_CID, false, NULL, nsAppShellConstructor }, { &kNS_SCREENMANAGER_CID, false, NULL, nsScreenManagerWinConstructor }, { &kNS_GFXINFO_CID, false, NULL, GfxInfoConstructor }, @@ -134,6 +210,9 @@ static const mozilla::Module::CIDEntry kWidgetCIDs[] = { { &kNS_WIN_JUMPLISTSHORTCUT_CID, false, NULL, JumpListShortcutConstructor }, { &kNS_DRAGSERVICE_CID, false, NULL, nsDragServiceConstructor }, { &kNS_BIDIKEYBOARD_CID, false, NULL, nsBidiKeyboardConstructor }, +#ifdef MOZ_METRO + { &kNS_WIN_METROUTILS_CID, false, NULL, nsWinMetroUtilsConstructor }, +#endif #ifdef NS_PRINTING { &kNS_PRINTSETTINGSSERVICE_CID, false, NULL, nsPrintOptionsWinConstructor }, { &kNS_PRINTER_ENUMERATOR_CID, false, NULL, nsPrinterEnumeratorWinConstructor }, @@ -165,6 +244,9 @@ static const mozilla::Module::ContractIDEntry kWidgetContracts[] = { { "@mozilla.org/windows-jumplistshortcut;1", &kNS_WIN_JUMPLISTSHORTCUT_CID }, { "@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID }, { "@mozilla.org/widget/bidikeyboard;1", &kNS_BIDIKEYBOARD_CID }, +#ifdef MOZ_METRO + { "@mozilla.org/windows-metroutils;1", &kNS_WIN_METROUTILS_CID }, +#endif #ifdef NS_PRINTING { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID }, { "@mozilla.org/gfx/printerenumerator;1", &kNS_PRINTER_ENUMERATOR_CID }, diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 67b2a9ed492d..1f11854f12cf 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -1953,7 +1953,7 @@ nsWindow::ResetLayout() // Send a gecko size event to trigger reflow. RECT clientRc = {0}; GetClientRect(mWnd, &clientRc); - nsIntRect evRect(nsWindowGfx::ToIntRect(clientRc)); + nsIntRect evRect(WinUtils::ToIntRect(clientRc)); OnResize(evRect); // Invalidate and update diff --git a/widget/windows/nsWindowGfx.cpp b/widget/windows/nsWindowGfx.cpp index 64f944f15355..b9b5541026a7 100644 --- a/widget/windows/nsWindowGfx.cpp +++ b/widget/windows/nsWindowGfx.cpp @@ -105,36 +105,6 @@ IsRenderMode(gfxWindowsPlatform::RenderMode rmode) return gfxWindowsPlatform::GetPlatform()->GetRenderMode() == rmode; } -nsIntRegion -nsWindowGfx::ConvertHRGNToRegion(HRGN aRgn) -{ - NS_ASSERTION(aRgn, "Don't pass NULL region here"); - - nsIntRegion rgn; - - DWORD size = ::GetRegionData(aRgn, 0, NULL); - nsAutoTArray buffer; - if (!buffer.SetLength(size)) - return rgn; - - RGNDATA* data = reinterpret_cast(buffer.Elements()); - if (!::GetRegionData(aRgn, size, data)) - return rgn; - - if (data->rdh.nCount > MAX_RECTS_IN_REGION) { - rgn = ToIntRect(data->rdh.rcBound); - return rgn; - } - - RECT* rects = reinterpret_cast(data->Buffer); - for (uint32_t i = 0; i < data->rdh.nCount; ++i) { - RECT* r = rects + i; - rgn.Or(rgn, ToIntRect(*r)); - } - - return rgn; -} - /************************************************************** ************************************************************** ** @@ -152,7 +122,7 @@ nsIntRegion nsWindow::GetRegionToPaint(bool aForceFullRepaint, if (aForceFullRepaint) { RECT paintRect; ::GetClientRect(mWnd, &paintRect); - return nsIntRegion(nsWindowGfx::ToIntRect(paintRect)); + return nsIntRegion(WinUtils::ToIntRect(paintRect)); } HRGN paintRgn = ::CreateRectRgn(0, 0, 0, 0); @@ -163,11 +133,11 @@ nsIntRegion nsWindow::GetRegionToPaint(bool aForceFullRepaint, ::MapWindowPoints(NULL, mWnd, &pt, 1); ::OffsetRgn(paintRgn, pt.x, pt.y); } - nsIntRegion rgn(nsWindowGfx::ConvertHRGNToRegion(paintRgn)); + nsIntRegion rgn(WinUtils::ConvertHRGNToRegion(paintRgn)); ::DeleteObject(paintRgn); return rgn; } - return nsIntRegion(nsWindowGfx::ToIntRect(ps.rcPaint)); + return nsIntRegion(WinUtils::ToIntRect(ps.rcPaint)); } #define WORDSSIZE(x) ((x).width * (x).height) diff --git a/widget/windows/nsWindowGfx.h b/widget/windows/nsWindowGfx.h index 83d656d3ecfe..7969b67452e0 100644 --- a/widget/windows/nsWindowGfx.h +++ b/widget/windows/nsWindowGfx.h @@ -20,14 +20,6 @@ class nsWindowGfx { public: - static nsIntRect ToIntRect(const RECT& aRect) - { - return nsIntRect(aRect.left, aRect.top, - aRect.right - aRect.left, aRect.bottom - aRect.top); - } - - static nsIntRegion ConvertHRGNToRegion(HRGN aRgn); - enum IconSizeType { kSmallIcon, kRegularIcon diff --git a/widget/xpwidgets/nsAppShellSingleton.h b/widget/xpwidgets/nsAppShellSingleton.h index cc6cc244d2ad..84702c02803b 100644 --- a/widget/xpwidgets/nsAppShellSingleton.h +++ b/widget/xpwidgets/nsAppShellSingleton.h @@ -27,19 +27,41 @@ * method for the nsAppShell class. */ -static nsAppShell *sAppShell; +#include "nsXULAppAPI.h" +#if defined(MOZ_METRO) && defined(XP_WIN) +#include "winrt/MetroAppShell.h" +#endif + +static nsIAppShell *sAppShell; static nsresult nsAppShellInit() { NS_ASSERTION(!sAppShell, "already initialized"); +#if !defined(MOZ_METRO) || !defined(XP_WIN) sAppShell = new nsAppShell(); +#else + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { + sAppShell = new MetroAppShell(); + } else { + sAppShell = new nsAppShell(); + } +#endif if (!sAppShell) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(sAppShell); - nsresult rv = sAppShell->Init(); + nsresult rv; +#if !defined(MOZ_METRO) || !defined(XP_WIN) + rv = static_cast(sAppShell)->Init(); +#else + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { + rv = static_cast(sAppShell)->Init(); + } else { + rv = static_cast(sAppShell)->Init(); + } +#endif if (NS_FAILED(rv)) { NS_RELEASE(sAppShell); return rv; From f485a6c791b5525d508e893e8bf593ad105cd952 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 2 Nov 2012 13:27:59 +0100 Subject: [PATCH 37/83] Bug 805807 - Make Components wrapper throw on denial. r=mrbkap There's really no reason to use the wishy-washy static COW Deny() here. Also, note that the xpcshell-test wasn't testing what it thought it was - interfaces is accessible from content code. --- .../libeditor/html/tests/test_bug468353.html | 1 - js/xpconnect/tests/unit/test_components.js | 35 +++++++++---------- js/xpconnect/wrappers/AccessCheck.cpp | 3 +- .../tests/chrome/sandbox_content_perms.html | 11 +++++- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/editor/libeditor/html/tests/test_bug468353.html b/editor/libeditor/html/tests/test_bug468353.html index dd5981fde924..f64581860b9a 100644 --- a/editor/libeditor/html/tests/test_bug468353.html +++ b/editor/libeditor/html/tests/test_bug468353.html @@ -42,7 +42,6 @@ function checkStylesheets() { function runTest() { const Ci = SpecialPowers.Ci; - const Cc = SpecialPowers.Components.classes; /** Found while fixing bug 440614 **/ var editframe = window.frames[0]; diff --git a/js/xpconnect/tests/unit/test_components.js b/js/xpconnect/tests/unit/test_components.js index 5bd1b62bbb54..7cff627e47a6 100644 --- a/js/xpconnect/tests/unit/test_components.js +++ b/js/xpconnect/tests/unit/test_components.js @@ -1,5 +1,6 @@ +const Cu = Components.utils; + function run_test() { - var Cu = Components.utils; var sb1 = Cu.Sandbox("http://www.blah.com"); var sb2 = Cu.Sandbox("http://www.blah.com"); var sb3 = Cu.Sandbox(this); @@ -13,25 +14,19 @@ function run_test() { // non-chrome accessing chrome Components sb1.C = Components; - rv = Cu.evalInSandbox("C.utils", sb1); - do_check_eq(rv, undefined); - rv = Cu.evalInSandbox("C.interfaces", sb1); - do_check_neq(rv, undefined); + checkThrows("C.utils", sb1); + checkThrows("C.classes", sb1); // non-chrome accessing own Components - rv = Cu.evalInSandbox("Components.utils", sb1); - do_check_eq(rv, undefined); - rv = Cu.evalInSandbox("Components.interfaces", sb1); - do_check_neq(rv, undefined); + checkThrows("Components.utils", sb1); + checkThrows("Components.classes", sb1); // non-chrome same origin var C2 = Cu.evalInSandbox("Components", sb2); - do_check_neq(rv, C2.utils); + do_check_neq(rv, C2.utils); sb1.C2 = C2; - rv = Cu.evalInSandbox("C2.utils", sb1); - do_check_eq(rv, undefined); - rv = Cu.evalInSandbox("C2.interfaces", sb1); - do_check_neq(rv, undefined); + checkThrows("C2.utils", sb1); + checkThrows("C2.classes", sb1); // chrome accessing chrome sb3.C = Components; @@ -40,9 +35,11 @@ function run_test() { // non-chrome cross origin sb4.C2 = C2; - rv = Cu.evalInSandbox("C2.interfaces", sb1); - do_check_neq(rv, undefined); - rv = Cu.evalInSandbox("C2.utils", sb1); - do_check_eq(rv, undefined); - + checkThrows("C2.utils", sb1); + checkThrows("C2.classes", sb1); +} + +function checkThrows(expression, sb) { + var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb); + do_check_true(!!/denied/.exec(result)); } diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index 2d4744eb1dcf..ad986e758fd6 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -523,7 +523,8 @@ ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper return true; } - return Deny(cx, id, act); + AccessCheck::deny(cx, id); + return false; } } diff --git a/toolkit/identity/tests/chrome/sandbox_content_perms.html b/toolkit/identity/tests/chrome/sandbox_content_perms.html index 1f99dd5d2882..59905f13c241 100644 --- a/toolkit/identity/tests/chrome/sandbox_content_perms.html +++ b/toolkit/identity/tests/chrome/sandbox_content_perms.html @@ -18,6 +18,15 @@ return false; } + function CcDenied() { + try { + Components.classes; + return false; + } catch (e) { + return !!/denied/.exec(e); + } + } + // Build an object with test results (true = pass) let results = { windowTop: window.top == window, @@ -28,7 +37,7 @@ .docCharsetIsForced; }), - ccAccess: SpecialPowers.Components.classes == null, + ccAccess: !!CcDenied(), }; let resultsJSON = JSON.stringify(results); From 6559feed97244f126e719324612f970da6449d8b Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 2 Nov 2012 13:27:59 +0100 Subject: [PATCH 38/83] Bug 805807 - Rearchitect filtering policies so that check() doesn't throw on denial. r=mrbkap This is another one of those annoying situaitons in XPConnect right now where we can't ask a question without potentially throwing if the answer is no. There's also a bunch of unused cruft in here (like the Perm*Access stuff), so this stuff was ripe for a spring cleaning. Unfortunately, I wasn't able to divide this patch up nicely. Sorry for the big diff. :-( In a nutshell, this patch changes things so that Policy::check() just becomes a predicate that says whether the access is allowed or not. There's the remote possibility that one of the underlying JSAPI calls in a ::check() implementation might throw, so callers to ::check() should check JS_IsExceptionPending afterwards (this doesn't catch OOM, but we can just continue along until the next OOM-triggering operation and throw there). Aside from exceptional cases, callers should call Policy::deny if they want to report the failure. Policy::deny returns success value that should be returned to the wrapper's consumer. --- js/src/jswrapper.h | 6 -- js/xpconnect/wrappers/AccessCheck.cpp | 55 ++++------------- js/xpconnect/wrappers/AccessCheck.h | 72 +++++++++------------- js/xpconnect/wrappers/FilteringWrapper.cpp | 25 +++----- 4 files changed, 51 insertions(+), 107 deletions(-) diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 4cbfecd642af..e8f834ab0753 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -43,12 +43,6 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler LAST_USED_FLAG = CROSS_COMPARTMENT }; - typedef enum { - PermitObjectAccess, - PermitPropertyAccess, - DenyAccess - } Permission; - static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler); diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index ad986e758fd6..1eded44fccee 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -329,17 +329,6 @@ AccessCheck::deny(JSContext *cx, jsid id) enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 }; -static bool -Deny(JSContext *cx, jsid id, Wrapper::Action act) -{ - // Refuse to perform the action and just return the default value. - if (act == Wrapper::GET) - return true; - // If its a set, deny it and throw an exception. - AccessCheck::deny(cx, id); - return false; -} - static bool IsInSandbox(JSContext *cx, JSObject *obj) { @@ -349,19 +338,15 @@ IsInSandbox(JSContext *cx, JSObject *obj) } bool -ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, - Permission &perm) +ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act) { JSObject *wrappedObject = Wrapper::wrappedObject(wrapper); - if (act == Wrapper::CALL) { - perm = PermitObjectAccess; + if (act == Wrapper::CALL) return true; - } - perm = DenyAccess; if (act == Wrapper::PUNCTURE) - return Deny(cx, id, act); + return false; jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS); @@ -380,7 +365,6 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: JS_IsTypedArrayObject(wrappedObject, cx)) && ((JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) || (JSID_IS_STRING(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length")))) { - perm = PermitPropertyAccess; return true; // Allow } @@ -405,26 +389,20 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: } } - perm = PermitPropertyAccess; return true; } - return Deny(cx, id, act); + return false; } - if (id == JSID_VOID) { - // This will force the caller to call us back for individual property accesses. - perm = PermitPropertyAccess; + if (id == JSID_VOID) return true; - } JS::Value exposedProps; if (!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, &exposedProps)) return false; - if (exposedProps.isNullOrUndefined()) { - JSAutoCompartment wrapperAC(cx, wrapper); - return Deny(cx, id, act); - } + if (exposedProps.isNullOrUndefined()) + return false; if (!exposedProps.isObject()) { JS_ReportError(cx, "__exposedProps__ must be undefined, null, or an Object"); @@ -440,10 +418,8 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: if (!JS_GetPropertyDescriptorById(cx, hallpass, id, JSRESOLVE_QUALIFIED, &desc)) { return false; // Error } - if (desc.obj == NULL || !(desc.attrs & JSPROP_ENUMERATE)) { - JSAutoCompartment wrapperAC(cx, wrapper); - return Deny(cx, id, act); - } + if (!desc.obj || !(desc.attrs & JSPROP_ENUMERATE)) + return false; if (!JSVAL_IS_STRING(desc.value)) { JS_ReportError(cx, "property must be a string"); @@ -487,19 +463,15 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: if ((act == Wrapper::SET && !(access & WRITE)) || (act != Wrapper::SET && !(access & READ))) { - JSAutoCompartment wrapperAC(cx, wrapper); - return Deny(cx, id, act); + return false; } - perm = PermitPropertyAccess; - return true; // Allow + return true; } bool -ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, - Permission &perm) +ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act) { - perm = DenyAccess; JSAutoCompartment ac(cx, wrapper); if (JSID_IS_STRING(id) && act == Wrapper::GET) { @@ -510,7 +482,6 @@ ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper JS_FlatStringEqualsAscii(flatId, "interfacesByID") || JS_FlatStringEqualsAscii(flatId, "results")) { - perm = PermitPropertyAccess; return true; } } @@ -519,11 +490,9 @@ ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper // so we need this dynamic check. This can go away when we expose Components // as SpecialPowers.wrap(Components) during automation. if (xpc::IsUniversalXPConnectEnabled(cx)) { - perm = PermitPropertyAccess; return true; } - AccessCheck::deny(cx, id); return false; } diff --git a/js/xpconnect/wrappers/AccessCheck.h b/js/xpconnect/wrappers/AccessCheck.h index 343a5a10a7e2..e85c3767f2f2 100644 --- a/js/xpconnect/wrappers/AccessCheck.h +++ b/js/xpconnect/wrappers/AccessCheck.h @@ -39,33 +39,16 @@ class AccessCheck { }; struct Policy { - typedef js::Wrapper::Permission Permission; - - static const Permission PermitObjectAccess = js::Wrapper::PermitObjectAccess; - static const Permission PermitPropertyAccess = js::Wrapper::PermitPropertyAccess; - static const Permission DenyAccess = js::Wrapper::DenyAccess; -}; - -// This policy permits access to all properties. -struct Permissive : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, - Permission &perm) { - perm = PermitObjectAccess; - return true; - } }; // This policy only permits access to the object if the subject can touch // system objects. struct OnlyIfSubjectIsSystem : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, - Permission &perm) { - if (AccessCheck::isSystemOnlyAccessPermitted(cx)) { - perm = PermitObjectAccess; - return true; - } - perm = DenyAccess; - JSAutoCompartment ac(cx, wrapper); + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { + return AccessCheck::isSystemOnlyAccessPermitted(cx); + } + + static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { AccessCheck::deny(cx, id); return false; } @@ -74,17 +57,12 @@ struct OnlyIfSubjectIsSystem : public Policy { // This policy only permits access to properties that are safe to be used // across origins. struct CrossOriginAccessiblePropertiesOnly : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, - Permission &perm) { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { // Location objects should always use LocationPolicy. MOZ_ASSERT(!WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper))); - - if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act)) { - perm = PermitPropertyAccess; - return true; - } - perm = DenyAccess; - JSAutoCompartment ac(cx, wrapper); + return AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act); + } + static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { AccessCheck::deny(cx, id); return false; } @@ -114,23 +92,19 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy { // state of the outer window to determine whether we happen to be same-origin // at the moment. struct LocationPolicy : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, - Permission &perm) { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { // We should only be dealing with Location objects here. MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper))); - // Default to deny. - perm = DenyAccess; - // Location object security is complicated enough. Don't allow punctures. if (act != js::Wrapper::PUNCTURE && (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) || AccessCheck::isLocationObjectSameOrigin(cx, wrapper))) { - perm = PermitPropertyAccess; return true; } - - JSAutoCompartment ac(cx, wrapper); + return false; + } + static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { AccessCheck::deny(cx, id); return false; } @@ -139,14 +113,26 @@ struct LocationPolicy : public Policy { // This policy only permits access to properties if they appear in the // objects exposed properties list. struct ExposedPropertiesOnly : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, - Permission &perm); + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act); + + static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { + // For gets, silently fail. + if (act == js::Wrapper::GET) + return true; + // For sets,throw an exception. + AccessCheck::deny(cx, id); + return false; + } }; // Components specific policy struct ComponentsObjectPolicy : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, - Permission &perm); + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act); + + static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { + AccessCheck::deny(cx, id); + return false; + } }; } diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index 9d84db8f1233..6fefa962797a 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -29,12 +29,6 @@ FilteringWrapper::~FilteringWrapper() { } -typedef Wrapper::Permission Permission; - -static const Permission PermitObjectAccess = Wrapper::PermitObjectAccess; -static const Permission PermitPropertyAccess = Wrapper::PermitPropertyAccess; -static const Permission DenyAccess = Wrapper::DenyAccess; - template static bool Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) @@ -42,11 +36,10 @@ Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) size_t w = 0; for (size_t n = 0; n < props.length(); ++n) { jsid id = props[n]; - Permission perm; - if (!Policy::check(cx, wrapper, id, Wrapper::GET, perm)) - return false; // Error - if (perm != DenyAccess) + if (Policy::check(cx, wrapper, id, Wrapper::GET)) props[w++] = id; + else if (JS_IsExceptionPending(cx)) + return false; } props.resize(w); return true; @@ -92,14 +85,16 @@ bool FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, bool *bp) { - Permission perm; - if (!Policy::check(cx, wrapper, id, act, perm)) { - *bp = false; + if (!Policy::check(cx, wrapper, id, act)) { + if (JS_IsExceptionPending(cx)) { + *bp = false; + return false; + } + JSAutoCompartment ac(cx, wrapper); + *bp = Policy::deny(cx, id, act); return false; } *bp = true; - if (perm == DenyAccess) - return false; return Base::enter(cx, wrapper, id, act, bp); } From 10682d00c09f25964ed28e74b7f97ad6d4f09ae9 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 30 Oct 2012 21:26:32 -0700 Subject: [PATCH 39/83] Bug 807223 - Implement JS_ArrayBufferViewBuffer. r=sfink This is really sfink's patch, but we wrote basically the same code and the review looks better this way. --HG-- extra : rebase_source : 90d52ff6c9b7050abde98fa582984c88fa909f0c --- js/src/jsapi-tests/testTypedArrays.cpp | 1 + js/src/jsfriendapi.h | 8 ++++++++ js/src/jstypedarray.cpp | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/js/src/jsapi-tests/testTypedArrays.cpp b/js/src/jsapi-tests/testTypedArrays.cpp index 8c26712142d3..7a4cb17953db 100644 --- a/js/src/jsapi-tests/testTypedArrays.cpp +++ b/js/src/jsapi-tests/testTypedArrays.cpp @@ -99,6 +99,7 @@ TestArrayFromBuffer(JSContext *cx) CHECK_EQUAL(JS_GetTypedArrayLength(array, cx), elts); CHECK_EQUAL(JS_GetTypedArrayByteOffset(array, cx), 0); CHECK_EQUAL(JS_GetTypedArrayByteLength(array, cx), nbytes); + CHECK_EQUAL(JS_GetArrayBufferViewBuffer(array, cx), (JSObject*) buffer); Element *data; CHECK(data = GetData(array, cx)); diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index b09ebeb3d269..89c3280421a7 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1335,6 +1335,14 @@ JS_GetFloat64ArrayData(JSObject *obj, JSContext *maybecx); extern JS_FRIEND_API(void *) JS_GetArrayBufferViewData(JSObject *obj, JSContext *maybecx); +/* + * Return the ArrayBuffer underlying an ArrayBufferView. If the buffer has been + * neutered, this will still return the neutered buffer. |obj| must be an + * object that would return true for JS_IsArrayBufferViewObject(). + */ +extern JS_FRIEND_API(JSObject *) +JS_GetArrayBufferViewBuffer(JSObject *obj, JSContext *maybecx); + /* * Check whether obj supports JS_GetDataView* APIs. Note that this may fail and * throw an exception if a security wrapper is encountered that denies the diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index a7e106b7265f..9eaf26ddb59f 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -3880,6 +3880,16 @@ JS_GetArrayBufferViewData(JSObject *obj, JSContext *maybecx) return obj->isDataView() ? obj->asDataView().dataPointer() : TypedArray::viewData(obj); } +JS_FRIEND_API(JSObject *) +JS_GetArrayBufferViewBuffer(JSObject *obj, JSContext *maybecx) +{ + obj = CheckedUnwrap(maybecx, obj); + if (!obj) + return NULL; + JS_ASSERT(obj->isTypedArray() || obj->isDataView()); + return &obj->getFixedSlot(BufferView::BUFFER_SLOT).toObject(); +} + JS_FRIEND_API(uint32_t) JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *maybecx) { From 629f20cb90248536485020447a20e14a114f2e7c Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 2 Nov 2012 23:42:58 +1300 Subject: [PATCH 40/83] Bug 807212. Call TypedArray methods instead of accessing slots directly. r=sfink --HG-- extra : rebase_source : 05af21bea8caf6d80bb3f97762d559f21e0c4157 --- js/src/jstypedarray.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 9eaf26ddb59f..4b3f39d0c276 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -3178,7 +3178,7 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) if (clasp != &TypedArray::classes[TypedArrayTemplate::ArrayTypeID()]) \ return NULL; \ \ - *length = obj->getSlot(TypedArray::LENGTH_SLOT).toInt32(); \ + *length = TypedArray::length(obj); \ *data = static_cast(TypedArray::viewData(obj)); \ \ return obj; \ @@ -3698,7 +3698,7 @@ JS_GetTypedArrayLength(JSObject *obj, JSContext *maybecx) if (!obj) return 0; JS_ASSERT(obj->isTypedArray()); - return obj->getSlot(TypedArray::LENGTH_SLOT).toInt32(); + return TypedArray::length(obj); } JS_FRIEND_API(uint32_t) @@ -3708,7 +3708,7 @@ JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *maybecx) if (!obj) return 0; JS_ASSERT(obj->isTypedArray()); - return obj->getSlot(TypedArray::BYTEOFFSET_SLOT).toInt32(); + return TypedArray::byteOffset(obj); } JS_FRIEND_API(uint32_t) @@ -3718,7 +3718,7 @@ JS_GetTypedArrayByteLength(JSObject *obj, JSContext *maybecx) if (!obj) return 0; JS_ASSERT(obj->isTypedArray()); - return obj->getSlot(TypedArray::BYTELENGTH_SLOT).toInt32(); + return TypedArray::byteLength(obj); } JS_FRIEND_API(JSArrayBufferViewType) @@ -3728,7 +3728,7 @@ JS_GetTypedArrayType(JSObject *obj, JSContext *maybecx) if (!obj) return ArrayBufferView::TYPE_MAX; JS_ASSERT(obj->isTypedArray()); - return static_cast(obj->getSlot(TypedArray::TYPE_SLOT).toInt32()); + return static_cast(TypedArray::type(obj)); } JS_FRIEND_API(int8_t *) @@ -3738,7 +3738,7 @@ JS_GetInt8ArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_INT8); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_INT8); return static_cast(TypedArray::viewData(obj)); } @@ -3749,7 +3749,7 @@ JS_GetUint8ArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_UINT8); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_UINT8); return static_cast(TypedArray::viewData(obj)); } @@ -3760,7 +3760,7 @@ JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_UINT8_CLAMPED); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_UINT8_CLAMPED); return static_cast(TypedArray::viewData(obj)); } @@ -3771,7 +3771,7 @@ JS_GetInt16ArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_INT16); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_INT16); return static_cast(TypedArray::viewData(obj)); } @@ -3782,7 +3782,7 @@ JS_GetUint16ArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_UINT16); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_UINT16); return static_cast(TypedArray::viewData(obj)); } @@ -3793,7 +3793,7 @@ JS_GetInt32ArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_INT32); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_INT32); return static_cast(TypedArray::viewData(obj)); } @@ -3804,7 +3804,7 @@ JS_GetUint32ArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_UINT32); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_UINT32); return static_cast(TypedArray::viewData(obj)); } @@ -3815,7 +3815,7 @@ JS_GetFloat32ArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_FLOAT32); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_FLOAT32); return static_cast(TypedArray::viewData(obj)); } @@ -3826,7 +3826,7 @@ JS_GetFloat64ArrayData(JSObject *obj, JSContext *maybecx) if (!obj) return NULL; JS_ASSERT(obj->isTypedArray()); - JS_ASSERT(obj->getSlot(TypedArray::TYPE_SLOT).toInt32() == ArrayBufferView::TYPE_FLOAT64); + JS_ASSERT(TypedArray::type(obj) == ArrayBufferView::TYPE_FLOAT64); return static_cast(TypedArray::viewData(obj)); } From 0fecd165f292f2dcbd381f24444746b2802b5f96 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 2 Nov 2012 23:42:59 +1300 Subject: [PATCH 41/83] Bug 807237. Add 'data' parameter to JS_StealArrayBufferContents. r=sfink --HG-- extra : rebase_source : 5c9fe8f9b09ac63f3e130e7ff3418301f868cb8d --- js/src/jsapi-tests/testArrayBuffer.cpp | 15 ++++++++++----- js/src/jsapi.h | 9 +++++++-- js/src/jsclone.cpp | 3 ++- js/src/jstypedarray.cpp | 10 +++++++--- js/src/jstypedarray.h | 3 ++- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/js/src/jsapi-tests/testArrayBuffer.cpp b/js/src/jsapi-tests/testArrayBuffer.cpp index 403d3e447fd8..2dd591e53593 100644 --- a/js/src/jsapi-tests/testArrayBuffer.cpp +++ b/js/src/jsapi-tests/testArrayBuffer.cpp @@ -52,8 +52,9 @@ BEGIN_TEST(testArrayBuffer_bug720949_steal) // Steal the contents void *contents; - CHECK(JS_StealArrayBufferContents(cx, obj, &contents)); + CHECK(JS_StealArrayBufferContents(cx, obj, &contents, &data)); CHECK(contents != NULL); + CHECK(data != NULL); // Check that the original ArrayBuffer is neutered CHECK_EQUAL(JS_GetArrayBufferByteLength(obj, cx), 0); @@ -111,9 +112,11 @@ BEGIN_TEST(testArrayBuffer_bug720949_viewList) buffer = JS_NewArrayBuffer(cx, 2000); js::RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1)); void *contents; - CHECK(JS_StealArrayBufferContents(cx, buffer, &contents)); + uint8_t *data; + CHECK(JS_StealArrayBufferContents(cx, buffer, &contents, &data)); CHECK(contents != NULL); - JS_free(cx, contents); + CHECK(data != NULL); + JS_free(NULL, contents); GC(cx); CHECK(isNeutered(view)); CHECK(isNeutered(buffer)); @@ -137,9 +140,11 @@ BEGIN_TEST(testArrayBuffer_bug720949_viewList) // Neuter void *contents; - CHECK(JS_StealArrayBufferContents(cx, buffer, &contents)); + uint8_t *data; + CHECK(JS_StealArrayBufferContents(cx, buffer, &contents, &data)); CHECK(contents != NULL); - JS_free(cx, contents); + CHECK(data != NULL); + JS_free(NULL, contents); CHECK(isNeutered(view1)); CHECK(isNeutered(view2)); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 0940bf6422c6..ab5cbaa654f6 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3409,6 +3409,7 @@ JS_realloc(JSContext *cx, void *p, size_t nbytes); /* * A wrapper for js_free(p) that may delay js_free(p) invocation as a * performance optimization. + * cx may be NULL. */ extern JS_PUBLIC_API(void) JS_free(JSContext *cx, void *p); @@ -4617,11 +4618,15 @@ JS_NewArrayBufferWithContents(JSContext *cx, void *contents); /* * Steal the contents of the given array buffer. The array buffer has its * length set to 0 and its contents array cleared. The caller takes ownership - * of |contents| and must free it or transfer ownership via + * of |*contents| and must free it or transfer ownership via * JS_NewArrayBufferWithContents when done using it. + * To free |*contents|, call free(). + * A pointer to the buffer's data is returned in |*data|. This pointer can + * be used until |*contents| is freed or has its ownership transferred. */ extern JS_PUBLIC_API(JSBool) -JS_StealArrayBufferContents(JSContext *cx, JSObject *obj, void **contents); +JS_StealArrayBufferContents(JSContext *cx, JSObject *obj, void **contents, + uint8_t **data); /* * Allocate memory that may be eventually passed to diff --git a/js/src/jsclone.cpp b/js/src/jsclone.cpp index d2409c6c1fe4..58d33da2580c 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -732,7 +732,8 @@ JSStructuredCloneWriter::writeTransferMap() return false; void *content; - if (!JS_StealArrayBufferContents(context(), obj, &content)) + uint8_t *data; + if (!JS_StealArrayBufferContents(context(), obj, &content, &data)) return false; if (!out.writePair(SCTAG_TRANSFER_MAP, 0) || !out.writePtr(content)) diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 4b3f39d0c276..ac0572628e7b 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -461,13 +461,15 @@ ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp } bool -ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents) +ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents, + uint8_t **data) { ArrayBufferObject &buffer = obj->asArrayBuffer(); JSObject *views = *GetViewList(&buffer); js::ObjectElements *header = js::ObjectElements::fromElements((js::HeapSlot*)buffer.dataPointer()); if (buffer.hasDynamicElements()) { *contents = header; + *data = buffer.dataPointer(); buffer.setFixedElements(); header = js::ObjectElements::fromElements((js::HeapSlot*)buffer.dataPointer()); @@ -482,6 +484,7 @@ ArrayBufferObject::stealContents(JSContext *cx, JSObject *obj, void **contents) ArrayBufferObject::setElementsHeader(newheader, length); *contents = newheader; + *data = reinterpret_cast(newheader + 1); } // Neuter the donor ArrayBuffer and all views of it @@ -3675,7 +3678,8 @@ JS_AllocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void **contents, } JS_PUBLIC_API(JSBool) -JS_StealArrayBufferContents(JSContext *cx, JSObject *obj, void **contents) +JS_StealArrayBufferContents(JSContext *cx, JSObject *obj, void **contents, + uint8_t **data) { if (!(obj = UnwrapObjectChecked(cx, obj))) return false; @@ -3685,7 +3689,7 @@ JS_StealArrayBufferContents(JSContext *cx, JSObject *obj, void **contents) return false; } - if (!ArrayBufferObject::stealContents(cx, obj, contents)) + if (!ArrayBufferObject::stealContents(cx, obj, contents, data)) return false; return true; diff --git a/js/src/jstypedarray.h b/js/src/jstypedarray.h index 4181f7a0580e..081776f54e14 100644 --- a/js/src/jstypedarray.h +++ b/js/src/jstypedarray.h @@ -132,7 +132,8 @@ class ArrayBufferObject : public JSObject static void sweepAll(JSRuntime *rt); - static bool stealContents(JSContext *cx, JSObject *obj, void **contents); + static bool stealContents(JSContext *cx, JSObject *obj, void **contents, + uint8_t **data); static inline void setElementsHeader(js::ObjectElements *header, uint32_t bytes); From 63baefdd10159e20a6b54b908e8d42693982d1a1 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 2 Nov 2012 23:42:59 +1300 Subject: [PATCH 42/83] Bug 807472. Pass WILL_SEND_DID_PAINT when calling PresShell::Paint with PAINT_COMPOSITE. r=mattwoodrow --- view/src/nsViewManager.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/view/src/nsViewManager.cpp b/view/src/nsViewManager.cpp index 9076d36446dd..9ce3c836af46 100644 --- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -363,14 +363,10 @@ void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion, #ifdef DEBUG_INVALIDATIONS printf("--COMPOSITE-- %p\n", mPresShell); #endif - if (IsRefreshDriverPaintingEnabled()) { - mPresShell->Paint(aView, damageRegion, nsIPresShell::PAINT_COMPOSITE); - } else { - mPresShell->Paint(aView, damageRegion, - nsIPresShell::PAINT_LAYERS | - nsIPresShell::PAINT_COMPOSITE | - (aWillSendDidPaint ? nsIPresShell::PAINT_WILL_SEND_DID_PAINT : 0)); - } + mPresShell->Paint(aView, damageRegion, + (IsRefreshDriverPaintingEnabled() ? 0 : nsIPresShell::PAINT_LAYERS) | + nsIPresShell::PAINT_COMPOSITE | + (aWillSendDidPaint ? nsIPresShell::PAINT_WILL_SEND_DID_PAINT : 0)); #ifdef DEBUG_INVALIDATIONS printf("--ENDCOMPOSITE--\n"); #endif From 1c668a4d54c6381f8b79e4a5b3839de0477176ba Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 2 Nov 2012 23:42:59 +1300 Subject: [PATCH 43/83] Bug 787831. Keep TrackIDs instead of Track pointers to guard against Tracks being deleted. r=jesup --- content/media/TrackUnionStream.h | 46 ++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/content/media/TrackUnionStream.h b/content/media/TrackUnionStream.h index 6f4a148099d4..7449f7acee0f 100644 --- a/content/media/TrackUnionStream.h +++ b/content/media/TrackUnionStream.h @@ -56,12 +56,13 @@ public: bool found = false; for (uint32_t j = 0; j < mTrackMap.Length(); ++j) { TrackMapEntry* map = &mTrackMap[j]; - if (map->mInputPort == mInputs[i] && map->mInputTrack == tracks.get()) { + if (map->mInputPort == mInputs[i] && map->mInputTrackID == tracks->GetID()) { bool trackFinished; - if (map->mOutputTrack->IsEnded()) { + StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID); + if (!outputTrack || outputTrack->IsEnded()) { trackFinished = true; } else { - CopyTrackData(j, aFrom, aTo, &trackFinished); + CopyTrackData(tracks.get(), j, aFrom, aTo, &trackFinished); } mappedTracksFinished[j] = trackFinished; mappedTracksWithMatchingInputTracks[j] = true; @@ -72,7 +73,7 @@ public: if (!found) { bool trackFinished = false; uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom); - CopyTrackData(mapIndex, aFrom, aTo, &trackFinished); + CopyTrackData(tracks.get(), mapIndex, aFrom, aTo, &trackFinished); mappedTracksFinished.AppendElement(trackFinished); mappedTracksWithMatchingInputTracks.AppendElement(true); } @@ -101,8 +102,13 @@ protected: // Only non-ended tracks are allowed to persist in this map. struct TrackMapEntry { MediaInputPort* mInputPort; - StreamBuffer::Track* mInputTrack; - StreamBuffer::Track* mOutputTrack; + // We keep track IDs instead of track pointers because + // tracks can be removed without us being notified (e.g. + // when a finished track is forgotten.) When we need a Track*, + // we call StreamBuffer::FindTrack, which will return null if + // the track has been deleted. + TrackID mInputTrackID; + TrackID mOutputTrackID; nsAutoPtr mSegment; }; @@ -137,15 +143,15 @@ protected: TrackMapEntry* map = mTrackMap.AppendElement(); map->mInputPort = aPort; - map->mInputTrack = aTrack; - map->mOutputTrack = track; + map->mInputTrackID = aTrack->GetID(); + map->mOutputTrackID = track->GetID(); map->mSegment = aTrack->GetSegment()->CreateEmptyClone(); return mTrackMap.Length() - 1; } void EndTrack(uint32_t aIndex) { - StreamBuffer::Track* outputTrack = mTrackMap[aIndex].mOutputTrack; - if (outputTrack->IsEnded()) + StreamBuffer::Track* outputTrack = mBuffer.FindTrack(mTrackMap[aIndex].mOutputTrackID); + if (!outputTrack || outputTrack->IsEnded()) return; for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; @@ -159,18 +165,18 @@ protected: } outputTrack->SetEnded(); } - void CopyTrackData(uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo, + void CopyTrackData(StreamBuffer::Track* aInputTrack, + uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo, bool* aOutputTrackFinished) { TrackMapEntry* map = &mTrackMap[aMapIndex]; - StreamBuffer::Track* inputTrack = map->mInputTrack; - StreamBuffer::Track* outputTrack = map->mOutputTrack; + StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID); + MOZ_ASSERT(outputTrack && !outputTrack->IsEnded(), "Can't copy to ended track"); + TrackRate rate = outputTrack->GetRate(); MediaSegment* segment = map->mSegment; MediaStream* source = map->mInputPort->GetSource(); - NS_ASSERTION(!outputTrack->IsEnded(), "Can't copy to ended track"); - GraphTime next; *aOutputTrackFinished = false; for (GraphTime t = aFrom; t < aTo; t = next) { @@ -194,10 +200,10 @@ protected: StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd); TrackTicks inputTrackEndPoint = TRACK_TICKS_MAX; - if (inputTrack->IsEnded()) { - TrackTicks inputEndTicks = inputTrack->TimeToTicksRoundDown(inputEnd); - if (inputTrack->GetEnd() <= inputEndTicks) { - inputTrackEndPoint = inputTrack->GetEnd(); + if (aInputTrack->IsEnded()) { + TrackTicks inputEndTicks = aInputTrack->TimeToTicksRoundDown(inputEnd); + if (aInputTrack->GetEnd() <= inputEndTicks) { + inputTrackEndPoint = aInputTrack->GetEnd(); *aOutputTrackFinished = true; } } @@ -217,7 +223,7 @@ protected: // We'll take the latest samples we can. TrackTicks inputEndTicks = TimeToTicksRoundUp(rate, inputEnd); TrackTicks inputStartTicks = inputEndTicks - ticks; - segment->AppendSlice(*inputTrack->GetSegment(), + segment->AppendSlice(*aInputTrack->GetSegment(), NS_MIN(inputTrackEndPoint, inputStartTicks), NS_MIN(inputTrackEndPoint, inputEndTicks)); LOG(PR_LOG_DEBUG, ("TrackUnionStream %p appending %lld ticks of input data to track %d", From 9a45cc9183e89d1347b7bb971f97a86b42d19508 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 2 Nov 2012 23:42:59 +1300 Subject: [PATCH 44/83] Bug 796452. Add mozpasspointerevents attribute for IFRAMEs in chrome windows. r=mats --- content/base/src/nsGkAtomList.h | 1 + layout/base/nsDisplayList.cpp | 2 +- layout/base/tests/chrome/Makefile.in | 2 ++ .../chrome/passpointerevents_window.html | 27 +++++++++++++++++++ .../tests/chrome/test_passpointerevents.html | 21 +++++++++++++++ layout/generic/nsSubDocumentFrame.cpp | 22 +++++++++++++-- layout/generic/nsSubDocumentFrame.h | 6 +++++ layout/style/nsStyleStruct.h | 4 ++- layout/style/nsStyleStructInlines.h | 16 +++++++++++ 9 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 layout/base/tests/chrome/passpointerevents_window.html create mode 100644 layout/base/tests/chrome/test_passpointerevents.html diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index f82b050b417d..0cdbe4a6b49a 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -570,6 +570,7 @@ GK_ATOM(mousethrough, "mousethrough") GK_ATOM(mouseup, "mouseup") GK_ATOM(mozfullscreenchange, "mozfullscreenchange") GK_ATOM(mozfullscreenerror, "mozfullscreenerror") +GK_ATOM(mozpasspointerevents, "mozpasspointerevents") GK_ATOM(mozpointerlockchange, "mozpointerlockchange") GK_ATOM(mozpointerlockerror, "mozpointerlockerror") GK_ATOM(moz_opaque, "moz-opaque") diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 11a7e5c83d86..75ad3abc734a 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1277,7 +1277,7 @@ void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, nsIFrame *f = outFrames.ElementAt(j); // Handle the XUL 'mousethrough' feature and 'pointer-events'. if (!GetMouseThrough(f) && - f->GetStyleVisibility()->mPointerEvents != NS_STYLE_POINTER_EVENTS_NONE) { + f->GetStyleVisibility()->GetEffectivePointerEvents(f) != NS_STYLE_POINTER_EVENTS_NONE) { writeFrames->AppendElement(f); } } diff --git a/layout/base/tests/chrome/Makefile.in b/layout/base/tests/chrome/Makefile.in index ebb56809b705..311d61809873 100644 --- a/layout/base/tests/chrome/Makefile.in +++ b/layout/base/tests/chrome/Makefile.in @@ -49,6 +49,8 @@ MOCHITEST_CHROME_FILES = \ test_dialog_with_positioning.html \ dialog_with_positioning_window.xul \ blue-32x32.png \ + test_passpointerevents.html \ + passpointerevents_window.html \ $(NULL) ifdef MOZ_DEBUG diff --git a/layout/base/tests/chrome/passpointerevents_window.html b/layout/base/tests/chrome/passpointerevents_window.html new file mode 100644 index 000000000000..c3dd913a1c0f --- /dev/null +++ b/layout/base/tests/chrome/passpointerevents_window.html @@ -0,0 +1,27 @@ + + + + Test that mozpasspointerevents works + + + + + + + diff --git a/layout/base/tests/chrome/test_passpointerevents.html b/layout/base/tests/chrome/test_passpointerevents.html new file mode 100644 index 000000000000..e1ffaa03f54a --- /dev/null +++ b/layout/base/tests/chrome/test_passpointerevents.html @@ -0,0 +1,21 @@ + + + + Test that mozpasspointerevents works + + + + + + +
+
+
+ + diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index ef08a2f51c0c..74e31eec5313 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -235,6 +235,23 @@ nsSubDocumentFrame::GetSubdocumentRootFrame() return subdocView ? subdocView->GetFrame() : nullptr; } +bool +nsSubDocumentFrame::PassPointerEventsToChildren() +{ + if (GetStyleVisibility()->mPointerEvents != NS_STYLE_POINTER_EVENTS_NONE) { + return true; + } + // Limit use of mozpasspointerevents to chrome documents, because + // this could be used by the parent document to discover which parts of the + // subdocument are transparent to events (if subdocument uses + // pointer-events:none on its root element, which is admittedly unlikely) + if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozpasspointerevents) && + PresContext()->IsChrome()) { + return true; + } + return false; +} + NS_IMETHODIMP nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, @@ -243,8 +260,9 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, if (!IsVisibleForPainting(aBuilder)) return NS_OK; - if (aBuilder->IsForEventDelivery() && - GetStyleVisibility()->mPointerEvents == NS_STYLE_POINTER_EVENTS_NONE) + // If mozpasspointerevents is set, then we should allow subdocument content + // to handle events even if we're pointer-events:none. + if (aBuilder->IsForEventDelivery() && !PassPointerEventsToChildren()) return NS_OK; nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists); diff --git a/layout/generic/nsSubDocumentFrame.h b/layout/generic/nsSubDocumentFrame.h index b3f724675867..2e451c315f91 100644 --- a/layout/generic/nsSubDocumentFrame.h +++ b/layout/generic/nsSubDocumentFrame.h @@ -136,6 +136,12 @@ protected: */ nsIFrame* ObtainIntrinsicSizeFrame(); + /** + * Return true if pointer event hit-testing should be allowed to target + * content in the subdocument. + */ + bool PassPointerEventsToChildren(); + nsRefPtr mFrameLoader; nsIView* mInnerView; bool mIsInline; diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 55816697ff50..5f423ead6697 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1375,7 +1375,7 @@ struct nsStyleVisibility { } uint8_t mDirection; // [inherited] see nsStyleConsts.h NS_STYLE_DIRECTION_* - uint8_t mVisible; // [inherited] + uint8_t mVisible; // [inherited] uint8_t mPointerEvents; // [inherited] see nsStyleConsts.h bool IsVisible() const { @@ -1386,6 +1386,8 @@ struct nsStyleVisibility { return ((mVisible == NS_STYLE_VISIBILITY_VISIBLE) || (mVisible == NS_STYLE_VISIBILITY_COLLAPSE)); } + + inline uint8_t GetEffectivePointerEvents(nsIFrame* aFrame) const; }; struct nsTimingFunction { diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index c968b79c362d..582692fc4ebf 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -142,4 +142,20 @@ nsStyleDisplay::IsAbsolutelyPositioned(const nsIFrame* aFrame) const return IsAbsolutelyPositionedStyle() && !aFrame->IsSVGText(); } +uint8_t +nsStyleVisibility::GetEffectivePointerEvents(nsIFrame* aFrame) const +{ + if (aFrame->GetContent() && !aFrame->GetContent()->GetParent()) { + // The root element has a cluster of frames associated with it + // (root scroll frame, canvas frame, the actual primary frame). Make + // those take their pointer-events value from the root element's primary + // frame. + nsIFrame* f = aFrame->GetContent()->GetPrimaryFrame(); + if (f) { + return f->GetStyleVisibility()->mPointerEvents; + } + } + return mPointerEvents; +} + #endif /* !defined(nsStyleStructInlines_h_) */ From 11676b36b35bfb12b15c0a9dbf948c11c86bb98b Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Sat, 3 Nov 2012 01:59:03 +1300 Subject: [PATCH 45/83] Bug 805331. Part 1: Refactor nsDisplayList::GetList Renames GetList to GetSameCoordinateSystemChildren, and adds an assertion to verify that the children have the same reference frame as the parent. Adds nsDisplayList::GetChildren to return whatever children there are. Obsoletes nsDisplayTransform::GetStoredList. --- layout/base/FrameLayerBuilder.cpp | 2 +- layout/base/nsDisplayList.cpp | 12 ++++++------ layout/base/nsDisplayList.h | 27 +++++++++++++++++++++------ layout/base/nsLayoutDebugger.cpp | 6 +----- layout/base/nsPresShell.cpp | 4 ++-- layout/generic/TextOverflow.cpp | 2 +- layout/generic/nsFrame.cpp | 11 ++++++++--- layout/generic/nsGfxScrollFrame.cpp | 2 +- layout/generic/nsPageFrame.cpp | 2 +- 9 files changed, 42 insertions(+), 26 deletions(-) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index e84277debc02..f8978f1bd605 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1986,7 +1986,7 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList, if (type == nsDisplayItem::TYPE_CLIP || type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) { FrameLayerBuilder::Clip childClip(aClip, item); - ProcessDisplayItems(*item->GetList(), childClip, aFlags); + ProcessDisplayItems(*item->GetSameCoordinateSystemChildren(), childClip, aFlags); continue; } diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 75ad3abc734a..2f32bf1b69c8 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -828,7 +828,7 @@ nsDisplayList::FlattenTo(nsTArray* aElements) { nsDisplayItem* item; while ((item = RemoveBottom()) != nullptr) { if (item->GetType() == nsDisplayItem::TYPE_WRAP_LIST) { - item->GetList()->FlattenTo(aElements); + item->GetSameCoordinateSystemChildren()->FlattenTo(aElements); item->~nsDisplayItem(); } else { aElements->AppendElement(item); @@ -927,7 +927,7 @@ nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, "If we have an nsDisplayTransform child (for the same frame)," "then we shouldn't be our own reference frame!"); - nsDisplayList* list = item->GetList(); + nsDisplayList* list = item->GetSameCoordinateSystemChildren(); if (aBuilder->AllowMergingAndFlattening()) { if (belowItem && item->TryMerge(aBuilder, belowItem)) { belowItem->~nsDisplayItem(); @@ -1366,7 +1366,7 @@ void nsDisplayList::ExplodeAnonymousChildLists(nsDisplayListBuilder* aBuilder) { if (i->GetUnderlyingFrame()) { tmp.AppendToTop(i); } else { - nsDisplayList* list = i->GetList(); + nsDisplayList* list = i->GetSameCoordinateSystemChildren(); NS_ASSERTION(list, "leaf items can't be anonymous"); list->ExplodeAnonymousChildLists(aBuilder); nsDisplayItem* j; @@ -2538,7 +2538,7 @@ bool nsDisplayWrapList::ChildrenCanBeInactive(nsDisplayListBuilder* aBuilder, if (state == LAYER_ACTIVE || state == LAYER_ACTIVE_FORCE) return false; if (state == LAYER_NONE) { - nsDisplayList* list = i->GetList(); + nsDisplayList* list = i->GetSameCoordinateSystemChildren(); if (list && !ChildrenCanBeInactive(aBuilder, aManager, aParameters, *list, aActiveScrolledRoot)) return false; } @@ -3816,7 +3816,7 @@ already_AddRefed nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBu } nsRefPtr container = aManager->GetLayerBuilder()-> - BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetList(), + BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetChildren(), aContainerParameters, &newTransformMatrix); // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags, @@ -3858,7 +3858,7 @@ nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder, return !mStoredList.ChildrenCanBeInactive(aBuilder, aManager, aParameters, - *mStoredList.GetList(), + *mStoredList.GetChildren(), activeScrolledRoot) ? LAYER_ACTIVE : LAYER_INACTIVE; } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index b7a84ec607b7..f8c9a5a204ba 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -1013,10 +1013,17 @@ public: } /** - * If this is a leaf item we return null, otherwise we return the wrapped - * list. + * If this has a child list where the children are in the same coordinate + * system as this item (i.e., they have the same reference frame), + * return the list. */ - virtual nsDisplayList* GetList() { return nullptr; } + virtual nsDisplayList* GetSameCoordinateSystemChildren() { return nullptr; } + + /** + * If this has a child list, return it, even if the children are in + * a different coordinate system to this item. + */ + virtual nsDisplayList* GetChildren() { return nullptr; } /** * Returns the visible rect. Should only be called after ComputeVisibility @@ -2068,8 +2075,16 @@ public: virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder); - virtual nsDisplayList* GetList() MOZ_OVERRIDE { return &mList; } - + virtual nsDisplayList* GetSameCoordinateSystemChildren() MOZ_OVERRIDE + { + NS_ASSERTION(mList.IsEmpty() || !ReferenceFrame() || + !mList.GetBottom()->ReferenceFrame() || + mList.GetBottom()->ReferenceFrame() == ReferenceFrame(), + "Children must have same reference frame"); + return &mList; + } + virtual nsDisplayList* GetChildren() MOZ_OVERRIDE { return &mList; } + /** * This creates a copy of this item, but wrapping aItem instead of * our existing list. Only gets called if this item returned nullptr @@ -2593,7 +2608,7 @@ public: return GetBounds(aBuilder, &snap); } - nsDisplayWrapList* GetStoredList() { return &mStoredList; } + virtual nsDisplayList* GetChildren() MOZ_OVERRIDE { return mStoredList.GetChildren(); } virtual void HitTest(nsDisplayListBuilder *aBuilder, const nsRect& aRect, HitTestState *aState, nsTArray *aOutFrames) MOZ_OVERRIDE; diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index eaa48630c8a2..43808129d3f1 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -164,12 +164,8 @@ PrintDisplayListTo(nsDisplayListBuilder* aBuilder, const nsDisplayList& aList, nscolor color; nsRect vis = i->GetVisibleRect(); nsRect component = i->GetComponentAlphaBounds(aBuilder); - nsDisplayList* list = i->GetList(); + nsDisplayList* list = i->GetChildren(); nsRegion opaque; - if (i->GetType() == nsDisplayItem::TYPE_TRANSFORM) { - nsDisplayTransform* t = static_cast(i); - list = t->GetStoredList()->GetList(); - } #ifdef DEBUG if (!list || list->DidComputeVisibility()) { opaque = i->GetOpaqueRegion(aBuilder, &snap); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 3141ef6d84db..5723cb492d5e 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -4503,7 +4503,7 @@ PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder, // insert the item into the list if necessary. If the item has a child // list, insert that as well - nsDisplayList* sublist = i->GetList(); + nsDisplayList* sublist = i->GetSameCoordinateSystemChildren(); if (itemToInsert || sublist) { tmpList.AppendToTop(itemToInsert ? itemToInsert : i); // if the item is a list, iterate over it as well @@ -4835,7 +4835,7 @@ AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame, bg->SetExtraBackgroundColor(aColor); return true; } - nsDisplayList* sublist = i->GetList(); + nsDisplayList* sublist = i->GetSameCoordinateSystemChildren(); if (sublist && AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor)) return true; } diff --git a/layout/generic/TextOverflow.cpp b/layout/generic/TextOverflow.cpp index a91e754f0910..e131ca573547 100644 --- a/layout/generic/TextOverflow.cpp +++ b/layout/generic/TextOverflow.cpp @@ -611,7 +611,7 @@ TextOverflow::PruneDisplayListContents(nsDisplayList* aList, continue; } - nsDisplayList* wrapper = item->GetList(); + nsDisplayList* wrapper = item->GetSameCoordinateSystemChildren(); if (wrapper) { if (!itemFrame || GetSelfOrNearestBlock(itemFrame) == mBlock) { PruneDisplayListContents(wrapper, aFramesToHide, aInsideMarkersArea); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index c2ba1ce54258..4b49c149f587 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1793,7 +1793,8 @@ WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsD } case nsDisplayItem::TYPE_WRAP_LIST: { nsDisplayWrapList *list = static_cast(item); - rv = WrapPreserve3DListInternal(aFrame, aBuilder, list->GetList(), aOutput, aIndex, aTemp); + rv = WrapPreserve3DListInternal(aFrame, aBuilder, + list->GetChildren(), aOutput, aIndex, aTemp); list->~nsDisplayWrapList(); break; } @@ -1803,11 +1804,15 @@ WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsD } nsDisplayOpacity *opacity = static_cast(item); nsDisplayList output; - rv = WrapPreserve3DListInternal(aFrame, aBuilder, opacity->GetList(), &output, aIndex, aTemp); + // Call GetChildren, not GetSameCoordinateSystemChildren, because + // the preserve-3d children of 'opacity' are temporarily not in the + // same coordinate system as the opacity --- until this wrapping is done. + rv = WrapPreserve3DListInternal(aFrame, aBuilder, + opacity->GetChildren(), &output, aIndex, aTemp); if (!aTemp->IsEmpty()) { output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); } - opacity->GetList()->AppendToTop(&output); + opacity->GetChildren()->AppendToTop(&output); opacity->UpdateBounds(aBuilder); aOutput->AppendToTop(item); break; diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 24ed41533a41..54f0c0e24df4 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1676,7 +1676,7 @@ InvalidateFixedBackgroundFramesFromList(nsDisplayListBuilder* aBuilder, const nsDisplayList& aList) { for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { - nsDisplayList* sublist = item->GetList(); + nsDisplayList* sublist = item->GetSameCoordinateSystemChildren(); if (sublist) { InvalidateFixedBackgroundFramesFromList(aBuilder, aMovingFrame, *sublist); continue; diff --git a/layout/generic/nsPageFrame.cpp b/layout/generic/nsPageFrame.cpp index a6e045c5a756..8c1c6bc3a085 100644 --- a/layout/generic/nsPageFrame.cpp +++ b/layout/generic/nsPageFrame.cpp @@ -381,7 +381,7 @@ PruneDisplayListForExtraPage(nsDisplayListBuilder* aBuilder, nsDisplayItem* i = aList->RemoveBottom(); if (!i) break; - nsDisplayList* subList = i->GetList(); + nsDisplayList* subList = i->GetSameCoordinateSystemChildren(); if (subList) { PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, aY, subList); nsDisplayItem::Type type = i->GetType(); From 38b604c5c153f2e89d35ced30746921c735b0b34 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Sat, 3 Nov 2012 01:59:03 +1300 Subject: [PATCH 46/83] Bug 805331. Only chrome display items that are leaves should be treated as especially opaque. r=mats --- layout/base/nsDisplayList.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 2f32bf1b69c8..ebf1c81aeb7b 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -861,12 +861,18 @@ TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) bool snap; nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap); if (aBuilder->IsForPluginGeometry()) { - // Treat all chrome items as opaque, unless their frames are opacity:0. + // Treat all leaf chrome items as opaque, unless their frames are opacity:0. // Since opacity:0 frames generate an nsDisplayOpacity, that item will // not be treated as opaque here, so opacity:0 chrome content will be // effectively ignored, as it should be. + // We treat leaf chrome items as opaque to ensure that they cover + // content plugins, for security reasons. + // Non-leaf chrome items don't render contents of their own so shouldn't + // be treated as opaque (and their bounds is just the union of their + // children, which might be a large area their contents don't really cover). nsIFrame* f = aItem->GetUnderlyingFrame(); - if (f && f->PresContext()->IsChrome() && f->GetStyleDisplay()->mOpacity != 0.0) { + if (f && f->PresContext()->IsChrome() && !aItem->GetChildren() && + f->GetStyleDisplay()->mOpacity != 0.0) { opaque = aItem->GetBounds(aBuilder, &snap); } } From 982b0d39e4bc6d401a191d0600af14e46ccadfc4 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 2 Nov 2012 14:12:51 +0000 Subject: [PATCH 47/83] Backout 23c9f61a243b & 6ca11f4b470c (bug 805807) for mochitest-1 orange in test_contextmenu.html --- .../libeditor/html/tests/test_bug468353.html | 1 + js/src/jswrapper.h | 6 ++ js/xpconnect/tests/unit/test_components.js | 33 +++++---- js/xpconnect/wrappers/AccessCheck.cpp | 56 +++++++++++---- js/xpconnect/wrappers/AccessCheck.h | 72 +++++++++++-------- js/xpconnect/wrappers/FilteringWrapper.cpp | 25 ++++--- .../tests/chrome/sandbox_content_perms.html | 11 +-- 7 files changed, 127 insertions(+), 77 deletions(-) diff --git a/editor/libeditor/html/tests/test_bug468353.html b/editor/libeditor/html/tests/test_bug468353.html index f64581860b9a..dd5981fde924 100644 --- a/editor/libeditor/html/tests/test_bug468353.html +++ b/editor/libeditor/html/tests/test_bug468353.html @@ -42,6 +42,7 @@ function checkStylesheets() { function runTest() { const Ci = SpecialPowers.Ci; + const Cc = SpecialPowers.Components.classes; /** Found while fixing bug 440614 **/ var editframe = window.frames[0]; diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index e8f834ab0753..4cbfecd642af 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -43,6 +43,12 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler LAST_USED_FLAG = CROSS_COMPARTMENT }; + typedef enum { + PermitObjectAccess, + PermitPropertyAccess, + DenyAccess + } Permission; + static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler); diff --git a/js/xpconnect/tests/unit/test_components.js b/js/xpconnect/tests/unit/test_components.js index 7cff627e47a6..5bd1b62bbb54 100644 --- a/js/xpconnect/tests/unit/test_components.js +++ b/js/xpconnect/tests/unit/test_components.js @@ -1,6 +1,5 @@ -const Cu = Components.utils; - function run_test() { + var Cu = Components.utils; var sb1 = Cu.Sandbox("http://www.blah.com"); var sb2 = Cu.Sandbox("http://www.blah.com"); var sb3 = Cu.Sandbox(this); @@ -14,19 +13,25 @@ function run_test() { // non-chrome accessing chrome Components sb1.C = Components; - checkThrows("C.utils", sb1); - checkThrows("C.classes", sb1); + rv = Cu.evalInSandbox("C.utils", sb1); + do_check_eq(rv, undefined); + rv = Cu.evalInSandbox("C.interfaces", sb1); + do_check_neq(rv, undefined); // non-chrome accessing own Components - checkThrows("Components.utils", sb1); - checkThrows("Components.classes", sb1); + rv = Cu.evalInSandbox("Components.utils", sb1); + do_check_eq(rv, undefined); + rv = Cu.evalInSandbox("Components.interfaces", sb1); + do_check_neq(rv, undefined); // non-chrome same origin var C2 = Cu.evalInSandbox("Components", sb2); - do_check_neq(rv, C2.utils); + do_check_neq(rv, C2.utils); sb1.C2 = C2; - checkThrows("C2.utils", sb1); - checkThrows("C2.classes", sb1); + rv = Cu.evalInSandbox("C2.utils", sb1); + do_check_eq(rv, undefined); + rv = Cu.evalInSandbox("C2.interfaces", sb1); + do_check_neq(rv, undefined); // chrome accessing chrome sb3.C = Components; @@ -35,11 +40,9 @@ function run_test() { // non-chrome cross origin sb4.C2 = C2; - checkThrows("C2.utils", sb1); - checkThrows("C2.classes", sb1); -} + rv = Cu.evalInSandbox("C2.interfaces", sb1); + do_check_neq(rv, undefined); + rv = Cu.evalInSandbox("C2.utils", sb1); + do_check_eq(rv, undefined); -function checkThrows(expression, sb) { - var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb); - do_check_true(!!/denied/.exec(result)); } diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index 1eded44fccee..2d4744eb1dcf 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -329,6 +329,17 @@ AccessCheck::deny(JSContext *cx, jsid id) enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 }; +static bool +Deny(JSContext *cx, jsid id, Wrapper::Action act) +{ + // Refuse to perform the action and just return the default value. + if (act == Wrapper::GET) + return true; + // If its a set, deny it and throw an exception. + AccessCheck::deny(cx, id); + return false; +} + static bool IsInSandbox(JSContext *cx, JSObject *obj) { @@ -338,15 +349,19 @@ IsInSandbox(JSContext *cx, JSObject *obj) } bool -ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act) +ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, + Permission &perm) { JSObject *wrappedObject = Wrapper::wrappedObject(wrapper); - if (act == Wrapper::CALL) + if (act == Wrapper::CALL) { + perm = PermitObjectAccess; return true; + } + perm = DenyAccess; if (act == Wrapper::PUNCTURE) - return false; + return Deny(cx, id, act); jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS); @@ -365,6 +380,7 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: JS_IsTypedArrayObject(wrappedObject, cx)) && ((JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) || (JSID_IS_STRING(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length")))) { + perm = PermitPropertyAccess; return true; // Allow } @@ -389,20 +405,26 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: } } + perm = PermitPropertyAccess; return true; } - return false; + return Deny(cx, id, act); } - if (id == JSID_VOID) + if (id == JSID_VOID) { + // This will force the caller to call us back for individual property accesses. + perm = PermitPropertyAccess; return true; + } JS::Value exposedProps; if (!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, &exposedProps)) return false; - if (exposedProps.isNullOrUndefined()) - return false; + if (exposedProps.isNullOrUndefined()) { + JSAutoCompartment wrapperAC(cx, wrapper); + return Deny(cx, id, act); + } if (!exposedProps.isObject()) { JS_ReportError(cx, "__exposedProps__ must be undefined, null, or an Object"); @@ -418,8 +440,10 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: if (!JS_GetPropertyDescriptorById(cx, hallpass, id, JSRESOLVE_QUALIFIED, &desc)) { return false; // Error } - if (!desc.obj || !(desc.attrs & JSPROP_ENUMERATE)) - return false; + if (desc.obj == NULL || !(desc.attrs & JSPROP_ENUMERATE)) { + JSAutoCompartment wrapperAC(cx, wrapper); + return Deny(cx, id, act); + } if (!JSVAL_IS_STRING(desc.value)) { JS_ReportError(cx, "property must be a string"); @@ -463,15 +487,19 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: if ((act == Wrapper::SET && !(access & WRITE)) || (act != Wrapper::SET && !(access & READ))) { - return false; + JSAutoCompartment wrapperAC(cx, wrapper); + return Deny(cx, id, act); } - return true; + perm = PermitPropertyAccess; + return true; // Allow } bool -ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act) +ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, + Permission &perm) { + perm = DenyAccess; JSAutoCompartment ac(cx, wrapper); if (JSID_IS_STRING(id) && act == Wrapper::GET) { @@ -482,6 +510,7 @@ ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper JS_FlatStringEqualsAscii(flatId, "interfacesByID") || JS_FlatStringEqualsAscii(flatId, "results")) { + perm = PermitPropertyAccess; return true; } } @@ -490,10 +519,11 @@ ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper // so we need this dynamic check. This can go away when we expose Components // as SpecialPowers.wrap(Components) during automation. if (xpc::IsUniversalXPConnectEnabled(cx)) { + perm = PermitPropertyAccess; return true; } - return false; + return Deny(cx, id, act); } } diff --git a/js/xpconnect/wrappers/AccessCheck.h b/js/xpconnect/wrappers/AccessCheck.h index e85c3767f2f2..343a5a10a7e2 100644 --- a/js/xpconnect/wrappers/AccessCheck.h +++ b/js/xpconnect/wrappers/AccessCheck.h @@ -39,16 +39,33 @@ class AccessCheck { }; struct Policy { + typedef js::Wrapper::Permission Permission; + + static const Permission PermitObjectAccess = js::Wrapper::PermitObjectAccess; + static const Permission PermitPropertyAccess = js::Wrapper::PermitPropertyAccess; + static const Permission DenyAccess = js::Wrapper::DenyAccess; +}; + +// This policy permits access to all properties. +struct Permissive : public Policy { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, + Permission &perm) { + perm = PermitObjectAccess; + return true; + } }; // This policy only permits access to the object if the subject can touch // system objects. struct OnlyIfSubjectIsSystem : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { - return AccessCheck::isSystemOnlyAccessPermitted(cx); - } - - static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, + Permission &perm) { + if (AccessCheck::isSystemOnlyAccessPermitted(cx)) { + perm = PermitObjectAccess; + return true; + } + perm = DenyAccess; + JSAutoCompartment ac(cx, wrapper); AccessCheck::deny(cx, id); return false; } @@ -57,12 +74,17 @@ struct OnlyIfSubjectIsSystem : public Policy { // This policy only permits access to properties that are safe to be used // across origins. struct CrossOriginAccessiblePropertiesOnly : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, + Permission &perm) { // Location objects should always use LocationPolicy. MOZ_ASSERT(!WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper))); - return AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act); - } - static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { + + if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act)) { + perm = PermitPropertyAccess; + return true; + } + perm = DenyAccess; + JSAutoCompartment ac(cx, wrapper); AccessCheck::deny(cx, id); return false; } @@ -92,19 +114,23 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy { // state of the outer window to determine whether we happen to be same-origin // at the moment. struct LocationPolicy : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, + Permission &perm) { // We should only be dealing with Location objects here. MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper))); + // Default to deny. + perm = DenyAccess; + // Location object security is complicated enough. Don't allow punctures. if (act != js::Wrapper::PUNCTURE && (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) || AccessCheck::isLocationObjectSameOrigin(cx, wrapper))) { + perm = PermitPropertyAccess; return true; } - return false; - } - static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { + + JSAutoCompartment ac(cx, wrapper); AccessCheck::deny(cx, id); return false; } @@ -113,26 +139,14 @@ struct LocationPolicy : public Policy { // This policy only permits access to properties if they appear in the // objects exposed properties list. struct ExposedPropertiesOnly : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act); - - static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { - // For gets, silently fail. - if (act == js::Wrapper::GET) - return true; - // For sets,throw an exception. - AccessCheck::deny(cx, id); - return false; - } + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, + Permission &perm); }; // Components specific policy struct ComponentsObjectPolicy : public Policy { - static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act); - - static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { - AccessCheck::deny(cx, id); - return false; - } + static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, + Permission &perm); }; } diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index 6fefa962797a..9d84db8f1233 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -29,6 +29,12 @@ FilteringWrapper::~FilteringWrapper() { } +typedef Wrapper::Permission Permission; + +static const Permission PermitObjectAccess = Wrapper::PermitObjectAccess; +static const Permission PermitPropertyAccess = Wrapper::PermitPropertyAccess; +static const Permission DenyAccess = Wrapper::DenyAccess; + template static bool Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) @@ -36,10 +42,11 @@ Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props) size_t w = 0; for (size_t n = 0; n < props.length(); ++n) { jsid id = props[n]; - if (Policy::check(cx, wrapper, id, Wrapper::GET)) + Permission perm; + if (!Policy::check(cx, wrapper, id, Wrapper::GET, perm)) + return false; // Error + if (perm != DenyAccess) props[w++] = id; - else if (JS_IsExceptionPending(cx)) - return false; } props.resize(w); return true; @@ -85,16 +92,14 @@ bool FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, bool *bp) { - if (!Policy::check(cx, wrapper, id, act)) { - if (JS_IsExceptionPending(cx)) { - *bp = false; - return false; - } - JSAutoCompartment ac(cx, wrapper); - *bp = Policy::deny(cx, id, act); + Permission perm; + if (!Policy::check(cx, wrapper, id, act, perm)) { + *bp = false; return false; } *bp = true; + if (perm == DenyAccess) + return false; return Base::enter(cx, wrapper, id, act, bp); } diff --git a/toolkit/identity/tests/chrome/sandbox_content_perms.html b/toolkit/identity/tests/chrome/sandbox_content_perms.html index 59905f13c241..1f99dd5d2882 100644 --- a/toolkit/identity/tests/chrome/sandbox_content_perms.html +++ b/toolkit/identity/tests/chrome/sandbox_content_perms.html @@ -18,15 +18,6 @@ return false; } - function CcDenied() { - try { - Components.classes; - return false; - } catch (e) { - return !!/denied/.exec(e); - } - } - // Build an object with test results (true = pass) let results = { windowTop: window.top == window, @@ -37,7 +28,7 @@ .docCharsetIsForced; }), - ccAccess: !!CcDenied(), + ccAccess: SpecialPowers.Components.classes == null, }; let resultsJSON = JSON.stringify(results); From 4dfae7eef7996d787447abf0a33b7d68f34d3730 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Fri, 2 Nov 2012 09:30:57 -0500 Subject: [PATCH 48/83] Bug 750901 - Revert change to nsClipboard and remove ancient crufty commented out code. r=bbondy --- widget/windows/nsClipboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/windows/nsClipboard.cpp b/widget/windows/nsClipboard.cpp index ff51ed904531..57eb17f98db0 100644 --- a/widget/windows/nsClipboard.cpp +++ b/widget/windows/nsClipboard.cpp @@ -308,7 +308,7 @@ nsresult nsClipboard::GetNativeDataOffClipboard(nsIWidget * aWidget, UINT /*aInd HGLOBAL hglb; nsresult result = NS_ERROR_FAILURE; - HWND nativeWin = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW); + HWND nativeWin = nullptr; if (::OpenClipboard(nativeWin)) { hglb = ::GetClipboardData(aFormat); result = GetGlobalData(hglb, aData, aLen); From 4622a2f0dc9950ddfa16fd4a275ac5632cbe5bbd Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 2 Nov 2012 11:02:59 -0400 Subject: [PATCH 49/83] Bug 803575 - Enable preemptive tab zombification in low-memory conditions. r=mfinkle --- mobile/android/app/mobile.js | 4 +-- mobile/android/chrome/content/browser.js | 44 ++++++++++++++++++++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 94548466bcec..7befa5edaa25 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -30,8 +30,8 @@ pref("browser.tabs.remote", false); // If a tab has not been active for this long (seconds), then it may be // turned into a zombie tab to preemptively free up memory. -1 disables time-based -// zombification (low-memory conditions may still require the tab to be zombified). -pref("browser.tabs.zombieTime", -1); +// expiration (but low-memory conditions may still require the tab to be zombified). +pref("browser.tabs.expireTime", 3600); // From libpref/src/init/all.js, extended to allow a slightly wider zoom range. pref("zoom.minPercent", 20); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index c8ca1f558df7..ba1d3d54324f 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -235,6 +235,7 @@ var BrowserApp = { ExternalApps.init(); MemoryObserver.init(); Distribution.init(); + Tabs.init(); #ifdef MOZ_TELEMETRY_REPORTING Telemetry.init(); #endif @@ -511,6 +512,7 @@ var BrowserApp = { ExternalApps.uninit(); MemoryObserver.uninit(); Distribution.uninit(); + Tabs.uninit(); #ifdef MOZ_TELEMETRY_REPORTING Telemetry.uninit(); #endif @@ -670,7 +672,7 @@ var BrowserApp = { evt.initUIEvent("TabOpen", true, false, window, null); newTab.browser.dispatchEvent(evt); - Tabs.zombifyLru(); + Tabs.expireLruTab(); return newTab; }, @@ -7709,13 +7711,43 @@ var Tabs = { // of tabs. Each tab has a timestamp associated with it that indicates when // it was last touched. + _enableTabExpiration: false, + + init: function() { + // on low-memory platforms, always allow tab expiration. on high-mem + // platforms, allow it to be turned on once we hit a low-mem situation + if (Cc["@mozilla.org/xpcom/memory-service;1"].getService(Ci.nsIMemory).isLowMemoryPlatform()) { + this._enableTabExpiration = true; + } else { + Services.obs.addObserver(this, "memory-pressure", false); + } + }, + + uninit: function() { + if (!this._enableTabExpiration) { + // if _enableTabExpiration is true then we won't have this + // observer registered any more. + Services.obs.removeObserver(this, "memory-pressure"); + } + }, + + observe: function(aSubject, aTopic, aData) { + if (aTopic == "memory-pressure" && aData != "heap-minimize") { + this._enableTabExpiration = true; + Services.obs.removeObserver(this, "memory-pressure"); + } + }, + touch: function(aTab) { aTab.lastTouchedAt = Date.now(); }, - zombifyLru: function() { - let zombieTimeMs = Services.prefs.getIntPref("browser.tabs.zombieTime") * 1000; - if (zombieTimeMs < 0) { + expireLruTab: function() { + if (!this._enableTabExpiration) { + return false; + } + let expireTimeMs = Services.prefs.getIntPref("browser.tabs.expireTime") * 1000; + if (expireTimeMs < 0) { // this behaviour is disabled return false; } @@ -7732,9 +7764,9 @@ var Tabs = { lruTab = tabs[i]; } } - // if the tab was last touched more than browser.tabs.zombieTime seconds ago, + // if the tab was last touched more than browser.tabs.expireTime seconds ago, // zombify it - if (lruTab && (Date.now() - lruTab.lastTouchedAt) > zombieTimeMs) { + if (lruTab && (Date.now() - lruTab.lastTouchedAt) > expireTimeMs) { MemoryObserver.zombify(lruTab); return true; } From 4864051dcab67f5cd9c3e5d6ece261d20ef5f57d Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 2 Nov 2012 11:15:10 -0400 Subject: [PATCH 50/83] Bug 807760 - Disable OMTC in b2g builds on Windows. r=cjones --- b2g/app/b2g.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 5b0a4d0726fb..dbfa309cd9f3 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -236,7 +236,9 @@ pref("ui.dragThresholdY", 25); // Layers Acceleration pref("layers.acceleration.disabled", false); +#ifndef XP_WIN pref("layers.offmainthreadcomposition.enabled", true); +#endif pref("layers.offmainthreadcomposition.animate-opacity", true); pref("layers.offmainthreadcomposition.animate-transform", true); pref("layers.async-video.enabled", true); From 01bc13d4463e1195e5b16ef69998b7c448ec8b5f Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 2 Nov 2012 11:24:47 -0400 Subject: [PATCH 51/83] Bug 807760 - Comment followup. DONTBUILD --- b2g/app/b2g.js | 1 + 1 file changed, 1 insertion(+) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index dbfa309cd9f3..c575c034a8cd 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -237,6 +237,7 @@ pref("ui.dragThresholdY", 25); // Layers Acceleration pref("layers.acceleration.disabled", false); #ifndef XP_WIN +//TODO: turn this on for Windows in bug 808016 pref("layers.offmainthreadcomposition.enabled", true); #endif pref("layers.offmainthreadcomposition.animate-opacity", true); From 17335c2b9e7ca772d7710f0ebeb8197472a09e7e Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Fri, 2 Nov 2012 11:30:40 -0400 Subject: [PATCH 52/83] Bug 784309 - CheckedInt.h Intel C++ compilation issue - r=Ms2ger --- mfbt/CheckedInt.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mfbt/CheckedInt.h b/mfbt/CheckedInt.h index 790fc6eabeb4..b56ac42b19c0 100644 --- a/mfbt/CheckedInt.h +++ b/mfbt/CheckedInt.h @@ -385,13 +385,13 @@ IsSubValid(T x, T y) } template::value, + bool IsTSigned = IsSigned::value, bool TwiceBiggerTypeIsSupported = IsSupported::Type>::value> struct IsMulValidImpl {}; -template -struct IsMulValidImpl +template +struct IsMulValidImpl { static bool run(T x, T y) { @@ -451,7 +451,7 @@ IsDivValid(T x, T y) } // This is just to shut up msvc warnings about negating unsigned ints. -template::value> +template::value> struct OppositeIfSignedImpl { static T run(T x) { return -x; } From 105b851f988263f414e13197ea4b85e1f2f841a5 Mon Sep 17 00:00:00 2001 From: rmkn85 Date: Wed, 24 Oct 2012 08:11:00 -0400 Subject: [PATCH 53/83] Bug 804592 - "CreatePipe from Chromium IPC requires Logon Session, fails otherwise" [r=jones.chris.g] --- ipc/chromium/src/chrome/common/ipc_channel_win.cc | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.cc b/ipc/chromium/src/chrome/common/ipc_channel_win.cc index 85ad6b42db3c..e146e340f4d7 100644 --- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc @@ -144,15 +144,6 @@ bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id, DCHECK(pipe_ == INVALID_HANDLE_VALUE); const std::wstring pipe_name = PipeName(channel_id); if (mode == MODE_SERVER) { - SECURITY_ATTRIBUTES security_attributes = {0}; - security_attributes.bInheritHandle = FALSE; - security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); - if (!win_util::GetLogonSessionOnlyDACL( - reinterpret_cast( - &security_attributes.lpSecurityDescriptor))) { - NOTREACHED(); - } - pipe_ = CreateNamedPipeW(pipe_name.c_str(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, @@ -163,8 +154,7 @@ bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id, // input buffer size (XXX tune) Channel::kReadBufferSize, 5000, // timeout in milliseconds (XXX tune) - &security_attributes); - LocalFree(security_attributes.lpSecurityDescriptor); + NULL); } else { pipe_ = CreateFileW(pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, From ea79da8beb3bb79833ee4d21161f84e5cf784658 Mon Sep 17 00:00:00 2001 From: rmkn85 Date: Wed, 24 Oct 2012 08:19:00 -0400 Subject: [PATCH 54/83] Bug 804511 - Use WT_EXECUTEDEFAULT instead of WT_EXECUTEWAITINTHREAD to avoid deadlocks while debugging. r=bent --- ipc/chromium/src/base/object_watcher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipc/chromium/src/base/object_watcher.cc b/ipc/chromium/src/base/object_watcher.cc index a15234b455f4..1fd29139d745 100644 --- a/ipc/chromium/src/base/object_watcher.cc +++ b/ipc/chromium/src/base/object_watcher.cc @@ -55,7 +55,7 @@ bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) { // Since our job is to just notice when an object is signaled and report the // result back to this thread, we can just run on a Windows wait thread. - DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE; + DWORD wait_flags = WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE; if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting, watch, INFINITE, wait_flags)) { From 1391d06573f1ff896ac7f9e9d6782ef11029a815 Mon Sep 17 00:00:00 2001 From: Jonathan Griffin Date: Thu, 1 Nov 2012 14:49:15 -0700 Subject: [PATCH 55/83] Bug 807817 - Dump Marionette logs as well as writing to logfile, r=philikon --- testing/marionette/components/marionettecomponent.js | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/marionette/components/marionettecomponent.js b/testing/marionette/components/marionettecomponent.js index b569d1a75ce3..92ff97b1652e 100644 --- a/testing/marionette/components/marionettecomponent.js +++ b/testing/marionette/components/marionettecomponent.js @@ -23,6 +23,7 @@ function MarionetteComponent() { let formatter = new Log4Moz.BasicFormatter(); this.logger.addAppender(new Log4Moz.FileAppender(logf, formatter)); + this.logger.addAppender(new Log4Moz.DumpAppender(formatter)); this.logger.info("MarionetteComponent loaded"); } From 86f48b4c62bfc3ecdb5bc6d0095a4489714e0c66 Mon Sep 17 00:00:00 2001 From: Girish Sharma Date: Fri, 2 Nov 2012 18:24:42 +0530 Subject: [PATCH 56/83] Bug 800231 - Intermittent browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js | Test timed out, | Found a Tools:StyleEditor after previous test timed out; r=msucan --- ...le_bug_782653_CSS_links_in_Style_Editor.js | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js b/browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js index 0df552d4ac76..11ca4e98b46a 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js @@ -44,9 +44,6 @@ function checkStyleEditorForSheetAndLine(aStyleSheetIndex, aLine, aCallback) { function doCheck(aEditor) { function checkLineAndCallback() { info("In checkLineAndCallback()"); - ok(aEditor.sourceEditor != null, "sourceeditor not null"); - ok(aEditor.sourceEditor.getCaretPosition() != null, "position not null"); - ok(aEditor.sourceEditor.getCaretPosition().line != null, "line not null"); is(aEditor.sourceEditor.getCaretPosition().line, aLine, "Correct line is selected"); if (aCallback) { @@ -54,10 +51,7 @@ function checkStyleEditorForSheetAndLine(aStyleSheetIndex, aLine, aCallback) { } } - ok(aEditor, "aEditor is defined."); - - // Source-editor is already loaded, check the current line of caret. - if (aEditor.sourceEditor) { + function checkForCorrectSheet() { if (aEditor.styleSheetIndex != SEC.selectedStyleSheetIndex) { ok(false, "Correct Style Sheet was not selected."); if (aCallback) { @@ -66,30 +60,28 @@ function checkStyleEditorForSheetAndLine(aStyleSheetIndex, aLine, aCallback) { return; } - info("Correct Style Sheet is selected in the editor"); info("Editor is already loaded, check the current line of caret"); executeSoon(checkLineAndCallback); + } + + ok(aEditor, "aEditor is defined."); + + // Source-editor is already loaded, check the current sheet and line. + if (aEditor.sourceEditor) { + checkForCorrectSheet(); return; } info("source editor is not loaded, waiting for it."); - // Wait for source editor to be loaded. - aEditor.addActionListener({ - onAttach: function onAttach() { - info("on attach happened"); - aEditor.removeActionListener(this); - info("this removed"); - executeSoon(function() { - if (aEditor.styleSheetIndex != SEC.selectedStyleSheetIndex) { - ok(false, "Correct Style Sheet was not selected."); - if (aCallback) { - aCallback(); - } - return; - } - checkLineAndCallback() - }); - } + // Source-editor is not loaded, polling regularly and waiting for it to load + waitForSuccess({ + name: "Wait for the source-editor to load", + validatorFn: function() + { + return aEditor.sourceEditor; + }, + successFn: checkForCorrectSheet, + failureFn: aCallback, }); } @@ -138,18 +130,14 @@ let observer = { .getEditorForWindow(content.window); ok(styleEditorWin, "Style Editor Window is defined"); waitForFocus(function() { - //styleEditorWin.addEventListener("load", function onStyleEditorWinLoad() { - //styleEditorWin.removeEventListener("load", onStyleEditorWinLoad); - - checkStyleEditorForSheetAndLine(0, 7, function() { - checkStyleEditorForSheetAndLine(1, 6, function() { - window.StyleEditor.toggle(); - styleEditorWin = null; - finishTest(); - }); - EventUtils.sendMouseEvent({ type: "click" }, nodes[1]); + checkStyleEditorForSheetAndLine(0, 7, function() { + checkStyleEditorForSheetAndLine(1, 6, function() { + window.StyleEditor.toggle(); + styleEditorWin = null; + finishTest(); }); - //}); + EventUtils.sendMouseEvent({ type: "click" }, nodes[1]); + }); }, styleEditorWin); }); } From 21d6dff002e437c715b112b3c80499e7b7ecff35 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 2 Nov 2012 10:32:40 -0700 Subject: [PATCH 57/83] Bug 807861 - Ensure state directory is present before running tests; r=rnewman DONTBUILD (NPOTB) --HG-- extra : rebase_source : fd66449c11968269d33eff3f686f22a8767b6898 --- testing/mochitest/mach_commands.py | 2 ++ testing/xpcshell/mach_commands.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/testing/mochitest/mach_commands.py b/testing/mochitest/mach_commands.py index 44aba41f953c..f93dd0deb077 100644 --- a/testing/mochitest/mach_commands.py +++ b/testing/mochitest/mach_commands.py @@ -109,5 +109,7 @@ class MachCommands(MozbuildObject): self.run_mochitest(test_file, 'a11y') def run_mochitest(self, test_file, flavor): + self._ensure_state_subdir_exists('.') + mochitest = self._spawn(MochitestRunner) mochitest.run_mochitest_test(test_file, flavor) diff --git a/testing/xpcshell/mach_commands.py b/testing/xpcshell/mach_commands.py index c33bcc8a1110..4059cc8246bd 100644 --- a/testing/xpcshell/mach_commands.py +++ b/testing/xpcshell/mach_commands.py @@ -125,6 +125,11 @@ class MachCommands(MozbuildObject): @CommandArgument('--shuffle', '-s', action='store_true', help='Randomize the execution order of tests.') def run_xpcshell_test(self, **params): + # We should probably have a utility function to ensure the tree is + # ready to run tests. Until then, we just create the state dir (in + # case the tree wasn't built with mach). + self._ensure_state_subdir_exists('.') + xpcshell = self._spawn(XPCShellRunner) xpcshell.run_test(**params) From c896275503e3d77ffdf3a229f5be0e1e543a4e53 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 2 Nov 2012 10:33:56 -0700 Subject: [PATCH 58/83] Bug 805835 - Update virtualenv to fix lib64 path issues; r=glandium The following commits were cherry-picked from virtualenv's Git repository from the develop branch: 0da2c50eafbf6841afad078e04aa873780905b99 e1ec5f3b9f5c3cfa533f5ce440d7ac251c14ad7d da95f04065328a98d16bcad1e9ad0e89f3a41ebe These should hopefully be part of virtualenv 1.8.3, whenever it is released. --HG-- extra : rebase_source : c759ea0d2874c17e39436780d5953f0c2cfaadee --- python/virtualenv/virtualenv.py | 281 +++++++++--------- python/virtualenv/virtualenv_embedded/site.py | 10 +- 2 files changed, 151 insertions(+), 140 deletions(-) diff --git a/python/virtualenv/virtualenv.py b/python/virtualenv/virtualenv.py index 0067e3289362..07b36d4953e6 100755 --- a/python/virtualenv/virtualenv.py +++ b/python/virtualenv/virtualenv.py @@ -1558,10 +1558,14 @@ def fix_lib64(lib_dir): assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], ( "Unexpected python lib dir: %r" % lib_dir) lib_parent = os.path.dirname(lib_dir) + top_level = os.path.dirname(lib_parent) + lib_dir = os.path.join(top_level, 'lib') + lib64_link = os.path.join(top_level, 'lib64') assert os.path.basename(lib_parent) == 'lib', ( "Unexpected parent dir: %r" % lib_parent) - os.symlink(os.path.join('.', os.path.basename(lib_parent)), - os.path.join(os.path.dirname(lib_parent), 'lib64')) + if os.path.lexists(lib64_link): + return + os.symlink('lib', lib64_link) def resolve_interpreter(exe): """ @@ -1827,142 +1831,143 @@ def convert(s): ##file site.py SITE_PY = convert(""" -eJzFPf1z2zaWv/OvwMqTIZXKdD66nR2n7o2TOK333MTbpLO5dT1aSoIs1hTJEqRl7c3d337vAwAB -kvLHpp3TdGKJBB4eHt43HtDRaHRcljJfiHWxaDIplEyq+UqUSb1SYllUol6l1WK/TKp6C0/n18mV -VKIuhNqqGFvFQfD0Cz/BU/FplSqDAnxLmrpYJ3U6T7JsK9J1WVS1XIhFU6X5lUjztE6TLP0XtCjy -WDz9cgyC01zAzLNUVuJGVgrgKlEsxfm2XhW5iJoS5/w8/nPycjwRal6lZQ0NKo0zUGSV1EEu5QLQ -hJaNAlKmtdxXpZyny3RuG26KJluIMkvmUvzznzw1ahqGgSrWcrOSlRQ5IAMwJcAqEQ/4mlZiXixk -LMRrOU9wAH7eEitgaBNcM4VkzAuRFfkVzCmXc6lUUm1FNGtqAkQoi0UBOKWAQZ1mWbApqms1hiWl -9djAI5Ewe/iTYfaAeeL4fc4BHD/kwc95ejth2MA9CK5eMdtUcpneigTBwk95K+dT/SxKl2KRLpdA -g7weY5OAEVAiS2cHJS3Ht3qFvjsgrCxXJjCGRJS5Mb+kHnFwWoskU8C2TYk0UoT5WzlLkxyokd/A -cAARSBoMjbNIVW3HodmJAgBUuI41SMlaiWidpDkw64/JnND+e5ovio0aEwVgtZT4tVG1O/9ogADQ -2iHAJMDFMqvZ5Fl6LbPtGBD4BNhXUjVZjQKxSCs5r4sqlYoAAGpbIW8B6YlIKqlJyJxp5HZC9Cea -pDkuLAoYCjy+RJIs06umIgkTyxQ4F7ji3YefxNuT16fH7zWPGWAss1drwBmg0EI7OMEA4qBR1UFW -gEDHwRn+EcligUJ2heMDXm2Dg3tXOohg7mXc7eMsOJBdL64eBuZYgzKhsQLq99/QZaJWQJ//uWe9 -g+B4F1Vo4vxtsypAJvNkLcUqYf5Czgi+1XC+i8t69Qq4QSGcGkilcHEQwRThAUlcmkVFLkUJLJal -uRwHQKEZtfVXEVjhfZHv01p3OAEgVEEOL51nYxoxlzDRPqxXqC9M4y3NTDcJ7Dqvi4oUB/B/Pidd -lCX5NeGoiKH420xepXmOCCEvBOFeSAOr6xQ4cRGLM2pFesE0EiFrL26JItEALyHTAU/K22RdZnLC -4ou69W41QoPJWpi1zpjjoGVN6pVWrZ3qIO+9iD93uI7QrFeVBODNzBO6ZVFMxAx0NmFTJmsWr3pT -EOcEA/JEnZAnqCX0xe9A0WOlmrW0L5FXQLMQQwXLIsuKDZDsMAiE2MNGxij7zAlv4R38C3Dx30zW -81UQOCNZwBoUIr8LFAIBkyBzzdUaCY/bNCt3lUyas6YoqoWsaKiHEfuAEX9gY5xr8L6otVHj6eIq -F+u0RpU00yYzZYuXhzXrx1c8b5gGWG5FNDNNWzqtcXpZuUpm0rgkM7lESdCL9MouO4wZDIxJtrgW -a7Yy8A7IIlO2IMOKBZXOspbkBAAMFr4kT8smo0YKGUwkMNC6JPjrBE16oZ0lYG82ywEqJDbfc7A/ -gNu/QIw2qxToMwcIoGFQS8HyzdK6Qgeh1UeBb/RNfx4fOPV0qW0TD7lM0kxb+SQPTunhSVWR+M5l -ib0mmhgKZpjX6Npd5UBHFPPRaBQExh3aKvO1UEFdbQ+BFYQZZzqdNSkavukUTb3+oQIeRTgDe91s -OwsPNITp9B6o5HRZVsUaX9u5fQRlAmNhj2BPnJOWkewge5z4CsnnqvTSNEXb7bCzQD0UnP908u70 -88lHcSQuWpU26eqzSxjzJE+ArckiAFN1hm11GbRExZei7hPvwLwTU4A9o94kvjKpG+BdQP1T1dBr -mMbcexmcvD9+fXYy/fnjyU/Tj6efTgBBsDMy2KMpo3lswGFUMQgHcOVCxdq+Br0e9OD18Uf7IJim -alpuyy08AEMJLFxFMN+JCPHhVNvgaZovi3BMjX9lJ/yI1Yr2uC4Ov74UR0ci/DW5ScIAvJ62KS/i -jyQAn7alhK41/IkKNQ6ChVyCsFxLFKnoKXmyY+4ARISWhbasvxZpbt4zH7lDkMRH1ANwmE7nWaIU -Np5OQyAtdRj4QIeY3WGUkwg6llu361ijgp9KwlLk2GWC/wygmMyoH6LBKLpdTCMQsPU8UZJb0fSh -33SKWmY6jfSAIH7E4+AiseIIhWmCWqZKwRMlXkGtM1NFhj8RPsotiQwGQ6jXcJF0sBPfJFkjVeRM -CogYRR0yompMFXEQOBUR2M526cbjLjUNz0AzIF9WgN6rOpTDzx54KKBgTNiFoRlHS0wzxPSvHBsQ -DuAkhqiglepAYX0mzk/OxctnL/bRAYEocWGp4zVHm5rmjbQPl7BaV7J2EOZe4YSEYezSZYmaEZ8e -3g1zHduV6bPCUi9xJdfFjVwAtsjAziqLn+gNxNIwj3kCqwiamCw4Kz3j6SUYOfLsQVrQ2gP11gTF -rL9Z+j0O32WuQHVwKEyk1nE6G6+yKm5SdA9mW/0SrBuoN7RxxhUJnIXzmAyNGGgI8FtzpNRGhqDA -qoZdTMIbQaKGX7SqMCZwZ6hbL+nrdV5s8inHrkeoJqOxZV0ULM282KBdgj3xDuwGIFlAKNYSjaGA -ky5QtvYBeZg+TBcoS9EAAALTrCjAcmCZ4IymyHEeDoswxq8ECW8l0cLfmCEoODLEcCDR29g+MFoC -IcHkrIKzqkEzGcqaaQYDOyTxue4s5qDRB9ChYgyGLtLQuJGh38UhKGdx5iolpx/a0M+fPzPbqBVl -RBCxGU4ajf6SzFtcbsEUpqATjA/F+RVigw24owCmUZo1xf5HUZTsP8F6nmvZBssN8Vhdl4cHB5vN -Jtb5gKK6OlDLgz//5Ztv/vKMdeJiQfwD03GkRSfH4gN6hz5o/K2xQN+ZlevwY5r73EiwIkl+FDmP -iN/3TbooxOH+2OpP5OLWsOK/xvkABTI1gzKVgbajFqMnav9J/FKNxBMRuW2jMXsS2qRaK+ZbXehR -F2C7wdOYF01eh44iVeIrsG4QUy/krLkK7eCejTQ/YKoop5Hlgf3nl4iBzxmGr4wpnqKWILZAi++Q -/idmm4T8Ga0hkLxoonrx7nZYixniLh4u79Y7dITGzDBVyB0oEX6TBwugbdyXHPxoZxTtnuOMmo9n -CIylDwzzalcwQsEhXHAtJq7UOVyNPipI04ZVMygYVzWCgga3bsbU1uDIRoYIEr0bE57zwuoWQKdO -rs9E9GYVoIU7Ts/adVnB8YSQB47Ec3oiwak97L17xkvbZBmlYDo86lGFAXsLjXa6AL6MDICJGFU/ -j7ilCSw+dBaF12AAWMFZG2SwZY+Z8I3rA472RgPs1LP6u3ozjYdA4CJFnD16EHRC+YhHqBRIUxn5 -PXexuCVuf7A7LQ4xlVkmEmm1Q7i6ymNQqO40TMs0R93rLFI8zwrwiq1WJEZq3/vOAkUu+HjImGkJ -1GRoyeE0OiJvzxPAULfDhNdVg6kBN3OCGK1TRdYNybSCf8CtoIwEpY+AlgTNgnmolPkT+x1kzs5X -f9nBHpbQyBBu011uSM9iaDjm/Z5AMur8CUhBDiTsCyO5jqwOMuAwZ4E84YbXcqd0E4xYgZw5FoTU -DOBOL70AB5/EuGdBEoqQb2slS/GVGMHydUX1Ybr7d+VSkzaInAbkKuh8w5Gbi3DyEEedvITP0H5G -gnY3ygI4eAYuj5uad9ncMK1Nk4Cz7ituixRoZMqcjMYuqpeGMG76909HTouWWGYQw1DeQN4mjBlp -HNjl1qBhwQ0Yb827Y+nHbsYC+0ZhoV7I9S3Ef2GVqnmhQgxwe7kL96O5ok8bi+1ZOhvBH28BRuNL -D5LMdP4Csyz/xiChBz0cgu5NFtMii6TapHlICkzT78hfmh4elpSekTv4SOHUAUwUc5QH7yoQENqs -PABxQk0AUbkMlXb7+2DvnOLIwuXuI89tvjh8edkn7mRXhsd+hpfq5LauEoWrlfGisVDgavUNOCpd -mFySb/V2o96OxjChKhREkeLDx88CCcGZ2E2yfdzUW4ZHbO6dk/cxqINeu5dcndkRuwAiqBWRUQ7C -x3Pkw5F97OTumNgjgDyKYe5YFANJ88m/A+euhYIx9hfbHPNoXZWBH3j9zdfTgcyoi+Q3X4/uGaVD -jCGxjzqeoB2ZygDE4LRNl0omGfkaTifKKuYt79g25ZgVOsV/mskuB5xO/Jj3xmS08HvNe4Gj+ewR -PSDMLma/QrCqdH7rJkkzSsoDGvv7qOdMnM2pg2F8PEh3o4w5KfBYnk0GQyF18QwWJuTAftyfjvaL -jk3udyAgNZ8yUX1U9vQGfLt/5G2qu3uHfajamBgeesaZ/hcDWsKb8ZBd/xINh5/fRRlYYB4NRkNk -9xzt/+9ZPvtjJvnAqZht39/RMD0S0O81E9bjDE3r8XHHIA4tu2sCDbAHWIodHuAdHlp/aN7oWxo/ -i1WSEk9Rdz0VG9rrpzQnbtoAlAW7YANwcBn1jvGbpqp435dUYCmrfdzLnAgsczJOGFVP9cEcvJc1 -YmKbzSlt7BTFFENqJNSJYDuTsHXhh+VsVZj0kcxv0gr6gsKNwh8+/HgS9hlAD4OdhsG562i45OEm -HOE+gmlDTZzwMX2YQo/p8u9LVTeK8AlqttNNclaTbdA++DlZE9IPr8E9yRlv75T3qDFYnq/k/Hoq -ad8d2RS7OvnpN/gaMbHb8X7xlEqWVAEGM5lnDdKKfWAs3Vs2+Zy2KmoJro6us8W6G9pN50zcMkuu -RESdF5gF0txIiaKbpNKOYFkVWNkpmnRxcJUuhPytSTKMsOVyCbjgPpJ+FfPwlAwSb7kggCv+lJw3 -VVpvgQSJKvQ2HNUOOA1nW55o5CHJOy5MQKwmOBQfcdr4ngm3MOQycbq/+YCTxBAYO5h9UuQueg7v -82KKo06pQHbCSPW3yOlx0B2hAAAjAArzH411Es1/I+mVu9dHa+4SFbWkR0o36C/IGUMo0RiTDvyb -fvqM6PLWDiyvdmN5dTeWV10srwaxvPKxvLobS1ckcGFt/shIwlAOqbvDMFis4qZ/eJiTZL7idlg4 -iQWSAFGUJtY1MsX1w16SibfaCAipbWfvlx62xScpV2RWBWejNUjkftxP0nG1qfx2OlMpi+7MUzHu -7K4CHL/vQRxTndWMurO8LZI6iT25uMqKGYitRXfSApiIbi0Opy3zm+mME60dSzU6/69PP3x4j80R -1MhUGlA3XEQ0LDiV6GlSXam+NLVxWAnsSC39mhjqpgHuPTDJxaPs8T9vqdgCGUdsqFigECV4AFQS -ZZu5hUNh2HmuK4z0c2Zy3vc5EqO8HrWT2kGk4/Pzt8efjkeUfRv978gVGENbXzpcfEwL26Dvv7nN -LcWxDwi1TjO1xs+dk0frliPut7EGbM+H7zx48RCDPRix+7P8QykFSwKEinUe9jGEenAM9EVhQo8+ -hhF7lXPuJhc7K/adI3uOi+KI/tAOQHcAf98RY4wpEEC7UJGJDNpgqqP0rXm9g6IO0Af6el8cgnVD -r24k41PUTmLAAXQoa5vtdv+8LRM2ekrWr0++P31/dvr6/PjTD44LiK7ch48HL8TJj58FlWqgAWOf -KMEqhRqLgsCwuKeExKKA/xrM/CyamvO10Ovt2ZneNFnjOREsHEabE8Nzriiy0Dh9xQlh+1CXAiFG -mQ6QnAM5VDlDB3YwXlrzYRBV6OJiOuczQ2e10aGXPmhlDmTRFnMM0geNXVIwCK72gldUAl6bqLDi -zTh9SGkAKW2jbY1GRum53s69sxVlNjq8nCV1hidtZ63oL0IX1/AyVmWWQiT3KrSypLthpUrLOPqh -3WtmvIY0oNMdRtYNedY7sUCr9Srkuen+45bRfmsAw5bB3sK8c0mVGlS+jHVmIsRGvKkSylv4apde -r4GCBcM9txoX0TBdCrNPILgWqxQCCODJFVhfjBMAQmcl/Nz8oZMdkAUWSoRv1ov9v4WaIH7rX34Z -aF5X2f4/RAlRkOCqnnCAmG7jtxD4xDIWJx/ejUNGjqpkxd8arK0Hh4QSoI60UykRb2ZPIyWzpS71 -8PUBvtB+Ar3udK9kWenuw65xiBLwREXkNTxRhn4hVl5Z2BOcyrgDGo8NWMzw+J1bEWA+e+LjSmaZ -LhY/fXt2Ar4jnmRACeItsBMYjvMluJut6+D4eGAHFO51w+sK2bhCF5bqHRax12wwaY0iR729Egm7 -TpQY7vfqZYGrJFUu2hFOm2GZWvwYWRnWwiwrs3anDVLYbUMUR5lhlpieV1RL6vME8DI9TTgkglgJ -z0mYDDxv6KZ5bYoHs3QOehRULijUCQgJEhcPAxLnFTnnwItKmTNE8LDcVunVqsZ9Bugc0/kFbP7j -8eez0/dU0//iZet1DzDnhCKBCddzHGG1HmY74ItbgYdcNZ0O8ax+hTBQ+8Cf7isuFDniAXr9OLGI -f7qv+BDXkRMJ8gxAQTVlVzwwAHC6DclNKwuMq42D8eNW47WY+WAoF4lnRnTNhTu/Pifalh1TQnkf -8/IRGzjL0laH6c5udVj3o+e4LHHHaRENN4K3Q7JlPjPoet17s6sOzf30pBDPkwJG/db+GKZQq9dU -T8dhtl3cQmGttrG/5E6u1Gk3z1GUgYiR23nsMtmwEtbNmQO9iuYeMPGtRtdI4qAqH/2Sj7SH4WFi -id2LU0xHOlFCRgAzGVIfnGnAh0KLAAqECnEjR3In46cvvDk61uD+OWrdBbbxB1CEuiyWjlsUFXAi -fPmNHUd+RWihHj0UoeOp5DIvbMkWfjYr9Cqf+3MclAFKYqLYVUl+JSOGNTEwv/KJvSMFS9rWI/VF -ejlkWMQpOKe3Ozi8LxfDGycGtQ4j9Npdy21XHfnkwQaDpzLuJJgPvko2oPvLpo54JYdFfvgg2m6o -90PEQkBoqvfBoxDTMb+FO9anBTxIDQ0LPbzfduzC8toYR9bax84Bo9C+0B7svILQrFa0LeOc7DO+ -qPUCWoN71Jr8kX2qa3bs74EjW05OyALlwV2Q3txGukEnnTDik0N87DKlyvT2YIt+t5A3MgOjAUY2 -woMHv9qDB+PYplMGS7K+GLvz7fl2GDd602J2aE5GoGemSli/OJf1AaIzmPG5C7MWGVzqX3RIkuTX -5CW/+fvpRLx5/xP8+1p+AFOKJwcn4h+AhnhTVBBf8tFXupMAD1XUHDgWjcLjhQSNtir4+gZ02849 -OuO2iD7t4R/zsJpSYIFrteY7QwBFniAdB2/9BHOGAX6bQ1Ydb9R4ikOLMtIvkQa7z53gWY0D3TJe -1esM7YWTJWlX82J0dvrm5P3Hk7i+RQ43P0dOFsWvjcLp6D3iCvfDJsI+mTf45NJxnH+QWTngN+ug -05xhwaBThBCXlDbQ5PsoEhtcJBVmDkS5XRTzGFsCy/OxuXoDjvTYiS/vNfSelUVY0VjvorXePD4G -aohfuopoBA2pj54T9SSEkhme3+LH8WjYFE8Epbbhz9PrzcLNjOuDODTBLqbtrCO/u9WFK6azhmc5 -ifA6sstgzmZmaaLWs7l7Zu9DLvR1IqDlaJ9DLpMmq4XMQXIpyKd7HUDTu8fsWEKYVdic0dkzStNk -m2SrnCKkRIkRjjqio+m4IUMZQ4jBf0yu2R7g+T/R8EFigE6IUvxUOF1VM1+xBHNIRNQbKDzYpPlL -t55HU5gH5Qh53jqyME90GxmjK1nr+fODaHzxvK10oKz03DtkOy/B6rlssgeqs3z69OlI/Mf93g+j -EmdFcQ1uGcAe9FrO6PUOy60nZ1er79mbNzHw43wlL+DBJWXP7fMmp9TkHV1pQaT9a2CEuDahZUbT -vmOXOWlX8UYzt+ANK205fs5TujQIU0sSla2+ewnTTkaaiCVBMYSJmqdpyGkKWI9t0eD5OEwzan6R -t8DxKYKZ4FvcIeNQe4UeJtWyWu6x6ByJEQEeUW0Zj0YHjOmEGOA5Pd9qNKeneVq3RzueuZun+iB9 -be8C0nwlkg1KhplHhxjOUUuPVVsPu7iTRb2IpZhfuAnHziz59X24A2uDpBXLpcEUHppFmheymhtz -iiuWztPaAWPaIRzuTFcgkfWJgwGURqDeySosrETbt3+y6+Ji+oH2kffNSLp8qLbXSnFyKMk7BYZx -3I5PaShLSMu35ssYRnlPaW3tCXhjiT/ppCrW9Xu3X7hHDJtc32rB9RvtVRcAh25SsgrSsqOnI5zr -uyx8Ztodd1Hgh0J0wu0mreomyab68oQpOmxTu7Gu8bRH0+48dGm9FXDyC/CA93UVPTgOpsoG6YlF -sOaUxJFY6hRF7J728g9GlQV6eS/YVwKfAimzmJozyiaJdGHZ1R7+1DWbjopHUF+ZA0U7PHNzkqV3 -CMTFfEJ1TuYIwg4v2uDSvVNCfHckoucT8edOIDQvt3grEqD8ZBE/WYS+T0ZdLw5ftHamH3h2IOwE -8vLy0dPN0hlNLxwq/76/ry46xABwDbKzTOVi/4lC7BjnL4WqobTz2s0pNGM8Hb5nq570wej2uAid -CpuBV79pFYqjWoz/aQcxJ661HuDDqSi0bIHsgXpTeNIp/rOXnmFhoEbPX1n0XKZDm1P4DS8ugfea -oK6js3PTUle4W7ADMbk+xshbUG3DluPv9ageJUrdGvFeK9yebCXOZf1H8HBIl7wQ03zV2Rb+I5mH -i/Z3bS72sPzm67vwdBXM4ImFgQX1FtNp9Qcy9U6WfezCPGC//n7+fzjv38X3j6aS7jVMKwylsJB5 -lfAbNIlNeWhTDUYl4FZQ5Ja34ae+HjwTw+oAdWN9Hd41fe5/19x1i8DO3Ozu9ubun31zaaD77uaX -IRpwmKcJ8aCa8VZgh3WBK8YTXVQwnLLUHyS/2wlnukMr3AfGlDBgApTYVGNvtPY6mbvfsUJmn693 -dY86DtqKzrR7Zz+7HP8QRc/VAPjcnn6mEo+F5kD2G+m+rikXDU7l1ZWaJnhX3JSCDSpw6XmRxn19 -R1d9yURtjdeJF6oACMNNuhTRrTYGxoCAhu+s5foQ5+YMQUNTFaVTlqnSBWeQtIsL4GLOHFF/k5nk -uspRJjHhp5qqrCAqGOmbTblwYajWswVqEhnrRF0b1E2Pib7oEofgahlzPJLzVRxpeNQBQvCpKefa -Ji5Unk7tO+CXZ+0x8HRiGULmzVpWSd1egeJvk6biO2cEOhSLC+ykKlrJ7HCKi1hq+cNBCpMF9vtX -2sn2gow7zn6PrdZ7OFtRD50Ce8yxcsf2GG5Ob+0VaO7VOwu6MNc18rZZy3322hdYCnOfF+lKnTvg -t/qOIb65kjOb6CY4fARy7x5J88tzrVpjJ8Wi4TxzFUP/Uhk81Uy2eOiuuB4X9G+F6wQadnxfb1hm -6YUmOxpbKmrXalDxtKON24gaK+nuiaj9aulHRtQe/AdG1PpmPzA4Gh+tDwbrp+8JvVlNuNfktYwA -faZAJNzZ61yyZkxm5FYjQ9ib3o7sNbWsM50jTsZMIEf2708iEHwdnnJLN73rqu6KqH3posffn314 -fXxGtJieH7/5z+PvqVoF08cdm/XglENe7DO19726WDf9oCsMhgZvsR24d5IPd2gIvfe9zdkBCMMH -eYYWtKvI3Ne7OvQORPQ7AeJ3T7sDdZfKHoTc88908b1bV9ApYA30U642NL+cLVvzyOxcsDi0OxPm -fZtM1jLay7XtWjin7q+vTrTfqm8q3JEHHNvqU1oBzCEjf9kUsjlKYBOV7Kh0/+cBVDKLx7DMLR8g -hXPp3DZHF80xqNr/vxRUoOwS3Adjh3Fib/yldpwuV/Yqa9wLm8vYEMQ7BzXqz88V9oXMdlAhCFjh -6bvUGBGj//QGk92OfaLExT6duNxHZXNpf+GaaSf37yluutb2TiLFlRu87QSNl03mbqTaPr0O5PxR -dr5YOiX+oPkOgM6teCpgb/SZWCHOtiKEQFJvGGLVINFRXyjmII9208He0OqZ2N91Hs89jybE890N -F50jb7rHC+6h7umhGnPqybHAWL6266Cd+I4g8/aOoEuIPOcD9xT13bfw9ebi+aFNtiK/42tHkVCZ -zcgx7BdOmdqdF9853YlZqgnVMWHM5hzT1C0uHajsE+yKcXq1+jviILPvy5BG3vvhIh/Tw7vQe9TF -1LLeIUxJRE/UmKblnG7QuNsn4/50W7XVB8InNR4ApKcCARaC6elGp3Juy+Wv0TMdFc4aujLUzbiH -jlRQFY3PEdzD+H4tft3udMLyQd0ZRdOfG3Q5UC85CDf7Dtxq7KVEdpuE7tRbPtjhAvBh1eH+zx/Q -v1/fZbu/uMuvtq1eDh6QYl8WSwKxUqJDIvM4BiMDejMibY115EbQ8X6Olo4uQ7VzQ75Ax4/KDPFC -YAowyJmdag/AGoXg/wBaZusT +eJzFPf1z2zaWv/OvwMqToeTKdOJ0OztO3RsncVrvuYm3SWdz63q0lARZrCmSJUjL6s3d337vAwAB +kpLtTXdO04klEnh4eHhfeHgPHQwGp0Uhs7lY5fM6lULJuJwtRRFXSyUWeSmqZVLOD4q4rDbwdHYb +30glqlyojYqwVRQE+1/4CfbFp2WiDArwLa6rfBVXySxO041IVkVeVnIu5nWZZDciyZIqidPkd2iR +Z5HY/3IMgvNMwMzTRJbiTpYK4CqRL8TlplrmmRjWBc75RfTn+OVoLNSsTIoKGpQaZ6DIMq6CTMo5 +oAktawWkTCp5oAo5SxbJzDZc53U6F0Uaz6T45z95atQ0DAOVr+R6KUspMkAGYEqAVSAe8DUpxSyf +y0iI13IW4wD8vCFWwNDGuGYKyZjlIs2zG5hTJmdSqbjciOG0rggQoSzmOeCUAAZVkqbBOi9v1QiW +lNZjDY9EzOzhT4bZA+aJ43c5B3D8kAU/Z8n9mGED9yC4aslsU8pFci9iBAs/5b2cTfSzYbIQ82Sx +ABpk1QibBIyAEmkyPSxoOb7VK/TdIWFluTKGMSSizI35JfWIgvNKxKkCtq0LpJEizN/KaRJnQI3s +DoYDiEDSoG+ceaIqOw7NTuQAoMR1rEBKVkoMV3GSAbP+GM8I7b8n2TxfqxFRAFZLiV9rVbnzH/YQ +AFo7BBgHuFhmNessTW5luhkBAp8A+1KqOq1QIOZJKWdVXiZSEQBAbSPkPSA9FnEpNQmZM43cjon+ +RJMkw4VFAUOBx5dIkkVyU5ckYWKRAOcCV7z78JN4e/b6/PS95jEDjGX2ZgU4AxRaaAcnGEAc1qo8 +THMQ6Ci4wD8ins9RyG5wfMCraXD44EoHQ5h7EbX7OAsOZNeLq4eBOVagTGisgPr9N3QZqyXQ538e +WO8gON1GFZo4f1svc5DJLF5JsYyZv5Azgm81nO+iolq+Am5QCKcCUilcHEQwQXhAEpdmwzyTogAW +S5NMjgKg0JTa+qsIrPA+zw5orVucABDKIIOXzrMRjZhJmGgX1ivUF6bxhmammwR2nVd5SYoD+D+b +kS5K4+yWcFTEUPxtKm+SLEOEkBeCcC+kgdVtApw4j8QFtSK9YBqJkLUXt0SRqIGXkOmAJ+V9vCpS +OWbxRd26W43QYLISZq1T5jhoWZF6pVVrptrLe0fR5xbXEZrVspQAvJ56QrfI87GYgs4mbIp4xeJV +rXPinKBHnqgT8gS1hL74HSh6qlS9kvYl8gpoFmKoYJGnab4Gkh0HgRB72MgYZZ854S28g38BLv6b +ymq2DAJnJAtYg0Lkt4FCIGASZKa5WiPhcZtm5baSSTLWFHk5lyUN9ThiHzLij2yMcw3e55U2ajxd +XOV8lVSokqbaZCZs8bKwYv34iucN0wDLrYhmpmlDpxVOLy2W8VQal2QqFygJepFe2WWHMYOeMckW +V2LFVgbeAVlkwhakX7Gg0llUkpwAgMHCF2dJUafUSCGDiRgGWhUEfxWjSc+1swTszWY5QIXE5nsG +9gdw+x3EaL1MgD4zgAAaBrUULN80qUp0EBp9FPhG3/Tn8YFTzxfaNvGQizhJtZWPs+CcHp6VJYnv +TBbYa6yJoWCGWYWu3U0GdEQxHwwGQWDcoY0yX3MVVOXmGFhBmHEmk2mdoOGbTNDU6x8q4FGEM7DX +zbaz8EBDmE7vgUpOl0WZr/C1ndtHUCYwFvYI9sQlaRnJDrLHia+QfK5KL0xTtN0OOwvUQ8HlT2fv +zj+ffRQn4qpRaeO2PruGMc+yGNiaLAIwVWvYRpdBS1R8Ceo+8Q7MOzEF2DPqTeIr46oG3gXUP5U1 +vYZpzLyXwdn709cXZ5OfP579NPl4/ukMEAQ7I4M9mjKaxxocRhWBcABXzlWk7WvQ6UEPXp9+tA+C +SaImxabYwAMwlMDC5RDmOxYhPpxoGzxJskUejqjxr+yEn7Ba0R7X1fHX1+LkRIS/xndxGIDX0zTl +RfyRBODTppDQtYI/w1yNgmAuFyAstxJFarhPnuyIOwARoWWuLeuveZKZ98xH7hAk8UPqAThMJrM0 +VgobTyYhkJY69HygQ8TuMMrJEDoWG7frSKOCn1LCUmTYZYz/9KAYT6kfosEoul1MIxCw1SxWklvR +9KHfZIJaZjIZ6gFB/IjHwUVixREK0wS1TJmAJ0q8glpnqvIUfyJ8lFsSGdwMoV7DRdKbneguTmup +hs6kgIjDYYuMqBoTRRwETsUQbGezdKNRm5qGZ6AZkC/NQe+VLcrhZw88FFAwZtuFWzPeLTHNENO/ +8t6AcAAnMUQFrVQLCuszcXl2KV4+PzpABwR2iXNLHa852tQkq6V9uIDVupGVgzD3CsckDCOXLgvU +jPj0eDfMVWRXpssKC73EpVzld3IO2CIDO6ssfqI3sJeGecxiWEXQxGTBWekZTy/GnSPPHqQFrT1Q +b0VQzPqbpd/j7bvMFKgO3goTqfU+nY1XUeZ3CboH041+CdYN1BvaOOOKBM7CeUyGRgw0BPitGVJq +LUNQYGXNLibhjSBRw88bVRgRuAvUrdf09TbL19mE964nqCaHI8u6KFiaebFBswR74h3YDUAyh61Y +QzSGAk66QNk6AORh+jBdoCztBgAQmGZFGywHltmc0RR5n4fDIozRK0HCW0q08HdmCNocGWI4kOht +ZB8YLYGQYHJWwVnVoJkMZc00g4EdkvhcdxHxptEH0KJiBIZuqKFxI0O/q2NQzuLCVUpOP7Shnz9/ +ZrZRS4qIIGJTnDQa/QWZt6jYgClMQCcYH4rjK8QGa3BHAUytNGuKg48iL9h/gvW81LINlhv2Y1VV +HB8ertfrSMcD8vLmUC0O//yXb775y3PWifM58Q9Mx5EWHRyLDukd+qDRt8YCfWdWrsWPSeZzI8Ea +SvKjyHlE/L6vk3kujg9GVn8iFzeGFf81zgcokIkZlKkMtB00GD1TB8+il2ognomh23Y4Yk9Cm1Rr +xXyrCz2qHGw3eBqzvM6q0FGkSnwF1g321HM5rW9CO7hnI80PmCrK6dDywMGLa8TA5wzDV8YUT1BL +EFugxXdI/xOzTUz+jNYQSF40UZ397qZfixnizh8v79Y7dITGzDBRyB0oEX6TRwugbdyVHPxoZxTt +nuOMmo9nCIylDwzzaldwiIJDuOBajF2pc7gafVSQpjWrZlAwrmoEBQ1u3ZSprcGRjQwRJHo3ZnvO +C6tbAJ1asT6zozerAC3ccTrWrs0KjieEPHAiXtATCU7tcefdc17aOk0pBNPiUY8qDNhbaLTTOfDl +0AAYi0H584Bbmo3Fh9ai8Br0AMs5aoMMtugwE75xfcDB3qCHnTpWf1tvpnEfCFykIUePHgWdUD7h +EUoF0lQM/Z7bWNwStzvYTotDTGWWiURabRGutvLoFaqdhmmRZKh7nUWKZmkOXrHVisRIzXvfWaCd +Cz7uM2ZaAjUZGnI4jU7I2/MEMNTtMOB1U2NowI2cIEarRJF1QzIt4R9wKygiQeEjoCVBs2AeK2X+ +xP4AmbPz1V+2sIclNDKE23SbG9KxGBqOeb8nkIw6fgJSkAEJu8JIriOrgxQ4zFkgT7jhtdwq3QQj +UiBnjgUhNQO400tvg4NPIjyzIAlFyPeVkoX4Sgxg+dqi+jjd/YdyqQkbDJ0G5CroeMOJG4tw4hAn +rbiEz9B+RIJON4ocOHgKLo8bmnfZ3DCtDZOAs+4rbosUaGSKnAxGLqrXhjBu+PdPJ06LhlhmEMNQ +3kDeIYwZaRTY5dagYcENGG/N22Ppx27EAvsOw1wdydU97P/CMlGzXIW4we3ELtyP5ooubSy2F8l0 +AH+8BRiMrj1IMtXxC4yy/AuDhB70sA+6N1kMi8zjcp1kISkwTb8Tf2k6eFhSekbu8CNtpw5hohij +PHxXgoDQYeUhiBNqAtiVy1Bpt78LducUBxYudx94bvPV8cvrLnHH2yI89tO/VGf3VRkrXK2UF42F +Alera8BR6cLk4myjjxv1cTRuE8pcwS5SfPj4WSAhOBK7jjdPm3rD8IjNg3PyPgZ10GsPkqs1O2IX +QAS1IjLKYfh0jnw8sk+d3I6JPQHIkxhmx6IYSJpP/hU4uxYKxjiYbzKMo7VVBn7g9TdfT3oioy6S +33w9eGCUFjH6xH7Y8gTtyJQGIHqnbbqUMk7J13A6UVQxa3jHtilGrNBp/6eZ7LrH6dR4UTwzvlfJ +71J8J472918e9bfFj4GH8XAJ7sLzcUPB7qzx43tWW+Fpk7UDWGfjaj57NAXY5ufTX2GzrHR87S5O +UjoUADIcHKCeNft8Dl30KxIP0k5d45Cgbyumrp4DY4QcWBh1p6P9slMTe+7ZEJtPEasuKns6AaA5 +v/IO9d2zyy5UveyGh5/zScNRj5byZtznV3yJhsXPH6KMLDCPBoM+sm9lx/+PWT7/90zykVMxx85/ +oGF8IqA/aiZsRxiatiM+rP5ld02wAfYIS7XFA93hIXaH5oPGhfHzWCUpsY+6a1+sKdeAwqx4aARQ +5uwC9sDBZdQn1m/qsuRzZ1KBhSwP8Cx1LDDNyjiBlL3VBXP4XlaIiW02o7C1k5ST96mRUAei7UzC +ZgvRL2fL3ISvZHaXlNAXFO4w/OHDj2dhvwnBkC50erwVebwLgXCfwLShJk74lD5Moad0+delqr2L +8QlqjvNNcFiTrdc++DFhE1LoX4MHgkPe2S2fkeNmfbaUs9uJpHN/ZFPs6sTH3+BrxMSmA/jJWype +UAYazGSW1kgr9sExdXBRZzM6KqkkuFo6zxfzfug0nyOBizS+EUPqPMcolOZGClTdxaV2RIsyx8xS +USfzw5tkLuRvdZziDl8uFoALnmPpVxEPT8Eo8ZYTEjjjUMlZXSbVBkgQq1wfA1LugtNwuuGJDj0k ++cSHCYjZDMfiI04b3zPh5oZcJk7gH37gJHELjh3MOS1yFz2H91k+wVEnlKA7ZqS6R/T0OGiPkAOA +AQCF+Q9GOojnv5H0yj1rpDV3iYpa0iOlG3TIyRlDKMMRBj34N/30GdHlrS1Y3mzH8mY3ljdtLG96 +sbzxsbzZjaUrEriwNn5lJKEvhtU+4ehNlnHDTzzMWTxbcjtM3MQETYAoCrPXNjLF+ctekIuP+ggI +qW3n7JkeNskvCWeEljlHwzVI5H48z9L7epN57nSmVBrdmadi3NltCUB+38MoojyvKXVneZvHVRx5 +cnGT5lMQW4vuuAEwFu1cIA6bZneTKQd6W5ZqcPlfn3748B6bI6iByXSgbriIaFhwKsP9uLxRXWlq +9oEFsCO19HNyqJsGuPfIIBuPssf/vKVkD2QcsaZkhVwU4AFQSpZt5iYuhWHruc5w0s+Zyfnc6UQM +smrQTGoLkU4vL9+efjodUPRv8L8DV2AMbX3pcPExLWyDrv/mNrcUxz4g1DrM1Rg/d04erRuOeNjG +GrAdH7714OgxBrs3YuDP8t9KKVgSIFSk48BPIdSj90BftE3o0McwYidzzz1kY2fFvnNkz3FRHNHv +O4FoD+Cfe+IeYwIE0C7U0OwMms1US+lb87qDog7QR/p6X7wFa2+92jsZn6J2Ej0OoENZ22y7++cd +2bDRU7J6ffb9+fuL89eXp59+cFxAdOU+fDw8Emc/fhaUKoIGjH2iGLMkKkxKAsPiVimJeQ7/1Rj5 +mdcVx4uh19uLC31os8I6FUxcRpsTwXPOaLLQOHzGAWn7UKciIUap3iA5BUGUuUMFQ7hfWnExisp1 +cjPVGU3RWa311ksXepmCMDrijkD6oLFLCgbB2WbwilLQK7MrLPkwUBdJ9SClbbTNEUkpPNjJHHCO +wsxBixczpc7wpOmsFf1V6OIaXkeqSBPYyb0KrSzpbpgp0zCOfmjPuhmvPg3odIeRdUOe9VYs0Gq9 +Cnluuv+oYbTfasCwYbC3MO9MUqYIpU9jnpsIsREf6oTyHr7apddroGDB8MyvwkU0TJfA7GPYXItl +AhsI4MklWF/cJwCE1kr4ZwPHTnRA5pioEb5ZzQ/+FmqC+K1/+aWneVWmB/8QBeyCBGcVhT3EdBu/ +hY1PJCNx9uHdKGTkKEtX/K3G3H5wSCgA6kg7pTLxYfpkqGS60Kkmvj7AF9pPoNet7qUsSt293zUO +UQKeqSF5Dc+UoV+ImV8W9hinMmqBxrIFixmW/7kZCeazJz4uZZrqZPXztxdn4DtiJQVKEB/BncFw +HC/B03Sdh8fliS1QeNYOr0tk4xJdWMq3mEdes96gNYoc9fZSNOw6UWC426sTBS7jRLloD3HaDMvU +AkTIyrAWZlmZtVttkMJuG6I4ygyzxOSypFxWnyeAl+lpzFsi2CthnYaJwPOBcpJVJnkxTWagR0Hl +gkIdg5AgcbEYkTgvzzgGnpfK1DDBw2JTJjfLCs85oHNE9RPY/MfTzxfn76mm4Ohl43X3MOeYdgJj +zic5wWxBjHbAFzcDELlqMunjWf0KYaD2gT/tV5yocsIDdPpxYBH/tF9xEdmJsxPkGYCCqou2eOAG +wOnWJzeNLDCudh+MHzcbsMHMB0OxSKxZ0Tkf7vy6nGhbtkwJxX3Myycc4CwKm52mO7vZae2PnuOi +wBOv+bC/Ebztky3zmULX286bbXlw7qcjhVjPChh1W/tjmESxTlM9HYfZtnELbWu1jf0lc2KlTrtZ +hqIMRBy6nUcuk/UrYd2cOdDLqO4AE99qdI0k9qrywS/ZQHsYHiaW2J19iulIFS1kBDCSIXXhTg0+ +FFoEUCCUCDx0JHc82j/y5uhYg4fnqHUX2MYfQBHqtFwq98hL4ET48hs7jvyK0EI9eixCx1PJZJbb +lDH8rJfoVb7w59grAxTERLEr4+xGDhnW2MD8yif2lhAsaVuP1FfJdZ9hEefgnN5v4fCuXPQfnBjU +WozQaXcrN2115JMHG/RWhewkmA++jNeg+4u6GvJKbjmH7i2E2w71YYiYiAhN9Tn8MMRwzG/hlvVp +APdSQ8NCD++3LaewvDbGkbX2sVXgFNoX2oOdlbA1qxQdyziVhcYXtV5AY3BPGpM/sE91zpD93VMy +5sSELFAe3AXpzW2gG7TCCQOuXOKyz4Qy45vCGv1uLu9kCkYDjOwQCx9+tYUPo8iGU3pTwr4Yu8vN +5aYfN3rTYHZsKjPQM1MFrF+UyeoQ0emN+OzCrEEGl/oXvSWJs1vykt/8/Xws3rz/Cf59LT+AKcXK +xbH4B6Ah3uQl7C+59JbuRMCijoo3jnmtsLyRoNFRBV8fgW7bpUdnPBbR1SZ+mYnVlAITbMsV31kC +KPIEqRy98RNMDQX8NkVeLW/UeIp9izLQL5EG2+tesFbkULeMltUqRXvhREma1bwaXJy/OXv/8Syq +7pHDzc+BE0Xxc7NwOvqMuMTzsLGwT2Y1Prl2HOcfZFr0+M1602lqaHDTKULYlxR2o8n3YcR2cxGX +GDkQxWaezyJsCSzPZXvVGhzpkbO/fNDQe1YWYQ1H+hSt8ebxMVBD/NJWRANoSH30nKgnIRRPsX6M +H0eDflM8FhTahj/7t+u5GxnXhUA0wTamzayHfnerC5dMZw3PchLhdWKXwdSGpkmsVtOZWzP4IRP6 +OhPQcnTOIRdxnVZCZiC5tMmneyVA07tlfiwhzCpszqj2jcI06TreKCcJKVZigKMOqDQeD2QoYgh7 +8B/jW7YHWH8oai5kBuiEKO2fcqerqmdLlmDeEhH1ehIP1kn20s3n0RTmQXmHPGscWZgnuo2M0Y2s +9Pz5wXB09aLJdKCo9Mwr8p0VYPVcNtkD1Vns7+8PxH887P0wKlGa57fglgHsXq/lgl5vsdx6cna1 +up69eRMBP86W8goeXFP03D6vMwpN7uhKCyLtXwMjxLUJLTOa9i27zEG7kg+auQUfWGnL8XOW0KVF +GFqSqGz13U8YdjLSRCwJiiGM1SxJQg5TwHps8hrr8zDMqPlF3gPHJwhmjG/xhIy32kv0MCmX1nKP +RedEDAjwgHLLeDQqcKYKNcBzcrnRaE7Os6RqSkueu4enupC/sncRab4S8Rolw8yjRQyn1NNj1cbD +zneyqLdjyWdXbsCxNUt+/RDuwNogafliYTCFh2aRZrksZ8ac4ools6RywJh2CIc70xVMZH2ioAel +Aah3sgpzK9H27Z/suriYfqBz5AMzkk4fquy1VhwcirNWgmEUNeNTGMoS0vKt+TKCUd5TWFt7At5Y +4k86qIp1Bd7tG26JY53pWzU4f6O5agPg0E1OVkFadvR0hHN9mIXPTLvlLgz80BadcLtLyqqO04m+ +vGGCDtvEHqxrPG1p3M6iT+utgJOfgwd8oLP4wXEwWTZIT0zCNVUaJ2KhQxSRW23mF2YVOXp5R+wr +gU+BlJlPTI20CSJdWXa1xac6Z9NR8QjqK1PQtMUzN5U0nSIUF/Mx5TmZEogtXrTBpX2nhfjuRAxf +jMWfWxuhWbHBW5kA5Wfz6Nk89H0y6np1fNTYme7GswVhK5CX10+ebppMaXphX/r5w3110iFuAFcg +O4tEzg+eKcSOcf5SqBpKM6/tnEIzxur0PZv1pAuzm3IVqkqbgle/bhSKo1qM/2kHMRXfWg9wcSwK +LVsgW9BvEk9ayX/20jVMDNTo+SuLnsuk73AKv+HFKfBeE9R1dLYeWuoMewu2Z0+uyyj5CKpp2HD8 +gx7Vk0SpnSPeaYXHk43Euaz/BB4O6ZIZYpqvWsfC/07m4aT9bYeLHSy/+XoXnq6C6a2Y6FnQx1Yx +8KK3SxeahTef/qCXxzJ9Xf940dkqGE9d/kdkBTwsZY+XsF3S9WQq6V79tMING6ZLL2N+g4a3Lo5t +QMMoHjxwGrpJdPipbnsrf1jpoAaubsNd0+f+u+auWwR25uYMuTN3v8LPpYHuu51f+mjAm0lNiEdl +pjdqoV/juMpirFMX+gOj+oPkdzvhTLfonofAmEQJDLMSm2rsjW1YxTP3O+bhHPAltm5BZ69Fak27 +o1jaHP8Yc8I5B/jc1nhTIslccyB7p3Qr2YRTEyfy5kZNYrwRb0JbGkqj6fiqxkl+RxeayVhtjG+L +18YACMNNOuHRzWkGxoBtE9/My1kozv0ggoamXE0n+VMlc45TaUcawEUcn6L+Jv7J2ZuDVGJYUdVl +UcLeY6Dvb+X0iL6M0gaoCZesYnVrUDc9xvo6TxyCc3JMEShHxXg/41EHCME63rmcitOhJxP7Dvjl +eVPsnowtQ8isXskyrpqLXvzD2ATsSzMClf7iAjsBkUYyW5ziIpZY/nCQwpCE/f6VduW9rcyOCveR +1XqPZyvqoQNtTymed2yP4ebk3l705l4wNKdrgV1XwjZruM9ebgNLYW4tI12pIxT8Vt+kxPdzcvwU +nRGHj0Du3cI3PwnYqjV2hSwazjNXMXSvzsHabbLFfTfidbige/ddaztjx/f1hmWWjhOypbGlonbg +ehVPM9qo2bdjvt4D+3Y/J/uJ+3YP/iP37fr+QjA4Gh+tD3qztB/Y4LOacC8DbBgB+kyASHh+2LpK +zpjMoZvzDJvr5H5gL+NlnekUUhkzgRzZvSWKQPClf8pNEPUu5dq1b/elix5/f/Hh9ekF0WJyefrm +P0+/p5wYDFK3bNajAxtZfsDUPvCyb90gh85j6Bu8wbbndk0uIdEQOu87R8A9EPrLhfoWtK3I3Nfb +OnTKLrqdAPHd025B3aayeyF3/DOd4u9mL7TSZAP9lHMazS/nYNg8MucjLA7N+Yd534SstYx2Inrb +Fs7JLuyqE+236vsYt0QbRzbHlVYAI9XIXzZQbQoWbDiUHZX2/yKBEnOx2MvcZQJSOJPOnXp0nR6D +qvz/F0MJyi7G0zZ2GMf2XmNqx0F5ZS/sxhO3mYwMQbxqq0F3fq6wz2W6hQpBwApP3xjHiBj9p4+x +7KHvMyWuDqiu8wCVzbX9hWumndy/J3i0W9mblxTnh/DhFjRe1Kl7XGv7dDqQ80dnAPnCKSQAzXcI +dG7EUwF7o8/ECnG6ESFsJPWxJOYmEh31tWkO8mg3HewNrZ6Lg21Vf27VmxAvtjectwrrdI8j7qEe +6KFqU1vlWGBMkttWzie+I8h8iCToqiXP+cCTS33DL3y9u3pxbEO6yO/42lEklMwzcAz7lZMMt/N6 +P6c7MUs5pmwp3LM5xaC6xbUDlX2CbXucTkXAln2QOV1mSAPvfX9UxfTwri0ftDG1rHcMUxLDZ2pE +03JqKDTu9smoO91GbXWBcD3II4B0VCDAQjAd3ejk5204yXb4XO8KpzVdjOrG9UNHKihXx+cI7mF8 +vwa/dneq43xUd0bR9OcGbQ7USw7Czb4Dtxp5IZHtJqE99YYPtrgAXBLb3//FI/p3s8hs96NdfrVt +9bK3DIt9WUw8xHyMFonM4wiMDOjNIWlrzFY3go63gDR0dBmqmRvyBTp+lMyI1x7TBoOc2Yn2AKxR +CP4PNIke9w== """) ##file ez_setup.py diff --git a/python/virtualenv/virtualenv_embedded/site.py b/python/virtualenv/virtualenv_embedded/site.py index 1faaa11ea1ef..4271f43905c4 100644 --- a/python/virtualenv/virtualenv_embedded/site.py +++ b/python/virtualenv/virtualenv_embedded/site.py @@ -238,7 +238,10 @@ def addsitepackages(known_paths, sys_prefix=sys.prefix, exec_prefix=sys.exec_pre lib64_dir = os.path.join(prefix, "lib64", "python" + sys.version[:3], "site-packages") if (os.path.exists(lib64_dir) and os.path.realpath(lib64_dir) not in [os.path.realpath(p) for p in sitedirs]): - sitedirs.append(lib64_dir) + if sys.maxsize > 2**32: + sitedirs.insert(0, lib64_dir) + else: + sitedirs.append(lib64_dir) try: # sys.getobjects only available in --with-pydebug build sys.getobjects @@ -577,7 +580,10 @@ def virtual_install_main_packages(): hardcoded_relative_dirs = paths[:] # for the special 'darwin' case below lib64_path = os.path.join(sys.real_prefix, 'lib64', 'python'+sys.version[:3]) if os.path.exists(lib64_path): - paths.append(lib64_path) + if sys.maxsize > 2**32: + paths.insert(0, lib64_path) + else: + paths.append(lib64_path) # This is hardcoded in the Python executable, but relative to sys.prefix: plat_path = os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3], 'plat-%s' % sys.platform) From 29ee6e6f9e143e326e43e781957d80d40000c688 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 2 Nov 2012 18:57:50 +0000 Subject: [PATCH 59/83] Bug 757838 - Misc remoteautomation.py cleanup; r=jmaher --- build/mobile/remoteautomation.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py index 98fce2a045e7..5e1fc26491dc 100644 --- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -61,12 +61,13 @@ class RemoteAutomation(Automation): return env - def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsDir): + def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath): + """ Wait for tests to finish (as evidenced by the process exiting), + or for maxTime elapse, in which case kill the process regardless. + """ # maxTime is used to override the default timeout, we should honor that status = proc.wait(timeout = maxTime) - print proc.stdout - if (status == 1 and self._devicemanager.processExist(proc.procName)): # Then we timed out, make sure Fennec is dead proc.kill() @@ -185,9 +186,8 @@ class RemoteAutomation(Automation): # function in dmSUT, so an error here is not necessarily # the end of the world return '' - tlen = len(t) retVal = t[self.stdoutlen:] - self.stdoutlen = tlen + self.stdoutlen = len(t) return retVal.strip('\n').strip() else: return '' @@ -207,6 +207,9 @@ class RemoteAutomation(Automation): if (timer > timeout): break + # Flush anything added to stdout during the sleep + print self.stdout + if (timer >= timeout): return 1 return 0 From 11ec75bdd64ba6d52327eb007cce78ecb21abfe1 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 2 Nov 2012 18:57:50 +0000 Subject: [PATCH 60/83] Bug 757838 - Make remoteautomation.py set lastTestSeen so Android crashes are output with the test filename; r=jmaher --- build/mobile/remoteautomation.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py index 5e1fc26491dc..5c2d48f719d6 100644 --- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. import time +import re import os import automationutils import tempfile @@ -23,6 +24,7 @@ class RemoteAutomation(Automation): # Default our product to fennec self._product = "fennec" + self.lastTestSeen = "remoteautomation.py" Automation.__init__(self) def setDeviceManager(self, deviceManager): @@ -67,6 +69,7 @@ class RemoteAutomation(Automation): """ # maxTime is used to override the default timeout, we should honor that status = proc.wait(timeout = maxTime) + self.lastTestSeen = proc.getLastTestSeen if (status == 1 and self._devicemanager.processExist(proc.procName)): # Then we timed out, make sure Fennec is dead @@ -129,6 +132,7 @@ class RemoteAutomation(Automation): def __init__(self, dm, cmd, stdout = None, stderr = None, env = None, cwd = None): self.dm = dm self.stdoutlen = 0 + self.lastTestSeen = "remoteautomation.py" self.proc = dm.launchProcess(cmd, stdout, cwd, env, True) if (self.proc is None): if cmd[0] == 'am': @@ -178,6 +182,9 @@ class RemoteAutomation(Automation): @property def stdout(self): + """ Fetch the full remote log file using devicemanager and return just + the new log entries since the last call (as a multi-line string). + """ if self.dm.fileExists(self.proc): try: t = self.dm.pullFile(self.proc) @@ -186,12 +193,22 @@ class RemoteAutomation(Automation): # function in dmSUT, so an error here is not necessarily # the end of the world return '' - retVal = t[self.stdoutlen:] + newLogContent = t[self.stdoutlen:] self.stdoutlen = len(t) - return retVal.strip('\n').strip() + # Match the test filepath from the last TEST-START line found in the new + # log content. These lines are in the form: + # 1234 INFO TEST-START | /filepath/we/wish/to/capture.html\n + testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", newLogContent) + if testStartFilenames: + self.lastTestSeen = testStartFilenames[-1] + return newLogContent.strip('\n').strip() else: return '' + @property + def getLastTestSeen(self): + return self.lastTestSeen + def wait(self, timeout = None): timer = 0 interval = 5 From 5da76eb7a9fc79dab0371a981612867bb7a8e9cb Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 2 Nov 2012 15:06:03 -0400 Subject: [PATCH 61/83] Bug 806243 - Fix blob memory reporter's SHA1s on 32-bit by passing a correct format string to nsPrintfCString. r=njn --- content/base/src/nsDOMFile.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp index 0cc5a9c3870f..61cba27f6c15 100644 --- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -744,21 +744,21 @@ class nsDOMMemoryFileDataOwnerMemoryReporter sha1.finish(digest); nsAutoCString digestString; - for (uint8_t i = 0; i < sizeof(digest); i++) { + for (size_t i = 0; i < sizeof(digest); i++) { digestString.AppendPrintf("%02x", digest[i]); } nsresult rv = aCallback->Callback( /* process */ NS_LITERAL_CSTRING(""), nsPrintfCString( - "explicit/dom/memory-file-data/large/file(length=%d, sha1=%s)", + "explicit/dom/memory-file-data/large/file(length=%llu, sha1=%s)", owner->mLength, digestString.get()), nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, size, nsPrintfCString( - "Memory used to back a memory file of length %d. The file has a " - "sha1 of %s.\n\n" + "Memory used to back a memory file of length %llu bytes. The file " + "has a sha1 of %s.\n\n" "Note that the allocator may round up a memory file's length -- " "that is, an N-byte memory file may take up more than N bytes of " "memory.", From f0a2aa9f360c7887d1c6941b2ac073f54bce6881 Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Fri, 2 Nov 2012 13:19:54 -0600 Subject: [PATCH 62/83] Bug 799863 - Fix paths so check-one-remote can find mozdevice; r=wlach --- config/makefiles/xpcshell.mk | 1 + js/src/config/makefiles/xpcshell.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/config/makefiles/xpcshell.mk b/config/makefiles/xpcshell.mk index ea080e00c911..3b04e6ef4d8c 100644 --- a/config/makefiles/xpcshell.mk +++ b/config/makefiles/xpcshell.mk @@ -107,6 +107,7 @@ check-one-remote: $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ -I$(topsrcdir)/build \ -I$(topsrcdir)/build/mobile \ + -I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \ $(testxpcsrcdir)/remotexpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ --build-info-json=$(DEPTH)/mozinfo.json \ diff --git a/js/src/config/makefiles/xpcshell.mk b/js/src/config/makefiles/xpcshell.mk index ea080e00c911..3b04e6ef4d8c 100644 --- a/js/src/config/makefiles/xpcshell.mk +++ b/js/src/config/makefiles/xpcshell.mk @@ -107,6 +107,7 @@ check-one-remote: $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ -I$(topsrcdir)/build \ -I$(topsrcdir)/build/mobile \ + -I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \ $(testxpcsrcdir)/remotexpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ --build-info-json=$(DEPTH)/mozinfo.json \ From 85004a8d19775d9d12fb5e906483e3b81f536b20 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 2 Nov 2012 12:24:19 -0700 Subject: [PATCH 63/83] Bug 739681 - Allow DumpHeapComplete to print unreachable objects (r=mccr8) --- js/src/builtin/TestingFunctions.cpp | 38 ++++++++++ js/src/jsfriendapi.cpp | 104 +++++++++++++--------------- 2 files changed, 85 insertions(+), 57 deletions(-) diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index eefbb03e5c47..701d73626a20 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -687,6 +687,40 @@ FinalizeCount(JSContext *cx, unsigned argc, jsval *vp) return true; } +static JSBool +DumpHeapComplete(JSContext *cx, unsigned argc, jsval *vp) +{ + const char *fileName = NULL; + JSAutoByteString fileNameBytes; + if (argc > 0) { + Value v = JS_ARGV(cx, vp)[0]; + if (v.isString()) { + JSString *str = v.toString(); + if (!fileNameBytes.encode(cx, str)) + return false; + fileName = fileNameBytes.ptr(); + } + } + + FILE *dumpFile; + if (!fileName) { + dumpFile = stdout; + } else { + dumpFile = fopen(fileName, "w"); + if (!dumpFile) { + JS_ReportError(cx, "can't open %s", fileName); + return false; + } + } + + js::DumpHeapComplete(JS_GetRuntime(cx), dumpFile); + + fclose(dumpFile); + + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return true; +} + JSBool MJitChunkLimit(JSContext *cx, unsigned argc, jsval *vp) { @@ -860,6 +894,10 @@ static JSFunctionSpecWithHelp TestingFunctions[] = { "isProxy(obj)", " If true, obj is a proxy of some sort"), + JS_FN_HELP("dumpHeapComplete", DumpHeapComplete, 1, 0, +"dumpHeapComplete([filename])", +" Dump reachable and unreachable objects to a file."), + JS_FN_HELP("mjitChunkLimit", MJitChunkLimit, 1, 0, "mjitChunkLimit(N)", " Specify limit on compiled chunk size during mjit compilation."), diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index c7781adaaede..20f7bafef1bf 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -597,32 +597,15 @@ js_DumpObject(JSObject *obj) #endif -struct DumpingChildInfo { - void *node; - JSGCTraceKind kind; - - DumpingChildInfo (void *n, JSGCTraceKind k) - : node(n), kind(k) - {} -}; - -typedef HashSet, SystemAllocPolicy> PtrSet; - -struct JSDumpHeapTracer : public JSTracer { - PtrSet visited; +struct JSDumpHeapTracer : public JSTracer +{ FILE *output; - Vector nodes; - char buffer[200]; - bool rootTracing; JSDumpHeapTracer(FILE *fp) : output(fp) {} }; -static void -DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind); - static char MarkDescriptor(void *thing) { @@ -634,65 +617,72 @@ MarkDescriptor(void *thing) } static void -DumpHeapPushIfNew(JSTracer *trc, void **thingp, JSGCTraceKind kind) +DumpHeapVisitCompartment(JSRuntime *rt, void *data, JSCompartment *comp) { - JS_ASSERT(trc->callback == DumpHeapPushIfNew || - trc->callback == DumpHeapVisitChild); - void *thing = *thingp; - JSDumpHeapTracer *dtrc = static_cast(trc); + char name[1024]; + if (rt->compartmentNameCallback) + (*rt->compartmentNameCallback)(rt, comp, name, sizeof(name)); + else + strcpy(name, ""); - /* - * If we're tracing roots, print root information. Do this even if we've - * already seen thing, for complete root information. - */ - if (dtrc->rootTracing) { - fprintf(dtrc->output, "%p %c %s\n", thing, MarkDescriptor(thing), - JS_GetTraceEdgeName(dtrc, dtrc->buffer, sizeof(dtrc->buffer))); - } + JSDumpHeapTracer *dtrc = static_cast(data); + fprintf(dtrc->output, "# compartment %s\n", name); +} - PtrSet::AddPtr ptrEntry = dtrc->visited.lookupForAdd(thing); - if (ptrEntry || !dtrc->visited.add(ptrEntry, thing)) - return; +static void +DumpHeapVisitArena(JSRuntime *rt, void *data, gc::Arena *arena, + JSGCTraceKind traceKind, size_t thingSize) +{ + JSDumpHeapTracer *dtrc = static_cast(data); + fprintf(dtrc->output, "# arena allockind=%u size=%u\n", + unsigned(arena->aheader.getAllocKind()), unsigned(thingSize)); +} - dtrc->nodes.append(DumpingChildInfo(thing, kind)); +static void +DumpHeapVisitCell(JSRuntime *rt, void *data, void *thing, + JSGCTraceKind traceKind, size_t thingSize) +{ + JSDumpHeapTracer *dtrc = static_cast(data); + char cellDesc[1024]; + JS_GetTraceThingInfo(cellDesc, sizeof(cellDesc), dtrc, thing, traceKind, true); + fprintf(dtrc->output, "%p %c %s\n", thing, MarkDescriptor(thing), cellDesc); + JS_TraceChildren(dtrc, thing, traceKind); } static void DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind) { - JS_ASSERT(trc->callback == DumpHeapVisitChild); JSDumpHeapTracer *dtrc = static_cast(trc); - const char *edgeName = JS_GetTraceEdgeName(dtrc, dtrc->buffer, sizeof(dtrc->buffer)); - fprintf(dtrc->output, "> %p %c %s\n", *thingp, MarkDescriptor(*thingp), edgeName); - DumpHeapPushIfNew(dtrc, thingp, kind); + char buffer[1024]; + fprintf(dtrc->output, "> %p %c %s\n", *thingp, MarkDescriptor(*thingp), + JS_GetTraceEdgeName(dtrc, buffer, sizeof(buffer))); +} + +static void +DumpHeapVisitRoot(JSTracer *trc, void **thingp, JSGCTraceKind kind) +{ + JSDumpHeapTracer *dtrc = static_cast(trc); + char buffer[1024]; + fprintf(dtrc->output, "%p %c %s\n", *thingp, MarkDescriptor(*thingp), + JS_GetTraceEdgeName(dtrc, buffer, sizeof(buffer))); } void js::DumpHeapComplete(JSRuntime *rt, FILE *fp) { JSDumpHeapTracer dtrc(fp); - JS_TracerInit(&dtrc, rt, DumpHeapPushIfNew); - if (!dtrc.visited.init(10000)) - return; - /* Store and log the root information. */ - dtrc.rootTracing = true; + JS_TracerInit(&dtrc, rt, DumpHeapVisitRoot); TraceRuntime(&dtrc); + fprintf(dtrc.output, "==========\n"); - /* Log the graph. */ - dtrc.rootTracing = false; - dtrc.callback = DumpHeapVisitChild; + JS_TracerInit(&dtrc, rt, DumpHeapVisitChild); + IterateCompartmentsArenasCells(rt, &dtrc, + DumpHeapVisitCompartment, + DumpHeapVisitArena, + DumpHeapVisitCell); - while (!dtrc.nodes.empty()) { - DumpingChildInfo dci = dtrc.nodes.popCopy(); - JS_GetTraceThingInfo(dtrc.buffer, sizeof(dtrc.buffer), - &dtrc, dci.node, dci.kind, JS_TRUE); - fprintf(fp, "%p %c %s\n", dci.node, MarkDescriptor(dci.node), dtrc.buffer); - JS_TraceChildren(&dtrc, dci.node, dci.kind); - } - - dtrc.visited.finish(); fflush(dtrc.output); } From 039dac9e067e7a523e4914c1f3d696bfbbc47f65 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Fri, 2 Nov 2012 15:28:11 -0400 Subject: [PATCH 64/83] Bug 807647: proxy incoming DTLS data to SCTP via the STS thread to avoid locking issues r=ekr --- netwerk/sctp/datachannel/DataChannel.cpp | 31 ++++++++++++++++-------- netwerk/sctp/datachannel/DataChannel.h | 4 +-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index 50b42cfcead8..4d211a4b12c6 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -351,7 +351,7 @@ DataChannelConnection::ConnectDTLS(TransportFlow *aFlow, uint16_t localport, uin NS_ENSURE_TRUE(aFlow, false); mTransportFlow = aFlow; - mTransportFlow->SignalPacketReceived.connect(this, &DataChannelConnection::PacketReceived); + mTransportFlow->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput); mLocalPort = localport; mRemotePort = remoteport; @@ -409,8 +409,8 @@ DataChannelConnection::DTLSConnectThread(void *data) } void -DataChannelConnection::PacketReceived(TransportFlow *flow, - const unsigned char *data, size_t len) +DataChannelConnection::SctpDtlsInput(TransportFlow *flow, + const unsigned char *data, size_t len) { //LOG(("%p: SCTP/DTLS received %ld bytes", this, len)); @@ -419,10 +419,13 @@ DataChannelConnection::PacketReceived(TransportFlow *flow, } int -DataChannelConnection::SendPacket(const unsigned char *data, size_t len) +DataChannelConnection::SendPacket(const unsigned char *data, size_t len, bool release) { //LOG(("%p: SCTP/DTLS sent %ld bytes", this, len)); - return mTransportFlow->SendPacket(data, len) < 0 ? 1 : 0; + int res = mTransportFlow->SendPacket(data, len) < 0 ? 1 : 0; + if (release) + delete data; + return res; } /* static */ @@ -433,17 +436,25 @@ DataChannelConnection::SctpDtlsOutput(void *addr, void *buffer, size_t length, DataChannelConnection *peer = static_cast(addr); int res; - if (peer->IsSTSThread()) { - res = peer->SendPacket(static_cast(buffer), length); + // We're async proxying even if on the STSThread because this is called + // with internal SCTP locks held in some cases (such as in usrsctp_connect()). + // SCTP has an option for Apple, on IP connections only, to release at least + // one of the locks before calling a packet output routine; with changes to + // the underlying SCTP stack this might remove the need to use an async proxy. + if (0 /*peer->IsSTSThread()*/) { + res = peer->SendPacket(static_cast(buffer), length, false); } else { + unsigned char *data = new unsigned char[length]; + memcpy(data, buffer, length); res = -1; // XXX It might be worthwhile to add an assertion against the thread // somehow getting into the DataChannel/SCTP code again, as // DISPATCH_SYNC is not fully blocking. This may be tricky, as it // needs to be a per-thread check, not a global. - peer->mSTS->Dispatch(WrapRunnableRet( - peer, &DataChannelConnection::SendPacket, static_cast(buffer), length, &res - ), NS_DISPATCH_SYNC); + peer->mSTS->Dispatch(WrapRunnable( + peer, &DataChannelConnection::SendPacket, data, length, true + ), NS_DISPATCH_NORMAL); + res = 0; // cheat! Packets can always be dropped later anyways } return res; } diff --git a/netwerk/sctp/datachannel/DataChannel.h b/netwerk/sctp/datachannel/DataChannel.h index bba9aa13c6af..d0b950962476 100644 --- a/netwerk/sctp/datachannel/DataChannel.h +++ b/netwerk/sctp/datachannel/DataChannel.h @@ -169,8 +169,8 @@ protected: private: #ifdef SCTP_DTLS_SUPPORTED static void DTLSConnectThread(void *data); - int SendPacket(const unsigned char* data, size_t len); - void PacketReceived(TransportFlow *flow, const unsigned char *data, size_t len); + int SendPacket(const unsigned char* data, size_t len, bool release); + void SctpDtlsInput(TransportFlow *flow, const unsigned char *data, size_t len); static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df); #endif DataChannel* FindChannelByStreamIn(uint16_t streamIn); From 0ce97e7c2d8fd9a85d384a1aa524799ad81d20f6 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Fri, 2 Nov 2012 15:28:13 -0400 Subject: [PATCH 65/83] Bug 807929: Make DataChannel refcounted r=mcmanus --- content/base/src/nsDOMDataChannel.cpp | 6 +- content/base/src/nsDOMDataChannel.h | 3 +- .../src/peerconnection/PeerConnectionImpl.cpp | 4 +- netwerk/sctp/datachannel/DataChannel.cpp | 142 ++++++++++-------- netwerk/sctp/datachannel/DataChannel.h | 35 ++--- 5 files changed, 102 insertions(+), 88 deletions(-) diff --git a/content/base/src/nsDOMDataChannel.cpp b/content/base/src/nsDOMDataChannel.cpp index 69892c0bdc67..83cdec3566da 100644 --- a/content/base/src/nsDOMDataChannel.cpp +++ b/content/base/src/nsDOMDataChannel.cpp @@ -49,7 +49,7 @@ class nsDOMDataChannel : public nsDOMEventTargetHelper, public mozilla::DataChannelListener { public: - nsDOMDataChannel(mozilla::DataChannel* aDataChannel) + nsDOMDataChannel(already_AddRefed aDataChannel) : mDataChannel(aDataChannel) , mBinaryType(DC_BINARY_TYPE_BLOB) {} @@ -92,7 +92,7 @@ private: JSContext *aCx); // Owning reference - nsAutoPtr mDataChannel; + nsRefPtr mDataChannel; nsString mOrigin; enum { @@ -492,7 +492,7 @@ nsDOMDataChannel::AppReady() /* static */ nsresult -NS_NewDOMDataChannel(mozilla::DataChannel* aDataChannel, +NS_NewDOMDataChannel(already_AddRefed aDataChannel, nsPIDOMWindow* aWindow, nsIDOMDataChannel** aDomDataChannel) { diff --git a/content/base/src/nsDOMDataChannel.h b/content/base/src/nsDOMDataChannel.h index d4e4abce8b43..c30dad0fd098 100644 --- a/content/base/src/nsDOMDataChannel.h +++ b/content/base/src/nsDOMDataChannel.h @@ -10,6 +10,7 @@ // This defines only what's necessary to create nsDOMDataChannels, since this // gets used with MOZ_INTERNAL_API not set for media/webrtc/signaling/testing +#include "nsCOMPtr.h" #include "nsIDOMDataChannel.h" namespace mozilla { @@ -19,7 +20,7 @@ namespace mozilla { class nsPIDOMWindow; nsresult -NS_NewDOMDataChannel(mozilla::DataChannel* dataChannel, +NS_NewDOMDataChannel(already_AddRefed dataChannel, nsPIDOMWindow* aWindow, nsIDOMDataChannel** domDataChannel); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 85601a8f480b..c0e148999278 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -644,7 +644,7 @@ PeerConnectionImpl::CreateDataChannel(const nsACString& aLabel, MOZ_ASSERT(aRetval); #ifdef MOZILLA_INTERNAL_API - mozilla::DataChannel* dataChannel; + nsRefPtr dataChannel; mozilla::DataChannelConnection::Type theType = static_cast(aType); @@ -661,7 +661,7 @@ PeerConnectionImpl::CreateDataChannel(const nsACString& aLabel, CSFLogDebugS(logTag, __FUNCTION__ << ": making DOMDataChannel"); - return NS_NewDOMDataChannel(dataChannel, mWindow, aRetval); + return NS_NewDOMDataChannel(dataChannel.forget(), mWindow, aRetval); #else return NS_OK; #endif diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index 4d211a4b12c6..018ed0ba1986 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -621,6 +621,12 @@ DataChannelConnection::FindFreeStreamOut() limit = MAX_NUM_STREAMS; for (i = 0; i < limit; ++i) { if (!mStreamsOut[i]) { + // Verify it's not still in the process of closing + for (uint32_t j = 0; j < mStreamsResetting.Length(); ++j) { + if (mStreamsResetting[j] == i) { + continue; + } + } break; } } @@ -760,7 +766,7 @@ bool DataChannelConnection::SendDeferredMessages() { uint32_t i; - DataChannel *channel; + nsRefPtr channel; // we may null out the refs to this bool still_blocked = false; bool sent = false; @@ -814,7 +820,6 @@ DataChannelConnection::SendDeferredMessages() // delete the channel. mStreamsIn[channel->mStreamIn] = nullptr; mStreamsOut[channel->mStreamOut] = nullptr; - delete channel; } } } @@ -899,7 +904,7 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_ size_t length, uint16_t streamIn) { - DataChannel *channel; + nsRefPtr channel; uint32_t prValue; uint16_t prPolicy; uint32_t flags; @@ -929,7 +934,8 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_ } prValue = ntohs(req->reliability_params); flags = ntohs(req->flags) & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED; - channel = new DataChannel(this, INVALID_STREAM, streamIn, + channel = new DataChannel(this, + INVALID_STREAM, streamIn, DataChannel::CONNECTING, label, prPolicy, prValue, @@ -937,17 +943,18 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_ nullptr, nullptr); mStreamsIn[streamIn] = channel; - OpenResponseFinish(channel); + OpenResponseFinish(channel.forget()); } void -DataChannelConnection::OpenResponseFinish(DataChannel *channel) +DataChannelConnection::OpenResponseFinish(already_AddRefed aChannel) { + nsRefPtr channel(aChannel); uint16_t streamOut = FindFreeStreamOut(); // may be INVALID_STREAM! mLock.AssertCurrentThreadOwns(); - LOG(("Finished response: channel %p, streamOut = %u", channel, streamOut)); + LOG(("Finished response: channel %p, streamOut = %u", channel.get(), streamOut)); if (streamOut == INVALID_STREAM) { if (!RequestMoreStreamsOut()) { @@ -955,23 +962,22 @@ DataChannelConnection::OpenResponseFinish(DataChannel *channel) mStreamsIn[channel->mStreamIn] = nullptr; // we can do this with the lock held because mStreamOut is INVALID_STREAM, // so there's no outbound channel to reset - delete channel; return; } - LOG(("Queuing channel %p to finish response", channel)); + LOG(("Queuing channel %d to finish response", channel->mStreamIn)); channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_RSP; - mPending.Push(channel); + DataChannel *temp = channel.get(); // Can't cast away already_AddRefed<> from channel.forget() + channel.forget(); + mPending.Push(temp); // can't notify the user until we can send an OpenResponse } else { channel->mStreamOut = streamOut; mStreamsOut[streamOut] = channel; if (SendOpenResponseMessage(streamOut, channel->mStreamIn)) { - LOG(("successful incoming open of '%s' in: %u, out: %u\n", - channel->mLabel.get(), channel->mStreamIn, streamOut)); - /* Notify ondatachannel */ // XXX We need to make sure connection sticks around until the message is delivered - LOG(("%s: sending ON_CHANNEL_CREATED for %p", __FUNCTION__, channel)); + LOG(("%s: sending ON_CHANNEL_CREATED for %s: %d/%d", __FUNCTION__, + channel->mLabel.get(), streamOut, channel->mStreamIn)); NS_DispatchToMainThread(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_CREATED, this, channel)); @@ -986,7 +992,6 @@ DataChannelConnection::OpenResponseFinish(DataChannel *channel) channel->mStreamOut = INVALID_STREAM; // we can do this with the lock held because mStreamOut is INVALID_STREAM, // so there's no outbound channel to reset (we failed to send on it) - delete channel; return; // paranoia against future changes since we unlocked } } @@ -1006,7 +1011,7 @@ DataChannelConnection::HandleOpenResponseMessage(const struct rtcweb_datachannel streamOut = ntohs(rsp->reverse_stream); channel = FindChannelByStreamOut(streamOut); - NS_ENSURE_TRUE(channel != nullptr, /* */); + NS_ENSURE_TRUE(channel, /* */); NS_ENSURE_TRUE(channel->mState == CONNECTING, /* */); if (rsp->error) { @@ -1044,7 +1049,7 @@ DataChannelConnection::HandleOpenAckMessage(const struct rtcweb_datachannel_ack channel = FindChannelByStreamIn(streamIn); - NS_ENSURE_TRUE(channel != nullptr, /* */); + NS_ENSURE_TRUE(channel, /* */); NS_ENSURE_TRUE(channel->mState == CONNECTING, /* */); channel->mState = channel->mReady ? DataChannel::OPEN : DataChannel::WAITING_TO_OPEN; @@ -1079,7 +1084,7 @@ DataChannelConnection::HandleDataMessage(uint32_t ppid, channel = FindChannelByStreamIn(streamIn); // XXX A closed channel may trip this... check - NS_ENSURE_TRUE(channel != nullptr, /* */); + NS_ENSURE_TRUE(channel, /* */); NS_ENSURE_TRUE(channel->mState != CONNECTING, /* */); // XXX should this be a simple if, no warnings/debugbreaks? @@ -1113,11 +1118,10 @@ DataChannelConnection::HandleDataMessage(uint32_t ppid, if (!channel->mBinaryBuffer.IsEmpty()) { channel->mBinaryBuffer += recvData; LOG(("%s: sending ON_DATA (binary fragmented) for %p", __FUNCTION__, channel)); - SendOrQueue(channel, - new DataChannelOnMessageAvailable( - DataChannelOnMessageAvailable::ON_DATA, this, - channel, channel->mBinaryBuffer, - channel->mBinaryBuffer.Length())); + channel->SendOrQueue(new DataChannelOnMessageAvailable( + DataChannelOnMessageAvailable::ON_DATA, this, + channel, channel->mBinaryBuffer, + channel->mBinaryBuffer.Length())); channel->mBinaryBuffer.Truncate(0); return; } @@ -1130,24 +1134,19 @@ DataChannelConnection::HandleDataMessage(uint32_t ppid, } /* Notify onmessage */ LOG(("%s: sending ON_DATA for %p", __FUNCTION__, channel)); - SendOrQueue(channel, - new DataChannelOnMessageAvailable( - DataChannelOnMessageAvailable::ON_DATA, this, - channel, recvData, length)); + channel->SendOrQueue(new DataChannelOnMessageAvailable( + DataChannelOnMessageAvailable::ON_DATA, this, + channel, recvData, length)); } } // Called with mLock locked! void -DataChannelConnection::SendOrQueue(DataChannel *aChannel, - DataChannelOnMessageAvailable *aMessage) +DataChannel::SendOrQueue(DataChannelOnMessageAvailable *aMessage) { - mLock.AssertCurrentThreadOwns(); - - if (!aChannel->mReady && - (aChannel->mState == DataChannel::CONNECTING || - aChannel->mState == DataChannel::WAITING_TO_OPEN)) { - aChannel->mQueuedMessages.AppendElement(aMessage); + if (!mReady && + (mState == CONNECTING || mState == WAITING_TO_OPEN)) { + mQueuedMessages.AppendElement(aMessage); } else { NS_DispatchToMainThread(aMessage); } @@ -1394,6 +1393,7 @@ DataChannelConnection::ResetOutgoingStream(uint16_t streamOut) uint32_t i; mLock.AssertCurrentThreadOwns(); + LOG(("Resetting outgoing stream %d",streamOut)); // Rarely has more than a couple items and only for a short time for (i = 0; i < mStreamsResetting.Length(); ++i) { if (mStreamsResetting[i] == streamOut) { @@ -1434,7 +1434,7 @@ void DataChannelConnection::HandleStreamResetEvent(const struct sctp_stream_reset_event *strrst) { uint32_t n, i; - DataChannel *channel; + nsRefPtr channel; // since we may null out the ref to the channel if (!(strrst->strreset_flags & SCTP_STREAM_RESET_DENIED) && !(strrst->strreset_flags & SCTP_STREAM_RESET_FAILED)) { @@ -1442,7 +1442,9 @@ DataChannelConnection::HandleStreamResetEvent(const struct sctp_stream_reset_eve for (i = 0; i < n; ++i) { if (strrst->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { channel = FindChannelByStreamIn(strrst->strreset_stream_list[i]); - if (channel != nullptr) { + if (channel) { + LOG(("Channel %d outgoing/%d incoming closed", + channel->mStreamOut,channel->mStreamIn)); mStreamsIn[channel->mStreamIn] = nullptr; channel->mStreamIn = INVALID_STREAM; if (channel->mStreamOut == INVALID_STREAM) { @@ -1462,6 +1464,8 @@ DataChannelConnection::HandleStreamResetEvent(const struct sctp_stream_reset_eve if (strrst->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) { channel = FindChannelByStreamOut(strrst->strreset_stream_list[i]); if (channel != nullptr && channel->mStreamOut != INVALID_STREAM) { + LOG(("Channel %d outgoing/%d incoming closed", + channel->mStreamOut,channel->mStreamIn)); mStreamsOut[channel->mStreamOut] = nullptr; channel->mStreamOut = INVALID_STREAM; if (channel->mStreamIn == INVALID_STREAM) { @@ -1484,7 +1488,7 @@ DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_e { uint16_t streamOut; uint32_t i; - DataChannel *channel; + nsRefPtr channel; if (strchg->strchange_flags == SCTP_STREAM_CHANGE_DENIED) { LOG(("*** Failed increasing number of streams from %u (%u/%u)", @@ -1531,18 +1535,19 @@ DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_e // Can't copy nsDeque's. Move into temp array since any that fail will // go back to mPending nsDeque temp; - while (nullptr != (channel = static_cast(mPending.PopFront()))) { - temp.Push(channel); + DataChannel *temp_channel; // really already_AddRefed<> + while (nullptr != (temp_channel = static_cast(mPending.PopFront()))) { + temp.Push(static_cast(temp_channel)); } // Now assign our new streams - while (nullptr != (channel = static_cast(temp.PopFront()))) { + while (nullptr != (channel = dont_AddRef(static_cast(temp.PopFront())))) { if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_RSP) { channel->mFlags &= ~DATA_CHANNEL_FLAGS_FINISH_RSP; - OpenResponseFinish(channel); // may reset the flag and re-push + OpenResponseFinish(channel.forget()); // may reset the flag and re-push } else if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) { channel->mFlags &= ~DATA_CHANNEL_FLAGS_FINISH_OPEN; - OpenFinish(channel); // may reset the flag and re-push + OpenFinish(channel.forget()); // may reset the flag and re-push } } } @@ -1660,12 +1665,11 @@ DataChannelConnection::ReceiveCallback(struct socket* sock, void *data, size_t d return 1; } -DataChannel * +already_AddRefed DataChannelConnection::Open(const nsACString& label, Type type, bool inOrder, uint32_t prValue, DataChannelListener *aListener, nsISupports *aContext) { - DataChannel *channel; uint16_t prPolicy = SCTP_PR_SCTP_NONE; uint32_t flags; @@ -1687,25 +1691,27 @@ DataChannelConnection::Open(const nsACString& label, Type type, bool inOrder, } flags = !inOrder ? DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED : 0; - channel = new DataChannel(this, INVALID_STREAM, INVALID_STREAM, - DataChannel::CONNECTING, - label, type, prValue, - flags, - aListener, aContext); // infallible malloc + nsRefPtr channel(new DataChannel(this, + INVALID_STREAM, INVALID_STREAM, + DataChannel::CONNECTING, + label, type, prValue, + flags, + aListener, aContext)); MutexAutoLock lock(mLock); // OpenFinish assumes this - return OpenFinish(channel); + return OpenFinish(channel.forget()); } // Separate routine so we can also call it to finish up from pending opens -DataChannel * -DataChannelConnection::OpenFinish(DataChannel *channel) +already_AddRefed +DataChannelConnection::OpenFinish(already_AddRefed aChannel) { uint16_t streamOut = FindFreeStreamOut(); // may be INVALID_STREAM! + nsRefPtr channel(aChannel); mLock.AssertCurrentThreadOwns(); - LOG(("Finishing open: channel %p, streamOut = %u", channel, streamOut)); + LOG(("Finishing open: channel %p, streamOut = %u", channel.get(), streamOut)); if (streamOut == INVALID_STREAM) { if (!RequestMoreStreamsOut()) { @@ -1713,18 +1719,18 @@ DataChannelConnection::OpenFinish(DataChannel *channel) // We already returned the channel to the app. Mark it closed channel->mState = CLOSED; NS_ERROR("Failed to request more streams"); - return channel; + return channel.forget(); } // we can do this with the lock held because mStreamOut is INVALID_STREAM, // so there's no outbound channel to reset - delete channel; return nullptr; } - LOG(("Queuing channel %p to finish open", channel)); + LOG(("Queuing channel %p to finish open", channel.get())); // Also serves to mark we told the app channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_OPEN; + channel->AddRef(); // we need a ref for the nsDeQue and one to return mPending.Push(channel); - return channel; + return channel.forget(); } mStreamsOut[streamOut] = channel; channel->mStreamOut = streamOut; @@ -1743,11 +1749,10 @@ DataChannelConnection::OpenFinish(DataChannel *channel) channel->mStreamOut = INVALID_STREAM; // we can do this with the lock held because mStreamOut is INVALID_STREAM, // so there's no outbound channel to reset (we didn't sent anything) - delete channel; return nullptr; } } - return channel; + return channel.forget(); } int32_t @@ -1930,14 +1935,15 @@ DataChannelConnection::SendMsgCommon(uint16_t stream, const nsACString &aMsg, void DataChannelConnection::Close(uint16_t streamOut) { - DataChannel *channel; + nsRefPtr channel; // make sure it doesn't go away on us MutexAutoLock lock(mLock); LOG(("Closing stream %d",streamOut)); channel = FindChannelByStreamOut(streamOut); if (channel) { channel->mBufferedData.Clear(); - ResetOutgoingStream(channel->mStreamOut); + if (channel->mStreamOut != INVALID_STREAM) + ResetOutgoingStream(channel->mStreamOut); SendOutgoingStreamReset(); channel->mState = CLOSING; } @@ -1961,9 +1967,15 @@ void DataChannelConnection::CloseAll() } // Clean up any pending opens for channels - DataChannel *channel; - while (nullptr != (channel = static_cast(mPending.PopFront()))) - channel->Close(); + nsRefPtr channel; + while (nullptr != (channel = dont_AddRef(static_cast(mPending.PopFront())))) + channel->Close(); // also releases the ref on each iteration +} + +DataChannel::~DataChannel() +{ + LOG(("Destroying Data channel %d/%d", mStreamOut, mStreamIn)); + Close(); } void diff --git a/netwerk/sctp/datachannel/DataChannel.h b/netwerk/sctp/datachannel/DataChannel.h index d0b950962476..c5305fee0c20 100644 --- a/netwerk/sctp/datachannel/DataChannel.h +++ b/netwerk/sctp/datachannel/DataChannel.h @@ -67,11 +67,11 @@ public: // Called when a DOMString message is received. virtual nsresult OnMessageAvailable(nsISupports *aContext, - const nsACString& message) = 0; + const nsACString& message) = 0; // Called when a binary message is received. virtual nsresult OnBinaryMessageAvailable(nsISupports *aContext, - const nsACString& message) = 0; + const nsACString& message) = 0; // Called when the channel is connected virtual nsresult OnChannelConnected(nsISupports *aContext) = 0; @@ -127,10 +127,11 @@ public: PARTIAL_RELIABLE_TIMED = 2 } Type; - DataChannel *Open(const nsACString& label, - Type type, bool inOrder, - uint32_t prValue, DataChannelListener *aListener, - nsISupports *aContext); + already_AddRefed Open(const nsACString& label, + Type type, bool inOrder, + uint32_t prValue, + DataChannelListener *aListener, + nsISupports *aContext); void Close(uint16_t stream); void CloseAll(); @@ -188,9 +189,8 @@ private: uint32_t len); int32_t SendMsgCommon(uint16_t stream, const nsACString &aMsg, bool isBinary); - DataChannel *OpenFinish(DataChannel *channel); + already_AddRefed OpenFinish(already_AddRefed channel); - void SendOrQueue(DataChannel *aChannel, DataChannelOnMessageAvailable *aMessage); void StartDefer(); bool SendDeferredMessages(); void SendOutgoingStreamReset(); @@ -198,7 +198,7 @@ private: void HandleOpenRequestMessage(const struct rtcweb_datachannel_open_request *req, size_t length, uint16_t streamIn); - void OpenResponseFinish(DataChannel *channel); + void OpenResponseFinish(already_AddRefed channel); void HandleOpenResponseMessage(const struct rtcweb_datachannel_open_response *rsp, size_t length, uint16_t streamIn); void HandleOpenAckMessage(const struct rtcweb_datachannel_ack *ack, @@ -228,9 +228,9 @@ private: // NOTE: while these arrays will auto-expand, increases in the number of // channels available from the stack must be negotiated! - nsAutoTArray mStreamsOut; - nsAutoTArray mStreamsIn; - nsDeque mPending; // Holds DataChannels + nsAutoTArray,16> mStreamsOut; + nsAutoTArray,16> mStreamsIn; + nsDeque mPending; // Holds already_AddRefeds -- careful! // Streams pending reset nsAutoTArray mStreamsResetting; @@ -285,10 +285,9 @@ public: NS_ASSERTION(mConnection,"NULL connection"); } - ~DataChannel() - { - Close(); - } + ~DataChannel(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataChannel); // Close this DataChannel. Can be called multiple times. void Close(); @@ -345,6 +344,8 @@ public: void AppReady(); + void SendOrQueue(DataChannelOnMessageAvailable *aMessage); + protected: DataChannelListener *mListener; @@ -465,7 +466,7 @@ private: int32_t mType; // XXX should use union - DataChannel *mChannel; // XXX careful of ownership! + nsRefPtr mChannel; nsRefPtr mConnection; nsCString mData; int32_t mLen; From a4c4d68d13f7c4bc0ef7a64eaa9e91352ccb8431 Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Fri, 2 Nov 2012 12:31:02 -0700 Subject: [PATCH 66/83] Bug 787065: Settings home button should go back home. [r=mifnkle] --HG-- extra : rebase_source : d761101ddd02fe9bf9d684b3c330481e77a73d2b --- mobile/android/base/GeckoPreferences.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mobile/android/base/GeckoPreferences.java b/mobile/android/base/GeckoPreferences.java index c87f582160d2..89b10c37134a 100644 --- a/mobile/android/base/GeckoPreferences.java +++ b/mobile/android/base/GeckoPreferences.java @@ -16,6 +16,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.os.Build; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; @@ -61,6 +62,9 @@ public class GeckoPreferences super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); registerEventListener("Sanitize:Finished"); + + if (Build.VERSION.SDK_INT >= 14) + getActionBar().setHomeButtonEnabled(true); } @Override From 5e7b664ebfc29bf5094aca98e6a69037ca540ed3 Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Fri, 2 Nov 2012 12:32:54 -0700 Subject: [PATCH 67/83] Bug 797039: Update notification icon. [r=mfinkle] --HG-- extra : rebase_source : 6a670ff139fb731169a6b26a3855502a42dca253 --- mobile/android/base/GeckoAppShell.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index ba0802b06fee..d186ffd94e59 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -1290,7 +1290,7 @@ public class GeckoAppShell "- cookie = '" + aAlertCookie +"'\n" + "- name = '" + aAlertName + "'"); - int icon = R.drawable.icon; // Just use the app icon by default + int icon = R.drawable.ic_status_logo; Uri imageUri = Uri.parse(aImageUrl); String scheme = imageUri.getScheme(); From 649ff82d7736d3856c0a015256077859faf6dcff Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Fri, 12 Oct 2012 12:48:25 +0200 Subject: [PATCH 68/83] Test for bug 798264. --- js/xpconnect/tests/mochitest/test_lookupMethod.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/js/xpconnect/tests/mochitest/test_lookupMethod.html b/js/xpconnect/tests/mochitest/test_lookupMethod.html index 408d6d22a528..4edac8b54b81 100644 --- a/js/xpconnect/tests/mochitest/test_lookupMethod.html +++ b/js/xpconnect/tests/mochitest/test_lookupMethod.html @@ -47,6 +47,11 @@ function go() { var gebtn = SC.lookupMethod(document, 'getElementsByTagName'); is(Function.call.apply(gebtn, [window, 'iframe']).length, 2, "method is bound"); + // Test that lookupMethod doesn't return ChromeOnly properties when used from + // content. + is(SC.lookupMethod(new XMLHttpRequest(), 'getInterface'), undefined, + "lookupMethod from content shouldn't find a ChromeOnly property"); + // Test that we throw for location objects. Location objects already use same- // compartment Xrays, so callers shouldn't need to use lookupMethod there. And // making it work would involve complicating the security surounding the From a53da3dcf1e8a5ceb2b512cb7915bcce8ebea96d Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Fri, 2 Nov 2012 10:21:19 -0400 Subject: [PATCH 69/83] Bug 807994 - Get rid of IME code raw type usage to prevent javac 1.7 compile errors. r=cpeterson --- mobile/android/base/GeckoEditable.java | 8 ++++---- mobile/android/base/GeckoInputConnection.java | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java index c0311ca2109b..6787095e9f9c 100644 --- a/mobile/android/base/GeckoEditable.java +++ b/mobile/android/base/GeckoEditable.java @@ -229,7 +229,7 @@ final class GeckoEditable mText = new SpannableStringBuilder(); - final Class[] PROXY_INTERFACES = { Editable.class }; + final Class[] PROXY_INTERFACES = { Editable.class }; mProxy = (Editable)Proxy.newProxyInstance( Editable.class.getClassLoader(), PROXY_INTERFACES, this); @@ -575,8 +575,7 @@ final class GeckoEditable } else if (obj instanceof CharSequence) { sb.append("\"").append(obj.toString().replace('\n', '\u21b2')).append("\""); } else if (obj.getClass().isArray()) { - Class cls = obj.getClass(); - sb.append(cls.getComponentType().getSimpleName()).append("[") + sb.append(obj.getClass().getComponentType().getSimpleName()).append("[") .append(java.lang.reflect.Array.getLength(obj)).append("]"); } else { sb.append(obj.toString()); @@ -588,7 +587,7 @@ final class GeckoEditable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object target; - final Class methodInterface = method.getDeclaringClass(); + final Class methodInterface = method.getDeclaringClass(); if (DEBUG) { // Editable methods should all be called from the UI thread GeckoApp.assertOnUiThread(); @@ -786,6 +785,7 @@ final class GeckoEditable } @Override + @SuppressWarnings("rawtypes") // nextSpanTransition uses raw Class in its Android declaration public int nextSpanTransition(int start, int limit, Class type) { throw new UnsupportedOperationException(); } diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java index a1b15682dbf5..64aab7b59151 100644 --- a/mobile/android/base/GeckoInputConnection.java +++ b/mobile/android/base/GeckoInputConnection.java @@ -593,8 +593,7 @@ final class DebugGeckoInputConnection } else if (obj instanceof CharSequence) { sb.append("\"").append(obj.toString().replace('\n', '\u21b2')).append("\""); } else if (obj.getClass().isArray()) { - Class cls = obj.getClass(); - sb.append(cls.getComponentType().getSimpleName()).append("[") + sb.append(obj.getClass().getComponentType().getSimpleName()).append("[") .append(java.lang.reflect.Array.getLength(obj)).append("]"); } else { sb.append(obj.toString()); From f923e97685bedba2e2c4ec718373726f104681d2 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 2 Nov 2012 12:59:20 -0700 Subject: [PATCH 70/83] Bug 790454 - Set touch target in touchstart. r=smaug --- layout/base/nsPresShell.cpp | 215 +++++++++++++++++--------- layout/xul/base/src/nsSliderFrame.cpp | 3 - 2 files changed, 138 insertions(+), 80 deletions(-) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 5723cb492d5e..58f6f1fdcf6f 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5652,6 +5652,23 @@ AppendToTouchList(const uint32_t& aKey, nsCOMPtr& aData, void *aTou return PL_DHASH_NEXT; } +static PLDHashOperator +FindAnyTarget(const uint32_t& aKey, nsCOMPtr& aData, + void* aAnyTarget) +{ + if (aData) { + nsCOMPtr target; + aData->GetTarget(getter_AddRefs(target)); + if (target) { + nsCOMPtr* content = + static_cast*>(aAnyTarget); + *content = do_QueryInterface(target); + return PL_DHASH_STOP; + } + } + return PL_DHASH_NEXT; +} + nsIFrame* GetNearestFrameContainingPresShell(nsIPresShell* aPresShell) { nsIView* view = aPresShell->GetViewManager()->GetRootView(); @@ -5855,7 +5872,6 @@ PresShell::HandleEvent(nsIFrame *aFrame, if (!captureRetarget && !isWindowLevelMouseExit) { nsPoint eventPoint; if (aEvent->message == NS_TOUCH_START) { - // Add any new touches to the queue nsTouchEvent* touchEvent = static_cast(aEvent); // if there is only one touch in this touchstart event, assume that it is // the start of a new touch session and evict any old touches in the @@ -5867,7 +5883,19 @@ PresShell::HandleEvent(nsIFrame *aFrame, EvictTouchPoint(touches[i]); } } - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { + // if this is a continuing session, ensure that all these events are + // in the same document by taking the target of the events already in + // the capture list + nsCOMPtr anyTarget; + if (gCaptureTouchList.Count() > 0) { + gCaptureTouchList.Enumerate(&FindAnyTarget, &anyTarget); + } else { + gPreventMouseEvents = false; + } + + // Add any new touches to the queue + for (int32_t i = touchEvent->touches.Length(); i; ) { + --i; nsIDOMTouch *touch = touchEvent->touches[i]; nsDOMTouch *domtouch = static_cast(touch); touch->mMessage = aEvent->message; @@ -5878,9 +5906,58 @@ PresShell::HandleEvent(nsIFrame *aFrame, // This event is a new touch. Mark it as a changedTouch and // add it to the queue. touch->mChanged = true; - gCaptureTouchList.Put(id, touch); - eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, touch->mRefPoint, frame); + // find the target for this touch + uint32_t flags = 0; + eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, + touch->mRefPoint, + frame); + nsIFrame* target = + FindFrameTargetedByInputEvent(aEvent->eventStructType, + frame, + eventPoint, + flags); + if (target && !anyTarget) { + target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget)); + while (anyTarget && !anyTarget->IsElement()) { + anyTarget = anyTarget->GetParent(); + } + domtouch->SetTarget(anyTarget); + gCaptureTouchList.Put(id, touch); + } else { + nsIFrame* newTargetFrame = nullptr; + for (nsIFrame* f = target; f; + f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { + if (f->PresContext()->Document() == anyTarget->OwnerDoc()) { + newTargetFrame = f; + break; + } + // We must be in a subdocument so jump directly to the root frame. + // GetParentOrPlaceholderForCrossDoc gets called immediately to + // jump up to the containing document. + f = f->PresContext()->GetPresShell()->GetRootFrame(); + } + + // if we couldn't find a target frame in the same document as + // anyTarget, remove the touch from the capture touch list, as + // well as the event->touches array. touchmove events that aren't + // in the captured touch list will be discarded + if (!newTargetFrame) { + touchEvent->touches.RemoveElementAt(i); + } else { + target = newTargetFrame; + nsCOMPtr targetContent; + target->GetContentForEvent(aEvent, getter_AddRefs(targetContent)); + while (targetContent && !targetContent->IsElement()) { + targetContent = targetContent->GetParent(); + } + touch->SetTarget(targetContent); + gCaptureTouchList.Put(id, touch); + } + } + if (target) { + frame = target; + } } else { // This touch is an old touch, we need to ensure that is not // marked as changed and set its target correctly @@ -5950,13 +6027,11 @@ PresShell::HandleEvent(nsIFrame *aFrame, PresShell* shell = static_cast(frame->PresContext()->PresShell()); - switch (aEvent->message) { case NS_TOUCH_MOVE: case NS_TOUCH_CANCEL: case NS_TOUCH_END: { - // Remove the changed touches - // need to make sure we only remove touches that are ending here + // get the correct shell to dispatch to nsTouchEvent* touchEvent = static_cast(aEvent); nsTArray > &touches = touchEvent->touches; for (uint32_t i = 0; i < touches.Length(); ++i) { @@ -5987,6 +6062,9 @@ PresShell::HandleEvent(nsIFrame *aFrame, shell = static_cast( contentFrame->PresContext()->PresShell()); + if (shell) { + break; + } } break; } @@ -6354,9 +6432,10 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) case NS_TOUCH_MOVE: { // Check for touches that changed. Mark them add to queue nsTouchEvent* touchEvent = static_cast(aEvent); - nsTArray > touches = touchEvent->touches; + nsTArray >& touches = touchEvent->touches; bool haveChanged = false; - for (uint32_t i = 0; i < touches.Length(); ++i) { + for (int32_t i = touches.Length(); i; ) { + --i; nsIDOMTouch *touch = touches[i]; nsDOMTouch *domtouch = static_cast(touch); if (!touch) { @@ -6369,6 +6448,7 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) nsCOMPtr oldTouch; gCaptureTouchList.Get(id, getter_AddRefs(oldTouch)); if (!oldTouch) { + touches.RemoveElementAt(i); continue; } if(domtouch->Equals(oldTouch)) { @@ -6378,6 +6458,10 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) nsCOMPtr targetPtr; oldTouch->GetTarget(getter_AddRefs(targetPtr)); + if (!targetPtr) { + touches.RemoveElementAt(i); + continue; + } domtouch->SetTarget(targetPtr); gCaptureTouchList.Put(id, touch); @@ -6509,7 +6593,6 @@ PresShell::DispatchTouchEvent(nsEvent *aEvent, nsPresShellEventCB* aEventCB, bool aTouchIsNew) { - nsresult rv = NS_OK; // calling preventDefault on touchstart or the first touchmove for a // point prevents mouse events bool canPrevent = aEvent->message == NS_TOUCH_START || @@ -6517,85 +6600,63 @@ PresShell::DispatchTouchEvent(nsEvent *aEvent, bool preventDefault = false; nsEventStatus tmpStatus = nsEventStatus_eIgnore; nsTouchEvent* touchEvent = static_cast(aEvent); - // touch events should fire on all targets - if (aEvent->message != NS_TOUCH_START) { - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { - nsIDOMTouch *touch = touchEvent->touches[i]; - if (!touch || !touch->mChanged) { - continue; - } - // copy the event - nsCOMPtr targetPtr; - touch->GetTarget(getter_AddRefs(targetPtr)); - if (!targetPtr) { + + // loop over all touches and dispatch events on any that have changed + for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { + nsIDOMTouch *touch = touchEvent->touches[i]; + if (!touch || !touch->mChanged) { + continue; + } + + nsCOMPtr targetPtr; + touch->GetTarget(getter_AddRefs(targetPtr)); + nsCOMPtr content = do_QueryInterface(targetPtr); + if (!content) { + continue; + } + + nsIDocument* doc = content->OwnerDoc(); + nsIContent* capturingContent = GetCapturingContent(); + if (capturingContent) { + if (capturingContent->OwnerDoc() != doc) { + // Wrong document, don't dispatch anything. continue; } + content = capturingContent; + } + // copy the event + nsTouchEvent newEvent(NS_IS_TRUSTED_EVENT(touchEvent) ? + true : false, + touchEvent); + newEvent.target = targetPtr; - nsTouchEvent newEvent(NS_IS_TRUSTED_EVENT(touchEvent) ? - true : false, - touchEvent); - newEvent.target = targetPtr; - - // If someone is capturing, all touch events are filtered to their target - nsCOMPtr content = GetCapturingContent(); - - // if no one is capturing, set the capturing target - if (!content) { - content = do_QueryInterface(targetPtr); - } - nsRefPtr contentPresShell; - if (content && content->OwnerDoc() == mDocument) { - contentPresShell = static_cast - (content->OwnerDoc()->GetShell()); - if (contentPresShell) { - contentPresShell->PushCurrentEventInfo( - content->GetPrimaryFrame(), content); - } - } - nsPresContext *context = nsContentUtils::GetContextForContent(content); - if (!context) { - context = mPresContext; - } - tmpStatus = nsEventStatus_eIgnore; - nsEventDispatcher::Dispatch(targetPtr, context, - &newEvent, nullptr, &tmpStatus, aEventCB); - if (nsEventStatus_eConsumeNoDefault == tmpStatus) { - preventDefault = true; - } + nsRefPtr contentPresShell; + if (doc == mDocument) { + contentPresShell = static_cast(doc->GetShell()); if (contentPresShell) { - contentPresShell->PopCurrentEventInfo(); - } - } - } else { - // touchevents need to have the target attribute set on each touch - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { - nsIDOMTouch *touch = touchEvent->touches[i]; - if (touch->mChanged) { - touch->SetTarget(mCurrentEventContent); + //XXXsmaug huge hack. Pushing possibly capturing content, + // even though event target is something else. + contentPresShell->PushCurrentEventInfo( + content->GetPrimaryFrame(), content); } } - if (mCurrentEventContent) { - nsEventDispatcher::Dispatch(mCurrentEventContent, mPresContext, - aEvent, nullptr, &tmpStatus, aEventCB); - } else { - nsCOMPtr targetContent; - rv = mCurrentEventFrame->GetContentForEvent(aEvent, - getter_AddRefs(targetContent)); - if (NS_SUCCEEDED(rv) && targetContent) { - nsEventDispatcher::Dispatch(targetContent, mPresContext, aEvent, - nullptr, &tmpStatus, aEventCB); - } else if (mDocument) { - nsEventDispatcher::Dispatch(mDocument, mPresContext, aEvent, - nullptr, &tmpStatus, nullptr); - } + nsIPresShell *presShell = doc->GetShell(); + if (!presShell) { + continue; } + + nsPresContext *context = presShell->GetPresContext(); + + tmpStatus = nsEventStatus_eIgnore; + nsEventDispatcher::Dispatch(targetPtr, context, + &newEvent, nullptr, &tmpStatus, aEventCB); if (nsEventStatus_eConsumeNoDefault == tmpStatus) { preventDefault = true; } - if (touchEvent->touches.Length() == 1) { - gPreventMouseEvents = false; + if (contentPresShell) { + contentPresShell->PopCurrentEventInfo(); } } diff --git a/layout/xul/base/src/nsSliderFrame.cpp b/layout/xul/base/src/nsSliderFrame.cpp index 2a2bb409499d..bfd064480f35 100644 --- a/layout/xul/base/src/nsSliderFrame.cpp +++ b/layout/xul/base/src/nsSliderFrame.cpp @@ -530,7 +530,6 @@ nsSliderFrame::HandleEvent(nsPresContext* aPresContext, static_cast(aEvent)->button == nsMouseEvent::eMiddleButton) || (aEvent->message == NS_TOUCH_START && GetScrollToClick())) { - nsPoint eventPoint; if (!GetEventPoint(aEvent, eventPoint)) { return NS_OK; @@ -850,7 +849,6 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aEvent) #ifdef DEBUG_SLIDER printf("Begin dragging\n"); #endif - if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters)) return NS_OK; @@ -988,7 +986,6 @@ nsSliderFrame::HandlePress(nsPresContext* aPresContext, nsEventStatus* aEventStatus) { if (aEvent->message == NS_TOUCH_START && GetScrollToClick()) { - printf("Bailing for touch\n"); return NS_OK; } From 5841ce43601810fda694f1836bed7f5f2a068520 Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Fri, 2 Nov 2012 20:06:43 +0000 Subject: [PATCH 71/83] Bug 797988 Focus outlines for compact menulists on Linux r=enn --- toolkit/themes/gnomestripe/global/menulist.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/toolkit/themes/gnomestripe/global/menulist.css b/toolkit/themes/gnomestripe/global/menulist.css index 258fb543821a..1069eed3233b 100644 --- a/toolkit/themes/gnomestripe/global/menulist.css +++ b/toolkit/themes/gnomestripe/global/menulist.css @@ -124,8 +124,12 @@ html|*.menulist-editable-input { list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif"); } +.menulist-compact > .menulist-label-box, +.menulist-compact[open="true"]:focus > .menulist-label-box { + border: 1px solid transparent; + -moz-appearance: none; +} + .menulist-compact:focus > .menulist-label-box { border: 1px dotted; - background-color: transparent; - color: inherit; } From 958b1668fa0e6156d1c2577387fdb0428f0a1c14 Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Fri, 2 Nov 2012 20:07:50 +0000 Subject: [PATCH 72/83] Bug 664695 Improve signature of nsIConsoleService::GetMessageArray r=bsmedberg --- .../browser/obsolete/browser_console_clear.js | 4 +--- .../tests/chrome/templates_shared.js | 5 ++--- .../tests/chrome/test_tmpl_errors.xul | 5 ++--- mobile/xul/chrome/content/console.js | 4 +--- .../console/content/consoleBindings.xml | 4 +--- .../test/xml/rfc4287/author_namespaces.xml | 2 +- .../devtools/webconsole/WebConsoleUtils.jsm | 19 +++++-------------- toolkit/xre/nsConsoleWriter.cpp | 2 +- xpcom/base/nsConsoleService.cpp | 2 +- xpcom/base/nsIConsoleService.idl | 6 +++--- 10 files changed, 18 insertions(+), 35 deletions(-) diff --git a/browser/components/privatebrowsing/test/browser/obsolete/browser_console_clear.js b/browser/components/privatebrowsing/test/browser/obsolete/browser_console_clear.js index 5b60b73c2b5a..a4953395e343 100644 --- a/browser/components/privatebrowsing/test/browser/obsolete/browser_console_clear.js +++ b/browser/components/privatebrowsing/test/browser/obsolete/browser_console_clear.js @@ -35,9 +35,7 @@ function test() { consoleService.registerListener(consoleObserver); function messageExists() { - let out = {}; - consoleService.getMessageArray(out, {}); - let messages = out.value || []; + let messages = consoleService.getMessageArray() || []; for (let i = 0; i < messages.length; ++i) { if (messages[i].message == TEST_MESSAGE) return true; diff --git a/content/xul/templates/tests/chrome/templates_shared.js b/content/xul/templates/tests/chrome/templates_shared.js index 267f11780146..c46612c7a556 100644 --- a/content/xul/templates/tests/chrome/templates_shared.js +++ b/content/xul/templates/tests/chrome/templates_shared.js @@ -434,9 +434,8 @@ function compareConsoleMessages() { var consoleService = Components.classes["@mozilla.org/consoleservice;1"]. getService(Components.interfaces.nsIConsoleService); - var out = {}; - consoleService.getMessageArray(out, {}); - var messages = (out.value || []).map(function (m) m.message); + var messages = consoleService.getMessageArray() || []; + messages = messages.map(function (m) m.message); // Copy to avoid modifying expectedConsoleMessages var expect = expectedConsoleMessages.concat(); for (var m = 0; m < messages.length; m++) { diff --git a/content/xul/templates/tests/chrome/test_tmpl_errors.xul b/content/xul/templates/tests/chrome/test_tmpl_errors.xul index 704dcc8fb956..e6852210fbdc 100644 --- a/content/xul/templates/tests/chrome/test_tmpl_errors.xul +++ b/content/xul/templates/tests/chrome/test_tmpl_errors.xul @@ -23,9 +23,8 @@ var consoleService = Components.classes["@mozilla.org/consoleservice;1"]. function checkConsole(expectedError) { - var out = {}; - consoleService.getMessageArray(out, {}); - is(out.value[0].message, expectedError, "logged message " + expectedError); + var message = consoleService.getMessageArray()[0].message; + is(message, expectedError, "logged message " + expectedError); } // each test consists of a pre function executed before the template build, an diff --git a/mobile/xul/chrome/content/console.js b/mobile/xul/chrome/content/console.js index e6f1ad99ea36..af4cd1c9d3a4 100644 --- a/mobile/xul/chrome/content/console.js +++ b/mobile/xul/chrome/content/console.js @@ -192,9 +192,7 @@ let ConsoleView = { }, appendInitialItems: function cv_appendInitialItems() { - let out = {}; // Throwaway references to support 'out' parameters. - Services.console.getMessageArray(out, {}); - let messages = out.value; + let messages = Services.console.getMessageArray(); // In case getMessageArray returns 0-length array as null if (!messages) diff --git a/toolkit/components/console/content/consoleBindings.xml b/toolkit/components/console/content/consoleBindings.xml index 5eb44e0977f1..3d30af416ec8 100644 --- a/toolkit/components/console/content/consoleBindings.xml +++ b/toolkit/components/console/content/consoleBindings.xml @@ -130,9 +130,7 @@ diff --git a/toolkit/devtools/webconsole/WebConsoleUtils.jsm b/toolkit/devtools/webconsole/WebConsoleUtils.jsm index b4abc490a4a2..abda0667e79c 100644 --- a/toolkit/devtools/webconsole/WebConsoleUtils.jsm +++ b/toolkit/devtools/webconsole/WebConsoleUtils.jsm @@ -1326,22 +1326,13 @@ PageErrorListener.prototype = { let innerWindowId = this.window ? WebConsoleUtils.getInnerWindowId(this.window) : null; - let result = []; - let errors = {}; - Services.console.getMessageArray(errors, {}); + let errors = Services.console.getMessageArray() || []; - (errors.value || []).forEach(function(aError) { - if (!(aError instanceof Ci.nsIScriptError) || - (innerWindowId && - (aError.innerWindowID != innerWindowId || - !this.isCategoryAllowed(aError.category)))) { - return; - } - - result.push(aError); + return errors.filter(function(aError) { + return aError instanceof Ci.nsIScriptError && + (!innerWindowId || aError.innerWindowID == innerWindowId) && + this.isCategoryAllowed(aError.category); }, this); - - return result; }, /** diff --git a/toolkit/xre/nsConsoleWriter.cpp b/toolkit/xre/nsConsoleWriter.cpp index 89fc5a192e94..b747dcd6952f 100644 --- a/toolkit/xre/nsConsoleWriter.cpp +++ b/toolkit/xre/nsConsoleWriter.cpp @@ -58,7 +58,7 @@ WriteConsoleLog() nsIConsoleMessage** messages; uint32_t mcount; - rv = csrv->GetMessageArray(&messages, &mcount); + rv = csrv->GetMessageArray(&mcount, &messages); if (NS_FAILED(rv)) { PR_Close(file); return; diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp index dc868eb9219e..137cb9ef96e8 100644 --- a/xpcom/base/nsConsoleService.cpp +++ b/xpcom/base/nsConsoleService.cpp @@ -208,7 +208,7 @@ nsConsoleService::LogStringMessage(const PRUnichar *message) } NS_IMETHODIMP -nsConsoleService::GetMessageArray(nsIConsoleMessage ***messages, uint32_t *count) +nsConsoleService::GetMessageArray(uint32_t *count, nsIConsoleMessage ***messages) { nsIConsoleMessage **messageArray; diff --git a/xpcom/base/nsIConsoleService.idl b/xpcom/base/nsIConsoleService.idl index cc26eb5b6a3f..71d63a7883ba 100644 --- a/xpcom/base/nsIConsoleService.idl +++ b/xpcom/base/nsIConsoleService.idl @@ -7,7 +7,7 @@ #include "nsIConsoleListener.idl" #include "nsIConsoleMessage.idl" -[scriptable, uuid(883472a0-ea9b-11da-8ad9-0800200c9a66)] +[scriptable, uuid(0eb81d20-c37e-42d4-82a8-ca9ae96bdf52)] interface nsIConsoleService : nsISupports { void logMessage(in nsIConsoleMessage message); @@ -23,8 +23,8 @@ interface nsIConsoleService : nsISupports * will allocate one word for messages, so as to show up as a * 0-length array when called from script. */ - void getMessageArray([array, size_is(count)] out nsIConsoleMessage messages, - out uint32_t count); + void getMessageArray([optional] out uint32_t count, + [retval, array, size_is(count)] out nsIConsoleMessage messages); /** * To guard against stack overflows from listeners that could log From 5572f5a7cc540c5bc246ef472b488b6388fad3cf Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Fri, 2 Nov 2012 16:11:50 -0400 Subject: [PATCH 73/83] Bug 703612 - Make DBUS calls asynchronous to prevent slowness because of DBus daemon being overloaded in Battery API UPower backend. r=mounir --- hal/linux/UPowerClient.cpp | 106 +++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 33 deletions(-) diff --git a/hal/linux/UPowerClient.cpp b/hal/linux/UPowerClient.cpp index e6cd2f716fc4..ae678b5cda72 100644 --- a/hal/linux/UPowerClient.cpp +++ b/hal/linux/UPowerClient.cpp @@ -16,13 +16,6 @@ * We are specializing nsAutoRef class. */ -template <> -class nsAutoRefTraits : public nsPointerRefTraits -{ -public: - static void Release(DBusGProxy* ptr) { g_object_unref(ptr); } -}; - template <> class nsAutoRefTraits : public nsPointerRefTraits { @@ -71,13 +64,17 @@ private: * Update the currently tracked device. * @return whether everything went ok. */ - void UpdateTrackedDevice(); + void UpdateTrackedDeviceSync(); /** * Returns a hash table with the properties of aDevice. * Note: the caller has to unref the hash table. */ - GHashTable* GetDeviceProperties(const gchar* aDevice); + GHashTable* GetDevicePropertiesSync(DBusGProxy* aProxy); + void GetDevicePropertiesAsync(DBusGProxy* aProxy); + static void GetDevicePropertiesCallback(DBusGProxy* aProxy, + DBusGProxyCall* aCall, + void* aData); /** * Using the device properties (aHashTable), this method updates the member @@ -107,6 +104,9 @@ private: // The path of the tracked device. gchar* mTrackedDevice; + // The DBusGProxy for the tracked device. + DBusGProxy* mTrackedDeviceProxy; + double mLevel; bool mCharging; double mRemainingTime; @@ -165,6 +165,7 @@ UPowerClient::UPowerClient() : mDBusConnection(nullptr) , mUPowerProxy(nullptr) , mTrackedDevice(nullptr) + , mTrackedDeviceProxy(nullptr) , mLevel(kDefaultLevel) , mCharging(kDefaultCharging) , mRemainingTime(kDefaultRemainingTime) @@ -173,7 +174,7 @@ UPowerClient::UPowerClient() UPowerClient::~UPowerClient() { - NS_ASSERTION(!mDBusConnection && !mUPowerProxy && !mTrackedDevice, + NS_ASSERTION(!mDBusConnection && !mUPowerProxy && !mTrackedDevice && !mTrackedDeviceProxy, "The observers have not been correctly removed! " "(StopListening should have been called)"); } @@ -206,7 +207,7 @@ UPowerClient::BeginListening() "/org/freedesktop/UPower", "org.freedesktop.UPower"); - UpdateTrackedDevice(); + UpdateTrackedDeviceSync(); /* * TODO: we should probably listen to DeviceAdded and DeviceRemoved signals. @@ -238,6 +239,11 @@ UPowerClient::StopListening() g_free(mTrackedDevice); mTrackedDevice = nullptr; + if (mTrackedDeviceProxy) { + g_object_unref(mTrackedDeviceProxy); + mTrackedDeviceProxy = nullptr; + } + g_object_unref(mUPowerProxy); mUPowerProxy = nullptr; @@ -251,19 +257,27 @@ UPowerClient::StopListening() } void -UPowerClient::UpdateTrackedDevice() +UPowerClient::UpdateTrackedDeviceSync() { GType typeGPtrArray = dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH); GPtrArray* devices = nullptr; GError* error = nullptr; + // Reset the current tracked device: + g_free(mTrackedDevice); + mTrackedDevice = nullptr; + + // Reset the current tracked device proxy: + if (mTrackedDeviceProxy) { + g_object_unref(mTrackedDeviceProxy); + mTrackedDeviceProxy = nullptr; + } + // If that fails, that likely means upower isn't installed. if (!dbus_g_proxy_call(mUPowerProxy, "EnumerateDevices", &error, G_TYPE_INVALID, typeGPtrArray, &devices, G_TYPE_INVALID)) { g_printerr ("Error: %s\n", error->message); - - mTrackedDevice = nullptr; g_error_free(error); return; } @@ -274,27 +288,34 @@ UPowerClient::UpdateTrackedDevice() */ for (guint i=0; ilen; ++i) { gchar* devicePath = static_cast(g_ptr_array_index(devices, i)); - nsAutoRef hashTable(GetDeviceProperties(devicePath)); + + DBusGProxy* proxy = dbus_g_proxy_new_from_proxy(mUPowerProxy, + "org.freedesktop.DBus.Properties", + devicePath); + + nsAutoRef hashTable(GetDevicePropertiesSync(proxy)); if (g_value_get_uint(static_cast(g_hash_table_lookup(hashTable, "Type"))) == sDeviceTypeBattery) { UpdateSavedInfo(hashTable); mTrackedDevice = devicePath; + mTrackedDeviceProxy = proxy; break; } + g_object_unref(proxy); g_free(devicePath); } -#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 22 - g_ptr_array_unref(devices); -#else - g_ptr_array_free(devices, true); -#endif + g_ptr_array_free(devices, true); } /* static */ void UPowerClient::DeviceChanged(DBusGProxy* aProxy, const gchar* aObjectPath, UPowerClient* aListener) { + if (!aListener->mTrackedDevice) { + return; + } + #if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 16 if (g_strcmp0(aObjectPath, aListener->mTrackedDevice)) { #else @@ -303,12 +324,7 @@ UPowerClient::DeviceChanged(DBusGProxy* aProxy, const gchar* aObjectPath, UPower return; } - nsAutoRef hashTable(aListener->GetDeviceProperties(aObjectPath)); - aListener->UpdateSavedInfo(hashTable); - - hal::NotifyBatteryChange(hal::BatteryInformation(aListener->mLevel, - aListener->mCharging, - aListener->mRemainingTime)); + aListener->GetDevicePropertiesAsync(aListener->mTrackedDeviceProxy); } /* static */ DBusHandlerResult @@ -325,18 +341,13 @@ UPowerClient::ConnectionSignalFilter(DBusConnection* aConnection, } GHashTable* -UPowerClient::GetDeviceProperties(const gchar* aDevice) +UPowerClient::GetDevicePropertiesSync(DBusGProxy* aProxy) { - nsAutoRef proxy(dbus_g_proxy_new_for_name(mDBusConnection, - "org.freedesktop.UPower", - aDevice, - "org.freedesktop.DBus.Properties")); - GError* error = nullptr; GHashTable* hashTable = nullptr; GType typeGHashTable = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE); - if (!dbus_g_proxy_call(proxy, "GetAll", &error, G_TYPE_STRING, + if (!dbus_g_proxy_call(aProxy, "GetAll", &error, G_TYPE_STRING, "org.freedesktop.UPower.Device", G_TYPE_INVALID, typeGHashTable, &hashTable, G_TYPE_INVALID)) { g_printerr("Error: %s\n", error->message); @@ -347,6 +358,35 @@ UPowerClient::GetDeviceProperties(const gchar* aDevice) return hashTable; } +/* static */ void +UPowerClient::GetDevicePropertiesCallback(DBusGProxy* aProxy, + DBusGProxyCall* aCall, void* aData) +{ + GError* error = nullptr; + GHashTable* hashTable = nullptr; + GType typeGHashTable = dbus_g_type_get_map("GHashTable", G_TYPE_STRING, + G_TYPE_VALUE); + if (!dbus_g_proxy_end_call(aProxy, aCall, &error, typeGHashTable, + &hashTable, G_TYPE_INVALID)) { + g_printerr("Error: %s\n", error->message); + g_error_free(error); + } else { + sInstance->UpdateSavedInfo(hashTable); + hal::NotifyBatteryChange(hal::BatteryInformation(sInstance->mLevel, + sInstance->mCharging, + sInstance->mRemainingTime)); + g_hash_table_unref(hashTable); + } +} + +void +UPowerClient::GetDevicePropertiesAsync(DBusGProxy* aProxy) +{ + dbus_g_proxy_begin_call(aProxy, "GetAll", GetDevicePropertiesCallback, nullptr, + nullptr, G_TYPE_STRING, + "org.freedesktop.UPower.Device", G_TYPE_INVALID); +} + void UPowerClient::UpdateSavedInfo(GHashTable* aHashTable) { From 55c513ee61f868cf97e388597eabddcb6302aa8f Mon Sep 17 00:00:00 2001 From: Vincent Chang Date: Thu, 1 Nov 2012 16:33:44 +0800 Subject: [PATCH 74/83] Bug 799825 - [wifi] Unable to connect to WPA Network. r=mrbkap --- dom/wifi/WifiWorker.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 50752f6297db..94018f79a367 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -1008,15 +1008,11 @@ var WifiManager = (function() { }); } - // Driver startup on the otoro takes longer than it takes for us + // Driver startup on certain platforms takes longer than it takes for us // to return from loadDriver, so wait 2 seconds before starting // the supplicant to give it a chance to start. - if (device === "otoro") { - timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.init(doStartSupplicant, 2000, Ci.nsITimer.TYPE_ONE_SHOT); - } else { - doStartSupplicant(); - } + timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.init(doStartSupplicant, 2000, Ci.nsITimer.TYPE_ONE_SHOT); }); }); }); From 0b606e0363e77e0652ef3a0ae7d60484251a2e3d Mon Sep 17 00:00:00 2001 From: Vincent Chang Date: Fri, 2 Nov 2012 11:18:11 +0800 Subject: [PATCH 75/83] Bug 799825 - [wifi] Unable to connect to WPA Network. r=mrbkap --- dom/wifi/WifiWorker.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 94018f79a367..79390ce84ace 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -40,14 +40,13 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService", // command always succeeds and we do a string/boolean check for the // expected results). var WifiManager = (function() { - function getSdkVersionAndDevice() { + function getSdkVersion() { Cu.import("resource://gre/modules/systemlibs.js"); let sdkVersion = libcutils.property_get("ro.build.version.sdk"); - return { sdkVersion: parseInt(sdkVersion, 10), - device: libcutils.property_get("ro.product.device") }; + return parseInt(sdkVersion, 10); } - let { sdkVersion, device } = getSdkVersionAndDevice(); + let sdkVersion = getSdkVersion(); var controlWorker = new ChromeWorker(WIFIWORKER_WORKER); var eventWorker = new ChromeWorker(WIFIWORKER_WORKER); @@ -720,7 +719,7 @@ var WifiManager = (function() { } manager.start = function() { - debug("detected SDK version " + sdkVersion + " and device " + device); + debug("detected SDK version " + sdkVersion); connectToSupplicant(connectCallback); } From d390a1808b500282ef72d86802ea94d0900fc6fb Mon Sep 17 00:00:00 2001 From: Mike Habicher Date: Fri, 2 Nov 2012 16:11:50 -0400 Subject: [PATCH 76/83] Bug 801693 - Plumb video recorder state-change (error, size/length limit) handling. r=jst --- dom/camera/CameraControlImpl.cpp | 28 +++++ dom/camera/CameraControlImpl.h | 35 ++++++ dom/camera/DOMCameraControl.cpp | 17 +++ dom/camera/GonkCameraControl.cpp | 165 ++++++++++++++++++++++++++++- dom/camera/GonkCameraControl.h | 3 +- dom/camera/ICameraControl.h | 2 + dom/camera/nsIDOMCameraManager.idl | 17 ++- 7 files changed, 259 insertions(+), 8 deletions(-) diff --git a/dom/camera/CameraControlImpl.cpp b/dom/camera/CameraControlImpl.cpp index ad18ca7bcc92..ad0a69d22f8d 100644 --- a/dom/camera/CameraControlImpl.cpp +++ b/dom/camera/CameraControlImpl.cpp @@ -26,6 +26,7 @@ CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThrea , mStartRecordingOnErrorCb(nullptr) , mOnShutterCb(nullptr) , mOnClosedCb(nullptr) + , mOnRecorderStateChangeCb(nullptr) { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); } @@ -221,6 +222,20 @@ CameraControlImpl::Get(nsICameraClosedCallback** aOnClosed) return NS_OK; } +nsresult +CameraControlImpl::Set(nsICameraRecorderStateChange* aOnRecorderStateChange) +{ + mOnRecorderStateChangeCb = aOnRecorderStateChange; + return NS_OK; +} + +nsresult +CameraControlImpl::Get(nsICameraRecorderStateChange** aOnRecorderStateChange) +{ + *aOnRecorderStateChange = mOnRecorderStateChangeCb; + return NS_OK; +} + already_AddRefed CameraControlImpl::GetRecorderProfileManager() { @@ -239,6 +254,7 @@ CameraControlImpl::Shutdown() mStartRecordingOnErrorCb = nullptr; mOnShutterCb = nullptr; mOnClosedCb = nullptr; + mOnRecorderStateChangeCb = nullptr; } void @@ -279,6 +295,18 @@ CameraControlImpl::OnClosed() } } +void +CameraControlImpl::OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber) +{ + DOM_CAMERA_LOGI("OnRecorderStateChange: '%s'\n", NS_ConvertUTF16toUTF8(aStateMsg).get()); + + nsCOMPtr onRecorderStateChange = new CameraRecorderStateChange(mOnRecorderStateChangeCb, aStateMsg, aStatus, aTrackNumber, mWindowId); + nsresult rv = NS_DispatchToMainThread(onRecorderStateChange); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to dispatch onRecorderStateChange event to main thread (%d)\n", rv); + } +} + nsresult CameraControlImpl::GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) { diff --git a/dom/camera/CameraControlImpl.h b/dom/camera/CameraControlImpl.h index f4be9b95ad0e..f23b1b46faae 100644 --- a/dom/camera/CameraControlImpl.h +++ b/dom/camera/CameraControlImpl.h @@ -67,6 +67,8 @@ public: nsresult Get(nsICameraShutterCallback** aOnShutter); nsresult Set(nsICameraClosedCallback* aOnClosed); nsresult Get(nsICameraClosedCallback** aOnClosed); + nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange); + nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange); nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) { @@ -96,6 +98,7 @@ public: bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder); void OnShutter(); void OnClosed(); + void OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber); uint64_t GetWindowId() { @@ -145,6 +148,7 @@ protected: nsCOMPtr mStartRecordingOnErrorCb; nsCOMPtr mOnShutterCb; nsCOMPtr mOnClosedCb; + nsCOMPtr mOnRecorderStateChangeCb; private: CameraControlImpl(const CameraControlImpl&) MOZ_DELETE; @@ -607,6 +611,37 @@ public: nsCOMPtr mOnErrorCb; }; +// Error result runnable +class CameraRecorderStateChange : public nsRunnable +{ +public: + CameraRecorderStateChange(nsICameraRecorderStateChange* onStateChange, const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber, uint64_t aWindowId) + : mOnStateChangeCb(onStateChange) + , mStateMsg(aStateMsg) + , mStatus(aStatus) + , mTrackNumber(aTrackNumber) + , mWindowId(aWindowId) + { } + + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread()); + + if (mOnStateChangeCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { + // For now, just pass the state message and swallow mStatus and mTrackNumber + mOnStateChangeCb->HandleStateChange(mStateMsg); + } + return NS_OK; + } + +protected: + nsCOMPtr mOnStateChangeCb; + const nsString mStateMsg; + int32_t mStatus; + int32_t mTrackNumber; + uint64_t mWindowId; +}; + } // namespace mozilla #endif // DOM_CAMERA_CAMERACONTROLIMPL_H diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp index 0ee21ff04978..3a9ce7f26463 100644 --- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -229,6 +229,18 @@ nsDOMCameraControl::SetOnClosed(nsICameraClosedCallback* aOnClosed) return mCameraControl->Set(aOnClosed); } +/* attribute nsICameraRecorderStateChange onRecorderStateChange; */ +NS_IMETHODIMP +nsDOMCameraControl::GetOnRecorderStateChange(nsICameraRecorderStateChange** aOnRecorderStateChange) +{ + return mCameraControl->Get(aOnRecorderStateChange); +} +NS_IMETHODIMP +nsDOMCameraControl::SetOnRecorderStateChange(nsICameraRecorderStateChange* aOnRecorderStateChange) +{ + return mCameraControl->Set(aOnRecorderStateChange); +} + /* [implicit_jscontext] void startRecording (in jsval aOptions, in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */ NS_IMETHODIMP nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsIDOMDeviceStorage* storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx) @@ -237,6 +249,11 @@ nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsIDOMDeviceStorag NS_ENSURE_TRUE(storageArea, NS_ERROR_INVALID_ARG); CameraStartRecordingOptions options; + + // Default values, until the dictionary parser can handle them. + options.rotation = 0; + options.maxFileSizeBytes = 0; + options.maxVideoLengthMs = 0; nsresult rv = options.Init(cx, &aOptions); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index 54fff17fc1ad..98a38e1af39e 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -29,6 +29,7 @@ #include "nsThread.h" #include #include "mozilla/FileUtils.h" +#include #include "nsDirectoryServiceDefs.h" // for NS_GetSpecialDirectory #include "nsPrintfCString.h" #include "DOMCameraManager.h" @@ -741,7 +742,7 @@ nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) return NS_ERROR_FAILURE; } - nsresult rv = SetupRecording(fd); + nsresult rv = SetupRecording(fd, aStartRecording->mOptions.maxFileSizeBytes, aStartRecording->mOptions.maxVideoLengthMs); NS_ENSURE_SUCCESS(rv, rv); if (mRecorder->start() != OK) { @@ -914,8 +915,154 @@ nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile) return NS_OK; } +class GonkRecorderListener : public IMediaRecorderClient +{ +public: + GonkRecorderListener(nsGonkCameraControl* aCameraControl) + : mCameraControl(aCameraControl) + { + DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n", __func__, __LINE__, this, mCameraControl.get()); + } + + void notify(int msg, int ext1, int ext2) + { + if (mCameraControl) { + mCameraControl->HandleRecorderEvent(msg, ext1, ext2); + } + } + + IBinder* onAsBinder() + { + DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n"); + return nullptr; + } + +protected: + ~GonkRecorderListener() { } + nsRefPtr mCameraControl; +}; + +void +nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2) +{ + /** + * Refer to base/include/media/mediarecorder.h for a complete list + * of error and info message codes. There are duplicate values + * within the status/error code space, as determined by code inspection: + * + * +------- msg + * | +----- ext1 + * | | +--- ext2 + * V V V + * 1 MEDIA_RECORDER_EVENT_ERROR + * 1 MEDIA_RECORDER_ERROR_UNKNOWN + * [3] ERROR_MALFORMED + * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED + * 0 + * 2 MEDIA_RECORDER_EVENT_INFO + * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED + * 0 + * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED + * 0 + * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1b] + * [3] UNKNOWN_ERROR, etc. + * 100 MEDIA_ERROR[4] + * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED + * 0 + * 100 MEDIA_RECORDER_TRACK_EVENT_ERROR + * 100 MEDIA_RECORDER_TRACK_ERROR_GENERAL[1a] + * [3] UNKNOWN_ERROR, etc. + * 200 MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME[2] + * ? + * 101 MEDIA_RECORDER_TRACK_EVENT_INFO + * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1a] + * [3] UNKNOWN_ERROR, etc. + * N see mediarecorder.h::media_recorder_info_type[5] + * + * 1. a) High 4 bits are the track number, the next 12 bits are reserved, + * and the final 16 bits are the actual error code (above). + * b) But not in this case. + * 2. Never actually used in AOSP code? + * 3. Specific error codes are from utils/Errors.h and/or + * include/media/stagefright/MediaErrors.h. + * 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp. + * 5. These are mostly informational and we can ignore them; note that + * although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and + * MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this + * enum, they are used with different ext1 codes. /o\ + */ + int trackNum = -1; // no track + + switch (msg) { + // Recorder-related events + case MEDIA_RECORDER_EVENT_INFO: + switch (ext1) { + case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: + DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n"); + OnRecorderStateChange(NS_LITERAL_STRING("FileSizeLimitReached"), ext2, trackNum); + return; + + case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: + DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n"); + OnRecorderStateChange(NS_LITERAL_STRING("VideoLengthLimitReached"), ext2, trackNum); + return; + + case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS: + DOM_CAMERA_LOGI("recorder-event : info: track completed\n"); + OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum); + return; + } + break; + + case MEDIA_RECORDER_EVENT_ERROR: + switch (ext1) { + case MEDIA_RECORDER_ERROR_UNKNOWN: + DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2, ext2); + OnRecorderStateChange(NS_LITERAL_STRING("MediaRecorderFailed"), ext2, trackNum); + return; + + case MEDIA_ERROR_SERVER_DIED: + DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n"); + OnRecorderStateChange(NS_LITERAL_STRING("MediaServerFailed"), ext2, trackNum); + return; + } + break; + + // Track-related events, see note 1(a) above. + case MEDIA_RECORDER_TRACK_EVENT_INFO: + trackNum = (ext1 & 0xF0000000) >> 28; + ext1 &= 0xFFFF; + switch (ext1) { + case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS: + if (ext2 == OK) { + DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); + OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum); + return; + } + DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); + OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum); + return; + + case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME: + DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2); + return; + } + break; + + case MEDIA_RECORDER_TRACK_EVENT_ERROR: + trackNum = (ext1 & 0xF0000000) >> 28; + ext1 &= 0xFFFF; + DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); + OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum); + return; + } + + // All unhandled cases wind up here + DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2); +} + nsresult -nsGonkCameraControl::SetupRecording(int aFd, int aMaxFileSizeBytes, int aMaxVideoLengthMs) +nsGonkCameraControl::SetupRecording(int aFd, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs) { // choosing a size big enough to hold the params const size_t SIZE = 256; @@ -929,15 +1076,25 @@ nsGonkCameraControl::SetupRecording(int aFd, int aMaxFileSizeBytes, int aMaxVide CHECK_SETARG(mRecorder->setCameraHandle((int32_t)mHwHandle)); - snprintf(buffer, SIZE, "max-duration=%d", aMaxVideoLengthMs); + DOM_CAMERA_LOGI("maxVideoLengthMs=%lld\n", aMaxVideoLengthMs); + if (aMaxVideoLengthMs == 0) { + aMaxVideoLengthMs = -1; + } + snprintf(buffer, SIZE, "max-duration=%lld", aMaxVideoLengthMs); CHECK_SETARG(mRecorder->setParameters(String8(buffer))); - snprintf(buffer, SIZE, "max-duration=%d", aMaxFileSizeBytes); + DOM_CAMERA_LOGI("maxFileSizeBytes=%lld\n", aMaxFileSizeBytes); + if (aMaxFileSizeBytes == 0) { + aMaxFileSizeBytes = -1; + } + snprintf(buffer, SIZE, "max-filesize=%lld", aMaxFileSizeBytes); CHECK_SETARG(mRecorder->setParameters(String8(buffer))); snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", mVideoRotation); CHECK_SETARG(mRecorder->setParameters(String8(buffer))); + CHECK_SETARG(mRecorder->setListener(new GonkRecorderListener(this))); + // recording API needs file descriptor of output file CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0)); CHECK_SETARG(mRecorder->prepare()); diff --git a/dom/camera/GonkCameraControl.h b/dom/camera/GonkCameraControl.h index 0a0dd46fe885..cf072836fc7a 100644 --- a/dom/camera/GonkCameraControl.h +++ b/dom/camera/GonkCameraControl.h @@ -54,11 +54,12 @@ public: nsresult GetVideoSizes(nsTArray& aVideoSizes); nsresult PushParameters(); - nsresult SetupRecording(int aFd, int aMaxFileSizeBytes = -1, int aMaxVideoLengthMs = -1); + nsresult SetupRecording(int aFd, int64_t aMaxFileSizeBytes = -1, int64_t aMaxVideoLengthMs = -1); nsresult SetupVideoMode(const nsAString& aProfile); void AutoFocusComplete(bool aSuccess); void TakePictureComplete(uint8_t* aData, uint32_t aLength); + void HandleRecorderEvent(int msg, int ext1, int ext2); protected: ~nsGonkCameraControl(); diff --git a/dom/camera/ICameraControl.h b/dom/camera/ICameraControl.h index 77fcd6fdd8f3..5d7fc6b816b6 100644 --- a/dom/camera/ICameraControl.h +++ b/dom/camera/ICameraControl.h @@ -42,6 +42,8 @@ public: virtual nsresult Get(nsICameraShutterCallback** aOnShutter) = 0; virtual nsresult Set(nsICameraClosedCallback* aOnClosed) = 0; virtual nsresult Get(nsICameraClosedCallback** aOnClosed) = 0; + virtual nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange) = 0; + virtual nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange) = 0; virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0; virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0; virtual nsresult GetVideoSizes(nsTArray& aVideoSizes) = 0; diff --git a/dom/camera/nsIDOMCameraManager.idl b/dom/camera/nsIDOMCameraManager.idl index 1af7aaa6f552..5e2b3de5dfed 100644 --- a/dom/camera/nsIDOMCameraManager.idl +++ b/dom/camera/nsIDOMCameraManager.idl @@ -195,8 +195,8 @@ dictionary CameraRecorderOptions dictionary CameraStartRecordingOptions { long rotation; - long maxFileSizeBytes; - long maxVideoLengthMs; + long long maxFileSizeBytes; + long long maxVideoLengthMs; }; [scriptable, function, uuid(0444a687-4bc9-462c-8246-5423f0fe46a4)] @@ -235,6 +235,12 @@ interface nsICameraClosedCallback : nsISupports void handleEvent(); }; +[scriptable, function, uuid(550d675a-257d-4713-8b3d-0da53eba68fc)] +interface nsICameraRecorderStateChange : nsISupports +{ + void handleStateChange(in DOMString newState); +}; + [scriptable, function, uuid(a302c6c9-3776-4d1d-a395-f4105d47c3d3)] interface nsICameraErrorCallback : nsISupports { @@ -245,7 +251,7 @@ interface nsICameraErrorCallback : nsISupports attributes here affect the preview, any pictures taken, and/or any video recorded by the camera. */ -[scriptable, uuid(0f206acd-196b-4bdf-8198-44c1a0cd1998)] +[scriptable, uuid(70f45209-b69b-4937-bbac-57d82600e2af)] interface nsICameraControl : nsISupports { readonly attribute nsICameraCapabilities capabilities; @@ -341,6 +347,11 @@ interface nsICameraControl : nsISupports recent call to get the camera. */ attribute nsICameraClosedCallback onClosed; + /* the function to call when the recorder changes state, either because + the recording process encountered an error, or because one of the + recording limits (see CameraStartRecordingOptions) was reached. */ + attribute nsICameraRecorderStateChange onRecorderStateChange; + /* tell the camera to attempt to focus the image */ void autoFocus(in nsICameraAutoFocusCallback onSuccess, [optional] in nsICameraErrorCallback onError); From 0365b4fe285ecbc5f37f79c1d79901cd6f01eeca Mon Sep 17 00:00:00 2001 From: Shelly Lin Date: Fri, 26 Oct 2012 10:17:36 +0800 Subject: [PATCH 77/83] Bug 803039 - Use TimeStamp instead of PRTime to store the idle time. r=jlebar --- widget/xpwidgets/nsIdleService.cpp | 88 ++++++++++++++---------------- widget/xpwidgets/nsIdleService.h | 9 +-- 2 files changed, 47 insertions(+), 50 deletions(-) diff --git a/widget/xpwidgets/nsIdleService.cpp b/widget/xpwidgets/nsIdleService.cpp index 891fd16e2f7a..a723a03d19d1 100644 --- a/widget/xpwidgets/nsIdleService.cpp +++ b/widget/xpwidgets/nsIdleService.cpp @@ -381,10 +381,10 @@ nsIdleService::GetInstance() return instance.forget(); } -nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAtInPR(0), +nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAt(TimeStamp()), mAnyObserverIdle(false), mDeltaToNextIdleSwitchInS(UINT32_MAX), - mLastUserInteractionInPR(PR_Now()) + mLastUserInteraction(TimeStamp::Now()) { #ifdef PR_LOGGING if (sLog == NULL) @@ -507,7 +507,8 @@ nsIdleService::ResetIdleTimeOut(uint32_t idleDeltaInMS) idleDeltaInMS)); // Store the time - mLastUserInteractionInPR = PR_Now() - (idleDeltaInMS * PR_USEC_PER_MSEC); + mLastUserInteraction = TimeStamp::Now() - + TimeDuration::FromMilliseconds(idleDeltaInMS); // If no one is idle, then we are done, any existing timers can keep running. if (!mAnyObserverIdle) { @@ -594,8 +595,8 @@ nsIdleService::GetIdleTime(uint32_t* idleTime) polledIdleTimeMS, polledIdleTimeIsValid)); // timeSinceReset is in milliseconds. - uint32_t timeSinceResetInMS = (PR_Now() - mLastUserInteractionInPR) / - PR_USEC_PER_MSEC; + TimeDuration timeSinceReset = TimeStamp::Now() - mLastUserInteraction; + uint32_t timeSinceResetInMS = timeSinceReset.ToMilliseconds(); PR_LOG(sLog, PR_LOG_DEBUG, ("idleService: Get idle time: time since reset %u msec", @@ -644,7 +645,7 @@ void nsIdleService::IdleTimerCallback(void) { // Remember that we no longer have a timer running. - mCurrentlySetToTimeoutAtInPR = 0; + mCurrentlySetToTimeoutAt = TimeStamp(); // Get the current idle time. uint32_t currentIdleTimeInMS; @@ -672,8 +673,8 @@ nsIdleService::IdleTimerCallback(void) // we do the calculation in ms to lessen the chance for rounding errors to // trigger wrong results, it is also very important that we call PR_Now AFTER // the call to GetIdleTime(). - if (((PR_Now() - mLastUserInteractionInPR) / PR_USEC_PER_MSEC) > - currentIdleTimeInMS) + TimeDuration aIdleTime = TimeStamp::Now() - mLastUserInteraction; + if (aIdleTime.ToMilliseconds() > currentIdleTimeInMS) { // We had user activity, so handle that part first (to ensure the listeners // don't risk getting an non-idle after they get a new idle indication. @@ -759,15 +760,16 @@ nsIdleService::IdleTimerCallback(void) } void -nsIdleService::SetTimerExpiryIfBefore(PRTime aNextTimeoutInPR) +nsIdleService::SetTimerExpiryIfBefore(TimeStamp aNextTimeout) { + TimeDuration nextTimeoutDuration = aNextTimeout - TimeStamp::Now(); PR_LOG(sLog, PR_LOG_DEBUG, - ("idleService: SetTimerExpiryIfBefore: next timeout %lld usec", - aNextTimeoutInPR)); + ("idleService: SetTimerExpiryIfBefore: next timeout %0.f msec from now", + nextTimeoutDuration.ToMilliseconds())); #ifdef ANDROID __android_log_print(ANDROID_LOG_INFO, "IdleService", - "SetTimerExpiryIfBefore: next timeout %lld usec", - aNextTimeoutInPR); + "SetTimerExpiryIfBefore: next timeout %0.f msec from now", + nextTimeoutDuration.ToMilliseconds()); #endif // Bail if we don't have a timer service. @@ -777,41 +779,37 @@ nsIdleService::SetTimerExpiryIfBefore(PRTime aNextTimeoutInPR) // If the new timeout is before the old one or we don't have a timer running, // then restart the timer. - if (mCurrentlySetToTimeoutAtInPR > aNextTimeoutInPR || - !mCurrentlySetToTimeoutAtInPR) { + if (mCurrentlySetToTimeoutAt.IsNull() || + mCurrentlySetToTimeoutAt > aNextTimeout) { -#if defined(PR_LOGGING) || defined(ANDROID) - PRTime oldTimeout = mCurrentlySetToTimeoutAtInPR; -#endif - - mCurrentlySetToTimeoutAtInPR = aNextTimeoutInPR ; + mCurrentlySetToTimeoutAt = aNextTimeout; // Stop the current timer (it's ok to try'n stop it, even it isn't running). mTimer->Cancel(); // Check that the timeout is actually in the future, otherwise make it so. - PRTime currentTimeInPR = PR_Now(); - if (currentTimeInPR > mCurrentlySetToTimeoutAtInPR) { - mCurrentlySetToTimeoutAtInPR = currentTimeInPR; + TimeStamp currentTime = TimeStamp::Now(); + if (currentTime > mCurrentlySetToTimeoutAt) { + mCurrentlySetToTimeoutAt = currentTime; } // Add 10 ms to ensure we don't undershoot, and never get a "0" timer. - mCurrentlySetToTimeoutAtInPR += 10 * PR_USEC_PER_MSEC; + mCurrentlySetToTimeoutAt += TimeDuration::FromMilliseconds(10); + TimeDuration deltaTime = mCurrentlySetToTimeoutAt - currentTime; PR_LOG(sLog, PR_LOG_DEBUG, - ("idleService: reset timer expiry from %lld usec to %lld usec", - oldTimeout, mCurrentlySetToTimeoutAtInPR)); + ("idleService: IdleService", "reset timer expiry to %0.f msec from now", + deltaTime.ToMilliseconds())); #ifdef ANDROID - __android_log_print(ANDROID_LOG_INFO, "IdleService", - "reset timer expiry from %lld usec to %lld usec", - oldTimeout, mCurrentlySetToTimeoutAtInPR); + __android_log_print(ANDROID_LOG_INFO, "IdleService", + "reset timer expiry to %0.f msec from now", + deltaTime.ToMilliseconds()); #endif // Start the timer mTimer->InitWithFuncCallback(StaticIdleTimerCallback, this, - (mCurrentlySetToTimeoutAtInPR - - currentTimeInPR) / PR_USEC_PER_MSEC, + deltaTime.ToMilliseconds(), nsITimer::TYPE_ONE_SHOT); } @@ -838,28 +836,26 @@ nsIdleService::ReconfigureTimer(void) // We need to store the current time, so we don't get artifacts from the time // ticking while we are processing. - PRTime curTimeInPR = PR_Now(); + TimeStamp curTime = TimeStamp::Now(); - PRTime nextTimeoutAtInPR = mLastUserInteractionInPR + - (((PRTime)mDeltaToNextIdleSwitchInS) * - PR_USEC_PER_SEC); + TimeStamp nextTimeoutAt = mLastUserInteraction + + TimeDuration::FromSeconds(mDeltaToNextIdleSwitchInS); + TimeDuration nextTimeoutDuration = nextTimeoutAt - curTime; PR_LOG(sLog, PR_LOG_DEBUG, - ("idleService: next timeout %lld usec (%u msec from now)", - nextTimeoutAtInPR, - (uint32_t)((nextTimeoutAtInPR - curTimeInPR) / PR_USEC_PER_MSEC))); + ("idleService: next timeout %0.f msec from now", + nextTimeoutDuration.ToMilliseconds())); #ifdef ANDROID __android_log_print(ANDROID_LOG_INFO, "IdleService", - "next timeout %lld usec (%lld msec from now)", - nextTimeoutAtInPR, - ((nextTimeoutAtInPR - curTimeInPR) / PR_USEC_PER_MSEC)); + "next timeout %0.f msec from now", + nextTimeoutDuration.ToMilliseconds()); #endif // Check if we should correct the timeout time because we should poll before. if (mAnyObserverIdle && UsePollMode()) { - PRTime pollTimeout = curTimeInPR + - MIN_IDLE_POLL_INTERVAL_MSEC * PR_USEC_PER_MSEC; + TimeStamp pollTimeout = + curTime + TimeDuration::FromMilliseconds(MIN_IDLE_POLL_INTERVAL_MSEC); - if (nextTimeoutAtInPR > pollTimeout) { + if (nextTimeoutAt > pollTimeout) { PR_LOG(sLog, PR_LOG_DEBUG, ("idleService: idle observers, reducing timeout to %u msec from now", MIN_IDLE_POLL_INTERVAL_MSEC)); @@ -868,10 +864,10 @@ nsIdleService::ReconfigureTimer(void) "idle observers, reducing timeout to %u msec from now", MIN_IDLE_POLL_INTERVAL_MSEC); #endif - nextTimeoutAtInPR = pollTimeout; + nextTimeoutAt = pollTimeout; } } - SetTimerExpiryIfBefore(nextTimeoutAtInPR); + SetTimerExpiryIfBefore(nextTimeoutAt); } diff --git a/widget/xpwidgets/nsIdleService.h b/widget/xpwidgets/nsIdleService.h index ae3d99ac675b..aeacaa2d3960 100644 --- a/widget/xpwidgets/nsIdleService.h +++ b/widget/xpwidgets/nsIdleService.h @@ -16,6 +16,7 @@ #include "nsIIdleService.h" #include "nsCategoryCache.h" #include "nsWeakReference.h" +#include "mozilla/TimeStamp.h" /** * Class we can use to store an observer with its associated idle time @@ -155,15 +156,15 @@ private: * * The function might not restart the timer if there is one running currently * - * @param aNextTimeoutInPR + * @param aNextTimeout * The last absolute time the timer should expire */ - void SetTimerExpiryIfBefore(PRTime aNextTimeoutInPR); + void SetTimerExpiryIfBefore(mozilla::TimeStamp aNextTimeout); /** * Stores the next timeout time, 0 means timer not running */ - PRTime mCurrentlySetToTimeoutAtInPR; + mozilla::TimeStamp mCurrentlySetToTimeoutAt; /** * mTimer holds the internal timer used by this class to detect when to poll @@ -199,7 +200,7 @@ private: /** * Absolute value for when the last user interaction took place. */ - PRTime mLastUserInteractionInPR; + mozilla::TimeStamp mLastUserInteraction; /** From b19777fbd25fa6d17d7221c5578dcfc9ca657736 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Fri, 2 Nov 2012 16:11:51 -0400 Subject: [PATCH 78/83] Bug 806139 - Add more supported h264 codecs, used by youtube for 1080p/720p and 240p. r=doublec --- content/html/content/public/nsHTMLMediaElement.h | 6 ++++-- content/html/content/src/nsHTMLMediaElement.cpp | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h index 9681e973c91f..028be6bf1b35 100644 --- a/content/html/content/public/nsHTMLMediaElement.h +++ b/content/html/content/public/nsHTMLMediaElement.h @@ -312,14 +312,16 @@ public: static bool IsGStreamerSupportedType(const nsACString& aType); static bool IsH264Type(const nsACString& aType); static const char gH264Types[3][16]; - static char const *const gH264Codecs[7]; #endif #ifdef MOZ_WIDGET_GONK static bool IsOmxEnabled(); static bool IsOmxSupportedType(const nsACString& aType); static const char gOmxTypes[5][16]; - static char const *const gH264Codecs[7]; +#endif + +#if defined(MOZ_GSTREAMER) || defined(MOZ_WIDGET_GONK) + static char const *const gH264Codecs[9]; #endif #ifdef MOZ_MEDIA_PLUGINS diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index 46d9c11af917..a8db13231c8f 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -2134,12 +2134,14 @@ nsHTMLMediaElement::IsWebMType(const nsACString& aType) #endif #if defined(MOZ_GSTREAMER) || defined(MOZ_WIDGET_GONK) -char const *const nsHTMLMediaElement::gH264Codecs[7] = { +char const *const nsHTMLMediaElement::gH264Codecs[9] = { "avc1.42E01E", // H.264 Constrained Baseline Profile Level 3.0 "avc1.42001E", // H.264 Baseline Profile Level 3.0 "avc1.58A01E", // H.264 Extended Profile Level 3.0 "avc1.4D401E", // H.264 Main Profile Level 3.0 "avc1.64001E", // H.264 High Profile Level 3.0 + "avc1.64001F", // H.264 High Profile Level 3.1 + "mp4v.20.3", // 3GPP "mp4a.40.2", // AAC-LC nullptr }; From b2c918dae81e8fe1dce44c5518738ce31e1b1de9 Mon Sep 17 00:00:00 2001 From: Jed Parsons Date: Fri, 2 Nov 2012 16:11:51 -0400 Subject: [PATCH 79/83] Bug 807078 - Clean up message listeners before closing window. r=benadida --- b2g/chrome/content/identity.js | 28 +++++++++++++--------------- b2g/components/SignInToWebsite.jsm | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/b2g/chrome/content/identity.js b/b2g/chrome/content/identity.js index 062f79cfc125..ce1295e58bf5 100644 --- a/b2g/chrome/content/identity.js +++ b/b2g/chrome/content/identity.js @@ -59,20 +59,13 @@ function identityCall(message) { sendAsyncMessage(kIdentityControllerDoMethod, message); } -function identityFinished() { - log("identity finished. closing dialog"); - closeIdentityDialog(function notifySuccess() { - // get ready for next call with a reinit - func = null; options = null; - - sendAsyncMessage(kIdentityDelegateFinished); - }); -} - /* - * Notify the UI to close the dialog and return to the caller application + * To close the dialog, we first tell the gecko SignInToWebsite manager that it + * can clean up. Then we tell the gaia component that we are finished. It is + * necessary to notify gecko first, so that the message can be sent before gaia + * destroys our context. */ -function closeIdentityDialog(aCallback) { +function closeIdentityDialog() { let randomId = uuidgen.generateUUID().toString(); let id = kReceivedIdentityAssertion + "-" + randomId; let browser = Services.wm.getMostRecentWindow("navigator:browser"); @@ -94,6 +87,11 @@ function closeIdentityDialog(aCallback) { } }); + // tell gecko we're done. fire and forget. + func = null; options = null; + sendAsyncMessage(kIdentityDelegateFinished); + + // tell gaia to shut us down browser.shell.sendChromeEvent(detail); } @@ -111,7 +109,7 @@ function doInternalWatch() { identityCall(aParams); if (aParams.method === "ready") { log("watch finished."); - identityFinished(); + closeIdentityDialog(); } }, JSON.stringify({loggedInUser: options.loggedInUser, origin: options.origin}), @@ -132,7 +130,7 @@ function doInternalRequest() { log("request -> assertion, so do login"); identityCall({method:'login',assertion:assertion}); } - identityFinished(); + closeIdentityDialog(); }, options); } @@ -145,7 +143,7 @@ function doInternalLogout(aOptions) { log("logging you out of ", options.origin); BrowserID.internal.logout(options.origin, function() { identityCall({method:'logout'}); - identityFinished(); + closeIdentityDialog(); }); } } diff --git a/b2g/components/SignInToWebsite.jsm b/b2g/components/SignInToWebsite.jsm index a3cd8127a89f..5d4ebfd6da4a 100644 --- a/b2g/components/SignInToWebsite.jsm +++ b/b2g/components/SignInToWebsite.jsm @@ -289,11 +289,11 @@ let SignInToWebsiteController = { */ _makeDoMethodCallback: function SignInToWebsiteController__makeDoMethodCallback(aRpId) { return function SignInToWebsiteController_methodCallback(aOptions) { - log("doMethod:", aOptions); let message = aOptions.json; if (typeof message === 'string') { message = JSON.parse(message); } + log("doMethod:", message.method); switch(message.method) { case "ready": IdentityService.doReady(aRpId); From 4124a8c7e6e01905a5986155bbf92e3bbbaf1f9f Mon Sep 17 00:00:00 2001 From: Kyle Machulis Date: Fri, 2 Nov 2012 13:16:45 -0700 Subject: [PATCH 80/83] Bug 791268: Make dylib symbol binding retry, let bluetooth firmware shutdown still work even on error; r=echou --HG-- extra : rebase_source : f17ca1a8d3fe84a73cab3c4e0db56ee4f15fe0d1 --- dom/bluetooth/gonk/BluetoothGonkService.cpp | 59 ++++++++------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/dom/bluetooth/gonk/BluetoothGonkService.cpp b/dom/bluetooth/gonk/BluetoothGonkService.cpp index dc2e8e4bc6e5..1519431b8187 100644 --- a/dom/bluetooth/gonk/BluetoothGonkService.cpp +++ b/dom/bluetooth/gonk/BluetoothGonkService.cpp @@ -29,11 +29,9 @@ USING_BLUETOOTH_NAMESPACE static struct BluedroidFunctions { bool initialized; - bool tried_initialization; BluedroidFunctions() : - initialized(false), - tried_initialization(false) + initialized(false) { } @@ -42,17 +40,13 @@ static struct BluedroidFunctions int (* bt_is_enabled)(); } sBluedroidFunctions; -bool +static bool EnsureBluetoothInit() { - if (sBluedroidFunctions.tried_initialization) - { - return sBluedroidFunctions.initialized; + if (sBluedroidFunctions.initialized) { + return true; } - sBluedroidFunctions.initialized = false; - sBluedroidFunctions.tried_initialization = true; - void* handle = dlopen("libbluedroid.so", RTLD_LAZY); if (!handle) { @@ -75,29 +69,12 @@ EnsureBluetoothInit() NS_ERROR("Failed to attach bt_is_enabled function"); return false; } + sBluedroidFunctions.initialized = true; return true; } -int -IsBluetoothEnabled() -{ - return sBluedroidFunctions.bt_is_enabled(); -} - -int -EnableBluetooth() -{ - return sBluedroidFunctions.bt_enable(); -} - -int -DisableBluetooth() -{ - return sBluedroidFunctions.bt_disable(); -} - -nsresult +static nsresult StartStopGonkBluetooth(bool aShouldEnable) { bool result; @@ -111,16 +88,26 @@ StartStopGonkBluetooth(bool aShouldEnable) } // return 1 if it's enabled, 0 if it's disabled, and -1 on error - int isEnabled = IsBluetoothEnabled(); + int isEnabled = sBluedroidFunctions.bt_is_enabled(); if ((isEnabled == 1 && aShouldEnable) || (isEnabled == 0 && !aShouldEnable)) { - result = true; - } else if (isEnabled < 0) { - result = false; - } else if (aShouldEnable) { - result = (EnableBluetooth() == 0) ? true : false; + return NS_OK; + } + if (aShouldEnable) { + result = (sBluedroidFunctions.bt_enable() == 0) ? true : false; + if (sBluedroidFunctions.bt_is_enabled() < 0) { + // if isEnabled < 0, this means we brought up the firmware, but something + // went wrong with bluetoothd. Post a warning message, but try to proceed + // with firmware unloading if that was requested, so we can retry later. + NS_WARNING("Bluetooth firmware up, but cannot connect to HCI socket! Check bluetoothd and try stopping/starting bluetooth again."); + // Just disable now, return an error. + if (sBluedroidFunctions.bt_disable() != 0) { + NS_WARNING("Problem shutting down bluetooth after error in bringup!"); + } + return NS_ERROR_FAILURE; + } } else { - result = (DisableBluetooth() == 0) ? true : false; + result = (sBluedroidFunctions.bt_disable() == 0) ? true : false; } if (!result) { NS_WARNING("Could not set gonk bluetooth firmware!"); From 9656961ce4caec10e33a30cf45a149fe214f66d4 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Fri, 2 Nov 2012 11:19:33 -0400 Subject: [PATCH 81/83] Bug 701613 - Add Win7 as supported OS in updater.exe.manifest. r= rstrong --- toolkit/mozapps/update/updater/updater.exe.manifest | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/toolkit/mozapps/update/updater/updater.exe.manifest b/toolkit/mozapps/update/updater/updater.exe.manifest index e242e49df462..4825ed1ff360 100644 --- a/toolkit/mozapps/update/updater/updater.exe.manifest +++ b/toolkit/mozapps/update/updater/updater.exe.manifest @@ -28,7 +28,10 @@ - + + + + From f1d654c74aaec220d623d2bcd8ec23ade6ebb17a Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 2 Nov 2012 17:12:22 -0400 Subject: [PATCH 82/83] Backout 06b998c1100d (bug 664695) due to mochitest-other orange. --- .../browser/obsolete/browser_console_clear.js | 4 +++- .../tests/chrome/templates_shared.js | 5 +++-- .../tests/chrome/test_tmpl_errors.xul | 5 +++-- mobile/xul/chrome/content/console.js | 4 +++- .../console/content/consoleBindings.xml | 4 +++- .../test/xml/rfc4287/author_namespaces.xml | 2 +- .../devtools/webconsole/WebConsoleUtils.jsm | 19 ++++++++++++++----- toolkit/xre/nsConsoleWriter.cpp | 2 +- xpcom/base/nsConsoleService.cpp | 2 +- xpcom/base/nsIConsoleService.idl | 6 +++--- 10 files changed, 35 insertions(+), 18 deletions(-) diff --git a/browser/components/privatebrowsing/test/browser/obsolete/browser_console_clear.js b/browser/components/privatebrowsing/test/browser/obsolete/browser_console_clear.js index a4953395e343..5b60b73c2b5a 100644 --- a/browser/components/privatebrowsing/test/browser/obsolete/browser_console_clear.js +++ b/browser/components/privatebrowsing/test/browser/obsolete/browser_console_clear.js @@ -35,7 +35,9 @@ function test() { consoleService.registerListener(consoleObserver); function messageExists() { - let messages = consoleService.getMessageArray() || []; + let out = {}; + consoleService.getMessageArray(out, {}); + let messages = out.value || []; for (let i = 0; i < messages.length; ++i) { if (messages[i].message == TEST_MESSAGE) return true; diff --git a/content/xul/templates/tests/chrome/templates_shared.js b/content/xul/templates/tests/chrome/templates_shared.js index c46612c7a556..267f11780146 100644 --- a/content/xul/templates/tests/chrome/templates_shared.js +++ b/content/xul/templates/tests/chrome/templates_shared.js @@ -434,8 +434,9 @@ function compareConsoleMessages() { var consoleService = Components.classes["@mozilla.org/consoleservice;1"]. getService(Components.interfaces.nsIConsoleService); - var messages = consoleService.getMessageArray() || []; - messages = messages.map(function (m) m.message); + var out = {}; + consoleService.getMessageArray(out, {}); + var messages = (out.value || []).map(function (m) m.message); // Copy to avoid modifying expectedConsoleMessages var expect = expectedConsoleMessages.concat(); for (var m = 0; m < messages.length; m++) { diff --git a/content/xul/templates/tests/chrome/test_tmpl_errors.xul b/content/xul/templates/tests/chrome/test_tmpl_errors.xul index e6852210fbdc..704dcc8fb956 100644 --- a/content/xul/templates/tests/chrome/test_tmpl_errors.xul +++ b/content/xul/templates/tests/chrome/test_tmpl_errors.xul @@ -23,8 +23,9 @@ var consoleService = Components.classes["@mozilla.org/consoleservice;1"]. function checkConsole(expectedError) { - var message = consoleService.getMessageArray()[0].message; - is(message, expectedError, "logged message " + expectedError); + var out = {}; + consoleService.getMessageArray(out, {}); + is(out.value[0].message, expectedError, "logged message " + expectedError); } // each test consists of a pre function executed before the template build, an diff --git a/mobile/xul/chrome/content/console.js b/mobile/xul/chrome/content/console.js index af4cd1c9d3a4..e6f1ad99ea36 100644 --- a/mobile/xul/chrome/content/console.js +++ b/mobile/xul/chrome/content/console.js @@ -192,7 +192,9 @@ let ConsoleView = { }, appendInitialItems: function cv_appendInitialItems() { - let messages = Services.console.getMessageArray(); + let out = {}; // Throwaway references to support 'out' parameters. + Services.console.getMessageArray(out, {}); + let messages = out.value; // In case getMessageArray returns 0-length array as null if (!messages) diff --git a/toolkit/components/console/content/consoleBindings.xml b/toolkit/components/console/content/consoleBindings.xml index 3d30af416ec8..5eb44e0977f1 100644 --- a/toolkit/components/console/content/consoleBindings.xml +++ b/toolkit/components/console/content/consoleBindings.xml @@ -130,7 +130,9 @@ diff --git a/toolkit/devtools/webconsole/WebConsoleUtils.jsm b/toolkit/devtools/webconsole/WebConsoleUtils.jsm index abda0667e79c..b4abc490a4a2 100644 --- a/toolkit/devtools/webconsole/WebConsoleUtils.jsm +++ b/toolkit/devtools/webconsole/WebConsoleUtils.jsm @@ -1326,13 +1326,22 @@ PageErrorListener.prototype = { let innerWindowId = this.window ? WebConsoleUtils.getInnerWindowId(this.window) : null; - let errors = Services.console.getMessageArray() || []; + let result = []; + let errors = {}; + Services.console.getMessageArray(errors, {}); - return errors.filter(function(aError) { - return aError instanceof Ci.nsIScriptError && - (!innerWindowId || aError.innerWindowID == innerWindowId) && - this.isCategoryAllowed(aError.category); + (errors.value || []).forEach(function(aError) { + if (!(aError instanceof Ci.nsIScriptError) || + (innerWindowId && + (aError.innerWindowID != innerWindowId || + !this.isCategoryAllowed(aError.category)))) { + return; + } + + result.push(aError); }, this); + + return result; }, /** diff --git a/toolkit/xre/nsConsoleWriter.cpp b/toolkit/xre/nsConsoleWriter.cpp index b747dcd6952f..89fc5a192e94 100644 --- a/toolkit/xre/nsConsoleWriter.cpp +++ b/toolkit/xre/nsConsoleWriter.cpp @@ -58,7 +58,7 @@ WriteConsoleLog() nsIConsoleMessage** messages; uint32_t mcount; - rv = csrv->GetMessageArray(&mcount, &messages); + rv = csrv->GetMessageArray(&messages, &mcount); if (NS_FAILED(rv)) { PR_Close(file); return; diff --git a/xpcom/base/nsConsoleService.cpp b/xpcom/base/nsConsoleService.cpp index 137cb9ef96e8..dc868eb9219e 100644 --- a/xpcom/base/nsConsoleService.cpp +++ b/xpcom/base/nsConsoleService.cpp @@ -208,7 +208,7 @@ nsConsoleService::LogStringMessage(const PRUnichar *message) } NS_IMETHODIMP -nsConsoleService::GetMessageArray(uint32_t *count, nsIConsoleMessage ***messages) +nsConsoleService::GetMessageArray(nsIConsoleMessage ***messages, uint32_t *count) { nsIConsoleMessage **messageArray; diff --git a/xpcom/base/nsIConsoleService.idl b/xpcom/base/nsIConsoleService.idl index 71d63a7883ba..cc26eb5b6a3f 100644 --- a/xpcom/base/nsIConsoleService.idl +++ b/xpcom/base/nsIConsoleService.idl @@ -7,7 +7,7 @@ #include "nsIConsoleListener.idl" #include "nsIConsoleMessage.idl" -[scriptable, uuid(0eb81d20-c37e-42d4-82a8-ca9ae96bdf52)] +[scriptable, uuid(883472a0-ea9b-11da-8ad9-0800200c9a66)] interface nsIConsoleService : nsISupports { void logMessage(in nsIConsoleMessage message); @@ -23,8 +23,8 @@ interface nsIConsoleService : nsISupports * will allocate one word for messages, so as to show up as a * 0-length array when called from script. */ - void getMessageArray([optional] out uint32_t count, - [retval, array, size_is(count)] out nsIConsoleMessage messages); + void getMessageArray([array, size_is(count)] out nsIConsoleMessage messages, + out uint32_t count); /** * To guard against stack overflows from listeners that could log From b880ee6c47c57b92aeb128066a309394d6b0e44e Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Fri, 2 Nov 2012 14:31:22 -0700 Subject: [PATCH 83/83] Bug 807631 - Receiving multi-part SMS doesn't work. r=philikon. --- dom/system/gonk/ril_worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index 8bdfa6dae47c..d3da5a855d0e 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -3866,7 +3866,7 @@ let RIL = { this.sendDOMMessage(message); } - if (message.messageClass == GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_2]) { + if (message && message.messageClass == GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_2]) { // `MS shall ensure that the message has been to the SMS data field in // the (U)SIM before sending an ACK to the SC.` ~ 3GPP 23.038 clause 4 return PDU_FCS_RESERVED;