From d505e6560d0c618ba7cc3bfcaa751bae28f3a786 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 9 Sep 2014 09:58:07 +0100 Subject: [PATCH 01/69] Bug 1063856 - Add more counter styles from the Predefined Counter Styles document, for better interop and web-compat. --- layout/style/counterstyles.css | 225 ++++++++++++++++++++++++--------- 1 file changed, 162 insertions(+), 63 deletions(-) diff --git a/layout/style/counterstyles.css b/layout/style/counterstyles.css index 93b0773597b4..bc2f4c7d9d21 100644 --- a/layout/style/counterstyles.css +++ b/layout/style/counterstyles.css @@ -113,104 +113,203 @@ system: extends trad-chinese-informal; } -/* Mozilla-specific counter styles */ +/* Predefined Counter Styles */ -/* Numeric */ +/* Some counter styles defined in Predefined Counter Styles are also included + * here for compatibility with other browsers. */ -@counter-style -moz-arabic-indic { +@counter-style arabic-indic { system: numeric; symbols: \660 \661 \662 \663 \664 \665 \666 \667 \668 \669; } -@counter-style -moz-persian { +@counter-style persian { system: numeric; symbols: \6F0 \6F1 \6F2 \6F3 \6F4 \6F5 \6F6 \6F7 \6F8 \6F9; } -@counter-style -moz-urdu { - system: extends -moz-persian; +@counter-style lower-armenian { + system: additive; + range: 1 9999; + additive-symbols: 9000 \584, 8000 \583, 7000 \582, 6000 \581, 5000 \580, 4000 \57F, 3000 \57E, 2000 \57D, 1000 \57C, 900 \57B, 800 \57A, 700 \579, 600 \578, 500 \577, 400 \576, 300 \575, 200 \574, 100 \573, 90 \572, 80 \571, 70 \570, 60 \56F, 50 \56E, 40 \56D, 30 \56C, 20 \56B, 10 \56A, 9 \569, 8 \568, 7 \567, 6 \566, 5 \565, 4 \564, 3 \563, 2 \562, 1 \561; } -@counter-style -moz-devanagari { +@counter-style upper-armenian { + system: additive; + range: 1 9999; + additive-symbols: 9000 \554, 8000 \553, 7000 \552, 6000 \551, 5000 \550, 4000 \54F, 3000 \54E, 2000 \54D, 1000 \54C, 900 \54B, 800 \54A, 700 \549, 600 \548, 500 \547, 400 \546, 300 \545, 200 \544, 100 \543, 90 \542, 80 \541, 70 \540, 60 \53F, 50 \53E, 40 \53D, 30 \53C, 20 \53B, 10 \53A, 9 \539, 8 \538, 7 \537, 6 \536, 5 \535, 4 \534, 3 \533, 2 \532, 1 \531; +} + +@counter-style bengali { system: numeric; - symbols: \966 \967 \968 \969 \96A \96B \96C \96D \96F \970; + symbols: \9E6 \9E7 \9E8 \9E9 \9EA \9EB \9EC \9ED \9EE \9EF; } -@counter-style -moz-bengali { +@counter-style devanagari { system: numeric; - symbols: \9E6 \9E7 \9E8 \9E9 \9EA \9EB \9EC \9ED \9EF \9F0; + symbols: \966 \967 \968 \969 \96A \96B \96C \96D \96E \96F; } -@counter-style -moz-gurmukhi { +@counter-style gujarati { system: numeric; - symbols: \A66 \A67 \A68 \A69 \A6A \A6B \A6C \A6D \A6F \A70; + symbols: \AE6 \AE7 \AE8 \AE9 \AEA \AEB \AEC \AED \AEE \AEF; } -@counter-style -moz-gujarati { +@counter-style gurmukhi { system: numeric; - symbols: \AE6 \AE7 \AE8 \AE9 \AEA \AEB \AEC \AED \AEF \AF0; + symbols: \A66 \A67 \A68 \A69 \A6A \A6B \A6C \A6D \A6E \A6F; } -@counter-style -moz-oriya { - system: numeric; - symbols: \B66 \B67 \B68 \B69 \B6A \B6B \B6C \B6D \B6F \B70; -} - -@counter-style -moz-tamil { - system: numeric; - symbols: \BE6 \BE7 \BE8 \BE9 \BEA \BEB \BEC \BED \BEE \BEF; -} - -@counter-style -moz-telugu { - system: numeric; - symbols: \C66 \C67 \C68 \C69 \C6A \C6B \C6C \C6D \C6F \C70; -} - -@counter-style -moz-kannada { - system: numeric; - symbols: \CE6 \CE7 \CE8 \CE9 \CEA \CEB \CEC \CED \CEF \CF0; -} - -@counter-style -moz-malayalam { - system: numeric; - symbols: \D66 \D67 \D68 \D69 \D6A \D6B \D6C \D6D \D6F \D70; -} - -@counter-style -moz-thai { - system: numeric; - symbols: \E50 \E51 \E52 \E53 \E54 \E55 \E56 \E57 \E58 \E59; -} - -@counter-style -moz-lao { - system: numeric; - symbols: \ED0 \ED1 \ED2 \ED3 \ED4 \ED5 \ED6 \ED7 \ED8 \ED9; -} - -@counter-style -moz-myanmar { - system: numeric; - symbols: \1040 \1041 \1042 \1043 \1044 \1045 \1046 \1047 \1048 \1049; -} - -@counter-style -moz-khmer { - system: numeric; - symbols: \17E0 \17E1 \17E2 \17E3 \17E4 \17E5 \17E6 \17E7 \17E8 \17E9; -} - -/* Alphabetic */ - -@counter-style -moz-cjk-heavenly-stem { +@counter-style cjk-heavenly-stem { system: alphabetic; symbols: \7532 \4E59 \4E19 \4E01 \620A \5DF1 \5E9A \8F9B \58EC \7678; fallback: cjk-decimal; suffix: '\3001'; } -@counter-style -moz-cjk-earthly-branch { + +@counter-style cjk-earthly-branch { system: alphabetic; symbols: \5B50 \4E11 \5BC5 \536F \8FB0 \5DF3 \5348 \672A \7533 \9149 \620C \4EA5; fallback: cjk-decimal; suffix: '\3001'; } +@counter-style kannada { + system: numeric; + symbols: \CE6 \CE7 \CE8 \CE9 \CEA \CEB \CEC \CED \CEE \CEF; +} + +@counter-style khmer { + system: numeric; + symbols: \17E0 \17E1 \17E2 \17E3 \17E4 \17E5 \17E6 \17E7 \17E8 \17E9; +} + +@counter-style cambodian { + system: extends khmer; +} + +@counter-style lao { + system: numeric; + symbols: \ED0 \ED1 \ED2 \ED3 \ED4 \ED5 \ED6 \ED7 \ED8 \ED9; +} + +@counter-style malayalam { + system: numeric; + symbols: \D66 \D67 \D68 \D69 \D6A \D6B \D6C \D6D \D6E \D6F; +} + +@counter-style mongolian { + system: numeric; + symbols: \1810 \1811 \1812 \1813 \1814 \1815 \1816 \1817 \1818 \1819; +} + +@counter-style myanmar { + system: numeric; + symbols: \1040 \1041 \1042 \1043 \1044 \1045 \1046 \1047 \1048 \1049; +} + +@counter-style oriya { + system: numeric; + symbols: \B66 \B67 \B68 \B69 \B6A \B6B \B6C \B6D \B6E \B6F; +} + +@counter-style tamil { + system: numeric; + symbols: \BE6 \BE7 \BE8 \BE9 \BEA \BEB \BEC \BED \BEE \BEF; +} + +@counter-style telugu { + system: numeric; + symbols: \C66 \C67 \C68 \C69 \C6A \C6B \C6C \C6D \C6E \C6F; +} + +@counter-style thai { + system: numeric; + symbols: \E50 \E51 \E52 \E53 \E54 \E55 \E56 \E57 \E58 \E59; +} + +@counter-style tibetan { + system: numeric; + symbols: \F20 \F21 \F22 \F23 \F24 \F25 \F26 \F27 \F28 \F29; +} + +/* Mozilla-specific counter styles */ + +/* Numeric */ + +@counter-style -moz-arabic-indic { + system: extends arabic-indic; +} + +@counter-style -moz-persian { + system: extends persian; +} + +@counter-style -moz-urdu { + system: extends persian; +} + +@counter-style -moz-devanagari { + system: extends devanagari; +} + +@counter-style -moz-bengali { + system: extends bengali; +} + +@counter-style -moz-gurmukhi { + system: extends gurmukhi; +} + +@counter-style -moz-gujarati { + system: extends gujarati; +} + +@counter-style -moz-oriya { + system: extends oriya; +} + +@counter-style -moz-tamil { + system: extends tamil; +} + +@counter-style -moz-telugu { + system: extends telugu; +} + +@counter-style -moz-kannada { + system: extends kannada; +} + +@counter-style -moz-malayalam { + system: extends malayalam; +} + +@counter-style -moz-thai { + system: extends thai; +} + +@counter-style -moz-lao { + system: extends lao; +} + +@counter-style -moz-myanmar { + system: extends myanmar; +} + +@counter-style -moz-khmer { + system: extends khmer; +} + +/* Alphabetic */ + +@counter-style -moz-cjk-heavenly-stem { + system: extends cjk-heavenly-stem; +} +@counter-style -moz-cjk-earthly-branch { + system: extends cjk-earthly-branch; +} + @counter-style -moz-hangul { system: alphabetic; symbols: \AC00 \B098 \B2E4 \B77C \B9C8 \BC14 \C0AC \C544 \C790 \CC28 \CE74 \D0C0 \D30C \D558; From 2d0bcec62992d38def86f1ab2846fe734c6f92c5 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Tue, 9 Sep 2014 10:48:28 +0100 Subject: [PATCH 02/69] Bug 1041262 Disable autofilling of search engines to avoid failures in unified complete tests when searchengines is in the platform directory. r=mak --- .../places/tests/unifiedcomplete/head_autocomplete.js | 1 + toolkit/components/places/tests/unifiedcomplete/test_417798.js | 2 ++ .../places/tests/unifiedcomplete/test_match_beginning.js | 2 ++ .../places/tests/unifiedcomplete/test_word_boundary_search.js | 2 ++ 4 files changed, 7 insertions(+) diff --git a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js index 633e4c1af56f..c1779be23583 100644 --- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js +++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js @@ -27,6 +27,7 @@ function* cleanup() { Services.prefs.clearUserPref("browser.urlbar.autocomplete.enabled"); Services.prefs.clearUserPref("browser.urlbar.autoFill"); Services.prefs.clearUserPref("browser.urlbar.autoFill.typed"); + Services.prefs.clearUserPref("browser.urlbar.autoFill.searchEngines"); remove_all_bookmarks(); yield promiseClearHistory(); } diff --git a/toolkit/components/places/tests/unifiedcomplete/test_417798.js b/toolkit/components/places/tests/unifiedcomplete/test_417798.js index 64336fbe5199..d10bf7ec2643 100644 --- a/toolkit/components/places/tests/unifiedcomplete/test_417798.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_417798.js @@ -8,6 +8,8 @@ */ add_task(function* test_javascript_match() { + Services.prefs.setBoolPref("browser.urlbar.autoFill.searchEngines", false); + let uri1 = NetUtil.newURI("http://abc/def"); let uri2 = NetUtil.newURI("javascript:5"); yield promiseAddVisits([ { uri: uri1, title: "Title with javascript:" } ]); diff --git a/toolkit/components/places/tests/unifiedcomplete/test_match_beginning.js b/toolkit/components/places/tests/unifiedcomplete/test_match_beginning.js index e34f7a38bcb0..e08c195a37f6 100644 --- a/toolkit/components/places/tests/unifiedcomplete/test_match_beginning.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_match_beginning.js @@ -8,6 +8,8 @@ */ add_task(function* test_match_beginning() { + Services.prefs.setBoolPref("browser.urlbar.autoFill.searchEngines", false); + let uri1 = NetUtil.newURI("http://x.com/y"); let uri2 = NetUtil.newURI("https://y.com/x"); yield promiseAddVisits([ { uri: uri1, title: "a b" }, diff --git a/toolkit/components/places/tests/unifiedcomplete/test_word_boundary_search.js b/toolkit/components/places/tests/unifiedcomplete/test_word_boundary_search.js index 550c7ae97aa3..3fd4683e1ecd 100644 --- a/toolkit/components/places/tests/unifiedcomplete/test_word_boundary_search.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_word_boundary_search.js @@ -18,6 +18,8 @@ let katakana = ["\u30a8", "\u30c9"]; // E, Do let ideograph = ["\u4efb", "\u5929", "\u5802"]; // Nin Ten Do add_task(function* test_escape() { + Services.prefs.setBoolPref("browser.urlbar.autoFill.searchEngines", false); + let uri1 = NetUtil.newURI("http://matchme/"); let uri2 = NetUtil.newURI("http://dontmatchme/"); let uri3 = NetUtil.newURI("http://title/1"); From 3f39c19e7f4f23f21cb1d62e63c5c1186f20f1bd Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Tue, 9 Sep 2014 13:00:03 +0100 Subject: [PATCH 03/69] Bug 1064063 - switch elements ignore default if allowReorder is set. r=dholbert --- content/svg/content/src/SVGSwitchElement.cpp | 14 +++++++--- content/svg/content/src/SVGTests.cpp | 4 +++ content/svg/content/src/SVGTests.h | 3 ++- content/svg/content/test/switch-helper.svg | 2 +- content/svg/content/test/test_switch.xhtml | 28 +++++++++++++++----- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/content/svg/content/src/SVGSwitchElement.cpp b/content/svg/content/src/SVGSwitchElement.cpp index a4304b4f35a6..4fb4f13a8b06 100644 --- a/content/svg/content/src/SVGSwitchElement.cpp +++ b/content/svg/content/src/SVGSwitchElement.cpp @@ -125,8 +125,8 @@ nsIContent * SVGSwitchElement::FindActiveChild() const { bool allowReorder = AttrValueIs(kNameSpaceID_None, - nsGkAtoms::allowReorder, - nsGkAtoms::yes, eCaseMatters); + nsGkAtoms::allowReorder, + nsGkAtoms::yes, eCaseMatters); const nsAdoptingString& acceptLangs = Preferences::GetLocalizedString("intl.accept_languages"); @@ -134,6 +134,7 @@ SVGSwitchElement::FindActiveChild() const if (allowReorder && !acceptLangs.IsEmpty()) { int32_t bestLanguagePreferenceRank = -1; nsIContent *bestChild = nullptr; + nsIContent *defaultChild = nullptr; for (nsIContent* child = nsINode::GetFirstChild(); child; child = child->GetNextSibling()) { @@ -152,7 +153,12 @@ SVGSwitchElement::FindActiveChild() const // best possible match return child; case -1: - // not found + // no match + break; + case -2: + // no systemLanguage attribute. If there's nothing better + // we'll use the last such child. + defaultChild = child; break; default: if (bestLanguagePreferenceRank == -1 || @@ -167,7 +173,7 @@ SVGSwitchElement::FindActiveChild() const bestChild = child; } } - return bestChild; + return bestChild ? bestChild : defaultChild; } for (nsIContent* child = nsINode::GetFirstChild(); diff --git a/content/svg/content/src/SVGTests.cpp b/content/svg/content/src/SVGTests.cpp index c08e06784151..b0ae3cc8d569 100644 --- a/content/svg/content/src/SVGTests.cpp +++ b/content/svg/content/src/SVGTests.cpp @@ -77,6 +77,10 @@ SVGTests::GetBestLanguagePreferenceRank(const nsSubstring& aAcceptLangs) const { const nsDefaultStringComparator defaultComparator; + if (!mStringListAttributes[LANGUAGE].IsExplicitlySet()) { + return -2; + } + int32_t lowestRank = -1; for (uint32_t i = 0; i < mStringListAttributes[LANGUAGE].Length(); i++) { diff --git a/content/svg/content/src/SVGTests.h b/content/svg/content/src/SVGTests.h index 6259f1f7b3fd..c5c7fbb8d0ea 100644 --- a/content/svg/content/src/SVGTests.h +++ b/content/svg/content/src/SVGTests.h @@ -42,7 +42,8 @@ public: * exactly equals one of the language names or exactly equals a prefix of * one of the language names in the systemLanguage attribute. * @returns 2 * the lowest index in the aAcceptLangs that matches + 1 - * if only the prefix matches, or -1 if no indices match. + * if only the prefix matches, -2 if there's no systemLanguage attribute, + * or -1 if no indices match. * XXX This algorithm is O(M*N). */ int32_t GetBestLanguagePreferenceRank(const nsSubstring& aAcceptLangs) const; diff --git a/content/svg/content/test/switch-helper.svg b/content/svg/content/test/switch-helper.svg index 2dbe13b074a6..842296efa776 100644 --- a/content/svg/content/test/switch-helper.svg +++ b/content/svg/content/test/switch-helper.svg @@ -7,6 +7,6 @@ - + diff --git a/content/svg/content/test/test_switch.xhtml b/content/svg/content/test/test_switch.xhtml index f8386c19b26e..887f123a1646 100644 --- a/content/svg/content/test/test_switch.xhtml +++ b/content/svg/content/test/test_switch.xhtml @@ -34,7 +34,7 @@ function checkBounds(element, x, y, w, h) ++test; } -function checkWidth(element, x, y, w, h) +function checkWidth(element, w) { var bbox = element.getBBox(); var name = element.nodeName; @@ -56,6 +56,7 @@ function run1() var s = doc.getElementById("s"); var first = doc.getElementById("first"); var second = doc.getElementById("second"); + var third = doc.getElementById("third"); /* test for an exact match */ second.setAttribute("systemLanguage", "en-gb"); @@ -63,11 +64,11 @@ function run1() /* test for a close match i.e. the same language prefix */ second.setAttribute("systemLanguage", "en-us"); - checkWidth(s, 75, 100, 50, 50); + checkWidth(s, 50); /* test that we pick the first match */ first.setAttribute("systemLanguage", "it"); - checkWidth(s, 75, 100, 70, 70); + checkWidth(s, 70); /* this time with reordering */ first.setAttribute("systemLanguage", "fr"); @@ -75,20 +76,33 @@ function run1() /* test for an exact match */ second.setAttribute("systemLanguage", "en-gb"); - checkWidth(s, 75, 100, 50, 50); + checkWidth(s, 50); /* test for a close match i.e. the same language prefix */ second.setAttribute("systemLanguage", "en-us"); - checkWidth(s, 75, 100, 50, 50); + checkWidth(s, 50); /* test that we pick the best match */ second.setAttribute("systemLanguage", "it"); - checkWidth(s, 75, 100, 50, 50); + checkWidth(s, 50); + + /* test that we use the default if nothing matches */ + second.setAttribute("systemLanguage", "fr"); + checkWidth(s, 80); + + /* test we still ignore non-matches */ + second.removeAttribute("systemLanguage"); + third.setAttribute("systemLanguage", "fr"); + checkWidth(s, 50); + + /* check what happens if nothing matches */ + second.setAttribute("systemLanguage", "fr"); + checkWidth(s, 0); /* test that we pick the best match */ first.setAttribute("systemLanguage", "en"); second.setAttribute("systemLanguage", "en-gb"); - checkWidth(s, 75, 100, 50, 50); + checkWidth(s, 50); } finally { SimpleTest.finish(); From d3a44f6093273106269e69f6a358e933210d10d3 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Tue, 9 Sep 2014 15:11:24 +0200 Subject: [PATCH 04/69] Bug 1064777 part 1 - Remove CNG check in TypeScript::InitObject. r=bhackett --- js/src/jsinferinlines.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index ba6ee73d7a03..3832f19bbbd6 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -709,10 +709,9 @@ TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoK { JS_ASSERT(!UseNewTypeForInitializer(script, pc, kind)); - /* :XXX: Limit script->length so we don't need to check the offset up front? */ uint32_t offset = script->pcToOffset(pc); - if (!script->compileAndGo() || offset >= AllocationSiteKey::OFFSET_LIMIT) + if (offset >= AllocationSiteKey::OFFSET_LIMIT) return GetTypeNewObject(cx, kind); AllocationSiteKey key; From 8422b9238fc7946d3d7d5071265806f0c8abdad9 Mon Sep 17 00:00:00 2001 From: Sotaro Ikeda Date: Tue, 9 Sep 2014 06:20:46 -0700 Subject: [PATCH 05/69] Bug 1064214 - Remove log r=Sushil --- widget/gonk/libdisplay/FramebufferSurface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/widget/gonk/libdisplay/FramebufferSurface.cpp b/widget/gonk/libdisplay/FramebufferSurface.cpp index 219e9be622eb..6f2b8ad83eef 100644 --- a/widget/gonk/libdisplay/FramebufferSurface.cpp +++ b/widget/gonk/libdisplay/FramebufferSurface.cpp @@ -171,7 +171,6 @@ int FramebufferSurface::GetPrevFBAcquireFd() { if (mPrevFBAcquireFence.get() && mPrevFBAcquireFence->isValid()) { return mPrevFBAcquireFence->dup(); } - ALOGE("GetPrevFBAcquireFd: FB acquire fence is invalid!"); return -1; } From 24dff6c377b2199291972827f88df291a6811d0c Mon Sep 17 00:00:00 2001 From: Sotaro Ikeda Date: Tue, 9 Sep 2014 06:27:24 -0700 Subject: [PATCH 06/69] Bug 1058452 - Use FakeMediaStreamGraph r=mikeh --- content/media/MediaStreamGraph.h | 2 +- dom/camera/CameraPreviewMediaStream.cpp | 21 +++++++++++++-------- dom/camera/CameraPreviewMediaStream.h | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/content/media/MediaStreamGraph.h b/content/media/MediaStreamGraph.h index 7dd3db611c65..9bc8adb78e8e 100644 --- a/content/media/MediaStreamGraph.h +++ b/content/media/MediaStreamGraph.h @@ -1188,7 +1188,7 @@ public: * Should only be called during MediaStreamListener callbacks or during * ProcessedMediaStream::ProcessInput(). */ - void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed aRunnable) + virtual void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed aRunnable) { *mPendingUpdateRunnables.AppendElement() = aRunnable; } diff --git a/dom/camera/CameraPreviewMediaStream.cpp b/dom/camera/CameraPreviewMediaStream.cpp index dd10d6c1d854..a1d57c5a1f46 100644 --- a/dom/camera/CameraPreviewMediaStream.cpp +++ b/dom/camera/CameraPreviewMediaStream.cpp @@ -19,6 +19,13 @@ using namespace mozilla::dom; namespace mozilla { +void +FakeMediaStreamGraph::DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed aRunnable) +{ + nsRefPtr task = aRunnable; + NS_DispatchToMainThread(task); +} + CameraPreviewMediaStream::CameraPreviewMediaStream(DOMMediaStream* aWrapper) : MediaStream(aWrapper) , mMutex("mozilla::camera::CameraPreviewMediaStream") @@ -27,6 +34,7 @@ CameraPreviewMediaStream::CameraPreviewMediaStream(DOMMediaStream* aWrapper) , mRateLimit(false) { SetGraphImpl(MediaStreamGraph::GetInstance()); + mFakeMediaStreamGraph = new FakeMediaStreamGraph(); mIsConsumed = false; } @@ -55,11 +63,10 @@ CameraPreviewMediaStream::AddVideoOutput(VideoFrameContainer* aContainer) if (mVideoOutputs.Length() > 1) { return; } - MediaStreamGraph* gm = MediaStreamGraph::GetInstance(); mIsConsumed = true; for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; - l->NotifyConsumptionChanged(gm, MediaStreamListener::CONSUMED); + l->NotifyConsumptionChanged(mFakeMediaStreamGraph, MediaStreamListener::CONSUMED); } } @@ -72,11 +79,10 @@ CameraPreviewMediaStream::RemoveVideoOutput(VideoFrameContainer* aContainer) if (!mVideoOutputs.IsEmpty()) { return; } - MediaStreamGraph* gm = MediaStreamGraph::GetInstance(); mIsConsumed = false; for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; - l->NotifyConsumptionChanged(gm, MediaStreamListener::NOT_CONSUMED); + l->NotifyConsumptionChanged(mFakeMediaStreamGraph, MediaStreamListener::NOT_CONSUMED); } } @@ -90,9 +96,9 @@ CameraPreviewMediaStream::AddListener(MediaStreamListener* aListener) { MutexAutoLock lock(mMutex); - MediaStreamGraph* gm = MediaStreamGraph::GetInstance(); MediaStreamListener* listener = *mListeners.AppendElement() = aListener; - listener->NotifyBlockingChanged(gm, MediaStreamListener::UNBLOCKED); + listener->NotifyBlockingChanged(mFakeMediaStreamGraph, MediaStreamListener::UNBLOCKED); + listener->NotifyHasCurrentData(mFakeMediaStreamGraph); } void @@ -100,10 +106,9 @@ CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener) { MutexAutoLock lock(mMutex); - MediaStreamGraph* gm = MediaStreamGraph::GetInstance(); nsRefPtr listener(aListener); mListeners.RemoveElement(aListener); - listener->NotifyEvent(gm, MediaStreamListener::EVENT_REMOVED); + listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamListener::EVENT_REMOVED); } void diff --git a/dom/camera/CameraPreviewMediaStream.h b/dom/camera/CameraPreviewMediaStream.h index 2a5f8ef8f2fd..c09d07d27226 100644 --- a/dom/camera/CameraPreviewMediaStream.h +++ b/dom/camera/CameraPreviewMediaStream.h @@ -11,6 +11,23 @@ namespace mozilla { +class FakeMediaStreamGraph : public MediaStreamGraph +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FakeMediaStreamGraph) +public: + FakeMediaStreamGraph() + : MediaStreamGraph() + { + } + + virtual void + DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed aRunnable) MOZ_OVERRIDE; + +protected: + ~FakeMediaStreamGraph() + {} +}; + /** * This is a stream for camera preview. * @@ -50,6 +67,7 @@ protected: int32_t mInvalidatePending; uint32_t mDiscardedFrames; bool mRateLimit; + nsRefPtr mFakeMediaStreamGraph; }; } From 5f4a0db07290b0f9df807d2b48db03dd6f9fef11 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 9 Sep 2014 08:15:35 -0600 Subject: [PATCH 07/69] Bug 1054601 - Don't unroll loops which aren't actually loops, r=jandem. --- js/src/jit-test/tests/ion/bug1054601.js | 8 ++++++++ js/src/jit/LoopUnroller.cpp | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/ion/bug1054601.js diff --git a/js/src/jit-test/tests/ion/bug1054601.js b/js/src/jit-test/tests/ion/bug1054601.js new file mode 100644 index 000000000000..3332501739a5 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1054601.js @@ -0,0 +1,8 @@ + +(function() { + for (let x = 0; x < 1; ++x) { + if (x % 6 == 4) {} else { + return; + } + } +})() diff --git a/js/src/jit/LoopUnroller.cpp b/js/src/jit/LoopUnroller.cpp index 627ad962d5fa..396d82796410 100644 --- a/js/src/jit/LoopUnroller.cpp +++ b/js/src/jit/LoopUnroller.cpp @@ -115,7 +115,10 @@ LoopUnroller::go(LoopIterationBound *bound) JitSpew(JitSpew_Unrolling, "Attempting to unroll loop"); header = bound->header; - JS_ASSERT(header->isLoopHeader()); + + // UCE might have determined this isn't actually a loop. + if (!header->isLoopHeader()) + return; backedge = header->backedge(); oldPreheader = header->loopPredecessor(); From 1ab12e0244b6fb3e6e1ee46ab60dd7e9515c6518 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 9 Sep 2014 08:21:13 -0600 Subject: [PATCH 08/69] Bug 1063182 - Always instantiate types for objects written to by Ion SETPROP caches, r=jandem. --- js/src/jit-test/tests/ion/bug1063182.js | 8 ++++++++ js/src/jit/IonCaches.cpp | 11 ++++------- 2 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 js/src/jit-test/tests/ion/bug1063182.js diff --git a/js/src/jit-test/tests/ion/bug1063182.js b/js/src/jit-test/tests/ion/bug1063182.js new file mode 100644 index 000000000000..9cda4809958d --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1063182.js @@ -0,0 +1,8 @@ +// |jit-test| error: ReferenceError + +eval("(function() { " + "\ +var o = {};\ +o.watch('p', function() { });\ +for (var i = 0; i < 10; \u5ede ++)\ + o.p = 123;\ +" + " })();"); diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index c6ab21eb13f9..ffc8f9b4db2f 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -2815,6 +2815,10 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, RootedPropertyName name(cx, cache.name()); RootedId id(cx, AtomToId(name)); + RootedTypeObject oldType(cx, obj->getType(cx)); + if (!oldType) + return false; + // Stop generating new stubs once we hit the stub count limit, see // GetPropertyCache. NativeSetPropCacheability canCache = CanAttachNone; @@ -2846,12 +2850,6 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, } } - // Make sure the object de-lazifies its type. We do this here so that - // the parallel IC can share code that assumes that native objects all - // have a type object. - if (obj->isNative() && !obj->getType(cx)) - return false; - RootedShape shape(cx); RootedObject holder(cx); bool checkTypeset; @@ -2873,7 +2871,6 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj, uint32_t oldSlots = obj->numDynamicSlots(); RootedShape oldShape(cx, obj->lastProperty()); - RootedTypeObject oldType(cx, obj->type()); // Set/Add the property on the object, the inlined cache are setup for the next execution. if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc())) From 7f465e0a47b9cf14df24f5e6e7f078de1325a3ce Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 9 Sep 2014 08:23:44 -0600 Subject: [PATCH 09/69] Bug 1059459 - Tolerate resume points with constants defined later in the block when unrolling loops, r=jandem. --- js/src/jit-test/tests/basic/bug1059459.js | 1 + js/src/jit/LoopUnroller.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/basic/bug1059459.js diff --git a/js/src/jit-test/tests/basic/bug1059459.js b/js/src/jit-test/tests/basic/bug1059459.js new file mode 100644 index 000000000000..47f0246c7148 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1059459.js @@ -0,0 +1 @@ +evalcx(" s = 3; s; for(let x = 0; x < 1; ++x) { null }", newGlobal()) diff --git a/js/src/jit/LoopUnroller.cpp b/js/src/jit/LoopUnroller.cpp index 396d82796410..4de3d924f3e3 100644 --- a/js/src/jit/LoopUnroller.cpp +++ b/js/src/jit/LoopUnroller.cpp @@ -59,7 +59,16 @@ LoopUnroller::getReplacementDefinition(MDefinition *def) } DefinitionMap::Ptr p = unrolledDefinitions.lookup(def); - JS_ASSERT(p); + if (!p) { + // After phi analysis (TypeAnalyzer::replaceRedundantPhi) the resume + // point at the start of a block can contain definitions from within + // the block itself. + JS_ASSERT(def->isConstant()); + + MConstant *constant = MConstant::New(alloc, def->toConstant()->value()); + oldPreheader->insertBefore(*oldPreheader->begin(), constant); + return constant; + } return p->value(); } From e4f70e766fb461b49f5586d9dfe55d47a2979f89 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Tue, 9 Sep 2014 10:28:27 -0400 Subject: [PATCH 10/69] Bug 963075 - disabled browser_pdfjs_[main|views].js leaks until shutdown when run as a standalone directory. r=RyanVM --- browser/extensions/pdfjs/test/browser.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/extensions/pdfjs/test/browser.ini b/browser/extensions/pdfjs/test/browser.ini index 927907dc313b..686574517e3d 100644 --- a/browser/extensions/pdfjs/test/browser.ini +++ b/browser/extensions/pdfjs/test/browser.ini @@ -3,5 +3,7 @@ skip-if = e10s # Bug 942707 - PDF viewer doesn't work with e10s. support-files = file_pdfjs_test.pdf [browser_pdfjs_main.js] +skip-if = debug # bug 1058695 [browser_pdfjs_savedialog.js] [browser_pdfjs_views.js] +skip-if = debug # bug 1058695 From 42c230d84d67c7069d513d6063a80dfdf066a490 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Tue, 9 Sep 2014 16:57:07 +0200 Subject: [PATCH 11/69] Bug 1002473 part 2 - Remove Baseline IsTopFrameConstructing and simplify the code. r=nbp --- js/src/jit/BaselineFrame.h | 3 +++ js/src/jit/BaselineIC.cpp | 21 ++------------------- js/src/jit/Ion.cpp | 11 +++++------ js/src/jit/Ion.h | 5 ++--- js/src/jit/IonFrames.cpp | 2 +- js/src/jit/IonFrames.h | 5 +++++ 6 files changed, 18 insertions(+), 29 deletions(-) diff --git a/js/src/jit/BaselineFrame.h b/js/src/jit/BaselineFrame.h index b11f6098241d..ee76e0dcbfcf 100644 --- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -134,6 +134,9 @@ class BaselineFrame uint8_t *pointer = (uint8_t *)this + Size() + offsetOfCalleeToken(); *(CalleeToken *)pointer = token; } + bool isConstructing() const { + return CalleeTokenIsConstructing(calleeToken()); + } JSScript *script() const { if (isEvalFrame()) return evalScript(); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 49f4768cac34..2019a2de8465 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -778,22 +778,6 @@ ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, Val // // UseCount_Fallback // -static bool -IsTopFrameConstructing(JSContext *cx) -{ - JS_ASSERT(cx->currentlyRunningInJit()); - JitActivationIterator activations(cx->runtime()); - JitFrameIterator iter(activations); - JS_ASSERT(iter.type() == JitFrame_Exit); - - ++iter; - JS_ASSERT(iter.type() == JitFrame_BaselineStub); - - ++iter; - JS_ASSERT(iter.isBaselineJS()); - - return iter.isConstructing(); -} static bool EnsureCanEnterIon(JSContext *cx, ICUseCount_Fallback *stub, BaselineFrame *frame, @@ -804,15 +788,14 @@ EnsureCanEnterIon(JSContext *cx, ICUseCount_Fallback *stub, BaselineFrame *frame bool isLoopEntry = (JSOp(*pc) == JSOP_LOOPENTRY); - bool isConstructing = IsTopFrameConstructing(cx); MethodStatus stat; if (isLoopEntry) { JS_ASSERT(LoopEntryCanIonOsr(pc)); JitSpew(JitSpew_BaselineOSR, " Compile at loop entry!"); - stat = CanEnterAtBranch(cx, script, frame, pc, isConstructing); + stat = CanEnterAtBranch(cx, script, frame, pc); } else if (frame->isFunctionFrame()) { JitSpew(JitSpew_BaselineOSR, " Compile function from top for later entry!"); - stat = CompileFunctionForBaseline(cx, script, frame, isConstructing); + stat = CompileFunctionForBaseline(cx, script, frame); } else { return true; } diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 4d3a26ef7180..17aed66665b4 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2195,8 +2195,7 @@ Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode // Decide if a transition from interpreter execution to Ion code should occur. // May compile or recompile the target JSScript. MethodStatus -jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame, - jsbytecode *pc, bool isConstructing) +jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame, jsbytecode *pc) { JS_ASSERT(jit::IsIonEnabled(cx)); JS_ASSERT((JSOp)*pc == JSOP_LOOPENTRY); @@ -2230,7 +2229,8 @@ jit::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame, // - Returns Method_Skipped if pc doesn't match // (This means a background thread compilation with that pc could have started or not.) RootedScript rscript(cx, script); - MethodStatus status = Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution); + MethodStatus status = Compile(cx, rscript, osrFrame, pc, osrFrame->isConstructing(), + SequentialExecution); if (status != Method_Compiled) { if (status == Method_CantCompile) ForbidCompilation(cx, script); @@ -2309,8 +2309,7 @@ jit::CanEnter(JSContext *cx, RunState &state) } MethodStatus -jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame, - bool isConstructing) +jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame) { JS_ASSERT(jit::IsIonEnabled(cx)); JS_ASSERT(frame->fun()->nonLazyScript()->canIonCompile()); @@ -2326,7 +2325,7 @@ jit::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFram // Attempt compilation. Returns Method_Compiled if already compiled. MethodStatus status = - Compile(cx, script, frame, nullptr, isConstructing, SequentialExecution); + Compile(cx, script, frame, nullptr, frame->isConstructing(), SequentialExecution); if (status != Method_Compiled) { if (status == Method_CantCompile) ForbidCompilation(cx, script); diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index a113a8d36faa..25d8caaf127c 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -84,10 +84,9 @@ void SetIonContext(IonContext *ctx); bool CanIonCompileScript(JSContext *cx, JSScript *script, bool osr); MethodStatus CanEnterAtBranch(JSContext *cx, JSScript *script, - BaselineFrame *frame, jsbytecode *pc, bool isConstructing); + BaselineFrame *frame, jsbytecode *pc); MethodStatus CanEnter(JSContext *cx, RunState &state); -MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame, - bool isConstructing); +MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame); MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs); MethodStatus CanEnterInParallel(JSContext *cx, HandleScript script); diff --git a/js/src/jit/IonFrames.cpp b/js/src/jit/IonFrames.cpp index bc95150ff09b..515150ef5b5a 100644 --- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -2015,7 +2015,7 @@ InlineFrameIterator::isConstructing() const bool JitFrameIterator::isConstructing() const { - return GetCalleeTokenTag(calleeToken()) == CalleeToken_FunctionConstructing; + return CalleeTokenIsConstructing(calleeToken()); } unsigned diff --git a/js/src/jit/IonFrames.h b/js/src/jit/IonFrames.h index 2638eb8c7b9d..8cc035b6a490 100644 --- a/js/src/jit/IonFrames.h +++ b/js/src/jit/IonFrames.h @@ -52,6 +52,11 @@ CalleeTokenIsFunction(CalleeToken token) CalleeTokenTag tag = GetCalleeTokenTag(token); return tag == CalleeToken_Function || tag == CalleeToken_FunctionConstructing; } +static inline bool +CalleeTokenIsConstructing(CalleeToken token) +{ + return GetCalleeTokenTag(token) == CalleeToken_FunctionConstructing; +} static inline JSFunction * CalleeTokenToFunction(CalleeToken token) { From 4736cfc4d00ab26dfe86d6058fb9efc7230e3c6e Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Tue, 9 Sep 2014 11:11:35 -0400 Subject: [PATCH 12/69] Bug 1057082 - Part 2 - Fix JitcodeGlobalEntry comparison function and add helper debug methods. r=luke --- js/src/jit/JitcodeMap.cpp | 19 ++++++++++++++++++- js/src/jit/JitcodeMap.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index c97d6d1988c9..1f0e5a7fb6e2 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -123,7 +123,24 @@ JitcodeGlobalEntry::compare(const JitcodeGlobalEntry &ent1, const JitcodeGlobalE // Ensure no overlaps for non-query lookups. JS_ASSERT_IF(!ent1.isQuery() && !ent2.isQuery(), !ent1.overlapsWith(ent2)); - return ComparePointers(ent1.nativeStartAddr(), ent2.nativeStartAddr()); + // For two non-query entries, just comapare the start addresses. + if (!ent1.isQuery() && !ent2.isQuery()) + return ComparePointers(ent1.nativeStartAddr(), ent2.nativeStartAddr()); + + void *ptr = ent1.isQuery() ? ent1.nativeStartAddr() : ent2.nativeStartAddr(); + const JitcodeGlobalEntry &ent = ent1.isQuery() ? ent2 : ent1; + int flip = ent1.isQuery() ? 1 : -1; + + if (ent.startsBelowPointer(ptr)) { + if (ent.endsAbovePointer(ptr)) + return 0; + + // query ptr > entry + return flip * 1; + } + + // query ptr < entry + return flip * -1; } bool diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h index e4aa521898d1..5eaef705b3f9 100644 --- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -7,6 +7,8 @@ #ifndef jit_JitcodeMap_h #define jit_JitcodeMap_h +#include + #include "ds/SplayTree.h" #include "jit/CompactBuffer.h" #include "jit/CompileInfo.h" @@ -488,6 +490,39 @@ class JitcodeGlobalTable return tree_.empty(); } + uint32_t sizeDebug() { + struct SZ { + uint32_t count_; + SZ() : count_(0) {}; + SZ(const SZ &other) = delete; + void operator()(JitcodeGlobalEntry &ent) { count_++; } + }; + SZ sz; + tree_.forEach(sz); + return sz.count_; + } + + bool lookupDebug(void *ptr) { + struct LOOKUP { + void *ptr_; + bool found_; + LOOKUP(void *ptr) : ptr_(ptr), found_(false) {} + LOOKUP(const LOOKUP &other) = delete; + void operator()(JitcodeGlobalEntry &ent) { + if (ent.containsPointer(ptr_)) + found_ = true; + } + }; + LOOKUP l(ptr); + tree_.forEach(l); + return l.found_; + } + + template + void forEach(T &t) { + return tree_.forEach(t); + } + bool lookup(void *ptr, JitcodeGlobalEntry *result); void lookupInfallible(void *ptr, JitcodeGlobalEntry *result); From 9eefe6154114991a38ffb56f470db1b9fe25e01b Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:44 +0200 Subject: [PATCH 13/69] Bug 1062832 - Remove nsSVGFilterProperty::mFilters. r=roc --- layout/svg/nsSVGEffects.cpp | 9 ++++----- layout/svg/nsSVGEffects.h | 2 -- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index 20dd82737394..874b363d7eb3 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -217,15 +217,14 @@ nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument, NS_IMPL_ISUPPORTS(nsSVGFilterProperty, nsISupports) nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray &aFilters, - nsIFrame *aFilteredFrame) : - mFilters(aFilters) + nsIFrame *aFilteredFrame) { - for (uint32_t i = 0; i < mFilters.Length(); i++) { - if (mFilters[i].GetType() != NS_STYLE_FILTER_URL) + for (uint32_t i = 0; i < aFilters.Length(); i++) { + if (aFilters[i].GetType() != NS_STYLE_FILTER_URL) continue; nsSVGFilterReference *reference = - new nsSVGFilterReference(mFilters[i].GetURL(), aFilteredFrame); + new nsSVGFilterReference(aFilters[i].GetURL(), aFilteredFrame); NS_ADDREF(reference); mReferences.AppendElement(reference); } diff --git a/layout/svg/nsSVGEffects.h b/layout/svg/nsSVGEffects.h index 0aaee9654b6c..101ce9388bf5 100644 --- a/layout/svg/nsSVGEffects.h +++ b/layout/svg/nsSVGEffects.h @@ -206,7 +206,6 @@ public: nsSVGFilterProperty(const nsTArray &aFilters, nsIFrame *aFilteredFrame); - const nsTArray& GetFilters() { return mFilters; } bool ReferencesValidResources(); bool IsInObserverLists() const; void Invalidate(); @@ -219,7 +218,6 @@ protected: private: nsTArray mReferences; - nsTArray mFilters; }; class nsSVGMarkerProperty : public nsSVGIDRenderingObserver { From 0184e500c32305aa91cf8b92c08904bf1c0f9aa6 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:45 +0200 Subject: [PATCH 14/69] Bug 1062832 - Use nsRefPtr in the filter references array instead of manual refcounting. r=roc --- layout/svg/nsSVGEffects.cpp | 10 +--------- layout/svg/nsSVGEffects.h | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index 874b363d7eb3..b995eafbeade 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -223,20 +223,12 @@ nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray &aFilters if (aFilters[i].GetType() != NS_STYLE_FILTER_URL) continue; - nsSVGFilterReference *reference = + nsRefPtr reference = new nsSVGFilterReference(aFilters[i].GetURL(), aFilteredFrame); - NS_ADDREF(reference); mReferences.AppendElement(reference); } } -nsSVGFilterProperty::~nsSVGFilterProperty() -{ - for (uint32_t i = 0; i < mReferences.Length(); i++) { - NS_RELEASE(mReferences[i]); - } -} - bool nsSVGFilterProperty::ReferencesValidResources() { diff --git a/layout/svg/nsSVGEffects.h b/layout/svg/nsSVGEffects.h index 101ce9388bf5..525746fe7e40 100644 --- a/layout/svg/nsSVGEffects.h +++ b/layout/svg/nsSVGEffects.h @@ -214,10 +214,10 @@ public: NS_DECL_ISUPPORTS protected: - virtual ~nsSVGFilterProperty(); + virtual ~nsSVGFilterProperty() {} private: - nsTArray mReferences; + nsTArray> mReferences; }; class nsSVGMarkerProperty : public nsSVGIDRenderingObserver { From f0ce0d317072fc207bc4786d04711b7488ae8c15 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:45 +0200 Subject: [PATCH 15/69] Bug 1062832 - Reorder stuff in nsSVGEffects.cpp. r=roc --- layout/svg/nsSVGEffects.cpp | 184 ++++++++++++++++++------------------ 1 file changed, 91 insertions(+), 93 deletions(-) diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index b995eafbeade..dafcec6ce024 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -47,41 +47,6 @@ nsSVGRenderingObserver::StopListening() NS_ASSERTION(!mInObserverList, "still in an observer list?"); } - - -/** - * Note that in the current setup there are two separate observer lists. - * - * In nsSVGIDRenderingObserver's ctor, the new object adds itself to the - * mutation observer list maintained by the referenced element. In this way the - * nsSVGIDRenderingObserver is notified if there are any attribute or content - * tree changes to the element or any of its *descendants*. - * - * In nsSVGIDRenderingObserver::GetReferencedElement() the - * nsSVGIDRenderingObserver object also adds itself to an - * nsSVGRenderingObserverList object belonging to the referenced - * element. - * - * XXX: it would be nice to have a clear and concise executive summary of the - * benefits/necessity of maintaining a second observer list. - */ - -nsSVGIDRenderingObserver::nsSVGIDRenderingObserver(nsIURI *aURI, - nsIFrame *aFrame, - bool aReferenceImage) - : mElement(MOZ_THIS_IN_INITIALIZER_LIST()), mFrame(aFrame), - mFramePresShell(aFrame->PresContext()->PresShell()) -{ - // Start watching the target element - mElement.Reset(aFrame->GetContent(), aURI, true, aReferenceImage); - StartListening(); -} - -nsSVGIDRenderingObserver::~nsSVGIDRenderingObserver() -{ - StopListening(); -} - static nsSVGRenderingObserverList * GetObserverList(Element *aElement) { @@ -130,25 +95,6 @@ nsSVGRenderingObserver::GetReferencedFrame(nsIAtom* aFrameType, bool* aOK) return nullptr; } -void -nsSVGIDRenderingObserver::DoUpdate() -{ - if (mFramePresShell->IsDestroying()) { - // mFrame is no longer valid. Bail out. - mFrame = nullptr; - return; - } - if (mElement.get() && mInObserverList) { - nsSVGEffects::RemoveRenderingObserver(mElement.get(), this); - mInObserverList = false; - } - if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) { - // Changes should propagate out to things that might be observing - // the referencing frame or its ancestors. - nsSVGEffects::InvalidateRenderingObservers(mFrame); - } -} - void nsSVGRenderingObserver::InvalidateViaReferencedElement() { @@ -214,6 +160,88 @@ nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument, DoUpdate(); } +/** + * Note that in the current setup there are two separate observer lists. + * + * In nsSVGIDRenderingObserver's ctor, the new object adds itself to the + * mutation observer list maintained by the referenced element. In this way the + * nsSVGIDRenderingObserver is notified if there are any attribute or content + * tree changes to the element or any of its *descendants*. + * + * In nsSVGIDRenderingObserver::GetReferencedElement() the + * nsSVGIDRenderingObserver object also adds itself to an + * nsSVGRenderingObserverList object belonging to the referenced + * element. + * + * XXX: it would be nice to have a clear and concise executive summary of the + * benefits/necessity of maintaining a second observer list. + */ + +nsSVGIDRenderingObserver::nsSVGIDRenderingObserver(nsIURI *aURI, + nsIFrame *aFrame, + bool aReferenceImage) + : mElement(MOZ_THIS_IN_INITIALIZER_LIST()), mFrame(aFrame), + mFramePresShell(aFrame->PresContext()->PresShell()) +{ + // Start watching the target element + mElement.Reset(aFrame->GetContent(), aURI, true, aReferenceImage); + StartListening(); +} + +nsSVGIDRenderingObserver::~nsSVGIDRenderingObserver() +{ + StopListening(); +} + +void +nsSVGIDRenderingObserver::DoUpdate() +{ + if (mFramePresShell->IsDestroying()) { + // mFrame is no longer valid. Bail out. + mFrame = nullptr; + return; + } + if (mElement.get() && mInObserverList) { + nsSVGEffects::RemoveRenderingObserver(mElement.get(), this); + mInObserverList = false; + } + if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) { + // Changes should propagate out to things that might be observing + // the referencing frame or its ancestors. + nsSVGEffects::InvalidateRenderingObservers(mFrame); + } +} + +NS_IMPL_ISUPPORTS_INHERITED(nsSVGFilterReference, + nsSVGIDRenderingObserver, + nsISVGFilterReference); + +nsSVGFilterFrame * +nsSVGFilterReference::GetFilterFrame() +{ + return static_cast + (GetReferencedFrame(nsGkAtoms::svgFilterFrame, nullptr)); +} + +void +nsSVGFilterReference::DoUpdate() +{ + nsSVGIDRenderingObserver::DoUpdate(); + if (!mFrame) + return; + + // Repaint asynchronously in case the filter frame is being torn down + nsChangeHint changeHint = + nsChangeHint(nsChangeHint_RepaintFrame); + + // Don't need to request UpdateOverflow if we're being reflowed. + if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) { + NS_UpdateHint(changeHint, nsChangeHint_UpdateOverflow); + } + mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( + mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); +} + NS_IMPL_ISUPPORTS(nsSVGFilterProperty, nsISupports) nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray &aFilters, @@ -257,45 +285,6 @@ nsSVGFilterProperty::Invalidate() } } -NS_IMPL_ISUPPORTS_INHERITED(nsSVGFilterReference, - nsSVGIDRenderingObserver, - nsISVGFilterReference); - -nsSVGFilterFrame * -nsSVGFilterReference::GetFilterFrame() -{ - return static_cast - (GetReferencedFrame(nsGkAtoms::svgFilterFrame, nullptr)); -} - -static void -InvalidateAllContinuations(nsIFrame* aFrame) -{ - for (nsIFrame* f = aFrame; f; - f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) { - f->InvalidateFrame(); - } -} - -void -nsSVGFilterReference::DoUpdate() -{ - nsSVGIDRenderingObserver::DoUpdate(); - if (!mFrame) - return; - - // Repaint asynchronously in case the filter frame is being torn down - nsChangeHint changeHint = - nsChangeHint(nsChangeHint_RepaintFrame); - - // Don't need to request UpdateOverflow if we're being reflowed. - if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) { - NS_UpdateHint(changeHint, nsChangeHint_UpdateOverflow); - } - mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( - mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); -} - void nsSVGMarkerProperty::DoUpdate() { @@ -362,6 +351,15 @@ nsSVGTextPathProperty::DoUpdate() mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); } +static void +InvalidateAllContinuations(nsIFrame* aFrame) +{ + for (nsIFrame* f = aFrame; f; + f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) { + f->InvalidateFrame(); + } +} + void nsSVGPaintingProperty::DoUpdate() { From 5ca816d53647d3a40c4fcaa5f620806fc60f90a2 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:45 +0200 Subject: [PATCH 16/69] Bug 1062832 - Remove frame handling from nsSVGIDRenderingObserver and put it into nsSVGRenderingObserverProperty. r=roc --- layout/svg/nsSVGEffects.cpp | 99 +++++++++++++++++++++++-------------- layout/svg/nsSVGEffects.h | 54 +++++++++++++++----- 2 files changed, 106 insertions(+), 47 deletions(-) diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index dafcec6ce024..f1b50c9437e1 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -177,14 +177,13 @@ nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument, * benefits/necessity of maintaining a second observer list. */ -nsSVGIDRenderingObserver::nsSVGIDRenderingObserver(nsIURI *aURI, - nsIFrame *aFrame, +nsSVGIDRenderingObserver::nsSVGIDRenderingObserver(nsIURI* aURI, + nsIContent* aObservingContent, bool aReferenceImage) - : mElement(MOZ_THIS_IN_INITIALIZER_LIST()), mFrame(aFrame), - mFramePresShell(aFrame->PresContext()->PresShell()) + : mElement(MOZ_THIS_IN_INITIALIZER_LIST()) { // Start watching the target element - mElement.Reset(aFrame->GetContent(), aURI, true, aReferenceImage); + mElement.Reset(aObservingContent, aURI, true, aReferenceImage); StartListening(); } @@ -196,19 +195,39 @@ nsSVGIDRenderingObserver::~nsSVGIDRenderingObserver() void nsSVGIDRenderingObserver::DoUpdate() { - if (mFramePresShell->IsDestroying()) { - // mFrame is no longer valid. Bail out. - mFrame = nullptr; - return; - } if (mElement.get() && mInObserverList) { nsSVGEffects::RemoveRenderingObserver(mElement.get(), this); mInObserverList = false; } - if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) { +} + +void +nsSVGFrameReferenceFromProperty::Detach() +{ + mFrame = nullptr; + mFramePresShell = nullptr; +} + +nsIFrame* +nsSVGFrameReferenceFromProperty::Get() +{ + if (mFramePresShell && mFramePresShell->IsDestroying()) { + // mFrame is no longer valid. + Detach(); + } + return mFrame; +} + +void +nsSVGRenderingObserverProperty::DoUpdate() +{ + nsSVGIDRenderingObserver::DoUpdate(); + + nsIFrame* frame = mFrameReference.Get(); + if (frame && frame->IsFrameOfType(nsIFrame::eSVG)) { // Changes should propagate out to things that might be observing // the referencing frame or its ancestors. - nsSVGEffects::InvalidateRenderingObservers(mFrame); + nsSVGEffects::InvalidateRenderingObservers(frame); } } @@ -226,8 +245,10 @@ nsSVGFilterReference::GetFilterFrame() void nsSVGFilterReference::DoUpdate() { - nsSVGIDRenderingObserver::DoUpdate(); - if (!mFrame) + nsSVGRenderingObserverProperty::DoUpdate(); + + nsIFrame* frame = mFrameReference.Get(); + if (!frame) return; // Repaint asynchronously in case the filter frame is being torn down @@ -235,11 +256,11 @@ nsSVGFilterReference::DoUpdate() nsChangeHint(nsChangeHint_RepaintFrame); // Don't need to request UpdateOverflow if we're being reflowed. - if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) { + if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) { NS_UpdateHint(changeHint, nsChangeHint_UpdateOverflow); } - mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( - mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); + frame->PresContext()->RestyleManager()->PostRestyleEvent( + frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); } NS_IMPL_ISUPPORTS(nsSVGFilterProperty, nsISupports) @@ -288,26 +309,28 @@ nsSVGFilterProperty::Invalidate() void nsSVGMarkerProperty::DoUpdate() { - nsSVGIDRenderingObserver::DoUpdate(); - if (!mFrame) + nsSVGRenderingObserverProperty::DoUpdate(); + + nsIFrame* frame = mFrameReference.Get(); + if (!frame) return; - NS_ASSERTION(mFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); + NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); // Repaint asynchronously in case the marker frame is being torn down nsChangeHint changeHint = nsChangeHint(nsChangeHint_RepaintFrame); // Don't need to request ReflowFrame if we're being reflowed. - if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) { + if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) { // XXXjwatt: We need to unify SVG into standard reflow so we can just use // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here. - nsSVGEffects::InvalidateRenderingObservers(mFrame); + nsSVGEffects::InvalidateRenderingObservers(frame); // XXXSDL KILL THIS!!! - nsSVGUtils::ScheduleReflowSVG(mFrame); + nsSVGUtils::ScheduleReflowSVG(frame); } - mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( - mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); + frame->PresContext()->RestyleManager()->PostRestyleEvent( + frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); } bool @@ -320,11 +343,13 @@ nsSVGTextPathProperty::TargetIsValid() void nsSVGTextPathProperty::DoUpdate() { - nsSVGIDRenderingObserver::DoUpdate(); - if (!mFrame) + nsSVGRenderingObserverProperty::DoUpdate(); + + nsIFrame* frame = mFrameReference.Get(); + if (!frame) return; - NS_ASSERTION(mFrame->IsFrameOfType(nsIFrame::eSVG) || mFrame->IsSVGText(), + NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG) || frame->IsSVGText(), "SVG frame expected"); // Avoid getting into an infinite loop of reflows if the is @@ -347,8 +372,8 @@ nsSVGTextPathProperty::DoUpdate() // Repaint asynchronously in case the path frame is being torn down nsChangeHint changeHint = nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath); - mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( - mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); + frame->PresContext()->RestyleManager()->PostRestyleEvent( + frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); } static void @@ -363,15 +388,17 @@ InvalidateAllContinuations(nsIFrame* aFrame) void nsSVGPaintingProperty::DoUpdate() { - nsSVGIDRenderingObserver::DoUpdate(); - if (!mFrame) + nsSVGRenderingObserverProperty::DoUpdate(); + + nsIFrame* frame = mFrameReference.Get(); + if (!frame) return; - if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { - nsSVGEffects::InvalidateRenderingObservers(mFrame); - mFrame->InvalidateFrameSubtree(); + if (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { + nsSVGEffects::InvalidateRenderingObservers(frame); + frame->InvalidateFrameSubtree(); } else { - InvalidateAllContinuations(mFrame); + InvalidateAllContinuations(frame); } } diff --git a/layout/svg/nsSVGEffects.h b/layout/svg/nsSVGEffects.h index 525746fe7e40..81e7b40be8f6 100644 --- a/layout/svg/nsSVGEffects.h +++ b/layout/svg/nsSVGEffects.h @@ -112,7 +112,7 @@ protected: class nsSVGIDRenderingObserver : public nsSVGRenderingObserver { public: typedef mozilla::dom::Element Element; - nsSVGIDRenderingObserver(nsIURI* aURI, nsIFrame *aFrame, + nsSVGIDRenderingObserver(nsIURI* aURI, nsIContent* aObservingContent, bool aReferenceImage); virtual ~nsSVGIDRenderingObserver(); @@ -142,16 +142,48 @@ protected: }; SourceReference mElement; - // The frame that this property is attached to +}; + +struct nsSVGFrameReferenceFromProperty +{ + nsSVGFrameReferenceFromProperty(nsIFrame* aFrame) + : mFrame(aFrame) + , mFramePresShell(aFrame->PresContext()->PresShell()) + {} + + // Clear our reference to the frame. + void Detach(); + + // null if the frame has become invalid + nsIFrame* Get(); + +private: + // The frame that this property is attached to, may be null nsIFrame *mFrame; // When a presshell is torn down, we don't delete the properties for // each frame until after the frames are destroyed. So here we remember // the presshell for the frames we care about and, before we use the frame, // we test the presshell to see if it's destroying itself. If it is, // then the frame pointer is not valid and we know the frame has gone away. + // mFramePresShell may be null, but when mFrame is non-null, mFramePresShell + // is guaranteed to be non-null, too. nsIPresShell *mFramePresShell; }; +class nsSVGRenderingObserverProperty : public nsSVGIDRenderingObserver { +public: + nsSVGRenderingObserverProperty(nsIURI* aURI, nsIFrame *aFrame, + bool aReferenceImage) + : nsSVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage) + , mFrameReference(aFrame) + {} + +protected: + virtual void DoUpdate() MOZ_OVERRIDE; + + nsSVGFrameReferenceFromProperty mFrameReference; +}; + /** * In a filter chain, there can be multiple SVG reference filters. * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2); @@ -165,10 +197,10 @@ protected: * The nsSVGFilterProperty class manages a list of nsSVGFilterReferences. */ class nsSVGFilterReference MOZ_FINAL : - public nsSVGIDRenderingObserver, public nsISVGFilterReference { + public nsSVGRenderingObserverProperty, public nsISVGFilterReference { public: nsSVGFilterReference(nsIURI *aURI, nsIFrame *aFilteredFrame) - : nsSVGIDRenderingObserver(aURI, aFilteredFrame, false) {} + : nsSVGRenderingObserverProperty(aURI, aFilteredFrame, false) {} bool ReferencesValidResource() { return GetFilterFrame(); } @@ -187,7 +219,7 @@ protected: virtual ~nsSVGFilterReference() {} private: - // nsSVGIDRenderingObserver + // nsSVGRenderingObserverProperty virtual void DoUpdate() MOZ_OVERRIDE; }; @@ -220,19 +252,19 @@ private: nsTArray> mReferences; }; -class nsSVGMarkerProperty : public nsSVGIDRenderingObserver { +class nsSVGMarkerProperty : public nsSVGRenderingObserverProperty { public: nsSVGMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) - : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage) {} + : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {} protected: virtual void DoUpdate() MOZ_OVERRIDE; }; -class nsSVGTextPathProperty : public nsSVGIDRenderingObserver { +class nsSVGTextPathProperty : public nsSVGRenderingObserverProperty { public: nsSVGTextPathProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) - : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage) + : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) , mValid(true) {} virtual bool ObservesReflow() MOZ_OVERRIDE { return false; } @@ -249,10 +281,10 @@ private: bool mValid; }; -class nsSVGPaintingProperty : public nsSVGIDRenderingObserver { +class nsSVGPaintingProperty : public nsSVGRenderingObserverProperty { public: nsSVGPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) - : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage) {} + : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {} protected: virtual void DoUpdate() MOZ_OVERRIDE; From b12757247a50650ac00b5a7fc703a4c331cb4383 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:46 +0200 Subject: [PATCH 17/69] Bug 1062832 - Move frame invalidation from nsSVGFilterReference to nsSVGFilterProperty. r=roc --- layout/svg/nsSVGEffects.cpp | 51 ++++++++++++++++++++++++------------- layout/svg/nsSVGEffects.h | 27 +++++++++++++++----- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index f1b50c9437e1..d80eb6f9f0bf 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -245,39 +245,37 @@ nsSVGFilterReference::GetFilterFrame() void nsSVGFilterReference::DoUpdate() { - nsSVGRenderingObserverProperty::DoUpdate(); + nsSVGIDRenderingObserver::DoUpdate(); - nsIFrame* frame = mFrameReference.Get(); - if (!frame) - return; - - // Repaint asynchronously in case the filter frame is being torn down - nsChangeHint changeHint = - nsChangeHint(nsChangeHint_RepaintFrame); - - // Don't need to request UpdateOverflow if we're being reflowed. - if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) { - NS_UpdateHint(changeHint, nsChangeHint_UpdateOverflow); + if (mFilterChainObserver) { + mFilterChainObserver->Invalidate(); } - frame->PresContext()->RestyleManager()->PostRestyleEvent( - frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); } NS_IMPL_ISUPPORTS(nsSVGFilterProperty, nsISupports) nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray &aFilters, nsIFrame *aFilteredFrame) + : mFrameReference(aFilteredFrame) { + nsIContent* filteredElement = aFilteredFrame->GetContent(); for (uint32_t i = 0; i < aFilters.Length(); i++) { if (aFilters[i].GetType() != NS_STYLE_FILTER_URL) continue; nsRefPtr reference = - new nsSVGFilterReference(aFilters[i].GetURL(), aFilteredFrame); + new nsSVGFilterReference(aFilters[i].GetURL(), filteredElement, this); mReferences.AppendElement(reference); } } +nsSVGFilterProperty::~nsSVGFilterProperty() +{ + for (uint32_t i = 0; i < mReferences.Length(); i++) { + mReferences[i]->DetachFromChainObserver(); + } +} + bool nsSVGFilterProperty::ReferencesValidResources() { @@ -299,11 +297,28 @@ nsSVGFilterProperty::IsInObserverLists() const } void -nsSVGFilterProperty::Invalidate() +nsSVGFilterProperty::DoUpdate() { - for (uint32_t i = 0; i < mReferences.Length(); i++) { - mReferences[i]->Invalidate(); + nsIFrame* frame = mFrameReference.Get(); + if (!frame) + return; + + if (frame && frame->IsFrameOfType(nsIFrame::eSVG)) { + // Changes should propagate out to things that might be observing + // the referencing frame or its ancestors. + nsSVGEffects::InvalidateRenderingObservers(frame); } + + // Repaint asynchronously in case the filter frame is being torn down + nsChangeHint changeHint = + nsChangeHint(nsChangeHint_RepaintFrame); + + // Don't need to request UpdateOverflow if we're being reflowed. + if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) { + NS_UpdateHint(changeHint, nsChangeHint_UpdateOverflow); + } + frame->PresContext()->RestyleManager()->PostRestyleEvent( + frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); } void diff --git a/layout/svg/nsSVGEffects.h b/layout/svg/nsSVGEffects.h index 81e7b40be8f6..26ddde338351 100644 --- a/layout/svg/nsSVGEffects.h +++ b/layout/svg/nsSVGEffects.h @@ -29,6 +29,7 @@ class nsSVGClipPathFrame; class nsSVGPaintServerFrame; class nsSVGFilterFrame; class nsSVGMaskFrame; +class nsSVGFilterProperty; /* * This interface allows us to be notified when a piece of SVG content is @@ -197,13 +198,20 @@ protected: * The nsSVGFilterProperty class manages a list of nsSVGFilterReferences. */ class nsSVGFilterReference MOZ_FINAL : - public nsSVGRenderingObserverProperty, public nsISVGFilterReference { + public nsSVGIDRenderingObserver, public nsISVGFilterReference { public: - nsSVGFilterReference(nsIURI *aURI, nsIFrame *aFilteredFrame) - : nsSVGRenderingObserverProperty(aURI, aFilteredFrame, false) {} + nsSVGFilterReference(nsIURI* aURI, + nsIContent* aObservingContent, + nsSVGFilterProperty* aFilterChainObserver) + : nsSVGIDRenderingObserver(aURI, aObservingContent, false) + , mFilterChainObserver(aFilterChainObserver) + { + } bool ReferencesValidResource() { return GetFilterFrame(); } + void DetachFromChainObserver() { mFilterChainObserver = nullptr; } + /** * @return the filter frame, or null if there is no filter frame */ @@ -218,9 +226,11 @@ public: protected: virtual ~nsSVGFilterReference() {} -private: - // nsSVGRenderingObserverProperty + // nsSVGIDRenderingObserver virtual void DoUpdate() MOZ_OVERRIDE; + +private: + nsSVGFilterProperty* mFilterChainObserver; }; /** @@ -240,16 +250,19 @@ public: bool ReferencesValidResources(); bool IsInObserverLists() const; - void Invalidate(); + void Invalidate() { DoUpdate(); } // nsISupports NS_DECL_ISUPPORTS protected: - virtual ~nsSVGFilterProperty() {} + virtual ~nsSVGFilterProperty(); + + virtual void DoUpdate(); private: nsTArray> mReferences; + nsSVGFrameReferenceFromProperty mFrameReference; }; class nsSVGMarkerProperty : public nsSVGRenderingObserverProperty { From 291a8d49669f3f3638c1c078d55fff2a0bc4caf2 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:46 +0200 Subject: [PATCH 18/69] Bug 1062832 - Create abstract class nsSVGFilterChainObserver for observers without frames. r=roc --- layout/svg/nsSVGEffects.cpp | 16 +++++++--------- layout/svg/nsSVGEffects.h | 33 +++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index d80eb6f9f0bf..fe0ceee9ef99 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -252,24 +252,22 @@ nsSVGFilterReference::DoUpdate() } } -NS_IMPL_ISUPPORTS(nsSVGFilterProperty, nsISupports) +NS_IMPL_ISUPPORTS(nsSVGFilterChainObserver, nsISupports) -nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray &aFilters, - nsIFrame *aFilteredFrame) - : mFrameReference(aFilteredFrame) +nsSVGFilterChainObserver::nsSVGFilterChainObserver(const nsTArray& aFilters, + nsIContent* aFilteredElement) { - nsIContent* filteredElement = aFilteredFrame->GetContent(); for (uint32_t i = 0; i < aFilters.Length(); i++) { if (aFilters[i].GetType() != NS_STYLE_FILTER_URL) continue; nsRefPtr reference = - new nsSVGFilterReference(aFilters[i].GetURL(), filteredElement, this); + new nsSVGFilterReference(aFilters[i].GetURL(), aFilteredElement, this); mReferences.AppendElement(reference); } } -nsSVGFilterProperty::~nsSVGFilterProperty() +nsSVGFilterChainObserver::~nsSVGFilterChainObserver() { for (uint32_t i = 0; i < mReferences.Length(); i++) { mReferences[i]->DetachFromChainObserver(); @@ -277,7 +275,7 @@ nsSVGFilterProperty::~nsSVGFilterProperty() } bool -nsSVGFilterProperty::ReferencesValidResources() +nsSVGFilterChainObserver::ReferencesValidResources() { for (uint32_t i = 0; i < mReferences.Length(); i++) { if (!mReferences[i]->ReferencesValidResource()) @@ -287,7 +285,7 @@ nsSVGFilterProperty::ReferencesValidResources() } bool -nsSVGFilterProperty::IsInObserverLists() const +nsSVGFilterChainObserver::IsInObserverLists() const { for (uint32_t i = 0; i < mReferences.Length(); i++) { if (!mReferences[i]->IsInObserverList()) diff --git a/layout/svg/nsSVGEffects.h b/layout/svg/nsSVGEffects.h index 26ddde338351..348c1d222c80 100644 --- a/layout/svg/nsSVGEffects.h +++ b/layout/svg/nsSVGEffects.h @@ -29,7 +29,7 @@ class nsSVGClipPathFrame; class nsSVGPaintServerFrame; class nsSVGFilterFrame; class nsSVGMaskFrame; -class nsSVGFilterProperty; +class nsSVGFilterChainObserver; /* * This interface allows us to be notified when a piece of SVG content is @@ -195,14 +195,14 @@ protected: * It fires invalidations when the SVG filter element's id changes or when * the SVG filter element's content changes. * - * The nsSVGFilterProperty class manages a list of nsSVGFilterReferences. + * The nsSVGFilterChainObserver class manages a list of nsSVGFilterReferences. */ class nsSVGFilterReference MOZ_FINAL : public nsSVGIDRenderingObserver, public nsISVGFilterReference { public: nsSVGFilterReference(nsIURI* aURI, nsIContent* aObservingContent, - nsSVGFilterProperty* aFilterChainObserver) + nsSVGFilterChainObserver* aFilterChainObserver) : nsSVGIDRenderingObserver(aURI, aObservingContent, false) , mFilterChainObserver(aFilterChainObserver) { @@ -230,7 +230,7 @@ protected: virtual void DoUpdate() MOZ_OVERRIDE; private: - nsSVGFilterProperty* mFilterChainObserver; + nsSVGFilterChainObserver* mFilterChainObserver; }; /** @@ -238,15 +238,15 @@ private: * reference filters in a filter chain. * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2); * - * In the above example, the nsSVGFilterProperty will manage two + * In the above example, the nsSVGFilterChainObserver will manage two * nsSVGFilterReferences, one for each SVG reference filter. CSS filters like * "blur(10px)" don't reference filter elements, so they don't need an * nsSVGFilterReference. The style system invalidates changes to CSS filters. */ -class nsSVGFilterProperty : public nsISupports { +class nsSVGFilterChainObserver : public nsISupports { public: - nsSVGFilterProperty(const nsTArray &aFilters, - nsIFrame *aFilteredFrame); + nsSVGFilterChainObserver(const nsTArray& aFilters, + nsIContent* aFilteredElement); bool ReferencesValidResources(); bool IsInObserverLists() const; @@ -256,12 +256,25 @@ public: NS_DECL_ISUPPORTS protected: - virtual ~nsSVGFilterProperty(); + virtual ~nsSVGFilterChainObserver(); - virtual void DoUpdate(); + virtual void DoUpdate() = 0; private: nsTArray> mReferences; +}; + +class nsSVGFilterProperty : public nsSVGFilterChainObserver { +public: + nsSVGFilterProperty(const nsTArray &aFilters, + nsIFrame *aFilteredFrame) + : nsSVGFilterChainObserver(aFilters, aFilteredFrame->GetContent()) + , mFrameReference(aFilteredFrame) + {} + +protected: + virtual void DoUpdate() MOZ_OVERRIDE; + nsSVGFrameReferenceFromProperty mFrameReference; }; From 1d10ec322f9d4383bb27121856185d5615252933 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:46 +0200 Subject: [PATCH 19/69] Bug 1062832 - Make nsSVGFilterChainObserver participate in cycle collection. r=roc --- layout/svg/nsSVGEffects.cpp | 31 +++++++++++++++++++++++++++---- layout/svg/nsSVGEffects.h | 23 ++++++++++++++++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index fe0ceee9ef99..0ed497a04dfb 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -16,6 +16,7 @@ #include "nsSVGMaskFrame.h" #include "nsIReflowCallback.h" #include "RestyleManager.h" +#include "nsCycleCollectionParticipant.h" using namespace mozilla; using namespace mozilla::dom; @@ -231,9 +232,24 @@ nsSVGRenderingObserverProperty::DoUpdate() } } -NS_IMPL_ISUPPORTS_INHERITED(nsSVGFilterReference, - nsSVGIDRenderingObserver, - nsISVGFilterReference); +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGFilterReference) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGFilterReference) + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsSVGFilterReference) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsSVGFilterReference) + tmp->mElement.Unlink(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsSVGFilterReference) + tmp->mElement.Traverse(&cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGFilterReference) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsSVGIDRenderingObserver) + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) + NS_INTERFACE_MAP_ENTRY(nsISVGFilterReference) +NS_INTERFACE_MAP_END nsSVGFilterFrame * nsSVGFilterReference::GetFilterFrame() @@ -252,7 +268,14 @@ nsSVGFilterReference::DoUpdate() } } -NS_IMPL_ISUPPORTS(nsSVGFilterChainObserver, nsISupports) +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGFilterChainObserver) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGFilterChainObserver) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGFilterChainObserver) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION(nsSVGFilterChainObserver, mReferences) nsSVGFilterChainObserver::nsSVGFilterChainObserver(const nsTArray& aFilters, nsIContent* aFilteredElement) diff --git a/layout/svg/nsSVGEffects.h b/layout/svg/nsSVGEffects.h index 348c1d222c80..9c093ee0a2d0 100644 --- a/layout/svg/nsSVGEffects.h +++ b/layout/svg/nsSVGEffects.h @@ -21,6 +21,7 @@ #include "nsSVGUtils.h" #include "nsTHashtable.h" #include "nsURIHashKey.h" +#include "nsCycleCollectionParticipant.h" class nsIAtom; class nsIPresShell; @@ -218,7 +219,8 @@ public: nsSVGFilterFrame *GetFilterFrame(); // nsISupports - NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsSVGFilterReference, nsSVGIDRenderingObserver) // nsISVGFilterReference virtual void Invalidate() MOZ_OVERRIDE { DoUpdate(); }; @@ -253,7 +255,8 @@ public: void Invalidate() { DoUpdate(); } // nsISupports - NS_DECL_ISUPPORTS + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(nsSVGFilterChainObserver) protected: virtual ~nsSVGFilterChainObserver(); @@ -272,6 +275,8 @@ public: , mFrameReference(aFilteredFrame) {} + void DetachFromFrame() { mFrameReference.Detach(); } + protected: virtual void DoUpdate() MOZ_OVERRIDE; @@ -390,12 +395,24 @@ public: (static_cast(aPropertyValue))->Release(); } + static void DestroyFilterProperty(void* aPropertyValue) + { + auto* prop = static_cast(aPropertyValue); + + // nsSVGFilterProperty is cycle-collected, so dropping the last reference + // doesn't necessarily destroy it. We need to tell it that the frame + // has now become invalid. + prop->DetachFromFrame(); + + prop->Release(); + } + static void DestroyHashtable(void* aPropertyValue) { delete static_cast (aPropertyValue); } - NS_DECLARE_FRAME_PROPERTY(FilterProperty, DestroySupports) + NS_DECLARE_FRAME_PROPERTY(FilterProperty, DestroyFilterProperty) NS_DECLARE_FRAME_PROPERTY(MaskProperty, DestroySupports) NS_DECLARE_FRAME_PROPERTY(ClipPathProperty, DestroySupports) NS_DECLARE_FRAME_PROPERTY(MarkerBeginProperty, DestroySupports) From 18e617e6e49b15aa3ada8a753fc1d9081fed584e Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:46 +0200 Subject: [PATCH 20/69] Bug 1043421 - Make native Mac menus observe mutations from the observed elements directly. r=smichaud --- widget/cocoa/nsMenuBarX.mm | 2 +- widget/cocoa/nsMenuGroupOwnerX.h | 1 - widget/cocoa/nsMenuGroupOwnerX.mm | 21 ++--- widget/tests/native_menus_window.xul | 8 ++ .../tests/standalone_native_menu_window.xul | 83 ++++++++++++++++--- 5 files changed, 87 insertions(+), 28 deletions(-) diff --git a/widget/cocoa/nsMenuBarX.mm b/widget/cocoa/nsMenuBarX.mm index cc11d1a0bddf..1934d5697b08 100644 --- a/widget/cocoa/nsMenuBarX.mm +++ b/widget/cocoa/nsMenuBarX.mm @@ -423,7 +423,7 @@ char nsMenuBarX::GetLocalizedAccelKey(const char *shortcutID) if (!sLastGeckoMenuBarPainted) return 0; - nsCOMPtr domDoc(do_QueryInterface(sLastGeckoMenuBarPainted->mDocument)); + nsCOMPtr domDoc(do_QueryInterface(sLastGeckoMenuBarPainted->mContent->OwnerDoc())); if (!domDoc) return 0; diff --git a/widget/cocoa/nsMenuGroupOwnerX.h b/widget/cocoa/nsMenuGroupOwnerX.h index 8408a582f92d..caed9a50cc44 100644 --- a/widget/cocoa/nsMenuGroupOwnerX.h +++ b/widget/cocoa/nsMenuGroupOwnerX.h @@ -47,7 +47,6 @@ protected: uint32_t mCurrentCommandID; // unique command id (per menu-bar) to // give to next item that asks - nsIDocument* mDocument; // pointer to document // stores observers for content change notification nsDataHashtable, nsChangeObserver *> mContentToObserverTable; diff --git a/widget/cocoa/nsMenuGroupOwnerX.mm b/widget/cocoa/nsMenuGroupOwnerX.mm index c3fd2dd649a3..3afde7221bc2 100644 --- a/widget/cocoa/nsMenuGroupOwnerX.mm +++ b/widget/cocoa/nsMenuGroupOwnerX.mm @@ -30,17 +30,14 @@ NS_IMPL_ISUPPORTS(nsMenuGroupOwnerX, nsIMutationObserver) nsMenuGroupOwnerX::nsMenuGroupOwnerX() -: mCurrentCommandID(eCommand_ID_Last), - mDocument(nullptr) +: mCurrentCommandID(eCommand_ID_Last) { } nsMenuGroupOwnerX::~nsMenuGroupOwnerX() { - // make sure we unregister ourselves as a document observer - if (mDocument) - mDocument->RemoveMutationObserver(this); + MOZ_ASSERT(mContentToObserverTable.Count() == 0, "have outstanding mutation observers!\n"); } @@ -51,12 +48,6 @@ nsresult nsMenuGroupOwnerX::Create(nsIContent* aContent) mContent = aContent; - nsIDocument* doc = aContent->OwnerDoc(); - if (!doc) - return NS_ERROR_FAILURE; - doc->AddMutationObserver(this); - mDocument = doc; - return NS_OK; } @@ -93,8 +84,6 @@ void nsMenuGroupOwnerX::ContentAppended(nsIDocument* aDocument, void nsMenuGroupOwnerX::NodeWillBeDestroyed(const nsINode * aNode) { - // our menu bar node is being destroyed - mDocument = nullptr; } @@ -187,12 +176,18 @@ void nsMenuGroupOwnerX::ParentChainChanged(nsIContent *aContent) void nsMenuGroupOwnerX::RegisterForContentChanges(nsIContent *aContent, nsChangeObserver *aMenuObject) { + if (!mContentToObserverTable.Contains(aContent)) { + aContent->AddMutationObserver(this); + } mContentToObserverTable.Put(aContent, aMenuObject); } void nsMenuGroupOwnerX::UnregisterForContentChanges(nsIContent *aContent) { + if (mContentToObserverTable.Contains(aContent)) { + aContent->RemoveMutationObserver(this); + } mContentToObserverTable.Remove(aContent); } diff --git a/widget/tests/native_menus_window.xul b/widget/tests/native_menus_window.xul index 3e240c36845c..6e614d017794 100644 --- a/widget/tests/native_menus_window.xul +++ b/widget/tests/native_menus_window.xul @@ -225,6 +225,14 @@ // At this point in the tests the state of the menus has been returned // to the originally diagramed state. + // Test command disabling + var cmd_NewItem0 = document.getElementById("cmd_NewItem0"); + ok(testItem("1|0", "cmd_NewItem0"), sa); + cmd_NewItem0.setAttribute("disabled", "true"); + ok(!testItem("1|0", "cmd_NewItem0"), sna); + cmd_NewItem0.removeAttribute("disabled"); + ok(testItem("1|0", "cmd_NewItem0"), sa); + // Remove menu. menubarNode.removeChild(newMenu0); ok(runBaseMenuTests(), sa); diff --git a/widget/tests/standalone_native_menu_window.xul b/widget/tests/standalone_native_menu_window.xul index 984a309da872..6783a66e6e01 100644 --- a/widget/tests/standalone_native_menu_window.xul +++ b/widget/tests/standalone_native_menu_window.xul @@ -102,27 +102,76 @@ testItem(menu, "0|3|1", "cmd_BarItem1"); } + function createStandaloneNativeMenu(menuNode) { + try { + let Cc = Components.classes; + let Ci = Components.interfaces; + let menu = Cc["@mozilla.org/widget/standalonenativemenu;1"].createInstance(Ci.nsIStandaloneNativeMenu); + menu.init(menuNode); + return menu; + } catch (e) { + ok(false, "Failed creating nsIStandaloneNativeMenu instance"); + throw e; + } + } + + function runDetachedMenuTests(addMenupopupBeforeCreatingSNM) { + let menu = createXULMenu("Detached menu"); + menu.setAttribute("image", 'data:image/svg+xml,'); + let menupopup = createXULMenuPopup(); + + let popupShowingFired = false; + let itemActivated = false; + + menupopup.addEventListener("popupshowing", function (e) { + popupShowingFired = true; + + let menuitem = document.createElementNS(XUL_NS, "menuitem"); + menuitem.setAttribute("label", "detached menu item"); + menuitem.addEventListener("command", function (e) { + itemActivated = true; + }) + menupopup.appendChild(menuitem); + }) + + // It shouldn't make a difference whether the menupopup is added to the + // menu element before or after we create the nsIStandaloneNativeMenu + // instance with it. We test both orders by calling this function twice + // with different values for addMenupopupBeforeCreatingSNM. + + var menuSNM = null; // the nsIStandaloneNativeMenu object for "menu" + if (addMenupopupBeforeCreatingSNM) { + menu.appendChild(menupopup); + menuSNM = createStandaloneNativeMenu(menu); + } else { + menuSNM = createStandaloneNativeMenu(menu); + menu.appendChild(menupopup); + } + + try { + ok(!popupShowingFired, "popupshowing shouldn't have fired before our call to menuWillOpen()"); + menuSNM.menuWillOpen(); + ok(popupShowingFired, "calling menuWillOpen() should have notified our popupshowing listener"); + + ok(!itemActivated, "our dynamically-added menuitem shouldn't have been activated yet"); + menuSNM.activateNativeMenuItemAt("0"); + ok(itemActivated, "the new menu item should have been activated now"); + } catch (ex) { + ok(false, "dynamic menu test failed: " + ex); + } + } + function onLoad() { var _delayedOnLoad = function() { - var Cc = Components.classes; - var Ci = Components.interfaces; + try { var menuNode = document.getElementById("standalonenativemenu"); - var menu; - try { - menu = Cc["@mozilla.org/widget/standalonenativemenu;1"].createInstance(Ci.nsIStandaloneNativeMenu); - menu.init(menuNode); - } catch (e) { - ok(false, "Create nsIStandaloneNativeMenu instance"); - onTestsFinished(); - return; - } + var menu = createStandaloneNativeMenu(menuNode); // First let's run the base menu tests. ok(runBaseMenuTests(menu), "base tests #1"); // Set up some nodes that we'll use. - //var menubarNode = document.getElementById("nativemenubar"); var newMenu0 = createXULMenu("NewMenu0"); var newMenu1 = createXULMenu("NewMenu1"); var newMenuPopup0 = createXULMenuPopup(); @@ -267,7 +316,15 @@ newMenu1.setAttribute("hidden", "false"); menu.forceUpdateNativeMenuAt("1"); - onTestsFinished(); + // Run tests where the menu nodes are not in the document's node tree. + runDetachedMenuTests(false); + runDetachedMenuTests(true); + + } catch (e) { + ok(false, "Caught an exception: " + e); + } finally { + onTestsFinished(); + } } setTimeout(_delayedOnLoad, 1000); From 6a2e242d0cf474aa7a1114ced6ccc4d740fb772e Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 9 Sep 2014 17:14:47 +0200 Subject: [PATCH 21/69] Bug 1062870 - Restore the post-filter dirtyRect after building the filtered display items. r=roc --- layout/generic/nsFrame.cpp | 6 ++++- layout/reftests/bugs/1059498-2.html | 40 ++++++++++++++++++++++++++++ layout/reftests/bugs/1059498-3.html | 41 +++++++++++++++++++++++++++++ layout/reftests/bugs/reftest.list | 2 ++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 layout/reftests/bugs/1059498-2.html create mode 100644 layout/reftests/bugs/1059498-3.html diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index a72cb8eea925..74a37847e07b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1936,6 +1936,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder); aBuilder->SetContainsBlendModes(BlendModeSet()); + nsRect dirtyRectOutsideTransform = dirtyRect; if (isTransformed) { const nsRect overflow = GetVisualOverflowRectRelativeToSelf(); if (aBuilder->IsForPainting() && @@ -1960,6 +1961,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, } bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this); + nsRect dirtyRectOutsideSVGEffects = dirtyRect; if (usingSVGEffects) { dirtyRect = nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect); @@ -2082,6 +2084,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, * output even if the element being filtered wouldn't otherwise do so. */ if (usingSVGEffects) { + // Revert to the post-filter dirty rect. + buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects); /* List now emptied, so add the new list to the top. */ resultList.AppendNewToTop( new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList)); @@ -2116,7 +2120,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, clipState.Restore(); // Revert to the dirtyrect coming in from the parent, without our transform // taken into account. - buildingDisplayList.SetDirtyRect(aDirtyRect); + buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform); // Revert to the outer reference frame and offset because all display // items we create from now on are outside the transform. const nsIFrame* outerReferenceFrame = diff --git a/layout/reftests/bugs/1059498-2.html b/layout/reftests/bugs/1059498-2.html new file mode 100644 index 000000000000..f0bddaec92a6 --- /dev/null +++ b/layout/reftests/bugs/1059498-2.html @@ -0,0 +1,40 @@ + + + +Test for bug 1059498 - Paint parts of the filter that are caused by parts of the source that are invisible + + + +
+
+
+ + + + + + + + diff --git a/layout/reftests/bugs/1059498-3.html b/layout/reftests/bugs/1059498-3.html new file mode 100644 index 000000000000..e287f53f6522 --- /dev/null +++ b/layout/reftests/bugs/1059498-3.html @@ -0,0 +1,41 @@ + + + +Test for bug 1059498 - Paint parts of the filter that are caused by parts of the source that are invisible + + + +
+
+
+ + + + + + + + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 5392f693e69b..143fed00678c 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1831,3 +1831,5 @@ pref(browser.display.use_document_fonts,0) == 1022481-1.html 1022481-1-ref.html test-pref(layout.css.grid.enabled,true) == 1053035-1-grid.html 1053035-1-ref.html == 1059167-1.html 1059167-1-ref.html == 1059498-1.html 1059498-1-ref.html +== 1059498-2.html 1059498-1-ref.html +== 1059498-3.html 1059498-1-ref.html From 01390fc30d57d0de9b5eb76a2b389793b0d42fb5 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Tue, 9 Sep 2014 08:21:45 -0700 Subject: [PATCH 22/69] Bug 1064437 - Remove Proxy special-case in ForceCOWBehavior. r=gabor --- js/xpconnect/wrappers/WrapperFactory.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index 3267bed23dc0..d3472ac6fbf2 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -120,10 +120,6 @@ ForceCOWBehavior(JSObject *obj) "instances modulo this hack"); return true; } - // Proxies get OpaqueXrayTraits, but we still need COWs to them for now to - // let the SpecialPowers wrapper work. - if (key == JSProto_Proxy) - return true; return false; } From 7b21808c591a0941d355f8fa5b755f3d81e678ce Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Tue, 9 Sep 2014 11:43:28 -0400 Subject: [PATCH 23/69] Backout 0abf93858f17 for bustage on CLOSED TREE r=tbpl-red --- js/src/jit/JitcodeMap.cpp | 19 +------------------ js/src/jit/JitcodeMap.h | 35 ----------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index 1f0e5a7fb6e2..c97d6d1988c9 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -123,24 +123,7 @@ JitcodeGlobalEntry::compare(const JitcodeGlobalEntry &ent1, const JitcodeGlobalE // Ensure no overlaps for non-query lookups. JS_ASSERT_IF(!ent1.isQuery() && !ent2.isQuery(), !ent1.overlapsWith(ent2)); - // For two non-query entries, just comapare the start addresses. - if (!ent1.isQuery() && !ent2.isQuery()) - return ComparePointers(ent1.nativeStartAddr(), ent2.nativeStartAddr()); - - void *ptr = ent1.isQuery() ? ent1.nativeStartAddr() : ent2.nativeStartAddr(); - const JitcodeGlobalEntry &ent = ent1.isQuery() ? ent2 : ent1; - int flip = ent1.isQuery() ? 1 : -1; - - if (ent.startsBelowPointer(ptr)) { - if (ent.endsAbovePointer(ptr)) - return 0; - - // query ptr > entry - return flip * 1; - } - - // query ptr < entry - return flip * -1; + return ComparePointers(ent1.nativeStartAddr(), ent2.nativeStartAddr()); } bool diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h index 5eaef705b3f9..e4aa521898d1 100644 --- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -7,8 +7,6 @@ #ifndef jit_JitcodeMap_h #define jit_JitcodeMap_h -#include - #include "ds/SplayTree.h" #include "jit/CompactBuffer.h" #include "jit/CompileInfo.h" @@ -490,39 +488,6 @@ class JitcodeGlobalTable return tree_.empty(); } - uint32_t sizeDebug() { - struct SZ { - uint32_t count_; - SZ() : count_(0) {}; - SZ(const SZ &other) = delete; - void operator()(JitcodeGlobalEntry &ent) { count_++; } - }; - SZ sz; - tree_.forEach(sz); - return sz.count_; - } - - bool lookupDebug(void *ptr) { - struct LOOKUP { - void *ptr_; - bool found_; - LOOKUP(void *ptr) : ptr_(ptr), found_(false) {} - LOOKUP(const LOOKUP &other) = delete; - void operator()(JitcodeGlobalEntry &ent) { - if (ent.containsPointer(ptr_)) - found_ = true; - } - }; - LOOKUP l(ptr); - tree_.forEach(l); - return l.found_; - } - - template - void forEach(T &t) { - return tree_.forEach(t); - } - bool lookup(void *ptr, JitcodeGlobalEntry *result); void lookupInfallible(void *ptr, JitcodeGlobalEntry *result); From 59dec3826a940ae1cb1d1b5c887592b415fbe798 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 9 Sep 2014 12:33:25 -0400 Subject: [PATCH 24/69] Bug 1064840 - Only display the unused-apz visual warning if the async transform is not applied to any content. r=botond --- gfx/layers/apz/src/AsyncPanZoomController.cpp | 4 +++- gfx/layers/apz/src/AsyncPanZoomController.h | 13 +++++++++++++ gfx/layers/composite/AsyncCompositionManager.cpp | 10 +++++++--- gfx/layers/composite/ContainerLayerComposite.cpp | 3 ++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 4195c418683d..6ea2ad74b7bb 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -853,7 +853,8 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId, mTouchBlockBalance(0), mTreeManager(aTreeManager), mAPZCId(sAsyncPanZoomControllerCount++), - mSharedLock(nullptr) + mSharedLock(nullptr), + mAsyncTransformAppliedToContent(false) { MOZ_COUNT_CTOR(AsyncPanZoomController); @@ -2434,6 +2435,7 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime) // fling is happening, it has to keep compositing so that the animation is // smooth. If an animation frame is requested, it is the compositor's // responsibility to schedule a composite. + mAsyncTransformAppliedToContent = false; bool requestAnimationFrame = false; Vector deferredTasks; diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index d651c80e0a91..335b8b9d3b01 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -1110,9 +1110,22 @@ public: mTestAsyncScrollOffset = aPoint; } + void MarkAsyncTransformAppliedToContent() + { + mAsyncTransformAppliedToContent = true; + } + + bool GetAsyncTransformAppliedToContent() const + { + return mAsyncTransformAppliedToContent; + } + private: // Extra offset to add in SampleContentTransformForFrame for testing CSSPoint mTestAsyncScrollOffset; + // Flag to track whether or not the APZ transform is not used. This + // flag is recomputed for every composition frame. + bool mAsyncTransformAppliedToContent; }; class AsyncPanZoomAnimation { diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp index d27704671ea5..38f623117d45 100644 --- a/gfx/layers/composite/AsyncCompositionManager.cpp +++ b/gfx/layers/composite/AsyncCompositionManager.cpp @@ -504,16 +504,16 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint) } static bool -SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp aPoint) +SampleAPZAnimations(const LayerMetricsWrapper& aLayer, TimeStamp aSampleTime) { bool activeAnimations = false; for (LayerMetricsWrapper child = aLayer.GetFirstChild(); child; child = child.GetNextSibling()) { - activeAnimations |= SampleAPZAnimations(child, aPoint); + activeAnimations |= SampleAPZAnimations(child, aSampleTime); } if (AsyncPanZoomController* apzc = aLayer.GetApzc()) { - activeAnimations |= apzc->AdvanceAnimations(aPoint); + activeAnimations |= apzc->AdvanceAnimations(aSampleTime); } return activeAnimations; @@ -574,6 +574,10 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer) scrollOffset, &overscrollTransform); + if (!aLayer->IsScrollInfoLayer()) { + controller->MarkAsyncTransformAppliedToContent(); + } + const FrameMetrics& metrics = aLayer->GetFrameMetrics(i); CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel(); CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ? diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp index 38e4f2d03bbe..e7bbf34d1bed 100644 --- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -435,7 +435,8 @@ ContainerRender(ContainerT* aContainer, // underlying layer. for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) { if (AsyncPanZoomController* apzc = i.GetApzc()) { - if (!Matrix4x4(apzc->GetCurrentAsyncTransform()).IsIdentity()) { + if (!apzc->GetAsyncTransformAppliedToContent() + && !Matrix4x4(apzc->GetCurrentAsyncTransform()).IsIdentity()) { aManager->UnusedApzTransformWarning(); break; } From 010164839f51982d8e9ae9e0203062eafe607748 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Tue, 9 Sep 2014 12:01:54 -0400 Subject: [PATCH 25/69] Bug 1057512 - --run-by-dir enabled for browser-chrome on trunk. r=ted --- testing/mochitest/runtests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index d0d8399711e1..1506c9bcf041 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -1634,6 +1634,10 @@ class Mochitest(MochitestUtilsMixin): self.setTestRoot(options) + # Until we have all green, this only runs on bc* jobs (not dt* jobs) + if options.browserChrome and not options.subsuite: + options.runByDir = True + if not options.runByDir: return self.runMochitests(options, onLaunch) From 856798cdc0de176c4ec28c9f4885ab2e928281b8 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 9 Sep 2014 11:33:56 -0500 Subject: [PATCH 26/69] Bug 1064668 - OdinMonkey: Maintain correct offsets for src line info (r=bbouvier) --HG-- extra : rebase_source : 62f73408bf57b273d9e043dc93be013a7d085a84 --- js/src/asmjs/AsmJSModule.h | 17 ++++++++++------- js/src/asmjs/AsmJSValidate.cpp | 10 ++-------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/js/src/asmjs/AsmJSModule.h b/js/src/asmjs/AsmJSModule.h index 60fd219975de..56ea9ba9df74 100644 --- a/js/src/asmjs/AsmJSModule.h +++ b/js/src/asmjs/AsmJSModule.h @@ -1127,18 +1127,21 @@ class AsmJSModule // the exported functions have been added. bool addExportedFunction(PropertyName *name, - uint32_t srcStart, - uint32_t srcEnd, + uint32_t funcSrcBegin, + uint32_t funcSrcEnd, PropertyName *maybeFieldName, ArgCoercionVector &&argCoercions, ReturnType returnType) { + // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource + // (the entire file) and ExportedFunctions store offsets relative to + // the beginning of the module (so that they are caching-invariant). JS_ASSERT(isFinishedWithFunctionBodies() && !isFinished()); - ExportedFunction func(name, srcStart, srcEnd, maybeFieldName, - mozilla::Move(argCoercions), returnType); - if (exports_.length() >= UINT32_MAX) - return false; - return exports_.append(mozilla::Move(func)); + JS_ASSERT(srcStart_ < funcSrcBegin); + JS_ASSERT(funcSrcBegin < funcSrcEnd); + ExportedFunction func(name, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_, + maybeFieldName, mozilla::Move(argCoercions), returnType); + return exports_.length() < UINT32_MAX && exports_.append(mozilla::Move(func)); } unsigned numExportedFunctions() const { JS_ASSERT(isFinishedWithFunctionBodies()); diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index d7c79cfd0bcb..31962413d43d 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -1015,13 +1015,8 @@ class MOZ_STACK_CLASS ModuleCompiler void define(ModuleCompiler &m, ParseNode *fn) { JS_ASSERT(!defined_); defined_ = true; - - // The begin/end char range is relative to the beginning of the module. - // hence the assertions. - JS_ASSERT(fn->pn_pos.begin > m.srcStart()); - JS_ASSERT(fn->pn_pos.begin <= fn->pn_pos.end); - srcBegin_ = fn->pn_pos.begin - m.srcStart(); - srcEnd_ = fn->pn_pos.end - m.srcStart(); + srcBegin_ = fn->pn_pos.begin; + srcEnd_ = fn->pn_pos.end; } uint32_t srcBegin() const { JS_ASSERT(defined_); return srcBegin_; } @@ -1462,7 +1457,6 @@ class MOZ_STACK_CLASS ModuleCompiler Label &syncInterruptLabel() { return syncInterruptLabel_; } bool hasError() const { return errorString_ != nullptr; } const AsmJSModule &module() const { return *module_.get(); } - uint32_t srcStart() const { return module_->srcStart(); } bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); } bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); } bool supportsSimd() const { return supportsSimd_; } From 917d692479ef551b9b7c59fb4f3aec2979e58b4a Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 9 Sep 2014 11:35:07 -0500 Subject: [PATCH 27/69] Bug 1064668 - OdinMonkey: Only add AsmJSActivation to profiling stack after it is fully initialized (r=djvj) --- js/src/vm/Stack-inl.h | 8 +------- js/src/vm/Stack.cpp | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 2ea3aa6c3cee..79693dd11881 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -744,20 +744,14 @@ Activation::Activation(ThreadSafeContext *cx, Kind kind) kind_(kind) { cx->perThreadData->activation_ = this; - - // Link the activation into the list of profiling activations if needed. - if (isProfiling()) - registerProfiling(); } Activation::~Activation() { + JS_ASSERT_IF(isProfiling(), this != cx_->perThreadData->profilingActivation_); JS_ASSERT(cx_->perThreadData->activation_ == this); JS_ASSERT(hideScriptedCallerCount_ == 0); cx_->perThreadData->activation_ = prev_; - - if (isProfiling()) - unregisterProfiling(); } bool diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index a3d2ec9f9d03..1e7038ab982b 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1554,11 +1554,11 @@ AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module) fp_(nullptr), exitReason_(AsmJSExit::None) { + (void) entrySP_; // squelch GCC warning + + // NB: this is a hack and can be removed once Ion switches over to + // JS::ProfilingFrameIterator. if (cx->runtime()->spsProfiler.enabled()) { - // Use a profiler string that matches jsMatch regex in - // browser/devtools/profiler/cleopatra/js/parserWorker.js. - // (For now use a single static string to avoid further slowing down - // calls into asm.js.) profiler_ = &cx->runtime()->spsProfiler; profiler_->enterAsmJS("asm.js code :0", this); } @@ -1568,14 +1568,21 @@ AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module) prevAsmJS_ = cx->mainThread().asmJSActivationStack_; - JSRuntime::AutoLockForInterrupt lock(cx->runtime()); - cx->mainThread().asmJSActivationStack_ = this; + { + JSRuntime::AutoLockForInterrupt lock(cx->runtime()); + cx->mainThread().asmJSActivationStack_ = this; + } - (void) entrySP_; // squelch GCC warning + // Now that the AsmJSActivation is fully initialized, make it visible to + // asynchronous profiling. + registerProfiling(); } AsmJSActivation::~AsmJSActivation() { + // Hide this activation from the profiler before is is destroyed. + unregisterProfiling(); + if (profiler_) profiler_->exitAsmJS(); From f85d7e6ce3dfaa77a1a3e3cbdf6d633c8c3b0b43 Mon Sep 17 00:00:00 2001 From: ProgramFOX Date: Sat, 6 Sep 2014 11:21:28 +0200 Subject: [PATCH 28/69] Bug 948379: Added int32x4 border test cases; r=bbouvier --- js/src/builtin/SIMD.cpp | 2 +- .../tests/ecma_6/TypedObject/simd/int32x4add.js | 13 +++++++++++-- .../tests/ecma_6/TypedObject/simd/int32x4and.js | 13 +++++++++++-- .../ecma_6/TypedObject/simd/int32x4equal.js | 2 -- .../TypedObject/simd/int32x4fromfloat32x4.js | 9 +++++++-- .../simd/int32x4fromfloat32x4bits.js | 17 +++++++++++------ .../TypedObject/simd/int32x4greaterthan.js | 2 -- .../ecma_6/TypedObject/simd/int32x4lessthan.js | 2 -- .../tests/ecma_6/TypedObject/simd/int32x4lsh.js | 12 ++++++++++-- .../tests/ecma_6/TypedObject/simd/int32x4mul.js | 13 +++++++++++-- .../tests/ecma_6/TypedObject/simd/int32x4neg.js | 12 ++++++++++-- .../tests/ecma_6/TypedObject/simd/int32x4not.js | 12 ++++++++++-- .../tests/ecma_6/TypedObject/simd/int32x4or.js | 13 +++++++++++-- .../tests/ecma_6/TypedObject/simd/int32x4rsh.js | 11 +++++++++-- .../ecma_6/TypedObject/simd/int32x4shuffle.js | 2 -- .../TypedObject/simd/int32x4shufflemix.js | 2 -- .../tests/ecma_6/TypedObject/simd/int32x4sub.js | 13 +++++++++++-- .../ecma_6/TypedObject/simd/int32x4ursh.js | 12 ++++++++++-- .../ecma_6/TypedObject/simd/int32x4with.js | 6 +++--- .../ecma_6/TypedObject/simd/int32x4withflag.js | 2 -- .../tests/ecma_6/TypedObject/simd/int32x4xor.js | 13 +++++++++++-- 21 files changed, 137 insertions(+), 46 deletions(-) diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 3a0f3f65caa5..5091939bc0bf 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -799,7 +799,7 @@ FuncConvert(JSContext *cx, unsigned argc, Value *vp) Elem *val = TypedObjectMemory(args[0]); RetElem result[Vret::lanes]; for (unsigned i = 0; i < Vret::lanes; i++) - result[i] = RetElem(val[i]); + result[i] = ConvertScalar(val[i]); return StoreResult(cx, args, result); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4add.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4add.js index 5e68933ed7d4..3682bbddf0c1 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4add.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4add.js @@ -8,8 +8,6 @@ var summary = 'int32x4 add'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var b = int32x4(10, 20, 30, 40); var c = SIMD.int32x4.add(a, b); @@ -18,6 +16,17 @@ function test() { assertEq(c.z, 33); assertEq(c.w, 44); + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var e = int32x4(1, -1, 0, 0); + var f = SIMD.int32x4.add(d, e); + assertEq(f.x, INT32_MIN); + assertEq(f.y, INT32_MAX); + assertEq(f.z, INT32_MAX); + assertEq(f.w, INT32_MIN); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4and.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4and.js index 0f1e91025b68..fb3a0edc9053 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4and.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4and.js @@ -8,8 +8,6 @@ var summary = 'int32x4 and'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var b = int32x4(10, 20, 30, 40); var c = SIMD.int32x4.and(a, b); @@ -18,6 +16,17 @@ function test() { assertEq(c.z, 2); assertEq(c.w, 0); + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var e = int32x4(INT32_MIN, INT32_MAX, INT32_MAX, INT32_MIN); + var f = SIMD.int32x4.and(d, e); + assertEq(f.x, (INT32_MAX & INT32_MIN) | 0); + assertEq(f.y, (INT32_MIN & INT32_MAX) | 0); + assertEq(f.z, (INT32_MAX & INT32_MAX) | 0); + assertEq(f.w, (INT32_MIN & INT32_MIN) | 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4equal.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4equal.js index 4729d486900e..693e7a2cbddd 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4equal.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4equal.js @@ -8,8 +8,6 @@ var summary = 'int32x4 equal'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 20, 30, 40); var b = int32x4(10, 20, 30, 4); var c = SIMD.int32x4.equal(a, b); diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4fromfloat32x4.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4fromfloat32x4.js index 870636637283..0028a441ca3a 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4fromfloat32x4.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4fromfloat32x4.js @@ -8,8 +8,6 @@ var summary = 'int32x4 fromFloat32x4'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = float32x4(1.1, 2.2, 3.3, 4.6); var c = SIMD.int32x4.fromFloat32x4(a); assertEq(c.x, 1); @@ -17,6 +15,13 @@ function test() { assertEq(c.z, 3); assertEq(c.w, 4); + var d = float32x4(NaN, -0, Infinity, -Infinity); + var f = SIMD.int32x4.fromFloat32x4(d); + assertEq(f.x, 0); + assertEq(f.y, 0); + assertEq(f.z, 0); + assertEq(f.w, 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4fromfloat32x4bits.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4fromfloat32x4bits.js index 83b2dab16c77..da1b7b37bd79 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4fromfloat32x4bits.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4fromfloat32x4bits.js @@ -8,14 +8,19 @@ var summary = 'int32x4 fromFloat32x4Bits'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = float32x4(1, 2, 3, 4); var c = SIMD.int32x4.fromFloat32x4Bits(a); - assertEq(c.x, 1065353216); - assertEq(c.y, 1073741824); - assertEq(c.z, 1077936128); - assertEq(c.w, 1082130432); + assertEq(c.x, 0x3f800000 | 0); + assertEq(c.y, 0x40000000 | 0); + assertEq(c.z, 0x40400000 | 0); + assertEq(c.w, 0x40800000 | 0); + + var d = float32x4(NaN, -0, Infinity, -Infinity); + var f = SIMD.int32x4.fromFloat32x4Bits(d); + assertEq(f.x, 0x7fc00000 | 0); + assertEq(f.y, 0x80000000 | 0); + assertEq(f.z, 0x7f800000 | 0); + assertEq(f.w, 0xff800000 | 0); if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4greaterthan.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4greaterthan.js index 85cf88a6588b..74c82a056a54 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4greaterthan.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4greaterthan.js @@ -8,8 +8,6 @@ var summary = 'int32x4 greaterThan'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 20, 3, 40); var b = int32x4(10, 2, 30, 4); var c = SIMD.int32x4.greaterThan(b,a); diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4lessthan.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4lessthan.js index 15b5747c2780..1f2a2a63f8de 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4lessthan.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4lessthan.js @@ -8,8 +8,6 @@ var summary = 'int32x4 lessThan'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 20, 3, 40); var b = int32x4(10, 2, 30, 4); var c = SIMD.int32x4.lessThan(a, b); diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4lsh.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4lsh.js index 6b9a361f3cde..17a4465c77ea 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4lsh.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4lsh.js @@ -8,8 +8,6 @@ var summary = 'int32x4 lsh'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - for (var bits = 0; bits < 32; bits++) { var a = int32x4(-1, 2, -3, 4); var c = SIMD.int32x4.shiftLeft(a, bits); @@ -19,6 +17,16 @@ function test() { assertEq(c.w, 4 << bits); } + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var f = SIMD.int32x4.shiftLeft(d, 1); + assertEq(f.x, (INT32_MAX << 1) | 0); + assertEq(f.y, (INT32_MIN << 1) | 0); + assertEq(f.z, (INT32_MAX << 1) | 0); + assertEq(f.w, (INT32_MIN << 1) | 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4mul.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4mul.js index 86d3183ebdea..3a39eb3cb57a 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4mul.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4mul.js @@ -8,8 +8,6 @@ var summary = 'int32x4 mul'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var b = int32x4(10, 20, 30, 40); var c = SIMD.int32x4.mul(a, b); @@ -18,6 +16,17 @@ function test() { assertEq(c.z, 90); assertEq(c.w, 160); + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var e = int32x4(-1, -1, INT32_MIN, INT32_MIN); + var f = SIMD.int32x4.mul(d, e); + assertEq(f.x, (INT32_MAX * -1) | 0); + assertEq(f.y, (INT32_MIN * -1) | 0); + assertEq(f.z, (INT32_MAX * INT32_MIN) | 0); + assertEq(f.w, (INT32_MIN * INT32_MIN) | 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4neg.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4neg.js index 12b607294572..967c6a81429e 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4neg.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4neg.js @@ -8,8 +8,6 @@ var summary = 'int32x4 neg'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var c = SIMD.int32x4.neg(a); assertEq(c.x, -1); @@ -17,6 +15,16 @@ function test() { assertEq(c.z, -3); assertEq(c.w, -4); + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, -0, 0); + var f = SIMD.int32x4.neg(d); + assertEq(f.x, -INT32_MAX | 0); + assertEq(f.y, -INT32_MIN | 0); + assertEq(f.z, 0); + assertEq(f.w, 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4not.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4not.js index 92ae3166254f..febb3e9fa031 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4not.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4not.js @@ -8,8 +8,6 @@ var summary = 'int32x4 not'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var c = SIMD.int32x4.not(a); assertEq(c.x, -2); @@ -17,6 +15,16 @@ function test() { assertEq(c.z, -4); assertEq(c.w, -5); + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, 0, 0); + var f = SIMD.int32x4.not(d); + assertEq(f.x, ~INT32_MAX | 0); + assertEq(f.y, ~INT32_MIN | 0); + assertEq(f.z, ~0 | 0); + assertEq(f.w, ~0 | 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4or.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4or.js index 9c6f76f28b40..55208240470e 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4or.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4or.js @@ -8,8 +8,6 @@ var summary = 'int32x4 or'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var b = int32x4(10, 20, 30, 40); var c = SIMD.int32x4.or(a, b); @@ -18,6 +16,17 @@ function test() { assertEq(c.z, 31); assertEq(c.w, 44); + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var e = int32x4(INT32_MIN, INT32_MAX, INT32_MAX, INT32_MIN); + var f = SIMD.int32x4.or(d, e); + assertEq(f.x, (INT32_MAX | INT32_MIN) | 0); + assertEq(f.y, (INT32_MIN | INT32_MAX) | 0); + assertEq(f.z, (INT32_MAX | INT32_MAX) | 0); + assertEq(f.w, (INT32_MIN | INT32_MIN) | 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4rsh.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4rsh.js index b8a2831ed3ac..f785adc5da0a 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4rsh.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4rsh.js @@ -8,8 +8,6 @@ var summary = 'int32x4 rsh'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - for (var bits = 0; bits < 32; bits++) { var a = int32x4(-1, 2, -3, 4); var c = SIMD.int32x4.shiftRight(a, bits); @@ -19,6 +17,15 @@ function test() { assertEq(c.w, 4 >> bits); } + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var f = SIMD.int32x4.shiftRight(d, 1); + assertEq(f.x, INT32_MAX >> 1); + assertEq(f.y, INT32_MIN >> 1); + assertEq(f.z, INT32_MAX >> 1); + assertEq(f.w, INT32_MIN >> 1); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4shuffle.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4shuffle.js index 0f358c43bfdd..9cb02a42bc3a 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4shuffle.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4shuffle.js @@ -8,8 +8,6 @@ var summary = 'int32x4 shuffle'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var c = SIMD.int32x4.shuffle(a, 0x1B); assertEq(c.x, 4); diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4shufflemix.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4shufflemix.js index 294b66d81382..a8f4e9d50ca2 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4shufflemix.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4shufflemix.js @@ -8,8 +8,6 @@ var summary = 'int32x4 shuffleMix'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var b = int32x4(10, 20, 30, 40); var c = SIMD.int32x4.shuffleMix(a,b, 0x1B); diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4sub.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4sub.js index 39e12d901e31..f76c1830fa1c 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4sub.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4sub.js @@ -8,8 +8,6 @@ var summary = 'int32x4 sub'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var b = int32x4(10, 20, 30, 40); var c = SIMD.int32x4.sub(b,a); @@ -18,6 +16,17 @@ function test() { assertEq(c.z, 27); assertEq(c.w, 36); + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(-1, 1, INT32_MAX, INT32_MIN); + var e = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var f = SIMD.int32x4.sub(e, d); + assertEq(f.x, (INT32_MAX - -1) | 0); + assertEq(f.y, (INT32_MIN - 1) | 0); + assertEq(f.z, 0); + assertEq(f.w, 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4ursh.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4ursh.js index c12a7f3ea72d..6a16d31ab359 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4ursh.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4ursh.js @@ -8,8 +8,6 @@ var summary = 'int32x4 ursh'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - for (var bits = 0; bits < 32; bits++) { var a = int32x4(-1, 2, -3, 4); var c = SIMD.int32x4.shiftRightLogical(a, bits); @@ -19,6 +17,16 @@ function test() { assertEq(c.w >>> 0, 4 >>> bits); } + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var f = SIMD.int32x4.shiftRightLogical(d, 0); + assertEq(f.x, (INT32_MAX >>> 0) | 0); + assertEq(f.y, (INT32_MIN >>> 0) | 0); + assertEq(f.z, (INT32_MAX >>> 0) | 0); + assertEq(f.w, (INT32_MIN >>> 0) | 0); + if (typeof reportCompare === "function") reportCompare(true, true); } diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4with.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4with.js index e6ce556afb5a..ccfa9babd9ab 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4with.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4with.js @@ -8,17 +8,17 @@ var summary = 'int32x4 with'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. + var INT32_MAX = Math.pow(2, 31) - 1; var a = int32x4(1, 2, 3, 4); var x = SIMD.int32x4.withX(a, 5); var y = SIMD.int32x4.withY(a, 5); var z = SIMD.int32x4.withZ(a, 5); - var w = SIMD.int32x4.withW(a, 5); + var w = SIMD.int32x4.withW(a, INT32_MAX + 1); assertEq(x.x, 5); assertEq(y.y, 5); assertEq(z.z, 5); - assertEq(w.w, 5); + assertEq(w.w, (INT32_MAX + 1) | 0); if (typeof reportCompare === "function") reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4withflag.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4withflag.js index 64cb0f46ec2f..7263dc5f7514 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4withflag.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4withflag.js @@ -8,8 +8,6 @@ var summary = 'int32x4 with'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var x = SIMD.int32x4.withFlagX(a, true); var y = SIMD.int32x4.withFlagY(a, false); diff --git a/js/src/tests/ecma_6/TypedObject/simd/int32x4xor.js b/js/src/tests/ecma_6/TypedObject/simd/int32x4xor.js index 027b8c1b262c..ddaa418f9b09 100644 --- a/js/src/tests/ecma_6/TypedObject/simd/int32x4xor.js +++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4xor.js @@ -8,8 +8,6 @@ var summary = 'int32x4 xor'; function test() { print(BUGNUMBER + ": " + summary); - // FIXME -- Bug 948379: Amend to check for correctness of border cases. - var a = int32x4(1, 2, 3, 4); var b = int32x4(10, 20, 30, 40); var c = SIMD.int32x4.xor(a, b); @@ -18,6 +16,17 @@ function test() { assertEq(c.z, 29); assertEq(c.w, 44); + var INT32_MAX = Math.pow(2, 31) - 1; + var INT32_MIN = -Math.pow(2, 31); + + var d = int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN); + var e = int32x4(INT32_MIN, INT32_MAX, INT32_MAX, INT32_MIN); + var f = SIMD.int32x4.xor(d, e); + assertEq(f.x, (INT32_MAX ^ INT32_MIN) | 0); + assertEq(f.y, (INT32_MIN ^ INT32_MAX) | 0); + assertEq(f.z, (INT32_MAX ^ INT32_MAX) | 0); + assertEq(f.w, (INT32_MIN ^ INT32_MIN) | 0); + if (typeof reportCompare === "function") reportCompare(true, true); } From 8cfe97f2c24a83bec96f782403702b43b9159aa7 Mon Sep 17 00:00:00 2001 From: "Nils Ohlmeier [:drno]" Date: Mon, 8 Sep 2014 04:53:00 -0400 Subject: [PATCH 29/69] Bug 1060103 - Add trickle ICE support for steeplechase. r=ted --- dom/media/tests/mochitest/pc.js | 106 ++++++++++++++++++++++++- dom/media/tests/mochitest/templates.js | 71 +++++++++++------ 2 files changed, 150 insertions(+), 27 deletions(-) diff --git a/dom/media/tests/mochitest/pc.js b/dom/media/tests/mochitest/pc.js index e6e85f710be6..2aab6e466127 100644 --- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -547,6 +547,8 @@ function PeerConnectionTest(options) { else this.pcRemote = null; + this.steeplechase = this.pcLocal === null || this.pcRemote === null; + // Create command chain instance and assign default commands this.chain = new CommandChain(this, options.commands); if (!options.is_local) { @@ -863,10 +865,109 @@ PCT_iceCandidateHandler(caller, candidate) { target.storeOrAddIceCandidate(candidate); } else { info("sending ice candidate to signaling server"); - send_message({"ice_candidate": candidate}); + send_message({"type": "ice_candidate", "ice_candidate": candidate}); } }; +/** + * Installs a polling function for the socket.io client to read + * all messages from the chat room into a message queue. + */ +PeerConnectionTest.prototype.setupSignalingClient = function +PCT_setupSignalingClient() { + var self = this; + + self.signalingMessageQueue = []; + self.signalingCallbacks = {}; + self.signalingLoopRun = true; + + function queueMessage(message) { + info("Received signaling message: " + JSON.stringify(message)); + var fired = false; + Object.keys(self.signalingCallbacks).forEach(function(name) { + if (name === message.type) { + info("Invoking callback for message type: " + name); + self.signalingCallbacks[name](message); + fired = true; + } + }); + if (!fired) { + self.signalingMessageQueue.push(message); + info("signalingMessageQueue.length: " + self.signalingMessageQueue.length); + } + if (self.signalingLoopRun) { + wait_for_message().then(queueMessage); + } else { + info("Exiting signaling message event loop"); + } + } + + wait_for_message().then(queueMessage); +} + +/** + * Sets a flag to stop reading further messages from the chat room. + */ +PeerConnectionTest.prototype.signalingMessagesFinished = function +PCT_signalingMessagesFinished() { + this.signalingLoopRun = false; +} + +/** + * Callback to stop reading message from chat room once trickle ICE + * on the far end is over. + * + * @param {string} caller + * The lable of the caller of the function + */ +PeerConnectionTest.prototype.signalEndOfTrickleIce = function +PCT_signalEndOfTrickleIce(caller) { + if (this.steeplechase) { + send_message({"type": "end_of_trickle_ice"}); + } +}; + +/** + * Register a callback function to deliver messages from the chat room + * directly instead of storing them in the message queue. + * + * @param {string} messageType + * For which message types should the callback get invoked. + * + * @param {function} onMessage + * The function which gets invoked if a message of the messageType + * has been received from the chat room. + */ +PeerConnectionTest.prototype.registerSignalingCallback = function +PCT_registerSignalingCallback(messageType, onMessage) { + this.signalingCallbacks[messageType] = onMessage; +} + +/** + * Searches the message queue for the first message of a given type + * and invokes the given callback function, or registers the callback + * function for future messages if the queue contains no such message. + * + * @param {string} messageType + * The type of message to search and register for. + * + * @param {function} onMessage + * The callback function which gets invoked with the messages + * of the given mesage type. + */ +PeerConnectionTest.prototype.getSignalingMessage = function +PCT_getSignalingMessage(messageType, onMessage) { + for(var i=0; i < this.signalingMessageQueue.length; i++) { + if (messageType === this.signalingMessageQueue[i].type) { + //FIXME + info("invoking callback on message " + i + " from message queue, for message type:" + messageType); + onMessage(this.signalingMessageQueue.splice(i, 1)[0]); + return; + } + } + this.registerSignalingCallback(messageType, onMessage); +} + /** * This class handles tests for data channels. * @@ -1991,6 +2092,7 @@ PeerConnectionWrapper.prototype = { if (!anEvent.candidate) { info(self.label + ": received end of trickle ICE event"); self.endOfTrickleIce = true; + test.signalEndOfTrickleIce(self.label); } else { if (self.endOfTrickleIce) { ok(false, "received ICE candidate after end of trickle"); @@ -2005,8 +2107,6 @@ PeerConnectionWrapper.prototype = { } } - //FIXME: in the steeplecase scenario we need to setup a permanent listener - // for ice candidates from the signaling server here self._pc.onicecandidate = iceCandidateCallback; }, diff --git a/dom/media/tests/mochitest/templates.js b/dom/media/tests/mochitest/templates.js index 3ddc97fe21b2..be034f445bd1 100644 --- a/dom/media/tests/mochitest/templates.js +++ b/dom/media/tests/mochitest/templates.js @@ -24,24 +24,27 @@ function dumpSdp(test) { dump("ERROR: SDP answer: " + test._remote_answer.sdp.replace(/[\r]/g, '')); } - if (typeof test.pcLocal.iceConnectionLog !== 'undefined') { + if ((test.pcLocal) && (typeof test.pcLocal.iceConnectionLog !== 'undefined')) { dump("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog + "\n"); } - if (typeof test.pcRemote.iceConnectionLog !== 'undefined') { + if ((test.pcRemote) && (typeof test.pcRemote.iceConnectionLog !== 'undefined')) { dump("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog + "\n"); } - if ((typeof test.pcLocal.setRemoteDescDate !== 'undefined') && + if ((test.pcLocal) && (test.pcRemote) && + (typeof test.pcLocal.setRemoteDescDate !== 'undefined') && (typeof test.pcRemote.setLocalDescDate !== 'undefined')) { var delta = deltaSeconds(test.pcLocal.setRemoteDescDate, test.pcRemote.setLocalDescDate); dump("Delay between pcLocal.setRemote <-> pcRemote.setLocal: " + delta + "\n"); } - if ((typeof test.pcLocal.setRemoteDescDate !== 'undefined') && + if ((test.pcLocal) && (test.pcRemote) && + (typeof test.pcLocal.setRemoteDescDate !== 'undefined') && (typeof test.pcLocal.setRemoteDescStableEventDate !== 'undefined')) { var delta = deltaSeconds(test.pcLocal.setRemoteDescDate, test.pcLocal.setRemoteDescStableEventDate); dump("Delay between pcLocal.setRemote <-> pcLocal.signalingStateStable: " + delta + "\n"); } - if ((typeof test.pcRemote.setLocalDescDate !== 'undefined') && + if ((test.pcLocal) && (test.pcRemote) && + (typeof test.pcRemote.setLocalDescDate !== 'undefined') && (typeof test.pcRemote.setLocalDescStableEventDate !== 'undefined')) { var delta = deltaSeconds(test.pcRemote.setLocalDescDate, test.pcRemote.setLocalDescStableEventDate); dump("Delay between pcRemote.setLocal <-> pcRemote.signalingStateStable: " + delta + "\n"); @@ -49,6 +52,22 @@ function dumpSdp(test) { } var commandsPeerConnection = [ + [ + 'PC_SETUP_SIGNALING_CLIENT', + function (test) { + if (test.steeplechase) { + test.setupSignalingClient(); + test.registerSignalingCallback("ice_candidate", function (message) { + var pc = test.pcRemote ? test.pcRemote : test.pcLocal; + pc.storeOrAddIceCandidate(new mozRTCIceCandidate(message.ice_candidate)); + }); + test.registerSignalingCallback("end_of_trickle_ice", function (message) { + test.signalingMessagesFinished(); + }); + } + test.next(); + } + ], [ 'PC_LOCAL_SETUP_ICE_LOGGER', function (test) { @@ -145,11 +164,12 @@ var commandsPeerConnection = [ test.createOffer(test.pcLocal, function (offer) { is(test.pcLocal.signalingState, STABLE, "Local create offer does not change signaling state"); - if (!test.pcRemote) { - send_message({"offer": test.originalOffer, + if (test.steeplechase) { + send_message({"type": "offer", + "offer": test.originalOffer, "offer_constraints": test.pcLocal.constraints, "offer_options": test.pcLocal.offerOptions}); - test._local_offer = test.pcLocal._last_offer; + test._local_offer = test.originalOffer; test._offer_constraints = test.pcLocal.constraints; test._offer_options = test.pcLocal.offerOptions; } @@ -170,13 +190,13 @@ var commandsPeerConnection = [ [ 'PC_REMOTE_GET_OFFER', function (test) { - if (test.pcLocal) { + if (!test.steeplechase) { test._local_offer = test.originalOffer; test._offer_constraints = test.pcLocal.constraints; test._offer_options = test.pcLocal.offerOptions; test.next(); } else { - wait_for_message().then(function(message) { + test.getSignalingMessage("offer", function (message) { ok("offer" in message, "Got an offer message"); test._local_offer = new mozRTCSessionDescription(message.offer); test._offer_constraints = message.offer_constraints; @@ -224,8 +244,9 @@ var commandsPeerConnection = [ test.createAnswer(test.pcRemote, function (answer) { is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER, "Remote createAnswer does not change signaling state"); - if (!test.pcLocal) { - send_message({"answer": test.originalAnswer, + if (test.steeplechase) { + send_message({"type": "answer", + "answer": test.originalAnswer, "answer_constraints": test.pcRemote.constraints}); test._remote_answer = test.pcRemote._last_answer; test._answer_constraints = test.pcRemote.constraints; @@ -261,7 +282,7 @@ var commandsPeerConnection = [ return resultArray; } - const offerTriples = _sdpCandidatesIntoArray(test.originalOffer.sdp); + const offerTriples = _sdpCandidatesIntoArray(test._local_offer.sdp); info("Offer ICE host candidates: " + JSON.stringify(offerTriples)); const answerTriples = _sdpCandidatesIntoArray(test.originalAnswer.sdp); @@ -269,7 +290,7 @@ var commandsPeerConnection = [ for (var i=0; i< offerTriples.length; i++) { if (answerTriples.indexOf(offerTriples[i]) !== -1) { - dump("SDP offer: " + test.originalOffer.sdp.replace(/[\r]/g, '') + "\n"); + dump("SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, '') + "\n"); dump("SDP answer: " + test.originalAnswer.sdp.replace(/[\r]/g, '') + "\n"); ok(false, "This IP:Port " + offerTriples[i] + " appears in SDP offer and answer!"); } @@ -293,12 +314,12 @@ var commandsPeerConnection = [ [ 'PC_LOCAL_GET_ANSWER', function (test) { - if (test.pcRemote) { + if (!test.steeplechase) { test._remote_answer = test.originalAnswer; test._answer_constraints = test.pcRemote.constraints; test.next(); } else { - wait_for_message().then(function(message) { + test.getSignalingMessage("answer", function (message) { ok("answer" in message, "Got an answer message"); test._remote_answer = new mozRTCSessionDescription(message.answer); test._answer_constraints = message.answer_constraints; @@ -762,8 +783,9 @@ var commandsDataChannel = [ "Local create offer does not change signaling state"); ok(offer.sdp.contains("m=application"), "m=application is contained in the SDP"); - if (!test.pcRemote) { - send_message({"offer": test.originalOffer, + if (test.steeplechase) { + send_message({"type": "offer", + "offer": test.originalOffer, "offer_constraints": test.pcLocal.constraints, "offer_options": test.pcLocal.offerOptions}); test._local_offer = test.pcLocal._last_offer; @@ -788,13 +810,13 @@ var commandsDataChannel = [ [ 'PC_REMOTE_GET_OFFER', function (test) { - if (test.pcLocal) { + if (!test.steeplechase) { test._local_offer = test.originalOffer; test._offer_constraints = test.pcLocal.constraints; test._offer_options = test.pcLocal.offerOptions; test.next(); } else { - wait_for_message().then(function(message) { + test.getSignalingMessage("offer", function (message) { ok("offer" in message, "Got an offer message"); test._local_offer = new mozRTCSessionDescription(message.offer); test._offer_constraints = message.offer_constraints; @@ -845,8 +867,9 @@ var commandsDataChannel = [ "Remote createAnswer does not change signaling state"); ok(answer.sdp.contains("m=application"), "m=application is contained in the SDP"); - if (!test.pcLocal) { - send_message({"answer": test.originalAnswer, + if (test.steeplechase) { + send_message({"type":"answer", + "answer": test.originalAnswer, "answer_constraints": test.pcRemote.constraints}); test._remote_answer = test.pcRemote._last_answer; test._answer_constraints = test.pcRemote.constraints; @@ -892,12 +915,12 @@ var commandsDataChannel = [ [ 'PC_LOCAL_GET_ANSWER', function (test) { - if (test.pcRemote) { + if (!test.steeplechase) { test._remote_answer = test.originalAnswer; test._answer_constraints = test.pcRemote.constraints; test.next(); } else { - wait_for_message().then(function(message) { + test.getSignalingMessage("answer", function (message) { ok("answer" in message, "Got an answer message"); test._remote_answer = new mozRTCSessionDescription(message.answer); test._answer_constraints = message.answer_constraints; From 180eb59b7f5a735fa7c97d77f73e1247c5f6139c Mon Sep 17 00:00:00 2001 From: "Roberto A. Vitillo" Date: Mon, 8 Sep 2014 05:06:00 -0400 Subject: [PATCH 30/69] Bug 1061191 - Add alerting e-mails to some histogram definitions. r=vladan --- toolkit/components/telemetry/Histograms.json | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 0165e286efa5..b41f3bc1b50a 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -508,6 +508,7 @@ "cpp_guard": "XP_WIN" }, "EARLY_GLUESTARTUP_READ_OPS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "linear", "high": "100", @@ -516,6 +517,7 @@ "cpp_guard": "XP_WIN" }, "EARLY_GLUESTARTUP_READ_TRANSFER": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "50 * 1024", @@ -525,6 +527,7 @@ "cpp_guard": "XP_WIN" }, "GLUESTARTUP_READ_OPS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "linear", "high": "100", @@ -533,6 +536,7 @@ "cpp_guard": "XP_WIN" }, "GLUESTARTUP_READ_TRANSFER": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "50 * 1024", @@ -542,6 +546,7 @@ "cpp_guard": "XP_WIN" }, "EARLY_GLUESTARTUP_HARD_FAULTS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "linear", "high": "100", @@ -550,6 +555,7 @@ "cpp_guard": "XP_UNIX" }, "GLUESTARTUP_HARD_FAULTS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "500", @@ -716,6 +722,7 @@ "description": "Startup cache age (hours)" }, "STARTUP_CACHE_INVALID": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "flag", "description": "Was the disk startup cache file detected as invalid" @@ -843,6 +850,7 @@ "description": "WebGL canvas used" }, "TOTAL_CONTENT_PAGE_LOAD_TIME": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "low": 100, @@ -2374,6 +2382,7 @@ "description": "How many speculative connections are made needlessly" }, "FIND_PLUGINS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "3000", @@ -2731,6 +2740,7 @@ "description": "SQLite write (bytes)" }, "MOZ_STORAGE_ASYNC_REQUESTS_MS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "32768", @@ -2739,6 +2749,7 @@ "description": "mozStorage async requests completion (ms)" }, "MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "boolean", "description": "mozStorage async requests success" @@ -2963,6 +2974,7 @@ "description": "PLACES: Number of tags" }, "PLACES_FOLDERS_COUNT": { + "alert_emails": ["places-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "200", @@ -3146,6 +3158,7 @@ "description": "PLACES: Database filesize (MB)" }, "PLACES_DATABASE_JOURNALSIZE_MB": { + "alert_emails": ["places-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "50", @@ -3250,6 +3263,7 @@ "description": "PLACES: Number of bookmarks annotations" }, "PLACES_ANNOS_BOOKMARKS_SIZE_KB": { + "alert_emails": ["places-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "low": 10, @@ -3268,6 +3282,7 @@ "description": "PLACES: Number of pages annotations" }, "PLACES_ANNOS_PAGES_SIZE_KB": { + "alert_emails": ["places-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "low": 10, @@ -3277,6 +3292,7 @@ "description": "PLACES: Size of pages annotations (KB)" }, "PLACES_FRECENCY_CALC_TIME_MS": { + "alert_emails": ["places-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "100", @@ -3434,6 +3450,7 @@ "description": "Firefox: Time taken to kick off image compression of the canvas that will be used during swiping through history (ms)." }, "FX_TAB_ANIM_OPEN_MS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "3000", @@ -3441,6 +3458,7 @@ "description": "Firefox: Time taken by the tab opening animation in milliseconds" }, "FX_TAB_ANIM_CLOSE_MS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "3000", @@ -3479,6 +3497,7 @@ "description": "Average paint duration during any tab open/close animation (excluding tabstrip scroll)" }, "FX_TAB_SWITCH_UPDATE_MS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "1000", @@ -3559,6 +3578,7 @@ "description": "THUMBNAILS: Thumbnail found" }, "EVENTLOOP_UI_LAG_EXP_MS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "low": 50, @@ -3632,6 +3652,7 @@ "description": "Session restore: Duration of the longest uninterruptible operation while collecting data in the content process (ms)" }, "FX_SESSION_RESTORE_SERIALIZE_DATA_MS": { + "alert_emails": ["session-restore-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "1000", @@ -4198,6 +4219,7 @@ "description": "DOM storage: size of values stored in sessionStorage" }, "RANGE_CHECKSUM_ERRORS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "3000", @@ -4206,6 +4228,7 @@ "description": "Number of histograms with range checksum errors" }, "BUCKET_ORDER_ERRORS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "3000", @@ -4214,6 +4237,7 @@ "description": "Number of histograms with bucket order errors" }, "TOTAL_COUNT_HIGH_ERRORS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "3000", @@ -4222,6 +4246,7 @@ "description": "Number of histograms with total count high errors" }, "TOTAL_COUNT_LOW_ERRORS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "exponential", "high": "3000", @@ -4472,6 +4497,7 @@ "description": "Whether enablePrivilege has ever been called during the current session" }, "READ_SAVED_PING_SUCCESS": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", "kind": "boolean", "description": "Successfully reading a saved ping file" From 4f986852bdc4395a8bd08072ff1322df5b23b230 Mon Sep 17 00:00:00 2001 From: Arnaud Sourioux Date: Tue, 9 Sep 2014 09:39:00 -0400 Subject: [PATCH 31/69] Bug 1063034 - Changes RasterImage::mAnim from raw pointer to UniquePtr. r=jrmuizel --- image/src/RasterImage.cpp | 5 +---- image/src/RasterImage.h | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 1249518faa3e..e5a1d38688dd 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -390,7 +390,6 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker, ImageResource(aURI), // invoke superclass's constructor mSize(0,0), mFrameDecodeFlags(DECODE_FLAGS_DEFAULT), - mAnim(nullptr), mLockCount(0), mDecodeCount(0), mRequestedSampleSize(0), @@ -463,7 +462,6 @@ RasterImage::~RasterImage() } } - delete mAnim; mAnim = nullptr; // Total statistics @@ -1123,7 +1121,7 @@ RasterImage::EnsureAnimExists() if (!mAnim) { // Create the animation context - mAnim = new FrameAnimator(mFrameBlender, mAnimationMode); + mAnim = MakeUnique(mFrameBlender, mAnimationMode); // We don't support discarding animated images (See bug 414259). // Lock the image and throw away the key. @@ -1646,7 +1644,6 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount) StopAnimation(); mAnimationFinished = false; if (mAnim) { - delete mAnim; mAnim = nullptr; } // If there's only one frame, this could cause flickering diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index 6f4d1e4ed194..e87c56f2a120 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -35,6 +35,7 @@ #include "mozilla/TimeStamp.h" #include "mozilla/StaticPtr.h" #include "mozilla/WeakPtr.h" +#include "mozilla/UniquePtr.h" #ifdef DEBUG #include "imgIContainerDebug.h" #endif @@ -644,7 +645,7 @@ private: // data // IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure // that the frames actually exist (they may have been discarded to save memory, or // we maybe decoding on draw). - FrameAnimator* mAnim; + UniquePtr mAnim; // Discard members uint32_t mLockCount; From 8ba31890b08ef46068d5899cc89843d4a666808e Mon Sep 17 00:00:00 2001 From: Ahmed Kachkach Date: Tue, 9 Sep 2014 08:01:00 -0400 Subject: [PATCH 32/69] Bug 1063099 - Include IPC tests filenames in logs. r=ahal --- content/media/webspeech/synth/ipc/test/file_ipc.html | 9 ++++++--- dom/devicestorage/ipc/test_ipc.html | 9 ++++++--- dom/indexedDB/ipc/test_ipc.html | 9 ++++++--- dom/media/tests/ipc/test_ipc.html | 9 ++++++--- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/content/media/webspeech/synth/ipc/test/file_ipc.html b/content/media/webspeech/synth/ipc/test/file_ipc.html index af2f50b291b3..b18c8d0ac7d0 100644 --- a/content/media/webspeech/synth/ipc/test/file_ipc.html +++ b/content/media/webspeech/synth/ipc/test/file_ipc.html @@ -52,10 +52,13 @@ if (validStructuredMessage(message)) { switch (message.action) { case "test_status": - ok(message.expected === undefined, message.subtest, message.message); - break; case "test_end": - ok(message.expected === undefined, message.test, message.message); + let test_tokens = message.test.split("/"); + let test_name = test_tokens[test_tokens.length - 1]; + if (message.subtest) { + test_name += " | " + message.subtest; + } + ok(message.expected === undefined, test_name, message.message); break; case "log": info(message.message); diff --git a/dom/devicestorage/ipc/test_ipc.html b/dom/devicestorage/ipc/test_ipc.html index 157638650ed4..88c4398d8459 100644 --- a/dom/devicestorage/ipc/test_ipc.html +++ b/dom/devicestorage/ipc/test_ipc.html @@ -51,10 +51,13 @@ if (validStructuredMessage(message)) { switch (message.action) { case "test_status": - ok(message.expected === undefined, message.subtest, message.message); - break; case "test_end": - ok(message.expected === undefined, message.test, message.message); + let test_tokens = message.test.split("/"); + let test_name = test_tokens[test_tokens.length - 1]; + if (message.subtest) { + test_name += " | " + message.subtest; + } + ok(message.expected === undefined, test_name, message.message); break; case "log": info(message.message); diff --git a/dom/indexedDB/ipc/test_ipc.html b/dom/indexedDB/ipc/test_ipc.html index 52ff6ee707a6..085ebef54354 100644 --- a/dom/indexedDB/ipc/test_ipc.html +++ b/dom/indexedDB/ipc/test_ipc.html @@ -67,10 +67,13 @@ if (validStructuredMessage(message)) { switch (message.action) { case "test_status": - ok(message.expected === undefined, message.subtest, message.message); - break; case "test_end": - ok(message.expected === undefined, message.test, message.message); + let test_tokens = message.test.split("/"); + let test_name = test_tokens[test_tokens.length - 1]; + if (message.subtest) { + test_name += " | " + message.subtest; + } + ok(message.expected === undefined, test_name, message.message); break; case "log": info(message.message); diff --git a/dom/media/tests/ipc/test_ipc.html b/dom/media/tests/ipc/test_ipc.html index 7d36799fbad7..dbd1463cd70e 100644 --- a/dom/media/tests/ipc/test_ipc.html +++ b/dom/media/tests/ipc/test_ipc.html @@ -60,10 +60,13 @@ if (validStructuredMessage(message)) { switch (message.action) { case "test_status": - ok(message.expected === undefined, message.subtest, message.message); - break; case "test_end": - ok(message.expected === undefined, message.test, message.message); + let test_tokens = message.test.split("/"); + let test_name = test_tokens[test_tokens.length - 1]; + if (message.subtest) { + test_name += " | " + message.subtest; + } + ok(message.expected === undefined, test_name, message.message); break; case "log": info(message.message); From d5feb83b80a478514851e6f5496a26f7763f2eb0 Mon Sep 17 00:00:00 2001 From: Irving Reid Date: Mon, 8 Sep 2014 14:15:04 -0400 Subject: [PATCH 33/69] Bug 1064424 - Use Timestamp::ProcessCreation to calculate Cu.now(). r=bz --- js/xpconnect/src/XPCComponents.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index d21ac708f3a1..dc06ef6f8dac 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -19,7 +19,6 @@ #include "mozilla/Attributes.h" #include "mozilla/Preferences.h" #include "nsJSEnvironment.h" -#include "mozilla/StartupTimeline.h" #include "mozilla/TimeStamp.h" #include "mozilla/XPTInterfaceInfoManager.h" #include "mozilla/dom/DOMException.h" @@ -3557,7 +3556,8 @@ nsXPCComponents_Utils::SetAddonInterposition(const nsACString &addonIdStr, NS_IMETHODIMP nsXPCComponents_Utils::Now(double *aRetval) { - TimeStamp start = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION); + bool isInconsistent = false; + TimeStamp start = TimeStamp::ProcessCreation(isInconsistent); *aRetval = (TimeStamp::Now() - start).ToMilliseconds(); return NS_OK; } From cccba9b15685fe3fabd38f8936f2a3cc89904998 Mon Sep 17 00:00:00 2001 From: Manu Jain Date: Mon, 8 Sep 2014 09:04:00 -0400 Subject: [PATCH 34/69] Bug 1063559 - Convert TelemetryStopwatch.jsm to use Components.utils.now(). r=irving --- toolkit/components/telemetry/TelemetryStopwatch.jsm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryStopwatch.jsm b/toolkit/components/telemetry/TelemetryStopwatch.jsm index ced24a056434..989485da9a4e 100644 --- a/toolkit/components/telemetry/TelemetryStopwatch.jsm +++ b/toolkit/components/telemetry/TelemetryStopwatch.jsm @@ -54,7 +54,7 @@ this.TelemetryStopwatch = { return false; } - timers[aHistogram] = Date.now(); + timers[aHistogram] = Components.utils.now(); return true; }, @@ -117,7 +117,8 @@ this.TelemetryStopwatch = { delete timers[aHistogram]; if (start) { - let delta = Date.now() - start; + let delta = Components.utils.now() - start; + delta = Math.round(delta); let histogram = Telemetry.getHistogramById(aHistogram); histogram.add(delta); return true; From be76304697cc60f8f42e95bf4d3d8623c8a00337 Mon Sep 17 00:00:00 2001 From: Jan-Ivar Bruaroey Date: Tue, 9 Sep 2014 01:12:24 -0400 Subject: [PATCH 35/69] Bug 1063808 - Support old constraint-like RTCOfferOptions for a bit. r=smaug, r=abr --- dom/media/PeerConnection.js | 41 ++++++++++++++++++- dom/media/tests/mochitest/pc.js | 26 +++++++++++- ...rConnection_offerRequiresReceiveAudio.html | 5 ++- ...rConnection_offerRequiresReceiveVideo.html | 4 +- dom/webidl/RTCPeerConnection.webidl | 15 ++++++- 5 files changed, 84 insertions(+), 7 deletions(-) diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index 4a3f67b2cf33..82d217642850 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -555,7 +555,46 @@ RTCPeerConnection.prototype = { }, createOffer: function(onSuccess, onError, options) { - options = options || {}; + + // TODO: Remove old constraint-like RTCOptions support soon (Bug 1064223). + function convertLegacyOptions(o) { + if (!(o.mandatory || o.optional) || + Object.keys(o).length != ((o.mandatory && o.optional)? 2 : 1)) { + return false; + } + let old = o.mandatory || {}; + if (o.mandatory) { + delete o.mandatory; + } + if (o.optional) { + o.optional.forEach(one => { + // The old spec had optional as an array of objects w/1 attribute each. + // Assumes our JS-webidl bindings only populate passed-in properties. + let key = Object.keys(one)[0]; + if (key && old[key] === undefined) { + old[key] = one[key]; + } + }); + delete o.optional; + } + o.offerToReceiveAudio = old.OfferToReceiveAudio; + o.offerToReceiveVideo = old.OfferToReceiveVideo; + o.mozDontOfferDataChannel = old.MozDontOfferDataChannel; + o.mozBundleOnly = old.MozBundleOnly; + Object.keys(o).forEach(k => { + if (o[k] === undefined) { + delete o[k]; + } + }); + return true; + } + + if (options && convertLegacyOptions(options)) { + this.logWarning( + "Mandatory/optional in createOffer options is deprecated! Use " + + JSON.stringify(options) + " instead (note the case difference)!", + null, 0); + } this._queueOrRun({ func: this._createOffer, args: [onSuccess, onError, options], diff --git a/dom/media/tests/mochitest/pc.js b/dom/media/tests/mochitest/pc.js index 2aab6e466127..1e4e4a6b8920 100644 --- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -2141,7 +2141,18 @@ PeerConnectionWrapper.prototype = { if (!options) { return 0; } - if (options.offerToReceiveAudio) { + + var offerToReceiveAudio = options.offerToReceiveAudio; + + // TODO: Remove tests of old constraint-like RTCOptions soon (Bug 1064223). + if (options.mandatory && options.mandatory.OfferToReceiveAudio !== undefined) { + offerToReceiveAudio = options.mandatory.OfferToReceiveAudio; + } else if (options.optional && options.optional[0] && + options.optional[0].OfferToReceiveAudio !== undefined) { + offerToReceiveAudio = options.optional[0].OfferToReceiveAudio; + } + + if (offerToReceiveAudio) { return 1; } else { return 0; @@ -2179,7 +2190,18 @@ PeerConnectionWrapper.prototype = { if (!options) { return 0; } - if (options.offerToReceiveVideo) { + + var offerToReceiveVideo = options.offerToReceiveVideo; + + // TODO: Remove tests of old constraint-like RTCOptions soon (Bug 1064223). + if (options.mandatory && options.mandatory.OfferToReceiveVideo !== undefined) { + offerToReceiveVideo = options.mandatory.OfferToReceiveVideo; + } else if (options.optional && options.optional[0] && + options.optional[0].OfferToReceiveVideo !== undefined) { + offerToReceiveVideo = options.optional[0].OfferToReceiveVideo; + } + + if (offerToReceiveVideo) { return 1; } else { return 0; diff --git a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html index 66a88ee05302..fe01f789cbec 100644 --- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html +++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html @@ -18,7 +18,10 @@ runNetworkTest(function() { var test = new PeerConnectionTest(); - test.setOfferOptions({ offerToReceiveAudio: true }); + + // TODO: Stop using old constraint-like RTCOptions soon (Bug 1064223). + // Watch out for case-difference when fixing: { offerToReceiveAudio: true } + test.setOfferOptions({ mandatory: { OfferToReceiveAudio: true } }); test.run(); }); diff --git a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html index b265fb04e75c..44c74ca6315e 100644 --- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html +++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html @@ -18,7 +18,9 @@ runNetworkTest(function() { var test = new PeerConnectionTest(); - test.setOfferOptions({ offerToReceiveVideo: true }); + // TODO: Stop using old constraint-like RTCOptions soon (Bug 1064223). + // Watch out for case-difference when fixing: { offerToReceiveVideo: true } + test.setOfferOptions({ optional: [{ OfferToReceiveAudio: true }] }); test.run(); }); diff --git a/dom/webidl/RTCPeerConnection.webidl b/dom/webidl/RTCPeerConnection.webidl index 3b56e6de8afb..2c3eb41a0722 100644 --- a/dom/webidl/RTCPeerConnection.webidl +++ b/dom/webidl/RTCPeerConnection.webidl @@ -55,8 +55,19 @@ dictionary RTCDataChannelInit { dictionary RTCOfferOptions { long offerToReceiveVideo; long offerToReceiveAudio; - boolean MozDontOfferDataChannel; - boolean MozBundleOnly; + boolean mozDontOfferDataChannel; + boolean mozBundleOnly; + + // TODO: Remove old constraint-like RTCOptions support soon (Bug 1064223). + DeprecatedRTCOfferOptionsSet mandatory; + sequence _optional; +}; + +dictionary DeprecatedRTCOfferOptionsSet { + boolean OfferToReceiveAudio; // Note the uppercase 'O' + boolean OfferToReceiveVideo; // Note the uppercase 'O' + boolean MozDontOfferDataChannel; // Note the uppercase 'M' + boolean MozBundleOnly; // Note the uppercase 'M' }; interface RTCDataChannel; From eeff43a02fea0a1175f2de3496f9f8e004acdb37 Mon Sep 17 00:00:00 2001 From: Jan-Ivar Bruaroey Date: Tue, 9 Sep 2014 10:52:23 -0400 Subject: [PATCH 36/69] Bug 1064088 - Deprecation warnings when required callbacks are missing. r=abr --- dom/media/PeerConnection.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index 82d217642850..0499c4711654 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -637,6 +637,12 @@ RTCPeerConnection.prototype = { }, setLocalDescription: function(desc, onSuccess, onError) { + if (!onSuccess || !onError) { + this.logWarning( + "setLocalDescription called without success/failure callbacks. This is deprecated, and will be an error in the future.", + null, 0); + } + this._localType = desc.type; let type; @@ -668,6 +674,11 @@ RTCPeerConnection.prototype = { }, setRemoteDescription: function(desc, onSuccess, onError) { + if (!onSuccess || !onError) { + this.logWarning( + "setRemoteDescription called without success/failure callbacks. This is deprecated, and will be an error in the future.", + null, 0); + } this._remoteType = desc.type; let type; @@ -804,6 +815,11 @@ RTCPeerConnection.prototype = { }, addIceCandidate: function(cand, onSuccess, onError) { + if (!onSuccess || !onError) { + this.logWarning( + "addIceCandidate called without success/failure callbacks. This is deprecated, and will be an error in the future.", + null, 0); + } if (!cand.candidate && !cand.sdpMLineIndex) { throw new this._win.DOMError("", "Invalid candidate passed to addIceCandidate!"); From 697875e21099342eccc17989c1c9b17ce7508300 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 8 Sep 2014 19:27:00 -0400 Subject: [PATCH 37/69] Bug 1064481 - URLSearchParams should encode % values correcty. r=ehsan --- dom/base/URLSearchParams.cpp | 2 +- dom/base/test/mochitest.ini | 1 + dom/base/test/test_bug1064481.html | 24 ++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 dom/base/test/test_bug1064481.html diff --git a/dom/base/URLSearchParams.cpp b/dom/base/URLSearchParams.cpp index 0c88a1d1bc9f..b9c30fe3f2b5 100644 --- a/dom/base/URLSearchParams.cpp +++ b/dom/base/URLSearchParams.cpp @@ -349,7 +349,7 @@ public: (*p >= 0x61 && *p <= 0x7A)) { mValue.Append(*p); } else { - mValue.AppendPrintf("%%%X", *p); + mValue.AppendPrintf("%%%.2X", *p); } ++p; diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index f7bef0e3e278..7484f9f26946 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -25,6 +25,7 @@ skip-if = buildapp == 'mulet' [test_bug999456.html] [test_bug1022229.html] [test_bug1043106.html] +[test_bug1064481.html] [test_clearTimeoutIntervalNoArg.html] [test_consoleEmptyStack.html] [test_constructor-assignment.html] diff --git a/dom/base/test/test_bug1064481.html b/dom/base/test/test_bug1064481.html new file mode 100644 index 000000000000..e5a3cec2662b --- /dev/null +++ b/dom/base/test/test_bug1064481.html @@ -0,0 +1,24 @@ + + + + + + Test for Bug 1064481 + + + + + Mozilla Bug 1064481 + + + + From fd9b507bb982a2cb02d9c32799806ab2045ac10f Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Tue, 9 Sep 2014 13:46:47 -0400 Subject: [PATCH 38/69] Bug 1060088 - Don't ship unused addon providers in Fennec; r=Unfocused --- toolkit/modules/moz.build | 2 +- toolkit/modules/tests/xpcshell/xpcshell.ini | 2 ++ toolkit/mozapps/extensions/extensions.manifest | 2 ++ toolkit/mozapps/extensions/internal/moz.build | 9 +++++++-- toolkit/mozapps/extensions/test/browser/browser.ini | 2 ++ .../mozapps/extensions/test/xpcshell/xpcshell-shared.ini | 2 ++ 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index d2b1df87bb5e..892127637aa5 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -61,7 +61,6 @@ EXTRA_JS_MODULES += [ EXTRA_PP_JS_MODULES += [ 'CertUtils.jsm', - 'GMPInstallManager.jsm', 'ResetProfile.jsm', 'Services.jsm', 'Troubleshoot.jsm', @@ -72,6 +71,7 @@ EXTRA_PP_JS_MODULES += [ if 'Android' != CONFIG['OS_TARGET']: EXTRA_PP_JS_MODULES += [ + 'GMPInstallManager.jsm', 'LightweightThemeConsumer.jsm', ] diff --git a/toolkit/modules/tests/xpcshell/xpcshell.ini b/toolkit/modules/tests/xpcshell/xpcshell.ini index 441b6b9ffd16..7fa2b1f38bb3 100644 --- a/toolkit/modules/tests/xpcshell/xpcshell.ini +++ b/toolkit/modules/tests/xpcshell/xpcshell.ini @@ -14,6 +14,8 @@ support-files = [test_DirectoryLinksProvider.js] [test_FileUtils.js] [test_GMPInstallManager.js] +# GMPInstallManager is not shipped on Android +skip-if = os == 'android' [test_Http.js] [test_Log.js] [test_NewTabUtils.js] diff --git a/toolkit/mozapps/extensions/extensions.manifest b/toolkit/mozapps/extensions/extensions.manifest index bf83502ad91c..af0f6c5966f4 100644 --- a/toolkit/mozapps/extensions/extensions.manifest +++ b/toolkit/mozapps/extensions/extensions.manifest @@ -13,6 +13,8 @@ contract @mozilla.org/addons/web-install-listener;1 {0f38e086-89a3-40a5-8ffc-9b6 component {9df8ef2b-94da-45c9-ab9f-132eb55fddf1} amInstallTrigger.js contract @mozilla.org/addons/installtrigger;1 {9df8ef2b-94da-45c9-ab9f-132eb55fddf1} category JavaScript-global-property InstallTrigger @mozilla.org/addons/installtrigger;1 +#ifndef MOZ_WIDGET_ANDROID category addon-provider-module PluginProvider resource://gre/modules/addons/PluginProvider.jsm category addon-provider-module OpenH264Provider resource://gre/modules/addons/OpenH264Provider.jsm #endif +#endif diff --git a/toolkit/mozapps/extensions/internal/moz.build b/toolkit/mozapps/extensions/internal/moz.build index 9fbc80486bba..24fe344bf7d2 100644 --- a/toolkit/mozapps/extensions/internal/moz.build +++ b/toolkit/mozapps/extensions/internal/moz.build @@ -11,11 +11,16 @@ EXTRA_JS_MODULES.addons += [ 'AddonUpdateChecker.jsm', 'Content.js', 'LightweightThemeImageOptimizer.jsm', - 'OpenH264Provider.jsm', - 'PluginProvider.jsm', 'SpellCheckDictionaryBootstrap.js', ] +# Don't ship unused providers on Android +if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': + EXTRA_JS_MODULES.addons += [ + 'OpenH264Provider.jsm', + 'PluginProvider.jsm', + ] + EXTRA_PP_JS_MODULES.addons += [ 'XPIProvider.jsm', 'XPIProviderUtils.js', diff --git a/toolkit/mozapps/extensions/test/browser/browser.ini b/toolkit/mozapps/extensions/test/browser/browser.ini index f8c0a069a882..5ba5c4ae47b4 100644 --- a/toolkit/mozapps/extensions/test/browser/browser.ini +++ b/toolkit/mozapps/extensions/test/browser/browser.ini @@ -45,6 +45,8 @@ skip-if = e10s [browser_newaddon.js] skip-if = e10s [browser_openH264.js] +# OpenH264Provider.jsm is not shipped on Android +skip-if = os == "android" [browser_select_compatoverrides.js] [browser_select_confirm.js] [browser_select_selection.js] diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini index a26933bff6f9..c384fc7185f4 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini +++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini @@ -220,6 +220,8 @@ skip-if = os == "android" [test_plugins.js] skip-if = buildapp == "mulet" [test_pluginchange.js] +# PluginProvider.jsm is not shipped on Android +skip-if = os == "android" [test_pluginBlocklistCtp.js] # Bug 676992: test consistently fails on Android fail-if = buildapp == "mulet" || os == "android" From 43dc4cf6658a05a53b3962a62deedf3cfa79eff7 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Tue, 9 Sep 2014 13:46:47 -0400 Subject: [PATCH 39/69] Bug 1062372 - Lazily load browser.js modules; r=mfinkle --- mobile/android/chrome/content/browser.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 69cdc9624659..b1eaf3e4222d 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -13,17 +13,23 @@ let Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/AddonManager.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); -Cu.import("resource://gre/modules/JNI.jsm"); Cu.import('resource://gre/modules/Payment.jsm'); Cu.import("resource://gre/modules/NotificationDB.jsm"); Cu.import("resource://gre/modules/SpatialNavigation.jsm"); -Cu.import("resource://gre/modules/UITelemetry.jsm"); #ifdef ACCESSIBILITY Cu.import("resource://gre/modules/accessibility/AccessFu.jsm"); #endif +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "JNI", + "resource://gre/modules/JNI.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry", + "resource://gre/modules/UITelemetry.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm"); From 5cdb401e298c04e08559f843a358051b374d657e Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Tue, 9 Sep 2014 13:46:48 -0400 Subject: [PATCH 40/69] Bug 1062379 - Don't reduce Gecko thread priority for multicore devices; r=mfinkle For multicore devices, the Gecko thread should be able to run on a separate core than the UI thread, so there's no reason to lower the Gecko thread priority in consideration for the UI thread. Patch also fixes some race conditions involving sGeckoThread in ThreadUtils. --- mobile/android/base/util/ThreadUtils.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/util/ThreadUtils.java b/mobile/android/base/util/ThreadUtils.java index 3f069ad4c795..0abb3732cd83 100644 --- a/mobile/android/base/util/ThreadUtils.java +++ b/mobile/android/base/util/ThreadUtils.java @@ -36,7 +36,7 @@ public final class ThreadUtils { // this out at compile time. public static Handler sGeckoHandler; public static MessageQueue sGeckoQueue; - public static Thread sGeckoThread; + public static volatile Thread sGeckoThread; // Delayed Runnable that resets the Gecko thread priority. private static final Runnable sPriorityResetRunnable = new Runnable() { @@ -130,6 +130,10 @@ public final class ThreadUtils { } public static void assertNotOnGeckoThread() { + if (sGeckoThread == null) { + // Cannot be on Gecko thread if Gecko thread is not live yet. + return; + } assertNotOnThread(sGeckoThread, AssertBehavior.THROW); } @@ -204,7 +208,13 @@ public final class ThreadUtils { * @param timeout Timeout in ms after which the priority will be reset */ public static void reduceGeckoPriority(long timeout) { - if (!sIsGeckoPriorityReduced) { + if (Runtime.getRuntime().availableProcessors() > 1) { + // Don't reduce priority for multicore devices. We use availableProcessors() + // for its fast performance. It may give false negatives (i.e. multicore + // detected as single-core), but we can tolerate this behavior. + return; + } + if (!sIsGeckoPriorityReduced && sGeckoThread != null) { sIsGeckoPriorityReduced = true; sGeckoThread.setPriority(Thread.MIN_PRIORITY); getUiHandler().postDelayed(sPriorityResetRunnable, timeout); From 74632e9bd371c18594f237d01c3f0ce0334c4449 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 2 Sep 2014 13:18:39 -0400 Subject: [PATCH 41/69] Bug 1062874 - part 1 - remove preprocessor-requiring bits from head_libmar.js.in; r=bbondy --- modules/libmar/tests/unit/head_libmar.js.in | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/modules/libmar/tests/unit/head_libmar.js.in b/modules/libmar/tests/unit/head_libmar.js.in index 111304c06f6a..1eec1b9a568a 100644 --- a/modules/libmar/tests/unit/head_libmar.js.in +++ b/modules/libmar/tests/unit/head_libmar.js.in @@ -1,15 +1,12 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -const BIN_SUFFIX = "@BIN_SUFFIX@"; const Cc = Components.classes; const Ci = Components.interfaces; -#ifdef XP_WIN - let refMARPrefix = "win_"; -#else - let refMARPrefix = ""; -#endif +const isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes); +const refMARPrefix = (isWindows ? "win_" : ""); +const BIN_SUFFIX = (isWindows ? ".exe" : ""); let tempDir = do_get_tempdir(); From 4a5959372c46a0fa76ec7b4ce0e876443df77473 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 2 Sep 2014 13:46:10 -0400 Subject: [PATCH 42/69] Bug 1062874 - part 2 - don't preprocess head_libmar.js; r=ted --- modules/libmar/tests/Makefile.in | 3 --- .../libmar/tests/unit/{head_libmar.js.in => head_libmar.js} | 0 modules/libmar/tests/unit/xpcshell.ini | 1 - 3 files changed, 4 deletions(-) rename modules/libmar/tests/unit/{head_libmar.js.in => head_libmar.js} (100%) diff --git a/modules/libmar/tests/Makefile.in b/modules/libmar/tests/Makefile.in index 7d24d6cdb7fc..0d11b3b53524 100644 --- a/modules/libmar/tests/Makefile.in +++ b/modules/libmar/tests/Makefile.in @@ -6,9 +6,6 @@ TESTROOT = $(abspath $(DEPTH))/_tests/xpcshell/$(relativesrcdir) include $(topsrcdir)/config/rules.mk -libs:: unit/head_libmar.js.in - $(call py_action,preprocessor,-Fsubstitution $(DEFINES) $(ACDEFINES) $^ -o $(TESTROOT)/unit/head_libmar.js) - ifneq ($(OS_TARGET),Android) ifndef MOZ_PROFILE_GENERATE libs:: diff --git a/modules/libmar/tests/unit/head_libmar.js.in b/modules/libmar/tests/unit/head_libmar.js similarity index 100% rename from modules/libmar/tests/unit/head_libmar.js.in rename to modules/libmar/tests/unit/head_libmar.js diff --git a/modules/libmar/tests/unit/xpcshell.ini b/modules/libmar/tests/unit/xpcshell.ini index 6b3d001f1ee1..f8f9a700cd76 100644 --- a/modules/libmar/tests/unit/xpcshell.ini +++ b/modules/libmar/tests/unit/xpcshell.ini @@ -1,7 +1,6 @@ [DEFAULT] head = head_libmar.js tail = -generated-files = head_libmar.js support-files = data/** [test_create.js] From 28f0bef008442e0e7c232818d87fe6a3553ccafb Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 9 Sep 2014 14:00:22 -0400 Subject: [PATCH 43/69] Bug 1065025 - Make the generated WebIDL ToJSON method const; r=baku --- dom/bindings/BindingDeclarations.h | 2 +- dom/bindings/BindingUtils.cpp | 2 +- dom/bindings/Codegen.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h index d40649b6e0ce..ad87bf9c887b 100644 --- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -38,7 +38,7 @@ protected: bool StringifyToJSON(JSContext* aCx, JS::MutableHandle aValue, - nsAString& aJSON); + nsAString& aJSON) const; private: // aString is expected to actually be an nsAString*. Should only be // called from StringifyToJSON. diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 650bd7a25569..260235997954 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1635,7 +1635,7 @@ DictionaryBase::ParseJSON(JSContext* aCx, bool DictionaryBase::StringifyToJSON(JSContext* aCx, JS::MutableHandle aValue, - nsAString& aJSON) + nsAString& aJSON) const { return JS_Stringify(aCx, aValue, JS::NullPtr(), JS::NullHandleValue, AppendJSONToString, &aJSON); diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 9ce9474241d4..d28b33f0067e 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -11240,7 +11240,7 @@ class CGDictionary(CGThing): JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope()); // Usage approved by bholley JS::Rooted obj(cx); return ToObjectInternal(cx, &obj) && StringifyToJSON(cx, &obj, aJSON); - """)) + """), const=True) def toObjectInternalMethod(self): body = "" From d64852855ea822647aca95595a71a33b837a5557 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 31 Aug 2014 20:36:26 -0700 Subject: [PATCH 44/69] Bug 1061024 (part 1) - Don't run DMD when about:memory's "Measure" button is pressed. r=jld. --HG-- extra : rebase_source : ddf0236352c64eb9839dfd0a219451a568aba10e --- dom/ipc/ContentParent.cpp | 20 +++++++++++--------- xpcom/base/nsMemoryReporterManager.cpp | 12 +++++++----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index a26f41107b51..8859957daa3d 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2641,16 +2641,18 @@ ContentParent::Observe(nsISupports* aSubject, MOZ_ASSERT(cmsg[identOffset - 1] == '='); FileDescriptor dmdFileDesc; #ifdef MOZ_DMD - FILE *dmdFile; nsAutoString dmdIdent(Substring(msg, identOffset)); - nsresult rv = nsMemoryInfoDumper::OpenDMDFile(dmdIdent, Pid(), &dmdFile); - if (NS_WARN_IF(NS_FAILED(rv))) { - // Proceed with the memory report as if DMD were disabled. - dmdFile = nullptr; - } - if (dmdFile) { - dmdFileDesc = FILEToFileDescriptor(dmdFile); - fclose(dmdFile); + if (!dmdIdent.IsEmpty()) { + FILE *dmdFile = nullptr; + nsresult rv = nsMemoryInfoDumper::OpenDMDFile(dmdIdent, Pid(), &dmdFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + // Proceed with the memory report as if DMD were disabled. + dmdFile = nullptr; + } + if (dmdFile) { + dmdFileDesc = FILEToFileDescriptor(dmdFile); + fclose(dmdFile); + } } #endif unused << SendPMemoryReportRequestConstructor( diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 8d92b674b4f8..215b33f04184 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -1189,11 +1189,13 @@ nsMemoryReporterManager::StartGettingReports() // Get reports for this process. FILE* parentDMDFile = nullptr; #ifdef MOZ_DMD - nsresult rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(), - &parentDMDFile); - if (NS_WARN_IF(NS_FAILED(rv))) { - // Proceed with the memory report as if DMD were disabled. - parentDMDFile = nullptr; + if (!s->mDMDDumpIdent.IsEmpty()) { + nsresult rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(), + &parentDMDFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + // Proceed with the memory report as if DMD were disabled. + parentDMDFile = nullptr; + } } #endif GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData, From cb8defd2fa3f3ec490be578756fce8ea9eda9611 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 31 Aug 2014 22:35:28 -0700 Subject: [PATCH 45/69] Bug 1061024 (part 2) - Some minor DMD clean-ups. r=jld. --HG-- extra : rebase_source : f780376e8803222c92a5a92bc4155600e068ce0c --- xpcom/base/nsIMemoryInfoDumper.idl | 7 ++++--- xpcom/base/nsMemoryInfoDumper.cpp | 21 +++------------------ xpcom/base/nsMemoryInfoDumper.h | 2 -- xpcom/base/nsMemoryReporterManager.cpp | 2 +- 4 files changed, 8 insertions(+), 24 deletions(-) diff --git a/xpcom/base/nsIMemoryInfoDumper.idl b/xpcom/base/nsIMemoryInfoDumper.idl index c6b26e208262..251ed27da988 100644 --- a/xpcom/base/nsIMemoryInfoDumper.idl +++ b/xpcom/base/nsIMemoryInfoDumper.idl @@ -139,9 +139,10 @@ interface nsIMemoryInfoDumper : nsISupports * similar, such as memory-reports---1.json.gz; no existing * file will be overwritten). * - * If DMD is enabled, this method also dumps gzipped DMD output to a file in - * the tmp directory called dmd--.txt.gz (or something - * similar; again, no existing file will be overwritten). + * If DMD is enabled, this method also dumps gzipped DMD output for this + * process and its child processes to files in the tmp directory called + * dmd--.txt.gz (or something similar; again, no existing + * file will be overwritten). * * @param aIdentifier this identifier will appear in the filename of our * about:memory dump and those of our children. diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp index 59b3c98d327e..a0c7031b48db 100644 --- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -677,7 +677,7 @@ nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier, finishReport, nullptr, aAnonymize, aMinimizeMemoryUsage, - identifier); + /* DMDident = */ identifier); return rv; } @@ -741,21 +741,6 @@ nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile) NS_WARN_IF(NS_FAILED(rv)); return rv; } - -nsresult -nsMemoryInfoDumper::DumpDMD(const nsAString& aIdentifier) -{ - nsresult rv; - FILE* dmdFile; - rv = OpenDMDFile(aIdentifier, getpid(), &dmdFile); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - if (!dmdFile) { - return NS_OK; - } - return DumpDMDToFile(dmdFile); -} #endif // MOZ_DMD NS_IMETHODIMP @@ -922,11 +907,11 @@ nsMemoryInfoDumper::DumpMemoryReportsToNamedFile( } // Process reports and finish up. + nsCOMPtr mgr = + do_GetService("@mozilla.org/memory-reporter-manager;1"); nsRefPtr dumpReport = new DumpReportCallback(mrWriter); nsRefPtr finishReporting = new FinishReportingCallback(aFinishDumping, aFinishDumpingData); - nsCOMPtr mgr = - do_GetService("@mozilla.org/memory-reporter-manager;1"); return mgr->GetReports(dumpReport, nullptr, finishReporting, mrWriter, aAnonymize); } diff --git a/xpcom/base/nsMemoryInfoDumper.h b/xpcom/base/nsMemoryInfoDumper.h index b77602df5699..39ed8f62ce70 100644 --- a/xpcom/base/nsMemoryInfoDumper.h +++ b/xpcom/base/nsMemoryInfoDumper.h @@ -32,8 +32,6 @@ public: static void Initialize(); #ifdef MOZ_DMD - // Write a DMD report. - static nsresult DumpDMD(const nsAString& aIdentifier); // Open an appropriately named file for a DMD report. If DMD is // disabled, return a null FILE* instead. static nsresult OpenDMDFile(const nsAString& aIdentifier, int aPid, diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 215b33f04184..a53d2f139be4 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -1094,7 +1094,7 @@ nsMemoryReporterManager::GetReports( aFinishReporting, aFinishReportingData, aAnonymize, /* minimize = */ false, - /* DMDident = */ nsString()); + /* DMDident = */ EmptyString()); } NS_IMETHODIMP From b662348a22c04ab15c4b968e811c1a4396e4ba4e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 31 Aug 2014 22:36:01 -0700 Subject: [PATCH 46/69] Bug 1061024 (part 3) - Remove a redundant dmd::ClearReports() call. r=jld. --HG-- extra : rebase_source : 5c79c9187fbffe2b5a8c63184cc9d3f158877a8e --- xpcom/base/nsMemoryInfoDumper.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp index a0c7031b48db..bb3b4b416a21 100644 --- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -614,12 +614,6 @@ nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier, nsString identifier(aIdentifier); EnsureNonEmptyIdentifier(identifier); -#ifdef MOZ_DMD - // Clear DMD's reportedness state before running the memory reporters, to - // avoid spurious twice-reported warnings. - dmd::ClearReports(); -#endif - // Open a new file named something like // // incomplete-memory-report--.json.gz From edab2d2a41bd74d11d40ece6c1a77625b5742b0a Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 9 Sep 2014 11:40:10 -0700 Subject: [PATCH 47/69] Bug 1060090 part 1: Rename ParseBackgroundPositionValues to ParsePositionValue. r=heycam --- layout/style/nsCSSParser.cpp | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 4cb11a59b6c9..b88597649ef7 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -652,11 +652,14 @@ protected: bool ParseBackgroundPosition(); // ParseBoxPositionValues parses the CSS 2.1 background-position syntax, - // which is still used by some properties. See ParseBackgroundPositionValues + // which is still used by some properties. See ParsePositionValue // for the css3-background syntax. bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit, bool aAllowExplicitCenter = true); // deprecated - bool ParseBackgroundPositionValues(nsCSSValue& aOut, bool aAcceptsInherit); + + // ParsePositionValue parses a CSS value, which is used by + // the 'background-position' property. + bool ParsePositionValue(nsCSSValue& aOut); bool ParseBackgroundSize(); bool ParseBackgroundSizeValues(nsCSSValuePair& aOut); @@ -10197,7 +10200,7 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) if (havePositionAndSize) return false; havePositionAndSize = true; - if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) { + if (!ParsePositionValue(aState.mPosition->mValue)) { return false; } if (ExpectSymbol('/', true)) { @@ -10281,7 +10284,7 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) if (havePositionAndSize) return false; havePositionAndSize = true; - if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) { + if (!ParsePositionValue(aState.mPosition->mValue)) { return false; } if (ExpectSymbol('/', true)) { @@ -10393,7 +10396,7 @@ CSSParserImpl::ParseBackgroundPosition() // 'initial', 'inherit' and 'unset' stand alone, no list permitted. if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { nsCSSValue itemValue; - if (!ParseBackgroundPositionValues(itemValue, false)) { + if (!ParsePositionValue(itemValue)) { return false; } nsCSSValueList* item = value.SetListValue(); @@ -10402,7 +10405,7 @@ CSSParserImpl::ParseBackgroundPosition() if (!ExpectSymbol(',', true)) { break; } - if (!ParseBackgroundPositionValues(itemValue, false)) { + if (!ParsePositionValue(itemValue)) { return false; } item->mNext = new nsCSSValueList; @@ -10515,19 +10518,11 @@ bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut, return true; } -bool CSSParserImpl::ParseBackgroundPositionValues(nsCSSValue& aOut, - bool aAcceptsInherit) +// Parses a CSS value, for e.g. the 'background-position' property. +// Spec reference: http://www.w3.org/TR/css3-background/#ltpositiongt +bool +CSSParserImpl::ParsePositionValue(nsCSSValue& aOut) { - // css3-background allows positions to be defined as offsets - // from an edge. There can be 2 keywords and 2 offsets given. These - // four 'values' are stored in an array in the following order: - // [keyword offset keyword offset]. If a keyword or offset isn't - // parsed the value of the corresponding array element is set - // to eCSSUnit_Null by a call to nsCSSValue::Reset(). - if (aAcceptsInherit && ParseVariant(aOut, VARIANT_INHERIT, nullptr)) { - return true; - } - nsRefPtr value = nsCSSValue::Array::Create(4); aOut.SetArrayValue(value, eCSSUnit_Array); From 46a6804c999bb0a609946894d5fd6a4d707ea543 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 9 Sep 2014 11:40:15 -0700 Subject: [PATCH 48/69] Bug 1060090 part 2: Spin out "ComputePositionValue", and rename its helper "ComputeBackgroundPositionCoord" to "ComputePositionCoord". r=heycam --- layout/style/nsRuleNode.cpp | 77 +++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index b1f445f047fb..1a7bf6aa9c5b 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -5937,18 +5937,17 @@ struct BackgroundItemComputer } }; -/* Helper function for - * BackgroundItemComputer - * It computes a single PositionCoord from an nsCSSValue object - * (contained in a list). +/* Helper function for ComputePositionValue. + * This function computes a single PositionCoord from two nsCSSValue objects, + * which represent an edge and an offset from that edge. */ typedef nsStyleBackground::Position::PositionCoord PositionCoord; static void -ComputeBackgroundPositionCoord(nsStyleContext* aStyleContext, - const nsCSSValue& aEdge, - const nsCSSValue& aOffset, - PositionCoord* aResult, - bool& aCanStoreInRuleTree) +ComputePositionCoord(nsStyleContext* aStyleContext, + const nsCSSValue& aEdge, + const nsCSSValue& aOffset, + PositionCoord* aResult, + bool& aCanStoreInRuleTree) { if (eCSSUnit_Percent == aOffset.GetUnit()) { aResult->mLength = 0; @@ -5992,6 +5991,40 @@ ComputeBackgroundPositionCoord(nsStyleContext* aStyleContext, } } +/* Helper function to convert a CSS specified value into its + * computed-style form. */ +static void +ComputePositionValue(nsStyleContext* aStyleContext, + const nsCSSValue& aValue, + nsStyleBackground::Position& aComputedValue, + bool& aCanStoreInRuleTree) +{ + NS_ASSERTION(aValue.GetUnit() == eCSSUnit_Array, + "unexpected unit for CSS value"); + + nsRefPtr positionArray = aValue.GetArrayValue(); + const nsCSSValue &xEdge = positionArray->Item(0); + const nsCSSValue &xOffset = positionArray->Item(1); + const nsCSSValue &yEdge = positionArray->Item(2); + const nsCSSValue &yOffset = positionArray->Item(3); + + NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() || + eCSSUnit_Null == xEdge.GetUnit()) && + (eCSSUnit_Enumerated == yEdge.GetUnit() || + eCSSUnit_Null == yEdge.GetUnit()) && + eCSSUnit_Enumerated != xOffset.GetUnit() && + eCSSUnit_Enumerated != yOffset.GetUnit(), + "Invalid background position"); + + ComputePositionCoord(aStyleContext, xEdge, xOffset, + &aComputedValue.mXPosition, + aCanStoreInRuleTree); + + ComputePositionCoord(aStyleContext, yEdge, yOffset, + &aComputedValue.mYPosition, + aCanStoreInRuleTree); +} + template <> struct BackgroundItemComputer { @@ -6000,30 +6033,8 @@ struct BackgroundItemComputer nsStyleBackground::Position& aComputedValue, bool& aCanStoreInRuleTree) { - NS_ASSERTION(aSpecifiedValue->mValue.GetUnit() == eCSSUnit_Array, "bg-position not an array"); - - nsRefPtr bgPositionArray = - aSpecifiedValue->mValue.GetArrayValue(); - const nsCSSValue &xEdge = bgPositionArray->Item(0); - const nsCSSValue &xOffset = bgPositionArray->Item(1); - const nsCSSValue &yEdge = bgPositionArray->Item(2); - const nsCSSValue &yOffset = bgPositionArray->Item(3); - - NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() || - eCSSUnit_Null == xEdge.GetUnit()) && - (eCSSUnit_Enumerated == yEdge.GetUnit() || - eCSSUnit_Null == yEdge.GetUnit()) && - eCSSUnit_Enumerated != xOffset.GetUnit() && - eCSSUnit_Enumerated != yOffset.GetUnit(), - "Invalid background position"); - - ComputeBackgroundPositionCoord(aStyleContext, xEdge, xOffset, - &aComputedValue.mXPosition, - aCanStoreInRuleTree); - - ComputeBackgroundPositionCoord(aStyleContext, yEdge, yOffset, - &aComputedValue.mYPosition, - aCanStoreInRuleTree); + ComputePositionValue(aStyleContext, aSpecifiedValue->mValue, + aComputedValue, aCanStoreInRuleTree); } }; From eebeee63460b65638612d30715dacfa7bf1e1972 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 9 Sep 2014 11:40:17 -0700 Subject: [PATCH 49/69] Bug 1060090 part 3: Refactor getComputedStyle implementation for 'background-position' to use helper-method for & its sub-parts. r=heycam --- layout/style/nsComputedDOMStyle.cpp | 58 +++++++++++++++-------------- layout/style/nsComputedDOMStyle.h | 7 ++++ 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index b28a926e6a2e..a6e112c8244b 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -51,6 +51,8 @@ using namespace mozilla; using namespace mozilla::dom; +typedef const nsStyleBackground::Position Position; +typedef const nsStyleBackground::Position::PositionCoord PositionCoord; #if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon) #define DEBUG_ComputedDOMStyle @@ -2050,6 +2052,34 @@ nsComputedDOMStyle::DoGetBackgroundOrigin() nsCSSProps::kBackgroundOriginKTable); } +void +nsComputedDOMStyle::SetValueToPositionCoord(const PositionCoord& aCoord, + nsROCSSPrimitiveValue* aValue) +{ + if (!aCoord.mHasPercent) { + NS_ABORT_IF_FALSE(aCoord.mPercent == 0.0f, + "Shouldn't have mPercent!"); + aValue->SetAppUnits(aCoord.mLength); + } else if (aCoord.mLength == 0) { + aValue->SetPercent(aCoord.mPercent); + } else { + SetValueToCalc(&aCoord, aValue); + } +} + +void +nsComputedDOMStyle::SetValueToPosition(const Position& aPosition, + nsDOMCSSValueList* aValueList) +{ + nsROCSSPrimitiveValue* valX = new nsROCSSPrimitiveValue; + aValueList->AppendCSSValue(valX); + SetValueToPositionCoord(aPosition.mXPosition, valX); + + nsROCSSPrimitiveValue* valY = new nsROCSSPrimitiveValue; + aValueList->AppendCSSValue(valY); + SetValueToPositionCoord(aPosition.mYPosition, valY); +} + CSSValue* nsComputedDOMStyle::DoGetBackgroundPosition() { @@ -2061,33 +2091,7 @@ nsComputedDOMStyle::DoGetBackgroundPosition() nsDOMCSSValueList *itemList = GetROCSSValueList(false); valueList->AppendCSSValue(itemList); - nsROCSSPrimitiveValue *valX = new nsROCSSPrimitiveValue; - itemList->AppendCSSValue(valX); - - nsROCSSPrimitiveValue *valY = new nsROCSSPrimitiveValue; - itemList->AppendCSSValue(valY); - - const nsStyleBackground::Position &pos = bg->mLayers[i].mPosition; - - if (!pos.mXPosition.mHasPercent) { - NS_ABORT_IF_FALSE(pos.mXPosition.mPercent == 0.0f, - "Shouldn't have mPercent!"); - valX->SetAppUnits(pos.mXPosition.mLength); - } else if (pos.mXPosition.mLength == 0) { - valX->SetPercent(pos.mXPosition.mPercent); - } else { - SetValueToCalc(&pos.mXPosition, valX); - } - - if (!pos.mYPosition.mHasPercent) { - NS_ABORT_IF_FALSE(pos.mYPosition.mPercent == 0.0f, - "Shouldn't have mPercent!"); - valY->SetAppUnits(pos.mYPosition.mLength); - } else if (pos.mYPosition.mLength == 0) { - valY->SetPercent(pos.mYPosition.mPercent); - } else { - SetValueToCalc(&pos.mYPosition, valY); - } + SetValueToPosition(bg->mLayers[i].mPosition, itemList); } return valueList; diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index b42dbdfcb040..bc47f23912f9 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -519,9 +519,16 @@ private: mozilla::dom::CSSValue* DoGetCustomProperty(const nsAString& aPropertyName); nsDOMCSSValueList* GetROCSSValueList(bool aCommaDelimited); + + /* Helper functions */ void SetToRGBAColor(nsROCSSPrimitiveValue* aValue, nscolor aColor); void SetValueToStyleImage(const nsStyleImage& aStyleImage, nsROCSSPrimitiveValue* aValue); + void SetValueToPositionCoord( + const nsStyleBackground::Position::PositionCoord& aCoord, + nsROCSSPrimitiveValue* aValue); + void SetValueToPosition(const nsStyleBackground::Position& aPosition, + nsDOMCSSValueList* aValueList); /** * A method to get a percentage base for a percentage value. Returns true From 9437318153ec9e9f4d1342f3c53ff7271a34d3c1 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 9 Sep 2014 11:40:18 -0700 Subject: [PATCH 50/69] Bug 1060090 part 4: Allow computed-style 'Position' struct to have different initial values beyond just 0% 0%. r=heycam --- layout/style/nsRuleNode.cpp | 2 +- layout/style/nsStyleStruct.cpp | 9 ++++----- layout/style/nsStyleStruct.h | 5 +++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 1a7bf6aa9c5b..3636940ca086 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -6368,7 +6368,7 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, // background-position: enum, length, percent (flags), inherit [pair list] nsStyleBackground::Position initialPosition; - initialPosition.SetInitialValues(); + initialPosition.SetInitialPercentValues(0.0f); SetBackgroundList(aContext, *aRuleData->ValueForBackgroundPosition(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mPosition, diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index e6fc4e05408b..548ab5cf7435 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -2136,13 +2136,12 @@ bool nsStyleBackground::IsTransparent() const } void -nsStyleBackground::Position::SetInitialValues() +nsStyleBackground::Position::SetInitialPercentValues(float aPercentVal) { - // Initial value is "0% 0%" - mXPosition.mPercent = 0.0f; + mXPosition.mPercent = aPercentVal; mXPosition.mLength = 0; mXPosition.mHasPercent = true; - mYPosition.mPercent = 0.0f; + mYPosition.mPercent = aPercentVal; mYPosition.mLength = 0; mYPosition.mHasPercent = true; } @@ -2273,7 +2272,7 @@ nsStyleBackground::Layer::SetInitialValues() mOrigin = NS_STYLE_BG_ORIGIN_PADDING; mRepeat.SetInitialValues(); mBlendMode = NS_STYLE_BLEND_NORMAL; - mPosition.SetInitialValues(); + mPosition.SetInitialPercentValues(0.0f); // Initial value is "0% 0%" mSize.SetInitialValues(); mImage.SetNull(); } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index c949f2665d63..e1de8b3f2e58 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -380,8 +380,9 @@ struct nsStyleBackground { // Initialize nothing Position() {} - // Initialize to initial values - void SetInitialValues(); + // Sets both mXPosition and mYPosition to the given percent value for the + // initial property-value (e.g. 0.0f for "0% 0%", or 0.5f for "50% 50%") + void SetInitialPercentValues(float aPercentVal); // True if the effective background image position described by this depends // on the size of the corresponding frame. From 8c5ca6de72713ea828cdecda92864c645d87e543 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 9 Sep 2014 11:40:20 -0700 Subject: [PATCH 51/69] Bug 1060090 part 5: Refactor StyleAnimationValue -handling code to use helper-methods. r=heycam --- layout/style/StyleAnimationValue.cpp | 144 ++++++++++++++++----------- 1 file changed, 87 insertions(+), 57 deletions(-) diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index a08208b303bc..e39266f0da59 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -401,6 +401,48 @@ GetURIAsUtf16StringBuffer(nsIURI* aUri) return nsCSSValue::BufferFromString(NS_ConvertUTF8toUTF16(utf8String)); } +double +CalcPositionSquareDistance(const nsCSSValue& aPos1, + const nsCSSValue& aPos2) +{ + NS_ASSERTION(aPos1.GetUnit() == eCSSUnit_Array && + aPos2.GetUnit() == eCSSUnit_Array, + "Expected two arrays"); + + PixelCalcValue calcVal[4]; + + nsCSSValue::Array* posArray = aPos1.GetArrayValue(); + NS_ABORT_IF_FALSE(posArray->Count() == 4, "Invalid position value"); + NS_ASSERTION(posArray->Item(0).GetUnit() == eCSSUnit_Null && + posArray->Item(2).GetUnit() == eCSSUnit_Null, + "Invalid list used"); + for (int i = 0; i < 2; ++i) { + NS_ABORT_IF_FALSE(posArray->Item(i*2+1).GetUnit() != eCSSUnit_Null, + "Invalid position value"); + calcVal[i] = ExtractCalcValue(posArray->Item(i*2+1)); + } + + posArray = aPos2.GetArrayValue(); + NS_ABORT_IF_FALSE(posArray->Count() == 4, "Invalid position value"); + NS_ASSERTION(posArray->Item(0).GetUnit() == eCSSUnit_Null && + posArray->Item(2).GetUnit() == eCSSUnit_Null, + "Invalid list used"); + for (int i = 0; i < 2; ++i) { + NS_ABORT_IF_FALSE(posArray->Item(i*2+1).GetUnit() != eCSSUnit_Null, + "Invalid position value"); + calcVal[i+2] = ExtractCalcValue(posArray->Item(i*2+1)); + } + + double squareDistance = 0.0; + for (int i = 0; i < 2; ++i) { + float difflen = calcVal[i+2].mLength - calcVal[i].mLength; + float diffpct = calcVal[i+2].mPercent - calcVal[i].mPercent; + squareDistance += difflen * difflen + diffpct * diffpct; + } + + return squareDistance; +} + // CLASS METHODS // ------------- @@ -790,40 +832,8 @@ StyleAnimationValue::ComputeDistance(nsCSSProperty aProperty, NS_ABORT_IF_FALSE(!position1 == !position2, "lists should be same length"); while (position1 && position2) { - NS_ASSERTION(position1->mValue.GetUnit() == eCSSUnit_Array && - position2->mValue.GetUnit() == eCSSUnit_Array, - "Expected two arrays"); - - PixelCalcValue calcVal[4]; - - nsCSSValue::Array* bgArray = position1->mValue.GetArrayValue(); - NS_ABORT_IF_FALSE(bgArray->Count() == 4, "Invalid background-position"); - NS_ASSERTION(bgArray->Item(0).GetUnit() == eCSSUnit_Null && - bgArray->Item(2).GetUnit() == eCSSUnit_Null, - "Invalid list used"); - for (int i = 0; i < 2; ++i) { - NS_ABORT_IF_FALSE(bgArray->Item(i*2+1).GetUnit() != eCSSUnit_Null, - "Invalid background-position"); - calcVal[i] = ExtractCalcValue(bgArray->Item(i*2+1)); - } - - bgArray = position2->mValue.GetArrayValue(); - NS_ABORT_IF_FALSE(bgArray->Count() == 4, "Invalid background-position"); - NS_ASSERTION(bgArray->Item(0).GetUnit() == eCSSUnit_Null && - bgArray->Item(2).GetUnit() == eCSSUnit_Null, - "Invalid list used"); - for (int i = 0; i < 2; ++i) { - NS_ABORT_IF_FALSE(bgArray->Item(i*2+1).GetUnit() != eCSSUnit_Null, - "Invalid background-position"); - calcVal[i+2] = ExtractCalcValue(bgArray->Item(i*2+1)); - } - - for (int i = 0; i < 2; ++i) { - float difflen = calcVal[i+2].mLength - calcVal[i].mLength; - float diffpct = calcVal[i+2].mPercent - calcVal[i].mPercent; - squareDistance += difflen * difflen + diffpct * diffpct; - } - + squareDistance += CalcPositionSquareDistance(position1->mValue, + position2->mValue); position1 = position1->mNext; position2 = position2->mNext; } @@ -1883,6 +1893,28 @@ AddTransformLists(double aCoeff1, const nsCSSValueList* aList1, return result.forget(); } +static void +AddPositions(double aCoeff1, const nsCSSValue& aPos1, + double aCoeff2, const nsCSSValue& aPos2, + nsCSSValue& aResultPos) +{ + const nsCSSValue::Array* posArray1 = aPos1.GetArrayValue(); + const nsCSSValue::Array* posArray2 = aPos2.GetArrayValue(); + nsCSSValue::Array* resultPosArray = nsCSSValue::Array::Create(4); + aResultPos.SetArrayValue(resultPosArray, eCSSUnit_Array); + + /* Only iterate over elements 1 and 3. The is + * 'uncomputed' to only those elements. + */ + for (size_t i = 1; i < 4; i += 2) { + const nsCSSValue& v1 = posArray1->Item(i); + const nsCSSValue& v2 = posArray2->Item(i); + nsCSSValue& vr = resultPosArray->Item(i); + AddCSSValueCanonicalCalc(aCoeff1, v1, + aCoeff2, v2, vr); + } +} + bool StyleAnimationValue::AddWeighted(nsCSSProperty aProperty, double aCoeff1, @@ -2368,21 +2400,8 @@ StyleAnimationValue::AddWeighted(nsCSSProperty aProperty, *resultTail = item; resultTail = &item->mNext; - nsCSSValue::Array* bgPos1 = position1->mValue.GetArrayValue(); - nsCSSValue::Array* bgPos2 = position2->mValue.GetArrayValue(); - nsCSSValue::Array* bgPosRes = nsCSSValue::Array::Create(4); - item->mValue.SetArrayValue(bgPosRes, eCSSUnit_Array); - - /* Only iterate over elements 1 and 3. The background position is - * 'uncomputed' to only those elements. - */ - for (int i = 1; i < 4; i+=2) { - const nsCSSValue& v1 = bgPos1->Item(i); - const nsCSSValue& v2 = bgPos2->Item(i); - nsCSSValue& vr = bgPosRes->Item(i); - AddCSSValueCanonicalCalc(aCoeff1, v1, - aCoeff2, v2, vr); - } + AddPositions(aCoeff1, position1->mValue, + aCoeff2, position2->mValue, item->mValue); position1 = position1->mNext; position2 = position2->mNext; @@ -2804,6 +2823,24 @@ StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue) return true; } +static void +SetPositionValue(const nsStyleBackground::Position& aPos, nsCSSValue& aCSSValue) +{ + nsRefPtr posArray = nsCSSValue::Array::Create(4); + aCSSValue.SetArrayValue(posArray.get(), eCSSUnit_Array); + + // NOTE: Array entries #0 and #2 here are intentionally left untouched, with + // eCSSUnit_Null. The purpose of these entries in our specified-style + // representation is to store edge names. But for values + // extracted from computed style (which is what we're dealing with here), + // we'll just have a normalized "x,y" position, with no edge names needed. + nsCSSValue& xValue = posArray->Item(1); + nsCSSValue& yValue = posArray->Item(3); + + SetCalcValue(&aPos.mXPosition, xValue); + SetCalcValue(&aPos.mYPosition, yValue); +} + /* * Assign |aOutput = aInput|, except with any non-pixel lengths * replaced with the equivalent in pixels, and any non-canonical calc() @@ -3153,14 +3190,7 @@ StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty, nsCSSValueList *item = new nsCSSValueList; *resultTail = item; resultTail = &item->mNext; - nsRefPtr bgArray = nsCSSValue::Array::Create(4); - item->mValue.SetArrayValue(bgArray.get(), eCSSUnit_Array); - - const nsStyleBackground::Position &pos = bg->mLayers[i].mPosition; - nsCSSValue &xValue = bgArray->Item(1), - &yValue = bgArray->Item(3); - SetCalcValue(&pos.mXPosition, xValue); - SetCalcValue(&pos.mYPosition, yValue); + SetPositionValue(bg->mLayers[i].mPosition, item->mValue); } aComputedValue.SetAndAdoptCSSValueListValue(result.forget(), From 2363a05381bca3e471bed1e0f5277cf3e1ee5054 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Tue, 9 Sep 2014 15:02:27 -0400 Subject: [PATCH 52/69] Backed out changeset 7b0ae11d2441 (bug 1057512) for introducing various new intermittent failures and making an existing one perma-fail. --- testing/mochitest/runtests.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index 1506c9bcf041..d0d8399711e1 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -1634,10 +1634,6 @@ class Mochitest(MochitestUtilsMixin): self.setTestRoot(options) - # Until we have all green, this only runs on bc* jobs (not dt* jobs) - if options.browserChrome and not options.subsuite: - options.runByDir = True - if not options.runByDir: return self.runMochitests(options, onLaunch) From 9a9a2da2907597b3d85905d18a48a7a300e1027a Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 9 Sep 2014 15:04:35 -0400 Subject: [PATCH 53/69] Bug 1065036 - nsIServiceWorkerManager.idl should be a builtinclass, r=nsm --- dom/interfaces/base/nsIServiceWorkerManager.idl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl index 99f699b10a73..e7ab1bc3c48f 100644 --- a/dom/interfaces/base/nsIServiceWorkerManager.idl +++ b/dom/interfaces/base/nsIServiceWorkerManager.idl @@ -8,7 +8,7 @@ interface nsIDocument; interface nsIURI; -[uuid(8d2f64db-5585-4876-a1c9-391e16549068)] +[builtinclass, uuid(79ad5b57-d65d-46d3-b5e9-32d27e16052d)] interface nsIServiceWorkerManager : nsISupports { /** From 0c4327b43fdff857d12dd6d4c2f676911b47a5e1 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:45 -0700 Subject: [PATCH 54/69] Bug 1031092 - Part 1: Move jsproxy.* to new js/src/proxy/. (r=bholley) --HG-- rename : js/src/jsproxy.cpp => js/src/proxy/jsproxy.cpp rename : js/src/jsproxy.h => js/src/proxy/jsproxy.h --- js/src/moz.build | 4 ++-- js/src/{ => proxy}/jsproxy.cpp | 0 js/src/{ => proxy}/jsproxy.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename js/src/{ => proxy}/jsproxy.cpp (100%) rename js/src/{ => proxy}/jsproxy.h (99%) diff --git a/js/src/moz.build b/js/src/moz.build index a812b5c8c3d7..4a73c1f13cfd 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -53,12 +53,12 @@ EXPORTS += [ 'jsfriendapi.h', 'jsprf.h', 'jsprototypes.h', - 'jsproxy.h', 'jspubtd.h', 'jstypes.h', 'jsversion.h', 'jswrapper.h', 'perf/jsperf.h', + 'proxy/jsproxy.h', ] # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so @@ -223,7 +223,6 @@ UNIFIED_SOURCES += [ 'jsopcode.cpp', 'jsprf.cpp', 'jspropertytree.cpp', - 'jsproxy.cpp', 'jsreflect.cpp', 'jsscript.cpp', 'jsstr.cpp', @@ -232,6 +231,7 @@ UNIFIED_SOURCES += [ 'jswrapper.cpp', 'perf/jsperf.cpp', 'prmjtime.cpp', + 'proxy/jsproxy.cpp', 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', 'vm/CallNonGenericMethod.cpp', diff --git a/js/src/jsproxy.cpp b/js/src/proxy/jsproxy.cpp similarity index 100% rename from js/src/jsproxy.cpp rename to js/src/proxy/jsproxy.cpp diff --git a/js/src/jsproxy.h b/js/src/proxy/jsproxy.h similarity index 99% rename from js/src/jsproxy.h rename to js/src/proxy/jsproxy.h index 4be6da78d54e..fb3feff1e5c8 100644 --- a/js/src/jsproxy.h +++ b/js/src/proxy/jsproxy.h @@ -4,8 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef jsproxy_h -#define jsproxy_h +#ifndef proxy_jsproxy_h +#define proxy_jsproxy_h #include "mozilla/Maybe.h" From 57bd4de2e4d076bba98c8b33bc98c33141040fce Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:45 -0700 Subject: [PATCH 55/69] Bug 1031092 - Part 2: Factor out BaseProxyHandler. (r=bholley) --- js/src/moz.build | 1 + js/src/proxy/BaseProxyHandler.cpp | 343 ++++++++++++++++++++++++++++++ js/src/proxy/jsproxy.cpp | 330 ---------------------------- 3 files changed, 344 insertions(+), 330 deletions(-) create mode 100644 js/src/proxy/BaseProxyHandler.cpp diff --git a/js/src/moz.build b/js/src/moz.build index 4a73c1f13cfd..a6a1ea85c2e3 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -231,6 +231,7 @@ UNIFIED_SOURCES += [ 'jswrapper.cpp', 'perf/jsperf.cpp', 'prmjtime.cpp', + 'proxy/BaseProxyHandler.cpp', 'proxy/jsproxy.cpp', 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp new file mode 100644 index 000000000000..59a54360617d --- /dev/null +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -0,0 +1,343 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsproxy.h" + +#include "vm/ProxyObject.h" + +#include "jscntxtinlines.h" + +using namespace js; + +bool +BaseProxyHandler::enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act, + bool *bp) const +{ + *bp = true; + return true; +} + +bool +BaseProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + assertEnteredPolicy(cx, proxy, id, GET); + Rooted desc(cx); + if (!getPropertyDescriptor(cx, proxy, id, &desc)) + return false; + *bp = !!desc.object(); + return true; +} + +bool +BaseProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + // Note: Proxy::set needs to invoke hasOwn to determine where the setter + // lives, so we allow SET operations to invoke us. + assertEnteredPolicy(cx, proxy, id, GET | SET); + Rooted desc(cx); + if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) + return false; + *bp = !!desc.object(); + return true; +} + +bool +BaseProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, MutableHandleValue vp) const +{ + assertEnteredPolicy(cx, proxy, id, GET); + + Rooted desc(cx); + if (!getPropertyDescriptor(cx, proxy, id, &desc)) + return false; + if (!desc.object()) { + vp.setUndefined(); + return true; + } + if (!desc.getter() || + (!desc.hasGetterObject() && desc.getter() == JS_PropertyStub)) + { + vp.set(desc.value()); + return true; + } + if (desc.hasGetterObject()) + return InvokeGetterOrSetter(cx, receiver, ObjectValue(*desc.getterObject()), + 0, nullptr, vp); + if (!desc.isShared()) + vp.set(desc.value()); + else + vp.setUndefined(); + + return CallJSPropertyOp(cx, desc.getter(), receiver, id, vp); +} + +bool +BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, bool strict, MutableHandleValue vp) const +{ + assertEnteredPolicy(cx, proxy, id, SET); + + // Find an own or inherited property. The code here is strange for maximum + // backward compatibility with earlier code written before ES6 and before + // SetPropertyIgnoringNamedGetter. + Rooted desc(cx); + if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) + return false; + bool descIsOwn = desc.object() != nullptr; + if (!descIsOwn) { + if (!getPropertyDescriptor(cx, proxy, id, &desc)) + return false; + } + + return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, descIsOwn, strict, + vp); +} + +bool +js::SetPropertyIgnoringNamedGetter(JSContext *cx, const BaseProxyHandler *handler, + HandleObject proxy, HandleObject receiver, + HandleId id, MutableHandle desc, + bool descIsOwn, bool strict, MutableHandleValue vp) +{ + /* The control-flow here differs from ::get() because of the fall-through case below. */ + if (descIsOwn) { + JS_ASSERT(desc.object()); + + // Check for read-only properties. + if (desc.isReadonly()) + return strict ? Throw(cx, id, JSMSG_READ_ONLY) : true; + if (!desc.setter()) { + // Be wary of the odd explicit undefined setter case possible through + // Object.defineProperty. + if (!desc.hasSetterObject()) + desc.setSetter(JS_StrictPropertyStub); + } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) { + if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp)) + return false; + if (!proxy->is() || proxy->as().handler() != handler) + return true; + if (desc.isShared()) + return true; + } + if (!desc.getter()) { + // Same as above for the null setter case. + if (!desc.hasGetterObject()) + desc.setGetter(JS_PropertyStub); + } + desc.value().set(vp.get()); + return handler->defineProperty(cx, receiver, id, desc); + } + if (desc.object()) { + // Check for read-only properties. + if (desc.isReadonly()) + return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true; + if (!desc.setter()) { + // Be wary of the odd explicit undefined setter case possible through + // Object.defineProperty. + if (!desc.hasSetterObject()) + desc.setSetter(JS_StrictPropertyStub); + } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) { + if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp)) + return false; + if (!proxy->is() || proxy->as().handler() != handler) + return true; + if (desc.isShared()) + return true; + } + if (!desc.getter()) { + // Same as above for the null setter case. + if (!desc.hasGetterObject()) + desc.setGetter(JS_PropertyStub); + } + desc.value().set(vp.get()); + return handler->defineProperty(cx, receiver, id, desc); + } + + desc.object().set(receiver); + desc.value().set(vp.get()); + desc.setAttributes(JSPROP_ENUMERATE); + desc.setGetter(nullptr); + desc.setSetter(nullptr); // Pick up the class getter/setter. + return handler->defineProperty(cx, receiver, id, desc); +} + +bool +BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); + JS_ASSERT(props.length() == 0); + + if (!getOwnPropertyNames(cx, proxy, props)) + return false; + + /* Select only the enumerable properties through in-place iteration. */ + RootedId id(cx); + size_t i = 0; + for (size_t j = 0, len = props.length(); j < len; j++) { + JS_ASSERT(i <= j); + id = props[j]; + if (JSID_IS_SYMBOL(id)) + continue; + + AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET); + Rooted desc(cx); + if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) + return false; + if (desc.object() && desc.isEnumerable()) + props[i++].set(id); + } + + JS_ASSERT(i <= props.length()); + props.resize(i); + + return true; +} + +bool +BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, + MutableHandleValue vp) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); + + AutoIdVector props(cx); + if ((flags & JSITER_OWNONLY) + ? !keys(cx, proxy, props) + : !enumerate(cx, proxy, props)) { + return false; + } + + return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp); +} + +bool +BaseProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const +{ + MOZ_CRASH("callable proxies should implement call trap"); +} + +bool +BaseProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const +{ + MOZ_CRASH("callable proxies should implement construct trap"); +} + +const char * +BaseProxyHandler::className(JSContext *cx, HandleObject proxy) const +{ + return proxy->isCallable() ? "Function" : "Object"; +} + +JSString * +BaseProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const +{ + if (proxy->isCallable()) + return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}"); + RootedValue v(cx, ObjectValue(*proxy)); + ReportIsNotFunction(cx, v); + return nullptr; +} + +bool +BaseProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, + RegExpGuard *g) const +{ + MOZ_CRASH("This should have been a wrapped regexp"); +} + +bool +BaseProxyHandler::boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const +{ + vp.setUndefined(); + return true; +} + +bool +BaseProxyHandler::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, + MutableHandleValue vp) const +{ + return DefaultValue(cx, proxy, hint, vp); +} + +bool +BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const +{ + ReportIncompatible(cx, args); + return false; +} + +bool +BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, + bool *bp) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, GET); + RootedValue val(cx, ObjectValue(*proxy.get())); + js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, + JSDVG_SEARCH_STACK, val, js::NullPtr()); + return false; +} + +bool +BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx) const +{ + return false; +} + +void +BaseProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy) const +{ +} + +void +BaseProxyHandler::objectMoved(JSObject *proxy, const JSObject *old) const +{ +} + +JSObject * +BaseProxyHandler::weakmapKeyDelegate(JSObject *proxy) const +{ + return nullptr; +} + +bool +BaseProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const +{ + MOZ_CRASH("Must override getPrototypeOf with lazy prototype."); +} + +bool +BaseProxyHandler::setPrototypeOf(JSContext *cx, HandleObject, HandleObject, bool *) const +{ + // Disallow sets of protos on proxies with lazy protos, but no hook. + // This keeps us away from the footgun of having the first proto set opt + // you out of having dynamic protos altogether. + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, + "incompatible Proxy"); + return false; +} + +bool +BaseProxyHandler::watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH, + proxy->getClass()->name); + return false; +} + +bool +BaseProxyHandler::unwatch(JSContext *cx, HandleObject proxy, HandleId id) const +{ + return true; +} + +bool +BaseProxyHandler::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, + HandleObject result) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, GET); + + return js::SliceSlowly(cx, proxy, proxy, begin, end, result); +} diff --git a/js/src/proxy/jsproxy.cpp b/js/src/proxy/jsproxy.cpp index cc5ea35db8a1..178a4e65173a 100644 --- a/js/src/proxy/jsproxy.cpp +++ b/js/src/proxy/jsproxy.cpp @@ -83,336 +83,6 @@ js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id, } #endif -bool -BaseProxyHandler::enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act, - bool *bp) const -{ - *bp = true; - return true; -} - -bool -BaseProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - assertEnteredPolicy(cx, proxy, id, GET); - Rooted desc(cx); - if (!getPropertyDescriptor(cx, proxy, id, &desc)) - return false; - *bp = !!desc.object(); - return true; -} - -bool -BaseProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - // Note: Proxy::set needs to invoke hasOwn to determine where the setter - // lives, so we allow SET operations to invoke us. - assertEnteredPolicy(cx, proxy, id, GET | SET); - Rooted desc(cx); - if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) - return false; - *bp = !!desc.object(); - return true; -} - -bool -BaseProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, MutableHandleValue vp) const -{ - assertEnteredPolicy(cx, proxy, id, GET); - - Rooted desc(cx); - if (!getPropertyDescriptor(cx, proxy, id, &desc)) - return false; - if (!desc.object()) { - vp.setUndefined(); - return true; - } - if (!desc.getter() || - (!desc.hasGetterObject() && desc.getter() == JS_PropertyStub)) - { - vp.set(desc.value()); - return true; - } - if (desc.hasGetterObject()) - return InvokeGetterOrSetter(cx, receiver, ObjectValue(*desc.getterObject()), - 0, nullptr, vp); - if (!desc.isShared()) - vp.set(desc.value()); - else - vp.setUndefined(); - - return CallJSPropertyOp(cx, desc.getter(), receiver, id, vp); -} - -bool -BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const -{ - assertEnteredPolicy(cx, proxy, id, SET); - - // Find an own or inherited property. The code here is strange for maximum - // backward compatibility with earlier code written before ES6 and before - // SetPropertyIgnoringNamedGetter. - Rooted desc(cx); - if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) - return false; - bool descIsOwn = desc.object() != nullptr; - if (!descIsOwn) { - if (!getPropertyDescriptor(cx, proxy, id, &desc)) - return false; - } - - return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, descIsOwn, strict, - vp); -} - -bool -js::SetPropertyIgnoringNamedGetter(JSContext *cx, const BaseProxyHandler *handler, - HandleObject proxy, HandleObject receiver, - HandleId id, MutableHandle desc, - bool descIsOwn, bool strict, MutableHandleValue vp) -{ - /* The control-flow here differs from ::get() because of the fall-through case below. */ - if (descIsOwn) { - JS_ASSERT(desc.object()); - - // Check for read-only properties. - if (desc.isReadonly()) - return strict ? Throw(cx, id, JSMSG_READ_ONLY) : true; - if (!desc.setter()) { - // Be wary of the odd explicit undefined setter case possible through - // Object.defineProperty. - if (!desc.hasSetterObject()) - desc.setSetter(JS_StrictPropertyStub); - } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) { - if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp)) - return false; - if (!proxy->is() || proxy->as().handler() != handler) - return true; - if (desc.isShared()) - return true; - } - if (!desc.getter()) { - // Same as above for the null setter case. - if (!desc.hasGetterObject()) - desc.setGetter(JS_PropertyStub); - } - desc.value().set(vp.get()); - return handler->defineProperty(cx, receiver, id, desc); - } - if (desc.object()) { - // Check for read-only properties. - if (desc.isReadonly()) - return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true; - if (!desc.setter()) { - // Be wary of the odd explicit undefined setter case possible through - // Object.defineProperty. - if (!desc.hasSetterObject()) - desc.setSetter(JS_StrictPropertyStub); - } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) { - if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp)) - return false; - if (!proxy->is() || proxy->as().handler() != handler) - return true; - if (desc.isShared()) - return true; - } - if (!desc.getter()) { - // Same as above for the null setter case. - if (!desc.hasGetterObject()) - desc.setGetter(JS_PropertyStub); - } - desc.value().set(vp.get()); - return handler->defineProperty(cx, receiver, id, desc); - } - - desc.object().set(receiver); - desc.value().set(vp.get()); - desc.setAttributes(JSPROP_ENUMERATE); - desc.setGetter(nullptr); - desc.setSetter(nullptr); // Pick up the class getter/setter. - return handler->defineProperty(cx, receiver, id, desc); -} - -bool -BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); - JS_ASSERT(props.length() == 0); - - if (!getOwnPropertyNames(cx, proxy, props)) - return false; - - /* Select only the enumerable properties through in-place iteration. */ - RootedId id(cx); - size_t i = 0; - for (size_t j = 0, len = props.length(); j < len; j++) { - JS_ASSERT(i <= j); - id = props[j]; - if (JSID_IS_SYMBOL(id)) - continue; - - AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET); - Rooted desc(cx); - if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) - return false; - if (desc.object() && desc.isEnumerable()) - props[i++].set(id); - } - - JS_ASSERT(i <= props.length()); - props.resize(i); - - return true; -} - -bool -BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, - MutableHandleValue vp) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); - - AutoIdVector props(cx); - if ((flags & JSITER_OWNONLY) - ? !keys(cx, proxy, props) - : !enumerate(cx, proxy, props)) { - return false; - } - - return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp); -} - -bool -BaseProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const -{ - MOZ_CRASH("callable proxies should implement call trap"); -} - -bool -BaseProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const -{ - MOZ_CRASH("callable proxies should implement construct trap"); -} - -const char * -BaseProxyHandler::className(JSContext *cx, HandleObject proxy) const -{ - return proxy->isCallable() ? "Function" : "Object"; -} - -JSString * -BaseProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const -{ - if (proxy->isCallable()) - return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}"); - RootedValue v(cx, ObjectValue(*proxy)); - ReportIsNotFunction(cx, v); - return nullptr; -} - -bool -BaseProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, - RegExpGuard *g) const -{ - MOZ_CRASH("This should have been a wrapped regexp"); -} - -bool -BaseProxyHandler::boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const -{ - vp.setUndefined(); - return true; -} - -bool -BaseProxyHandler::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, - MutableHandleValue vp) const -{ - return DefaultValue(cx, proxy, hint, vp); -} - -bool -BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) const -{ - ReportIncompatible(cx, args); - return false; -} - -bool -BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, - bool *bp) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, GET); - RootedValue val(cx, ObjectValue(*proxy.get())); - js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, - JSDVG_SEARCH_STACK, val, js::NullPtr()); - return false; -} - -bool -BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx) const -{ - return false; -} - -void -BaseProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy) const -{ -} - -void -BaseProxyHandler::objectMoved(JSObject *proxy, const JSObject *old) const -{ -} - -JSObject * -BaseProxyHandler::weakmapKeyDelegate(JSObject *proxy) const -{ - return nullptr; -} - -bool -BaseProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const -{ - MOZ_CRASH("Must override getPrototypeOf with lazy prototype."); -} - -bool -BaseProxyHandler::setPrototypeOf(JSContext *cx, HandleObject, HandleObject, bool *) const -{ - // Disallow sets of protos on proxies with lazy protos, but no hook. - // This keeps us away from the footgun of having the first proto set opt - // you out of having dynamic protos altogether. - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, - "incompatible Proxy"); - return false; -} - -bool -BaseProxyHandler::watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH, - proxy->getClass()->name); - return false; -} - -bool -BaseProxyHandler::unwatch(JSContext *cx, HandleObject proxy, HandleId id) const -{ - return true; -} - -bool -BaseProxyHandler::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, - HandleObject result) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, GET); - - return js::SliceSlowly(cx, proxy, proxy, begin, end, result); -} - bool DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const From 4a4cc447283af8f68a921179dd94c9a9ccccad67 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:45 -0700 Subject: [PATCH 56/69] Bug 1030192 - Part 3: Factor out DirectProxyHandler. (r=bholley) --- js/src/moz.build | 1 + js/src/proxy/DirectProxyHandler.cpp | 250 ++++++++++++++++++++++++++++ js/src/proxy/jsproxy.cpp | 236 -------------------------- 3 files changed, 251 insertions(+), 236 deletions(-) create mode 100644 js/src/proxy/DirectProxyHandler.cpp diff --git a/js/src/moz.build b/js/src/moz.build index a6a1ea85c2e3..687268beccaa 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -232,6 +232,7 @@ UNIFIED_SOURCES += [ 'perf/jsperf.cpp', 'prmjtime.cpp', 'proxy/BaseProxyHandler.cpp', + 'proxy/DirectProxyHandler.cpp', 'proxy/jsproxy.cpp', 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', diff --git a/js/src/proxy/DirectProxyHandler.cpp b/js/src/proxy/DirectProxyHandler.cpp new file mode 100644 index 000000000000..c53a2a4650af --- /dev/null +++ b/js/src/proxy/DirectProxyHandler.cpp @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsproxy.h" +#include "jswrapper.h" // UncheckedUnwrap + +#include "vm/ProxyObject.h" + +#include "jsobjinlines.h" + +using namespace js; + +bool +DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. + RootedObject target(cx, proxy->as().target()); + return JS_GetPropertyDescriptorById(cx, target, id, desc); +} + +bool +DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR); + RootedObject target(cx, proxy->as().target()); + return js::GetOwnPropertyDescriptor(cx, target, id, desc); +} + +bool +DirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + assertEnteredPolicy(cx, proxy, id, SET); + RootedObject target(cx, proxy->as().target()); + RootedValue v(cx, desc.value()); + return CheckDefineProperty(cx, target, id, v, desc.attributes(), desc.getter(), desc.setter()) && + JS_DefinePropertyById(cx, target, id, v, desc.attributes(), desc.getter(), desc.setter()); +} + +bool +DirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); + RootedObject target(cx, proxy->as().target()); + return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props); +} + +bool +DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + assertEnteredPolicy(cx, proxy, id, SET); + RootedObject target(cx, proxy->as().target()); + return JSObject::deleteGeneric(cx, target, id, bp); +} + +bool +DirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. + RootedObject target(cx, proxy->as().target()); + return GetPropertyNames(cx, target, 0, &props); +} + +bool +DirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); + RootedValue target(cx, proxy->as().private_()); + return Invoke(cx, args.thisv(), target, args.length(), args.array(), args.rval()); +} + +bool +DirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); + RootedValue target(cx, proxy->as().private_()); + return InvokeConstructor(cx, target, args.length(), args.array(), args.rval().address()); +} + +bool +DirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const +{ + args.setThis(ObjectValue(*args.thisv().toObject().as().target())); + if (!test(args.thisv())) { + ReportIncompatible(cx, args); + return false; + } + + return CallNativeImpl(cx, impl, args); +} + +bool +DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, + bool *bp) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, GET); + bool b; + RootedObject target(cx, proxy->as().target()); + if (!HasInstance(cx, target, v, &b)) + return false; + *bp = !!b; + return true; +} + +bool +DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const +{ + RootedObject target(cx, proxy->as().target()); + return JSObject::getProto(cx, target, protop); +} + +bool +DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const +{ + RootedObject target(cx, proxy->as().target()); + return JSObject::setProto(cx, target, proto, bp); +} + +bool +DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, + JSContext *cx) const +{ + RootedObject target(cx, proxy->as().target()); + return ObjectClassIs(target, classValue, cx); +} + +const char * +DirectProxyHandler::className(JSContext *cx, HandleObject proxy) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, GET); + RootedObject target(cx, proxy->as().target()); + return JSObject::className(cx, target); +} + +JSString * +DirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, + unsigned indent) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, GET); + RootedObject target(cx, proxy->as().target()); + return fun_toStringHelper(cx, target, indent); +} + +bool +DirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, + RegExpGuard *g) const +{ + RootedObject target(cx, proxy->as().target()); + return RegExpToShared(cx, target, g); +} + +bool +DirectProxyHandler::boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const +{ + RootedObject target(cx, proxy->as().target()); + return Unbox(cx, target, vp); +} + +JSObject * +DirectProxyHandler::weakmapKeyDelegate(JSObject *proxy) const +{ + return UncheckedUnwrap(proxy); +} + +bool +DirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + assertEnteredPolicy(cx, proxy, id, GET); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. + bool found; + RootedObject target(cx, proxy->as().target()); + if (!JS_HasPropertyById(cx, target, id, &found)) + return false; + *bp = !!found; + return true; +} + +bool +DirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + // Note: Proxy::set needs to invoke hasOwn to determine where the setter + // lives, so we allow SET operations to invoke us. + assertEnteredPolicy(cx, proxy, id, GET | SET); + RootedObject target(cx, proxy->as().target()); + Rooted desc(cx); + if (!JS_GetPropertyDescriptorById(cx, target, id, &desc)) + return false; + *bp = (desc.object() == target); + return true; +} + +bool +DirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, MutableHandleValue vp) const +{ + assertEnteredPolicy(cx, proxy, id, GET); + RootedObject target(cx, proxy->as().target()); + return JSObject::getGeneric(cx, target, receiver, id, vp); +} + +bool +DirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, bool strict, MutableHandleValue vp) const +{ + assertEnteredPolicy(cx, proxy, id, SET); + RootedObject target(cx, proxy->as().target()); + return JSObject::setGeneric(cx, target, receiver, id, vp, strict); +} + +bool +DirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); + RootedObject target(cx, proxy->as().target()); + return GetPropertyNames(cx, target, JSITER_OWNONLY, &props); +} + +bool +DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, + MutableHandleValue vp) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. + RootedObject target(cx, proxy->as().target()); + return GetIterator(cx, target, flags, vp); +} + +bool +DirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const +{ + RootedObject target(cx, proxy->as().target()); + return JSObject::isExtensible(cx, target, extensible); +} + +bool +DirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const +{ + RootedObject target(cx, proxy->as().target()); + return JSObject::preventExtensions(cx, target); +} diff --git a/js/src/proxy/jsproxy.cpp b/js/src/proxy/jsproxy.cpp index 178a4e65173a..b74a99bb9843 100644 --- a/js/src/proxy/jsproxy.cpp +++ b/js/src/proxy/jsproxy.cpp @@ -83,242 +83,6 @@ js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id, } #endif -bool -DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR); - JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. - RootedObject target(cx, proxy->as().target()); - return JS_GetPropertyDescriptorById(cx, target, id, desc); -} - -bool -DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR); - RootedObject target(cx, proxy->as().target()); - return js::GetOwnPropertyDescriptor(cx, target, id, desc); -} - -bool -DirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - assertEnteredPolicy(cx, proxy, id, SET); - RootedObject target(cx, proxy->as().target()); - RootedValue v(cx, desc.value()); - return CheckDefineProperty(cx, target, id, v, desc.attributes(), desc.getter(), desc.setter()) && - JS_DefinePropertyById(cx, target, id, v, desc.attributes(), desc.getter(), desc.setter()); -} - -bool -DirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); - RootedObject target(cx, proxy->as().target()); - return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props); -} - -bool -DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - assertEnteredPolicy(cx, proxy, id, SET); - RootedObject target(cx, proxy->as().target()); - return JSObject::deleteGeneric(cx, target, id, bp); -} - -bool -DirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); - JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. - RootedObject target(cx, proxy->as().target()); - return GetPropertyNames(cx, target, 0, &props); -} - -bool -DirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); - RootedValue target(cx, proxy->as().private_()); - return Invoke(cx, args.thisv(), target, args.length(), args.array(), args.rval()); -} - -bool -DirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); - RootedValue target(cx, proxy->as().private_()); - return InvokeConstructor(cx, target, args.length(), args.array(), args.rval().address()); -} - -bool -DirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) const -{ - args.setThis(ObjectValue(*args.thisv().toObject().as().target())); - if (!test(args.thisv())) { - ReportIncompatible(cx, args); - return false; - } - - return CallNativeImpl(cx, impl, args); -} - -bool -DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, - bool *bp) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, GET); - bool b; - RootedObject target(cx, proxy->as().target()); - if (!HasInstance(cx, target, v, &b)) - return false; - *bp = !!b; - return true; -} - -bool -DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const -{ - RootedObject target(cx, proxy->as().target()); - return JSObject::getProto(cx, target, protop); -} - -bool -DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const -{ - RootedObject target(cx, proxy->as().target()); - return JSObject::setProto(cx, target, proto, bp); -} - -bool -DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, - JSContext *cx) const -{ - RootedObject target(cx, proxy->as().target()); - return ObjectClassIs(target, classValue, cx); -} - -const char * -DirectProxyHandler::className(JSContext *cx, HandleObject proxy) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, GET); - RootedObject target(cx, proxy->as().target()); - return JSObject::className(cx, target); -} - -JSString * -DirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, - unsigned indent) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, GET); - RootedObject target(cx, proxy->as().target()); - return fun_toStringHelper(cx, target, indent); -} - -bool -DirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, - RegExpGuard *g) const -{ - RootedObject target(cx, proxy->as().target()); - return RegExpToShared(cx, target, g); -} - -bool -DirectProxyHandler::boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const -{ - RootedObject target(cx, proxy->as().target()); - return Unbox(cx, target, vp); -} - -JSObject * -DirectProxyHandler::weakmapKeyDelegate(JSObject *proxy) const -{ - return UncheckedUnwrap(proxy); -} - -bool -DirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - assertEnteredPolicy(cx, proxy, id, GET); - JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. - bool found; - RootedObject target(cx, proxy->as().target()); - if (!JS_HasPropertyById(cx, target, id, &found)) - return false; - *bp = !!found; - return true; -} - -bool -DirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - // Note: Proxy::set needs to invoke hasOwn to determine where the setter - // lives, so we allow SET operations to invoke us. - assertEnteredPolicy(cx, proxy, id, GET | SET); - RootedObject target(cx, proxy->as().target()); - Rooted desc(cx); - if (!JS_GetPropertyDescriptorById(cx, target, id, &desc)) - return false; - *bp = (desc.object() == target); - return true; -} - -bool -DirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, MutableHandleValue vp) const -{ - assertEnteredPolicy(cx, proxy, id, GET); - RootedObject target(cx, proxy->as().target()); - return JSObject::getGeneric(cx, target, receiver, id, vp); -} - -bool -DirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const -{ - assertEnteredPolicy(cx, proxy, id, SET); - RootedObject target(cx, proxy->as().target()); - return JSObject::setGeneric(cx, target, receiver, id, vp, strict); -} - -bool -DirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); - RootedObject target(cx, proxy->as().target()); - return GetPropertyNames(cx, target, JSITER_OWNONLY, &props); -} - -bool -DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, - MutableHandleValue vp) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE); - JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. - RootedObject target(cx, proxy->as().target()); - return GetIterator(cx, target, flags, vp); -} - -bool -DirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const -{ - RootedObject target(cx, proxy->as().target()); - return JSObject::isExtensible(cx, target, extensible); -} - -bool -DirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const -{ - RootedObject target(cx, proxy->as().target()); - return JSObject::preventExtensions(cx, target); -} - static bool GetFundamentalTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, MutableHandleValue fvalp) From 1d90da9992d9f700700c6292555499d0aec9e6fb Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:45 -0700 Subject: [PATCH 57/69] Bug 1031092 - Part 4: Factor out ScriptedDirectProxyHandler. (r=bholley) --- js/src/moz.build | 1 + js/src/proxy/ScriptedDirectProxyHandler.cpp | 1170 ++++++++++++++++++ js/src/proxy/ScriptedDirectProxyHandler.h | 78 ++ js/src/proxy/jsproxy.cpp | 1211 +------------------ 4 files changed, 1250 insertions(+), 1210 deletions(-) create mode 100644 js/src/proxy/ScriptedDirectProxyHandler.cpp create mode 100644 js/src/proxy/ScriptedDirectProxyHandler.h diff --git a/js/src/moz.build b/js/src/moz.build index 687268beccaa..a2700332626f 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -234,6 +234,7 @@ UNIFIED_SOURCES += [ 'proxy/BaseProxyHandler.cpp', 'proxy/DirectProxyHandler.cpp', 'proxy/jsproxy.cpp', + 'proxy/ScriptedDirectProxyHandler.cpp', 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', 'vm/CallNonGenericMethod.cpp', diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp new file mode 100644 index 000000000000..dad7d116e45e --- /dev/null +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -0,0 +1,1170 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "proxy/ScriptedDirectProxyHandler.h" + +#include "jsapi.h" + +#include "vm/PropDesc.h" + +#include "jsobjinlines.h" + +using namespace js; +using mozilla::ArrayLength; + +static inline bool +IsDataDescriptor(const PropertyDescriptor &desc) +{ + return desc.obj && !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)); +} + +static inline bool +IsAccessorDescriptor(const PropertyDescriptor &desc) +{ + return desc.obj && desc.attrs & (JSPROP_GETTER | JSPROP_SETTER); +} + +// ES6 (5 April 2014) ValidateAndApplyPropertyDescriptor(O, P, Extensible, Desc, Current) +// Since we are actually performing 9.1.6.2 IsCompatiblePropertyDescriptor(Extensible, Desc, +// Current), some parameters are omitted. +static bool +ValidatePropertyDescriptor(JSContext *cx, bool extensible, Handle desc, + Handle current, bool *bp) +{ + // step 2 + if (!current.object()) { + // Since |O| is always undefined, substeps c and d fall away. + *bp = extensible; + return true; + } + + // step 3 + if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGet() && !desc.hasSet() && + !desc.hasEnumerable() && !desc.hasConfigurable()) + { + *bp = true; + return true; + } + + // step 4 + if ((!desc.hasWritable() || desc.writable() == !current.isReadonly()) && + (!desc.hasGet() || desc.getter() == current.getter()) && + (!desc.hasSet() || desc.setter() == current.setter()) && + (!desc.hasEnumerable() || desc.enumerable() == current.isEnumerable()) && + (!desc.hasConfigurable() || desc.configurable() == !current.isPermanent())) + { + if (!desc.hasValue()) { + *bp = true; + return true; + } + bool same = false; + if (!SameValue(cx, desc.value(), current.value(), &same)) + return false; + if (same) { + *bp = true; + return true; + } + } + + // step 5 + if (current.isPermanent()) { + if (desc.hasConfigurable() && desc.configurable()) { + *bp = false; + return true; + } + + if (desc.hasEnumerable() && + desc.enumerable() != current.isEnumerable()) + { + *bp = false; + return true; + } + } + + // step 6 + if (desc.isGenericDescriptor()) { + *bp = true; + return true; + } + + // step 7a + if (IsDataDescriptor(current) != desc.isDataDescriptor()) { + *bp = !current.isPermanent(); + return true; + } + + // step 8 + if (IsDataDescriptor(current)) { + JS_ASSERT(desc.isDataDescriptor()); // by step 7a + if (current.isPermanent() && current.isReadonly()) { + if (desc.hasWritable() && desc.writable()) { + *bp = false; + return true; + } + + if (desc.hasValue()) { + bool same; + if (!SameValue(cx, desc.value(), current.value(), &same)) + return false; + if (!same) { + *bp = false; + return true; + } + } + } + + *bp = true; + return true; + } + + // step 9 + JS_ASSERT(IsAccessorDescriptor(current)); // by step 8 + JS_ASSERT(desc.isAccessorDescriptor()); // by step 7a + *bp = (!current.isPermanent() || + ((!desc.hasSet() || desc.setter() == current.setter()) && + (!desc.hasGet() || desc.getter() == current.getter()))); + return true; +} + +// Aux.6 IsSealed(O, P) +static bool +IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool *bp) +{ + // step 1 + Rooted desc(cx); + if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) + return false; + + // steps 2-3 + *bp = desc.object() && desc.isPermanent(); + return true; +} + +static bool +HasOwn(JSContext *cx, HandleObject obj, HandleId id, bool *bp) +{ + Rooted desc(cx); + if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc)) + return false; + *bp = (desc.object() == obj); + return true; +} + +// Get the [[ProxyHandler]] of a scripted direct proxy. +static JSObject * +GetDirectProxyHandlerObject(JSObject *proxy) +{ + JS_ASSERT(proxy->as().handler() == &ScriptedDirectProxyHandler::singleton); + return proxy->as().extra(ScriptedDirectProxyHandler::HANDLER_EXTRA).toObjectOrNull(); +} + +static inline void +ReportInvalidTrapResult(JSContext *cx, JSObject *proxy, JSAtom *atom) +{ + RootedValue v(cx, ObjectOrNullValue(proxy)); + JSAutoByteString bytes; + if (!AtomToPrintableString(cx, atom, &bytes)) + return; + js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK, v, + js::NullPtr(), bytes.ptr()); +} + +// This function is shared between getOwnPropertyNames, enumerate, and keys +static bool +ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleValue v, + AutoIdVector &props, unsigned flags, JSAtom *trapName_) +{ + JS_ASSERT(v.isObject()); + RootedObject array(cx, &v.toObject()); + RootedAtom trapName(cx, trapName_); + + // steps g-h + uint32_t n; + if (!GetLengthProperty(cx, array, &n)) + return false; + + // steps i-k + for (uint32_t i = 0; i < n; ++i) { + // step i + RootedValue v(cx); + if (!JSObject::getElement(cx, array, array, i, &v)) + return false; + + // step ii + RootedId id(cx); + if (!ValueToId(cx, v, &id)) + return false; + + // step iii + for (uint32_t j = 0; j < i; ++j) { + if (props[j].get() == id) { + ReportInvalidTrapResult(cx, proxy, trapName); + return false; + } + } + + // step iv + bool isFixed; + if (!HasOwn(cx, target, id, &isFixed)) + return false; + + // step v + bool extensible; + if (!JSObject::isExtensible(cx, target, &extensible)) + return false; + if (!extensible && !isFixed) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW); + return false; + } + + // step vi + if (!props.append(id)) + return false; + } + + // step l + AutoIdVector ownProps(cx); + if (!GetPropertyNames(cx, target, flags, &ownProps)) + return false; + + // step m + for (size_t i = 0; i < ownProps.length(); ++i) { + RootedId id(cx, ownProps[i]); + + bool found = false; + for (size_t j = 0; j < props.length(); ++j) { + if (props[j].get() == id) { + found = true; + break; + } + } + if (found) + continue; + + // step i + bool sealed; + if (!IsSealed(cx, target, id, &sealed)) + return false; + if (sealed) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC); + return false; + } + + // step ii + bool isFixed; + if (!HasOwn(cx, target, id, &isFixed)) + return false; + + // step iii + bool extensible; + if (!JSObject::isExtensible(cx, target, &extensible)) + return false; + if (!extensible && isFixed) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); + return false; + } + } + + // step n + return true; +} + +// ES6 (22 May, 2014) 9.5.4 Proxy.[[PreventExtensions]]() +bool +ScriptedDirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const +{ + // step 1 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 2 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 3 + RootedObject target(cx, proxy->as().target()); + + // step 4-5 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().preventExtensions, &trap)) + return false; + + // step 6 + if (trap.isUndefined()) + return DirectProxyHandler::preventExtensions(cx, proxy); + + // step 7, 9 + Value argv[] = { + ObjectValue(*target) + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 8 + bool success = ToBoolean(trapResult); + if (success) { + // step 10 + bool extensible; + if (!JSObject::isExtensible(cx, target, &extensible)) + return false; + if (extensible) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE); + return false; + } + // step 11 "return true" + return true; + } + + // step 11 "return false" + // This actually corresponds to 19.1.2.5 step 4. We cannot pass the failure back, so throw here + // directly instead. + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); + return false; +} + +// Corresponds to the "standard" property descriptor getOwn getPrototypeOf dance. It's so explicit +// here because ScriptedDirectProxyHandler allows script visibility for this operation. +bool +ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + JS_CHECK_RECURSION(cx, return false); + + if (!GetOwnPropertyDescriptor(cx, proxy, id, desc)) + return false; + if (desc.object()) + return true; + RootedObject proto(cx); + if (!JSObject::getProto(cx, proxy, &proto)) + return false; + if (!proto) { + JS_ASSERT(!desc.object()); + return true; + } + return JS_GetPropertyDescriptorById(cx, proto, id, desc); +} + +// ES6 (5 April 2014) 9.5.5 Proxy.[[GetOwnProperty]](P) +bool +ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + // step 2 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 3 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 4 + RootedObject target(cx, proxy->as().target()); + + // step 5-6 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().getOwnPropertyDescriptor, &trap)) + return false; + + // step 7 + if (trap.isUndefined()) + return DirectProxyHandler::getOwnPropertyDescriptor(cx, proxy, id, desc); + + // step 8-9 + RootedValue propKey(cx); + if (!IdToStringOrSymbol(cx, id, &propKey)) + return false; + + Value argv[] = { + ObjectValue(*target), + propKey + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 10 + if (!trapResult.isUndefined() && !trapResult.isObject()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_GETOWN_OBJORUNDEF); + return false; + } + + //step 11-12 + Rooted targetDesc(cx); + if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) + return false; + + // step 13 + if (trapResult.isUndefined()) { + // substep a + if (!targetDesc.object()) { + desc.object().set(nullptr); + return true; + } + + // substep b + if (targetDesc.isPermanent()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE); + return false; + } + + // substep c-e + bool extensibleTarget; + if (!JSObject::isExtensible(cx, target, &extensibleTarget)) + return false; + if (!extensibleTarget) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); + return false; + } + + // substep f + desc.object().set(nullptr); + return true; + } + + // step 14-15 + bool extensibleTarget; + if (!JSObject::isExtensible(cx, target, &extensibleTarget)) + return false; + + // step 16-17 + Rooted resultDesc(cx); + if (!resultDesc.initialize(cx, trapResult)) + return false; + + // step 18 + resultDesc.complete(); + + // step 19 + bool valid; + if (!ValidatePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc, &valid)) + return false; + + // step 20 + if (!valid) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_INVALID); + return false; + } + + // step 21 + if (!resultDesc.configurable()) { + if (!targetDesc.object()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC); + return false; + } + + if (!targetDesc.isPermanent()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_C_AS_NC); + return false; + } + } + + // step 22 + resultDesc.populatePropertyDescriptor(proxy, desc); + return true; +} + +// ES6 (5 April 2014) 9.5.6 Proxy.[[DefineOwnProperty]](O,P) +bool +ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + // step 2 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 3 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 4 + RootedObject target(cx, proxy->as().target()); + + // step 5-6 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().defineProperty, &trap)) + return false; + + // step 7 + if (trap.isUndefined()) + return DirectProxyHandler::defineProperty(cx, proxy, id, desc); + + // step 8-9 + RootedValue descObj(cx); + if (!NewPropertyDescriptorObject(cx, desc, &descObj)) + return false; + + // step 10, 12 + RootedValue propKey(cx); + if (!IdToStringOrSymbol(cx, id, &propKey)) + return false; + + Value argv[] = { + ObjectValue(*target), + propKey, + descObj + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 11, 13 + if (ToBoolean(trapResult)) { + // step 14-15 + Rooted targetDesc(cx); + if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) + return false; + + // step 16-17 + bool extensibleTarget; + if (!JSObject::isExtensible(cx, target, &extensibleTarget)) + return false; + + // step 18-19 + bool settingConfigFalse = desc.isPermanent(); + if (!targetDesc.object()) { + // step 20a + if (!extensibleTarget) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW); + return false; + } + // step 20b + if (settingConfigFalse) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC); + return false; + } + } else { + // step 21 + bool valid; + Rooted pd(cx); + pd.initFromPropertyDescriptor(desc); + if (!ValidatePropertyDescriptor(cx, extensibleTarget, pd, targetDesc, &valid)) + return false; + if (!valid || (settingConfigFalse && !targetDesc.isPermanent())) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID); + return false; + } + } + } + + // [[DefineProperty]] should return a boolean value, which is used to do things like + // strict-mode throwing. At present, the engine is not prepared to do that. See bug 826587. + return true; +} + +// This is secretly [[OwnPropertyKeys]]. SM uses the old wiki name, internally. +// ES6 (5 April 2014) 9.5.12 Proxy.[[OwnPropertyKeys]]() +bool +ScriptedDirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const +{ + // step 1 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 2 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 3 + RootedObject target(cx, proxy->as().target()); + + // step 4-5 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().ownKeys, &trap)) + return false; + + // step 6 + if (trap.isUndefined()) + return DirectProxyHandler::getOwnPropertyNames(cx, proxy, props); + + // step 7-8 + Value argv[] = { + ObjectValue(*target) + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 9 + if (trapResult.isPrimitive()) { + ReportInvalidTrapResult(cx, proxy, cx->names().ownKeys); + return false; + } + + // Here we add a bunch of extra sanity checks. It is unclear if they will also appear in + // the spec. See step 10-11 + return ArrayToIdVector(cx, proxy, target, trapResult, props, + JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, + cx->names().getOwnPropertyNames); +} + +// ES6 (5 April 2014) 9.5.10 Proxy.[[Delete]](P) +bool +ScriptedDirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + // step 2 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 3 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 4 + RootedObject target(cx, proxy->as().target()); + + // step 5 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().deleteProperty, &trap)) + return false; + + // step 7 + if (trap.isUndefined()) + return DirectProxyHandler::delete_(cx, proxy, id, bp); + + // step 8 + RootedValue value(cx); + if (!IdToStringOrSymbol(cx, id, &value)) + return false; + Value argv[] = { + ObjectValue(*target), + value + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 9 + if (ToBoolean(trapResult)) { + // step 12 + Rooted desc(cx); + if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) + return false; + + // step 14-15 + if (desc.object() && desc.isPermanent()) { + RootedValue v(cx, IdToValue(id)); + js_ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, js::NullPtr()); + return false; + } + + // step 16 + *bp = true; + return true; + } + + // step 11 + *bp = false; + return true; +} + +// ES6 (22 May, 2014) 9.5.11 Proxy.[[Enumerate]] +bool +ScriptedDirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +{ + // step 1 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 2 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 3 + RootedObject target(cx, proxy->as().target()); + + // step 4-5 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().enumerate, &trap)) + return false; + + // step 6 + if (trap.isUndefined()) + return DirectProxyHandler::enumerate(cx, proxy, props); + + // step 7-8 + Value argv[] = { + ObjectOrNullValue(target) + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 9 + if (trapResult.isPrimitive()) { + JSAutoByteString bytes; + if (!AtomToPrintableString(cx, cx->names().enumerate, &bytes)) + return false; + RootedValue v(cx, ObjectOrNullValue(proxy)); + js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_SEARCH_STACK, + v, js::NullPtr(), bytes.ptr()); + return false; + } + + // step 10 + // FIXME: the trap should return an iterator object, see bug 783826. Since this isn't very + // useful for us internally, we convery to an id vector. + return ArrayToIdVector(cx, proxy, target, trapResult, props, 0, cx->names().enumerate); +} + +// ES6 (22 May, 2014) 9.5.7 Proxy.[[HasProperty]](P) +bool +ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + // step 2 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 3 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 4 + RootedObject target(cx, proxy->as().target()); + + // step 5-6 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().has, &trap)) + return false; + + // step 7 + if (trap.isUndefined()) + return DirectProxyHandler::has(cx, proxy, id, bp); + + // step 8,10 + RootedValue value(cx); + if (!IdToStringOrSymbol(cx, id, &value)) + return false; + Value argv[] = { + ObjectOrNullValue(target), + value + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 9 + bool success = ToBoolean(trapResult); + + // step 11 + if (!success) { + Rooted desc(cx); + if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) + return false; + + if (desc.object()) { + if (desc.isPermanent()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE); + return false; + } + + bool extensible; + if (!JSObject::isExtensible(cx, target, &extensible)) + return false; + if (!extensible) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); + return false; + } + } + } + + // step 12 + *bp = success; + return true; +} + +// ES6 (22 May, 2014) 9.5.8 Proxy.[[GetP]](P, Receiver) +bool +ScriptedDirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, MutableHandleValue vp) const +{ + // step 2 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 3 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 4 + RootedObject target(cx, proxy->as().target()); + + // step 5-6 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().get, &trap)) + return false; + + // step 7 + if (trap.isUndefined()) + return DirectProxyHandler::get(cx, proxy, receiver, id, vp); + + // step 8-9 + RootedValue value(cx); + if (!IdToStringOrSymbol(cx, id, &value)) + return false; + Value argv[] = { + ObjectOrNullValue(target), + value, + ObjectOrNullValue(receiver) + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 10-11 + Rooted desc(cx); + if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) + return false; + + // step 12 + if (desc.object()) { + if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) { + bool same; + if (!SameValue(cx, trapResult, desc.value(), &same)) + return false; + if (!same) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MUST_REPORT_SAME_VALUE); + return false; + } + } + + if (IsAccessorDescriptor(desc) && desc.isPermanent() && !desc.hasGetterObject()) { + if (!trapResult.isUndefined()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED); + return false; + } + } + } + + // step 13 + vp.set(trapResult); + return true; +} + +// ES6 (22 May, 2014) 9.5.9 Proxy.[[SetP]](P, V, Receiver) +bool +ScriptedDirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, bool strict, MutableHandleValue vp) const +{ + // step 2 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 3 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 4 + RootedObject target(cx, proxy->as().target()); + + // step 5-6 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().set, &trap)) + return false; + + // step 7 + if (trap.isUndefined()) + return DirectProxyHandler::set(cx, proxy, receiver, id, strict, vp); + + // step 8,10 + RootedValue value(cx); + if (!IdToStringOrSymbol(cx, id, &value)) + return false; + Value argv[] = { + ObjectOrNullValue(target), + value, + vp.get(), + ObjectValue(*receiver) + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 9 + bool success = ToBoolean(trapResult); + + if (success) { + // step 12-13 + Rooted desc(cx); + if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) + return false; + + // step 14 + if (desc.object()) { + if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) { + bool same; + if (!SameValue(cx, vp, desc.value(), &same)) + return false; + if (!same) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_NW_NC); + return false; + } + } + + if (IsAccessorDescriptor(desc) && desc.isPermanent() && !desc.hasSetterObject()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER); + return false; + } + } + } + + // step 11, 15 + vp.set(BooleanValue(success)); + return true; +} + +// ES6 (5 April, 2014) 9.5.3 Proxy.[[IsExtensible]]() +bool +ScriptedDirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const +{ + // step 1 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 2 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 3 + RootedObject target(cx, proxy->as().target()); + + // step 4-5 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().isExtensible, &trap)) + return false; + + // step 6 + if (trap.isUndefined()) + return DirectProxyHandler::isExtensible(cx, proxy, extensible); + + // step 7, 9 + Value argv[] = { + ObjectValue(*target) + }; + RootedValue trapResult(cx); + if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) + return false; + + // step 8 + bool booleanTrapResult = ToBoolean(trapResult); + + // step 10-11 + bool targetResult; + if (!JSObject::isExtensible(cx, target, &targetResult)) + return false; + + // step 12 + if (targetResult != booleanTrapResult) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY); + return false; + } + + // step 13 + *extensible = booleanTrapResult; + return true; +} + +bool +ScriptedDirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, + MutableHandleValue vp) const +{ + // FIXME: Provide a proper implementation for this trap, see bug 787004 + return DirectProxyHandler::iterate(cx, proxy, flags, vp); +} + +// ES6 (22 May, 2014) 9.5.13 Proxy.[[Call]] +bool +ScriptedDirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const +{ + // step 1 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 2 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 3 + RootedObject target(cx, proxy->as().target()); + + /* + * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get + * called for non-callable objects. + */ + + // step 7 + RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); + if (!argsArray) + return false; + + // step 4-5 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().apply, &trap)) + return false; + + // step 6 + if (trap.isUndefined()) + return DirectProxyHandler::call(cx, proxy, args); + + // step 8 + Value argv[] = { + ObjectValue(*target), + args.thisv(), + ObjectValue(*argsArray) + }; + RootedValue thisValue(cx, ObjectValue(*handler)); + return Invoke(cx, thisValue, trap, ArrayLength(argv), argv, args.rval()); +} + +// ES6 (22 May, 2014) 9.5.14 Proxy.[[Construct]] +bool +ScriptedDirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const +{ + // step 1 + RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); + + // step 2 + if (!handler) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); + return false; + } + + // step 3 + RootedObject target(cx, proxy->as().target()); + + /* + * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get + * called for non-callable objects. FIXME: See bug 1041756 + */ + + // step 7 + RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); + if (!argsArray) + return false; + + // step 4-5 + RootedValue trap(cx); + if (!JSObject::getProperty(cx, handler, handler, cx->names().construct, &trap)) + return false; + + // step 6 + if (trap.isUndefined()) + return DirectProxyHandler::construct(cx, proxy, args); + + // step 8-9 + Value constructArgv[] = { + ObjectValue(*target), + ObjectValue(*argsArray) + }; + RootedValue thisValue(cx, ObjectValue(*handler)); + if (!Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval())) + return false; + + // step 10 + if (!args.rval().isObject()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT); + return false; + } + return true; +} + +const char ScriptedDirectProxyHandler::family = 0; +const ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton; + +bool +js::proxy(JSContext *cx, unsigned argc, jsval *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 2) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, + "Proxy", "1", "s"); + return false; + } + RootedObject target(cx, NonNullObject(cx, args[0])); + if (!target) + return false; + RootedObject handler(cx, NonNullObject(cx, args[1])); + if (!handler) + return false; + RootedValue priv(cx, ObjectValue(*target)); + ProxyOptions options; + options.selectDefaultClass(target->isCallable()); + ProxyObject *proxy = + ProxyObject::New(cx, &ScriptedDirectProxyHandler::singleton, + priv, TaggedProto(TaggedProto::LazyProto), cx->global(), + options); + if (!proxy) + return false; + proxy->setExtra(ScriptedDirectProxyHandler::HANDLER_EXTRA, ObjectValue(*handler)); + args.rval().setObject(*proxy); + return true; +} + +static bool +RevokeProxy(JSContext *cx, unsigned argc, Value *vp) +{ + CallReceiver rec = CallReceiverFromVp(vp); + + RootedFunction func(cx, &rec.callee().as()); + RootedObject p(cx, func->getExtendedSlot(ScriptedDirectProxyHandler::REVOKE_SLOT).toObjectOrNull()); + + if (p) { + func->setExtendedSlot(ScriptedDirectProxyHandler::REVOKE_SLOT, NullValue()); + + MOZ_ASSERT(p->is()); + + p->as().setSameCompartmentPrivate(NullValue()); + p->as().setExtra(ScriptedDirectProxyHandler::HANDLER_EXTRA, NullValue()); + } + + rec.rval().setUndefined(); + return true; +} + +bool +js::proxy_revocable(JSContext *cx, unsigned argc, Value *vp) +{ + CallReceiver args = CallReceiverFromVp(vp); + + if (!proxy(cx, argc, vp)) + return false; + + RootedValue proxyVal(cx, args.rval()); + MOZ_ASSERT(proxyVal.toObject().is()); + + RootedObject revoker(cx, NewFunctionByIdWithReserved(cx, RevokeProxy, 0, 0, cx->global(), + AtomToId(cx->names().revoke))); + if (!revoker) + return false; + + revoker->as().initExtendedSlot(ScriptedDirectProxyHandler::REVOKE_SLOT, proxyVal); + + RootedObject result(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); + if (!result) + return false; + + RootedValue revokeVal(cx, ObjectValue(*revoker)); + if (!JSObject::defineProperty(cx, result, cx->names().proxy, proxyVal) || + !JSObject::defineProperty(cx, result, cx->names().revoke, revokeVal)) + { + return false; + } + + args.rval().setObject(*result); + return true; +} diff --git a/js/src/proxy/ScriptedDirectProxyHandler.h b/js/src/proxy/ScriptedDirectProxyHandler.h new file mode 100644 index 000000000000..02af5b74b5a3 --- /dev/null +++ b/js/src/proxy/ScriptedDirectProxyHandler.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef proxy_ScriptedDirectProxyHandler_h +#define proxy_ScriptedDirectProxyHandler_h + +#include "jsproxy.h" + +namespace js { + +/* Derived class for all scripted direct proxy handlers. */ +class ScriptedDirectProxyHandler : public DirectProxyHandler { + public: + MOZ_CONSTEXPR ScriptedDirectProxyHandler() + : DirectProxyHandler(&family) + { } + + /* ES5 Harmony fundamental proxy traps. */ + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; + virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; + + /* ES5 Harmony derived proxy traps. */ + virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE { + return BaseProxyHandler::hasOwn(cx, proxy, id, bp); + } + virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; + // Kick keys out to getOwnPropertyName and then filter. [[GetOwnProperty]] could potentially + // change the enumerability of the target's properties. + virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE { + return BaseProxyHandler::keys(cx, proxy, props); + } + virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, + MutableHandleValue vp) const MOZ_OVERRIDE; + + /* ES6 Harmony traps */ + virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; + + /* Spidermonkey extensions. */ + virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool isScripted() const MOZ_OVERRIDE { return true; } + + static const char family; + static const ScriptedDirectProxyHandler singleton; + + // The "proxy extra" slot index in which the handler is stored. Revocable proxies need to set + // this at revocation time. + static const int HANDLER_EXTRA = 0; + // The "function extended" slot index in which the revocation object is stored. Per spec, this + // is to be cleared during the first revocation. + static const int REVOKE_SLOT = 0; +}; + +bool +proxy(JSContext *cx, unsigned argc, jsval *vp); + +bool +proxy_revocable(JSContext *cx, unsigned argc, jsval *vp); + +} /* namespace js */ + +#endif /* proxy_ScriptedDirectProxyHandler_h */ diff --git a/js/src/proxy/jsproxy.cpp b/js/src/proxy/jsproxy.cpp index b74a99bb9843..3527a62dd454 100644 --- a/js/src/proxy/jsproxy.cpp +++ b/js/src/proxy/jsproxy.cpp @@ -15,6 +15,7 @@ #include "jswrapper.h" #include "gc/Marking.h" +#include "proxy/ScriptedDirectProxyHandler.h" #include "vm/WrapperObject.h" #include "jsatominlines.h" @@ -25,7 +26,6 @@ using namespace js; using namespace js::gc; -using mozilla::ArrayLength; void js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext *cx, jsid id) @@ -489,1132 +489,6 @@ ScriptedIndirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, un const ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton; -/* Derived class for all scripted direct proxy handlers. */ -class ScriptedDirectProxyHandler : public DirectProxyHandler { - public: - MOZ_CONSTEXPR ScriptedDirectProxyHandler() - : DirectProxyHandler(&family) - { } - - /* ES5 Harmony fundamental proxy traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; - - /* ES5 Harmony derived proxy traps. */ - virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE { - return BaseProxyHandler::hasOwn(cx, proxy, id, bp); - } - virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, - MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; - // Kick keys out to getOwnPropertyName and then filter. [[GetOwnProperty]] could potentially - // change the enumerability of the target's properties. - virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE { - return BaseProxyHandler::keys(cx, proxy, props); - } - virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, - MutableHandleValue vp) const MOZ_OVERRIDE; - - /* ES6 Harmony traps */ - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool isScripted() const MOZ_OVERRIDE { return true; } - - static const char family; - static const ScriptedDirectProxyHandler singleton; - - // The "proxy extra" slot index in which the handler is stored. Revocable proxies need to set - // this at revocation time. - static const int HANDLER_EXTRA = 0; - // The "function extended" slot index in which the revocation object is stored. Per spec, this - // is to be cleared during the first revocation. - static const int REVOKE_SLOT = 0; -}; - -static inline bool -IsDataDescriptor(const PropertyDescriptor &desc) -{ - return desc.obj && !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)); -} - -static inline bool -IsAccessorDescriptor(const PropertyDescriptor &desc) -{ - return desc.obj && desc.attrs & (JSPROP_GETTER | JSPROP_SETTER); -} - -// ES6 (5 April 2014) ValidateAndApplyPropertyDescriptor(O, P, Extensible, Desc, Current) -// Since we are actually performing 9.1.6.2 IsCompatiblePropertyDescriptor(Extensible, Desc, -// Current), some parameters are omitted. -static bool -ValidatePropertyDescriptor(JSContext *cx, bool extensible, Handle desc, - Handle current, bool *bp) -{ - // step 2 - if (!current.object()) { - // Since |O| is always undefined, substeps c and d fall away. - *bp = extensible; - return true; - } - - // step 3 - if (!desc.hasValue() && !desc.hasWritable() && !desc.hasGet() && !desc.hasSet() && - !desc.hasEnumerable() && !desc.hasConfigurable()) - { - *bp = true; - return true; - } - - // step 4 - if ((!desc.hasWritable() || desc.writable() == !current.isReadonly()) && - (!desc.hasGet() || desc.getter() == current.getter()) && - (!desc.hasSet() || desc.setter() == current.setter()) && - (!desc.hasEnumerable() || desc.enumerable() == current.isEnumerable()) && - (!desc.hasConfigurable() || desc.configurable() == !current.isPermanent())) - { - if (!desc.hasValue()) { - *bp = true; - return true; - } - bool same = false; - if (!SameValue(cx, desc.value(), current.value(), &same)) - return false; - if (same) { - *bp = true; - return true; - } - } - - // step 5 - if (current.isPermanent()) { - if (desc.hasConfigurable() && desc.configurable()) { - *bp = false; - return true; - } - - if (desc.hasEnumerable() && - desc.enumerable() != current.isEnumerable()) - { - *bp = false; - return true; - } - } - - // step 6 - if (desc.isGenericDescriptor()) { - *bp = true; - return true; - } - - // step 7a - if (IsDataDescriptor(current) != desc.isDataDescriptor()) { - *bp = !current.isPermanent(); - return true; - } - - // step 8 - if (IsDataDescriptor(current)) { - JS_ASSERT(desc.isDataDescriptor()); // by step 7a - if (current.isPermanent() && current.isReadonly()) { - if (desc.hasWritable() && desc.writable()) { - *bp = false; - return true; - } - - if (desc.hasValue()) { - bool same; - if (!SameValue(cx, desc.value(), current.value(), &same)) - return false; - if (!same) { - *bp = false; - return true; - } - } - } - - *bp = true; - return true; - } - - // step 9 - JS_ASSERT(IsAccessorDescriptor(current)); // by step 8 - JS_ASSERT(desc.isAccessorDescriptor()); // by step 7a - *bp = (!current.isPermanent() || - ((!desc.hasSet() || desc.setter() == current.setter()) && - (!desc.hasGet() || desc.getter() == current.getter()))); - return true; -} - -// Aux.6 IsSealed(O, P) -static bool -IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool *bp) -{ - // step 1 - Rooted desc(cx); - if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) - return false; - - // steps 2-3 - *bp = desc.object() && desc.isPermanent(); - return true; -} - -static bool -HasOwn(JSContext *cx, HandleObject obj, HandleId id, bool *bp) -{ - Rooted desc(cx); - if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc)) - return false; - *bp = (desc.object() == obj); - return true; -} - -// Get the [[ProxyHandler]] of a scripted direct proxy. -static JSObject * -GetDirectProxyHandlerObject(JSObject *proxy) -{ - JS_ASSERT(proxy->as().handler() == &ScriptedDirectProxyHandler::singleton); - return proxy->as().extra(ScriptedDirectProxyHandler::HANDLER_EXTRA).toObjectOrNull(); -} - -static inline void -ReportInvalidTrapResult(JSContext *cx, JSObject *proxy, JSAtom *atom) -{ - RootedValue v(cx, ObjectOrNullValue(proxy)); - JSAutoByteString bytes; - if (!AtomToPrintableString(cx, atom, &bytes)) - return; - js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK, v, - js::NullPtr(), bytes.ptr()); -} - -// This function is shared between getOwnPropertyNames, enumerate, and keys -static bool -ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleValue v, - AutoIdVector &props, unsigned flags, JSAtom *trapName_) -{ - JS_ASSERT(v.isObject()); - RootedObject array(cx, &v.toObject()); - RootedAtom trapName(cx, trapName_); - - // steps g-h - uint32_t n; - if (!GetLengthProperty(cx, array, &n)) - return false; - - // steps i-k - for (uint32_t i = 0; i < n; ++i) { - // step i - RootedValue v(cx); - if (!JSObject::getElement(cx, array, array, i, &v)) - return false; - - // step ii - RootedId id(cx); - if (!ValueToId(cx, v, &id)) - return false; - - // step iii - for (uint32_t j = 0; j < i; ++j) { - if (props[j].get() == id) { - ReportInvalidTrapResult(cx, proxy, trapName); - return false; - } - } - - // step iv - bool isFixed; - if (!HasOwn(cx, target, id, &isFixed)) - return false; - - // step v - bool extensible; - if (!JSObject::isExtensible(cx, target, &extensible)) - return false; - if (!extensible && !isFixed) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW); - return false; - } - - // step vi - if (!props.append(id)) - return false; - } - - // step l - AutoIdVector ownProps(cx); - if (!GetPropertyNames(cx, target, flags, &ownProps)) - return false; - - // step m - for (size_t i = 0; i < ownProps.length(); ++i) { - RootedId id(cx, ownProps[i]); - - bool found = false; - for (size_t j = 0; j < props.length(); ++j) { - if (props[j].get() == id) { - found = true; - break; - } - } - if (found) - continue; - - // step i - bool sealed; - if (!IsSealed(cx, target, id, &sealed)) - return false; - if (sealed) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC); - return false; - } - - // step ii - bool isFixed; - if (!HasOwn(cx, target, id, &isFixed)) - return false; - - // step iii - bool extensible; - if (!JSObject::isExtensible(cx, target, &extensible)) - return false; - if (!extensible && isFixed) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); - return false; - } - } - - // step n - return true; -} - -// ES6 (22 May, 2014) 9.5.4 Proxy.[[PreventExtensions]]() -bool -ScriptedDirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const -{ - // step 1 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 3 - RootedObject target(cx, proxy->as().target()); - - // step 4-5 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().preventExtensions, &trap)) - return false; - - // step 6 - if (trap.isUndefined()) - return DirectProxyHandler::preventExtensions(cx, proxy); - - // step 7, 9 - Value argv[] = { - ObjectValue(*target) - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 8 - bool success = ToBoolean(trapResult); - if (success) { - // step 10 - bool extensible; - if (!JSObject::isExtensible(cx, target, &extensible)) - return false; - if (extensible) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE); - return false; - } - // step 11 "return true" - return true; - } - - // step 11 "return false" - // This actually corresponds to 19.1.2.5 step 4. We cannot pass the failure back, so throw here - // directly instead. - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); - return false; -} - -// Corresponds to the "standard" property descriptor getOwn getPrototypeOf dance. It's so explicit -// here because ScriptedDirectProxyHandler allows script visibility for this operation. -bool -ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - JS_CHECK_RECURSION(cx, return false); - - if (!GetOwnPropertyDescriptor(cx, proxy, id, desc)) - return false; - if (desc.object()) - return true; - RootedObject proto(cx); - if (!JSObject::getProto(cx, proxy, &proto)) - return false; - if (!proto) { - JS_ASSERT(!desc.object()); - return true; - } - return JS_GetPropertyDescriptorById(cx, proto, id, desc); -} - -// ES6 (5 April 2014) 9.5.5 Proxy.[[GetOwnProperty]](P) -bool -ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - // step 2 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 4 - RootedObject target(cx, proxy->as().target()); - - // step 5-6 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().getOwnPropertyDescriptor, &trap)) - return false; - - // step 7 - if (trap.isUndefined()) - return DirectProxyHandler::getOwnPropertyDescriptor(cx, proxy, id, desc); - - // step 8-9 - RootedValue propKey(cx); - if (!IdToStringOrSymbol(cx, id, &propKey)) - return false; - - Value argv[] = { - ObjectValue(*target), - propKey - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 10 - if (!trapResult.isUndefined() && !trapResult.isObject()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_GETOWN_OBJORUNDEF); - return false; - } - - //step 11-12 - Rooted targetDesc(cx); - if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) - return false; - - // step 13 - if (trapResult.isUndefined()) { - // substep a - if (!targetDesc.object()) { - desc.object().set(nullptr); - return true; - } - - // substep b - if (targetDesc.isPermanent()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE); - return false; - } - - // substep c-e - bool extensibleTarget; - if (!JSObject::isExtensible(cx, target, &extensibleTarget)) - return false; - if (!extensibleTarget) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); - return false; - } - - // substep f - desc.object().set(nullptr); - return true; - } - - // step 14-15 - bool extensibleTarget; - if (!JSObject::isExtensible(cx, target, &extensibleTarget)) - return false; - - // step 16-17 - Rooted resultDesc(cx); - if (!resultDesc.initialize(cx, trapResult)) - return false; - - // step 18 - resultDesc.complete(); - - // step 19 - bool valid; - if (!ValidatePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc, &valid)) - return false; - - // step 20 - if (!valid) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_INVALID); - return false; - } - - // step 21 - if (!resultDesc.configurable()) { - if (!targetDesc.object()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC); - return false; - } - - if (!targetDesc.isPermanent()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_C_AS_NC); - return false; - } - } - - // step 22 - resultDesc.populatePropertyDescriptor(proxy, desc); - return true; -} - -// ES6 (5 April 2014) 9.5.6 Proxy.[[DefineOwnProperty]](O,P) -bool -ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - // step 2 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 4 - RootedObject target(cx, proxy->as().target()); - - // step 5-6 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().defineProperty, &trap)) - return false; - - // step 7 - if (trap.isUndefined()) - return DirectProxyHandler::defineProperty(cx, proxy, id, desc); - - // step 8-9 - RootedValue descObj(cx); - if (!NewPropertyDescriptorObject(cx, desc, &descObj)) - return false; - - // step 10, 12 - RootedValue propKey(cx); - if (!IdToStringOrSymbol(cx, id, &propKey)) - return false; - - Value argv[] = { - ObjectValue(*target), - propKey, - descObj - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 11, 13 - if (ToBoolean(trapResult)) { - // step 14-15 - Rooted targetDesc(cx); - if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) - return false; - - // step 16-17 - bool extensibleTarget; - if (!JSObject::isExtensible(cx, target, &extensibleTarget)) - return false; - - // step 18-19 - bool settingConfigFalse = desc.isPermanent(); - if (!targetDesc.object()) { - // step 20a - if (!extensibleTarget) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW); - return false; - } - // step 20b - if (settingConfigFalse) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC); - return false; - } - } else { - // step 21 - bool valid; - Rooted pd(cx); - pd.initFromPropertyDescriptor(desc); - if (!ValidatePropertyDescriptor(cx, extensibleTarget, pd, targetDesc, &valid)) - return false; - if (!valid || (settingConfigFalse && !targetDesc.isPermanent())) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID); - return false; - } - } - } - - // [[DefineProperty]] should return a boolean value, which is used to do things like - // strict-mode throwing. At present, the engine is not prepared to do that. See bug 826587. - return true; -} - -// This is secretly [[OwnPropertyKeys]]. SM uses the old wiki name, internally. -// ES6 (5 April 2014) 9.5.12 Proxy.[[OwnPropertyKeys]]() -bool -ScriptedDirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const -{ - // step 1 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 3 - RootedObject target(cx, proxy->as().target()); - - // step 4-5 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().ownKeys, &trap)) - return false; - - // step 6 - if (trap.isUndefined()) - return DirectProxyHandler::getOwnPropertyNames(cx, proxy, props); - - // step 7-8 - Value argv[] = { - ObjectValue(*target) - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 9 - if (trapResult.isPrimitive()) { - ReportInvalidTrapResult(cx, proxy, cx->names().ownKeys); - return false; - } - - // Here we add a bunch of extra sanity checks. It is unclear if they will also appear in - // the spec. See step 10-11 - return ArrayToIdVector(cx, proxy, target, trapResult, props, - JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, - cx->names().getOwnPropertyNames); -} - -// ES6 (5 April 2014) 9.5.10 Proxy.[[Delete]](P) -bool -ScriptedDirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - // step 2 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 4 - RootedObject target(cx, proxy->as().target()); - - // step 5 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().deleteProperty, &trap)) - return false; - - // step 7 - if (trap.isUndefined()) - return DirectProxyHandler::delete_(cx, proxy, id, bp); - - // step 8 - RootedValue value(cx); - if (!IdToStringOrSymbol(cx, id, &value)) - return false; - Value argv[] = { - ObjectValue(*target), - value - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 9 - if (ToBoolean(trapResult)) { - // step 12 - Rooted desc(cx); - if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) - return false; - - // step 14-15 - if (desc.object() && desc.isPermanent()) { - RootedValue v(cx, IdToValue(id)); - js_ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, js::NullPtr()); - return false; - } - - // step 16 - *bp = true; - return true; - } - - // step 11 - *bp = false; - return true; -} - -// ES6 (22 May, 2014) 9.5.11 Proxy.[[Enumerate]] -bool -ScriptedDirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const -{ - // step 1 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 3 - RootedObject target(cx, proxy->as().target()); - - // step 4-5 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().enumerate, &trap)) - return false; - - // step 6 - if (trap.isUndefined()) - return DirectProxyHandler::enumerate(cx, proxy, props); - - // step 7-8 - Value argv[] = { - ObjectOrNullValue(target) - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 9 - if (trapResult.isPrimitive()) { - JSAutoByteString bytes; - if (!AtomToPrintableString(cx, cx->names().enumerate, &bytes)) - return false; - RootedValue v(cx, ObjectOrNullValue(proxy)); - js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_SEARCH_STACK, - v, js::NullPtr(), bytes.ptr()); - return false; - } - - // step 10 - // FIXME: the trap should return an iterator object, see bug 783826. Since this isn't very - // useful for us internally, we convery to an id vector. - return ArrayToIdVector(cx, proxy, target, trapResult, props, 0, cx->names().enumerate); -} - -// ES6 (22 May, 2014) 9.5.7 Proxy.[[HasProperty]](P) -bool -ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - // step 2 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 4 - RootedObject target(cx, proxy->as().target()); - - // step 5-6 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().has, &trap)) - return false; - - // step 7 - if (trap.isUndefined()) - return DirectProxyHandler::has(cx, proxy, id, bp); - - // step 8,10 - RootedValue value(cx); - if (!IdToStringOrSymbol(cx, id, &value)) - return false; - Value argv[] = { - ObjectOrNullValue(target), - value - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 9 - bool success = ToBoolean(trapResult); - - // step 11 - if (!success) { - Rooted desc(cx); - if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) - return false; - - if (desc.object()) { - if (desc.isPermanent()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE); - return false; - } - - bool extensible; - if (!JSObject::isExtensible(cx, target, &extensible)) - return false; - if (!extensible) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); - return false; - } - } - } - - // step 12 - *bp = success; - return true; -} - -// ES6 (22 May, 2014) 9.5.8 Proxy.[[GetP]](P, Receiver) -bool -ScriptedDirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, MutableHandleValue vp) const -{ - // step 2 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 4 - RootedObject target(cx, proxy->as().target()); - - // step 5-6 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().get, &trap)) - return false; - - // step 7 - if (trap.isUndefined()) - return DirectProxyHandler::get(cx, proxy, receiver, id, vp); - - // step 8-9 - RootedValue value(cx); - if (!IdToStringOrSymbol(cx, id, &value)) - return false; - Value argv[] = { - ObjectOrNullValue(target), - value, - ObjectOrNullValue(receiver) - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 10-11 - Rooted desc(cx); - if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) - return false; - - // step 12 - if (desc.object()) { - if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) { - bool same; - if (!SameValue(cx, trapResult, desc.value(), &same)) - return false; - if (!same) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MUST_REPORT_SAME_VALUE); - return false; - } - } - - if (IsAccessorDescriptor(desc) && desc.isPermanent() && !desc.hasGetterObject()) { - if (!trapResult.isUndefined()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED); - return false; - } - } - } - - // step 13 - vp.set(trapResult); - return true; -} - -// ES6 (22 May, 2014) 9.5.9 Proxy.[[SetP]](P, V, Receiver) -bool -ScriptedDirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const -{ - // step 2 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 4 - RootedObject target(cx, proxy->as().target()); - - // step 5-6 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().set, &trap)) - return false; - - // step 7 - if (trap.isUndefined()) - return DirectProxyHandler::set(cx, proxy, receiver, id, strict, vp); - - // step 8,10 - RootedValue value(cx); - if (!IdToStringOrSymbol(cx, id, &value)) - return false; - Value argv[] = { - ObjectOrNullValue(target), - value, - vp.get(), - ObjectValue(*receiver) - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 9 - bool success = ToBoolean(trapResult); - - if (success) { - // step 12-13 - Rooted desc(cx); - if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) - return false; - - // step 14 - if (desc.object()) { - if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) { - bool same; - if (!SameValue(cx, vp, desc.value(), &same)) - return false; - if (!same) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_NW_NC); - return false; - } - } - - if (IsAccessorDescriptor(desc) && desc.isPermanent() && !desc.hasSetterObject()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER); - return false; - } - } - } - - // step 11, 15 - vp.set(BooleanValue(success)); - return true; -} - -// ES6 (5 April, 2014) 9.5.3 Proxy.[[IsExtensible]]() -bool -ScriptedDirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const -{ - // step 1 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 3 - RootedObject target(cx, proxy->as().target()); - - // step 4-5 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().isExtensible, &trap)) - return false; - - // step 6 - if (trap.isUndefined()) - return DirectProxyHandler::isExtensible(cx, proxy, extensible); - - // step 7, 9 - Value argv[] = { - ObjectValue(*target) - }; - RootedValue trapResult(cx); - if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) - return false; - - // step 8 - bool booleanTrapResult = ToBoolean(trapResult); - - // step 10-11 - bool targetResult; - if (!JSObject::isExtensible(cx, target, &targetResult)) - return false; - - // step 12 - if (targetResult != booleanTrapResult) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY); - return false; - } - - // step 13 - *extensible = booleanTrapResult; - return true; -} - -bool -ScriptedDirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, - MutableHandleValue vp) const -{ - // FIXME: Provide a proper implementation for this trap, see bug 787004 - return DirectProxyHandler::iterate(cx, proxy, flags, vp); -} - -// ES6 (22 May, 2014) 9.5.13 Proxy.[[Call]] -bool -ScriptedDirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const -{ - // step 1 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 3 - RootedObject target(cx, proxy->as().target()); - - /* - * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get - * called for non-callable objects. - */ - - // step 7 - RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); - if (!argsArray) - return false; - - // step 4-5 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().apply, &trap)) - return false; - - // step 6 - if (trap.isUndefined()) - return DirectProxyHandler::call(cx, proxy, args); - - // step 8 - Value argv[] = { - ObjectValue(*target), - args.thisv(), - ObjectValue(*argsArray) - }; - RootedValue thisValue(cx, ObjectValue(*handler)); - return Invoke(cx, thisValue, trap, ArrayLength(argv), argv, args.rval()); -} - -// ES6 (22 May, 2014) 9.5.14 Proxy.[[Construct]] -bool -ScriptedDirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const -{ - // step 1 - RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 2 - if (!handler) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); - return false; - } - - // step 3 - RootedObject target(cx, proxy->as().target()); - - /* - * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get - * called for non-callable objects. FIXME: See bug 1041756 - */ - - // step 7 - RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); - if (!argsArray) - return false; - - // step 4-5 - RootedValue trap(cx); - if (!JSObject::getProperty(cx, handler, handler, cx->names().construct, &trap)) - return false; - - // step 6 - if (trap.isUndefined()) - return DirectProxyHandler::construct(cx, proxy, args); - - // step 8-9 - Value constructArgv[] = { - ObjectValue(*target), - ObjectValue(*argsArray) - }; - RootedValue thisValue(cx, ObjectValue(*handler)); - if (!Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval())) - return false; - - // step 10 - if (!args.rval().isObject()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT); - return false; - } - return true; -} - -const char ScriptedDirectProxyHandler::family = 0; -const ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton; #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \ JS_BEGIN_MACRO \ @@ -2407,89 +1281,6 @@ ProxyObject::renew(JSContext *cx, const BaseProxyHandler *handler, Value priv) setSlot(EXTRA_SLOT + 1, UndefinedValue()); } -static bool -proxy(JSContext *cx, unsigned argc, jsval *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - if (args.length() < 2) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, - "Proxy", "1", "s"); - return false; - } - RootedObject target(cx, NonNullObject(cx, args[0])); - if (!target) - return false; - RootedObject handler(cx, NonNullObject(cx, args[1])); - if (!handler) - return false; - RootedValue priv(cx, ObjectValue(*target)); - ProxyOptions options; - options.selectDefaultClass(target->isCallable()); - ProxyObject *proxy = - ProxyObject::New(cx, &ScriptedDirectProxyHandler::singleton, - priv, TaggedProto(TaggedProto::LazyProto), cx->global(), - options); - if (!proxy) - return false; - proxy->setExtra(ScriptedDirectProxyHandler::HANDLER_EXTRA, ObjectValue(*handler)); - args.rval().setObject(*proxy); - return true; -} - -static bool -RevokeProxy(JSContext *cx, unsigned argc, Value *vp) -{ - CallReceiver rec = CallReceiverFromVp(vp); - - RootedFunction func(cx, &rec.callee().as()); - RootedObject p(cx, func->getExtendedSlot(ScriptedDirectProxyHandler::REVOKE_SLOT).toObjectOrNull()); - - if (p) { - func->setExtendedSlot(ScriptedDirectProxyHandler::REVOKE_SLOT, NullValue()); - - MOZ_ASSERT(p->is()); - - p->as().setSameCompartmentPrivate(NullValue()); - p->as().setExtra(ScriptedDirectProxyHandler::HANDLER_EXTRA, NullValue()); - } - - rec.rval().setUndefined(); - return true; -} - -static bool -proxy_revocable(JSContext *cx, unsigned argc, Value *vp) -{ - CallReceiver args = CallReceiverFromVp(vp); - - if (!proxy(cx, argc, vp)) - return false; - - RootedValue proxyVal(cx, args.rval()); - MOZ_ASSERT(proxyVal.toObject().is()); - - RootedObject revoker(cx, NewFunctionByIdWithReserved(cx, RevokeProxy, 0, 0, cx->global(), - AtomToId(cx->names().revoke))); - if (!revoker) - return false; - - revoker->as().initExtendedSlot(ScriptedDirectProxyHandler::REVOKE_SLOT, proxyVal); - - RootedObject result(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); - if (!result) - return false; - - RootedValue revokeVal(cx, ObjectValue(*revoker)); - if (!JSObject::defineProperty(cx, result, cx->names().proxy, proxyVal) || - !JSObject::defineProperty(cx, result, cx->names().revoke, revokeVal)) - { - return false; - } - - args.rval().setObject(*result); - return true; -} - static bool proxy_create(JSContext *cx, unsigned argc, Value *vp) { From 9b52d16c0f847874b72735b713fd8716c4f0ddf0 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:45 -0700 Subject: [PATCH 58/69] Bug 1031092 - Part 5: Factor out ScriptedIndirectProxyHandler. (r=bholley) --- js/src/moz.build | 1 + js/src/proxy/ScriptedIndirectProxyHandler.cpp | 462 ++++++++++++++++ js/src/proxy/ScriptedIndirectProxyHandler.h | 67 +++ js/src/proxy/jsproxy.cpp | 494 +----------------- 4 files changed, 531 insertions(+), 493 deletions(-) create mode 100644 js/src/proxy/ScriptedIndirectProxyHandler.cpp create mode 100644 js/src/proxy/ScriptedIndirectProxyHandler.h diff --git a/js/src/moz.build b/js/src/moz.build index a2700332626f..6843f9b035a2 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -235,6 +235,7 @@ UNIFIED_SOURCES += [ 'proxy/DirectProxyHandler.cpp', 'proxy/jsproxy.cpp', 'proxy/ScriptedDirectProxyHandler.cpp', + 'proxy/ScriptedIndirectProxyHandler.cpp', 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', 'vm/CallNonGenericMethod.cpp', diff --git a/js/src/proxy/ScriptedIndirectProxyHandler.cpp b/js/src/proxy/ScriptedIndirectProxyHandler.cpp new file mode 100644 index 000000000000..08adcf7490a2 --- /dev/null +++ b/js/src/proxy/ScriptedIndirectProxyHandler.cpp @@ -0,0 +1,462 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "proxy/ScriptedIndirectProxyHandler.h" + +#include "jsapi.h" +#include "jscntxt.h" + +#include "jsobjinlines.h" + +using namespace js; + +static bool +GetFundamentalTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, + MutableHandleValue fvalp) +{ + JS_CHECK_RECURSION(cx, return false); + + return JSObject::getProperty(cx, handler, handler, name, fvalp); +} + +static bool +GetDerivedTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, + MutableHandleValue fvalp) +{ + JS_ASSERT(name == cx->names().has || + name == cx->names().hasOwn || + name == cx->names().get || + name == cx->names().set || + name == cx->names().keys || + name == cx->names().iterate); + + return JSObject::getProperty(cx, handler, handler, name, fvalp); +} + +static bool +Trap(JSContext *cx, HandleObject handler, HandleValue fval, unsigned argc, Value* argv, + MutableHandleValue rval) +{ + return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval); +} + +static bool +Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval) +{ + if (!IdToStringOrSymbol(cx, id, rval)) + return false; + return Trap(cx, handler, fval, 1, rval.address(), rval); +} + +static bool +Trap2(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value v_, + MutableHandleValue rval) +{ + RootedValue v(cx, v_); + if (!IdToStringOrSymbol(cx, id, rval)) + return false; + JS::AutoValueArray<2> argv(cx); + argv[0].set(rval); + argv[1].set(v); + return Trap(cx, handler, fval, 2, argv.begin(), rval); +} + +static bool +IndicatePropertyNotFound(MutableHandle desc) +{ + desc.object().set(nullptr); + return true; +} + +static bool +ValueToBool(HandleValue v, bool *bp) +{ + *bp = ToBoolean(v); + return true; +} + +static bool +ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props) +{ + JS_ASSERT(props.length() == 0); + + if (array.isPrimitive()) + return true; + + RootedObject obj(cx, &array.toObject()); + uint32_t length; + if (!GetLengthProperty(cx, obj, &length)) + return false; + + RootedValue v(cx); + for (uint32_t n = 0; n < length; ++n) { + if (!CheckForInterrupt(cx)) + return false; + if (!JSObject::getElement(cx, obj, obj, n, &v)) + return false; + RootedId id(cx); + if (!ValueToId(cx, v, &id)) + return false; + if (!props.append(id)) + return false; + } + + return true; +} + +namespace { + +/* + * Old-style indirect proxies allow callers to specify distinct scripted + * [[Call]] and [[Construct]] traps. We use an intermediate object so that we + * can stash this information in a single reserved slot on the proxy object. + * + * Note - Currently this is slightly unnecesary, because we actually have 2 + * extra slots, neither of which are used for ScriptedIndirectProxy. But we're + * eventually moving towards eliminating one of those slots, and so we don't + * want to add a dependency here. + */ +static const Class CallConstructHolder = { + "CallConstructHolder", + JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS +}; + +} /* anonymous namespace */ + +// This variable exists solely to provide a unique address for use as an identifier. +const char ScriptedIndirectProxyHandler::family = 0; + +bool +ScriptedIndirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, + bool *extensible) const +{ + // Scripted indirect proxies don't support extensibility changes. + *extensible = true; + return true; +} + +bool +ScriptedIndirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const +{ + // See above. + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); + return false; +} + +static bool +ReturnedValueMustNotBePrimitive(JSContext *cx, HandleObject proxy, JSAtom *atom, const Value &v) +{ + if (v.isPrimitive()) { + JSAutoByteString bytes; + if (AtomToPrintableString(cx, atom, &bytes)) { + RootedValue val(cx, ObjectOrNullValue(proxy)); + js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE, + JSDVG_SEARCH_STACK, val, js::NullPtr(), bytes.ptr()); + } + return false; + } + return true; +} + +static JSObject * +GetIndirectProxyHandlerObject(JSObject *proxy) +{ + return proxy->as().private_().toObjectOrNull(); +} + +bool +ScriptedIndirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue fval(cx), value(cx); + return GetFundamentalTrap(cx, handler, cx->names().getPropertyDescriptor, &fval) && + Trap1(cx, handler, fval, id, &value) && + ((value.get().isUndefined() && IndicatePropertyNotFound(desc)) || + (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) && + ParsePropertyDescriptorObject(cx, proxy, value, desc))); +} + +bool +ScriptedIndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue fval(cx), value(cx); + return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &fval) && + Trap1(cx, handler, fval, id, &value) && + ((value.get().isUndefined() && IndicatePropertyNotFound(desc)) || + (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) && + ParsePropertyDescriptorObject(cx, proxy, value, desc))); +} + +bool +ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue fval(cx), value(cx); + return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) && + NewPropertyDescriptorObject(cx, desc, &value) && + Trap2(cx, handler, fval, id, value, &value); +} + +bool +ScriptedIndirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue fval(cx), value(cx); + return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyNames, &fval) && + Trap(cx, handler, fval, 0, nullptr, &value) && + ArrayToIdVector(cx, value, props); +} + +bool +ScriptedIndirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue fval(cx), value(cx); + return GetFundamentalTrap(cx, handler, cx->names().delete_, &fval) && + Trap1(cx, handler, fval, id, &value) && + ValueToBool(value, bp); +} + +bool +ScriptedIndirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue fval(cx), value(cx); + return GetFundamentalTrap(cx, handler, cx->names().enumerate, &fval) && + Trap(cx, handler, fval, 0, nullptr, &value) && + ArrayToIdVector(cx, value, props); +} + +bool +ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue fval(cx), value(cx); + if (!GetDerivedTrap(cx, handler, cx->names().has, &fval)) + return false; + if (!IsCallable(fval)) + return BaseProxyHandler::has(cx, proxy, id, bp); + return Trap1(cx, handler, fval, id, &value) && + ValueToBool(value, bp); +} + +bool +ScriptedIndirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue fval(cx), value(cx); + if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval)) + return false; + if (!IsCallable(fval)) + return BaseProxyHandler::hasOwn(cx, proxy, id, bp); + return Trap1(cx, handler, fval, id, &value) && + ValueToBool(value, bp); +} + +bool +ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, MutableHandleValue vp) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue idv(cx); + if (!IdToStringOrSymbol(cx, id, &idv)) + return false; + JS::AutoValueArray<2> argv(cx); + argv[0].setObjectOrNull(receiver); + argv[1].set(idv); + RootedValue fval(cx); + if (!GetDerivedTrap(cx, handler, cx->names().get, &fval)) + return false; + if (!IsCallable(fval)) + return BaseProxyHandler::get(cx, proxy, receiver, id, vp); + return Trap(cx, handler, fval, 2, argv.begin(), vp); +} + +bool +ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, + HandleId id, bool strict, MutableHandleValue vp) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue idv(cx); + if (!IdToStringOrSymbol(cx, id, &idv)) + return false; + JS::AutoValueArray<3> argv(cx); + argv[0].setObjectOrNull(receiver); + argv[1].set(idv); + argv[2].set(vp); + RootedValue fval(cx); + if (!GetDerivedTrap(cx, handler, cx->names().set, &fval)) + return false; + if (!IsCallable(fval)) + return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); + return Trap(cx, handler, fval, 3, argv.begin(), &idv); +} + +bool +ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue value(cx); + if (!GetDerivedTrap(cx, handler, cx->names().keys, &value)) + return false; + if (!IsCallable(value)) + return BaseProxyHandler::keys(cx, proxy, props); + return Trap(cx, handler, value, 0, nullptr, &value) && + ArrayToIdVector(cx, value, props); +} + +bool +ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, + MutableHandleValue vp) const +{ + RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); + RootedValue value(cx); + if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value)) + return false; + if (!IsCallable(value)) + return BaseProxyHandler::iterate(cx, proxy, flags, vp); + return Trap(cx, handler, value, 0, nullptr, vp) && + ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, vp); +} + +bool +ScriptedIndirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); + RootedObject ccHolder(cx, &proxy->as().extra(0).toObject()); + JS_ASSERT(ccHolder->getClass() == &CallConstructHolder); + RootedValue call(cx, ccHolder->getReservedSlot(0)); + JS_ASSERT(call.isObject() && call.toObject().isCallable()); + return Invoke(cx, args.thisv(), call, args.length(), args.array(), args.rval()); +} + +bool +ScriptedIndirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); + RootedObject ccHolder(cx, &proxy->as().extra(0).toObject()); + JS_ASSERT(ccHolder->getClass() == &CallConstructHolder); + RootedValue construct(cx, ccHolder->getReservedSlot(1)); + JS_ASSERT(construct.isObject() && construct.toObject().isCallable()); + return InvokeConstructor(cx, construct, args.length(), args.array(), + args.rval().address()); +} + +bool +ScriptedIndirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const +{ + return BaseProxyHandler::nativeCall(cx, test, impl, args); +} + +JSString * +ScriptedIndirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const +{ + assertEnteredPolicy(cx, proxy, JSID_VOID, GET); + if (!proxy->isCallable()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, + JSMSG_INCOMPATIBLE_PROTO, + js_Function_str, js_toString_str, + "object"); + return nullptr; + } + RootedObject obj(cx, &proxy->as().extra(0).toObject().getReservedSlot(0).toObject()); + return fun_toStringHelper(cx, obj, indent); +} + +const ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton; + +bool +js::proxy_create(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 1) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, + "create", "0", "s"); + return false; + } + JSObject *handler = NonNullObject(cx, args[0]); + if (!handler) + return false; + JSObject *proto, *parent = nullptr; + if (args.get(1).isObject()) { + proto = &args[1].toObject(); + parent = proto->getParent(); + } else { + JS_ASSERT(IsFunctionObject(&args.callee())); + proto = nullptr; + } + if (!parent) + parent = args.callee().getParent(); + RootedValue priv(cx, ObjectValue(*handler)); + JSObject *proxy = NewProxyObject(cx, &ScriptedIndirectProxyHandler::singleton, + priv, proto, parent); + if (!proxy) + return false; + + args.rval().setObject(*proxy); + return true; +} + +bool +js::proxy_createFunction(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 2) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, + "createFunction", "1", ""); + return false; + } + RootedObject handler(cx, NonNullObject(cx, args[0])); + if (!handler) + return false; + RootedObject proto(cx), parent(cx); + parent = args.callee().getParent(); + proto = parent->global().getOrCreateFunctionPrototype(cx); + if (!proto) + return false; + parent = proto->getParent(); + + RootedObject call(cx, ValueToCallable(cx, args[1], args.length() - 2)); + if (!call) + return false; + RootedObject construct(cx, nullptr); + if (args.length() > 2) { + construct = ValueToCallable(cx, args[2], args.length() - 3); + if (!construct) + return false; + } else { + construct = call; + } + + // Stash the call and construct traps on a holder object that we can stick + // in a slot on the proxy. + RootedObject ccHolder(cx, JS_NewObjectWithGivenProto(cx, Jsvalify(&CallConstructHolder), + js::NullPtr(), cx->global())); + if (!ccHolder) + return false; + ccHolder->setReservedSlot(0, ObjectValue(*call)); + ccHolder->setReservedSlot(1, ObjectValue(*construct)); + + RootedValue priv(cx, ObjectValue(*handler)); + ProxyOptions options; + options.selectDefaultClass(true); + JSObject *proxy = + ProxyObject::New(cx, &ScriptedIndirectProxyHandler::singleton, + priv, TaggedProto(proto), parent, options); + if (!proxy) + return false; + proxy->as().setExtra(0, ObjectValue(*ccHolder)); + + args.rval().setObject(*proxy); + return true; +} diff --git a/js/src/proxy/ScriptedIndirectProxyHandler.h b/js/src/proxy/ScriptedIndirectProxyHandler.h new file mode 100644 index 000000000000..9ea8fa960d71 --- /dev/null +++ b/js/src/proxy/ScriptedIndirectProxyHandler.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef proxy_ScriptedIndirectProxyHandler_h +#define proxy_ScriptedIndirectProxyHandler_h + +#include "jsproxy.h" + +namespace js { + +/* Derived class for all scripted indirect proxy handlers. */ +class ScriptedIndirectProxyHandler : public BaseProxyHandler +{ + public: + MOZ_CONSTEXPR ScriptedIndirectProxyHandler() + : BaseProxyHandler(&family) + { } + + /* ES5 Harmony fundamental proxy traps. */ + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, + AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; + virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; + + /* ES5 Harmony derived proxy traps. */ + virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; + virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; + virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, + MutableHandleValue vp) const MOZ_OVERRIDE; + + /* Spidermonkey extensions. */ + virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; + virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const MOZ_OVERRIDE; + virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE; + virtual bool isScripted() const MOZ_OVERRIDE { return true; } + + static const char family; + static const ScriptedIndirectProxyHandler singleton; +}; + +bool +proxy_create(JSContext *cx, unsigned argc, Value *vp); + +bool +proxy_createFunction(JSContext *cx, unsigned argc, Value *vp); + +} /* namespace js */ + +#endif /* proxy_ScriptedIndirectProxyHandler_h */ diff --git a/js/src/proxy/jsproxy.cpp b/js/src/proxy/jsproxy.cpp index 3527a62dd454..8ae74ca5f534 100644 --- a/js/src/proxy/jsproxy.cpp +++ b/js/src/proxy/jsproxy.cpp @@ -16,6 +16,7 @@ #include "gc/Marking.h" #include "proxy/ScriptedDirectProxyHandler.h" +#include "proxy/ScriptedIndirectProxyHandler.h" #include "vm/WrapperObject.h" #include "jsatominlines.h" @@ -83,413 +84,6 @@ js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id, } #endif -static bool -GetFundamentalTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, - MutableHandleValue fvalp) -{ - JS_CHECK_RECURSION(cx, return false); - - return JSObject::getProperty(cx, handler, handler, name, fvalp); -} - -static bool -GetDerivedTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, - MutableHandleValue fvalp) -{ - JS_ASSERT(name == cx->names().has || - name == cx->names().hasOwn || - name == cx->names().get || - name == cx->names().set || - name == cx->names().keys || - name == cx->names().iterate); - - return JSObject::getProperty(cx, handler, handler, name, fvalp); -} - -static bool -Trap(JSContext *cx, HandleObject handler, HandleValue fval, unsigned argc, Value* argv, - MutableHandleValue rval) -{ - return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval); -} - -static bool -Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval) -{ - if (!IdToStringOrSymbol(cx, id, rval)) - return false; - return Trap(cx, handler, fval, 1, rval.address(), rval); -} - -static bool -Trap2(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value v_, - MutableHandleValue rval) -{ - RootedValue v(cx, v_); - if (!IdToStringOrSymbol(cx, id, rval)) - return false; - JS::AutoValueArray<2> argv(cx); - argv[0].set(rval); - argv[1].set(v); - return Trap(cx, handler, fval, 2, argv.begin(), rval); -} - -static bool -IndicatePropertyNotFound(MutableHandle desc) -{ - desc.object().set(nullptr); - return true; -} - -static bool -ValueToBool(HandleValue v, bool *bp) -{ - *bp = ToBoolean(v); - return true; -} - -static bool -ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props) -{ - JS_ASSERT(props.length() == 0); - - if (array.isPrimitive()) - return true; - - RootedObject obj(cx, &array.toObject()); - uint32_t length; - if (!GetLengthProperty(cx, obj, &length)) - return false; - - RootedValue v(cx); - for (uint32_t n = 0; n < length; ++n) { - if (!CheckForInterrupt(cx)) - return false; - if (!JSObject::getElement(cx, obj, obj, n, &v)) - return false; - RootedId id(cx); - if (!ValueToId(cx, v, &id)) - return false; - if (!props.append(id)) - return false; - } - - return true; -} - -namespace { - -/* Derived class for all scripted indirect proxy handlers. */ -class ScriptedIndirectProxyHandler : public BaseProxyHandler -{ - public: - MOZ_CONSTEXPR ScriptedIndirectProxyHandler() - : BaseProxyHandler(&family) - { } - - /* ES5 Harmony fundamental proxy traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; - - /* ES5 Harmony derived proxy traps. */ - virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, - MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE; - virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, - MutableHandleValue vp) const MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; - virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) const MOZ_OVERRIDE; - virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE; - virtual bool isScripted() const MOZ_OVERRIDE { return true; } - - static const char family; - static const ScriptedIndirectProxyHandler singleton; -}; - -/* - * Old-style indirect proxies allow callers to specify distinct scripted - * [[Call]] and [[Construct]] traps. We use an intermediate object so that we - * can stash this information in a single reserved slot on the proxy object. - * - * Note - Currently this is slightly unnecesary, because we actually have 2 - * extra slots, neither of which are used for ScriptedIndirectProxy. But we're - * eventually moving towards eliminating one of those slots, and so we don't - * want to add a dependency here. - */ -static const Class CallConstructHolder = { - "CallConstructHolder", - JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS -}; - -} /* anonymous namespace */ - -// This variable exists solely to provide a unique address for use as an identifier. -const char ScriptedIndirectProxyHandler::family = 0; - -bool -ScriptedIndirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, - bool *extensible) const -{ - // Scripted indirect proxies don't support extensibility changes. - *extensible = true; - return true; -} - -bool -ScriptedIndirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const -{ - // See above. - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); - return false; -} - -static bool -ReturnedValueMustNotBePrimitive(JSContext *cx, HandleObject proxy, JSAtom *atom, const Value &v) -{ - if (v.isPrimitive()) { - JSAutoByteString bytes; - if (AtomToPrintableString(cx, atom, &bytes)) { - RootedValue val(cx, ObjectOrNullValue(proxy)); - js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE, - JSDVG_SEARCH_STACK, val, js::NullPtr(), bytes.ptr()); - } - return false; - } - return true; -} - -static JSObject * -GetIndirectProxyHandlerObject(JSObject *proxy) -{ - return proxy->as().private_().toObjectOrNull(); -} - -bool -ScriptedIndirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue fval(cx), value(cx); - return GetFundamentalTrap(cx, handler, cx->names().getPropertyDescriptor, &fval) && - Trap1(cx, handler, fval, id, &value) && - ((value.get().isUndefined() && IndicatePropertyNotFound(desc)) || - (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) && - ParsePropertyDescriptorObject(cx, proxy, value, desc))); -} - -bool -ScriptedIndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue fval(cx), value(cx); - return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &fval) && - Trap1(cx, handler, fval, id, &value) && - ((value.get().isUndefined() && IndicatePropertyNotFound(desc)) || - (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) && - ParsePropertyDescriptorObject(cx, proxy, value, desc))); -} - -bool -ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue fval(cx), value(cx); - return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) && - NewPropertyDescriptorObject(cx, desc, &value) && - Trap2(cx, handler, fval, id, value, &value); -} - -bool -ScriptedIndirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, - AutoIdVector &props) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue fval(cx), value(cx); - return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyNames, &fval) && - Trap(cx, handler, fval, 0, nullptr, &value) && - ArrayToIdVector(cx, value, props); -} - -bool -ScriptedIndirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue fval(cx), value(cx); - return GetFundamentalTrap(cx, handler, cx->names().delete_, &fval) && - Trap1(cx, handler, fval, id, &value) && - ValueToBool(value, bp); -} - -bool -ScriptedIndirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue fval(cx), value(cx); - return GetFundamentalTrap(cx, handler, cx->names().enumerate, &fval) && - Trap(cx, handler, fval, 0, nullptr, &value) && - ArrayToIdVector(cx, value, props); -} - -bool -ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue fval(cx), value(cx); - if (!GetDerivedTrap(cx, handler, cx->names().has, &fval)) - return false; - if (!IsCallable(fval)) - return BaseProxyHandler::has(cx, proxy, id, bp); - return Trap1(cx, handler, fval, id, &value) && - ValueToBool(value, bp); -} - -bool -ScriptedIndirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue fval(cx), value(cx); - if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval)) - return false; - if (!IsCallable(fval)) - return BaseProxyHandler::hasOwn(cx, proxy, id, bp); - return Trap1(cx, handler, fval, id, &value) && - ValueToBool(value, bp); -} - -bool -ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, MutableHandleValue vp) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue idv(cx); - if (!IdToStringOrSymbol(cx, id, &idv)) - return false; - JS::AutoValueArray<2> argv(cx); - argv[0].setObjectOrNull(receiver); - argv[1].set(idv); - RootedValue fval(cx); - if (!GetDerivedTrap(cx, handler, cx->names().get, &fval)) - return false; - if (!IsCallable(fval)) - return BaseProxyHandler::get(cx, proxy, receiver, id, vp); - return Trap(cx, handler, fval, 2, argv.begin(), vp); -} - -bool -ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue idv(cx); - if (!IdToStringOrSymbol(cx, id, &idv)) - return false; - JS::AutoValueArray<3> argv(cx); - argv[0].setObjectOrNull(receiver); - argv[1].set(idv); - argv[2].set(vp); - RootedValue fval(cx); - if (!GetDerivedTrap(cx, handler, cx->names().set, &fval)) - return false; - if (!IsCallable(fval)) - return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); - return Trap(cx, handler, fval, 3, argv.begin(), &idv); -} - -bool -ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue value(cx); - if (!GetDerivedTrap(cx, handler, cx->names().keys, &value)) - return false; - if (!IsCallable(value)) - return BaseProxyHandler::keys(cx, proxy, props); - return Trap(cx, handler, value, 0, nullptr, &value) && - ArrayToIdVector(cx, value, props); -} - -bool -ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, - MutableHandleValue vp) const -{ - RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); - RootedValue value(cx); - if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value)) - return false; - if (!IsCallable(value)) - return BaseProxyHandler::iterate(cx, proxy, flags, vp); - return Trap(cx, handler, value, 0, nullptr, vp) && - ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, vp); -} - -bool -ScriptedIndirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); - RootedObject ccHolder(cx, &proxy->as().extra(0).toObject()); - JS_ASSERT(ccHolder->getClass() == &CallConstructHolder); - RootedValue call(cx, ccHolder->getReservedSlot(0)); - JS_ASSERT(call.isObject() && call.toObject().isCallable()); - return Invoke(cx, args.thisv(), call, args.length(), args.array(), args.rval()); -} - -bool -ScriptedIndirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, CALL); - RootedObject ccHolder(cx, &proxy->as().extra(0).toObject()); - JS_ASSERT(ccHolder->getClass() == &CallConstructHolder); - RootedValue construct(cx, ccHolder->getReservedSlot(1)); - JS_ASSERT(construct.isObject() && construct.toObject().isCallable()); - return InvokeConstructor(cx, construct, args.length(), args.array(), - args.rval().address()); -} - -bool -ScriptedIndirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) const -{ - return BaseProxyHandler::nativeCall(cx, test, impl, args); -} - -JSString * -ScriptedIndirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const -{ - assertEnteredPolicy(cx, proxy, JSID_VOID, GET); - if (!proxy->isCallable()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, - JSMSG_INCOMPATIBLE_PROTO, - js_Function_str, js_toString_str, - "object"); - return nullptr; - } - RootedObject obj(cx, &proxy->as().extra(0).toObject().getReservedSlot(0).toObject()); - return fun_toStringHelper(cx, obj, indent); -} - -const ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton; - - #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \ JS_BEGIN_MACRO \ RootedObject proto(cx); \ @@ -1281,92 +875,6 @@ ProxyObject::renew(JSContext *cx, const BaseProxyHandler *handler, Value priv) setSlot(EXTRA_SLOT + 1, UndefinedValue()); } -static bool -proxy_create(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - if (args.length() < 1) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, - "create", "0", "s"); - return false; - } - JSObject *handler = NonNullObject(cx, args[0]); - if (!handler) - return false; - JSObject *proto, *parent = nullptr; - if (args.get(1).isObject()) { - proto = &args[1].toObject(); - parent = proto->getParent(); - } else { - JS_ASSERT(IsFunctionObject(&args.callee())); - proto = nullptr; - } - if (!parent) - parent = args.callee().getParent(); - RootedValue priv(cx, ObjectValue(*handler)); - JSObject *proxy = NewProxyObject(cx, &ScriptedIndirectProxyHandler::singleton, - priv, proto, parent); - if (!proxy) - return false; - - args.rval().setObject(*proxy); - return true; -} - -static bool -proxy_createFunction(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - if (args.length() < 2) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, - "createFunction", "1", ""); - return false; - } - RootedObject handler(cx, NonNullObject(cx, args[0])); - if (!handler) - return false; - RootedObject proto(cx), parent(cx); - parent = args.callee().getParent(); - proto = parent->global().getOrCreateFunctionPrototype(cx); - if (!proto) - return false; - parent = proto->getParent(); - - RootedObject call(cx, ValueToCallable(cx, args[1], args.length() - 2)); - if (!call) - return false; - RootedObject construct(cx, nullptr); - if (args.length() > 2) { - construct = ValueToCallable(cx, args[2], args.length() - 3); - if (!construct) - return false; - } else { - construct = call; - } - - // Stash the call and construct traps on a holder object that we can stick - // in a slot on the proxy. - RootedObject ccHolder(cx, JS_NewObjectWithGivenProto(cx, Jsvalify(&CallConstructHolder), - js::NullPtr(), cx->global())); - if (!ccHolder) - return false; - ccHolder->setReservedSlot(0, ObjectValue(*call)); - ccHolder->setReservedSlot(1, ObjectValue(*construct)); - - RootedValue priv(cx, ObjectValue(*handler)); - ProxyOptions options; - options.selectDefaultClass(true); - JSObject *proxy = - ProxyObject::New(cx, &ScriptedIndirectProxyHandler::singleton, - priv, TaggedProto(proto), parent, options); - if (!proxy) - return false; - proxy->as().setExtra(0, ObjectValue(*ccHolder)); - - args.rval().setObject(*proxy); - return true; -} - JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, HandleObject obj) { From b27a7fb109e9bf1abbc5768b64dfd213c9a81562 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:46 -0700 Subject: [PATCH 59/69] Bug 1031092 - Part 6: Factor out engine Proxy entry point. (r=bholley) --HG-- rename : js/src/proxy/jsproxy.cpp => js/src/proxy/Proxy.cpp --- js/src/moz.build | 2 +- js/src/proxy/{jsproxy.cpp => Proxy.cpp} | 0 js/src/proxy/Proxy.h | 82 +++++++++++++++++++++++++ js/src/proxy/jsproxy.h | 62 ------------------- js/src/vm/CallNonGenericMethod.cpp | 1 + js/src/vm/ObjectImpl-inl.h | 2 +- js/src/vm/RegExpObject.h | 2 +- 7 files changed, 86 insertions(+), 65 deletions(-) rename js/src/proxy/{jsproxy.cpp => Proxy.cpp} (100%) create mode 100644 js/src/proxy/Proxy.h diff --git a/js/src/moz.build b/js/src/moz.build index 6843f9b035a2..b729dd012307 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -233,7 +233,7 @@ UNIFIED_SOURCES += [ 'prmjtime.cpp', 'proxy/BaseProxyHandler.cpp', 'proxy/DirectProxyHandler.cpp', - 'proxy/jsproxy.cpp', + 'proxy/Proxy.cpp', 'proxy/ScriptedDirectProxyHandler.cpp', 'proxy/ScriptedIndirectProxyHandler.cpp', 'vm/ArgumentsObject.cpp', diff --git a/js/src/proxy/jsproxy.cpp b/js/src/proxy/Proxy.cpp similarity index 100% rename from js/src/proxy/jsproxy.cpp rename to js/src/proxy/Proxy.cpp diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h new file mode 100644 index 000000000000..a76241cc551e --- /dev/null +++ b/js/src/proxy/Proxy.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef proxy_Proxy_h +#define proxy_Proxy_h + +#include "NamespaceImports.h" + +#include "js/Class.h" + +namespace js { + +class RegExpGuard; + +/* + * Dispatch point for handlers that executes the appropriate C++ or scripted traps. + * + * Important: All proxy traps need either (a) an AutoEnterPolicy in their + * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug + * 945826 comment 0. + */ +class Proxy +{ + public: + /* ES5 Harmony fundamental proxy traps. */ + static bool preventExtensions(JSContext *cx, HandleObject proxy); + static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc); + static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandleValue vp); + static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc); + static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandleValue vp); + static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, + MutableHandle desc); + static bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props); + static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); + static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props); + + /* ES5 Harmony derived proxy traps. */ + static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); + static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); + static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + MutableHandleValue vp); + static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + bool strict, MutableHandleValue vp); + static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props); + static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp); + + /* Spidermonkey extensions. */ + static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible); + static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args); + static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args); + static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args); + static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp); + static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx); + static const char *className(JSContext *cx, HandleObject proxy); + static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent); + static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g); + static bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp); + static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp); + static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); + static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); + + static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable); + static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id); + + static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end, + HandleObject result); + + /* IC entry path for handling __noSuchMethod__ on access. */ + static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id, + MutableHandleValue vp); +}; + +} /* namespace js */ + +#endif /* proxy_Proxy_h */ diff --git a/js/src/proxy/jsproxy.h b/js/src/proxy/jsproxy.h index fb3feff1e5c8..28fd99e401d8 100644 --- a/js/src/proxy/jsproxy.h +++ b/js/src/proxy/jsproxy.h @@ -308,68 +308,6 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler virtual JSObject *weakmapKeyDelegate(JSObject *proxy) const MOZ_OVERRIDE; }; -/* - * Dispatch point for handlers that executes the appropriate C++ or scripted traps. - * - * Important: All proxy traps need either (a) an AutoEnterPolicy in their - * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug - * 945826 comment 0. - */ -class Proxy -{ - public: - /* ES5 Harmony fundamental proxy traps. */ - static bool preventExtensions(JSContext *cx, HandleObject proxy); - static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc); - static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandleValue vp); - static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc); - static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandleValue vp); - static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, - MutableHandle desc); - static bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props); - static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); - static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props); - - /* ES5 Harmony derived proxy traps. */ - static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); - static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); - static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, - MutableHandleValue vp); - static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp); - static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props); - static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp); - - /* Spidermonkey extensions. */ - static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible); - static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args); - static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args); - static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args); - static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp); - static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx); - static const char *className(JSContext *cx, HandleObject proxy); - static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent); - static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g); - static bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp); - static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp); - static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); - static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); - - static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable); - static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id); - - static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end, - HandleObject result); - - /* IC entry path for handling __noSuchMethod__ on access. */ - static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id, - MutableHandleValue vp); -}; - // Use these in places where you don't want to #include vm/ProxyObject.h. extern JS_FRIEND_DATA(const js::Class* const) CallableProxyClassPtr; extern JS_FRIEND_DATA(const js::Class* const) UncallableProxyClassPtr; diff --git a/js/src/vm/CallNonGenericMethod.cpp b/js/src/vm/CallNonGenericMethod.cpp index f3435d595107..a387ca60555e 100644 --- a/js/src/vm/CallNonGenericMethod.cpp +++ b/js/src/vm/CallNonGenericMethod.cpp @@ -9,6 +9,7 @@ #include "jsfun.h" #include "jsobj.h" +#include "proxy/Proxy.h" #include "vm/ProxyObject.h" using namespace js; diff --git a/js/src/vm/ObjectImpl-inl.h b/js/src/vm/ObjectImpl-inl.h index ae973859b2e6..0cf3bf08dab9 100644 --- a/js/src/vm/ObjectImpl-inl.h +++ b/js/src/vm/ObjectImpl-inl.h @@ -10,8 +10,8 @@ #include "vm/ObjectImpl.h" #include "jscntxt.h" -#include "jsproxy.h" +#include "proxy/Proxy.h" #include "vm/ProxyObject.h" #include "vm/TypedArrayObject.h" diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 43f21ef8e3e4..4ffac98c50da 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -11,10 +11,10 @@ #include "mozilla/MemoryReporting.h" #include "jscntxt.h" -#include "jsproxy.h" #include "gc/Marking.h" #include "gc/Zone.h" +#include "proxy/Proxy.h" #include "vm/Shape.h" /* From d97dbebd631a69e3f4898d4738a9be507fdf71b0 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:46 -0700 Subject: [PATCH 60/69] Bug 1031092 - Part 7: Move jswrapper.* to js/src/proxy/. (r=bholley) --HG-- rename : js/src/jswrapper.cpp => js/src/proxy/Wrapper.cpp rename : js/src/jswrapper.h => js/src/proxy/jswrapper.h --- js/src/moz.build | 4 ++-- js/src/{jswrapper.cpp => proxy/Wrapper.cpp} | 0 js/src/{ => proxy}/jswrapper.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename js/src/{jswrapper.cpp => proxy/Wrapper.cpp} (100%) rename js/src/{ => proxy}/jswrapper.h (99%) diff --git a/js/src/moz.build b/js/src/moz.build index b729dd012307..281eeff5cd41 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -56,9 +56,9 @@ EXPORTS += [ 'jspubtd.h', 'jstypes.h', 'jsversion.h', - 'jswrapper.h', 'perf/jsperf.h', 'proxy/jsproxy.h', + 'proxy/jswrapper.h', ] # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so @@ -228,7 +228,6 @@ UNIFIED_SOURCES += [ 'jsstr.cpp', 'jswatchpoint.cpp', 'jsweakmap.cpp', - 'jswrapper.cpp', 'perf/jsperf.cpp', 'prmjtime.cpp', 'proxy/BaseProxyHandler.cpp', @@ -236,6 +235,7 @@ UNIFIED_SOURCES += [ 'proxy/Proxy.cpp', 'proxy/ScriptedDirectProxyHandler.cpp', 'proxy/ScriptedIndirectProxyHandler.cpp', + 'proxy/Wrapper.cpp', 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', 'vm/CallNonGenericMethod.cpp', diff --git a/js/src/jswrapper.cpp b/js/src/proxy/Wrapper.cpp similarity index 100% rename from js/src/jswrapper.cpp rename to js/src/proxy/Wrapper.cpp diff --git a/js/src/jswrapper.h b/js/src/proxy/jswrapper.h similarity index 99% rename from js/src/jswrapper.h rename to js/src/proxy/jswrapper.h index f55fea530128..de1fc881b0f6 100644 --- a/js/src/jswrapper.h +++ b/js/src/proxy/jswrapper.h @@ -4,8 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef jswrapper_h -#define jswrapper_h +#ifndef proxy_jswrapper_h +#define proxy_jswrapper_h #include "mozilla/Attributes.h" From fa1db4fb82575f3d034b75ae142ea37fd708d4be Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:46 -0700 Subject: [PATCH 61/69] Bug 1031092 - Part 8: Factor out DeadObjectProxy. (r=bholley) --- js/src/jscompartment.cpp | 1 + js/src/jsfriendapi.cpp | 7 +- js/src/jsgc.cpp | 1 + js/src/moz.build | 1 + js/src/proxy/DeadObjectProxy.cpp | 161 +++++++++++++++++++++++++++++++ js/src/proxy/DeadObjectProxy.h | 61 ++++++++++++ js/src/proxy/Proxy.cpp | 1 + js/src/proxy/Wrapper.cpp | 144 +-------------------------- js/src/proxy/jswrapper.h | 49 ---------- 9 files changed, 229 insertions(+), 197 deletions(-) create mode 100644 js/src/proxy/DeadObjectProxy.cpp create mode 100644 js/src/proxy/DeadObjectProxy.h diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 02ea14eeb7b5..a4ec5300a6e7 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -20,6 +20,7 @@ #include "gc/Marking.h" #include "jit/JitCompartment.h" #include "js/RootingAPI.h" +#include "proxy/DeadObjectProxy.h" #include "vm/Debugger.h" #include "vm/StopIterationObject.h" #include "vm/WrapperObject.h" diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 97a8074b1639..4a4cce1657dc 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -21,6 +21,7 @@ #include "prmjtime.h" #include "builtin/TestingFunctions.h" +#include "proxy/DeadObjectProxy.h" #include "vm/WrapperObject.h" #include "jsobjinlines.h" @@ -597,11 +598,7 @@ JS_GetCustomIteratorCount(JSContext *cx) JS_FRIEND_API(bool) JS_IsDeadWrapper(JSObject *obj) { - if (!obj->is()) { - return false; - } - - return obj->as().handler()->family() == &DeadObjectProxy::family; + return IsDeadProxyObject(obj); } void diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index f7509f682e1d..5e00e2cc9e44 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -218,6 +218,7 @@ #include "jit/BaselineJIT.h" #include "jit/IonCode.h" #include "js/SliceBudget.h" +#include "proxy/DeadObjectProxy.h" #include "vm/Debugger.h" #include "vm/ForkJoin.h" #include "vm/ProxyObject.h" diff --git a/js/src/moz.build b/js/src/moz.build index 281eeff5cd41..c95792b2652b 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -231,6 +231,7 @@ UNIFIED_SOURCES += [ 'perf/jsperf.cpp', 'prmjtime.cpp', 'proxy/BaseProxyHandler.cpp', + 'proxy/DeadObjectProxy.cpp', 'proxy/DirectProxyHandler.cpp', 'proxy/Proxy.cpp', 'proxy/ScriptedDirectProxyHandler.cpp', diff --git a/js/src/proxy/DeadObjectProxy.cpp b/js/src/proxy/DeadObjectProxy.cpp new file mode 100644 index 000000000000..23493fa52b9c --- /dev/null +++ b/js/src/proxy/DeadObjectProxy.cpp @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "proxy/DeadObjectProxy.h" + +#include "jsapi.h" +#include "jsfun.h" // XXXefaust Bug 1064662 + +#include "vm/ProxyObject.h" + +using namespace js; +using namespace js::gc; + +typedef JSPropertyDescriptor PropertyDescriptor; + +bool +DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const +{ + // This is kind of meaningless, but dead-object semantics aside, + // [[Extensible]] always being true is consistent with other proxy types. + *extensible = true; + return true; +} + +bool +DeadObjectProxy::preventExtensions(JSContext *cx, HandleObject proxy) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, + AutoIdVector &props) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, + bool *bp) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +const char * +DeadObjectProxy::className(JSContext *cx, HandleObject wrapper) const +{ + return "DeadObject"; +} + +JSString * +DeadObjectProxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const +{ + return nullptr; +} + +bool +DeadObjectProxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::defaultValue(JSContext *cx, HandleObject obj, JSType hint, + MutableHandleValue vp) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; +} + +bool +DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const +{ + protop.set(nullptr); + return true; +} + +const char DeadObjectProxy::family = 0; +const DeadObjectProxy DeadObjectProxy::singleton; + + +bool +js::IsDeadProxyObject(JSObject *obj) +{ + return obj->is() && + obj->as().handler() == &DeadObjectProxy::singleton; +} diff --git a/js/src/proxy/DeadObjectProxy.h b/js/src/proxy/DeadObjectProxy.h new file mode 100644 index 000000000000..06d7a6aa318e --- /dev/null +++ b/js/src/proxy/DeadObjectProxy.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef proxy_DeadObjectProxy_h +#define proxy_DeadObjectProxy_h + +#include "jsproxy.h" + +namespace js { + +class DeadObjectProxy : public BaseProxyHandler +{ + public: + explicit MOZ_CONSTEXPR DeadObjectProxy() + : BaseProxyHandler(&family) + { } + + /* ES5 Harmony fundamental wrapper traps. */ + virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; + virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const MOZ_OVERRIDE; + virtual bool getOwnPropertyNames(JSContext *cx, HandleObject wrapper, + AutoIdVector &props) const MOZ_OVERRIDE; + virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE; + virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE; + + /* Spidermonkey extensions. */ + virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; + virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; + virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const MOZ_OVERRIDE; + virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, + bool *bp) const MOZ_OVERRIDE; + virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, + JSContext *cx) const MOZ_OVERRIDE; + virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; + virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE; + virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE; + virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, + MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, + MutableHandleObject protop) const MOZ_OVERRIDE; + + static const char family; + static const DeadObjectProxy singleton; +}; + +bool +IsDeadProxyObject(JSObject *obj); + +} /* namespace js */ + +#endif /* proxy_DeadObjectProxy_h */ diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index 8ae74ca5f534..e20477c2e631 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -15,6 +15,7 @@ #include "jswrapper.h" #include "gc/Marking.h" +#include "proxy/DeadObjectProxy.h" #include "proxy/ScriptedDirectProxyHandler.h" #include "proxy/ScriptedIndirectProxyHandler.h" #include "vm/WrapperObject.h" diff --git a/js/src/proxy/Wrapper.cpp b/js/src/proxy/Wrapper.cpp index eb2cc78958d6..6a055c81655a 100644 --- a/js/src/proxy/Wrapper.cpp +++ b/js/src/proxy/Wrapper.cpp @@ -12,6 +12,7 @@ #include "jsgc.h" #include "jsiter.h" +#include "proxy/DeadObjectProxy.h" #include "vm/ErrorObject.h" #include "vm/WrapperObject.h" @@ -720,149 +721,6 @@ SecurityWrapper::unwatch(JSContext *cx, HandleObject proxy, template class js::SecurityWrapper; template class js::SecurityWrapper; -bool -DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const -{ - // This is kind of meaningless, but dead-object semantics aside, - // [[Extensible]] always being true is consistent with other proxy types. - *extensible = true; - return true; -} - -bool -DeadObjectProxy::preventExtensions(JSContext *cx, HandleObject proxy) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, - AutoIdVector &props) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, - bool *bp) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -const char * -DeadObjectProxy::className(JSContext *cx, HandleObject wrapper) const -{ - return "DeadObject"; -} - -JSString * -DeadObjectProxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const -{ - return nullptr; -} - -bool -DeadObjectProxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::defaultValue(JSContext *cx, HandleObject obj, JSType hint, - MutableHandleValue vp) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); - return false; -} - -bool -DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const -{ - protop.set(nullptr); - return true; -} - -const char DeadObjectProxy::family = 0; -const DeadObjectProxy DeadObjectProxy::singleton; - -bool -js::IsDeadProxyObject(JSObject *obj) -{ - return obj->is() && - obj->as().handler() == &DeadObjectProxy::singleton; -} - void js::NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper) { diff --git a/js/src/proxy/jswrapper.h b/js/src/proxy/jswrapper.h index de1fc881b0f6..eef0fe1e86e3 100644 --- a/js/src/proxy/jswrapper.h +++ b/js/src/proxy/jswrapper.h @@ -209,48 +209,6 @@ class JS_FRIEND_API(SecurityWrapper) : public Base typedef SecurityWrapper SameCompartmentSecurityWrapper; typedef SecurityWrapper CrossCompartmentSecurityWrapper; -class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler -{ - public: - explicit MOZ_CONSTEXPR DeadObjectProxy() - : BaseProxyHandler(&family) - { } - - /* ES5 Harmony fundamental wrapper traps. */ - virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const MOZ_OVERRIDE; - virtual bool getOwnPropertyNames(JSContext *cx, HandleObject wrapper, - AutoIdVector &props) const MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE; - virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE; - virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) const MOZ_OVERRIDE; - virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, - bool *bp) const MOZ_OVERRIDE; - virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, - JSContext *cx) const MOZ_OVERRIDE; - virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE; - virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE; - virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE; - virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, - MutableHandleValue vp) const MOZ_OVERRIDE; - virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, - MutableHandleObject protop) const MOZ_OVERRIDE; - - static const char family; - static const DeadObjectProxy singleton; -}; - extern JSObject * TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj, HandleObject parent); @@ -283,13 +241,6 @@ UnwrapOneChecked(JSObject *obj, bool stopAtOuter = true); JS_FRIEND_API(bool) IsCrossCompartmentWrapper(JSObject *obj); -bool -IsDeadProxyObject(JSObject *obj); - -JSObject * -NewDeadProxyObject(JSContext *cx, JSObject *parent, - const ProxyOptions &options = ProxyOptions()); - void NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper); From 4caf5b559e91533693877038d2c90e84ff49b365 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:46 -0700 Subject: [PATCH 62/69] Bug 1031092 - Part 9: Factor out CrossCompartmentWrapper. (r=bholley) --- js/src/moz.build | 1 + js/src/proxy/CrossCompartmentWrapper.cpp | 637 +++++++++++++++++++++++ js/src/proxy/Wrapper.cpp | 625 ---------------------- 3 files changed, 638 insertions(+), 625 deletions(-) create mode 100644 js/src/proxy/CrossCompartmentWrapper.cpp diff --git a/js/src/moz.build b/js/src/moz.build index c95792b2652b..a4d93630b653 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -231,6 +231,7 @@ UNIFIED_SOURCES += [ 'perf/jsperf.cpp', 'prmjtime.cpp', 'proxy/BaseProxyHandler.cpp', + 'proxy/CrossCompartmentWrapper.cpp', 'proxy/DeadObjectProxy.cpp', 'proxy/DirectProxyHandler.cpp', 'proxy/Proxy.cpp', diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp new file mode 100644 index 000000000000..e145da0399ea --- /dev/null +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -0,0 +1,637 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jswrapper.h" + +#include "jsiter.h" + +#include "proxy/DeadObjectProxy.h" +#include "vm/WrapperObject.h" + +#include "jscompartmentinlines.h" +#include "jsobjinlines.h" + +using namespace js; + +#define PIERCE(cx, wrapper, pre, op, post) \ + JS_BEGIN_MACRO \ + bool ok; \ + { \ + AutoCompartment call(cx, wrappedObject(wrapper)); \ + ok = (pre) && (op); \ + } \ + return ok && (post); \ + JS_END_MACRO + +#define NOTHING (true) + +bool +CrossCompartmentWrapper::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::isExtensible(cx, wrapper, extensible), + NOTHING); +} + +bool +CrossCompartmentWrapper::preventExtensions(JSContext *cx, HandleObject wrapper) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::preventExtensions(cx, wrapper), + NOTHING); +} + +bool +CrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::getPropertyDescriptor(cx, wrapper, id, desc), + cx->compartment()->wrap(cx, desc)); +} + +bool +CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc), + cx->compartment()->wrap(cx, desc)); +} + +bool +CrossCompartmentWrapper::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, + MutableHandle desc) const +{ + Rooted desc2(cx, desc); + PIERCE(cx, wrapper, + cx->compartment()->wrap(cx, &desc2), + Wrapper::defineProperty(cx, wrapper, id, &desc2), + NOTHING); +} + +bool +CrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, + AutoIdVector &props) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::getOwnPropertyNames(cx, wrapper, props), + NOTHING); +} + +bool +CrossCompartmentWrapper::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::delete_(cx, wrapper, id, bp), + NOTHING); +} + +bool +CrossCompartmentWrapper::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::enumerate(cx, wrapper, props), + NOTHING); +} + +bool +CrossCompartmentWrapper::has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::has(cx, wrapper, id, bp), + NOTHING); +} + +bool +CrossCompartmentWrapper::hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::hasOwn(cx, wrapper, id, bp), + NOTHING); +} + +bool +CrossCompartmentWrapper::get(JSContext *cx, HandleObject wrapper, HandleObject receiver, + HandleId id, MutableHandleValue vp) const +{ + RootedObject receiverCopy(cx, receiver); + { + AutoCompartment call(cx, wrappedObject(wrapper)); + if (!cx->compartment()->wrap(cx, &receiverCopy)) + return false; + + if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp)) + return false; + } + return cx->compartment()->wrap(cx, vp); +} + +bool +CrossCompartmentWrapper::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, + HandleId id, bool strict, MutableHandleValue vp) const +{ + RootedObject receiverCopy(cx, receiver); + PIERCE(cx, wrapper, + cx->compartment()->wrap(cx, &receiverCopy) && + cx->compartment()->wrap(cx, vp), + Wrapper::set(cx, wrapper, receiverCopy, id, strict, vp), + NOTHING); +} + +bool +CrossCompartmentWrapper::keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::keys(cx, wrapper, props), + NOTHING); +} + +/* + * We can reify non-escaping iterator objects instead of having to wrap them. This + * allows fast iteration over objects across a compartment boundary. + */ +static bool +CanReify(HandleValue vp) +{ + JSObject *obj; + return vp.isObject() && + (obj = &vp.toObject())->is() && + (obj->as().getNativeIterator()->flags & JSITER_ENUMERATE); +} + +struct AutoCloseIterator +{ + AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(cx, obj) {} + + ~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); } + + void clear() { obj = nullptr; } + + private: + JSContext *cx; + RootedObject obj; +}; + +static bool +Reify(JSContext *cx, JSCompartment *origin, MutableHandleValue vp) +{ + Rooted iterObj(cx, &vp.toObject().as()); + NativeIterator *ni = iterObj->getNativeIterator(); + + AutoCloseIterator close(cx, iterObj); + + /* Wrap the iteratee. */ + RootedObject obj(cx, ni->obj); + if (!origin->wrap(cx, &obj)) + return false; + + /* + * Wrap the elements in the iterator's snapshot. + * N.B. the order of closing/creating iterators is important due to the + * implicit cx->enumerators state. + */ + size_t length = ni->numKeys(); + bool isKeyIter = ni->isKeyIter(); + AutoIdVector keys(cx); + if (length > 0) { + if (!keys.reserve(length)) + return false; + for (size_t i = 0; i < length; ++i) { + RootedId id(cx); + RootedValue v(cx, StringValue(ni->begin()[i])); + if (!ValueToId(cx, v, &id)) + return false; + keys.infallibleAppend(id); + } + } + + close.clear(); + if (!CloseIterator(cx, iterObj)) + return false; + + if (isKeyIter) { + if (!VectorToKeyIterator(cx, obj, ni->flags, keys, vp)) + return false; + } else { + if (!VectorToValueIterator(cx, obj, ni->flags, keys, vp)) + return false; + } + return true; +} + +bool +CrossCompartmentWrapper::iterate(JSContext *cx, HandleObject wrapper, unsigned flags, + MutableHandleValue vp) const +{ + { + AutoCompartment call(cx, wrappedObject(wrapper)); + if (!Wrapper::iterate(cx, wrapper, flags, vp)) + return false; + } + + if (CanReify(vp)) + return Reify(cx, cx->compartment(), vp); + return cx->compartment()->wrap(cx, vp); +} + +bool +CrossCompartmentWrapper::call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const +{ + RootedObject wrapped(cx, wrappedObject(wrapper)); + + { + AutoCompartment call(cx, wrapped); + + args.setCallee(ObjectValue(*wrapped)); + if (!cx->compartment()->wrap(cx, args.mutableThisv())) + return false; + + for (size_t n = 0; n < args.length(); ++n) { + if (!cx->compartment()->wrap(cx, args[n])) + return false; + } + + if (!Wrapper::call(cx, wrapper, args)) + return false; + } + + return cx->compartment()->wrap(cx, args.rval()); +} + +bool +CrossCompartmentWrapper::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const +{ + RootedObject wrapped(cx, wrappedObject(wrapper)); + { + AutoCompartment call(cx, wrapped); + + for (size_t n = 0; n < args.length(); ++n) { + if (!cx->compartment()->wrap(cx, args[n])) + return false; + } + if (!Wrapper::construct(cx, wrapper, args)) + return false; + } + return cx->compartment()->wrap(cx, args.rval()); +} + +bool +CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs srcArgs) const +{ + RootedObject wrapper(cx, &srcArgs.thisv().toObject()); + JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) || + !UncheckedUnwrap(wrapper)->is()); + + RootedObject wrapped(cx, wrappedObject(wrapper)); + { + AutoCompartment call(cx, wrapped); + InvokeArgs dstArgs(cx); + if (!dstArgs.init(srcArgs.length())) + return false; + + Value *src = srcArgs.base(); + Value *srcend = srcArgs.array() + srcArgs.length(); + Value *dst = dstArgs.base(); + + RootedValue source(cx); + for (; src < srcend; ++src, ++dst) { + source = *src; + if (!cx->compartment()->wrap(cx, &source)) + return false; + *dst = source.get(); + + // Handle |this| specially. When we rewrap on the other side of the + // membrane, we might apply a same-compartment security wrapper that + // will stymie this whole process. If that happens, unwrap the wrapper. + // This logic can go away when same-compartment security wrappers go away. + if ((src == srcArgs.base() + 1) && dst->isObject()) { + RootedObject thisObj(cx, &dst->toObject()); + if (thisObj->is() && + Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy()) + { + JS_ASSERT(!thisObj->is()); + *dst = ObjectValue(*Wrapper::wrappedObject(thisObj)); + } + } + } + + if (!CallNonGenericMethod(cx, test, impl, dstArgs)) + return false; + + srcArgs.rval().set(dstArgs.rval()); + } + return cx->compartment()->wrap(cx, srcArgs.rval()); +} + +bool +CrossCompartmentWrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, + bool *bp) const +{ + AutoCompartment call(cx, wrappedObject(wrapper)); + if (!cx->compartment()->wrap(cx, v)) + return false; + return Wrapper::hasInstance(cx, wrapper, v, bp); +} + +const char * +CrossCompartmentWrapper::className(JSContext *cx, HandleObject wrapper) const +{ + AutoCompartment call(cx, wrappedObject(wrapper)); + return Wrapper::className(cx, wrapper); +} + +JSString * +CrossCompartmentWrapper::fun_toString(JSContext *cx, HandleObject wrapper, unsigned indent) const +{ + RootedString str(cx); + { + AutoCompartment call(cx, wrappedObject(wrapper)); + str = Wrapper::fun_toString(cx, wrapper, indent); + if (!str) + return nullptr; + } + if (!cx->compartment()->wrap(cx, str.address())) + return nullptr; + return str; +} + +bool +CrossCompartmentWrapper::regexp_toShared(JSContext *cx, HandleObject wrapper, RegExpGuard *g) const +{ + RegExpGuard wrapperGuard(cx); + { + AutoCompartment call(cx, wrappedObject(wrapper)); + if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard)) + return false; + } + + // Get an equivalent RegExpShared associated with the current compartment. + RegExpShared *re = wrapperGuard.re(); + return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g); +} + +bool +CrossCompartmentWrapper::boxedValue_unbox(JSContext *cx, HandleObject wrapper, MutableHandleValue vp) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::boxedValue_unbox(cx, wrapper, vp), + cx->compartment()->wrap(cx, vp)); +} + +bool +CrossCompartmentWrapper::defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, + MutableHandleValue vp) const +{ + PIERCE(cx, wrapper, + NOTHING, + Wrapper::defaultValue(cx, wrapper, hint, vp), + cx->compartment()->wrap(cx, vp)); +} + +bool +CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, + MutableHandleObject protop) const +{ + { + RootedObject wrapped(cx, wrappedObject(wrapper)); + AutoCompartment call(cx, wrapped); + if (!JSObject::getProto(cx, wrapped, protop)) + return false; + if (protop) + protop->setDelegate(cx); + } + + return cx->compartment()->wrap(cx, protop); +} + +bool +CrossCompartmentWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper, + HandleObject proto, bool *bp) const +{ + RootedObject protoCopy(cx, proto); + PIERCE(cx, wrapper, + cx->compartment()->wrap(cx, &protoCopy), + Wrapper::setPrototypeOf(cx, wrapper, protoCopy, bp), + NOTHING); +} + +const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u); + +bool +js::IsCrossCompartmentWrapper(JSObject *obj) +{ + return IsWrapper(obj) && + !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT); +} + +void +js::NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper) +{ + JS_ASSERT(wrapper->is()); + + NotifyGCNukeWrapper(wrapper); + + wrapper->as().nuke(&DeadObjectProxy::singleton); + + JS_ASSERT(IsDeadProxyObject(wrapper)); +} + +/* + * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts + * all of the cross-compartment wrappers that point to objects parented to + * obj's global. The snag here is that we need to avoid cutting wrappers that + * point to the window object on page navigation (inner window destruction) + * and only do that on tab close (outer window destruction). Thus the + * option of how to handle the global object. + */ +JS_FRIEND_API(bool) +js::NukeCrossCompartmentWrappers(JSContext* cx, + const CompartmentFilter& sourceFilter, + const CompartmentFilter& targetFilter, + js::NukeReferencesToWindow nukeReferencesToWindow) +{ + CHECK_REQUEST(cx); + JSRuntime *rt = cx->runtime(); + + // Iterate through scopes looking for system cross compartment wrappers + // that point to an object that shares a global with obj. + + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + if (!sourceFilter.match(c)) + continue; + + // Iterate the wrappers looking for anything interesting. + for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { + // Some cross-compartment wrappers are for strings. We're not + // interested in those. + const CrossCompartmentKey &k = e.front().key(); + if (k.kind != CrossCompartmentKey::ObjectWrapper) + continue; + + AutoWrapperRooter wobj(cx, WrapperValue(e)); + JSObject *wrapped = UncheckedUnwrap(wobj); + + if (nukeReferencesToWindow == DontNukeWindowReferences && + wrapped->getClass()->ext.innerObject) + continue; + + if (targetFilter.match(wrapped->compartment())) { + // We found a wrapper to nuke. + e.removeFront(); + NukeCrossCompartmentWrapper(cx, wobj); + } + } + } + + return true; +} + +// Given a cross-compartment wrapper |wobj|, update it to point to +// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be +// useful even if wrapper already points to newTarget. +bool +js::RemapWrapper(JSContext *cx, JSObject *wobjArg, JSObject *newTargetArg) +{ + RootedObject wobj(cx, wobjArg); + RootedObject newTarget(cx, newTargetArg); + JS_ASSERT(wobj->is()); + JS_ASSERT(!newTarget->is()); + JSObject *origTarget = Wrapper::wrappedObject(wobj); + JS_ASSERT(origTarget); + Value origv = ObjectValue(*origTarget); + JSCompartment *wcompartment = wobj->compartment(); + + AutoDisableProxyCheck adpc(cx->runtime()); + + // If we're mapping to a different target (as opposed to just recomputing + // for the same target), we must not have an existing wrapper for the new + // target, otherwise this will break. + JS_ASSERT_IF(origTarget != newTarget, + !wcompartment->lookupWrapper(ObjectValue(*newTarget))); + + // The old value should still be in the cross-compartment wrapper map, and + // the lookup should return wobj. + WrapperMap::Ptr p = wcompartment->lookupWrapper(origv); + JS_ASSERT(&p->value().unsafeGet()->toObject() == wobj); + wcompartment->removeWrapper(p); + + // When we remove origv from the wrapper map, its wrapper, wobj, must + // immediately cease to be a cross-compartment wrapper. Neuter it. + NukeCrossCompartmentWrapper(cx, wobj); + + // First, we wrap it in the new compartment. We try to use the existing + // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has + // the choice to reuse |wobj| or not. + RootedObject tobj(cx, newTarget); + AutoCompartment ac(cx, wobj); + if (!wcompartment->wrap(cx, &tobj, wobj)) + MOZ_CRASH(); + + // If wrap() reused |wobj|, it will have overwritten it and returned with + // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj| + // will still be nuked. In the latter case, we replace |wobj| with the + // contents of the new wrapper in |tobj|. + if (tobj != wobj) { + // Now, because we need to maintain object identity, we do a brain + // transplant on the old object so that it contains the contents of the + // new one. + if (!JSObject::swap(cx, wobj, tobj)) + MOZ_CRASH(); + } + + // Before swapping, this wrapper came out of wrap(), which enforces the + // invariant that the wrapper in the map points directly to the key. + JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget); + + // Update the entry in the compartment's wrapper map to point to the old + // wrapper, which has now been updated (via reuse or swap). + JS_ASSERT(wobj->is()); + wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj)); + return true; +} + +// Remap all cross-compartment wrappers pointing to |oldTarget| to point to +// |newTarget|. All wrappers are recomputed. +JS_FRIEND_API(bool) +js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTargetArg, + JSObject *newTargetArg) +{ + RootedValue origv(cx, ObjectValue(*oldTargetArg)); + RootedObject newTarget(cx, newTargetArg); + + AutoWrapperVector toTransplant(cx); + if (!toTransplant.reserve(cx->runtime()->numCompartments)) + return false; + + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { + if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) { + // We found a wrapper. Remember and root it. + toTransplant.infallibleAppend(WrapperValue(wp)); + } + } + + for (WrapperValue *begin = toTransplant.begin(), *end = toTransplant.end(); + begin != end; ++begin) + { + if (!RemapWrapper(cx, &begin->toObject(), newTarget)) + MOZ_CRASH(); + } + + return true; +} + +JS_FRIEND_API(bool) +js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, + const CompartmentFilter &targetFilter) +{ + AutoWrapperVector toRecompute(cx); + + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { + // Filter by source compartment. + if (!sourceFilter.match(c)) + continue; + + // Iterate over the wrappers, filtering appropriately. + for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { + // Filter out non-objects. + const CrossCompartmentKey &k = e.front().key(); + if (k.kind != CrossCompartmentKey::ObjectWrapper) + continue; + + // Filter by target compartment. + if (!targetFilter.match(static_cast(k.wrapped)->compartment())) + continue; + + // Add it to the list. + if (!toRecompute.append(WrapperValue(e))) + return false; + } + } + + // Recompute all the wrappers in the list. + for (WrapperValue *begin = toRecompute.begin(), *end = toRecompute.end(); begin != end; ++begin) + { + JSObject *wrapper = &begin->toObject(); + JSObject *wrapped = Wrapper::wrappedObject(wrapper); + if (!RemapWrapper(cx, wrapper, wrapped)) + MOZ_CRASH(); + } + + return true; +} diff --git a/js/src/proxy/Wrapper.cpp b/js/src/proxy/Wrapper.cpp index 6a055c81655a..836089abd736 100644 --- a/js/src/proxy/Wrapper.cpp +++ b/js/src/proxy/Wrapper.cpp @@ -9,17 +9,13 @@ #include "jscntxt.h" #include "jscompartment.h" #include "jsexn.h" -#include "jsgc.h" -#include "jsiter.h" -#include "proxy/DeadObjectProxy.h" #include "vm/ErrorObject.h" #include "vm/WrapperObject.h" #include "jsobjinlines.h" using namespace js; -using namespace js::gc; /* * Wrapper forwards this call directly to the wrapped object for efficiency @@ -122,13 +118,6 @@ js::UnwrapOneChecked(JSObject *obj, bool stopAtOuter) return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj); } -bool -js::IsCrossCompartmentWrapper(JSObject *obj) -{ - return IsWrapper(obj) && - !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT); -} - const char Wrapper::family = 0; const Wrapper Wrapper::singleton((unsigned)0); const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true); @@ -161,8 +150,6 @@ ErrorCopier::~ErrorCopier() } } -/* Cross compartment wrappers. */ - bool Wrapper::finalizeInBackground(Value priv) const { if (!priv.isObject()) @@ -180,423 +167,6 @@ bool Wrapper::finalizeInBackground(Value priv) const return IsBackgroundFinalized(priv.toObject().tenuredGetAllocKind()); } -#define PIERCE(cx, wrapper, pre, op, post) \ - JS_BEGIN_MACRO \ - bool ok; \ - { \ - AutoCompartment call(cx, wrappedObject(wrapper)); \ - ok = (pre) && (op); \ - } \ - return ok && (post); \ - JS_END_MACRO - -#define NOTHING (true) - -bool -CrossCompartmentWrapper::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::isExtensible(cx, wrapper, extensible), - NOTHING); -} - -bool -CrossCompartmentWrapper::preventExtensions(JSContext *cx, HandleObject wrapper) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::preventExtensions(cx, wrapper), - NOTHING); -} - -bool -CrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::getPropertyDescriptor(cx, wrapper, id, desc), - cx->compartment()->wrap(cx, desc)); -} - -bool -CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc), - cx->compartment()->wrap(cx, desc)); -} - -bool -CrossCompartmentWrapper::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const -{ - Rooted desc2(cx, desc); - PIERCE(cx, wrapper, - cx->compartment()->wrap(cx, &desc2), - Wrapper::defineProperty(cx, wrapper, id, &desc2), - NOTHING); -} - -bool -CrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, - AutoIdVector &props) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::getOwnPropertyNames(cx, wrapper, props), - NOTHING); -} - -bool -CrossCompartmentWrapper::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::delete_(cx, wrapper, id, bp), - NOTHING); -} - -bool -CrossCompartmentWrapper::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::enumerate(cx, wrapper, props), - NOTHING); -} - -bool -CrossCompartmentWrapper::has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::has(cx, wrapper, id, bp), - NOTHING); -} - -bool -CrossCompartmentWrapper::hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::hasOwn(cx, wrapper, id, bp), - NOTHING); -} - -bool -CrossCompartmentWrapper::get(JSContext *cx, HandleObject wrapper, HandleObject receiver, - HandleId id, MutableHandleValue vp) const -{ - RootedObject receiverCopy(cx, receiver); - { - AutoCompartment call(cx, wrappedObject(wrapper)); - if (!cx->compartment()->wrap(cx, &receiverCopy)) - return false; - - if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp)) - return false; - } - return cx->compartment()->wrap(cx, vp); -} - -bool -CrossCompartmentWrapper::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const -{ - RootedObject receiverCopy(cx, receiver); - PIERCE(cx, wrapper, - cx->compartment()->wrap(cx, &receiverCopy) && - cx->compartment()->wrap(cx, vp), - Wrapper::set(cx, wrapper, receiverCopy, id, strict, vp), - NOTHING); -} - -bool -CrossCompartmentWrapper::keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::keys(cx, wrapper, props), - NOTHING); -} - -/* - * We can reify non-escaping iterator objects instead of having to wrap them. This - * allows fast iteration over objects across a compartment boundary. - */ -static bool -CanReify(HandleValue vp) -{ - JSObject *obj; - return vp.isObject() && - (obj = &vp.toObject())->is() && - (obj->as().getNativeIterator()->flags & JSITER_ENUMERATE); -} - -struct AutoCloseIterator -{ - AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(cx, obj) {} - - ~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); } - - void clear() { obj = nullptr; } - - private: - JSContext *cx; - RootedObject obj; -}; - -static bool -Reify(JSContext *cx, JSCompartment *origin, MutableHandleValue vp) -{ - Rooted iterObj(cx, &vp.toObject().as()); - NativeIterator *ni = iterObj->getNativeIterator(); - - AutoCloseIterator close(cx, iterObj); - - /* Wrap the iteratee. */ - RootedObject obj(cx, ni->obj); - if (!origin->wrap(cx, &obj)) - return false; - - /* - * Wrap the elements in the iterator's snapshot. - * N.B. the order of closing/creating iterators is important due to the - * implicit cx->enumerators state. - */ - size_t length = ni->numKeys(); - bool isKeyIter = ni->isKeyIter(); - AutoIdVector keys(cx); - if (length > 0) { - if (!keys.reserve(length)) - return false; - for (size_t i = 0; i < length; ++i) { - RootedId id(cx); - RootedValue v(cx, StringValue(ni->begin()[i])); - if (!ValueToId(cx, v, &id)) - return false; - keys.infallibleAppend(id); - } - } - - close.clear(); - if (!CloseIterator(cx, iterObj)) - return false; - - if (isKeyIter) { - if (!VectorToKeyIterator(cx, obj, ni->flags, keys, vp)) - return false; - } else { - if (!VectorToValueIterator(cx, obj, ni->flags, keys, vp)) - return false; - } - return true; -} - -bool -CrossCompartmentWrapper::iterate(JSContext *cx, HandleObject wrapper, unsigned flags, - MutableHandleValue vp) const -{ - { - AutoCompartment call(cx, wrappedObject(wrapper)); - if (!Wrapper::iterate(cx, wrapper, flags, vp)) - return false; - } - - if (CanReify(vp)) - return Reify(cx, cx->compartment(), vp); - return cx->compartment()->wrap(cx, vp); -} - -bool -CrossCompartmentWrapper::call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const -{ - RootedObject wrapped(cx, wrappedObject(wrapper)); - - { - AutoCompartment call(cx, wrapped); - - args.setCallee(ObjectValue(*wrapped)); - if (!cx->compartment()->wrap(cx, args.mutableThisv())) - return false; - - for (size_t n = 0; n < args.length(); ++n) { - if (!cx->compartment()->wrap(cx, args[n])) - return false; - } - - if (!Wrapper::call(cx, wrapper, args)) - return false; - } - - return cx->compartment()->wrap(cx, args.rval()); -} - -bool -CrossCompartmentWrapper::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const -{ - RootedObject wrapped(cx, wrappedObject(wrapper)); - { - AutoCompartment call(cx, wrapped); - - for (size_t n = 0; n < args.length(); ++n) { - if (!cx->compartment()->wrap(cx, args[n])) - return false; - } - if (!Wrapper::construct(cx, wrapper, args)) - return false; - } - return cx->compartment()->wrap(cx, args.rval()); -} - -bool -CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs srcArgs) const -{ - RootedObject wrapper(cx, &srcArgs.thisv().toObject()); - JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) || - !UncheckedUnwrap(wrapper)->is()); - - RootedObject wrapped(cx, wrappedObject(wrapper)); - { - AutoCompartment call(cx, wrapped); - InvokeArgs dstArgs(cx); - if (!dstArgs.init(srcArgs.length())) - return false; - - Value *src = srcArgs.base(); - Value *srcend = srcArgs.array() + srcArgs.length(); - Value *dst = dstArgs.base(); - - RootedValue source(cx); - for (; src < srcend; ++src, ++dst) { - source = *src; - if (!cx->compartment()->wrap(cx, &source)) - return false; - *dst = source.get(); - - // Handle |this| specially. When we rewrap on the other side of the - // membrane, we might apply a same-compartment security wrapper that - // will stymie this whole process. If that happens, unwrap the wrapper. - // This logic can go away when same-compartment security wrappers go away. - if ((src == srcArgs.base() + 1) && dst->isObject()) { - RootedObject thisObj(cx, &dst->toObject()); - if (thisObj->is() && - Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy()) - { - JS_ASSERT(!thisObj->is()); - *dst = ObjectValue(*Wrapper::wrappedObject(thisObj)); - } - } - } - - if (!CallNonGenericMethod(cx, test, impl, dstArgs)) - return false; - - srcArgs.rval().set(dstArgs.rval()); - } - return cx->compartment()->wrap(cx, srcArgs.rval()); -} - -bool -CrossCompartmentWrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, - bool *bp) const -{ - AutoCompartment call(cx, wrappedObject(wrapper)); - if (!cx->compartment()->wrap(cx, v)) - return false; - return Wrapper::hasInstance(cx, wrapper, v, bp); -} - -const char * -CrossCompartmentWrapper::className(JSContext *cx, HandleObject wrapper) const -{ - AutoCompartment call(cx, wrappedObject(wrapper)); - return Wrapper::className(cx, wrapper); -} - -JSString * -CrossCompartmentWrapper::fun_toString(JSContext *cx, HandleObject wrapper, unsigned indent) const -{ - RootedString str(cx); - { - AutoCompartment call(cx, wrappedObject(wrapper)); - str = Wrapper::fun_toString(cx, wrapper, indent); - if (!str) - return nullptr; - } - if (!cx->compartment()->wrap(cx, str.address())) - return nullptr; - return str; -} - -bool -CrossCompartmentWrapper::regexp_toShared(JSContext *cx, HandleObject wrapper, RegExpGuard *g) const -{ - RegExpGuard wrapperGuard(cx); - { - AutoCompartment call(cx, wrappedObject(wrapper)); - if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard)) - return false; - } - - // Get an equivalent RegExpShared associated with the current compartment. - RegExpShared *re = wrapperGuard.re(); - return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g); -} - -bool -CrossCompartmentWrapper::boxedValue_unbox(JSContext *cx, HandleObject wrapper, MutableHandleValue vp) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::boxedValue_unbox(cx, wrapper, vp), - cx->compartment()->wrap(cx, vp)); -} - -bool -CrossCompartmentWrapper::defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, - MutableHandleValue vp) const -{ - PIERCE(cx, wrapper, - NOTHING, - Wrapper::defaultValue(cx, wrapper, hint, vp), - cx->compartment()->wrap(cx, vp)); -} - -bool -CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, - MutableHandleObject protop) const -{ - { - RootedObject wrapped(cx, wrappedObject(wrapper)); - AutoCompartment call(cx, wrapped); - if (!JSObject::getProto(cx, wrapped, protop)) - return false; - if (protop) - protop->setDelegate(cx); - } - - return cx->compartment()->wrap(cx, protop); -} - -bool -CrossCompartmentWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper, - HandleObject proto, bool *bp) const -{ - RootedObject protoCopy(cx, proto); - PIERCE(cx, wrapper, - cx->compartment()->wrap(cx, &protoCopy), - Wrapper::setPrototypeOf(cx, wrapper, protoCopy, bp), - NOTHING); -} - -const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u); - /* Security wrappers. */ template @@ -721,198 +291,3 @@ SecurityWrapper::unwatch(JSContext *cx, HandleObject proxy, template class js::SecurityWrapper; template class js::SecurityWrapper; -void -js::NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper) -{ - JS_ASSERT(wrapper->is()); - - NotifyGCNukeWrapper(wrapper); - - wrapper->as().nuke(&DeadObjectProxy::singleton); - - JS_ASSERT(IsDeadProxyObject(wrapper)); -} - -/* - * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts - * all of the cross-compartment wrappers that point to objects parented to - * obj's global. The snag here is that we need to avoid cutting wrappers that - * point to the window object on page navigation (inner window destruction) - * and only do that on tab close (outer window destruction). Thus the - * option of how to handle the global object. - */ -JS_FRIEND_API(bool) -js::NukeCrossCompartmentWrappers(JSContext* cx, - const CompartmentFilter& sourceFilter, - const CompartmentFilter& targetFilter, - js::NukeReferencesToWindow nukeReferencesToWindow) -{ - CHECK_REQUEST(cx); - JSRuntime *rt = cx->runtime(); - - // Iterate through scopes looking for system cross compartment wrappers - // that point to an object that shares a global with obj. - - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - if (!sourceFilter.match(c)) - continue; - - // Iterate the wrappers looking for anything interesting. - for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { - // Some cross-compartment wrappers are for strings. We're not - // interested in those. - const CrossCompartmentKey &k = e.front().key(); - if (k.kind != CrossCompartmentKey::ObjectWrapper) - continue; - - AutoWrapperRooter wobj(cx, WrapperValue(e)); - JSObject *wrapped = UncheckedUnwrap(wobj); - - if (nukeReferencesToWindow == DontNukeWindowReferences && - wrapped->getClass()->ext.innerObject) - continue; - - if (targetFilter.match(wrapped->compartment())) { - // We found a wrapper to nuke. - e.removeFront(); - NukeCrossCompartmentWrapper(cx, wobj); - } - } - } - - return true; -} - -// Given a cross-compartment wrapper |wobj|, update it to point to -// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be -// useful even if wrapper already points to newTarget. -bool -js::RemapWrapper(JSContext *cx, JSObject *wobjArg, JSObject *newTargetArg) -{ - RootedObject wobj(cx, wobjArg); - RootedObject newTarget(cx, newTargetArg); - JS_ASSERT(wobj->is()); - JS_ASSERT(!newTarget->is()); - JSObject *origTarget = Wrapper::wrappedObject(wobj); - JS_ASSERT(origTarget); - Value origv = ObjectValue(*origTarget); - JSCompartment *wcompartment = wobj->compartment(); - - AutoDisableProxyCheck adpc(cx->runtime()); - - // If we're mapping to a different target (as opposed to just recomputing - // for the same target), we must not have an existing wrapper for the new - // target, otherwise this will break. - JS_ASSERT_IF(origTarget != newTarget, - !wcompartment->lookupWrapper(ObjectValue(*newTarget))); - - // The old value should still be in the cross-compartment wrapper map, and - // the lookup should return wobj. - WrapperMap::Ptr p = wcompartment->lookupWrapper(origv); - JS_ASSERT(&p->value().unsafeGet()->toObject() == wobj); - wcompartment->removeWrapper(p); - - // When we remove origv from the wrapper map, its wrapper, wobj, must - // immediately cease to be a cross-compartment wrapper. Neuter it. - NukeCrossCompartmentWrapper(cx, wobj); - - // First, we wrap it in the new compartment. We try to use the existing - // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has - // the choice to reuse |wobj| or not. - RootedObject tobj(cx, newTarget); - AutoCompartment ac(cx, wobj); - if (!wcompartment->wrap(cx, &tobj, wobj)) - MOZ_CRASH(); - - // If wrap() reused |wobj|, it will have overwritten it and returned with - // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj| - // will still be nuked. In the latter case, we replace |wobj| with the - // contents of the new wrapper in |tobj|. - if (tobj != wobj) { - // Now, because we need to maintain object identity, we do a brain - // transplant on the old object so that it contains the contents of the - // new one. - if (!JSObject::swap(cx, wobj, tobj)) - MOZ_CRASH(); - } - - // Before swapping, this wrapper came out of wrap(), which enforces the - // invariant that the wrapper in the map points directly to the key. - JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget); - - // Update the entry in the compartment's wrapper map to point to the old - // wrapper, which has now been updated (via reuse or swap). - JS_ASSERT(wobj->is()); - wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj)); - return true; -} - -// Remap all cross-compartment wrappers pointing to |oldTarget| to point to -// |newTarget|. All wrappers are recomputed. -JS_FRIEND_API(bool) -js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTargetArg, - JSObject *newTargetArg) -{ - RootedValue origv(cx, ObjectValue(*oldTargetArg)); - RootedObject newTarget(cx, newTargetArg); - - AutoWrapperVector toTransplant(cx); - if (!toTransplant.reserve(cx->runtime()->numCompartments)) - return false; - - for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { - if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) { - // We found a wrapper. Remember and root it. - toTransplant.infallibleAppend(WrapperValue(wp)); - } - } - - for (WrapperValue *begin = toTransplant.begin(), *end = toTransplant.end(); - begin != end; ++begin) - { - if (!RemapWrapper(cx, &begin->toObject(), newTarget)) - MOZ_CRASH(); - } - - return true; -} - -JS_FRIEND_API(bool) -js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, - const CompartmentFilter &targetFilter) -{ - AutoWrapperVector toRecompute(cx); - - for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { - // Filter by source compartment. - if (!sourceFilter.match(c)) - continue; - - // Iterate over the wrappers, filtering appropriately. - for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { - // Filter out non-objects. - const CrossCompartmentKey &k = e.front().key(); - if (k.kind != CrossCompartmentKey::ObjectWrapper) - continue; - - // Filter by target compartment. - if (!targetFilter.match(static_cast(k.wrapped)->compartment())) - continue; - - // Add it to the list. - if (!toRecompute.append(WrapperValue(e))) - return false; - } - } - - // Recompute all the wrappers in the list. - for (WrapperValue *begin = toRecompute.begin(), *end = toRecompute.end(); begin != end; ++begin) - { - JSObject *wrapper = &begin->toObject(); - JSObject *wrapped = Wrapper::wrappedObject(wrapper); - if (!RemapWrapper(cx, wrapper, wrapped)) - MOZ_CRASH(); - } - - return true; -} From 88719d2f943bc81fc0329b8bedb263dc048f1ed2 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 12:09:46 -0700 Subject: [PATCH 63/69] Bug 1031092 - Part 10: Factor out SecurityWrapper. (r=bholley) --- js/src/moz.build | 1 + js/src/proxy/SecurityWrapper.cpp | 135 +++++++++++++++++++++++++++++++ js/src/proxy/Wrapper.cpp | 125 ---------------------------- 3 files changed, 136 insertions(+), 125 deletions(-) create mode 100644 js/src/proxy/SecurityWrapper.cpp diff --git a/js/src/moz.build b/js/src/moz.build index a4d93630b653..69cec8ee7bf5 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -237,6 +237,7 @@ UNIFIED_SOURCES += [ 'proxy/Proxy.cpp', 'proxy/ScriptedDirectProxyHandler.cpp', 'proxy/ScriptedIndirectProxyHandler.cpp', + 'proxy/SecurityWrapper.cpp', 'proxy/Wrapper.cpp', 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', diff --git a/js/src/proxy/SecurityWrapper.cpp b/js/src/proxy/SecurityWrapper.cpp new file mode 100644 index 000000000000..c830a4ea15cc --- /dev/null +++ b/js/src/proxy/SecurityWrapper.cpp @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jswrapper.h" + +#include "jsapi.h" + +#include "jsatominlines.h" + +using namespace js; + +template +bool +SecurityWrapper::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const +{ + // Just like BaseProxyHandler, SecurityWrappers claim by default to always + // be extensible, so as not to leak information about the state of the + // underlying wrapped thing. + *extensible = true; + return true; +} + +template +bool +SecurityWrapper::preventExtensions(JSContext *cx, HandleObject wrapper) const +{ + // See above. + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); + return false; +} + +template +bool +SecurityWrapper::enter(JSContext *cx, HandleObject wrapper, HandleId id, + Wrapper::Action act, bool *bp) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); + *bp = false; + return false; +} + +template +bool +SecurityWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); + return false; +} + +template +bool +SecurityWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper, + HandleObject proto, bool *bp) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); + return false; +} + +// For security wrappers, we run the DefaultValue algorithm on the wrapper +// itself, which means that the existing security policy on operations like +// toString() will take effect and do the right thing here. +template +bool +SecurityWrapper::defaultValue(JSContext *cx, HandleObject wrapper, + JSType hint, MutableHandleValue vp) const +{ + return DefaultValue(cx, wrapper, hint, vp); +} + +template +bool +SecurityWrapper::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const +{ + return false; +} + +template +bool +SecurityWrapper::regexp_toShared(JSContext *cx, HandleObject obj, RegExpGuard *g) const +{ + return Base::regexp_toShared(cx, obj, g); +} + +template +bool +SecurityWrapper::boxedValue_unbox(JSContext *cx, HandleObject obj, MutableHandleValue vp) const +{ + vp.setUndefined(); + return true; +} + +template +bool +SecurityWrapper::defineProperty(JSContext *cx, HandleObject wrapper, + HandleId id, MutableHandle desc) const +{ + if (desc.getter() || desc.setter()) { + JSString *str = IdToString(cx, id); + AutoStableStringChars chars(cx); + const char16_t *prop = nullptr; + if (str->ensureFlat(cx) && chars.initTwoByte(cx, str)) + prop = chars.twoByteChars(); + JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr, + JSMSG_ACCESSOR_DEF_DENIED, prop); + return false; + } + + return Base::defineProperty(cx, wrapper, id, desc); +} + +template +bool +SecurityWrapper::watch(JSContext *cx, HandleObject proxy, + HandleId id, HandleObject callable) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); + return false; +} + +template +bool +SecurityWrapper::unwatch(JSContext *cx, HandleObject proxy, + HandleId id) const +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); + return false; +} + + +template class js::SecurityWrapper; +template class js::SecurityWrapper; diff --git a/js/src/proxy/Wrapper.cpp b/js/src/proxy/Wrapper.cpp index 836089abd736..b9561e46ceb2 100644 --- a/js/src/proxy/Wrapper.cpp +++ b/js/src/proxy/Wrapper.cpp @@ -166,128 +166,3 @@ bool Wrapper::finalizeInBackground(Value priv) const return true; return IsBackgroundFinalized(priv.toObject().tenuredGetAllocKind()); } - -/* Security wrappers. */ - -template -bool -SecurityWrapper::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const -{ - // Just like BaseProxyHandler, SecurityWrappers claim by default to always - // be extensible, so as not to leak information about the state of the - // underlying wrapped thing. - *extensible = true; - return true; -} - -template -bool -SecurityWrapper::preventExtensions(JSContext *cx, HandleObject wrapper) const -{ - // See above. - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); - return false; -} - -template -bool -SecurityWrapper::enter(JSContext *cx, HandleObject wrapper, HandleId id, - Wrapper::Action act, bool *bp) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); - *bp = false; - return false; -} - -template -bool -SecurityWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); - return false; -} - -template -bool -SecurityWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper, - HandleObject proto, bool *bp) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); - return false; -} - -// For security wrappers, we run the DefaultValue algorithm on the wrapper -// itself, which means that the existing security policy on operations like -// toString() will take effect and do the right thing here. -template -bool -SecurityWrapper::defaultValue(JSContext *cx, HandleObject wrapper, - JSType hint, MutableHandleValue vp) const -{ - return DefaultValue(cx, wrapper, hint, vp); -} - -template -bool -SecurityWrapper::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const -{ - return false; -} - -template -bool -SecurityWrapper::regexp_toShared(JSContext *cx, HandleObject obj, RegExpGuard *g) const -{ - return Base::regexp_toShared(cx, obj, g); -} - -template -bool -SecurityWrapper::boxedValue_unbox(JSContext *cx, HandleObject obj, MutableHandleValue vp) const -{ - vp.setUndefined(); - return true; -} - -template -bool -SecurityWrapper::defineProperty(JSContext *cx, HandleObject wrapper, - HandleId id, MutableHandle desc) const -{ - if (desc.getter() || desc.setter()) { - JSString *str = IdToString(cx, id); - AutoStableStringChars chars(cx); - const char16_t *prop = nullptr; - if (str->ensureFlat(cx) && chars.initTwoByte(cx, str)) - prop = chars.twoByteChars(); - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr, - JSMSG_ACCESSOR_DEF_DENIED, prop); - return false; - } - - return Base::defineProperty(cx, wrapper, id, desc); -} - -template -bool -SecurityWrapper::watch(JSContext *cx, HandleObject proxy, - HandleId id, HandleObject callable) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); - return false; -} - -template -bool -SecurityWrapper::unwatch(JSContext *cx, HandleObject proxy, - HandleId id) const -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); - return false; -} - - -template class js::SecurityWrapper; -template class js::SecurityWrapper; - From ad4b772da26e134aee7ef4ce6715f3a791b408b7 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 9 Sep 2014 15:12:14 -0400 Subject: [PATCH 64/69] Bug 1063494 - Properly handle async-scrolled layers that are also fixed/sticky-position. r=botond,BenWa --- .../composite/AsyncCompositionManager.cpp | 18 +++++++++++++----- .../fixed-pos-scrollable-1-ref.html | 8 ++++++++ .../fixed-pos-scrollable-1.html | 15 +++++++++++++++ layout/reftests/async-scrolling/reftest.list | 2 ++ .../sticky-pos-scrollable-1-ref.html | 8 ++++++++ .../sticky-pos-scrollable-1.html | 15 +++++++++++++++ 6 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 layout/reftests/async-scrolling/fixed-pos-scrollable-1-ref.html create mode 100644 layout/reftests/async-scrolling/fixed-pos-scrollable-1.html create mode 100644 layout/reftests/async-scrolling/sticky-pos-scrollable-1-ref.html create mode 100644 layout/reftests/async-scrolling/sticky-pos-scrollable-1.html diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp index 38f623117d45..3807a9b265b0 100644 --- a/gfx/layers/composite/AsyncCompositionManager.cpp +++ b/gfx/layers/composite/AsyncCompositionManager.cpp @@ -138,7 +138,8 @@ GetBaseTransform2D(Layer* aLayer, Matrix* aTransform) static void TranslateShadowLayer2D(Layer* aLayer, - const gfxPoint& aTranslation) + const gfxPoint& aTranslation, + bool aAdjustClipRect) { // This layer might also be a scrollable layer and have an async transform. // To make sure we don't clobber that, we start with the shadow transform. @@ -173,7 +174,7 @@ TranslateShadowLayer2D(Layer* aLayer, layerComposite->SetShadowTransformSetByAnimation(false); const nsIntRect* clipRect = aLayer->GetClipRect(); - if (clipRect) { + if (aAdjustClipRect && clipRect) { nsIntRect transformedClipRect(*clipRect); transformedClipRect.MoveBy(aTranslation.x, aTranslation.y); layerComposite->SetShadowClipRect(&transformedClipRect); @@ -257,7 +258,7 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer, // aTransformedSubtreeRoot. Also, once we do encounter such a child, we don't // need to recurse any deeper because the fixed layers are relative to their // nearest scrollable layer. - if (aLayer == aTransformedSubtreeRoot || !isFixedOrSticky) { + if (!isFixedOrSticky) { // ApplyAsyncContentTransformToTree will call this function again for // nested scrollable layers, so we don't need to recurse if the layer is // scrollable. @@ -349,8 +350,15 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer, IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost()); } - // Finally, apply the 2D translation to the layer transform. - TranslateShadowLayer2D(aLayer, ThebesPoint(translation)); + // Finally, apply the 2D translation to the layer transform. Note that in + // general we need to apply the same translation to the layer's clip rect, so + // that the effective transform on the clip rect takes it back to where it was + // originally, had there been no async scroll. In the case where the + // fixed/sticky layer is the same as aTransformedSubtreeRoot, then the clip + // rect is not affected by the scroll-induced async scroll transform anyway + // (since the clip is applied post-transform) so we don't need to make the + // adjustment. + TranslateShadowLayer2D(aLayer, ThebesPoint(translation), aLayer != aTransformedSubtreeRoot); } static void diff --git a/layout/reftests/async-scrolling/fixed-pos-scrollable-1-ref.html b/layout/reftests/async-scrolling/fixed-pos-scrollable-1-ref.html new file mode 100644 index 000000000000..5996b776ed92 --- /dev/null +++ b/layout/reftests/async-scrolling/fixed-pos-scrollable-1-ref.html @@ -0,0 +1,8 @@ + + + + +
+
+ + diff --git a/layout/reftests/async-scrolling/fixed-pos-scrollable-1.html b/layout/reftests/async-scrolling/fixed-pos-scrollable-1.html new file mode 100644 index 000000000000..cabd57a9d3fe --- /dev/null +++ b/layout/reftests/async-scrolling/fixed-pos-scrollable-1.html @@ -0,0 +1,15 @@ + + + + + +
+
+ + diff --git a/layout/reftests/async-scrolling/reftest.list b/layout/reftests/async-scrolling/reftest.list index 1e8076fe378f..44f81a59c992 100644 --- a/layout/reftests/async-scrolling/reftest.list +++ b/layout/reftests/async-scrolling/reftest.list @@ -12,6 +12,8 @@ pref(layout.async-containerless-scrolling.enabled,true) pref(apz.subframe.enable pref(layout.async-containerless-scrolling.enabled,true) pref(apz.subframe.enabled,true) skip-if(!asyncPanZoom) == split-layers-1.html split-layers-1-ref.html pref(layout.async-containerless-scrolling.enabled,true) pref(apz.subframe.enabled,true) skip-if(!asyncPanZoom) == split-layers-multi-scrolling-1.html split-layers-multi-scrolling-1-ref.html pref(layout.async-containerless-scrolling.enabled,true) pref(apz.subframe.enabled,true) skip-if(!asyncPanZoom) == split-opacity-layers-1.html split-opacity-layers-1-ref.html +pref(layout.async-containerless-scrolling.enabled,true) pref(apz.subframe.enabled,true) skip-if(!asyncPanZoom) == sticky-pos-scrollable-1.html sticky-pos-scrollable-1-ref.html +pref(layout.async-containerless-scrolling.enabled,true) pref(apz.subframe.enabled,true) skip-if(!asyncPanZoom) == fixed-pos-scrollable-1.html fixed-pos-scrollable-1-ref.html pref(layout.async-containerless-scrolling.enabled,false) skip-if(!asyncPanZoom) == bg-fixed-1.html bg-fixed-1-ref.html pref(layout.async-containerless-scrolling.enabled,false) skip-if(!asyncPanZoom) == bg-fixed-cover-1.html bg-fixed-cover-1-ref.html diff --git a/layout/reftests/async-scrolling/sticky-pos-scrollable-1-ref.html b/layout/reftests/async-scrolling/sticky-pos-scrollable-1-ref.html new file mode 100644 index 000000000000..2522f82e2e6b --- /dev/null +++ b/layout/reftests/async-scrolling/sticky-pos-scrollable-1-ref.html @@ -0,0 +1,8 @@ + + + +
+
+
+
+
diff --git a/layout/reftests/async-scrolling/sticky-pos-scrollable-1.html b/layout/reftests/async-scrolling/sticky-pos-scrollable-1.html new file mode 100644 index 000000000000..83c973b39072 --- /dev/null +++ b/layout/reftests/async-scrolling/sticky-pos-scrollable-1.html @@ -0,0 +1,15 @@ + + + +
+ +
+
+
+
From 8c3b097bacfffad3fbc84b341ab7b075b842dde5 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Tue, 9 Sep 2014 14:31:20 -0700 Subject: [PATCH 65/69] Bug 1031092 followup - Give up on moving jsproxy.h and jswrapper.h; fix bustage on a CLOSED TREE. (r=BurningManWasLastWeekRight?) --HG-- rename : js/src/proxy/jsproxy.h => js/src/jsproxy.h rename : js/src/proxy/jswrapper.h => js/src/jswrapper.h --- js/src/{proxy => }/jsproxy.h | 4 ++-- js/src/{proxy => }/jswrapper.h | 4 ++-- js/src/moz.build | 4 ++-- js/src/proxy/CrossCompartmentWrapper.cpp | 3 +-- js/src/proxy/DeadObjectProxy.cpp | 2 -- js/src/proxy/Proxy.cpp | 3 +-- js/src/proxy/SecurityWrapper.cpp | 3 +-- js/src/proxy/Wrapper.cpp | 3 +-- 8 files changed, 10 insertions(+), 16 deletions(-) rename js/src/{proxy => }/jsproxy.h (99%) rename js/src/{proxy => }/jswrapper.h (99%) diff --git a/js/src/proxy/jsproxy.h b/js/src/jsproxy.h similarity index 99% rename from js/src/proxy/jsproxy.h rename to js/src/jsproxy.h index 28fd99e401d8..4d90072c6e17 100644 --- a/js/src/proxy/jsproxy.h +++ b/js/src/jsproxy.h @@ -4,8 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef proxy_jsproxy_h -#define proxy_jsproxy_h +#ifndef jsproxy_h +#define jsproxy_h #include "mozilla/Maybe.h" diff --git a/js/src/proxy/jswrapper.h b/js/src/jswrapper.h similarity index 99% rename from js/src/proxy/jswrapper.h rename to js/src/jswrapper.h index eef0fe1e86e3..1e507a70f10d 100644 --- a/js/src/proxy/jswrapper.h +++ b/js/src/jswrapper.h @@ -4,8 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef proxy_jswrapper_h -#define proxy_jswrapper_h +#ifndef jswrapper_h +#define jswrapper_h #include "mozilla/Attributes.h" diff --git a/js/src/moz.build b/js/src/moz.build index 69cec8ee7bf5..159b48bd6760 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -53,12 +53,12 @@ EXPORTS += [ 'jsfriendapi.h', 'jsprf.h', 'jsprototypes.h', + 'jsproxy.h', 'jspubtd.h', 'jstypes.h', 'jsversion.h', + 'jswrapper.h', 'perf/jsperf.h', - 'proxy/jsproxy.h', - 'proxy/jswrapper.h', ] # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp index e145da0399ea..aa4bc30f7215 100644 --- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -4,9 +4,8 @@ * 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 "jswrapper.h" - #include "jsiter.h" +#include "jswrapper.h" #include "proxy/DeadObjectProxy.h" #include "vm/WrapperObject.h" diff --git a/js/src/proxy/DeadObjectProxy.cpp b/js/src/proxy/DeadObjectProxy.cpp index 23493fa52b9c..600d446fc02b 100644 --- a/js/src/proxy/DeadObjectProxy.cpp +++ b/js/src/proxy/DeadObjectProxy.cpp @@ -14,8 +14,6 @@ using namespace js; using namespace js::gc; -typedef JSPropertyDescriptor PropertyDescriptor; - bool DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const { diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index e20477c2e631..5ae02cd685dc 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -4,14 +4,13 @@ * 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 "jsproxy.h" - #include #include "jsapi.h" #include "jscntxt.h" #include "jsfun.h" #include "jsgc.h" +#include "jsproxy.h" #include "jswrapper.h" #include "gc/Marking.h" diff --git a/js/src/proxy/SecurityWrapper.cpp b/js/src/proxy/SecurityWrapper.cpp index c830a4ea15cc..b41d88f2b87d 100644 --- a/js/src/proxy/SecurityWrapper.cpp +++ b/js/src/proxy/SecurityWrapper.cpp @@ -4,9 +4,8 @@ * 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 "jswrapper.h" - #include "jsapi.h" +#include "jswrapper.h" #include "jsatominlines.h" diff --git a/js/src/proxy/Wrapper.cpp b/js/src/proxy/Wrapper.cpp index b9561e46ceb2..c8d2dd59f573 100644 --- a/js/src/proxy/Wrapper.cpp +++ b/js/src/proxy/Wrapper.cpp @@ -4,11 +4,10 @@ * 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 "jswrapper.h" - #include "jscntxt.h" #include "jscompartment.h" #include "jsexn.h" +#include "jswrapper.h" #include "vm/ErrorObject.h" #include "vm/WrapperObject.h" From 5d8e4ad44912a7ee84ceddb4c2267d99695ee420 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 9 Sep 2014 18:01:07 -0400 Subject: [PATCH 66/69] Bug 1065109 - Remove the MOZ_ASSERT(IsControllingDocuments()) in ServiceWorkerManager CLOSED TREE, r=nsm --- dom/workers/ServiceWorkerManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 32aa670c4f4c..02c55b15a1b8 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -343,7 +343,9 @@ ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& a ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo() { - MOZ_ASSERT(!IsControllingDocuments()); + if (IsControllingDocuments()) { + NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive."); + } } ////////////////////////// From edfc2cb7f767a048e13f3b2579fd1c6d0e491f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Wang?= Date: Wed, 27 Aug 2014 11:15:00 -0400 Subject: [PATCH 67/69] Bug 1059427 - Make GeckoViews ignore accessibility events when they are not "important for accessibility". r=mfinkle --- mobile/android/base/GeckoView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/GeckoView.java b/mobile/android/base/GeckoView.java index ce4e81d20922..dad133cb6f8e 100644 --- a/mobile/android/base/GeckoView.java +++ b/mobile/android/base/GeckoView.java @@ -27,6 +27,7 @@ import android.content.res.TypedArray; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; +import android.view.View; import java.util.ArrayList; import java.util.Collections; @@ -63,7 +64,11 @@ public class GeckoView extends LayerView } else if (event.equals("Prompt:Show") || event.equals("Prompt:ShowTop")) { handlePrompt(message); } else if (event.equals("Accessibility:Event")) { - GeckoAccessibility.sendAccessibilityEvent(message); + int mode = getImportantForAccessibility(); + if (mode == View.IMPORTANT_FOR_ACCESSIBILITY_YES || + mode == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + GeckoAccessibility.sendAccessibilityEvent(message); + } } } catch (Exception e) { Log.e(LOGTAG, "handleMessage threw for " + event, e); From b277edf2b49006411789cf992085b1afd3dac684 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Tue, 9 Sep 2014 09:46:00 -0400 Subject: [PATCH 68/69] Bug 1064935 - Alias ctypes.jschar to ctypes.char16_t. r=cpeterson --- js/src/ctypes/CTypes.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 5ef9156198ea..65f1621f9b0f 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -1296,6 +1296,12 @@ InitTypeClasses(JSContext* cx, HandleObject parent) JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) return false; + // Alias 'ctypes.jschar' as 'ctypes.char16_t' to prevent breaking addons + // that are still using jschar (bug 1064935). + if (!JS_DefineProperty(cx, parent, "jschar", typeObj_char16_t, + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) + return false; + // Create objects representing the special types void_t and voidptr_t. RootedObject typeObj(cx, CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void", From 2691734865553da70b8e4100eaf76fdeff155787 Mon Sep 17 00:00:00 2001 From: Sam Penrose Date: Mon, 8 Sep 2014 16:32:10 -0700 Subject: [PATCH 69/69] Bug 1064606 - Update FxAccountsManager tests with Principal. r=ferjm CLOSED TREE --- .../fxaccounts/tests/xpcshell/test_manager.js | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/services/fxaccounts/tests/xpcshell/test_manager.js b/services/fxaccounts/tests/xpcshell/test_manager.js index b22af36c8038..eaf40a3542d1 100644 --- a/services/fxaccounts/tests/xpcshell/test_manager.js +++ b/services/fxaccounts/tests/xpcshell/test_manager.js @@ -16,6 +16,9 @@ Cu.import("resource://gre/modules/Promise.jsm"); let passwordResetOnServer = false; let deletedOnServer = false; +// Mock RP +let principal = {origin: 'app://settings.gaiamobile.org', appId: 27} + // Override FxAccountsUIGlue. const kFxAccountsUIGlueUUID = "{8f6d5d87-41ed-4bb5-aa28-625de57564c5}"; const kFxAccountsUIGlueContractID = @@ -304,7 +307,7 @@ add_test(function(test_getAssertion_no_audience) { add_test(function(test_getAssertion_no_session_ui_error) { do_print("= getAssertion no session, UI error ="); FxAccountsUIGlue._reject = true; - FxAccountsManager.getAssertion("audience").then( + FxAccountsManager.getAssertion("audience", principal).then( () => { do_throw("Unexpected success"); }, @@ -319,7 +322,7 @@ add_test(function(test_getAssertion_no_session_ui_error) { add_test(function(test_getAssertion_no_session_ui_success) { do_print("= getAssertion no session, UI success ="); - FxAccountsManager.getAssertion("audience").then( + FxAccountsManager.getAssertion("audience", principal).then( () => { do_throw("Unexpected success"); }, @@ -334,7 +337,7 @@ add_test(function(test_getAssertion_no_session_ui_success) { add_test(function(test_getAssertion_active_session_unverified_account) { do_print("= getAssertion active session, unverified account ="); - FxAccountsManager.getAssertion("audience").then( + FxAccountsManager.getAssertion("audience", principal).then( result => { do_throw("Unexpected success"); }, @@ -350,7 +353,7 @@ add_test(function(test_getAssertion_active_session_verified_account) { do_print("= getAssertion active session, verified account ="); FxAccountsManager._fxAccounts._signedInUser.verified = true; FxAccountsManager._activeSession.verified = true; - FxAccountsManager.getAssertion("audience").then( + FxAccountsManager.getAssertion("audience", principal).then( result => { do_check_false(FxAccountsUIGlue._signInFlowCalled); do_check_eq(result, "assertion"); @@ -375,7 +378,7 @@ add_test(function(test_getAssertion_refreshAuth) { FxAccountsManager._activeSession.verified = true; FxAccountsManager._activeSession.authAt = (Date.now() / 1000) - gracePeriod; - FxAccountsManager.getAssertion("audience", { + FxAccountsManager.getAssertion("audience", principal, { "refreshAuthentication": gracePeriod }).then( result => { @@ -396,7 +399,7 @@ add_test(function(test_getAssertion_server_state_change) { FxAccountsManager._fxAccounts._signedInUser.verified = true; FxAccountsManager._activeSession.verified = true; passwordResetOnServer = true; - FxAccountsManager.getAssertion("audience").then( + FxAccountsManager.getAssertion("audience", principal).then( (result) => { // For password reset, the UIGlue mock simulates sucessful // refreshAuth which supplies new password, not signin/signup. @@ -408,7 +411,7 @@ add_test(function(test_getAssertion_server_state_change) { ).then( () => { deletedOnServer = true; - FxAccountsManager.getAssertion("audience").then( + FxAccountsManager.getAssertion("audience", principal).then( (result) => { // For account deletion, the UIGlue's signin/signup is called. do_check_true(FxAccountsUIGlue._signInFlowCalled) @@ -427,7 +430,7 @@ add_test(function(test_getAssertion_server_state_change) { add_test(function(test_getAssertion_refreshAuth_NaN) { do_print("= getAssertion refreshAuth NaN="); let gracePeriod = "NaN"; - FxAccountsManager.getAssertion("audience", { + FxAccountsManager.getAssertion("audience", principal, { "refreshAuthentication": gracePeriod }).then( result => { @@ -449,7 +452,7 @@ add_test(function(test_getAssertion_refresh_auth_no_refresh) { FxAccountsManager._activeSession.verified = true; FxAccountsManager._activeSession.authAt = (Date.now() / 1000) + 10000; - FxAccountsManager.getAssertion("audience", { + FxAccountsManager.getAssertion("audience", principal, { "refreshAuthentication": 1 }).then( result => {