From bb8609bee72b07529ed5b1ef59da3ac21b2c2659 Mon Sep 17 00:00:00 2001 From: Cykesiopka Date: Mon, 25 Feb 2013 09:07:15 -0500 Subject: [PATCH 01/64] Bug 326317 - Fix issues with comments in nsTSubstring.h. r=dbaron --- xpcom/string/public/nsTSubstring.h | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/xpcom/string/public/nsTSubstring.h b/xpcom/string/public/nsTSubstring.h index e63388a6bca..28ca1fa5af9 100644 --- a/xpcom/string/public/nsTSubstring.h +++ b/xpcom/string/public/nsTSubstring.h @@ -275,9 +275,10 @@ class nsTSubstring_CharT */ bool NS_FASTCALL EqualsASCII( const char* data ) const; - // EqualsLiteral must ONLY be applied to an actual literal string. - // Do not attempt to use it with a regular char* pointer, or with a char - // array variable. + // EqualsLiteral must ONLY be applied to an actual literal string, or + // a char array *constant* declared without an explicit size. + // Do not attempt to use it with a regular char* pointer, or with a + // non-constant char array variable. Use EqualsASCII for them. // The template trick to acquire the array length at compile time without // using a macro is due to Corey Kosak, with much thanks. #ifdef NS_DISABLE_LITERAL_TEMPLATE @@ -309,8 +310,9 @@ class nsTSubstring_CharT bool NS_FASTCALL LowerCaseEqualsASCII( const char* data ) const; // LowerCaseEqualsLiteral must ONLY be applied to an actual - // literal string. Do not attempt to use it with a regular char* - // pointer, or with a char array variable. Use + // literal string, or a char array *constant* declared without an + // explicit size. Do not attempt to use it with a regular char* + // pointer, or with a non-constant char array variable. Use // LowerCaseEqualsASCII for them. #ifdef NS_DISABLE_LITERAL_TEMPLATE inline bool LowerCaseEqualsLiteral( const char* str ) const @@ -360,9 +362,10 @@ class nsTSubstring_CharT return AssignASCII(data, strlen(data), fallible_t()); } - // AssignLiteral must ONLY be applied to an actual literal string. - // Do not attempt to use it with a regular char* pointer, or with a char - // array variable. Use AssignASCII for those. + // AssignLiteral must ONLY be applied to an actual literal string, or + // a char array *constant* declared without an explicit size. + // Do not attempt to use it with a regular char* pointer, or with a + // non-constant char array variable. Use AssignASCII for those. // There are not fallible version of these methods because they only really // apply to small allocations that we wouldn't want to check anyway. #ifdef NS_DISABLE_LITERAL_TEMPLATE @@ -482,8 +485,11 @@ class nsTSubstring_CharT */ /** - * Attempts to set the capacity to the given size, without affecting - * the length of the string. Also ensures that the buffer is mutable. + * Attempts to set the capacity to the given size in number of + * characters, without affecting the length of the string. + * There is no need to include room for the null terminator: it is + * the job of the string class. + * Also ensures that the buffer is mutable. */ void NS_FASTCALL SetCapacity( size_type newCapacity ); bool NS_FASTCALL SetCapacity( size_type newCapacity, const fallible_t& ) NS_WARN_UNUSED_RESULT; From a034506aa9f4996cd14f6ccaa359c971ab2c9cc9 Mon Sep 17 00:00:00 2001 From: Andrew Quartey Date: Mon, 25 Feb 2013 09:07:16 -0500 Subject: [PATCH 02/64] Bug 715767 - Make LayerManagerD3D10::ReportFailure threadsafe. r=joe --- widget/xpwidgets/GfxInfoBase.cpp | 11 +++++++---- widget/xpwidgets/GfxInfoBase.h | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/widget/xpwidgets/GfxInfoBase.cpp b/widget/xpwidgets/GfxInfoBase.cpp index 5164e65c933..11090cd6baf 100644 --- a/widget/xpwidgets/GfxInfoBase.cpp +++ b/widget/xpwidgets/GfxInfoBase.cpp @@ -33,6 +33,7 @@ using namespace mozilla::widget; using namespace mozilla; +using mozilla::MutexAutoLock; nsTArray* GfxInfoBase::mDriverInfo; bool GfxInfoBase::mDriverInfoObserverInitialized; @@ -86,7 +87,7 @@ void InitGfxDriverInfoShutdownObserver() using namespace mozilla::widget; using namespace mozilla; -NS_IMPL_ISUPPORTS3(GfxInfoBase, nsIGfxInfo, nsIObserver, nsISupportsWeakReference) +NS_IMPL_THREADSAFE_ISUPPORTS3(GfxInfoBase, nsIGfxInfo, nsIObserver, nsISupportsWeakReference) #define BLACKLIST_PREF_BRANCH "gfx.blacklist." #define SUGGESTED_VERSION_PREF BLACKLIST_PREF_BRANCH "suggested-driver-version" @@ -531,6 +532,7 @@ GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic, GfxInfoBase::GfxInfoBase() : mFailureCount(0) + , mMutex("GfxInfoBase") { } @@ -838,14 +840,15 @@ GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray& aDriverInfo) NS_IMETHODIMP_(void) GfxInfoBase::LogFailure(const nsACString &failure) { + MutexAutoLock lock(mMutex); /* We only keep the first 9 failures */ if (mFailureCount < ArrayLength(mFailures)) { mFailures[mFailureCount++] = failure; /* record it in the crash notes too */ -#if defined(MOZ_CRASHREPORTER) - CrashReporter::AppendAppNotesToCrashReport(failure); -#endif + #if defined(MOZ_CRASHREPORTER) + CrashReporter::AppendAppNotesToCrashReport(failure); + #endif } } diff --git a/widget/xpwidgets/GfxInfoBase.h b/widget/xpwidgets/GfxInfoBase.h index 6f343caf051..bb86f74cadb 100644 --- a/widget/xpwidgets/GfxInfoBase.h +++ b/widget/xpwidgets/GfxInfoBase.h @@ -17,6 +17,7 @@ #include "nsString.h" #include "GfxInfoCollector.h" #include "nsIGfxInfoDebug.h" +#include "mozilla/Mutex.h" namespace mozilla { namespace widget { @@ -95,6 +96,7 @@ private: nsCString mFailures[9]; // The choice of 9 is Ehsan's uint32_t mFailureCount; + Mutex mMutex; }; From ebfb1bde3d0075a2087995f60d59d636124dcd86 Mon Sep 17 00:00:00 2001 From: Mohit Gahlot Date: Mon, 28 Jan 2013 00:00:25 +0530 Subject: [PATCH 03/64] Bug 823939 - now doesnt consider base followed by only and no (presubscript presuperscript) pair as invalid-markup. r=fredw, r=Pike --- .../en-US/chrome/mathml/mathml.properties | 3 ++- layout/mathml/nsMathMLmmultiscriptsFrame.cpp | 6 +++--- layout/mathml/tests/test_bug553917.html | 2 +- .../mathml/mathml-mmultiscript-base-ref.html | 14 ++++++++++++++ .../mathml/mathml-mmultiscript-base.html | 16 ++++++++++++++++ .../mathml-mmultiscript-mprescript-ref.html | 15 +++++++++++++++ .../mathml/mathml-mmultiscript-mprescript.html | 18 ++++++++++++++++++ layout/reftests/mathml/reftest.list | 2 ++ 8 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 layout/reftests/mathml/mathml-mmultiscript-base-ref.html create mode 100644 layout/reftests/mathml/mathml-mmultiscript-base.html create mode 100644 layout/reftests/mathml/mathml-mmultiscript-mprescript-ref.html create mode 100644 layout/reftests/mathml/mathml-mmultiscript-mprescript.html diff --git a/dom/locales/en-US/chrome/mathml/mathml.properties b/dom/locales/en-US/chrome/mathml/mathml.properties index f46d8e2d2a5..c4f7e4e9d8f 100644 --- a/dom/locales/en-US/chrome/mathml/mathml.properties +++ b/dom/locales/en-US/chrome/mathml/mathml.properties @@ -4,7 +4,8 @@ ChildCountIncorrect=Invalid markup: Incorrect number of children for <%1$S/> tag. DuplicateMprescripts=Invalid markup: More than one in . -NoSubSup=Invalid markup: Expected at least one subscript/superscript pair in . Found none. +# LOCALIZATION NOTE: The first child of is the base, that is the element to which scripts are attached. +NoBase=Invalid markup: Expected exactly one Base element in . Found none. SubSupMismatch=Invalid markup: Incomplete subscript/superscript pair in . # LOCALIZATION NOTE: When localizing the single quotes ('), follow the conventions in css.properties for your target locale. diff --git a/layout/mathml/nsMathMLmmultiscriptsFrame.cpp b/layout/mathml/nsMathMLmmultiscriptsFrame.cpp index a528242f8a3..13b70032f89 100644 --- a/layout/mathml/nsMathMLmmultiscriptsFrame.cpp +++ b/layout/mathml/nsMathMLmmultiscriptsFrame.cpp @@ -359,11 +359,11 @@ nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext, childFrame = childFrame->GetNextSibling(); } // note: width=0 if all sup-sub pairs match correctly - if ((0 != width) || !baseFrame || !subScriptFrame || !supScriptFrame) { + if ((0 != width) || !baseFrame) { // report an error, encourage people to get their markups in order if (aPlaceOrigin) { - if (count <= 1 || (count == 2 && mprescriptsFrame)) { - ReportErrorToConsole("NoSubSup"); + if (!baseFrame) { + ReportErrorToConsole("NoBase"); } else { ReportErrorToConsole("SubSupMismatch"); } diff --git a/layout/mathml/tests/test_bug553917.html b/layout/mathml/tests/test_bug553917.html index 97df26f36ad..20e5dd97182 100644 --- a/layout/mathml/tests/test_bug553917.html +++ b/layout/mathml/tests/test_bug553917.html @@ -62,7 +62,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=553917 xy*/ MMultiscriptsErrors: { status: [false, false, false], - args: ["DuplicateMprescripts", "NoSubSup", "SubSupMismatch"] + args: ["NoBase","DuplicateMprescripts", "SubSupMismatch"] }, /*+*/ UnitlessValuesAreDeprecated : { diff --git a/layout/reftests/mathml/mathml-mmultiscript-base-ref.html b/layout/reftests/mathml/mathml-mmultiscript-base-ref.html new file mode 100644 index 00000000000..45f43e9f9bb --- /dev/null +++ b/layout/reftests/mathml/mathml-mmultiscript-base-ref.html @@ -0,0 +1,14 @@ + + + + mmultiscript with only base + + + + + + base + + + + diff --git a/layout/reftests/mathml/mathml-mmultiscript-base.html b/layout/reftests/mathml/mathml-mmultiscript-base.html new file mode 100644 index 00000000000..f2832df4795 --- /dev/null +++ b/layout/reftests/mathml/mathml-mmultiscript-base.html @@ -0,0 +1,16 @@ + + + + mmultiscript with only base and none as placeholder + + + + + + base + + + + + + diff --git a/layout/reftests/mathml/mathml-mmultiscript-mprescript-ref.html b/layout/reftests/mathml/mathml-mmultiscript-mprescript-ref.html new file mode 100644 index 00000000000..b30e7fb9762 --- /dev/null +++ b/layout/reftests/mathml/mathml-mmultiscript-mprescript-ref.html @@ -0,0 +1,15 @@ + + + + mmultiscript with mprescripts without none + + + + + + base + + + + + diff --git a/layout/reftests/mathml/mathml-mmultiscript-mprescript.html b/layout/reftests/mathml/mathml-mmultiscript-mprescript.html new file mode 100644 index 00000000000..5bc0b5a5ad5 --- /dev/null +++ b/layout/reftests/mathml/mathml-mmultiscript-mprescript.html @@ -0,0 +1,18 @@ + + + + mmultiscript with mprescripts with none as placeholders + + + + + + base + + + + + + + + diff --git a/layout/reftests/mathml/reftest.list b/layout/reftests/mathml/reftest.list index b4945594c9c..f3a6c557438 100644 --- a/layout/reftests/mathml/reftest.list +++ b/layout/reftests/mathml/reftest.list @@ -116,3 +116,5 @@ fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 787215 == operator-1.xhtml operator-1-ref.xhtml == scriptshift-1.xhtml scriptshift-1-ref.xhtml == number-size-1.xhtml number-size-1-ref.xhtml +== mathml-mmultiscript-base.html mathml-mmultiscript-base-ref.html +== mathml-mmultiscript-mprescript.html mathml-mmultiscript-mprescript-ref.html From e7c2255848b05c92419ab90da1e228126a33d76d Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Mon, 25 Feb 2013 09:07:16 -0500 Subject: [PATCH 04/64] Bug 828223 - Write nsSearchService cache with OS.File + nsSearchService cache tests. r=gavin --- toolkit/components/search/nsSearchService.js | 29 ++--- .../search/tests/xpcshell/head_search.js | 30 ++++++ .../search/tests/xpcshell/test_nocache.js | 100 ++++++++++++++++++ .../search/tests/xpcshell/xpcshell.ini | 1 + 4 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 toolkit/components/search/tests/xpcshell/test_nocache.js diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index 2d31f517fa5..1a7b49bf47f 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -2664,29 +2664,20 @@ SearchService.prototype = { cache.directories[cacheKey].engines.push(engine._serializeToJSON(true)); } - let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. - createInstance(Ci.nsIScriptableUnicodeConverter); - let cacheFile = getDir(NS_APP_USER_PROFILE_50_DIR); - cacheFile.append("search.json"); - try { LOG("_buildCache: Writing to cache file."); - ostream.init(cacheFile, (MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE), PERMS_FILE, ostream.DEFER_OPEN); - converter.charset = "UTF-8"; - let data = converter.convertToInputStream(JSON.stringify(cache)); + let path = OS.Path.join(OS.Constants.Path.profileDir, "search.json"); + let data = gEncoder.encode(JSON.stringify(cache)); + let promise = OS.File.writeAtomic(path, data, { tmpPath: path + ".tmp"}); - // Write to the cache file asynchronously - NetUtil.asyncCopy(data, ostream, function(rv) { - if (Components.isSuccessCode(rv)) { - Services.obs.notifyObservers(null, - SEARCH_SERVICE_TOPIC, - SEARCH_SERVICE_CACHE_WRITTEN); - } else { - LOG("_buildCache: failure during asyncCopy: " + rv); + promise.then( + function onSuccess() { + Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, SEARCH_SERVICE_CACHE_WRITTEN); + }, + function onError(e) { + LOG("_buildCache: failure during writeAtomic: " + e); } - }); + ); } catch (ex) { LOG("_buildCache: Could not write to cache file: " + ex); } diff --git a/toolkit/components/search/tests/xpcshell/head_search.js b/toolkit/components/search/tests/xpcshell/head_search.js index cb6b397146d..4775ae4a017 100644 --- a/toolkit/components/search/tests/xpcshell/head_search.js +++ b/toolkit/components/search/tests/xpcshell/head_search.js @@ -51,6 +51,19 @@ function removeCacheFile() } } +/** + * Clean the profile of any cache file left from a previous run. + */ +function removeCache() +{ + let file = gProfD.clone(); + file.append("search.json"); + if (file.exists()) { + file.remove(false); + } + +} + /** * Run some callback once metadata has been committed to disk. */ @@ -67,6 +80,23 @@ function afterCommit(callback) Services.obs.addObserver(obs, "browser-search-service", false); } +/** + * Run some callback once cache has been built. + */ +function afterCache(callback) +{ + let obs = function(result, topic, verb) { + do_print("afterCache: " + verb); + if (verb == "write-cache-to-disk-complete") { + Services.obs.removeObserver(obs, topic); + callback(result); + } else { + dump("TOPIC: " + topic+ "\n"); + } + } + Services.obs.addObserver(obs, "browser-search-service", false); +} + function parseJsonFromStream(aInputStream) { const json = Cc["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON); const data = json.decodeFromStream(aInputStream, aInputStream.available()); diff --git a/toolkit/components/search/tests/xpcshell/test_nocache.js b/toolkit/components/search/tests/xpcshell/test_nocache.js new file mode 100644 index 00000000000..600a2229d55 --- /dev/null +++ b/toolkit/components/search/tests/xpcshell/test_nocache.js @@ -0,0 +1,100 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +let Cu = Components.utils; +let Ci = Components.interfaces; + +Cu.import("resource://gre/modules/osfile.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://testing-common/httpd.js"); + +/* + * test_nocache: Start search engine + * - without search.json + * + * Ensure that : + * - nothing explodes; + * - search.json is created. + */ + + +function run_test() +{ + removeCache(); + updateAppInfo(); + do_load_manifest("data/chrome.manifest"); + + let httpServer = new HttpServer(); + httpServer.start(4444); + httpServer.registerDirectory("/", do_get_cwd()); + + let search = Services.search; + + do_test_pending(); + + // Check that cache is created at startup + afterCache(function cacheCreated() { + // Check that search.json has been created. + let cache = gProfD.clone(); + cache.append("search.json"); + do_check_true(cache.exists()); + }); + + // Perform initialization + search.init(function ss_initialized(rv) { + do_check_true(Components.isSuccessCode(rv)); + + do_print("Setting up observer"); + function observer(aSubject, aTopic, aData) { + do_print("Observing topic " + aTopic); + if ("engine-added" == aData) { + let engine = search.getEngineByName("Test search engine"); + if (!engine) { + return; + } + Services.obs.removeObserver(observer, "browser-search-engine-modified"); + do_print("Engine has been added, let's wait for the cache to be built"); + afterCache(function() { + do_print("Success"); + + Task.spawn(function task() { + do_print("Searching test engine in cache"); + try { + let path = OS.Path.join(OS.Constants.Path.profileDir, "search.json"); + let data = yield OS.File.read(path); + let text = new TextDecoder().decode(data); + let cache = JSON.parse(text); + let found = false; + for (let dirName in cache.directories) { + for (let engine of cache.directories[dirName].engines) { + if (engine._id == "[app]/test-search-engine.xml") { + found = true; + break; + } + } + if (found) { + break; + } + } + do_check_true(found); + } catch (ex) { + do_throw(ex); + } finally { + removeCache(); + httpServer.stop(function() { + // httpServer doesn't seem to stop cleanly if there is no callback + }); + do_test_finished(); + } + }); + }); + } + }; + Services.obs.addObserver(observer, "browser-search-engine-modified", false); + + // Add an engine, check if it appears in the cache + search.addEngine("http://localhost:4444/data/engine.xml", + Ci.nsISearchEngine.DATA_XML, + null, false); + }); +} diff --git a/toolkit/components/search/tests/xpcshell/xpcshell.ini b/toolkit/components/search/tests/xpcshell/xpcshell.ini index f46bd6b92e1..daaa4774470 100644 --- a/toolkit/components/search/tests/xpcshell/xpcshell.ini +++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini @@ -3,6 +3,7 @@ head = head_search.js tail = firefox-appdir = browser +[test_nocache.js] [test_645970.js] [test_init_async_multiple.js] [test_init_async_multiple_then_sync.js] From a266fca5cdd8880c3a4945492b820bf0f157186a Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Mon, 25 Feb 2013 09:07:16 -0500 Subject: [PATCH 05/64] Bug 840436 - OS.File.writeAtomic without flush doesn't rename anymore. r=froydnj --- .../components/osfile/osfile_shared_front.jsm | 45 +++++++++------- .../tests/mochi/main_test_osfile_async.js | 54 +++++++++++++++++++ 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/toolkit/components/osfile/osfile_shared_front.jsm b/toolkit/components/osfile/osfile_shared_front.jsm index a3f5d70cd9a..b7e01202f2d 100644 --- a/toolkit/components/osfile/osfile_shared_front.jsm +++ b/toolkit/components/osfile/osfile_shared_front.jsm @@ -308,17 +308,15 @@ AbstractFile.read = function read(path, bytes) { }; /** - * Write a file, atomically. + * Write a file in one operation. * - * By opposition to a regular |write|, this operation ensures that, - * until the contents are fully written, the destination file is - * not modified. - * - * By default, files are flushed for additional safety, i.e. to lower - * the risks of losing data in case the device is suddenly removed or - * in case of sudden shutdown. This additional safety is important - * for user-critical data (e.g. preferences, application data, etc.) - * but comes at a performance cost. For non-critical data (e.g. cache, + * By default, this operation ensures that, until the contents are + * fully written, the destination file is not modified. By default, + * files are flushed for additional safety, i.e. to lower the risks of + * losing data in case the device is suddenly removed or in case of + * sudden shutdown. This additional safety is important for + * user-critical data (e.g. preferences, application data, etc.) but + * comes at a performance cost. For non-critical data (e.g. cache, * thumbnails, etc.), you may wish to deactivate flushing by passing * option |flush: false|. * @@ -347,23 +345,34 @@ AbstractFile.writeAtomic = function writeAtomic(path, buffer, options) { options = options || noOptions; - let tmpPath = options.tmpPath; - if (!tmpPath) { - throw new TypeError("Expected option tmpPath"); - } - let noOverwrite = options.noOverwrite; if (noOverwrite && OS.File.exists(path)) { throw OS.File.Error.exists("writeAtomic"); } + if ("flush" in options && !options.flush) { + // Just write, without any renaming trick + let dest; + try { + dest = OS.File.open(path, {write: true, truncate: true}); + return dest.write(buffer, options); + } finally { + dest.close(); + } + } + + + let tmpPath = options.tmpPath; + if (!tmpPath) { + throw new TypeError("Expected option tmpPath"); + } + + let tmpFile = OS.File.open(tmpPath, {write: true, truncate: true}); let bytesWritten; try { bytesWritten = tmpFile.write(buffer, options); - if ("flush" in options && options.flush) { - tmpFile.flush(); - } + tmpFile.flush(); } catch (x) { OS.File.remove(tmpPath); throw x; diff --git a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js index d4386f5d54f..7193c643abd 100644 --- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js +++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js @@ -402,6 +402,60 @@ let test_read_write_all = maketest("read_write_all", function read_write_all(tes // Cleanup. OS.File.remove(pathDest); + + // Same tests with |flush: false| + // Check that read + writeAtomic performs a correct copy + options = {tmpPath: tmpPath, flush: false}; + optionsBackup = {tmpPath: tmpPath, flush: false}; + bytesWritten = yield OS.File.writeAtomic(pathDest, contents, options); + test.is(contents.byteLength, bytesWritten, "Wrote the correct number of bytes (without flush)"); + + // Check that options are not altered + test.is(Object.keys(options).length, Object.keys(optionsBackup).length, + "The number of options was not changed (without flush)"); + for (let k in options) { + test.is(options[k], optionsBackup[k], "Option was not changed (without flush)"); + } + yield reference_compare_files(pathSource, pathDest, test); + + // Check that temporary file was removed + test.info("Compare complete (without flush)"); + test.ok(!(new FileUtils.File(tmpPath).exists()), "Temporary file was removed (without flush)"); + + // Check that writeAtomic fails if noOverwrite is true and the destination + // file already exists! + view = new Uint8Array(contents.buffer, 10, 200); + try { + options = {tmpPath: tmpPath, noOverwrite: true, flush: false}; + yield OS.File.writeAtomic(pathDest, view, options); + test.fail("With noOverwrite, writeAtomic should have refused to overwrite file (without flush)"); + } catch (err) { + test.info("With noOverwrite, writeAtomic correctly failed (without flush)"); + test.ok(err instanceof OS.File.Error, "writeAtomic correctly failed with a file error (without flush)"); + test.ok(err.becauseExists, "writeAtomic file error confirmed that the file already exists (without flush)"); + } + yield reference_compare_files(pathSource, pathDest, test); + test.ok(!(new FileUtils.File(tmpPath).exists()), "Temporary file was removed (without flush)"); + + // Now write a subset + START = 10; + LENGTH = 100; + view = new Uint8Array(contents.buffer, START, LENGTH); + bytesWritten = yield OS.File.writeAtomic(pathDest, view, {tmpPath: tmpPath, flush: false}); + test.is(bytesWritten, LENGTH, "Partial write wrote the correct number of bytes (without flush)"); + array2 = yield OS.File.read(pathDest); + view1 = new Uint8Array(contents.buffer, START, LENGTH); + test.is(view1.length, array2.length, "Re-read partial write with the correct number of bytes (without flush)"); + for (let i = 0; i < LENGTH; ++i) { + if (view1[i] != array2[i]) { + test.is(view1[i], array2[i], "Offset " + i + " is correct (without flush)"); + } + test.ok(true, "Compared re-read of partial write (without flush)"); + } + + // Cleanup. + OS.File.remove(pathDest); + }); }); From 9705cc157e8f03c595688bcec6fdeb4e1a316f9c Mon Sep 17 00:00:00 2001 From: Alex Xu Date: Mon, 25 Feb 2013 09:07:16 -0500 Subject: [PATCH 06/64] Bug 840834 - Fix compiler warning. r=jesup --- dom/media/MediaManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index dd7ca00bf08..a63a6df14dd 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -912,7 +912,7 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow, uint32_t permission; nsCOMPtr doc = aWindow->GetExtantDoc(); pm->TestPermission(doc->NodePrincipal(), &permission); - if ((permission == nsIPopupWindowManager::DENY_POPUP)) { + if (permission == nsIPopupWindowManager::DENY_POPUP) { nsCOMPtr domDoc = aWindow->GetExtantDocument(); nsGlobalWindow::FirePopupBlockedEvent( domDoc, aWindow, nullptr, EmptyString(), EmptyString() From 5771db118a20d3509d03ac1ab74ff5e6aeee0027 Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Mon, 25 Feb 2013 09:07:17 -0500 Subject: [PATCH 07/64] Bug 843479 - More logging for test_init_async_multiple_then_sync.js. --- .../test_init_async_multiple_then_sync.js | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/toolkit/components/search/tests/xpcshell/test_init_async_multiple_then_sync.js b/toolkit/components/search/tests/xpcshell/test_init_async_multiple_then_sync.js index 85c427f5281..48adba74a55 100644 --- a/toolkit/components/search/tests/xpcshell/test_init_async_multiple_then_sync.js +++ b/toolkit/components/search/tests/xpcshell/test_init_async_multiple_then_sync.js @@ -36,26 +36,33 @@ function run_test() { // Ensure that all asynchronous initializers eventually complete let init_complete = function init_complete(i) { + do_print("init complete " + i); do_check_true(pending[i]); pending[i] = false; numberPending--; do_check_true(numberPending >= 0); do_check_true(Services.search.isInitialized); - if (numberPending == 0) { - // Just check that we can access a list of engines. - let engines = Services.search.getEngines(); - do_check_neq(engines, null); - - // Wait a little before quitting: if some initializer is - // triggered twice, we want to catch that error. - do_timeout(1000, function() { - do_test_finished(); - }); + if (numberPending != 0) { + do_print("Still waiting for the following initializations: " + JSON.stringify(pending)); + return; } + do_print("All initializations have completed"); + // Just check that we can access a list of engines. + let engines = Services.search.getEngines(); + do_check_neq(engines, null); + + do_print("Waiting a second before quitting"); + // Wait a little before quitting: if some initializer is + // triggered twice, we want to catch that error. + do_timeout(1000, function() { + do_print("Test is complete"); + do_test_finished(); + }); }; // ... but don't wait for asynchronous initializations to complete let engines = Services.search.getEngines(); do_check_neq(engines, null); + do_print("Synchronous part of the test complete"); } From 5c45942eaee964ce45d9f6bf4c0d7141183e64e3 Mon Sep 17 00:00:00 2001 From: Steve Singer Date: Mon, 25 Feb 2013 15:11:30 +0100 Subject: [PATCH 08/64] Bug 814693 Followup - fix/enable webrtc on ppc. r=ted,jesup --- configure.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 44090398124..35182a86c87 100644 --- a/configure.in +++ b/configure.in @@ -8971,7 +8971,7 @@ echo $MAKEFILES > unallmakefiles AC_OUTPUT($MAKEFILES) -# target_arch is from {ia32|x64|arm} +# target_arch is from {ia32|x64|arm|ppc} case "$CPU_ARCH" in x86_64 | ia64) WEBRTC_TARGET_ARCH=x64 @@ -8985,6 +8985,9 @@ x86) WEBRTC_TARGET_ARCH=ia32 ;; +ppc*) + WEBRTC_TARGET_ARCH=ppc + ;; *) # unsupported arch for webrtc WEBRTC_TARGET_ARCH=unknown From efd5e3ed651c5ab7ebdd4a1e8dd0c46e21134d20 Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Mon, 25 Feb 2013 15:16:39 +0100 Subject: [PATCH 09/64] Bug 757188 - Implement Script.getAllColumnOffsets; r=jorendorff --- .../debug/Script-getAllColumnOffsets-01.js | 19 +++++++ js/src/vm/Debugger.cpp | 54 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js diff --git a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js new file mode 100644 index 00000000000..ab1cd0ec9af --- /dev/null +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js @@ -0,0 +1,19 @@ +// getColumnOffsets correctly places the various parts of a ForStatement. + +var global = newGlobal('new-compartment'); +Debugger(global).onDebuggerStatement = function (frame) { + var script = frame.eval("f").return.script; + script.getAllColumnOffsets().forEach(function (offset) { + script.setBreakpoint(offset.offset, { + hit: function (frame) { + assertEq(offset.lineNumber, 17); + global.log += offset.columnNumber + " "; + } + }); + }); +}; + +global.log = ''; +global.eval("function f(n) { for (var i = 0; i < n; ++i) log += '. '; log += '! '; } debugger;"); +global.f(3); +assertEq(global.log, "21 32 44 . 39 32 44 . 39 32 44 . 39 32 57 ! 69 "); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index e080c48595e..480bf76d8e7 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -3185,6 +3185,59 @@ DebuggerScript_getAllOffsets(JSContext *cx, unsigned argc, Value *vp) return true; } +static JSBool +DebuggerScript_getAllColumnOffsets(JSContext *cx, unsigned argc, Value *vp) +{ + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getAllColumnOffsets", args, obj, script); + + /* + * First pass: determine which offsets in this script are jump targets and + * which positions jump to them. + */ + FlowGraphSummary flowData(cx); + if (!flowData.populate(cx, script)) + return false; + + /* Second pass: build the result array. */ + RootedObject result(cx, NewDenseEmptyArray(cx)); + if (!result) + return false; + for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) { + size_t lineno = r.frontLineNumber(); + size_t column = r.frontColumnNumber(); + size_t offset = r.frontOffset(); + + /* Make a note, if the current instruction is an entry point for the current position. */ + if (!flowData[offset].hasNoEdges() && + (flowData[offset].lineno() != lineno || + flowData[offset].column() != column)) { + RootedObject entry(cx, NewBuiltinClassInstance(cx, &ObjectClass)); + if (!entry) + return false; + + RootedId id(cx, NameToId(cx->names().lineNumber)); + RootedValue value(cx, NumberValue(lineno)); + if (!JSObject::defineGeneric(cx, entry, id, value)) + return false; + + value = NumberValue(column); + if (!JSObject::defineProperty(cx, entry, cx->names().columnNumber, value)) + return false; + + id = NameToId(cx->names().offset); + value = NumberValue(offset); + if (!JSObject::defineGeneric(cx, entry, id, value)) + return false; + + if (!js_NewbornArrayPush(cx, result, ObjectValue(*entry))) + return false; + } + } + + args.rval().setObject(*result); + return true; +} + static JSBool DebuggerScript_getLineOffsets(JSContext *cx, unsigned argc, Value *vp) { @@ -3356,6 +3409,7 @@ static JSPropertySpec DebuggerScript_properties[] = { static JSFunctionSpec DebuggerScript_methods[] = { JS_FN("getChildScripts", DebuggerScript_getChildScripts, 0, 0), JS_FN("getAllOffsets", DebuggerScript_getAllOffsets, 0, 0), + JS_FN("getAllColumnOffsets", DebuggerScript_getAllColumnOffsets, 0, 0), JS_FN("getLineOffsets", DebuggerScript_getLineOffsets, 1, 0), JS_FN("getOffsetLine", DebuggerScript_getOffsetLine, 0, 0), JS_FN("setBreakpoint", DebuggerScript_setBreakpoint, 2, 0), From e72e340ce511cf5d011271a593cfee1f6a5e51f8 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 25 Feb 2013 08:33:56 -0600 Subject: [PATCH 10/64] Bug 844270 - Move visual manifests into branding, and set custom background colors for each release. r=glandium --- browser/branding/aurora/Makefile.in | 7 +++++++ .../branding/aurora/VisualElementsManifest.xml | 16 ++++++++++++++++ browser/branding/nightly/Makefile.in | 7 +++++++ .../branding/nightly/VisualElementsManifest.xml | 16 ++++++++++++++++ browser/branding/official/Makefile.in | 7 +++++++ .../official/VisualElementsManifest.xml} | 0 browser/branding/unofficial/Makefile.in | 7 +++++++ .../unofficial/VisualElementsManifest.xml | 16 ++++++++++++++++ browser/metro/shell/Makefile.in | 7 ------- 9 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 browser/branding/aurora/VisualElementsManifest.xml create mode 100644 browser/branding/nightly/VisualElementsManifest.xml rename browser/{metro/shell/VisualElementsManifest.xml.in => branding/official/VisualElementsManifest.xml} (100%) create mode 100644 browser/branding/unofficial/VisualElementsManifest.xml diff --git a/browser/branding/aurora/Makefile.in b/browser/branding/aurora/Makefile.in index e43a887fc6c..7bc0bdd4759 100644 --- a/browser/branding/aurora/Makefile.in +++ b/browser/branding/aurora/Makefile.in @@ -69,4 +69,11 @@ BRANDING_DEST := $(DIST)/branding BRANDING_TARGET := export INSTALL_TARGETS += BRANDING +ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro) +VISUALMANIFEST := VisualElementsManifest.xml +VISUALMANIFEST_FLAGS := -Fsubstitution -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME} +VISUALMANIFEST_PATH := $(DIST)/bin +PP_TARGETS += VISUALMANIFEST +endif + include $(topsrcdir)/config/rules.mk diff --git a/browser/branding/aurora/VisualElementsManifest.xml b/browser/branding/aurora/VisualElementsManifest.xml new file mode 100644 index 00000000000..866b61cfb82 --- /dev/null +++ b/browser/branding/aurora/VisualElementsManifest.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/browser/branding/nightly/Makefile.in b/browser/branding/nightly/Makefile.in index e43a887fc6c..7bc0bdd4759 100644 --- a/browser/branding/nightly/Makefile.in +++ b/browser/branding/nightly/Makefile.in @@ -69,4 +69,11 @@ BRANDING_DEST := $(DIST)/branding BRANDING_TARGET := export INSTALL_TARGETS += BRANDING +ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro) +VISUALMANIFEST := VisualElementsManifest.xml +VISUALMANIFEST_FLAGS := -Fsubstitution -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME} +VISUALMANIFEST_PATH := $(DIST)/bin +PP_TARGETS += VISUALMANIFEST +endif + include $(topsrcdir)/config/rules.mk diff --git a/browser/branding/nightly/VisualElementsManifest.xml b/browser/branding/nightly/VisualElementsManifest.xml new file mode 100644 index 00000000000..5e97c80c75b --- /dev/null +++ b/browser/branding/nightly/VisualElementsManifest.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/browser/branding/official/Makefile.in b/browser/branding/official/Makefile.in index e43a887fc6c..7bc0bdd4759 100644 --- a/browser/branding/official/Makefile.in +++ b/browser/branding/official/Makefile.in @@ -69,4 +69,11 @@ BRANDING_DEST := $(DIST)/branding BRANDING_TARGET := export INSTALL_TARGETS += BRANDING +ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro) +VISUALMANIFEST := VisualElementsManifest.xml +VISUALMANIFEST_FLAGS := -Fsubstitution -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME} +VISUALMANIFEST_PATH := $(DIST)/bin +PP_TARGETS += VISUALMANIFEST +endif + include $(topsrcdir)/config/rules.mk diff --git a/browser/metro/shell/VisualElementsManifest.xml.in b/browser/branding/official/VisualElementsManifest.xml similarity index 100% rename from browser/metro/shell/VisualElementsManifest.xml.in rename to browser/branding/official/VisualElementsManifest.xml diff --git a/browser/branding/unofficial/Makefile.in b/browser/branding/unofficial/Makefile.in index e43a887fc6c..7bc0bdd4759 100644 --- a/browser/branding/unofficial/Makefile.in +++ b/browser/branding/unofficial/Makefile.in @@ -69,4 +69,11 @@ BRANDING_DEST := $(DIST)/branding BRANDING_TARGET := export INSTALL_TARGETS += BRANDING +ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows metro) +VISUALMANIFEST := VisualElementsManifest.xml +VISUALMANIFEST_FLAGS := -Fsubstitution -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME} +VISUALMANIFEST_PATH := $(DIST)/bin +PP_TARGETS += VISUALMANIFEST +endif + include $(topsrcdir)/config/rules.mk diff --git a/browser/branding/unofficial/VisualElementsManifest.xml b/browser/branding/unofficial/VisualElementsManifest.xml new file mode 100644 index 00000000000..5e97c80c75b --- /dev/null +++ b/browser/branding/unofficial/VisualElementsManifest.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/browser/metro/shell/Makefile.in b/browser/metro/shell/Makefile.in index 83541fff4ac..8fc5c6684e3 100644 --- a/browser/metro/shell/Makefile.in +++ b/browser/metro/shell/Makefile.in @@ -19,15 +19,8 @@ endif export:: $(NSINSTALL) $(srcdir)/resources.pri $(DIST)/bin - $(RM) $(DIST)/bin/VisualElementsManifest.xml - $(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) -DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME} \ - $(srcdir)/VisualElementsManifest.xml.in > $(DIST)/bin/VisualElementsManifest.xml install:: $(NSINSTALL) $(srcdir)/resources.pri $(DIST)/bin -# bug 744566 -# $(RM) $(DIST)/bin/resources.pri -# $(MAKEPRI) new -v -pr $(srcdir)/tileresources -cf $(srcdir)/priconfig.xml -mn $(srcdir)/AppManifest.xml -of $(DIST)/bin/resources.pri -o - include $(topsrcdir)/config/rules.mk From fbdf0875d69b8d8c9499b34a8459e8f006a99d70 Mon Sep 17 00:00:00 2001 From: Gene Lian Date: Fri, 8 Feb 2013 18:45:04 +0800 Subject: [PATCH 11/64] Bug 839436 - B2G MMS: make DB be able to save MMS messages. r=vicamo --- .../nsIRilMobileMessageDatabaseService.idl | 38 ++-- .../src/ril/MobileMessageDatabaseService.js | 174 +++++++++--------- dom/sms/tests/test_smsdatabaseservice.xul | 22 +-- dom/system/gonk/RadioInterfaceLayer.js | 92 +++++---- 4 files changed, 174 insertions(+), 152 deletions(-) diff --git a/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl b/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl index 87169c328bf..609447ddcc4 100644 --- a/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl +++ b/dom/mobilemessage/interfaces/nsIRilMobileMessageDatabaseService.idl @@ -2,30 +2,42 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "domstubs.idl" #include "nsISupports.idl" #include "nsIMobileMessageDatabaseService.idl" -interface nsIDOMMozSmsMessage; - -[scriptable, function, uuid(9cd80750-6a08-11e2-ac93-bf895e53f40e)] +[scriptable, function, uuid(0bffae74-71db-11e2-962a-73cf64d6393e)] interface nsIRilMobileMessageDatabaseCallback : nsISupports { - void notify(in nsresult aRv, in nsIDOMMozSmsMessage aSms); + /** + * |aRecord| Object: the mobile-message database record + */ + void notify(in nsresult aRv, in jsval aRecord); }; -[scriptable, uuid(89528354-6a08-11e2-8243-af4cf90404a9)] +[scriptable, uuid(3592525a-71d6-11e2-82ca-f75ae6e08ee2)] interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService { - long saveReceivedMessage(in DOMString aSender, - in DOMString aBody, - in DOMString aMessageClass, - in unsigned long long aDate, + /** + * |aMessage| Object: should contain the following properties for internal use: + * - |type| DOMString: "sms" or "mms" + * - |sender| DOMString: the phone number of sender + * - |timestamp| Number: the timestamp of received message + * - |messageClass| DOMString: the message class of received message + */ + long saveReceivedMessage(in jsval aMessage, [optional] in nsIRilMobileMessageDatabaseCallback aCallback); - long saveSendingMessage(in DOMString aReceiver, - in DOMString aBody, - in DOMString aDeliveryStatus, - in unsigned long long aDate, + + /** + * |aMessage| Object: should contain the following properties for internal use: + * - |type| DOMString: "sms" or "mms" + * - |receiver| DOMString: the phone number of receiver + * - |timestamp| Number: the timestamp of sending message + * - |deliveryStatus| DOMString: the delivery status of sending message + */ + long saveSendingMessage(in jsval aMessage, [optional] in nsIRilMobileMessageDatabaseCallback aCallback); + void setMessageDelivery(in long aMessageId, in DOMString aDelivery, in DOMString aDeliveryStatus, diff --git a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js index 4d8f02d1f43..a53765504c2 100644 --- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js +++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js @@ -413,8 +413,8 @@ MobileMessageDatabaseService.prototype = { }; }, - createMessageFromRecord: function createMessageFromRecord(aRecord) { - if (DEBUG) debug("createMessageFromRecord: " + JSON.stringify(aRecord)); + createSmsMessageFromRecord: function createSmsMessageFromRecord(aRecord) { + if (DEBUG) debug("createSmsMessageFromRecord: " + JSON.stringify(aRecord)); return gSmsService.createSmsMessage(aRecord.id, aRecord.delivery, aRecord.deliveryStatus, @@ -482,7 +482,7 @@ MobileMessageDatabaseService.prototype = { let getRequest = aObjectStore.get(firstMessageId); let self = this; getRequest.onsuccess = function onsuccess(event) { - let sms = self.createMessageFromRecord(event.target.result); + let sms = self.createSmsMessageFromRecord(event.target.result); if (aMessageList.listId >= 0) { if (DEBUG) { debug("notifyNextMessageInListGot - listId: " @@ -657,8 +657,7 @@ MobileMessageDatabaseService.prototype = { if (!aCallback) { return; } - let sms = self.createMessageFromRecord(aRecord); - aCallback.notify(rv, sms); + aCallback.notify(rv, aRecord); } this.newTxn(READ_WRITE, function(error, txn, stores) { @@ -678,9 +677,8 @@ MobileMessageDatabaseService.prototype = { // First add to main objectStore. stores[0].put(aRecord); - let number = getNumberFromRecord(aRecord); - // Next update the other objectStore. + let number = getNumberFromRecord(aRecord); stores[1].get(number).onsuccess = function onsuccess(event) { let mostRecentEntry = event.target.result; if (mostRecentEntry) { @@ -713,100 +711,96 @@ MobileMessageDatabaseService.prototype = { return aRecord.id; }, + getRilIccInfoMsisdn: function getRilIccInfoMsisdn() { + let iccInfo = this.mRIL.rilContext.iccInfo; + let number = iccInfo ? iccInfo.msisdn : null; + + // Workaround an xpconnect issue with undefined string objects. + // See bug 808220 + if (number === undefined || number === "undefined") { + return null; + } + return number; + }, + + makePhoneNumberInternational: function makePhoneNumberInternational(aNumber) { + if (!aNumber) { + return aNumber; + } + let parsedNumber = PhoneNumberUtils.parse(aNumber.toString()); + if (!parsedNumber || !parsedNumber.internationalNumber) { + return aNumber; + } + return parsedNumber.internationalNumber; + }, /** * nsIRilMobileMessageDatabaseService API */ - saveReceivedMessage: function saveReceivedMessage( - aSender, aBody, aMessageClass, aDate, aCallback) { - let iccInfo = this.mRIL.rilContext.iccInfo; - let receiver = iccInfo ? iccInfo.msisdn : null; - - // Workaround an xpconnect issue with undefined string objects. - // See bug 808220 - if (receiver === undefined || receiver === "undefined") { - receiver = null; + saveReceivedMessage: function saveReceivedMessage(aMessage, aCallback) { + if (aMessage.type === undefined || + aMessage.sender === undefined || + aMessage.messageClass === undefined || + aMessage.timestamp === undefined) { + if (aCallback) { + aCallback.notify(Cr.NS_ERROR_FAILURE, null); + } + return; } - if (receiver) { - let parsedNumber = PhoneNumberUtils.parse(receiver); - receiver = (parsedNumber && parsedNumber.internationalNumber) - ? parsedNumber.internationalNumber - : receiver; - } + let receiver = this.getRilIccInfoMsisdn(); + receiver = this.makePhoneNumberInternational(receiver); - let sender = aSender; - if (sender) { - let parsedNumber = PhoneNumberUtils.parse(sender); - sender = (parsedNumber && parsedNumber.internationalNumber) - ? parsedNumber.internationalNumber - : sender; - } + let sender = aMessage.sender = + this.makePhoneNumberInternational(aMessage.sender); - let record = { - deliveryIndex: [DELIVERY_RECEIVED, aDate], - numberIndex: [[sender, aDate], [receiver, aDate]], - readIndex: [FILTER_READ_UNREAD, aDate], + let timestamp = aMessage.timestamp; - delivery: DELIVERY_RECEIVED, - deliveryStatus: DELIVERY_STATUS_SUCCESS, - sender: sender, - receiver: receiver, - body: aBody, - messageClass: aMessageClass, - timestamp: aDate, - read: FILTER_READ_UNREAD - }; - return this.saveRecord(record, aCallback); + // Adding needed indexes and extra attributes for internal use. + aMessage.deliveryIndex = [DELIVERY_RECEIVED, timestamp]; + aMessage.numberIndex = [[sender, timestamp], [receiver, timestamp]]; + aMessage.readIndex = [FILTER_READ_UNREAD, timestamp]; + aMessage.delivery = DELIVERY_RECEIVED; + aMessage.deliveryStatus = DELIVERY_STATUS_SUCCESS; + aMessage.receiver = receiver; + aMessage.read = FILTER_READ_UNREAD; + + return this.saveRecord(aMessage, aCallback); }, - saveSendingMessage: function saveSendingMessage( - aReceiver, aBody, aDeliveryStatus, aDate, aCallback) { + saveSendingMessage: function saveSendingMessage(aMessage, aCallback) { + if (aMessage.type === undefined || + aMessage.receiver === undefined || + aMessage.deliveryStatus === undefined || + aMessage.timestamp === undefined) { + if (aCallback) { + aCallback.notify(Cr.NS_ERROR_FAILURE, null); + } + return; + } + + let sender = this.getRilIccInfoMsisdn(); + let receiver = aMessage.receiver; + let rilContext = this.mRIL.rilContext; - let sender = rilContext.iccInfo - ? rilContext.iccInfo.msisdn - : null; - - // Workaround an xpconnect issue with undefined string objects. - // See bug 808220 - if (sender === undefined || sender === "undefined") { - sender = null; - } - - let receiver = aReceiver; - if (rilContext.voice.network.mcc === rilContext.iccInfo.mcc) { - if (receiver) { - let parsedNumber = PhoneNumberUtils.parse(receiver.toString()); - receiver = (parsedNumber && parsedNumber.internationalNumber) - ? parsedNumber.internationalNumber - : receiver; - } - - if (sender) { - let parsedNumber = PhoneNumberUtils.parse(sender.toString()); - sender = (parsedNumber && parsedNumber.internationalNumber) - ? parsedNumber.internationalNumber - : sender; - } + receiver = aMessage.receiver = this.makePhoneNumberInternational(receiver); + sender = this.makePhoneNumberInternational(sender); } - let record = { - deliveryIndex: [DELIVERY_SENDING, aDate], - numberIndex: [[sender, aDate], [receiver, aDate]], - readIndex: [FILTER_READ_READ, aDate], + let timestamp = aMessage.timestamp; - delivery: DELIVERY_SENDING, - deliveryStatus: aDeliveryStatus, - sender: sender, - receiver: receiver, - body: aBody, - messageClass: MESSAGE_CLASS_NORMAL, - timestamp: aDate, - read: FILTER_READ_READ - }; - return this.saveRecord(record, aCallback); + // Adding needed indexes and extra attributes for internal use. + aMessage.deliveryIndex = [DELIVERY_SENDING, timestamp]; + aMessage.numberIndex = [[sender, timestamp], [receiver, timestamp]]; + aMessage.readIndex = [FILTER_READ_READ, timestamp]; + aMessage.delivery = DELIVERY_SENDING; + aMessage.sender = sender; + aMessage.messageClass = MESSAGE_CLASS_NORMAL; + aMessage.read = FILTER_READ_READ; + + return this.saveRecord(aMessage, aCallback); }, setMessageDelivery: function setMessageDelivery( @@ -822,11 +816,7 @@ MobileMessageDatabaseService.prototype = { if (!callback) { return; } - let sms = null; - if (record) { - sms = self.createMessageFromRecord(record); - } - callback.notify(rv, sms); + callback.notify(rv, record); } this.newTxn(READ_WRITE, function (error, txn, store) { @@ -914,7 +904,7 @@ MobileMessageDatabaseService.prototype = { aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.UNKNOWN_ERROR); return; } - let sms = self.createMessageFromRecord(record); + let sms = self.createSmsMessageFromRecord(record); aRequest.notifyMessageGot(sms); }; @@ -1220,7 +1210,7 @@ MobileMessageDatabaseService.prototype = { // For all numbers. processing: filter.numbers.length, results: [] - }]; + }]; let timeRange = null; if (filter.startDate != null && filter.endDate != null) { @@ -1327,7 +1317,7 @@ MobileMessageDatabaseService.prototype = { if (DEBUG) debug("Could not get message id " + messageId); aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.NOT_FOUND_ERROR); } - let sms = self.createMessageFromRecord(record); + let sms = self.createSmsMessageFromRecord(record); aRequest.notifyNextMessageInListGot(sms); }; diff --git a/dom/sms/tests/test_smsdatabaseservice.xul b/dom/sms/tests/test_smsdatabaseservice.xul index 9f3f621f1e7..3bd3e523617 100644 --- a/dom/sms/tests/test_smsdatabaseservice.xul +++ b/dom/sms/tests/test_smsdatabaseservice.xul @@ -177,21 +177,27 @@ function newRandomId() { let sms = [ { + type: "sms", sender: "+34666000000", receiver: "+34666111000", body: "message 0", + messageClass: "normal", timestamp: 1329999861762 }, { + type: "sms", sender: "+34666000111", receiver: "+34666111111", body: "message 1", + messageClass: "normal", timestamp: 1329999861763 }, { + type: "sms", sender: "+34666000222", receiver: "+34666111222", body: "message 2", + messageClass: "normal", timestamp: 1329999861764 }, ]; @@ -201,9 +207,7 @@ let sms = [ */ add_test(function test_saveReceivedMessage() { info("test_saveReceivedMessage"); - let messageId = gMobileMessageDatabaseService.saveReceivedMessage(sms[0].sender, - sms[0].body, - sms[0].timestamp); + let messageId = gMobileMessageDatabaseService.saveReceivedMessage(sms[0]); checkDB(function (store) { let request = store.get(messageId); request.onsuccess = function onsuccess() { @@ -262,9 +266,7 @@ add_test(function test_getMessage_success() { run_next_test(); } }); - let messageId = gMobileMessageDatabaseService.saveReceivedMessage(sms[2].sender, - sms[2].body, - sms[2].timestamp); + let messageId = gMobileMessageDatabaseService.saveReceivedMessage(sms[2]); SimpleTest.executeSoon(function () { gMobileMessageDatabaseService.getMessage(messageId, fakeRequestId); }); @@ -821,9 +823,7 @@ add_test(function test_deleteMessage_success() { run_next_test(); } }); - let messageId = gMobileMessageDatabaseService.saveReceivedMessage(sms[0].sender, - sms[0].body, - sms[0].timestamp); + let messageId = gMobileMessageDatabaseService.saveReceivedMessage(sms[0]); SimpleTest.executeSoon(function () { gMobileMessageDatabaseService.deleteMessage(messageId, fakeRequestId); }); @@ -852,9 +852,7 @@ add_test(function test_markMessageRead_success() { run_next_test(); } }); - let messageId = gMobileMessageDatabaseService.saveReceivedMessage(sms[2].sender, - sms[2].body, - sms[2].timestamp); + let messageId = gMobileMessageDatabaseService.saveReceivedMessage(sms[2]); SimpleTest.executeSoon(function () { gMobileMessageDatabaseService.markMessageRead(messageId, true, fakeRequestId); }); diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index 28e0cb51fe2..e2214d58340 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -1476,6 +1476,18 @@ RadioInterfaceLayer.prototype = { 0, options); }, + createSmsMessageFromRecord: function createSmsMessageFromRecord(aRecord) { + return gSmsService.createSmsMessage(aRecord.id, + aRecord.delivery, + aRecord.deliveryStatus, + aRecord.sender, + aRecord.receiver, + aRecord.body, + aRecord.messageClass, + aRecord.timestamp, + aRecord.read); + }, + portAddressedSmsApps: null, handleSmsReceived: function handleSmsReceived(message) { debug("handleSmsReceived: " + JSON.stringify(message)); @@ -1501,6 +1513,11 @@ RadioInterfaceLayer.prototype = { return true; } + message.type = "sms"; + message.sender = message.sender || null; + message.receiver = message.receiver || null; + message.body = message.fullBody = message.fullBody || null; + // TODO: Bug #768441 // For now we don't store indicators persistently. When the mwi.discard // flag is false, we'll need to persist the indicator to EFmwis. @@ -1508,13 +1525,14 @@ RadioInterfaceLayer.prototype = { let mwi = message.mwi; if (mwi) { - mwi.returnNumber = message.sender || null; - mwi.returnMessage = message.fullBody || null; + mwi.returnNumber = message.sender; + mwi.returnMessage = message.fullBody; this._sendTargetMessage("voicemail", "RIL:VoicemailNotification", mwi); return true; } - let notifyReceived = function notifyReceived(rv, sms) { + let notifyReceived = function notifyReceived(rv, record) { + let sms = this.createSmsMessageFromRecord(record); let success = Components.isSuccessCode(rv); // Acknowledge the reception of the SMS. @@ -1532,38 +1550,30 @@ RadioInterfaceLayer.prototype = { } gSystemMessenger.broadcastMessage("sms-received", { - id: message.id, - delivery: DOM_SMS_DELIVERY_RECEIVED, - deliveryStatus: RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS, - sender: message.sender || null, - receiver: message.receiver || null, - body: message.fullBody || null, - messageClass: message.messageClass, - timestamp: message.timestamp, - read: false + id: message.id, + delivery: DOM_SMS_DELIVERY_RECEIVED, + deliveryStatus: RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS, + sender: message.sender, + receiver: message.receiver, + body: message.fullBody, + messageClass: message.messageClass, + timestamp: message.timestamp, + read: false }); + Services.obs.notifyObservers(sms, kSmsReceivedObserverTopic, null); }.bind(this); if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) { - message.id = gMobileMessageDatabaseService.saveReceivedMessage( - message.sender || null, - message.fullBody || null, - message.messageClass, - message.timestamp, - notifyReceived); + message.id = gMobileMessageDatabaseService.saveReceivedMessage(message, + notifyReceived); } else { message.id = -1; - let sms = gSmsService.createSmsMessage(message.id, - DOM_SMS_DELIVERY_RECEIVED, - RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS, - message.sender || null, - message.receiver || null, - message.fullBody || null, - message.messageClass, - message.timestamp, - false); - notifyReceived(Cr.NS_OK, sms); + message.delivery = DOM_SMS_DELIVERY_RECEIVED; + message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS; + message.read = false; + + notifyReceived(Cr.NS_OK, message); } // SMS ACK will be sent in notifyReceived. Return false here. @@ -1596,7 +1606,8 @@ RadioInterfaceLayer.prototype = { gMobileMessageDatabaseService.setMessageDelivery(options.sms.id, DOM_SMS_DELIVERY_SENT, options.sms.deliveryStatus, - function notifyResult(rv, sms) { + function notifyResult(rv, record) { + let sms = this.createSmsMessageFromRecord(record); //TODO bug 832140 handle !Components.isSuccessCode(rv) gSystemMessenger.broadcastMessage("sms-sent", {id: options.sms.id, @@ -1634,13 +1645,14 @@ RadioInterfaceLayer.prototype = { gMobileMessageDatabaseService.setMessageDelivery(options.sms.id, options.sms.delivery, message.deliveryStatus, - function notifyResult(rv, sms) { + function notifyResult(rv, record) { + let sms = this.createSmsMessageFromRecord(record); //TODO bug 832140 handle !Components.isSuccessCode(rv) let topic = (message.deliveryStatus == RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS) ? kSmsDeliverySuccessObserverTopic : kSmsDeliveryErrorObserverTopic; Services.obs.notifyObservers(sms, topic, null); - }); + }.bind(this)); }, handleSmsSendFailed: function handleSmsSendFailed(message) { @@ -1662,11 +1674,12 @@ RadioInterfaceLayer.prototype = { gMobileMessageDatabaseService.setMessageDelivery(options.sms.id, DOM_SMS_DELIVERY_ERROR, RIL.GECKO_SMS_DELIVERY_STATUS_ERROR, - function notifyResult(rv, sms) { + function notifyResult(rv, record) { + let sms = this.createSmsMessageFromRecord(record); //TODO bug 832140 handle !Components.isSuccessCode(rv) options.request.notifySendMessageFailed(error); Services.obs.notifyObservers(sms, kSmsFailedObserverTopic, null); - }); + }.bind(this)); }, /** @@ -2645,8 +2658,17 @@ RadioInterfaceLayer.prototype = { let deliveryStatus = options.requestStatusReport ? RIL.GECKO_SMS_DELIVERY_STATUS_PENDING : RIL.GECKO_SMS_DELIVERY_STATUS_NOT_APPLICABLE; - let id = gMobileMessageDatabaseService.saveSendingMessage(number, message, deliveryStatus, timestamp, - function notifyResult(rv, sms) { + let sendingMessage = { + type: "sms", + receiver: number, + body: message, + deliveryStatus: deliveryStatus, + timestamp: timestamp + }; + + let id = gMobileMessageDatabaseService.saveSendingMessage(sendingMessage, + function notifyResult(rv, record) { + let sms = this.createSmsMessageFromRecord(record); //TODO bug 832140 handle !Components.isSuccessCode(rv) Services.obs.notifyObservers(sms, kSmsSendingObserverTopic, null); From 92eb08092d145ef42dc146da3378e3396ae352a1 Mon Sep 17 00:00:00 2001 From: Henri Sivonen Date: Mon, 25 Feb 2013 17:12:13 +0200 Subject: [PATCH 12/64] Bug 844792 - Warn about the upcoming removal of multipart support in XHR. r=smaug. --- content/base/src/nsXMLHttpRequest.cpp | 2 ++ dom/locales/en-US/chrome/dom/dom.properties | 1 + 2 files changed, 3 insertions(+) diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 4cf210070db..4147515dbac 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -3371,6 +3371,8 @@ nsXMLHttpRequest::SetMultipart(bool aMultipart, nsresult& aRv) return; } + LogMessage("MultipartXHRWarning", GetOwner()); + if (aMultipart) { mState |= XML_HTTP_REQUEST_MULTIPART; } else { diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 52324c07b23..5efe29a0fe5 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -84,6 +84,7 @@ RemovedFullScreenElement=Exited full-screen because full-screen element was remo FocusedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin was focused. HTMLMultipartXHRWarning=HTML parsing in XMLHttpRequest is not supported for multipart responses. HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode. +MultipartXHRWarning=Support for multipart responses in XMLHttpRequest is going to be removed in an upcoming version. Please migrate to chunked responses or to Web Sockets. InvalidRedirectChannelWarning=Unable to redirect to %S because the channel doesn't implement nsIWritablePropertyBag2. ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are other non-report-only CSP policies applied. ResponseTypeSyncXHRWarning=Use of XMLHttpRequest's responseType attribute is no longer supported in the synchronous mode in window context. From 5ee039184ba6d385cd41b8232ed4dcdb2f17445f Mon Sep 17 00:00:00 2001 From: Marco Chen Date: Mon, 25 Feb 2013 16:54:07 +0800 Subject: [PATCH 13/64] Bug 836655 - [Audio] To remove ChildID if there is no content channel playing in foreground. And only send audio-channel-changed with normal when ChildID in the background has content channel in playing state. r=baku --- dom/audiochannel/AudioChannelService.cpp | 38 ++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index c1a0c8fcc27..24683a02d2a 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -135,6 +135,14 @@ AudioChannelService::UnregisterType(AudioChannelType aType, // In order to avoid race conditions, it's safer to notify any existing // agent any time a new one is registered. if (XRE_GetProcessType() == GeckoProcessType_Default) { + // We only remove ChildID when it is in the foreground. + // If in the background, we kept ChildID for allowing it to play next song. + if (aType == AUDIO_CHANNEL_CONTENT && + mActiveContentChildIDs.Contains(aChildID) && + (!aElementHidden && + !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID))) { + mActiveContentChildIDs.RemoveElement(aChildID); + } SendAudioChannelChangedNotification(); Notify(); } @@ -175,23 +183,31 @@ AudioChannelService::GetMutedInternal(AudioChannelType aType, uint64_t aChildID, // If the audio content channel is visible, let's remember this ChildID. if (newType == AUDIO_CHANNEL_INT_CONTENT && - oldType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN && - !mActiveContentChildIDs.Contains(aChildID)) { + oldType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN) { if (mActiveContentChildIDsFrozen) { mActiveContentChildIDsFrozen = false; mActiveContentChildIDs.Clear(); } - mActiveContentChildIDs.AppendElement(aChildID); + if (!mActiveContentChildIDs.Contains(aChildID)) { + mActiveContentChildIDs.AppendElement(aChildID); + } } - // If nothing is visible, the list has to been frozen. else if (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN && oldType == AUDIO_CHANNEL_INT_CONTENT && - !mActiveContentChildIDsFrozen && - mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) { - mActiveContentChildIDsFrozen = true; + !mActiveContentChildIDsFrozen) { + // If nothing is visible, the list has to been frozen. + // Or if there is still any one with other ChildID in foreground then + // it should be removed from list and left other ChildIDs in the foreground + // to keep playing. Finally only last one childID which go to background + // will be in list. + if (mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) { + mActiveContentChildIDsFrozen = true; + } else if (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID)) { + mActiveContentChildIDs.RemoveElement(aChildID); + } } if (newType != oldType && aType == AUDIO_CHANNEL_CONTENT) { @@ -291,8 +307,12 @@ AudioChannelService::SendAudioChannelChangedNotification() higher = AUDIO_CHANNEL_NOTIFICATION; } - // Content channels play in background if just one is active. - else if (!mActiveContentChildIDs.IsEmpty()) { + // There is only one Child can play content channel in the background. + // And need to check whether there is any content channels under playing + // now. + else if (!mActiveContentChildIDs.IsEmpty() && + mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains( + mActiveContentChildIDs[0])) { higher = AUDIO_CHANNEL_CONTENT; } } From 39952e6c0f6d6a6040dc56831c6862a2d298bc79 Mon Sep 17 00:00:00 2001 From: Marco Chen Date: Mon, 25 Feb 2013 16:55:24 +0800 Subject: [PATCH 14/64] Bug 836655 - Modify unit test for adapting change in this bug. r=baku --- .../tests/TestAudioChannelService.cpp | 274 +++++++++++------- 1 file changed, 175 insertions(+), 99 deletions(-) diff --git a/dom/audiochannel/tests/TestAudioChannelService.cpp b/dom/audiochannel/tests/TestAudioChannelService.cpp index d03e67e4a30..604a174a88b 100644 --- a/dom/audiochannel/tests/TestAudioChannelService.cpp +++ b/dom/audiochannel/tests/TestAudioChannelService.cpp @@ -1,6 +1,11 @@ /* 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/. */ +#ifdef XP_WIN +#include +#else +#include +#endif #include "TestHarness.h" @@ -19,19 +24,25 @@ using namespace mozilla::dom; -class Agent +class Agent : public nsIAudioChannelAgentCallback { public: + NS_DECL_ISUPPORTS + Agent(AudioChannelType aType) : mType(aType) + , mWaitCallback(false) , mRegistered(false) + , mCanPlay(false) { mAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1"); } + virtual ~Agent() {} + nsresult Init() { - nsresult rv = mAgent->Init(mType, nullptr); + nsresult rv = mAgent->Init(mType, this); NS_ENSURE_SUCCESS(rv, rv); return mAgent->SetVisibilityState(false); @@ -50,26 +61,74 @@ public: nsresult StopPlaying() { mRegistered = false; + int loop = 0; + while (mWaitCallback) { + #ifdef XP_WIN + Sleep(1000); + #else + sleep(1); + #endif + if (loop++ == 5) { + TEST_ENSURE_BASE(false, "StopPlaying timeout"); + } + } return mAgent->StopPlaying(); } + nsresult SetVisibilityState(bool visible) + { + if (mRegistered) { + mWaitCallback = true; + } + return mAgent->SetVisibilityState(visible); + } + + NS_IMETHODIMP CanPlayChanged(bool canPlay) + { + mCanPlay = canPlay; + mWaitCallback = false; + return NS_OK; + } + + nsresult GetCanPlay(bool *_ret) + { + int loop = 0; + while (mWaitCallback) { + #ifdef XP_WIN + Sleep(1000); + #else + sleep(1); + #endif + if (loop++ == 5) { + TEST_ENSURE_BASE(false, "GetCanPlay timeout"); + } + } + *_ret = mCanPlay; + return NS_OK; + } + nsCOMPtr mAgent; AudioChannelType mType; + bool mWaitCallback; bool mRegistered; + bool mCanPlay; }; +NS_IMPL_ISUPPORTS1(Agent, nsIAudioChannelAgentCallback) + nsresult TestDoubleStartPlaying() { - Agent agent(AUDIO_CHANNEL_NORMAL); - nsresult rv = agent.Init(); + nsCOMPtr agent = new Agent(AUDIO_CHANNEL_NORMAL); + + nsresult rv = agent->Init(); NS_ENSURE_SUCCESS(rv, rv); bool playing; - rv = agent.mAgent->StartPlaying(&playing); + rv = agent->mAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); - rv = agent.mAgent->StartPlaying(&playing); + rv = agent->mAgent->StartPlaying(&playing); TEST_ENSURE_BASE(NS_FAILED(rv), "Test0: StartPlaying calling twice should return error"); return NS_OK; @@ -78,19 +137,19 @@ TestDoubleStartPlaying() nsresult TestOneNormalChannel() { - Agent agent(AUDIO_CHANNEL_NORMAL); - nsresult rv = agent.Init(); + nsCOMPtr agent = new Agent(AUDIO_CHANNEL_NORMAL); + nsresult rv = agent->Init(); NS_ENSURE_SUCCESS(rv, rv); bool playing; - rv = agent.StartPlaying(&playing); + rv = agent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(!playing, "Test1: A normal channel unvisible agent must not be playing"); - rv = agent.mAgent->SetVisibilityState(true); + rv = agent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); - rv = agent.StartPlaying(&playing); + rv = agent->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test1: A normal channel visible agent should be playing"); @@ -100,34 +159,34 @@ TestOneNormalChannel() nsresult TestTwoNormalChannels() { - Agent agent1(AUDIO_CHANNEL_NORMAL); - nsresult rv = agent1.Init(); + nsCOMPtr agent1 = new Agent(AUDIO_CHANNEL_NORMAL); + nsresult rv = agent1->Init(); NS_ENSURE_SUCCESS(rv, rv); - Agent agent2(AUDIO_CHANNEL_NORMAL); - rv = agent2.Init(); + nsCOMPtr agent2 = new Agent(AUDIO_CHANNEL_NORMAL); + rv = agent2->Init(); NS_ENSURE_SUCCESS(rv, rv); bool playing; - rv = agent1.StartPlaying(&playing); + rv = agent1->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(!playing, "Test2: A normal channel unvisible agent1 must not be playing"); - rv = agent2.StartPlaying(&playing); + rv = agent2->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(!playing, "Test2: A normal channel unvisible agent2 must not be playing"); - rv = agent1.mAgent->SetVisibilityState(true); + rv = agent1->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); - rv = agent2.mAgent->SetVisibilityState(true); + rv = agent2->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); - rv = agent1.StartPlaying(&playing); + rv = agent1->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test2: A normal channel visible agent1 should be playing"); - rv = agent2.StartPlaying(&playing); + rv = agent2->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test2: A normal channel visible agent2 should be playing"); @@ -137,213 +196,230 @@ TestTwoNormalChannels() nsresult TestContentChannels() { - Agent agent1(AUDIO_CHANNEL_CONTENT); - nsresult rv = agent1.Init(); + nsCOMPtr agent1 = new Agent(AUDIO_CHANNEL_CONTENT); + nsresult rv = agent1->Init(); NS_ENSURE_SUCCESS(rv, rv); - Agent agent2(AUDIO_CHANNEL_CONTENT); - rv = agent2.Init(); + nsCOMPtr agent2 = new Agent(AUDIO_CHANNEL_CONTENT); + rv = agent2->Init(); NS_ENSURE_SUCCESS(rv, rv); - rv = agent1.mAgent->SetVisibilityState(true); + /* All content channels in the foreground can be allowed to play */ + rv = agent1->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); - rv = agent2.mAgent->SetVisibilityState(true); + rv = agent2->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); bool playing; - rv = agent1.StartPlaying(&playing); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent1 should be playing"); - - rv = agent2.StartPlaying(&playing); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing"); - - rv = agent1.mAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent2.mAgent->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1.StartPlaying(&playing); + rv = agent1->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent1 should be playing"); - rv = agent2.StartPlaying(&playing); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing"); - - rv = agent1.mAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent2.mAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1.StartPlaying(&playing); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent1 should be playing"); - - rv = agent2.StartPlaying(&playing); + rv = agent2->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test3: A content channel visible agent2 should be playing"); + /* Test the transition state of one content channel tried to set non-visible + * state first when app is going to background. */ + rv = agent1->SetVisibilityState(false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = agent1->GetCanPlay(&playing); + NS_ENSURE_SUCCESS(rv, rv); + TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent1 should be playing from foreground to background"); + + /* Test all content channels set non-visible already */ + rv = agent2->SetVisibilityState(false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = agent2->GetCanPlay(&playing); + NS_ENSURE_SUCCESS(rv, rv); + TEST_ENSURE_BASE(playing, "Test3: A content channel unvisible agent2 should be playing from foreground to background"); + + /* Clear the content channels & mActiveContentChildIDs in AudioChannelService. + * If agent stop playing in the background, we will reserve it's childID in + * mActiveContentChildIDs, then it can allow to play next song. So we set agents + * to foreground first then stopping to play */ + rv = agent1->SetVisibilityState(true); + NS_ENSURE_SUCCESS(rv, rv); + rv = agent2->SetVisibilityState(true); + NS_ENSURE_SUCCESS(rv, rv); + rv = agent1->StopPlaying(); + NS_ENSURE_SUCCESS(rv, rv); + rv = agent2->StopPlaying(); + NS_ENSURE_SUCCESS(rv, rv); + + /* Test that content channels can't be allow to play when they starts from the background state */ + rv = agent1->SetVisibilityState(false); + NS_ENSURE_SUCCESS(rv, rv); + rv = agent2->SetVisibilityState(false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = agent1->StartPlaying(&playing); + NS_ENSURE_SUCCESS(rv, rv); + TEST_ENSURE_BASE(!playing, "Test3: A content channel unvisible agent1 must not be playing from background state"); + + rv = agent2->StartPlaying(&playing); + NS_ENSURE_SUCCESS(rv, rv); + TEST_ENSURE_BASE(!playing, "Test3: A content channel unvisible agent2 must not be playing from background state"); + return rv; } nsresult TestPriorities() { - Agent normalAgent(AUDIO_CHANNEL_NORMAL); - nsresult rv = normalAgent.Init(); + nsCOMPtr normalAgent = new Agent(AUDIO_CHANNEL_NORMAL); + nsresult rv = normalAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); - Agent contentAgent(AUDIO_CHANNEL_CONTENT); - rv = contentAgent.Init(); + nsCOMPtr contentAgent = new Agent(AUDIO_CHANNEL_CONTENT); + rv = contentAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); - Agent notificationAgent(AUDIO_CHANNEL_NOTIFICATION); - rv = notificationAgent.Init(); + nsCOMPtr notificationAgent = new Agent(AUDIO_CHANNEL_NOTIFICATION); + rv = notificationAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); - Agent alarmAgent(AUDIO_CHANNEL_ALARM); - rv = alarmAgent.Init(); + nsCOMPtr alarmAgent = new Agent(AUDIO_CHANNEL_ALARM); + rv = alarmAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); - Agent telephonyAgent(AUDIO_CHANNEL_TELEPHONY); - rv = telephonyAgent.Init(); + nsCOMPtr telephonyAgent = new Agent(AUDIO_CHANNEL_TELEPHONY); + rv = telephonyAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); - Agent ringerAgent(AUDIO_CHANNEL_RINGER); - rv = ringerAgent.Init(); + nsCOMPtr ringerAgent = new Agent(AUDIO_CHANNEL_RINGER); + rv = ringerAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); - Agent pNotificationAgent(AUDIO_CHANNEL_PUBLICNOTIFICATION); - rv = pNotificationAgent.Init(); + nsCOMPtr pNotificationAgent = new Agent(AUDIO_CHANNEL_PUBLICNOTIFICATION); + rv = pNotificationAgent->Init(); NS_ENSURE_SUCCESS(rv, rv); bool playing; // Normal should not be playing because not visible. - rv = normalAgent.StartPlaying(&playing); + rv = normalAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(!playing, "Test4: A normal channel unvisible agent should not be playing"); + TEST_ENSURE_BASE(!playing, "Test4: A normal channel unvisible agent must not be playing"); // Content should be playing. - rv = contentAgent.StartPlaying(&playing); + rv = contentAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playing, "Test4: A content channel unvisible agent should be playing"); + TEST_ENSURE_BASE(!playing, "Test4: A content channel unvisible agent must not be playing from background state"); // Notification should be playing. - rv = notificationAgent.StartPlaying(&playing); + rv = notificationAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: An notification channel unvisible agent should be playing"); // Now content should be not playing because of the notification playing. - rv = contentAgent.StartPlaying(&playing); + rv = contentAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(!playing, "Test4: A content channel unvisible agent should not be playing when notification channel is playing"); // Adding an alarm. - rv = alarmAgent.StartPlaying(&playing); + rv = alarmAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: An alarm channel unvisible agent should be playing"); // Now notification should be not playing because of the alarm playing. - rv = notificationAgent.StartPlaying(&playing); + rv = notificationAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(!playing, "Test4: A notification channel unvisible agent should not be playing when an alarm is playing"); // Adding an telephony. - rv = telephonyAgent.StartPlaying(&playing); + rv = telephonyAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: An telephony channel unvisible agent should be playing"); // Now alarm should be not playing because of the telephony playing. - rv = alarmAgent.StartPlaying(&playing); + rv = alarmAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(!playing, "Test4: A alarm channel unvisible agent should not be playing when a telephony is playing"); // Adding an ringer. - rv = ringerAgent.StartPlaying(&playing); + rv = ringerAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: An ringer channel unvisible agent should be playing"); // Now telephony should be not playing because of the ringer playing. - rv = telephonyAgent.StartPlaying(&playing); + rv = telephonyAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(!playing, "Test4: A telephony channel unvisible agent should not be playing when a riger is playing"); // Adding an pNotification. - rv = pNotificationAgent.StartPlaying(&playing); + rv = pNotificationAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: An pNotification channel unvisible agent should be playing"); // Now ringer should be not playing because of the public notification playing. - rv = ringerAgent.StartPlaying(&playing); + rv = ringerAgent->StartPlaying(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(!playing, "Test4: A ringer channel unvisible agent should not be playing when a public notification is playing"); // Settings visible the normal channel. - rv = normalAgent.mAgent->SetVisibilityState(true); + rv = normalAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); // Normal should be playing because visible. - rv = normalAgent.StartPlaying(&playing); + rv = normalAgent->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: A normal channel visible agent should be playing"); // Settings visible the content channel. - rv = contentAgent.mAgent->SetVisibilityState(true); + rv = contentAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); // Content should be playing because visible. - rv = contentAgent.StartPlaying(&playing); + rv = contentAgent->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: A content channel visible agent should be playing"); // Settings visible the notification channel. - rv = notificationAgent.mAgent->SetVisibilityState(true); + rv = notificationAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); // Notification should be playing because visible. - rv = notificationAgent.StartPlaying(&playing); + rv = notificationAgent->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: A notification channel visible agent should be playing"); // Settings visible the alarm channel. - rv = alarmAgent.mAgent->SetVisibilityState(true); + rv = alarmAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); // Alarm should be playing because visible. - rv = alarmAgent.StartPlaying(&playing); + rv = alarmAgent->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: A alarm channel visible agent should be playing"); // Settings visible the telephony channel. - rv = telephonyAgent.mAgent->SetVisibilityState(true); + rv = telephonyAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); // Telephony should be playing because visible. - rv = telephonyAgent.StartPlaying(&playing); + rv = telephonyAgent->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: A telephony channel visible agent should be playing"); // Settings visible the ringer channel. - rv = ringerAgent.mAgent->SetVisibilityState(true); + rv = ringerAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); // Ringer should be playing because visible. - rv = ringerAgent.StartPlaying(&playing); + rv = ringerAgent->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: A ringer channel visible agent should be playing"); // Settings visible the pNotification channel. - rv = pNotificationAgent.mAgent->SetVisibilityState(true); + rv = pNotificationAgent->SetVisibilityState(true); NS_ENSURE_SUCCESS(rv, rv); // pNotification should be playing because visible. - rv = pNotificationAgent.StartPlaying(&playing); + rv = pNotificationAgent->GetCanPlay(&playing); NS_ENSURE_SUCCESS(rv, rv); TEST_ENSURE_BASE(playing, "Test4: A pNotification channel visible agent should be playing"); From 0dfdf59d242f586b78cda0b49e34d226611770c7 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Mon, 25 Feb 2013 15:56:19 +0000 Subject: [PATCH 15/64] bug 844454 - don't try to get a Unicode decoder for an empty charset name. r=hsivonen --- content/base/src/nsScriptLoader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp index 4abc02a6c09..3c52647aca2 100644 --- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -1025,7 +1025,8 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData, if (!unicodeDecoder && aChannel && - NS_SUCCEEDED(aChannel->GetContentCharset(charset))) { + NS_SUCCEEDED(aChannel->GetContentCharset(charset)) && + !charset.IsEmpty()) { charsetConv->GetUnicodeDecoder(charset.get(), getter_AddRefs(unicodeDecoder)); } From 46779ca2d99ed56a5bd0e2566d55e6a0e4e29e06 Mon Sep 17 00:00:00 2001 From: EKR Date: Tue, 19 Feb 2013 09:01:12 -0800 Subject: [PATCH 16/64] Bug 840508 - assert that nr_timer is called in the STS thread. r=abr --- media/mtransport/nr_timer.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/media/mtransport/nr_timer.cpp b/media/mtransport/nr_timer.cpp index 80e83f4d23a..4517ddb126d 100644 --- a/media/mtransport/nr_timer.cpp +++ b/media/mtransport/nr_timer.cpp @@ -53,9 +53,11 @@ #include "nsCOMPtr.h" #include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" #include "nsIEventTarget.h" #include "nsITimer.h" #include "nsNetCID.h" +#include "runnable_utils.h" extern "C" { #include "nr_api.h" @@ -105,12 +107,25 @@ NS_IMETHODIMP nrappkitTimerCallback::Notify(nsITimer *timer) { using namespace mozilla; +// These timers must only be used from the STS thread. +// This function is a helper that enforces that. +static void CheckSTSThread() { + nsresult rv; + + nsCOMPtr sts_thread; + + sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + MOZ_ASSERT(NS_SUCCEEDED(rv)); + ASSERT_ON_THREAD(sts_thread); +} + int NR_async_timer_set(int timeout, NR_async_cb cb, void *arg, char *func, int l, void **handle) { nsresult rv; + CheckSTSThread(); nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); - if (NS_FAILED(rv)) { return(R_FAILED); } @@ -134,10 +149,14 @@ int NR_async_timer_set(int timeout, NR_async_cb cb, void *arg, char *func, } int NR_async_schedule(NR_async_cb cb, void *arg, char *func, int l) { + // No need to check the thread because we check it next in the + // timer set. return NR_async_timer_set(0, cb, arg, func, l, nullptr); } int NR_async_timer_cancel(void *handle) { + CheckSTSThread(); + if (!handle) return 0; From 5a5bc20b4543704b7640be5ebbe81a501aa0d93f Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 25 Feb 2013 11:33:29 -0500 Subject: [PATCH 17/64] Bug 831144 - Add Android XBL key bindings; r=neil --HG-- rename : content/xbl/builtin/unix/Makefile.in => content/xbl/builtin/android/Makefile.in rename : content/xbl/builtin/unix/jar.mn => content/xbl/builtin/android/jar.mn --- content/xbl/builtin/Makefile.in | 4 + content/xbl/builtin/android/Makefile.in | 13 ++ content/xbl/builtin/android/jar.mn | 6 + .../builtin/android/platformHTMLBindings.xml | 170 ++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 content/xbl/builtin/android/Makefile.in create mode 100644 content/xbl/builtin/android/jar.mn create mode 100644 content/xbl/builtin/android/platformHTMLBindings.xml diff --git a/content/xbl/builtin/Makefile.in b/content/xbl/builtin/Makefile.in index c3a670f7571..928da3ccc8e 100644 --- a/content/xbl/builtin/Makefile.in +++ b/content/xbl/builtin/Makefile.in @@ -16,6 +16,9 @@ else ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) DIRS = mac else +ifeq (android,$(MOZ_WIDGET_TOOLKIT)) +DIRS = android +else ifneq (,$(filter qt gtk2,$(MOZ_WIDGET_TOOLKIT))) DIRS = unix else @@ -23,5 +26,6 @@ DIRS = emacs endif endif endif +endif include $(topsrcdir)/config/rules.mk diff --git a/content/xbl/builtin/android/Makefile.in b/content/xbl/builtin/android/Makefile.in new file mode 100644 index 00000000000..955c2ba8abc --- /dev/null +++ b/content/xbl/builtin/android/Makefile.in @@ -0,0 +1,13 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/rules.mk diff --git a/content/xbl/builtin/android/jar.mn b/content/xbl/builtin/android/jar.mn new file mode 100644 index 00000000000..9f05c2dd6cd --- /dev/null +++ b/content/xbl/builtin/android/jar.mn @@ -0,0 +1,6 @@ +# 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/. + +toolkit.jar: +* content/global/platformHTMLBindings.xml (platformHTMLBindings.xml) diff --git a/content/xbl/builtin/android/platformHTMLBindings.xml b/content/xbl/builtin/android/platformHTMLBindings.xml new file mode 100644 index 00000000000..a67ce72ab4a --- /dev/null +++ b/content/xbl/builtin/android/platformHTMLBindings.xml @@ -0,0 +1,170 @@ + + + + + + + + +#include ../input-fields-base.inc + + + + + + + + + + + + + + + + + + + + + + + + + +#include ../textareas-base.inc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#include ../browser-base.inc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#include ../editor-base.inc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3ea987130f130fd6b6b0bb4d097c1b05830c3400 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 25 Feb 2013 11:33:30 -0500 Subject: [PATCH 18/64] Bug 831144 - Properly pass meta states to Gecko; r=cpeterson --- mobile/android/base/GeckoEvent.java | 4 ++++ widget/android/nsWindow.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 994c9e7688b..2c3ed6ea7c3 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -194,6 +194,10 @@ public class GeckoEvent { mFlags = k.getFlags(); mKeyCode = k.getKeyCode(); mUnicodeChar = k.getUnicodeChar(); + if (mUnicodeChar == 0) { + // e.g. for Ctrl+A, Android returns 0, but Gecko expects 'a' as mUnicodeChar + mUnicodeChar = k.getUnicodeChar(0); + } mRepeatCount = k.getRepeatCount(); mCharacters = k.getCharacters(); mDomKeyLocation = isJoystickButton(mKeyCode) ? DOM_KEY_LOCATION_JOYSTICK : DOM_KEY_LOCATION_MOBILE; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index e7dfb5c4702..c24c0cc312d 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1501,10 +1501,10 @@ nsWindow::InitKeyEvent(nsKeyEvent& event, AndroidGeckoEvent& key, event.pluginEvent = pluginEvent; } - event.InitBasicModifiers(gMenu, + event.InitBasicModifiers(gMenu || key.IsCtrlPressed(), key.IsAltPressed(), key.IsShiftPressed(), - false); + key.IsMetaPressed()); event.location = key.DomKeyLocation(); event.time = key.Time(); @@ -1598,6 +1598,8 @@ nsWindow::OnKeyEvent(AndroidGeckoEvent *ae) case AndroidKeyEvent::KEYCODE_SHIFT_RIGHT: case AndroidKeyEvent::KEYCODE_ALT_LEFT: case AndroidKeyEvent::KEYCODE_ALT_RIGHT: + case AndroidKeyEvent::KEYCODE_CTRL_LEFT: + case AndroidKeyEvent::KEYCODE_CTRL_RIGHT: firePress = false; break; case AndroidKeyEvent::KEYCODE_BACK: From defd4224684d035659695554483a9cc9cc80806d Mon Sep 17 00:00:00 2001 From: "Adam Roach [:abr]" Date: Mon, 25 Feb 2013 11:12:33 -0600 Subject: [PATCH 19/64] Bug 843595 - Null checks on pair dtor r=ekr --- .../nICEr/src/ice/ice_candidate_pair.c | 8 ++- .../third_party/nICEr/src/ice/ice_peer_ctx.c | 4 +- .../third_party/nICEr/src/net/nr_socket.c | 5 +- .../signaling/test/signaling_unittests.cpp | 68 +++++++++++++++++-- 4 files changed, 76 insertions(+), 9 deletions(-) diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c index 547d354d60c..a5100b9775b 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c @@ -172,9 +172,11 @@ int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp) RFREE(pair->as_string); RFREE(pair->foundation); nr_ice_socket_deregister(pair->local->isock,pair->stun_client_handle); - RFREE(pair->stun_client->params.ice_binding_request.username); - RFREE(pair->stun_client->params.ice_binding_request.password.data); - nr_stun_client_ctx_destroy(&pair->stun_client); + if (pair->stun_client) { + RFREE(pair->stun_client->params.ice_binding_request.username); + RFREE(pair->stun_client->params.ice_binding_request.password.data); + nr_stun_client_ctx_destroy(&pair->stun_client); + } RFREE(pair->r2l_user); RFREE(pair->r2l_pwd.data); diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c index 87b0189c164..b5e71b11d08 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c @@ -312,7 +312,9 @@ static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg) STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry); nr_ice_media_stream_destroy(&str1); } - STAILQ_REMOVE(&pctx->ctx->peers, pctx, nr_ice_peer_ctx_, entry); + assert(pctx->ctx); + if (pctx->ctx) + STAILQ_REMOVE(&pctx->ctx->peers, pctx, nr_ice_peer_ctx_, entry); RFREE(pctx); } diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket.c b/media/mtransport/third_party/nICEr/src/net/nr_socket.c index 5ec27ad25fc..7f37e582021 100644 --- a/media/mtransport/third_party/nICEr/src/net/nr_socket.c +++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.c @@ -34,6 +34,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. static char *RCSSTRING __UNUSED__="$Id: nr_socket.c,v 1.2 2008/04/28 17:59:02 ekr Exp $"; +#include #include #include "nr_socket.h" @@ -65,7 +66,9 @@ int nr_socket_destroy(nr_socket **sockp) sock=*sockp; *sockp=0; - sock->vtbl->destroy(&sock->obj); + assert(sock->vtbl); + if (sock->vtbl) + sock->vtbl->destroy(&sock->obj); RFREE(sock); diff --git a/media/webrtc/signaling/test/signaling_unittests.cpp b/media/webrtc/signaling/test/signaling_unittests.cpp index 4863eb1c0ff..547db450b7e 100644 --- a/media/webrtc/signaling/test/signaling_unittests.cpp +++ b/media/webrtc/signaling/test/signaling_unittests.cpp @@ -746,20 +746,26 @@ void CreateAnswer(sipcc::MediaConstraints& constraints, std::string offer, offer_ = pObserver->lastString; } - void SetRemote(TestObserver::Action action, std::string remote) { + void SetRemote(TestObserver::Action action, std::string remote, + bool ignoreError = false) { pObserver->state = TestObserver::stateNoResponse; ASSERT_EQ(pc->SetRemoteDescription(action, remote.c_str()), NS_OK); ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse, kDefaultTimeout); - ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess); + if (!ignoreError) { + ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess); + } } - void SetLocal(TestObserver::Action action, std::string local) { + void SetLocal(TestObserver::Action action, std::string local, + bool ignoreError = false) { pObserver->state = TestObserver::stateNoResponse; ASSERT_EQ(pc->SetLocalDescription(action, local.c_str()), NS_OK); ASSERT_TRUE_WAIT(pObserver->state != TestObserver::stateNoResponse, kDefaultTimeout); - ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess); + if (!ignoreError) { + ASSERT_TRUE(pObserver->state == TestObserver::stateSuccess); + } } void DoTrickleIce(ParsedSDP &sdp) { @@ -1934,6 +1940,60 @@ TEST_F(SignalingAgentTest, CreateUntilFailThenWait) { PR_Sleep(10000); // Wait to see if we crash } +/* + * Test for Bug 843595 + */ +TEST_F(SignalingTest, missingUfrag) +{ + sipcc::MediaConstraints constraints; + std::string offer = + "v=0\r\n" + "o=Mozilla-SIPUA 2208 0 IN IP4 0.0.0.0\r\n" + "s=SIP Call\r\n" + "t=0 0\r\n" + "a=ice-pwd:4450d5a4a5f097855c16fa079893be18\r\n" + "a=fingerprint:sha-256 23:9A:2E:43:94:42:CF:46:68:FC:62:F9:F4:48:61:DB:" + "2F:8C:C9:FF:6B:25:54:9D:41:09:EF:83:A8:19:FC:B6\r\n" + "m=audio 56187 RTP/SAVPF 109 0 8 101\r\n" + "c=IN IP4 77.9.79.167\r\n" + "a=rtpmap:109 opus/48000/2\r\n" + "a=ptime:20\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:101 telephone-event/8000\r\n" + "a=fmtp:101 0-15\r\n" + "a=sendrecv\r\n" + "a=candidate:0 1 UDP 2113601791 192.168.178.20 56187 typ host\r\n" + "a=candidate:1 1 UDP 1694236671 77.9.79.167 56187 typ srflx raddr " + "192.168.178.20 rport 56187\r\n" + "a=candidate:0 2 UDP 2113601790 192.168.178.20 52955 typ host\r\n" + "a=candidate:1 2 UDP 1694236670 77.9.79.167 52955 typ srflx raddr " + "192.168.178.20 rport 52955\r\n" + "m=video 49929 RTP/SAVPF 120\r\n" + "c=IN IP4 77.9.79.167\r\n" + "a=rtpmap:120 VP8/90000\r\n" + "a=recvonly\r\n" + "a=candidate:0 1 UDP 2113601791 192.168.178.20 49929 typ host\r\n" + "a=candidate:1 1 UDP 1694236671 77.9.79.167 49929 typ srflx raddr " + "192.168.178.20 rport 49929\r\n" + "a=candidate:0 2 UDP 2113601790 192.168.178.20 50769 typ host\r\n" + "a=candidate:1 2 UDP 1694236670 77.9.79.167 50769 typ srflx raddr " + "192.168.178.20 rport 50769\r\n" + "m=application 54054 SCTP/DTLS 5000 \r\n" + "c=IN IP4 77.9.79.167\r\n" + "a=fmtp:HuRUu]Dtcl\\zM,7(OmEU%O$gU]x/z\tD protocol=webrtc-datachannel;" + "streams=16\r\n" + "a=sendrecv\r\n"; + + a1_.SetLocal(TestObserver::OFFER, offer, true); + a2_.SetRemote(TestObserver::OFFER, offer, true); + a2_.CreateAnswer(constraints, offer, OFFER_AV | ANSWER_AV); + a2_.SetLocal(TestObserver::ANSWER, a2_.answer(), true); + a1_.SetRemote(TestObserver::ANSWER, a2_.answer(), true); + // We don't check anything in particular for success here -- simply not + // crashing by now is enough to declare success. +} + } // End namespace test. int main(int argc, char **argv) { From f6564cf84f4ca24b805707b90ca4398a0f468f5f Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 25 Feb 2013 12:35:33 -0500 Subject: [PATCH 20/64] Backed out changesets 77a94dc888c9 and 3f6e6912e71a (bug 831144) for Android mochitest failures on a CLOSED TREE. --- content/xbl/builtin/Makefile.in | 4 - content/xbl/builtin/android/Makefile.in | 13 -- content/xbl/builtin/android/jar.mn | 6 - .../builtin/android/platformHTMLBindings.xml | 170 ------------------ mobile/android/base/GeckoEvent.java | 4 - widget/android/nsWindow.cpp | 6 +- 6 files changed, 2 insertions(+), 201 deletions(-) delete mode 100644 content/xbl/builtin/android/Makefile.in delete mode 100644 content/xbl/builtin/android/jar.mn delete mode 100644 content/xbl/builtin/android/platformHTMLBindings.xml diff --git a/content/xbl/builtin/Makefile.in b/content/xbl/builtin/Makefile.in index 928da3ccc8e..c3a670f7571 100644 --- a/content/xbl/builtin/Makefile.in +++ b/content/xbl/builtin/Makefile.in @@ -16,9 +16,6 @@ else ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) DIRS = mac else -ifeq (android,$(MOZ_WIDGET_TOOLKIT)) -DIRS = android -else ifneq (,$(filter qt gtk2,$(MOZ_WIDGET_TOOLKIT))) DIRS = unix else @@ -26,6 +23,5 @@ DIRS = emacs endif endif endif -endif include $(topsrcdir)/config/rules.mk diff --git a/content/xbl/builtin/android/Makefile.in b/content/xbl/builtin/android/Makefile.in deleted file mode 100644 index 955c2ba8abc..00000000000 --- a/content/xbl/builtin/android/Makefile.in +++ /dev/null @@ -1,13 +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/. - -DEPTH = @DEPTH@ -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -include $(topsrcdir)/config/rules.mk diff --git a/content/xbl/builtin/android/jar.mn b/content/xbl/builtin/android/jar.mn deleted file mode 100644 index 9f05c2dd6cd..00000000000 --- a/content/xbl/builtin/android/jar.mn +++ /dev/null @@ -1,6 +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/. - -toolkit.jar: -* content/global/platformHTMLBindings.xml (platformHTMLBindings.xml) diff --git a/content/xbl/builtin/android/platformHTMLBindings.xml b/content/xbl/builtin/android/platformHTMLBindings.xml deleted file mode 100644 index a67ce72ab4a..00000000000 --- a/content/xbl/builtin/android/platformHTMLBindings.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - -#include ../input-fields-base.inc - - - - - - - - - - - - - - - - - - - - - - - - - -#include ../textareas-base.inc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#include ../browser-base.inc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#include ../editor-base.inc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 2c3ed6ea7c3..994c9e7688b 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -194,10 +194,6 @@ public class GeckoEvent { mFlags = k.getFlags(); mKeyCode = k.getKeyCode(); mUnicodeChar = k.getUnicodeChar(); - if (mUnicodeChar == 0) { - // e.g. for Ctrl+A, Android returns 0, but Gecko expects 'a' as mUnicodeChar - mUnicodeChar = k.getUnicodeChar(0); - } mRepeatCount = k.getRepeatCount(); mCharacters = k.getCharacters(); mDomKeyLocation = isJoystickButton(mKeyCode) ? DOM_KEY_LOCATION_JOYSTICK : DOM_KEY_LOCATION_MOBILE; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index c24c0cc312d..e7dfb5c4702 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1501,10 +1501,10 @@ nsWindow::InitKeyEvent(nsKeyEvent& event, AndroidGeckoEvent& key, event.pluginEvent = pluginEvent; } - event.InitBasicModifiers(gMenu || key.IsCtrlPressed(), + event.InitBasicModifiers(gMenu, key.IsAltPressed(), key.IsShiftPressed(), - key.IsMetaPressed()); + false); event.location = key.DomKeyLocation(); event.time = key.Time(); @@ -1598,8 +1598,6 @@ nsWindow::OnKeyEvent(AndroidGeckoEvent *ae) case AndroidKeyEvent::KEYCODE_SHIFT_RIGHT: case AndroidKeyEvent::KEYCODE_ALT_LEFT: case AndroidKeyEvent::KEYCODE_ALT_RIGHT: - case AndroidKeyEvent::KEYCODE_CTRL_LEFT: - case AndroidKeyEvent::KEYCODE_CTRL_RIGHT: firePress = false; break; case AndroidKeyEvent::KEYCODE_BACK: From 36a231a8ac620443a58d98756966434d8e037ade Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Sun, 24 Feb 2013 12:36:44 -0800 Subject: [PATCH 21/64] Bug 784812: Implement real dependencies for WebIDL bindings. r=bz,ted --- dom/bindings/BindingGen.py | 31 +++-- dom/bindings/Codegen.py | 38 ++++++ dom/bindings/Makefile.in | 42 ++++++- dom/bindings/parser/WebIDL.py | 119 +++++++++++++++++- .../parser/tests/test_nullable_equivalency.py | 2 +- dom/bindings/test/Makefile.in | 1 - 6 files changed, 213 insertions(+), 20 deletions(-) diff --git a/dom/bindings/BindingGen.py b/dom/bindings/BindingGen.py index 3c8c30d7e1b..07cf5c9958a 100644 --- a/dom/bindings/BindingGen.py +++ b/dom/bindings/BindingGen.py @@ -5,46 +5,53 @@ import os import cPickle from Configuration import Configuration -from Codegen import CGBindingRoot, replaceFileIfChanged +from Codegen import CGBindingRoot -def generate_binding_header(config, outputprefix, webidlfile): +def generate_binding_header(config, outputprefix, srcprefix, webidlfile): """ |config| Is the configuration object. |outputprefix| is a prefix to use for the header guards and filename. """ filename = outputprefix + ".h" + depsname = ".deps/" + filename + ".pp" root = CGBindingRoot(config, outputprefix, webidlfile) - if replaceFileIfChanged(filename, root.declare()): - print "Generating binding header: %s" % (filename) + with open(filename, 'wb') as f: + f.write(root.declare()) + with open(depsname, 'wb') as f: + f.write("\n".join(filename + ": " + os.path.join(srcprefix, x) for x in root.deps())) -def generate_binding_cpp(config, outputprefix, webidlfile): +def generate_binding_cpp(config, outputprefix, srcprefix, webidlfile): """ |config| Is the configuration object. |outputprefix| is a prefix to use for the header guards and filename. """ filename = outputprefix + ".cpp" + depsname = ".deps/" + filename + ".pp" root = CGBindingRoot(config, outputprefix, webidlfile) - if replaceFileIfChanged(filename, root.define()): - print "Generating binding implementation: %s" % (filename) + with open(filename, 'wb') as f: + f.write(root.define()) + with open(depsname, 'wb') as f: + f.write("\n".join(filename + ": " + os.path.join(srcprefix, x) for x in root.deps())) def main(): # Parse arguments. from optparse import OptionParser - usagestring = "usage: %prog [header|cpp] configFile outputPrefix webIDLFile" + usagestring = "usage: %prog [header|cpp] configFile outputPrefix srcPrefix webIDLFile" o = OptionParser(usage=usagestring) o.add_option("--verbose-errors", action='store_true', default=False, help="When an error happens, display the Python traceback.") (options, args) = o.parse_args() - if len(args) != 4 or (args[0] != "header" and args[0] != "cpp"): + if len(args) != 5 or (args[0] != "header" and args[0] != "cpp"): o.error(usagestring) buildTarget = args[0] configFile = os.path.normpath(args[1]) outputPrefix = args[2] - webIDLFile = os.path.normpath(args[3]) + srcPrefix = os.path.normpath(args[3]) + webIDLFile = os.path.normpath(args[4]) # Load the parsing results f = open('ParserResults.pkl', 'rb') @@ -56,9 +63,9 @@ def main(): # Generate the prototype classes. if buildTarget == "header": - generate_binding_header(config, outputPrefix, webIDLFile); + generate_binding_header(config, outputPrefix, srcPrefix, webIDLFile); elif buildTarget == "cpp": - generate_binding_cpp(config, outputPrefix, webIDLFile); + generate_binding_cpp(config, outputPrefix, srcPrefix, webIDLFile); else: assert False # not reached diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index bd446138adc..55a3a5e07e4 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -59,6 +59,9 @@ class CGThing(): def define(self): """Produce code for a cpp file.""" assert(False) # Override me! + def deps(self): + """Produce the deps for a pp file""" + assert(False) # Override me! class CGNativePropertyHooks(CGThing): """ @@ -309,6 +312,13 @@ class CGList(CGThing): return self.join(child.declare() for child in self.children if child is not None) def define(self): return self.join(child.define() for child in self.children if child is not None) + def deps(self): + deps = set() + for child in self.children: + if child is None: + continue + deps = deps.union(child.deps()) + return deps class CGGeneric(CGThing): """ @@ -322,6 +332,8 @@ class CGGeneric(CGThing): return self.declareText def define(self): return self.defineText + def deps(self): + return set() # We'll want to insert the indent at the beginnings of lines, but we # don't want to indent empty lines. So only indent lines that have a @@ -387,6 +399,9 @@ class CGWrapper(CGThing): defn.replace("\n", "\n" + (" " * len(self.definePre)))) return self.definePre + defn + self.definePost + def deps(self): + return self.child.deps() + class CGIfWrapper(CGWrapper): def __init__(self, child, condition): pre = CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", @@ -4853,6 +4868,9 @@ class CGEnum(CGThing): """ % (len(self.enum.values()) + 1, ",\n ".join(['{"' + val + '", ' + str(len(val)) + '}' for val in self.enum.values()])) + def deps(self): + return self.enum.getDeps() + def getUnionAccessorSignatureType(type, descriptorProvider): """ Returns the types that are used in the getter and setter signatures for @@ -5743,6 +5761,8 @@ class CGPrototypeTraitsClass(CGClass): templateArgs=templateArgs, templateSpecialization=templateSpecialization, enums=enums, typedefs=typedefs, isStruct=True) + def deps(self): + return set() class CGPrototypeIDMapClass(CGClass): def __init__(self, descriptor, indent=''): @@ -5754,6 +5774,8 @@ class CGPrototypeIDMapClass(CGClass): templateArgs=templateArgs, templateSpecialization=templateSpecialization, enums=enums, isStruct=True) + def deps(self): + return set() class CGClassForwardDeclare(CGThing): def __init__(self, name, isStruct=False): @@ -5766,6 +5788,8 @@ class CGClassForwardDeclare(CGThing): def define(self): # Header only return '' + def deps(self): + return set() class CGProxySpecialOperation(CGPerSignatureCall): """ @@ -6442,6 +6466,8 @@ class CGDescriptor(CGThing): assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject() + self._deps = descriptor.interface.getDeps() + cgThings = [] # These are set to true if at least one non-static # method/getter/setter exist on the interface. @@ -6578,6 +6604,8 @@ class CGDescriptor(CGThing): return self.cgRoot.declare() def define(self): return self.cgRoot.define() + def deps(self): + return self._deps class CGNamespacedEnum(CGThing): def __init__(self, namespace, enumName, names, values, comment=""): @@ -6782,6 +6810,9 @@ class CGDictionary(CGThing): "defineMembers": "\n\n".join(memberDefines) }) + def deps(self): + return self.dictionary.getDeps() + @staticmethod def makeDictionaryName(dictionary, workers): suffix = "Workers" if workers else "" @@ -7203,6 +7234,9 @@ class CGBindingRoot(CGThing): def define(self): return stripTrailingWhitespace(self.root.define()) + def deps(self): + return self.root.deps() + class CGNativeMember(ClassMethod): def __init__(self, descriptor, member, name, signature, extendedAttrs, breakAfter=True, passCxAsNeeded=True, visibility="public", @@ -7778,6 +7812,7 @@ class CGCallback(CGClass): def __init__(self, idlObject, descriptorProvider, baseName, methods, getters=[], setters=[]): self.baseName = baseName + self._deps = idlObject.getDeps() name = idlObject.identifier.name if descriptorProvider.workers: name += "Workers" @@ -7867,6 +7902,9 @@ class CGCallback(CGClass): body=bodyWithoutThis), method] + def deps(self): + return self._deps + class CGCallbackFunction(CGCallback): def __init__(self, callback, descriptorProvider): CGCallback.__init__(self, callback, descriptorProvider, diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index db84b09eccf..5898408e1de 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -75,7 +75,6 @@ EXPORTS_$(binding_include_path) = \ TypedArray.h \ UnionConversions.h \ UnionTypes.h \ - $(exported_binding_headers) \ $(NULL) LOCAL_INCLUDES += -I$(topsrcdir)/js/xpconnect/src \ @@ -97,8 +96,34 @@ LOCAL_INCLUDES += \ $(NULL) endif +# XXXkhuey this is a terrible hack to avoid blowing out the command line +ifneq (,$(filter-out all chrome default export realchrome tools clean clobber clobber_all distclean realclean,$(MAKECMDGOALS))) +$(shell echo "$(addsuffix .pp,$(binding_header_files))" > pp.list) +$(shell echo "$(addsuffix .pp,$(binding_cpp_files))" >> pp.list) + +# The script mddepend.pl checks the dependencies and writes to stdout +# one rule to force out-of-date objects. For example, +# foo.o boo.o: FORCE +# The script has an advantage over including the *.pp files directly +# because it handles the case when header files are removed from the build. +# 'make' would complain that there is no way to build missing headers. +ALL_PP_RESULTS = $(shell cat pp.list | $(PERL) $(BUILD_TOOLS)/mddepend.pl) +$(eval $(ALL_PP_RESULTS)) + +endif + +EXPORTS_GENERATED_FILES := $(exported_binding_headers) +EXPORTS_GENERATED_DEST := $(DIST)/include/$(binding_include_path) +EXPORTS_GENERATED_TARGET := webidl-export +INSTALL_TARGETS += EXPORTS_GENERATED + include $(topsrcdir)/config/rules.mk +# We need to create a separate target so we can ensure that the pickle is +# done before generating headers. +export:: ParserResults.pkl + $(MAKE) webidl-export + # If you change bindinggen_dependencies here, change it in # dom/bindings/test/Makefile.in too. bindinggen_dependencies := \ @@ -107,7 +132,6 @@ bindinggen_dependencies := \ Configuration.py \ Codegen.py \ parser/WebIDL.py \ - ParserResults.pkl \ $(GLOBAL_DEPS) \ $(NULL) @@ -129,20 +153,26 @@ $(test_webidl_files): %: $(srcdir)/test/% $(binding_header_files): %Binding.h: $(bindinggen_dependencies) \ %.webidl \ + $(call mkdir_deps,$(MDDEPDIR)) \ $(NULL) PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ $(PLY_INCLUDE) -I$(srcdir)/parser \ $(srcdir)/BindingGen.py header \ - $(srcdir)/Bindings.conf $*Binding \ + $(srcdir)/Bindings.conf \ + $*Binding \ + $(topsrcdir)/dom/webidl/ \ $*.webidl $(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \ %.webidl \ + $(call mkdir_deps,$(MDDEPDIR)) \ $(NULL) PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ $(PLY_INCLUDE) -I$(srcdir)/parser \ $(srcdir)/BindingGen.py cpp \ - $(srcdir)/Bindings.conf $*Binding \ + $(srcdir)/Bindings.conf \ + $*Binding \ + $(topsrcdir)/dom/webidl/ \ $*.webidl $(globalgen_targets): ParserResults.pkl @@ -195,4 +225,6 @@ GARBAGE += \ # don't have issues with .cpp files being compiled before we've generated the # headers they depend on. This is really only needed for the test files, since # the non-test headers are all exported above anyway. -export:: $(binding_header_files) +webidl-export:: $(binding_header_files) + +.PHONY: webidl-export diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 112c040973b..fa79a042e30 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -174,6 +174,38 @@ class IDLObject(object): def handleExtendedAttribute(self, attr): assert False # Override me! + def _getDependentObjects(self): + assert False # Override me! + + def getDeps(self, visited=None): + """ Return a set of files that this object depends on. If any of + these files are changed the parser needs to be rerun to regenerate + a new IDLObject. + + The visited argument is a set of all the objects already visited. + We must test to see if we are in it, and if so, do nothing. This + prevents infinite recursion.""" + + # NB: We can't use visited=set() above because the default value is + # evaluated when the def statement is evaluated, not when the function + # is executed, so there would be one set for all invocations. + if visited == None: + visited = set() + + if self in visited: + return set() + + visited.add(self) + + deps = set() + if self.filename() != "": + deps.add(self.filename()) + + for d in self._getDependentObjects(): + deps = deps.union(d.getDeps(visited)) + + return deps + class IDLScope(IDLObject): def __init__(self, location, parentScope, identifier): IDLObject.__init__(self, location) @@ -443,6 +475,9 @@ class IDLExternalInterface(IDLObjectWithIdentifier): def resolve(self, parentScope): pass + def _getDependentObjects(self): + return set() + class IDLInterface(IDLObjectWithScope): def __init__(self, location, parentScope, name, parent, members, isPartial): @@ -916,6 +951,13 @@ class IDLInterface(IDLObjectWithScope): # Put the new members at the beginning self.members = members + self.members + def _getDependentObjects(self): + deps = set(self.members) + deps.union(self.implementedInterfaces) + if self.parent: + deps.add(self.parent) + return deps + class IDLDictionary(IDLObjectWithScope): def __init__(self, location, parentScope, name, parent, members): assert isinstance(parentScope, IDLScope) @@ -986,6 +1028,11 @@ class IDLDictionary(IDLObjectWithScope): def addExtendedAttributes(self, attrs): assert len(attrs) == 0 + def _getDependentObjects(self): + deps = set(self.members) + if (self.parent): + deps.add(self.parent) + return deps class IDLEnum(IDLObjectWithIdentifier): def __init__(self, location, parentScope, name, values): @@ -1014,6 +1061,9 @@ class IDLEnum(IDLObjectWithIdentifier): def addExtendedAttributes(self, attrs): assert len(attrs) == 0 + def _getDependentObjects(self): + return set() + class IDLType(IDLObject): Tags = enum( # The integer types @@ -1303,6 +1353,9 @@ class IDLNullableType(IDLType): return False return self.inner.isDistinguishableFrom(other) + def _getDependentObjects(self): + return self.inner._getDependentObjects() + class IDLSequenceType(IDLType): def __init__(self, location, parameterType): assert not parameterType.isVoid() @@ -1373,6 +1426,9 @@ class IDLSequenceType(IDLType): return (other.isPrimitive() or other.isString() or other.isEnum() or other.isDate() or other.isNonCallbackInterface()) + def _getDependentObjects(self): + return self.inner._getDependentObjects() + class IDLUnionType(IDLType): def __init__(self, location, memberTypes): IDLType.__init__(self, location, "") @@ -1476,6 +1532,9 @@ class IDLUnionType(IDLType): return False return True + def _getDependentObjects(self): + return set(self.memberTypes) + class IDLArrayType(IDLType): def __init__(self, location, parameterType): assert not parameterType.isVoid() @@ -1551,6 +1610,9 @@ class IDLArrayType(IDLType): return (other.isPrimitive() or other.isString() or other.isEnum() or other.isDate() or other.isNonCallbackInterface()) + def _getDependentObjects(self): + return self.inner._getDependentObjects() + class IDLTypedefType(IDLType, IDLObjectWithIdentifier): def __init__(self, location, innerType, name): IDLType.__init__(self, location, innerType.name) @@ -1638,6 +1700,9 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier): def isDistinguishableFrom(self, other): return self.inner.isDistinguishableFrom(other) + def _getDependentObjects(self): + return self.inner._getDependentObjects() + class IDLWrapperType(IDLType): def __init__(self, location, inner): IDLType.__init__(self, location, inner.identifier.name) @@ -1741,6 +1806,23 @@ class IDLWrapperType(IDLType): assert other.isObject() return False + def _getDependentObjects(self): + # NB: The codegen for an interface type depends on + # a) That the identifier is in fact an interface (as opposed to + # a dictionary or something else). + # b) The native type of the interface. + # If we depend on the interface object we will also depend on + # anything the interface depends on which is undesirable. We + # considered implementing a dependency just on the interface type + # file, but then every modification to an interface would cause this + # to be regenerated which is still undesirable. We decided not to + # depend on anything, reasoning that: + # 1) Changing the concrete type of the interface requires modifying + # Bindings.conf, which is still a global dependency. + # 2) Changing an interface to a dictionary (or vice versa) with the + # same identifier should be incredibly rare. + return set() + class IDLBuiltinType(IDLType): Types = enum( @@ -1906,6 +1988,9 @@ class IDLBuiltinType(IDLType): (self.isTypedArray() and not other.isArrayBufferView() and not (other.isTypedArray() and other.name == self.name))))) + def _getDependentObjects(self): + return set() + BuiltinTypes = { IDLBuiltinType.Types.byte: IDLBuiltinType(BuiltinLocation(""), "Byte", @@ -2064,6 +2149,9 @@ class IDLValue(IDLObject): raise WebIDLError("Cannot coerce type %s to type %s." % (self.type, type), [location]) + def _getDependentObjects(self): + return set() + class IDLNullValue(IDLObject): def __init__(self, location): IDLObject.__init__(self, location) @@ -2081,7 +2169,10 @@ class IDLNullValue(IDLObject): nullValue = IDLNullValue(self.location) nullValue.type = type return nullValue - + + def _getDependentObjects(self): + return set() + class IDLInterfaceMember(IDLObjectWithIdentifier): @@ -2162,6 +2253,9 @@ class IDLConst(IDLInterfaceMember): def validate(self): pass + def _getDependentObjects(self): + return set([self.type, self.value]) + class IDLAttribute(IDLInterfaceMember): def __init__(self, location, identifier, type, readonly, inherit=False, static=False, stringifier=False): @@ -2307,6 +2401,9 @@ class IDLAttribute(IDLInterfaceMember): def isUnforgeable(self): return self._unforgeable + def _getDependentObjects(self): + return set([self.type]) + class IDLArgument(IDLObjectWithIdentifier): def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False): IDLObjectWithIdentifier.__init__(self, location, None, identifier) @@ -2379,6 +2476,12 @@ class IDLArgument(IDLObjectWithIdentifier): self.location) assert self.defaultValue + def _getDependentObjects(self): + deps = set([self.type]) + if self.defaultValue: + deps.add(self.defaultValue) + return deps + class IDLCallbackType(IDLType, IDLObjectWithScope): def __init__(self, location, parentScope, identifier, returnType, arguments): assert isinstance(returnType, IDLType) @@ -2446,6 +2549,9 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): if len(unhandledAttrs) != 0: IDLType.addExtendedAttributes(self, unhandledAttrs) + def _getDependentObjects(self): + return set([self._returnType] + self._arguments) + class IDLMethodOverload: """ A class that represents a single overload of a WebIDL method. This is not @@ -2461,6 +2567,11 @@ class IDLMethodOverload: self.arguments = list(arguments) self.location = location + def _getDependentObjects(self): + deps = set(self.arguments) + deps.add(self.returnType) + return deps + class IDLMethod(IDLInterfaceMember, IDLScope): Special = enum( @@ -2808,6 +2919,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope): [attr.location, self.location]) IDLInterfaceMember.handleExtendedAttribute(self, attr) + def _getDependentObjects(self): + deps = set() + for overload in self._overloads: + deps.union(overload._getDependentObjects()) + return deps + class IDLImplementsStatement(IDLObject): def __init__(self, location, implementor, implementee): IDLObject.__init__(self, location) diff --git a/dom/bindings/parser/tests/test_nullable_equivalency.py b/dom/bindings/parser/tests/test_nullable_equivalency.py index f36941de3ac..86e2d3b70b6 100644 --- a/dom/bindings/parser/tests/test_nullable_equivalency.py +++ b/dom/bindings/parser/tests/test_nullable_equivalency.py @@ -90,7 +90,7 @@ def checkEquivalent(iface, harness): for attr in dir(type1): if attr.startswith('_') or \ attr in ['nullable', 'builtin', 'filename', 'location', - 'inner', 'QName'] or \ + 'inner', 'QName', 'getDeps'] or \ (hasattr(type(type1), attr) and not callable(getattr(type1, attr))): continue diff --git a/dom/bindings/test/Makefile.in b/dom/bindings/test/Makefile.in index ddf2c89cb58..1e8b2c095b1 100644 --- a/dom/bindings/test/Makefile.in +++ b/dom/bindings/test/Makefile.in @@ -44,7 +44,6 @@ bindinggen_dependencies := \ ../Configuration.py \ ../Codegen.py \ ../parser/WebIDL.py \ - ../ParserResults.pkl \ ../Makefile \ $(GLOBAL_DEPS) \ $(NULL) From 440bea3fd71d6d7fc33553b2a8aa0263cc99ff32 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 10:43:03 -0800 Subject: [PATCH 22/64] Bug 843711 - Fix up new |Components| culprits in test suite. r=mccr8 --- browser/base/content/test/test_bug787619.html | 2 +- content/base/test/test_XHR_anon.html | 6 ++-- content/base/test/test_bug425013.html | 2 +- content/base/test/test_bug429157.html | 2 +- content/base/test/test_bug810494.html | 2 +- .../test_child_process_shutdown_message.html | 6 ++-- .../test_messagemanager_assertpermission.html | 14 ++++----- .../test_webgl_conformance_test_suite.html | 10 +++--- content/xbl/test/test_bug397934.xhtml | 2 +- .../browserElement_AppFramePermission.js | 2 +- .../test/test_archivereader_nonUnicode.html | 4 +-- dom/indexedDB/test/helpers.js | 2 +- .../tests/test_permission_basics.html | 3 +- .../tests/test_settings_onsettingchange.html | 3 +- .../mochitest/webapps/file_bug_779982.js | 2 +- image/test/crashtests/ownerdiscard.html | 4 +-- js/xpconnect/tests/mochitest/Makefile.in | 1 - .../tests/mochitest/test_bug478438.html | 2 +- .../tests/mochitest/test_bug553407.html | 31 ------------------- .../tests/mochitest/test_bug785096.html | 4 +-- layout/inspector/tests/test_bug462787.html | 2 +- layout/inspector/tests/test_bug806192.html | 2 +- .../passwordmgr/test/test_bug_654348.html | 2 +- .../passwordmgr/test/test_bug_776171.html | 2 +- .../test/test_notifications_popup.html | 6 ++-- .../passwordmgr/test/test_prompt_async.html | 12 +++---- .../tests/chrome/sandbox_content_perms.html | 4 +++ 27 files changed, 48 insertions(+), 86 deletions(-) delete mode 100644 js/xpconnect/tests/mochitest/test_bug553407.html diff --git a/browser/base/content/test/test_bug787619.html b/browser/base/content/test/test_bug787619.html index b6fbbc9e15c..93d7a84aa83 100644 --- a/browser/base/content/test/test_bug787619.html +++ b/browser/base/content/test/test_bug787619.html @@ -18,7 +18,7 @@ - - - -Mozilla Bug 553407 -

- -
-
-
- - diff --git a/js/xpconnect/tests/mochitest/test_bug785096.html b/js/xpconnect/tests/mochitest/test_bug785096.html index 84d5b6503c2..49a44a2edeb 100644 --- a/js/xpconnect/tests/mochitest/test_bug785096.html +++ b/js/xpconnect/tests/mochitest/test_bug785096.html @@ -24,10 +24,10 @@ function test() option.text="Fubar"; sel.options.add(option); try { - Components.lookupMethod(sel.options, "add")(option); + SpecialPowers.Components.lookupMethod(sel.options, "add")(option); ok(true, "function call should not throw") } catch(e) { - do_throw("this call should just work without any exceptions"); + ok(false, "this call should just work without any exceptions"); } SimpleTest.finish(); } diff --git a/layout/inspector/tests/test_bug462787.html b/layout/inspector/tests/test_bug462787.html index 0e4ac006283..84932c66438 100644 --- a/layout/inspector/tests/test_bug462787.html +++ b/layout/inspector/tests/test_bug462787.html @@ -23,7 +23,7 @@ function do_test() { const INVALID_POINTER = 0x80004003; var utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"] - .getService(Components.interfaces.inIDOMUtils); + .getService(SpecialPowers.Ci.inIDOMUtils); try { utils.getCSSStyleRules(null); ok(false, "expected an exception"); diff --git a/layout/inspector/tests/test_bug806192.html b/layout/inspector/tests/test_bug806192.html index 263fab9887a..2a19ab6334f 100644 --- a/layout/inspector/tests/test_bug806192.html +++ b/layout/inspector/tests/test_bug806192.html @@ -13,7 +13,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=806192 diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 922b0b83301..a0514b3985e 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -39,3 +39,4 @@ load 697643.html load 706283-1.html load 708405-1.html load 745495.html +load 844559.html diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 55a3a5e07e4..8e1c9183fef 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1000,14 +1000,30 @@ class CGNamedConstructors(CGThing): def define(self): if len(self.descriptor.interface.namedConstructors) == 0: return "" + + constructorID = "constructors::id::" + if self.descriptor.interface.hasInterfaceObject(): + constructorID += self.descriptor.name + else: + constructorID += "_ID_Count" + nativePropertyHooks = """const NativePropertyHooks sNamedConstructorNativePropertyHooks = { + nullptr, + nullptr, + { nullptr, nullptr }, + prototypes::id::%s, + %s, + nullptr +}; + +""" % (self.descriptor.name, constructorID) namedConstructors = CGList([], ",\n") for n in self.descriptor.interface.namedConstructors: - namedConstructors.append(CGGeneric("{ \"%s\", { %s, nullptr }, %i }" % (n.identifier.name, NamedConstructorName(n), methodLength(n)))) + namedConstructors.append(CGGeneric("{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i }" % (n.identifier.name, NamedConstructorName(n), methodLength(n)))) namedConstructors.append(CGGeneric("{ nullptr, { nullptr, nullptr }, 0 }")) namedConstructors = CGWrapper(CGIndenter(namedConstructors), pre="static const NamedConstructor namedConstructors[] = {\n", post="\n};\n") - return namedConstructors.define() + return nativePropertyHooks + namedConstructors.define() class CGClassHasInstanceHook(CGAbstractStaticMethod): def __init__(self, descriptor): diff --git a/dom/tests/mochitest/chrome/test_sandbox_bindings.xul b/dom/tests/mochitest/chrome/test_sandbox_bindings.xul index 256510774b0..6fa86267828 100644 --- a/dom/tests/mochitest/chrome/test_sandbox_bindings.xul +++ b/dom/tests/mochitest/chrome/test_sandbox_bindings.xul @@ -49,6 +49,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741267 } catch (e) { ok(false, "'XMLHttpRequest.prototype' shouldn't throw in a sandbox"); } + try { + var img = Components.utils.evalInSandbox("Image.prototype", sandbox); + ok(img, "'Image.prototype' in a sandbox should return the interface prototype object"); + ok(isXrayWrapper(img), "Getting an interface prototype object on an Xray wrapper should return an Xray wrapper"); + } catch (e) { + ok(false, "'Image.prototype' shouldn't throw in a sandbox"); + } try { var xhr = Components.utils.evalInSandbox("XMLHttpRequest", sandbox); xhr.prototype = false; From 43540aa59a734392ab205dd01d502752ee72b223 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:17 -0800 Subject: [PATCH 45/64] Bug 836301 - Assert against JSID_VOID in JSID wrapping. r=mrbkap Luke explained to me that it should never get there. --- js/src/jscompartment.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 680dd096ef0..a3c349c4d52 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -432,6 +432,7 @@ JSCompartment::wrap(JSContext *cx, JSObject **objp, JSObject *existingArg) bool JSCompartment::wrapId(JSContext *cx, jsid *idp) { + MOZ_ASSERT(*idp != JSID_VOID, "JSID_VOID is an out-of-band sentinel value"); if (JSID_IS_INT(*idp)) return true; RootedValue value(cx, IdToValue(*idp)); From 6f2c3cdc974fb4e4189fb7aece4795f23f362e46 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:17 -0800 Subject: [PATCH 46/64] Bug 836301 - Stop using JSRESOLVE_ASSIGNING to determine GET vs SET. r=mrbkap This is just a heuristic, anyway, and some of the usage is downright broken. There are two cases here: 1 - Deciding what to do for get{Own,}PropertyDescriptor. In these cases, we can just enter with GET and rely on the filtering machinery to filter out dangerous setters for security wrappers. 2 - Custom Xray props. None of these make sense in a |set| context. In fact, they generally have null setters anyway, so we can just assume GET. The policy-entering code in XrayWrapper is super haphazard. We'll get rid of it entirely later in these patches. --- js/src/jswrapper.cpp | 3 +-- js/xpconnect/wrappers/XrayWrapper.cpp | 17 +++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index b05bbde51df..cc06c67893d 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -151,8 +151,7 @@ Wrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapperArg, RootedObject wrapper(cx, wrapperArg); JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. desc->obj = NULL; // default result if we refuse to perform this action - CHECKED(DirectProxyHandler::getPropertyDescriptor(cx, wrapper, id, desc, flags), - (flags & JSRESOLVE_ASSIGNING) ? SET : GET); + CHECKED(DirectProxyHandler::getPropertyDescriptor(cx, wrapper, id, desc, flags), GET); } bool diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index a3d0610c5c5..7797723fdc9 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -726,8 +726,7 @@ XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext *cx, JSObject *wrapp { MOZ_ASSERT(js::GetObjectJSClass(holder) == &HolderClass); XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); - if (!(flags & JSRESOLVE_ASSIGNING) && - id == rt->GetStringID(XPCJSRuntime::IDX_MOZMATCHESSELECTOR) && + if (id == rt->GetStringID(XPCJSRuntime::IDX_MOZMATCHESSELECTOR) && Is(wrapper)) { // XPC calling mechanism cannot handle call/bind properly in some cases @@ -986,9 +985,8 @@ XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext *cx, js::Wrapper &jsWra (id == rt->GetStringID(XPCJSRuntime::IDX_DOCUMENTURIOBJECT) && Is(wrapper)))) { bool status; - Wrapper::Action action = (flags & JSRESOLVE_ASSIGNING) ? Wrapper::SET : Wrapper::GET; desc->obj = NULL; // default value - if (!jsWrapper.enter(cx, wrapper, id, action, &status)) + if (!jsWrapper.enter(cx, wrapper, id, Wrapper::GET, &status)) return status; desc->obj = wrapper; @@ -1410,9 +1408,8 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe } bool status; - Wrapper::Action action = (flags & JSRESOLVE_ASSIGNING) ? Wrapper::SET : Wrapper::GET; desc->obj = NULL; // default value - if (!this->enter(cx, wrapper, id, action, &status)) + if (!this->enter(cx, wrapper, id, Wrapper::GET, &status)) return status; typename Traits::ResolvingIdImpl resolving(wrapper, id); @@ -1441,9 +1438,8 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe if (AccessCheck::wrapperSubsumes(wrapper) && id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) { bool status; - Wrapper::Action action = (flags & JSRESOLVE_ASSIGNING) ? Wrapper::SET : Wrapper::GET; desc->obj = NULL; // default value - if (!this->enter(cx, wrapper, id, action, &status)) + if (!this->enter(cx, wrapper, id, Wrapper::GET, &status)) return status; desc->obj = wrapper; @@ -1540,9 +1536,8 @@ XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wra } bool status; - Wrapper::Action action = (flags & JSRESOLVE_ASSIGNING) ? Wrapper::SET : Wrapper::GET; desc->obj = NULL; // default value - if (!this->enter(cx, wrapper, id, action, &status)) + if (!this->enter(cx, wrapper, id, Wrapper::GET, &status)) return status; typename Traits::ResolvingIdImpl resolving(wrapper, id); @@ -1596,6 +1591,8 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid desc->attrs); } + // NB: We still need JSRESOLVE_ASSIGNING here for the time being, because it + // tells things like nodelists whether they should create the property or not. PropertyDescriptor existing_desc; if (!getOwnPropertyDescriptor(cx, wrapper, id, &existing_desc, JSRESOLVE_ASSIGNING)) return false; From a1a88275ee740752ce5886c3f830c8591bb85a75 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:17 -0800 Subject: [PATCH 47/64] Bug 836301 - Add Special handling to allow us to call enter() for defineProperty on Xrays. r=mrbkap --- js/xpconnect/wrappers/FilteringWrapper.cpp | 16 ++++++++++++++++ js/xpconnect/wrappers/XrayWrapper.cpp | 13 +++++++++++++ js/xpconnect/wrappers/XrayWrapper.h | 3 +++ 3 files changed, 32 insertions(+) diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index da60024ffa2..fd4e81de9e5 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -129,6 +129,22 @@ bool FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, bool *bp) { + // This is a super ugly hacky to get around Xray Resolve wonkiness. + // + // Basically, XPCWN Xrays sometimes call into the Resolve hook of the + // scriptable helper, and pass the wrapper itself as the object upon which + // the resolve is happening. Then, special handling happens in + // XrayWrapper::defineProperty to detect the resolve and redefine the + // property on the holder. Really, we should just pass the holder itself to + // NewResolve, but there's too much code in nsDOMClassInfo that assumes this + // isn't the case (in particular, code expects to be able to look up + // properties on the object, which doesn't work for the holder). Given that + // these hooks are going away eventually with the new DOM bindings, let's + // just hack around this for now. + if (XrayUtils::IsXrayResolving(cx, wrapper, id)) { + *bp = true; + return true; + } if (!Policy::check(cx, wrapper, id, act)) { if (JS_IsExceptionPending(cx)) { *bp = false; diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 7797723fdc9..e7b442873db 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -1317,6 +1317,19 @@ GetNativePropertiesObject(JSContext *cx, JSObject *wrapper) return holder; } +bool +IsXrayResolving(JSContext *cx, JSObject *wrapper, jsid id) +{ + if (!WrapperFactory::IsXrayWrapper(wrapper) || + GetXrayType(wrapper) != XrayForWrappedNative) + { + return false; + } + JSObject *holder = + XPCWrappedNativeXrayTraits::singleton.ensureHolder(cx, wrapper); + return XPCWrappedNativeXrayTraits::isResolving(cx, holder, id); +} + } static JSBool diff --git a/js/xpconnect/wrappers/XrayWrapper.h b/js/xpconnect/wrappers/XrayWrapper.h index b1f3f9ddd82..fb76135fa9a 100644 --- a/js/xpconnect/wrappers/XrayWrapper.h +++ b/js/xpconnect/wrappers/XrayWrapper.h @@ -38,6 +38,9 @@ IsTransparent(JSContext *cx, JSObject *wrapper, jsid id); JSObject * GetNativePropertiesObject(JSContext *cx, JSObject *wrapper); +bool +IsXrayResolving(JSContext *cx, JSObject *wrapper, jsid id); + } class XrayTraits; From 1c9777f5049f623ad6873f620ecb2a9e8dc66662 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:17 -0800 Subject: [PATCH 48/64] Bug 836301 - Stop enter()ing with CALL for nativeCall. r=mrbkap This is kind of nonsensical, because CALL means "the wrapped object is being called", whereas nativeCall means "the wrapped object is being unwrapped to have a JSNative invoked on it", which are two very different things. We _could_ add a NATIVECALL enter() trap, but our current policy enforcement around nativeCall involves overriding the trap itself, so we wouldn't use it for anything. So let's just get rid of it. --- js/src/jswrapper.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index cc06c67893d..b731278cd5b 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -306,9 +306,10 @@ Wrapper::construct(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *ar bool Wrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) { - const jsid id = JSID_VOID; RootedObject wrapper(cx, &args.thisv().toObject()); - CHECKED(DirectProxyHandler::nativeCall(cx, test, impl, args), CALL); + // Note - we don't enter a policy here because our security architecture guards + // against nativeCall by overriding the trap itself in the right circumstances. + return DirectProxyHandler::nativeCall(cx, test, impl, args); } bool From 97705ea7e1850ed5840a2ae08a39b2eb1687bb71 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:18 -0800 Subject: [PATCH 49/64] Bug 836301 - Add tracking for whether we have a non-trivial enter() trap. r=mrbkap This will allow us to skip the virtual function call for non-security-wrapper proxies, which are the cases where we care most about performance. --- js/src/jsproxy.cpp | 3 ++- js/src/jsproxy.h | 6 ++++++ js/src/jswrapper.cpp | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 2cfb7cef514..7d431c9614d 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -51,7 +51,8 @@ GetFunctionProxyConstruct(UnrootedObject proxy) BaseProxyHandler::BaseProxyHandler(void *family) : mFamily(family), - mHasPrototype(false) + mHasPrototype(false), + mHasPolicy(false) { } diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 304f04a2a53..0bba56ef876 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -51,9 +51,11 @@ class JS_FRIEND_API(Wrapper); class JS_FRIEND_API(BaseProxyHandler) { void *mFamily; bool mHasPrototype; + bool mHasPolicy; protected: // Subclasses may set this in their constructor. void setHasPrototype(bool aHasPrototype) { mHasPrototype = aHasPrototype; } + void setHasPolicy(bool aHasPolicy) { mHasPolicy = aHasPolicy; } public: explicit BaseProxyHandler(void *family); @@ -63,6 +65,10 @@ class JS_FRIEND_API(BaseProxyHandler) { return mHasPrototype; } + bool hasPolicy() { + return mHasPolicy; + } + inline void *family() { return mFamily; } diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index b731278cd5b..305769f04da 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -838,6 +838,7 @@ SecurityWrapper::SecurityWrapper(unsigned flags) : Base(flags) { Base::setSafeToUnwrap(false); + BaseProxyHandler::setHasPolicy(true); } template From 15161f36d395bc0f4ea88afd6050ec83cd10a517 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:18 -0800 Subject: [PATCH 50/64] Bug 836301 - Hoist enter() into BaseProxyHandler. r=mrbkap --- js/src/jsproxy.cpp | 8 ++++++++ js/src/jsproxy.h | 16 ++++++++++++++++ js/src/jswrapper.cpp | 7 ------- js/src/jswrapper.h | 17 +---------------- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 7d431c9614d..daf9f1c397e 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -60,6 +60,14 @@ BaseProxyHandler::~BaseProxyHandler() { } +bool +BaseProxyHandler::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, + bool *bp) +{ + *bp = true; + return true; +} + bool BaseProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) { diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 0bba56ef876..0d0b70e1e8b 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -77,6 +77,22 @@ class JS_FRIEND_API(BaseProxyHandler) { return false; } + /* Policy enforcement traps. + * + * enter() allows the policy to specify whether the caller may perform |act| + * on the proxy's |id| property. In the case when |act| is CALL, |id| is + * generally JSID_VOID. + * + * The |act| parameter to enter() specifies the action being performed. + */ + enum Action { + GET, + SET, + CALL + }; + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, + bool *bp); + /* ES5 Harmony fundamental proxy traps. */ virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc, unsigned flags) = 0; diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 305769f04da..2fc1c947f07 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -70,13 +70,6 @@ Wrapper::wrappedObject(RawObject wrapper) return GetProxyTargetObject(wrapper); } -bool -Wrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp) -{ - *bp = true; - return true; -} - JS_FRIEND_API(JSObject *) js::UnwrapObject(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp) { diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 010b8e1414e..db03e9f55f4 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -32,11 +32,7 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler bool mSafeToUnwrap; public: - enum Action { - GET, - SET, - CALL - }; + using BaseProxyHandler::Action; enum Flags { CROSS_COMPARTMENT = 1 << 0, @@ -65,17 +61,6 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler return mFlags; } - /* Policy enforcement traps. - * - * enter() allows the policy to specify whether the caller may perform |act| - * on the underlying object's |id| property. In the case when |act| is CALL, - * |id| is generally JSID_VOID. - * - * The |act| parameter to enter() specifies the action being performed. - */ - virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, - bool *bp); - explicit Wrapper(unsigned flags, bool hasPrototype = false); virtual ~Wrapper(); From 83a57c8d91aa1aca57b92d527771190d4fb39452 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:18 -0800 Subject: [PATCH 51/64] Bug 836301 - Introduce an RAII class for entering policies. r=mrbkap This will allow us to make some hard assertions that a given policy has been entered exactly once. --- js/src/js.msg | 4 ++-- js/src/jsproxy.cpp | 14 +++++++++++++ js/src/jsproxy.h | 24 ++++++++++++++++++++++ js/src/jswrapper.cpp | 6 +++--- js/xpconnect/wrappers/AccessCheck.cpp | 18 ---------------- js/xpconnect/wrappers/AccessCheck.h | 24 +++++++--------------- js/xpconnect/wrappers/FilteringWrapper.cpp | 7 +------ 7 files changed, 51 insertions(+), 46 deletions(-) diff --git a/js/src/js.msg b/js/src/js.msg index 7b13a1cda09..c72d0952188 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -79,8 +79,8 @@ MSG_DEF(JSMSG_TOO_DEEP, 25, 1, JSEXN_INTERNALERR, "{0} nested too MSG_DEF(JSMSG_OVER_RECURSED, 26, 0, JSEXN_INTERNALERR, "too much recursion") MSG_DEF(JSMSG_IN_NOT_OBJECT, 27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}") MSG_DEF(JSMSG_BAD_NEW_RESULT, 28, 1, JSEXN_TYPEERR, "invalid new expression result {0}") -MSG_DEF(JSMSG_UNUSED29, 29, 0, JSEXN_NONE, "") -MSG_DEF(JSMSG_UNUSED30, 30, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_OBJECT_ACCESS_DENIED, 29, 0, JSEXN_ERR, "Permission denied to access object") +MSG_DEF(JSMSG_PROPERTY_ACCESS_DENIED, 30, 1, JSEXN_ERR, "Permission denied to access property '{0}'") MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}") MSG_DEF(JSMSG_BAD_BYTECODE, 32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}") MSG_DEF(JSMSG_BAD_RADIX, 33, 0, JSEXN_RANGEERR, "radix must be an integer at least 2 and no greater than 36") diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index daf9f1c397e..ebdc57ba827 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -49,6 +49,20 @@ GetFunctionProxyConstruct(UnrootedObject proxy) return proxy->getSlotRef(JSSLOT_PROXY_CONSTRUCT); } +void +js::AutoEnterPolicy::reportError(JSContext *cx, jsid id) +{ + if (JSID_IS_VOID(id)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_OBJECT_ACCESS_DENIED); + } else { + JSString *str = IdToString(cx, id); + const jschar *prop = str ? str->getCharsZ(cx) : NULL; + JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, + JSMSG_PROPERTY_ACCESS_DENIED, prop); + } +} + BaseProxyHandler::BaseProxyHandler(void *family) : mFamily(family), mHasPrototype(false), diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 0d0b70e1e8b..315fb9b01eb 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -84,6 +84,8 @@ class JS_FRIEND_API(BaseProxyHandler) { * generally JSID_VOID. * * The |act| parameter to enter() specifies the action being performed. + * If |bp| is false, the trap suggests that the caller throw (though it + * may still decide to squelch the error). */ enum Action { GET, @@ -341,6 +343,28 @@ NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv, JSObject * RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv); +class JS_FRIEND_API(AutoEnterPolicy) +{ + public: + typedef BaseProxyHandler::Action Action; + AutoEnterPolicy(JSContext *cx, BaseProxyHandler *handler, + JSObject *wrapper, jsid id, Action act, bool mayThrow) + { + allow = handler->hasPolicy() ? handler->enter(cx, wrapper, id, act, &rv) + : true; + if (!allow && !rv && mayThrow) + reportError(cx, id); + } + + inline bool allowed() { return allow; } + inline bool returnValue() { JS_ASSERT(!allowed()); return rv; } + + protected: + void reportError(JSContext *cx, jsid id); + bool allow; + bool rv; +}; + } /* namespace js */ extern JS_FRIEND_API(JSObject *) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 2fc1c947f07..0d4899e8d3f 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -117,9 +117,9 @@ js::IsCrossCompartmentWrapper(RawObject wrapper) #define CHECKED(op, act) \ JS_BEGIN_MACRO \ - bool status; \ - if (!enter(cx, wrapper, id, act, &status)) \ - return status; \ + AutoEnterPolicy policy(cx, this, wrapper, id, act, true); \ + if (!policy.allowed()) \ + return policy.returnValue(); \ return (op); \ JS_END_MACRO diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index d5072d87499..3865c4aeb0f 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -267,24 +267,6 @@ AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper) return false; } -void -AccessCheck::deny(JSContext *cx, jsid id) -{ - if (id == JSID_VOID) { - JS_ReportError(cx, "Permission denied to access object"); - } else { - jsval idval; - if (!JS_IdToValue(cx, id, &idval)) - return; - JSString *str = JS_ValueToString(cx, idval); - if (!str) - return; - const jschar *chars = JS_GetStringCharsZ(cx, str); - if (chars) - JS_ReportError(cx, "Permission denied to access property '%hs'", chars); - } -} - enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 }; static bool diff --git a/js/xpconnect/wrappers/AccessCheck.h b/js/xpconnect/wrappers/AccessCheck.h index 8a7dcc5a5b4..dd95e040731 100644 --- a/js/xpconnect/wrappers/AccessCheck.h +++ b/js/xpconnect/wrappers/AccessCheck.h @@ -33,8 +33,6 @@ class AccessCheck { static bool needsSystemOnlyWrapper(JSObject *obj); static bool isScriptAccessOnly(JSContext *cx, JSObject *wrapper); - - static void deny(JSContext *cx, jsid id); }; struct Policy { @@ -45,8 +43,7 @@ struct Opaque : public Policy { static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { return act == js::Wrapper::CALL; } - static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { - AccessCheck::deny(cx, id); + static bool deny(js::Wrapper::Action act) { return false; } static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) @@ -62,8 +59,7 @@ struct OnlyIfSubjectIsSystem : public Policy { return AccessCheck::isSystemOnlyAccessPermitted(cx); } - static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { - AccessCheck::deny(cx, id); + static bool deny(js::Wrapper::Action act) { return false; } @@ -79,8 +75,7 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy { static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { return AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act); } - static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) { - AccessCheck::deny(cx, id); + static bool deny(js::Wrapper::Action act) { return false; } static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) @@ -94,13 +89,9 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy { 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 deny(js::Wrapper::Action act) { + // Fail silently for GETs. + return act == js::Wrapper::GET; } static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl); }; @@ -109,8 +100,7 @@ struct ExposedPropertiesOnly : public 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); + static bool deny(js::Wrapper::Action act) { return false; } static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) { diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index fd4e81de9e5..69c5a976b48 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -146,12 +146,7 @@ FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, return true; } 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); + *bp = JS_IsExceptionPending(cx) ? false : Policy::deny(act); return false; } *bp = true; From f84dddd71f01e6da291a2f46e6f903a19f6f7746 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:18 -0800 Subject: [PATCH 52/64] Bug 836301 - Hoist enter() calls from {Xray,}Wrapper::foo into Proxy::foo. r=mrbkap --- js/src/jsproxy.cpp | 141 +++++++++++++++--- js/src/jswrapper.cpp | 80 ++-------- js/xpconnect/tests/chrome/test_bug760109.xul | 13 +- js/xpconnect/tests/unit/test_bug780370.js | 5 +- js/xpconnect/wrappers/ChromeObjectWrapper.cpp | 79 +++++++--- js/xpconnect/wrappers/ChromeObjectWrapper.h | 3 + js/xpconnect/wrappers/XrayWrapper.cpp | 23 +-- 7 files changed, 216 insertions(+), 128 deletions(-) diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index ebdc57ba827..6998383c7a4 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -2200,7 +2200,6 @@ ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton; return protoCall; \ JS_END_MACRO \ - bool Proxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy_, jsid id_, PropertyDescriptor *desc, unsigned flags) @@ -2209,6 +2208,10 @@ Proxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy_, jsid id_, Property RootedObject proxy(cx, proxy_); RootedId id(cx, id_); BaseProxyHandler *handler = GetProxyHandler(proxy); + desc->obj = NULL; // default result if we refuse to perform this action + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); if (!handler->hasPrototype()) return handler->getPropertyDescriptor(cx, proxy, id, desc, flags); if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc, flags)) @@ -2241,7 +2244,12 @@ Proxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy_, jsid id, Proper { JS_CHECK_RECURSION(cx, return false); RootedObject proxy(cx, proxy_); - return GetProxyHandler(proxy)->getOwnPropertyDescriptor(cx, proxy, id, desc, flags); + BaseProxyHandler *handler = GetProxyHandler(proxy); + desc->obj = NULL; // default result if we refuse to perform this action + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); + return handler->getOwnPropertyDescriptor(cx, proxy, id, desc, flags); } bool @@ -2265,7 +2273,11 @@ bool Proxy::defineProperty(JSContext *cx, JSObject *proxy_, jsid id, PropertyDescriptor *desc) { JS_CHECK_RECURSION(cx, return false); + BaseProxyHandler *handler = GetProxyHandler(proxy_); RootedObject proxy(cx, proxy_); + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); + if (!policy.allowed()) + return policy.returnValue(); return GetProxyHandler(proxy)->defineProperty(cx, proxy, id, desc); } @@ -2284,7 +2296,11 @@ bool Proxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy_, AutoIdVector &props) { JS_CHECK_RECURSION(cx, return false); + BaseProxyHandler *handler = GetProxyHandler(proxy_); RootedObject proxy(cx, proxy_); + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); return GetProxyHandler(proxy)->getOwnPropertyNames(cx, proxy, props); } @@ -2292,7 +2308,12 @@ bool Proxy::delete_(JSContext *cx, JSObject *proxy_, jsid id, bool *bp) { JS_CHECK_RECURSION(cx, return false); + BaseProxyHandler *handler = GetProxyHandler(proxy_); RootedObject proxy(cx, proxy_); + *bp = true; // default result if we refuse to perform this action + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); + if (!policy.allowed()) + return policy.returnValue(); return GetProxyHandler(proxy)->delete_(cx, proxy, id, bp); } @@ -2322,6 +2343,9 @@ Proxy::enumerate(JSContext *cx, JSObject *proxy_, AutoIdVector &props) JS_CHECK_RECURSION(cx, return false); RootedObject proxy(cx, proxy_); BaseProxyHandler *handler = GetProxyHandler(proxy); + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); if (!handler->hasPrototype()) return GetProxyHandler(proxy)->enumerate(cx, proxy, props); if (!handler->keys(cx, proxy, props)) @@ -2339,6 +2363,10 @@ Proxy::has(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) RootedObject proxy(cx, proxy_); RootedId id(cx, id_); BaseProxyHandler *handler = GetProxyHandler(proxy); + *bp = false; // default result if we refuse to perform this action + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); if (!handler->hasPrototype()) return handler->has(cx, proxy, id, bp); if (!handler->hasOwn(cx, proxy, id, bp)) @@ -2356,7 +2384,12 @@ Proxy::hasOwn(JSContext *cx, JSObject *proxy_, jsid id, bool *bp) { JS_CHECK_RECURSION(cx, return false); RootedObject proxy(cx, proxy_); - return GetProxyHandler(proxy)->hasOwn(cx, proxy, id, bp); + BaseProxyHandler *handler = GetProxyHandler(proxy); + *bp = false; // default result if we refuse to perform this action + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); + return handler->hasOwn(cx, proxy, id, bp); } bool @@ -2365,6 +2398,10 @@ Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id { JS_CHECK_RECURSION(cx, return false); BaseProxyHandler *handler = GetProxyHandler(proxy); + vp.setUndefined(); // default result if we refuse to perform this action + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); bool own; if (!handler->hasPrototype()) { own = true; @@ -2383,17 +2420,20 @@ Proxy::getElementIfPresent(JSContext *cx, HandleObject proxy, HandleObject recei { JS_CHECK_RECURSION(cx, return false); - BaseProxyHandler *handler = GetProxyHandler(proxy); - - if (!handler->hasPrototype()) { - return GetProxyHandler(proxy)->getElementIfPresent(cx, proxy, receiver, index, - vp.address(), present); - } - RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; + BaseProxyHandler *handler = GetProxyHandler(proxy); + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); + + if (!handler->hasPrototype()) { + return handler->getElementIfPresent(cx, proxy, receiver, index, + vp.address(), present); + } + bool hasOwn; if (!handler->hasOwn(cx, proxy, id, &hasOwn)) return false; @@ -2413,6 +2453,9 @@ Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id { JS_CHECK_RECURSION(cx, return false); BaseProxyHandler *handler = GetProxyHandler(proxy); + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); + if (!policy.allowed()) + return policy.returnValue(); if (handler->hasPrototype()) { // If we're using a prototype, we still want to use the proxy trap unless // we have a non-own property with a setter. @@ -2440,7 +2483,11 @@ Proxy::keys(JSContext *cx, JSObject *proxy_, AutoIdVector &props) { JS_CHECK_RECURSION(cx, return false); RootedObject proxy(cx, proxy_); - return GetProxyHandler(proxy)->keys(cx, proxy, props); + BaseProxyHandler *handler = GetProxyHandler(proxy); + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); + return handler->keys(cx, proxy, props); } bool @@ -2448,8 +2495,19 @@ Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleV { JS_CHECK_RECURSION(cx, return false); BaseProxyHandler *handler = GetProxyHandler(proxy); - if (!handler->hasPrototype()) - return GetProxyHandler(proxy)->iterate(cx, proxy, flags, vp.address()); + vp.setUndefined(); // default result if we refuse to perform this action + if (!handler->hasPrototype()) { + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, + BaseProxyHandler::GET, /* mayThrow = */ false); + // If the policy denies access but wants us to return true, we need + // to hand a valid (empty) iterator object to the caller. + if (!policy.allowed()) { + AutoIdVector props(cx); + return policy.returnValue() && + EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp); + } + return handler->iterate(cx, proxy, flags, vp.address()); + } AutoIdVector props(cx); // The other Proxy::foo methods do the prototype-aware work for us here. if ((flags & JSITER_OWNONLY) @@ -2465,7 +2523,19 @@ Proxy::call(JSContext *cx, JSObject *proxy_, unsigned argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); RootedObject proxy(cx, proxy_); - return GetProxyHandler(proxy)->call(cx, proxy, argc, vp); + BaseProxyHandler *handler = GetProxyHandler(proxy); + + // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we + // can only set our default value once we're sure that we're not calling the + // trap. + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, + BaseProxyHandler::CALL, true); + if (!policy.allowed()) { + vp->setUndefined(); + return policy.returnValue(); + } + + return handler->call(cx, proxy, argc, vp); } bool @@ -2473,7 +2543,19 @@ Proxy::construct(JSContext *cx, JSObject *proxy_, unsigned argc, Value *argv, Va { JS_CHECK_RECURSION(cx, return false); RootedObject proxy(cx, proxy_); - return GetProxyHandler(proxy)->construct(cx, proxy, argc, argv, rval); + BaseProxyHandler *handler = GetProxyHandler(proxy); + + // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we + // can only set our default value once we're sure that we're not calling the + // trap. + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, + BaseProxyHandler::CALL, true); + if (!policy.allowed()) { + rval->setUndefined(); + return policy.returnValue(); + } + + return handler->construct(cx, proxy, argc, argv, rval); } bool @@ -2481,6 +2563,9 @@ Proxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArg { JS_CHECK_RECURSION(cx, return false); Rooted proxy(cx, &args.thisv().toObject()); + // Note - we don't enter a policy here because our security architecture + // guards against nativeCall by overriding the trap itself in the right + // circumstances. return GetProxyHandler(proxy)->nativeCall(cx, test, impl, args); } @@ -2488,6 +2573,11 @@ bool Proxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) { JS_CHECK_RECURSION(cx, return false); + BaseProxyHandler *handler = GetProxyHandler(proxy); + *bp = false; // default result if we refuse to perform this action + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, BaseProxyHandler::GET, true); + if (!policy.allowed()) + return policy.returnValue(); return GetProxyHandler(proxy)->hasInstance(cx, proxy, v, bp); } @@ -2503,7 +2593,14 @@ Proxy::obj_toString(JSContext *cx, JSObject *proxy_) { JS_CHECK_RECURSION(cx, return NULL); RootedObject proxy(cx, proxy_); - return GetProxyHandler(proxy)->obj_toString(cx, proxy); + BaseProxyHandler *handler = GetProxyHandler(proxy); + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, + BaseProxyHandler::GET, /* mayThrow = */ false); + // Do the safe thing if the policy rejects. + if (!policy.allowed()) { + return handler->BaseProxyHandler::obj_toString(cx, proxy); + } + return handler->obj_toString(cx, proxy); } JSString * @@ -2511,7 +2608,17 @@ Proxy::fun_toString(JSContext *cx, JSObject *proxy_, unsigned indent) { JS_CHECK_RECURSION(cx, return NULL); RootedObject proxy(cx, proxy_); - return GetProxyHandler(proxy)->fun_toString(cx, proxy, indent); + BaseProxyHandler *handler = GetProxyHandler(proxy); + AutoEnterPolicy policy(cx, handler, proxy, JSID_VOID, + BaseProxyHandler::GET, /* mayThrow = */ false); + // Do the safe thing if the policy rejects. + if (!policy.allowed()) { + if (proxy->isCallable()) + return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}"); + ReportIsNotFunction(cx, ObjectValue(*proxy)); + return NULL; + } + return handler->fun_toString(cx, proxy, indent); } bool diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 0d4899e8d3f..819b64a8b60 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -115,17 +115,6 @@ js::IsCrossCompartmentWrapper(RawObject wrapper) !!(Wrapper::wrapperHandler(wrapper)->flags() & Wrapper::CROSS_COMPARTMENT); } -#define CHECKED(op, act) \ - JS_BEGIN_MACRO \ - AutoEnterPolicy policy(cx, this, wrapper, id, act, true); \ - if (!policy.allowed()) \ - return policy.returnValue(); \ - return (op); \ - JS_END_MACRO - -#define SET(action) CHECKED(action, SET) -#define GET(action) CHECKED(action, GET) - Wrapper::Wrapper(unsigned flags, bool hasPrototype) : DirectProxyHandler(&sWrapperFamily) , mFlags(flags) , mSafeToUnwrap(true) @@ -143,8 +132,7 @@ Wrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapperArg, { RootedObject wrapper(cx, wrapperArg); JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - desc->obj = NULL; // default result if we refuse to perform this action - CHECKED(DirectProxyHandler::getPropertyDescriptor(cx, wrapper, id, desc, flags), GET); + return DirectProxyHandler::getPropertyDescriptor(cx, wrapper, id, desc, flags); } bool @@ -152,8 +140,7 @@ Wrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapperArg, jsid id, PropertyDescriptor *desc, unsigned flags) { RootedObject wrapper(cx, wrapperArg); - desc->obj = NULL; // default result if we refuse to perform this action - CHECKED(DirectProxyHandler::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags), GET); + return DirectProxyHandler::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags); } bool @@ -161,7 +148,7 @@ Wrapper::defineProperty(JSContext *cx, JSObject *wrapperArg, jsid id, PropertyDescriptor *desc) { RootedObject wrapper(cx, wrapperArg); - SET(DirectProxyHandler::defineProperty(cx, wrapper, id, desc)); + return DirectProxyHandler::defineProperty(cx, wrapper, id, desc); } bool @@ -169,17 +156,14 @@ Wrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props) { RootedObject wrapper(cx, wrapperArg); - // if we refuse to perform this action, props remains empty - jsid id = JSID_VOID; - GET(DirectProxyHandler::getOwnPropertyNames(cx, wrapper, props)); + return DirectProxyHandler::getOwnPropertyNames(cx, wrapper, props); } bool Wrapper::delete_(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) { RootedObject wrapper(cx, wrapperArg); - *bp = true; // default result if we refuse to perform this action - SET(DirectProxyHandler::delete_(cx, wrapper, id, bp)); + return DirectProxyHandler::delete_(cx, wrapper, id, bp); } bool @@ -187,9 +171,7 @@ Wrapper::enumerate(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props) { RootedObject wrapper(cx, wrapperArg); JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - // if we refuse to perform this action, props remains empty - static jsid id = JSID_VOID; - GET(DirectProxyHandler::enumerate(cx, wrapper, props)); + return DirectProxyHandler::enumerate(cx, wrapper, props); } /* @@ -231,24 +213,21 @@ Wrapper::has(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) { RootedObject wrapper(cx, wrapperArg); JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - *bp = false; // default result if we refuse to perform this action - GET(DirectProxyHandler::has(cx, wrapper, id, bp)); + return DirectProxyHandler::has(cx, wrapper, id, bp); } bool Wrapper::hasOwn(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) { RootedObject wrapper(cx, wrapperArg); - *bp = false; // default result if we refuse to perform this action - GET(DirectProxyHandler::hasOwn(cx, wrapper, id, bp)); + return DirectProxyHandler::hasOwn(cx, wrapper, id, bp); } bool Wrapper::get(JSContext *cx, JSObject *wrapperArg, JSObject *receiver, jsid id, Value *vp) { RootedObject wrapper(cx, wrapperArg); - vp->setUndefined(); // default result if we refuse to perform this action - GET(DirectProxyHandler::get(cx, wrapper, receiver, id, vp)); + return DirectProxyHandler::get(cx, wrapper, receiver, id, vp); } bool @@ -256,16 +235,14 @@ Wrapper::set(JSContext *cx, JSObject *wrapperArg, JSObject *receiver, jsid id, b Value *vp) { RootedObject wrapper(cx, wrapperArg); - SET(DirectProxyHandler::set(cx, wrapper, receiver, id, strict, vp)); + return DirectProxyHandler::set(cx, wrapper, receiver, id, strict, vp); } bool Wrapper::keys(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props) { RootedObject wrapper(cx, wrapperArg); - // if we refuse to perform this action, props remains empty - const jsid id = JSID_VOID; - GET(DirectProxyHandler::keys(cx, wrapper, props)); + return DirectProxyHandler::keys(cx, wrapper, props); } bool @@ -273,27 +250,21 @@ Wrapper::iterate(JSContext *cx, JSObject *wrapperArg, unsigned flags, Value *vp) { RootedObject wrapper(cx, wrapperArg); JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - vp->setUndefined(); // default result if we refuse to perform this action - const jsid id = JSID_VOID; - GET(DirectProxyHandler::iterate(cx, wrapper, flags, vp)); + return DirectProxyHandler::iterate(cx, wrapper, flags, vp); } bool Wrapper::call(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *vp) { RootedObject wrapper(cx, wrapperArg); - vp->setUndefined(); // default result if we refuse to perform this action - const jsid id = JSID_VOID; - CHECKED(DirectProxyHandler::call(cx, wrapper, argc, vp), CALL); + return DirectProxyHandler::call(cx, wrapper, argc, vp); } bool Wrapper::construct(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *argv, Value *vp) { RootedObject wrapper(cx, wrapperArg); - vp->setUndefined(); // default result if we refuse to perform this action - const jsid id = JSID_VOID; - CHECKED(DirectProxyHandler::construct(cx, wrapper, argc, argv, vp), CALL); + return DirectProxyHandler::construct(cx, wrapper, argc, argv, vp); } bool @@ -308,23 +279,13 @@ Wrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallA bool Wrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, bool *bp) { - *bp = false; // default result if we refuse to perform this action - const jsid id = JSID_VOID; - GET(DirectProxyHandler::hasInstance(cx, wrapper, v, bp)); + return DirectProxyHandler::hasInstance(cx, wrapper, v, bp); } JSString * Wrapper::obj_toString(JSContext *cx, JSObject *wrapperArg) { RootedObject wrapper(cx, wrapperArg); - bool status; - if (!enter(cx, wrapper, JSID_VOID, GET, &status)) { - if (status) { - // Perform some default behavior that doesn't leak any information. - return JS_NewStringCopyZ(cx, "[object Object]"); - } - return NULL; - } JSString *str = DirectProxyHandler::obj_toString(cx, wrapper); return str; } @@ -332,17 +293,6 @@ Wrapper::obj_toString(JSContext *cx, JSObject *wrapperArg) JSString * Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) { - bool status; - if (!enter(cx, wrapper, JSID_VOID, GET, &status)) { - if (status) { - // Perform some default behavior that doesn't leak any information. - if (wrapper->isCallable()) - return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}"); - ReportIsNotFunction(cx, ObjectValue(*wrapper)); - return NULL; - } - return NULL; - } JSString *str = DirectProxyHandler::fun_toString(cx, wrapper, indent); return str; } diff --git a/js/xpconnect/tests/chrome/test_bug760109.xul b/js/xpconnect/tests/chrome/test_bug760109.xul index 311ccb1e6ed..aa26158d510 100644 --- a/js/xpconnect/tests/chrome/test_bug760109.xul +++ b/js/xpconnect/tests/chrome/test_bug760109.xul @@ -31,12 +31,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=760109 ok(protoProto === Object.prototype, "Object prototype remapped properly"); - // Check |constructor|. The semantics of this weird for the case of an - // object with a custom chrome-implemented prototype, because we'll end up - // bouncing up the prototype chain to Object, even though that's not fully - // accurate. It's unlikely to be a problem though, so we just verify that - // it does what we expect. - ok(chromeObject.constructor === Object, "Object constructor does what we expect"); + // Check |constructor|. + // Note that the 'constructor' property of the underlying chrome object + // will be resolved on SomeConstructor.prototype, which has an empty + // __exposedProps__. This means that we shouldn't remap the property, even + // though we'd also be able to find it on Object.prototype. Some recent + // refactoring has made it possible to do the right thing here. + is(typeof chromeObject.constructor, "undefined", "Object constructor does what we expect"); ok(chromeArray.constructor === Array, "Array constructor remapped properly"); // We should be able to .forEach on the Array. diff --git a/js/xpconnect/tests/unit/test_bug780370.js b/js/xpconnect/tests/unit/test_bug780370.js index 1e335d51a45..027f1341889 100644 --- a/js/xpconnect/tests/unit/test_bug780370.js +++ b/js/xpconnect/tests/unit/test_bug780370.js @@ -16,5 +16,8 @@ function run_test() do_check_eq(Cu.evalInSandbox('typeof obj.foo', sb), 'undefined', "COW works as expected"); do_check_true(Cu.evalInSandbox('obj.hasOwnProperty === Object.prototype.hasOwnProperty', sb), "Remapping happens even when the property is explicitly exposed"); - do_check_eq(Cu.evalInSandbox('Object.prototype.bar = 10; obj.bar', sb), 10); + // NB: We used to test for the following, but such behavior became very + // difficult to implement in a recent refactor. We're moving away from this + // API anyway, so we decided to explicitly drop support for this. + // do_check_eq(Cu.evalInSandbox('Object.prototype.bar = 10; obj.bar', sb), 10); } diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp index f2f6c79e6af..5868089d5d7 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp @@ -14,6 +14,16 @@ namespace xpc { // their prototype, we have to instrument the traps to do this manually. ChromeObjectWrapper ChromeObjectWrapper::singleton; +static bool +AllowedByBase(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) +{ + MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) == + &ChromeObjectWrapper::singleton); + bool bp; + ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton; + return handler->ChromeObjectWrapperBase::enter(cx, wrapper, id, act, &bp); +} + static bool PropIsFromStandardPrototype(JSContext *cx, JSPropertyDescriptor *desc) { @@ -23,16 +33,35 @@ PropIsFromStandardPrototype(JSContext *cx, JSPropertyDescriptor *desc) return JS_IdentifyClassPrototype(cx, unwrapped) != JSProto_Null; } +// Note that we're past the policy enforcement stage, here, so we can query +// ChromeObjectWrapperBase and get an unfiltered view of the underlying object. +// This lets us determine whether the property we would have found (given a +// transparent wrapper) would have come off a standard prototype. +static bool +PropIsFromStandardPrototype(JSContext *cx, JSObject *wrapper, jsid id) +{ + MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) == + &ChromeObjectWrapper::singleton); + JSPropertyDescriptor desc; + ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton; + if (!handler->ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id, + &desc, 0) || + !desc.obj) + { + return false; + } + return PropIsFromStandardPrototype(cx, &desc); +} + bool ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc, unsigned flags) { - // First, try the lookup on the base wrapper. This can throw for various - // reasons, including sets (gets fail silently). There's nothing we can really - // do for sets, so we can conveniently propagate any exception we hit here. + // First, try a lookup on the base wrapper if permitted. desc->obj = NULL; - if (!ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id, + if (AllowedByBase(cx, wrapper, id, Wrapper::GET) && + !ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id, desc, flags)) { return false; } @@ -58,9 +87,12 @@ ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, bool ChromeObjectWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { - // Try the lookup on the base wrapper. - if (!ChromeObjectWrapperBase::has(cx, wrapper, id, bp)) + // Try the lookup on the base wrapper if permitted. + if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) && + !ChromeObjectWrapperBase::has(cx, wrapper, id, bp)) + { return false; + } // If we found something or have no prototype, we're done. JSObject *wrapperProto; @@ -82,19 +114,14 @@ bool ChromeObjectWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp) { - // Start with a call to getPropertyDescriptor. We unfortunately need to do - // this because the call signature of ::get doesn't give us any way to - // determine the object upon which the property was found. - JSPropertyDescriptor desc; - if (!ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id, &desc, - 0)) { - return false; - } - - // Only call through to the get trap on the underlying object if we'll find - // something, and if what we'll find is not on a standard prototype. vp->setUndefined(); - if (desc.obj && !PropIsFromStandardPrototype(cx, &desc)) { + JSPropertyDescriptor desc; + // Only call through to the get trap on the underlying object if we're + // allowed to see the property, and if what we'll find is not on a standard + // prototype. + if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) && + !PropIsFromStandardPrototype(cx, wrapper, id)) + { // Call the get trap. if (!ChromeObjectWrapperBase::get(cx, wrapper, receiver, id, vp)) return false; @@ -115,4 +142,20 @@ ChromeObjectWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, return js::GetGeneric(cx, wrapperProto, receiver, id, vp); } +// This mechanism isn't ideal because we end up calling enter() on the base class +// twice (once during enter() here and once during the trap itself), and policy +// enforcement or COWs isn't cheap. But it results in the cleanest code, and this +// whole proto remapping thing for COWs is going to be phased out anyway. +bool +ChromeObjectWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, + js::Wrapper::Action act, bool *bp) +{ + if (AllowedByBase(cx, wrapper, id, act)) + return true; + // COWs fail silently for GETs, and that also happens to be the only case + // where we might want to redirect the lookup to the home prototype chain. + *bp = (act == Wrapper::GET); + return *bp && PropIsFromStandardPrototype(cx, wrapper, id); +} + } diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.h b/js/xpconnect/wrappers/ChromeObjectWrapper.h index b5e0b7e9708..1a98f3a9026 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.h +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.h @@ -37,6 +37,9 @@ class ChromeObjectWrapper : public ChromeObjectWrapperBase virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp) MOZ_OVERRIDE; + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, + js::Wrapper::Action act, bool *bp) MOZ_OVERRIDE; + // NB: One might think we'd need to implement enumerate(), keys(), iterate(), // and getPropertyNames() here. However, ES5 built-in properties aren't // enumerable (and SpiderMonkey's implementation seems to match the spec diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index e7b442873db..84dcab856fc 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -983,12 +983,8 @@ XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext *cx, js::Wrapper &jsWra id == rt->GetStringID(XPCJSRuntime::IDX_NODEPRINCIPAL)) && Is(wrapper)) || (id == rt->GetStringID(XPCJSRuntime::IDX_DOCUMENTURIOBJECT) && - Is(wrapper)))) { - bool status; - desc->obj = NULL; // default value - if (!jsWrapper.enter(cx, wrapper, id, Wrapper::GET, &status)) - return status; - + Is(wrapper)))) + { desc->obj = wrapper; desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED; if (id == rt->GetStringID(XPCJSRuntime::IDX_BASEURIOBJECT)) @@ -1420,11 +1416,6 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe return true; } - bool status; - desc->obj = NULL; // default value - if (!this->enter(cx, wrapper, id, Wrapper::GET, &status)) - return status; - typename Traits::ResolvingIdImpl resolving(wrapper, id); // Redirect access straight to the wrapper if we should be transparent. @@ -1450,11 +1441,6 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrappe XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (AccessCheck::wrapperSubsumes(wrapper) && id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) { - bool status; - desc->obj = NULL; // default value - if (!this->enter(cx, wrapper, id, Wrapper::GET, &status)) - return status; - desc->obj = wrapper; desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED; desc->getter = wrappedJSObject_getter; @@ -1548,11 +1534,6 @@ XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wra return true; } - bool status; - desc->obj = NULL; // default value - if (!this->enter(cx, wrapper, id, Wrapper::GET, &status)) - return status; - typename Traits::ResolvingIdImpl resolving(wrapper, id); // NB: Nothing we do here acts on the wrapped native itself, so we don't From c014987e4f6b14c1dca86a302dbbc918cdf48773 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 25 Feb 2013 13:54:18 -0800 Subject: [PATCH 53/64] Bug 836301 - Hoist some assertions, remove a bunch of no-op trap overrides, and add assertions that we've entered our policy. r=mrbkap --- configure.in | 3 +- js/src/configure.in | 3 +- js/src/jsapi.cpp | 3 + js/src/jscntxt.h | 6 +- js/src/jsfriendapi.h | 7 + js/src/jsproxy.cpp | 63 ++++++++ js/src/jsproxy.h | 44 ++++++ js/src/jswrapper.cpp | 137 ------------------ js/src/jswrapper.h | 32 ---- js/xpconnect/wrappers/ChromeObjectWrapper.cpp | 13 +- js/xpconnect/wrappers/FilteringWrapper.cpp | 6 + js/xpconnect/wrappers/XrayWrapper.cpp | 8 + 12 files changed, 152 insertions(+), 173 deletions(-) diff --git a/configure.in b/configure.in index 35182a86c87..e01e33a274b 100644 --- a/configure.in +++ b/configure.in @@ -2157,8 +2157,9 @@ ia64*-hpux*) # that behavior) that it's better to turn it off. # MSVC warning C4819 warns some UTF-8 characters (e.g. copyright sign) # on non-Western system locales even if it is in a comment. + # khuey says we can safely ignore MSVC warning C4251 CFLAGS="$CFLAGS -wd4819" - CXXFLAGS="$CXXFLAGS -wd4345 -wd4351 -wd4482 -wd4800 -wd4819" + CXXFLAGS="$CXXFLAGS -wd4251 -wd4345 -wd4351 -wd4482 -wd4800 -wd4819" # make 'foo == bar;' error out CFLAGS="$CFLAGS -we4553" CXXFLAGS="$CXXFLAGS -we4553" diff --git a/js/src/configure.in b/js/src/configure.in index 2cb27e94943..cfaae2a3d8c 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1684,7 +1684,8 @@ ia64*-hpux*) CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" # MSVC warning C4244 is ubiquitous, useless, and annoying. - CXXFLAGS="$CXXFLAGS -wd4244" + # khuey says we can safely ignore MSVC warning C4251 + CXXFLAGS="$CXXFLAGS -wd4244 -wd4251" # make 'foo == bar;' error out CFLAGS="$CFLAGS -we4553" CXXFLAGS="$CXXFLAGS -we4553" diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e6a244b63be..232ffd03e86 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -881,6 +881,9 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) ionReturnOverride_(MagicValue(JS_ARG_POISON)), useHelperThreads_(useHelperThreads), requestedHelperThreadCount(-1), +#ifdef DEBUG + enteredPolicy(NULL), +#endif rngNonce(0) { /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 31b9cb6d458..d3b7f33e26b 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -569,7 +569,7 @@ struct MallocProvider namespace gc { class MarkingValidator; } // namespace gc - +class AutoEnterPolicy; } // namespace js struct JSRuntime : js::RuntimeFriendFields, @@ -1301,7 +1301,11 @@ struct JSRuntime : js::RuntimeFriendFields, return 0; #endif } +#ifdef DEBUG + public: + js::AutoEnterPolicy *enteredPolicy; +#endif private: /* * Used to ensure that compartments created at the same time get different diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 0fb00e637e2..e8cf5a0eade 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1416,6 +1416,13 @@ class JS_FRIEND_API(AutoCTypesActivityCallback) { } }; +#ifdef DEBUG +extern JS_FRIEND_API(void) +assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id); +#else +inline JS_FRIEND_API(void) assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id) {}; +#endif + } /* namespace js */ #endif /* jsfriendapi_h___ */ diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 6998383c7a4..d344998c2aa 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -63,6 +63,38 @@ js::AutoEnterPolicy::reportError(JSContext *cx, jsid id) } } +#ifdef DEBUG +void +js::AutoEnterPolicy::recordEnter(JSContext *cx, JSObject *proxy, jsid id) +{ + if (allowed()) { + context = cx; + enteredProxy.construct(cx, proxy); + enteredId.construct(cx, id); + prev = cx->runtime->enteredPolicy; + cx->runtime->enteredPolicy = this; + } +} + +void +js::AutoEnterPolicy::recordLeave() +{ + if (!enteredProxy.empty()) { + JS_ASSERT(context->runtime->enteredPolicy == this); + context->runtime->enteredPolicy = prev; + } +} + +JS_FRIEND_API(void) +js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id) +{ + MOZ_ASSERT(proxy->isProxy()); + MOZ_ASSERT(cx->runtime->enteredPolicy); + MOZ_ASSERT(cx->runtime->enteredPolicy->enteredProxy.ref().get() == proxy); + MOZ_ASSERT(cx->runtime->enteredPolicy->enteredId.ref().get() == id); +} +#endif + BaseProxyHandler::BaseProxyHandler(void *family) : mFamily(family), mHasPrototype(false), @@ -85,6 +117,7 @@ BaseProxyHandler::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool BaseProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) { + assertEnteredPolicy(cx, proxy_, id_); RootedObject proxy(cx, proxy_); RootedId id(cx, id_); AutoPropertyDescriptorRooter desc(cx); @@ -97,6 +130,7 @@ BaseProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) bool BaseProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) { + assertEnteredPolicy(cx, proxy_, id_); RootedObject proxy(cx, proxy_); RootedId id(cx, id_); AutoPropertyDescriptorRooter desc(cx); @@ -109,6 +143,7 @@ BaseProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) bool BaseProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver_, jsid id_, Value *vp) { + assertEnteredPolicy(cx, proxy, id_); RootedObject receiver(cx, receiver_); RootedId id(cx, id_); @@ -150,6 +185,7 @@ BaseProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy_, JSObject RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; + assertEnteredPolicy(cx, proxy, id); if (!has(cx, proxy, id, present)) return false; @@ -166,6 +202,7 @@ bool BaseProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver_, jsid id_, bool strict, Value *vp) { + assertEnteredPolicy(cx, proxy_, id_); RootedObject proxy(cx, proxy_), receiver(cx, receiver_); RootedId id(cx, id_); @@ -242,6 +279,7 @@ BaseProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver_, jsid bool BaseProxyHandler::keys(JSContext *cx, JSObject *proxyArg, AutoIdVector &props) { + assertEnteredPolicy(cx, proxyArg, JSID_VOID); JS_ASSERT(props.length() == 0); RootedObject proxy(cx, proxyArg); @@ -255,6 +293,7 @@ BaseProxyHandler::keys(JSContext *cx, JSObject *proxyArg, AutoIdVector &props) for (size_t j = 0, len = props.length(); j < len; j++) { JS_ASSERT(i <= j); jsid id = props[j]; + AutoWaivePolicy policy(cx, proxy, id); if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, 0)) return false; if (desc.obj && (desc.attrs & JSPROP_ENUMERATE)) @@ -270,6 +309,7 @@ BaseProxyHandler::keys(JSContext *cx, JSObject *proxyArg, AutoIdVector &props) bool BaseProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp) { + assertEnteredPolicy(cx, proxy_, JSID_VOID); RootedObject proxy(cx, proxy_); AutoIdVector props(cx); @@ -291,6 +331,7 @@ bool BaseProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp) { + assertEnteredPolicy(cx, proxy, JSID_VOID); AutoValueRooter rval(cx); RootedValue call(cx, GetCall(proxy)); JSBool ok = Invoke(cx, vp[1], call, argc, JS_ARGV(cx, vp), rval.addr()); @@ -303,6 +344,7 @@ bool BaseProxyHandler::construct(JSContext *cx, JSObject *proxy_, unsigned argc, Value *argv, Value *rval) { + assertEnteredPolicy(cx, proxy_, JSID_VOID); RootedObject proxy(cx, proxy_); RootedValue fval(cx, GetConstruct(proxy_)); if (fval.isUndefined()) @@ -321,6 +363,7 @@ BaseProxyHandler::obj_toString(JSContext *cx, JSObject *proxy) JSString * BaseProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent) { + assertEnteredPolicy(cx, proxy, JSID_VOID); Value fval = GetCall(proxy); if (IsFunctionProxy(proxy) && (fval.isPrimitive() || !fval.toObject().isFunction())) { @@ -366,6 +409,7 @@ BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl im bool BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) { + assertEnteredPolicy(cx, proxy, JSID_VOID); RootedValue val(cx, ObjectValue(*proxy.get())); js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, JSDVG_SEARCH_STACK, val, NullPtr()); @@ -402,6 +446,8 @@ bool DirectProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, proxy, id); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. RootedObject target(cx, GetProxyTargetObject(proxy)); return JS_GetPropertyDescriptorById(cx, target, id, 0, desc); } @@ -426,6 +472,7 @@ bool DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, proxy, id); RootedObject target(cx, GetProxyTargetObject(proxy)); return GetOwnPropertyDescriptor(cx, target, id, 0, desc); } @@ -434,6 +481,7 @@ bool DirectProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id_, PropertyDescriptor *desc) { + assertEnteredPolicy(cx, proxy, id_); RootedObject obj(cx, GetProxyTargetObject(proxy)); Rooted id(cx, id_); Rooted v(cx, desc->value); @@ -445,6 +493,7 @@ bool DirectProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props) { + assertEnteredPolicy(cx, proxy, JSID_VOID); RootedObject target(cx, GetProxyTargetObject(proxy)); return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN, &props); } @@ -453,6 +502,7 @@ bool DirectProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { RootedValue v(cx); + assertEnteredPolicy(cx, proxy, id); RootedObject target(cx, GetProxyTargetObject(proxy)); if (!JS_DeletePropertyById2(cx, target, id, v.address())) return false; @@ -467,6 +517,8 @@ bool DirectProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props) { + assertEnteredPolicy(cx, proxy, JSID_VOID); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. RootedObject target(cx, GetProxyTargetObject(proxy)); return GetPropertyNames(cx, target, 0, &props); } @@ -488,6 +540,7 @@ bool DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) { + assertEnteredPolicy(cx, proxy, JSID_VOID); JSBool b; RootedObject target(cx, GetProxyTargetObject(proxy)); if (!JS_HasInstance(cx, target, v, &b)) @@ -508,6 +561,7 @@ DirectProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue, JSString * DirectProxyHandler::obj_toString(JSContext *cx, JSObject *proxy) { + assertEnteredPolicy(cx, proxy, JSID_VOID); return obj_toStringHelper(cx, GetProxyTargetObject(proxy)); } @@ -515,6 +569,7 @@ JSString * DirectProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent) { + assertEnteredPolicy(cx, proxy, JSID_VOID); RootedObject target(cx, GetProxyTargetObject(proxy)); return fun_toStringHelper(cx, target, indent); } @@ -550,6 +605,8 @@ DirectProxyHandler::DirectProxyHandler(void *family) bool DirectProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { + assertEnteredPolicy(cx, proxy, id); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. JSBool found; RootedObject target(cx, GetProxyTargetObject(proxy)); if (!JS_HasPropertyById(cx, target, id, &found)) @@ -561,6 +618,7 @@ DirectProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) bool DirectProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { + assertEnteredPolicy(cx, proxy, id); RootedObject target(cx, GetProxyTargetObject(proxy)); AutoPropertyDescriptorRooter desc(cx); if (!JS_GetPropertyDescriptorById(cx, target, id, 0, &desc)) @@ -573,6 +631,7 @@ bool DirectProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver_, jsid id_, Value *vp) { + assertEnteredPolicy(cx, proxy, id_); RootedObject receiver(cx, receiver_); RootedId id(cx, id_); RootedValue value(cx); @@ -588,6 +647,7 @@ bool DirectProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiverArg, jsid id_, bool strict, Value *vp) { + assertEnteredPolicy(cx, proxy, id_); RootedId id(cx, id_); Rooted receiver(cx, receiverArg); RootedValue value(cx, *vp); @@ -602,6 +662,7 @@ DirectProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiverArg, bool DirectProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props) { + assertEnteredPolicy(cx, proxy, JSID_VOID); return GetPropertyNames(cx, GetProxyTargetObject(proxy), JSITER_OWNONLY, &props); } @@ -609,6 +670,8 @@ bool DirectProxyHandler::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp) { + assertEnteredPolicy(cx, proxy, JSID_VOID); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. Rooted target(cx, GetProxyTargetObject(proxy)); RootedValue value(cx); if (!GetIterator(cx, target, flags, &value)) diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 315fb9b01eb..b2615df9833 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -349,22 +349,66 @@ class JS_FRIEND_API(AutoEnterPolicy) typedef BaseProxyHandler::Action Action; AutoEnterPolicy(JSContext *cx, BaseProxyHandler *handler, JSObject *wrapper, jsid id, Action act, bool mayThrow) +#ifdef DEBUG + : context(NULL) +#endif { allow = handler->hasPolicy() ? handler->enter(cx, wrapper, id, act, &rv) : true; + recordEnter(cx, wrapper, id); if (!allow && !rv && mayThrow) reportError(cx, id); } + virtual ~AutoEnterPolicy() { recordLeave(); } inline bool allowed() { return allow; } inline bool returnValue() { JS_ASSERT(!allowed()); return rv; } protected: + // no-op constructor for subclass + AutoEnterPolicy() +#ifdef DEBUG + : context(NULL) +#endif + {}; void reportError(JSContext *cx, jsid id); bool allow; bool rv; + +#ifdef DEBUG + JSContext *context; + mozilla::Maybe enteredProxy; + mozilla::Maybe enteredId; + // NB: We explicitly don't track the entered action here, because sometimes + // SET traps do an implicit GET during their implementation, leading to + // spurious assertions. + AutoEnterPolicy *prev; + void recordEnter(JSContext *cx, JSObject *proxy, jsid id); + void recordLeave(); + + friend JS_FRIEND_API(void) assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id); +#else + inline void recordEnter(JSContext *cx, JSObject *proxy, jsid id) {} + inline void recordLeave() {} +#endif + }; +#ifdef DEBUG +class JS_FRIEND_API(AutoWaivePolicy) : public AutoEnterPolicy { +public: + AutoWaivePolicy(JSContext *cx, JSObject *proxy, jsid id) + { + allow = true; + recordEnter(cx, proxy, id); + } +}; +#else +class JS_FRIEND_API(AutoWaivePolicy) { + public: AutoWaivePolicy(JSContext *cx, JSObject *proxy, jsid id) {}; +}; +#endif + } /* namespace js */ extern JS_FRIEND_API(JSObject *) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 819b64a8b60..332b4c3ae1e 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -126,54 +126,6 @@ Wrapper::~Wrapper() { } -bool -Wrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapperArg, - jsid id, PropertyDescriptor *desc, unsigned flags) -{ - RootedObject wrapper(cx, wrapperArg); - JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - return DirectProxyHandler::getPropertyDescriptor(cx, wrapper, id, desc, flags); -} - -bool -Wrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapperArg, - jsid id, PropertyDescriptor *desc, unsigned flags) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags); -} - -bool -Wrapper::defineProperty(JSContext *cx, JSObject *wrapperArg, jsid id, - PropertyDescriptor *desc) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::defineProperty(cx, wrapper, id, desc); -} - -bool -Wrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapperArg, - AutoIdVector &props) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::getOwnPropertyNames(cx, wrapper, props); -} - -bool -Wrapper::delete_(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::delete_(cx, wrapper, id, bp); -} - -bool -Wrapper::enumerate(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props) -{ - RootedObject wrapper(cx, wrapperArg); - JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - return DirectProxyHandler::enumerate(cx, wrapper, props); -} - /* * Ordinarily, the convert trap would require unwrapping. However, the default * implementation of convert, JS_ConvertStub, obtains a default value by calling @@ -208,95 +160,6 @@ Wrapper::defaultValue(JSContext *cx, JSObject *wrapperArg, JSType hint, Value *v return DirectProxyHandler::defaultValue(cx, wrapper, hint, vp); } -bool -Wrapper::has(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) -{ - RootedObject wrapper(cx, wrapperArg); - JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - return DirectProxyHandler::has(cx, wrapper, id, bp); -} - -bool -Wrapper::hasOwn(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::hasOwn(cx, wrapper, id, bp); -} - -bool -Wrapper::get(JSContext *cx, JSObject *wrapperArg, JSObject *receiver, jsid id, Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::get(cx, wrapper, receiver, id, vp); -} - -bool -Wrapper::set(JSContext *cx, JSObject *wrapperArg, JSObject *receiver, jsid id, bool strict, - Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::set(cx, wrapper, receiver, id, strict, vp); -} - -bool -Wrapper::keys(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::keys(cx, wrapper, props); -} - -bool -Wrapper::iterate(JSContext *cx, JSObject *wrapperArg, unsigned flags, Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - return DirectProxyHandler::iterate(cx, wrapper, flags, vp); -} - -bool -Wrapper::call(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::call(cx, wrapper, argc, vp); -} - -bool -Wrapper::construct(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *argv, Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::construct(cx, wrapper, argc, argv, vp); -} - -bool -Wrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) -{ - RootedObject wrapper(cx, &args.thisv().toObject()); - // Note - we don't enter a policy here because our security architecture guards - // against nativeCall by overriding the trap itself in the right circumstances. - return DirectProxyHandler::nativeCall(cx, test, impl, args); -} - -bool -Wrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, bool *bp) -{ - return DirectProxyHandler::hasInstance(cx, wrapper, v, bp); -} - -JSString * -Wrapper::obj_toString(JSContext *cx, JSObject *wrapperArg) -{ - RootedObject wrapper(cx, wrapperArg); - JSString *str = DirectProxyHandler::obj_toString(cx, wrapper); - return str; -} - -JSString * -Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) -{ - JSString *str = DirectProxyHandler::fun_toString(cx, wrapper, indent); - return str; -} - Wrapper Wrapper::singleton((unsigned)0); Wrapper Wrapper::singletonWithPrototype((unsigned)0, true); diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index db03e9f55f4..c3967026ef6 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -66,38 +66,6 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler virtual ~Wrapper(); /* ES5 Harmony fundamental wrapper traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, PropertyDescriptor *desc, - unsigned flags) MOZ_OVERRIDE; - virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, PropertyDescriptor *desc, - unsigned flags) MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - - /* ES5 Harmony derived wrapper traps. */ - virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; - virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) MOZ_OVERRIDE; - virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict, - Value *vp) MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE; - virtual bool iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp) MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp) MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *rval) MOZ_OVERRIDE; - virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) MOZ_OVERRIDE; - virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, bool *bp) MOZ_OVERRIDE; - virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; - virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, Value *vp) MOZ_OVERRIDE; diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp index 5868089d5d7..68a84d0dc70 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp @@ -14,6 +14,8 @@ namespace xpc { // their prototype, we have to instrument the traps to do this manually. ChromeObjectWrapper ChromeObjectWrapper::singleton; +using js::assertEnteredPolicy; + static bool AllowedByBase(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { @@ -58,6 +60,7 @@ ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); // First, try a lookup on the base wrapper if permitted. desc->obj = NULL; if (AllowedByBase(cx, wrapper, id, Wrapper::GET) && @@ -87,6 +90,7 @@ ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, bool ChromeObjectWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { + assertEnteredPolicy(cx, wrapper, id); // Try the lookup on the base wrapper if permitted. if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) && !ChromeObjectWrapperBase::has(cx, wrapper, id, bp)) @@ -114,6 +118,7 @@ bool ChromeObjectWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp) { + assertEnteredPolicy(cx, wrapper, id); vp->setUndefined(); JSPropertyDescriptor desc; // Only call through to the get trap on the underlying object if we're @@ -155,7 +160,13 @@ ChromeObjectWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, // COWs fail silently for GETs, and that also happens to be the only case // where we might want to redirect the lookup to the home prototype chain. *bp = (act == Wrapper::GET); - return *bp && PropIsFromStandardPrototype(cx, wrapper, id); + if (!*bp || id == JSID_VOID) + return false; + + // Note that PropIsFromStandardPrototype needs to invoke getPropertyDescriptor + // before we've fully entered the policy. Waive our policy. + js::AutoWaivePolicy policy(cx, wrapper, id); + return PropIsFromStandardPrototype(cx, wrapper, id); } } diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index 69c5a976b48..d8d06841f92 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -63,6 +63,7 @@ bool FilteringWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); if (!Base::getPropertyDescriptor(cx, wrapper, id, desc, flags)) return false; return FilterSetter(cx, wrapper, id, desc); @@ -74,6 +75,7 @@ FilteringWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags)) return false; return FilterSetter(cx, wrapper, id, desc); @@ -83,6 +85,7 @@ template bool FilteringWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::getOwnPropertyNames(cx, wrapper, props) && Filter(cx, wrapper, props); } @@ -91,6 +94,7 @@ template bool FilteringWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::enumerate(cx, wrapper, props) && Filter(cx, wrapper, props); } @@ -99,6 +103,7 @@ template bool FilteringWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::keys(cx, wrapper, props) && Filter(cx, wrapper, props); } @@ -107,6 +112,7 @@ template bool FilteringWrapper::iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); // We refuse to trigger the iterator hook across chrome wrappers because // we don't know how to censor custom iterator objects. Instead we trigger // the default proxy iterate trap, which will ask enumerate() for the list diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 84dcab856fc..f20e04b1b6f 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -1410,6 +1410,7 @@ bool XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper); if (Traits::isResolving(cx, holder, id)) { desc->obj = NULL; @@ -1528,6 +1529,7 @@ bool XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper); if (Traits::isResolving(cx, holder, id)) { desc->obj = NULL; @@ -1574,6 +1576,7 @@ bool XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc) { + assertEnteredPolicy(cx, wrapper, id); // Redirect access straight to the wrapper if we should be transparent. if (XrayUtils::IsTransparent(cx, wrapper, id)) { JSObject *obj = Traits::getTargetObject(wrapper); @@ -1626,6 +1629,7 @@ bool XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, JS::AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props); } @@ -1633,6 +1637,7 @@ template bool XrayWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { + assertEnteredPolicy(cx, wrapper, id); // Redirect access straight to the wrapper if we should be transparent. if (XrayUtils::IsTransparent(cx, wrapper, id)) { JSObject *obj = Traits::getTargetObject(wrapper); @@ -1669,6 +1674,7 @@ bool XrayWrapper::enumerate(JSContext *cx, JSObject *wrapper, unsigned flags, JS::AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); // Redirect access straight to the wrapper if we should be transparent. if (XrayUtils::IsTransparent(cx, wrapper, JSID_VOID)) { JSObject *obj = Traits::getTargetObject(wrapper); @@ -1762,6 +1768,7 @@ template bool XrayWrapper::call(JSContext *cx, JSObject *wrapper, unsigned argc, js::Value *vp) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Traits::call(cx, wrapper, argc, vp); } @@ -1770,6 +1777,7 @@ bool XrayWrapper::construct(JSContext *cx, JSObject *wrapper, unsigned argc, js::Value *argv, js::Value *rval) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Traits::construct(cx, wrapper, argc, argv, rval); } From d71109ac5ff7ccafc2b06002c0dc23d73e75e8f1 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Mon, 25 Feb 2013 16:56:45 -0500 Subject: [PATCH 54/64] Bug 845008 - Metro's save file picker always throws on appendFilter. r=tabraldes --- widget/windows/winrt/nsMetroFilePicker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/widget/windows/winrt/nsMetroFilePicker.cpp b/widget/windows/winrt/nsMetroFilePicker.cpp index beb4b3aafd3..be37823bcc2 100644 --- a/widget/windows/winrt/nsMetroFilePicker.cpp +++ b/widget/windows/winrt/nsMetroFilePicker.cpp @@ -474,6 +474,7 @@ nsMetroFilePicker::AppendFilter(const nsAString& aTitle, boolean replaced; map->Insert(key.Get(), saveTypes.Get(), &replaced); } + break; default: return NS_ERROR_FAILURE; From 0c513c127dbad5c8d746fd90d11bc34bd563aa57 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Mon, 25 Feb 2013 17:11:02 -0500 Subject: [PATCH 55/64] Bug 843888 - Reduce LayerView overdraw by removing the background. r=sriram --- mobile/android/base/resources/layout/shared_ui_components.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mobile/android/base/resources/layout/shared_ui_components.xml b/mobile/android/base/resources/layout/shared_ui_components.xml index 51b7119c6a3..964cf4283e4 100644 --- a/mobile/android/base/resources/layout/shared_ui_components.xml +++ b/mobile/android/base/resources/layout/shared_ui_components.xml @@ -10,8 +10,7 @@ + android:layout_height="fill_parent"/> Date: Mon, 25 Feb 2013 15:17:12 -0700 Subject: [PATCH 56/64] Bug 844364 - Fix bogus assertion, inverted test when compiling JSOP_EVAL, r=jandem. --- js/src/ion/IonBuilder.cpp | 17 ++++++++--------- js/src/jit-test/tests/ion/bug844364.js | 6 ++++++ js/src/jit-test/tests/ion/bug844459.js | 7 +++++++ js/src/jsinterpinlines.h | 6 ++++-- 4 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 js/src/jit-test/tests/ion/bug844364.js create mode 100644 js/src/jit-test/tests/ion/bug844459.js diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index 0a9062b7e8e..a90fc9cf70e 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -4488,15 +4488,14 @@ IonBuilder::jsop_eval(uint32_t argc) return abort("Direct eval in global code"); types::StackTypeSet *thisTypes = oracle->thisTypeSet(script()); - if (!thisTypes) { - // The 'this' value for the outer and eval scripts must be the - // same. This is not guaranteed if a primitive string/number/etc. - // is passed through to the eval invoke as the primitive may be - // boxed into different objects if accessed via 'this'. - JSValueType type = thisTypes->getKnownTypeTag(); - if (type != JSVAL_TYPE_OBJECT && type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED) - return abort("Direct eval from script with maybe-primitive 'this'"); - } + + // The 'this' value for the outer and eval scripts must be the + // same. This is not guaranteed if a primitive string/number/etc. + // is passed through to the eval invoke as the primitive may be + // boxed into different objects if accessed via 'this'. + JSValueType type = thisTypes->getKnownTypeTag(); + if (type != JSVAL_TYPE_OBJECT && type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED) + return abort("Direct eval from script with maybe-primitive 'this'"); CallInfo callInfo(cx, /* constructing = */ false); if (!callInfo.init(current, argc)) diff --git a/js/src/jit-test/tests/ion/bug844364.js b/js/src/jit-test/tests/ion/bug844364.js new file mode 100644 index 00000000000..c94514c8036 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug844364.js @@ -0,0 +1,6 @@ + +function f() { + eval("this") +} +f() +f() diff --git a/js/src/jit-test/tests/ion/bug844459.js b/js/src/jit-test/tests/ion/bug844459.js new file mode 100644 index 00000000000..b1901d77478 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug844459.js @@ -0,0 +1,7 @@ + + +function testEvalThrow(x, y) { + eval(""); +} +for (var i = 0; i < 5; i++) + testEvalThrow.call(""); diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c2900a4b29d..4d12a86fe32 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -97,9 +97,11 @@ ComputeThis(JSContext *cx, AbstractFramePtr frame) * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the * eval's frame will get the wrapper, but the function's frame will not. To prevent * this, we always wrap a function's |this| before pushing an eval frame, and should - * thus never see an unwrapped primitive in a non-strict eval function frame. + * thus never see an unwrapped primitive in a non-strict eval function frame. Null + * and undefined |this| values will unwrap to the same object in the function and + * eval frames, so are not required to be wrapped. */ - JS_ASSERT(!frame.isEvalFrame()); + JS_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull()); } bool modified; if (!BoxNonStrictThis(cx, &thisv, &modified)) From b9530756adf4148e4b14ed51f359ffd6703c61ba Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 25 Feb 2013 15:21:13 -0700 Subject: [PATCH 57/64] Bug 844482 - Watch for OOM disabling TI under CreateThisForFunction, 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 bd91164fcee..8e0034941f9 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -1495,7 +1495,7 @@ ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, bool isConst RootedScript scriptRoot(cx, script); RootedObject callee(cx, &fp.callee()); RootedObject obj(cx, CreateThisForFunction(cx, callee, fp.useNewType())); - if (!obj) + if (!obj || !ion::IsEnabled(cx)) // Note: OOM under CreateThis can disable TI. return Method_Skipped; fp.thisValue().setObject(*obj); script = scriptRoot; From 3a2fdeb18a30150fc2a8f8a034ffd8c1c0c97261 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 25 Feb 2013 12:10:41 -0500 Subject: [PATCH 58/64] Bug 780474 - Disable the struct/class mismatch warning on MSVC as well; r=glandium --HG-- extra : rebase_source : 3e593b71e589df349abc41f9182739305478690f --- build/autoconf/compiler-opts.m4 | 9 +++++++++ js/src/build/autoconf/compiler-opts.m4 | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 index ec5fe6b688a..e09a4563c3c 100644 --- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -94,6 +94,15 @@ if test "$CLANG_CXX"; then _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage -Wno-mismatched-tags" fi +if test -z "$GNU_CC"; then + case "$target" in + *-mingw*) + ## Warning 4099 (equivalent of mismatched-tags) is disabled (bug 780474) + ## for the same reasons as above. + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -wd4099" + esac +fi + if test "$GNU_CC"; then CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fno-exceptions" diff --git a/js/src/build/autoconf/compiler-opts.m4 b/js/src/build/autoconf/compiler-opts.m4 index ec5fe6b688a..e09a4563c3c 100644 --- a/js/src/build/autoconf/compiler-opts.m4 +++ b/js/src/build/autoconf/compiler-opts.m4 @@ -94,6 +94,15 @@ if test "$CLANG_CXX"; then _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage -Wno-mismatched-tags" fi +if test -z "$GNU_CC"; then + case "$target" in + *-mingw*) + ## Warning 4099 (equivalent of mismatched-tags) is disabled (bug 780474) + ## for the same reasons as above. + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -wd4099" + esac +fi + if test "$GNU_CC"; then CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections -fno-exceptions" From 87b07b289898f1ba73d2ac9fb1c85355b3655873 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 22 Feb 2013 19:59:26 -0500 Subject: [PATCH 59/64] Bug 844169 - Part 1: Rename nsHTMLIFrameElement to mozilla::dom::HTMLIFrameElement; r=bzbarsky --HG-- rename : content/html/content/src/nsHTMLIFrameElement.cpp => content/html/content/src/HTMLIFrameElement.cpp rename : content/html/content/src/nsHTMLIFrameElement.h => content/html/content/src/HTMLIFrameElement.h extra : rebase_source : 67c393954a0c46f80b8729277504e9ced5c64d33 --- content/base/src/nsFrameLoader.cpp | 6 +- ...FrameElement.cpp => HTMLIFrameElement.cpp} | 93 ++++++++++--------- ...TMLIFrameElement.h => HTMLIFrameElement.h} | 25 +++-- content/html/content/src/Makefile.in | 4 +- dom/browser-element/BrowserElementParent.cpp | 13 +-- 5 files changed, 78 insertions(+), 63 deletions(-) rename content/html/content/src/{nsHTMLIFrameElement.cpp => HTMLIFrameElement.cpp} (71%) rename content/html/content/src/{nsHTMLIFrameElement.h => HTMLIFrameElement.h} (73%) diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 06f8e5d279b..2b581c4049f 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -86,7 +86,7 @@ #include "sampler.h" #include "jsapi.h" -#include "nsHTMLIFrameElement.h" +#include "mozilla/dom/HTMLIFrameElement.h" #include "nsSandboxFlags.h" #include "mozilla/dom/StructuredCloneUtils.h" @@ -450,8 +450,8 @@ nsFrameLoader::ReallyStartLoadingInternal() // Is this an