From 3dd2184f4c9356a268219bf3da732070d47e010f Mon Sep 17 00:00:00 2001 From: Eliza Balazs Date: Fri, 24 Aug 2018 02:37:00 +0300 Subject: [PATCH 01/53] Bug 1376771 - Disable browser_mcb_redirect.js on mac and linux for frequent failures. r=jmaher --- browser/base/content/test/siteIdentity/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/test/siteIdentity/browser.ini b/browser/base/content/test/siteIdentity/browser.ini index d46c8d475c98..c0535471ddf3 100644 --- a/browser/base/content/test/siteIdentity/browser.ini +++ b/browser/base/content/test/siteIdentity/browser.ini @@ -56,7 +56,7 @@ support-files = !/toolkit/components/passwordmgr/test/browser/insecure_test.html !/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html [browser_mcb_redirect.js] -skip-if = verify && !debug && os == 'mac' +skip-if = (verify && !debug && os == 'mac') || (os == 'linux') || (os == 'mac') # Bug 1376771 tags = mcb support-files = test_mcb_redirect.html From 7bebfa874ef7df27ab3f6fb7329ad026c64197ab Mon Sep 17 00:00:00 2001 From: Eliza Balazs Date: Fri, 24 Aug 2018 02:25:00 +0300 Subject: [PATCH 02/53] Bug 1356347 - Disable browser_opened_file_tab_navigated_to_web.js on linux and mac for frequent failures. r=jmaher --- browser/base/content/test/tabs/browser.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/base/content/test/tabs/browser.ini b/browser/base/content/test/tabs/browser.ini index 509606f3376a..9e3dc559a5fa 100644 --- a/browser/base/content/test/tabs/browser.ini +++ b/browser/base/content/test/tabs/browser.ini @@ -52,6 +52,7 @@ skip-if = !e10s # Pref and test only relevant for e10s. [browser_newwindow_tabstrip_overflow.js] [browser_open_newtab_start_observer_notification.js] [browser_opened_file_tab_navigated_to_web.js] +skip-if = (os == 'mac' && debug) || (os == 'linux' && debug) # Bug 1356347 [browser_overflowScroll.js] [browser_pinnedTabs_clickOpen.js] [browser_pinnedTabs_closeByKeyboard.js] From 218f188deef66f10ac405314823d8aa01d142aff Mon Sep 17 00:00:00 2001 From: Eliza Balazs Date: Fri, 24 Aug 2018 01:38:00 +0300 Subject: [PATCH 03/53] Bug 1479273 - Disable browser_thumbnails_capture.js on win for frequent failures. r=jmaher --- toolkit/components/thumbnails/test/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/thumbnails/test/browser.ini b/toolkit/components/thumbnails/test/browser.ini index c5eeb9d418c6..f023b56132d7 100644 --- a/toolkit/components/thumbnails/test/browser.ini +++ b/toolkit/components/thumbnails/test/browser.ini @@ -37,7 +37,7 @@ skip-if = verify [browser_thumbnails_bug818225.js] skip-if = (verify && debug && (os == 'linux')) [browser_thumbnails_capture.js] -skip-if = os == "mac" && !debug # bug 1314039 +skip-if = (os == "mac" && !debug) || (os == 'win') # bug 1314039 # Bug 1345418 [browser_thumbnails_expiration.js] [browser_thumbnails_privacy.js] [browser_thumbnails_redirect.js] From eea523aa87cad6c3fe4db51e58bc8ee6ab90177d Mon Sep 17 00:00:00 2001 From: Eliza Balazs Date: Fri, 24 Aug 2018 01:22:00 +0300 Subject: [PATCH 04/53] Bug 1336075 - Disable browser_largeAllocation_non_win32.js on linux and mac for frequent failures. r=jmaher --- dom/tests/browser/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/tests/browser/browser.ini b/dom/tests/browser/browser.ini index 794ded8dd6e0..43cd0374a890 100644 --- a/dom/tests/browser/browser.ini +++ b/dom/tests/browser/browser.ini @@ -52,7 +52,7 @@ run-if = e10s [browser_largeAllocation_win32.js] skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s [browser_largeAllocation_non_win32.js] -skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (os == 'linux')) # Large-Allocation requires e10s +skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (os == 'linux')) || (os == 'linux') || (os = 'mac' && debug) # Large-Allocation requires e10s # Bug 1336075 [browser_localStorage_e10s.js] skip-if = !e10s || verify # This is a test of e10s functionality. [browser_localStorage_privatestorageevent.js] From 160fe540b318fa239ce34bcb8a80d215e6a38e4f Mon Sep 17 00:00:00 2001 From: Margareta Eliza Balazs Date: Fri, 24 Aug 2018 14:56:41 +0300 Subject: [PATCH 05/53] Backed out changeset f2de935788af (bug 1479273) for landing the patch with the wrong bug no. --- toolkit/components/thumbnails/test/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/thumbnails/test/browser.ini b/toolkit/components/thumbnails/test/browser.ini index f023b56132d7..c5eeb9d418c6 100644 --- a/toolkit/components/thumbnails/test/browser.ini +++ b/toolkit/components/thumbnails/test/browser.ini @@ -37,7 +37,7 @@ skip-if = verify [browser_thumbnails_bug818225.js] skip-if = (verify && debug && (os == 'linux')) [browser_thumbnails_capture.js] -skip-if = (os == "mac" && !debug) || (os == 'win') # bug 1314039 # Bug 1345418 +skip-if = os == "mac" && !debug # bug 1314039 [browser_thumbnails_expiration.js] [browser_thumbnails_privacy.js] [browser_thumbnails_redirect.js] From 9a8512dd92110a49d24d4df4ed2037f821e8a0aa Mon Sep 17 00:00:00 2001 From: "Thomas P." Date: Fri, 24 Aug 2018 08:30:24 +0000 Subject: [PATCH 06/53] Bug 1475448 - Add ContentSecurityPolicyParser fuzzing target. r=ckerschb, sr=decoder --- dom/security/fuzztest/csp_fuzzer.cpp | 45 +++++++++++++ dom/security/fuzztest/csp_fuzzer.dict | 95 +++++++++++++++++++++++++++ dom/security/fuzztest/moz.build | 21 ++++++ dom/security/moz.build | 8 +++ 4 files changed, 169 insertions(+) create mode 100644 dom/security/fuzztest/csp_fuzzer.cpp create mode 100644 dom/security/fuzztest/csp_fuzzer.dict create mode 100644 dom/security/fuzztest/moz.build diff --git a/dom/security/fuzztest/csp_fuzzer.cpp b/dom/security/fuzztest/csp_fuzzer.cpp new file mode 100644 index 000000000000..73affaf47adf --- /dev/null +++ b/dom/security/fuzztest/csp_fuzzer.cpp @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "FuzzingInterface.h" +#include "nsCSPContext.h" +#include "nsNetUtil.h" +#include "nsStringFwd.h" + +static int +LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + nsresult ret; + nsCOMPtr selfURI; + ret = NS_NewURI(getter_AddRefs(selfURI), "http://selfuri.com"); + if (ret != NS_OK) + return 0; + + mozilla::OriginAttributes attrs; + nsCOMPtr selfURIPrincipal = + mozilla::BasePrincipal::CreateCodebasePrincipal(selfURI, attrs); + if (!selfURIPrincipal) + return 0; + + nsCOMPtr csp = + do_CreateInstance(NS_CSPCONTEXT_CONTRACTID, &ret); + if (ret != NS_OK) + return 0; + + ret = csp->SetRequestContext(nullptr, selfURIPrincipal); + if (ret != NS_OK) + return 0; + + NS_ConvertASCIItoUTF16 policy(reinterpret_cast(data), size); + if (!policy.get()) + return 0; + csp->AppendPolicy(policy, false, false); + + return 0; +} + +MOZ_FUZZING_INTERFACE_RAW(nullptr, LLVMFuzzerTestOneInput, ContentSecurityPolicyParser); + diff --git a/dom/security/fuzztest/csp_fuzzer.dict b/dom/security/fuzztest/csp_fuzzer.dict new file mode 100644 index 000000000000..37a167122282 --- /dev/null +++ b/dom/security/fuzztest/csp_fuzzer.dict @@ -0,0 +1,95 @@ +### dom/security/nsCSPParser.cpp +# tokens +":" +";" +"/" +"+" +"-" +"." +"_" +"~" +"*" +"'" +"#" +"?" +"%" +"!" +"$" +"&" +"(" +")" +"=" +"@" + +### https://www.w3.org/TR/{CSP,CSP2,CSP3}/ +# directive names +"default-src" +"script-src" +"object-src" +"style-src" +"img-src" +"media-src" +"frame-src" +"font-src" +"connect-src" +"report-uri" +"frame-ancestors" +"reflected-xss" +"base-uri" +"form-action" +"manifest-src" +"upgrade-insecure-requests" +"child-src" +"block-all-mixed-content" +"require-sri-for" +"sandbox" +"worker-src" +"plugin-types" +"disown-opener" +"report-to" + +# directive values +"'self'" +"'unsafe-inline'" +"'unsafe-eval'" +"'none'" +"'strict-dynamic'" +"'unsafe-hashed-attributes'" +"'nonce-AA=='" +"'sha256-fw=='" +"'sha384-/w=='" +"'sha512-//8='" + +# subresources +"a" +"audio" +"embed" +"iframe" +"img" +"link" +"object" +"script" +"source" +"style" +"track" +"video" + +# sandboxing flags +"allow-forms" +"allow-pointer-lock" +"allow-popups" +"allow-same-origin" +"allow-scripts" +"allow-top-navigation" + +# URI components +"https:" +"ws:" +"blob:" +"data:" +"filesystem:" +"javascript:" +"http://" +"selfuri.com" +"127.0.0.1" +"::1" diff --git a/dom/security/fuzztest/moz.build b/dom/security/fuzztest/moz.build new file mode 100644 index 000000000000..400f8d032efb --- /dev/null +++ b/dom/security/fuzztest/moz.build @@ -0,0 +1,21 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Library('FuzzingDOMSecurity') + +LOCAL_INCLUDES += [ + '/dom/security', + '/netwerk/base', +] + +include('/tools/fuzzing/libfuzzer-config.mozbuild') + +SOURCES += [ + 'csp_fuzzer.cpp' +] + +FINAL_LIBRARY = 'xul-gtest' + diff --git a/dom/security/moz.build b/dom/security/moz.build index ebfb490a23f2..bf7b75214f02 100644 --- a/dom/security/moz.build +++ b/dom/security/moz.build @@ -51,3 +51,11 @@ LOCAL_INCLUDES += [ '/netwerk/base', '/netwerk/protocol/data', # for nsDataHandler.h ] + +include('/tools/fuzzing/libfuzzer-config.mozbuild') + +if CONFIG['FUZZING_INTERFACES']: + TEST_DIRS += [ + 'fuzztest' + ] + From e89e6924f57b5345a0400d5e09214b42451f88e2 Mon Sep 17 00:00:00 2001 From: Eliza Balazs Date: Fri, 24 Aug 2018 01:38:00 -0400 Subject: [PATCH 07/53] Bug 1479273 - Disable browser_thumbnails_capture.js on win for frequent failures. r=jmaher --- toolkit/components/thumbnails/test/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/thumbnails/test/browser.ini b/toolkit/components/thumbnails/test/browser.ini index c5eeb9d418c6..f023b56132d7 100644 --- a/toolkit/components/thumbnails/test/browser.ini +++ b/toolkit/components/thumbnails/test/browser.ini @@ -37,7 +37,7 @@ skip-if = verify [browser_thumbnails_bug818225.js] skip-if = (verify && debug && (os == 'linux')) [browser_thumbnails_capture.js] -skip-if = os == "mac" && !debug # bug 1314039 +skip-if = (os == "mac" && !debug) || (os == 'win') # bug 1314039 # Bug 1345418 [browser_thumbnails_expiration.js] [browser_thumbnails_privacy.js] [browser_thumbnails_redirect.js] From 03bb063efc908e0339ee083db1ef1e98f15e71ca Mon Sep 17 00:00:00 2001 From: Margareta Eliza Balazs Date: Fri, 24 Aug 2018 15:21:27 +0300 Subject: [PATCH 08/53] Backed out changeset 9b254e372c54 (bug 1336075) for "=" missing in the skip-if line. CLOSED TREE --- dom/tests/browser/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/tests/browser/browser.ini b/dom/tests/browser/browser.ini index 43cd0374a890..794ded8dd6e0 100644 --- a/dom/tests/browser/browser.ini +++ b/dom/tests/browser/browser.ini @@ -52,7 +52,7 @@ run-if = e10s [browser_largeAllocation_win32.js] skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s [browser_largeAllocation_non_win32.js] -skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (os == 'linux')) || (os == 'linux') || (os = 'mac' && debug) # Large-Allocation requires e10s # Bug 1336075 +skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (os == 'linux')) # Large-Allocation requires e10s [browser_localStorage_e10s.js] skip-if = !e10s || verify # This is a test of e10s functionality. [browser_localStorage_privatestorageevent.js] From 9a5548276d19045feb81dcad717c47063cdbd097 Mon Sep 17 00:00:00 2001 From: Margareta Eliza Balazs Date: Fri, 24 Aug 2018 15:22:24 +0300 Subject: [PATCH 09/53] Backed out changeset bb2a3ca0fa4d (bug 1479273) for landing the patch with the wrong bug number. CLOSED TREE --- toolkit/components/thumbnails/test/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/thumbnails/test/browser.ini b/toolkit/components/thumbnails/test/browser.ini index f023b56132d7..c5eeb9d418c6 100644 --- a/toolkit/components/thumbnails/test/browser.ini +++ b/toolkit/components/thumbnails/test/browser.ini @@ -37,7 +37,7 @@ skip-if = verify [browser_thumbnails_bug818225.js] skip-if = (verify && debug && (os == 'linux')) [browser_thumbnails_capture.js] -skip-if = (os == "mac" && !debug) || (os == 'win') # bug 1314039 # Bug 1345418 +skip-if = os == "mac" && !debug # bug 1314039 [browser_thumbnails_expiration.js] [browser_thumbnails_privacy.js] [browser_thumbnails_redirect.js] From ddfb466039425b99a5a4708af86c84214df0171e Mon Sep 17 00:00:00 2001 From: Eliza Balazs Date: Fri, 24 Aug 2018 01:38:00 +0300 Subject: [PATCH 10/53] Bug 1345418 - Disable browser_thumbnails_capture.js on win for frequent failures. r=jmaher --- toolkit/components/thumbnails/test/browser.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/thumbnails/test/browser.ini b/toolkit/components/thumbnails/test/browser.ini index c5eeb9d418c6..f023b56132d7 100644 --- a/toolkit/components/thumbnails/test/browser.ini +++ b/toolkit/components/thumbnails/test/browser.ini @@ -37,7 +37,7 @@ skip-if = verify [browser_thumbnails_bug818225.js] skip-if = (verify && debug && (os == 'linux')) [browser_thumbnails_capture.js] -skip-if = os == "mac" && !debug # bug 1314039 +skip-if = (os == "mac" && !debug) || (os == 'win') # bug 1314039 # Bug 1345418 [browser_thumbnails_expiration.js] [browser_thumbnails_privacy.js] [browser_thumbnails_redirect.js] From a6d35e41994b1fe0a4f910a1d6f9471a7eab58a5 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Wed, 22 Aug 2018 09:02:13 +0900 Subject: [PATCH 11/53] Bug 1481670 - Add a non-mangled version of the valgrind suppression for SaveWordToEnv. r=njn This is similar to what was done with SaveToEnv in bug 1457999. --- build/valgrind/cross-architecture.sup | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build/valgrind/cross-architecture.sup b/build/valgrind/cross-architecture.sup index a66c90c15161..f2df52d83acd 100644 --- a/build/valgrind/cross-architecture.sup +++ b/build/valgrind/cross-architecture.sup @@ -25,6 +25,13 @@ fun:_ZL13SaveWordToEnvPKcRK12nsTSubstringIcE ... } +{ + PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793549.) + Memcheck:Leak + ... + fun:SaveWordToEnv + ... +} { PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 944133.) Memcheck:Leak From 4adb3fdde00ef63b1e9b539bfd7f1aac2adc1bff Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Wed, 22 Aug 2018 09:11:41 +0900 Subject: [PATCH 12/53] Bug 1481670 - Initialize some stack variables to make valgrind happier. r=jandem clang likes to transform conditions like: if (A && B) into if (B && A) where B might be undefined when A is true (or variants of the above with ! and ||, or split between multiple ifs). This doesn't go well with valgrind, although it's valid (albeit seemingly convoluted). In both cases fixed here, the condition A involves calling a function with a pointer to a variable on the stack that may or may not be filled by the function call, and the condition B checking the value of said variable. --- js/src/jit/IonBuilder.cpp | 2 +- js/src/jit/VMFunctions.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index e6b14f61af33..926b01e502ee 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -11703,7 +11703,7 @@ IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj, TypedObjectPrediction fieldPrediction; size_t fieldOffset; size_t fieldIndex; - bool fieldMutable; + bool fieldMutable = false; if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex, &fieldMutable)) return Ok(); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index d170b0ee3c41..0e4a26ba3fa7 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -781,7 +781,7 @@ GetIndexFromString(JSString* str) if (!str->isFlat()) return -1; - uint32_t index; + uint32_t index = UINT32_MAX; if (!str->asFlat().isIndex(&index) || index > INT32_MAX) return -1; From b97b9da1785ce585fa990e684f6119df18102d4f Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Wed, 22 Aug 2018 09:23:45 +0900 Subject: [PATCH 13/53] Bug 1481670 - Reduce the size of AnyRegister::Code and MIRType. r=jandem For the same reason as previous commit, the code clang generates for ~AutoOutputRegister when inlined in CacheIRCompiler::emitArrayJoinResult makes valgrind unhappy. The reason is that TypedOrValueRegister contains a union with two types of different sizes, where clang makes the code do a check against the larger variant before validating the union tag says that's the type in use, and that doesn't go well under valgrind when the union value was set for the smaller variant. One way to fix this is to make the TypedOrValueRegister constructor fill the uninitialized bytes of the union, but it also appears that the types used in the union, as well as the union tag itself, are larger than they actually need to be: - AnyRegister::Code is a 32-bits integer representing a register number, when no supported platform has more than 254 registers (accounting for AnyRegister::Invalid), - MIRType is a 32-bits integer (enum, actually) representing values that are all below 256. Changing only the former would make valgrind happy, avoiding the uninitialized bytes being tested against in ~AutoOutputRegister, but it was too tempting to do MIRType as well. --- js/src/jit/IonTypes.h | 4 ++-- js/src/jit/RegisterSets.h | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index c094b6c8beb4..6ff3f68ee6a3 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -418,7 +418,7 @@ enum class IntConversionInputKind { // The ordering of this enumeration is important: Anything < Value is a // specialized type. Furthermore, anything < String has trivial conversion to // a number. -enum class MIRType +enum class MIRType: uint8_t { Undefined, Null, @@ -462,7 +462,7 @@ enum class MIRType static inline bool IsSimdType(MIRType type) { - return ((unsigned(type) >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK) != 0; + return ((uint8_t(type) >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK) != 0; } static inline MIRType diff --git a/js/src/jit/RegisterSets.h b/js/src/jit/RegisterSets.h index 1857bbc71a5f..f0f00320a81a 100644 --- a/js/src/jit/RegisterSets.h +++ b/js/src/jit/RegisterSets.h @@ -19,10 +19,13 @@ namespace js { namespace jit { struct AnyRegister { - typedef uint32_t Code; + typedef uint8_t Code; - static const uint32_t Total = Registers::Total + FloatRegisters::Total; - static const uint32_t Invalid = UINT_MAX; + static const uint8_t Total = Registers::Total + FloatRegisters::Total; + static const uint8_t Invalid = UINT8_MAX; + + static_assert(size_t(Registers::Total) + FloatRegisters::Total <= UINT8_MAX, + "Number of registers must fit in uint8_t"); private: Code code_; @@ -36,7 +39,7 @@ struct AnyRegister { explicit AnyRegister(FloatRegister fpu) { code_ = fpu.code() + Registers::Total; } - static AnyRegister FromCode(uint32_t i) { + static AnyRegister FromCode(uint8_t i) { MOZ_ASSERT(i < Total); AnyRegister r; r.code_ = i; @@ -72,7 +75,7 @@ struct AnyRegister { bool volatile_() const { return isFloat() ? fpu().volatile_() : gpr().volatile_(); } - AnyRegister aliased(uint32_t aliasIdx) const { + AnyRegister aliased(uint8_t aliasIdx) const { AnyRegister ret; if (isFloat()) ret = AnyRegister(fpu().aliased(aliasIdx)); @@ -81,7 +84,7 @@ struct AnyRegister { MOZ_ASSERT_IF(aliasIdx == 0, ret == *this); return ret; } - uint32_t numAliased() const { + uint8_t numAliased() const { if (isFloat()) return fpu().numAliased(); return gpr().numAliased(); From 866a1313ee43ae01a09e2cbfa3cdc33f76a228a2 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Wed, 22 Aug 2018 09:43:10 +0900 Subject: [PATCH 14/53] Bug 1485219 - Switch valgrind builds to clang. r=chmanchester --- taskcluster/ci/valgrind/kind.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/taskcluster/ci/valgrind/kind.yml b/taskcluster/ci/valgrind/kind.yml index 758ff5305ce9..04315c18c5f4 100644 --- a/taskcluster/ci/valgrind/kind.yml +++ b/taskcluster/ci/valgrind/kind.yml @@ -31,7 +31,6 @@ jobs: max-run-time: 72000 env: PERFHERDER_EXTRA_OPTIONS: valgrind - FORCE_GCC: '1' run: using: mozharness actions: [get-secrets build valgrind-test] From 4edecb9d3adfd7a609c9a543bc4f6497c7b9377f Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Fri, 17 Aug 2018 09:58:51 +0900 Subject: [PATCH 15/53] Bug 1477602 - Part 1: Move debug target related components to dedicated folder. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D3570 --HG-- rename : devtools/client/aboutdebugging-new/src/components/DebugTargetItem.css => devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css rename : devtools/client/aboutdebugging-new/src/components/DebugTargetItem.js => devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js rename : devtools/client/aboutdebugging-new/src/components/DebugTargetList.css => devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css rename : devtools/client/aboutdebugging-new/src/components/DebugTargetList.js => devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.js rename : devtools/client/aboutdebugging-new/src/components/DebugTargetPane.js => devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js extra : rebase_source : 6f424fe74a7ccdf8accb5243cb4f827fbe141aba --- devtools/client/aboutdebugging-new/aboutdebugging.css | 4 ++-- .../aboutdebugging-new/src/components/RuntimePage.js | 2 +- .../components/{ => debugtarget}/DebugTargetItem.css | 0 .../components/{ => debugtarget}/DebugTargetItem.js | 11 +++++------ .../components/{ => debugtarget}/DebugTargetList.css | 0 .../components/{ => debugtarget}/DebugTargetList.js | 0 .../components/{ => debugtarget}/DebugTargetPane.js | 0 .../src/components/debugtarget/moz.build | 5 +++++ .../aboutdebugging-new/src/components/moz.build | 5 ----- 9 files changed, 13 insertions(+), 14 deletions(-) rename devtools/client/aboutdebugging-new/src/components/{ => debugtarget}/DebugTargetItem.css (100%) rename devtools/client/aboutdebugging-new/src/components/{ => debugtarget}/DebugTargetItem.js (86%) rename devtools/client/aboutdebugging-new/src/components/{ => debugtarget}/DebugTargetList.css (100%) rename devtools/client/aboutdebugging-new/src/components/{ => debugtarget}/DebugTargetList.js (100%) rename devtools/client/aboutdebugging-new/src/components/{ => debugtarget}/DebugTargetPane.js (100%) diff --git a/devtools/client/aboutdebugging-new/aboutdebugging.css b/devtools/client/aboutdebugging-new/aboutdebugging.css index ae9111ef76cd..97e87cc9c9de 100644 --- a/devtools/client/aboutdebugging-new/aboutdebugging.css +++ b/devtools/client/aboutdebugging-new/aboutdebugging.css @@ -5,11 +5,11 @@ @import "chrome://global/skin/in-content/common.css"; @import "resource://devtools/client/aboutdebugging-new/src/components/App.css"; @import "resource://devtools/client/aboutdebugging-new/src/components/ConnectPage.css"; -@import "resource://devtools/client/aboutdebugging-new/src/components/DebugTargetItem.css"; -@import "resource://devtools/client/aboutdebugging-new/src/components/DebugTargetList.css"; @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css"; @import "resource://devtools/client/aboutdebugging-new/src/components/Sidebar.css"; @import "resource://devtools/client/aboutdebugging-new/src/components/SidebarItem.css"; +@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css"; +@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css"; @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionDetail.css"; :root { diff --git a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js index 66bd8aed1fae..b320fa27563d 100644 --- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js +++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js @@ -9,7 +9,7 @@ const { createFactory, PureComponent } = require("devtools/client/shared/vendor/ const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); -const DebugTargetPane = createFactory(require("./DebugTargetPane")); +const DebugTargetPane = createFactory(require("./debugtarget/DebugTargetPane")); const RuntimeInfo = createFactory(require("./RuntimeInfo")); const TemporaryExtensionInstaller = createFactory(require("./debugtarget/TemporaryExtensionInstaller")); diff --git a/devtools/client/aboutdebugging-new/src/components/DebugTargetItem.css b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css similarity index 100% rename from devtools/client/aboutdebugging-new/src/components/DebugTargetItem.css rename to devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css diff --git a/devtools/client/aboutdebugging-new/src/components/DebugTargetItem.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js similarity index 86% rename from devtools/client/aboutdebugging-new/src/components/DebugTargetItem.js rename to devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js index 565e239cc92f..0b27586008df 100644 --- a/devtools/client/aboutdebugging-new/src/components/DebugTargetItem.js +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js @@ -8,13 +8,12 @@ const { createFactory, PureComponent } = require("devtools/client/shared/vendor/ const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); -const ExtensionDetail = createFactory(require("./debugtarget/ExtensionDetail")); -const TabDetail = createFactory(require("./debugtarget/TabDetail")); -const TemporaryExtensionAction = - createFactory(require("./debugtarget/TemporaryExtensionAction")); +const ExtensionDetail = createFactory(require("./ExtensionDetail")); +const TabDetail = createFactory(require("./TabDetail")); +const TemporaryExtensionAction = createFactory(require("./TemporaryExtensionAction")); -const Actions = require("../actions/index"); -const { DEBUG_TARGETS } = require("../constants"); +const Actions = require("../../actions/index"); +const { DEBUG_TARGETS } = require("../../constants"); /** * This component displays debug target. diff --git a/devtools/client/aboutdebugging-new/src/components/DebugTargetList.css b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css similarity index 100% rename from devtools/client/aboutdebugging-new/src/components/DebugTargetList.css rename to devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css diff --git a/devtools/client/aboutdebugging-new/src/components/DebugTargetList.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.js similarity index 100% rename from devtools/client/aboutdebugging-new/src/components/DebugTargetList.js rename to devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.js diff --git a/devtools/client/aboutdebugging-new/src/components/DebugTargetPane.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js similarity index 100% rename from devtools/client/aboutdebugging-new/src/components/DebugTargetPane.js rename to devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build index 3070d2e879c2..27c84ea6c6b5 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build @@ -3,6 +3,11 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'DebugTargetItem.css', + 'DebugTargetItem.js', + 'DebugTargetList.css', + 'DebugTargetList.js', + 'DebugTargetPane.js', 'ExtensionDetail.css', 'ExtensionDetail.js', 'TabDetail.js', diff --git a/devtools/client/aboutdebugging-new/src/components/moz.build b/devtools/client/aboutdebugging-new/src/components/moz.build index d43450465aaf..b61217005c8c 100644 --- a/devtools/client/aboutdebugging-new/src/components/moz.build +++ b/devtools/client/aboutdebugging-new/src/components/moz.build @@ -11,11 +11,6 @@ DevToolsModules( 'App.js', 'ConnectPage.css', 'ConnectPage.js', - 'DebugTargetItem.css', - 'DebugTargetItem.js', - 'DebugTargetList.css', - 'DebugTargetList.js', - 'DebugTargetPane.js', 'RuntimeInfo.css', 'RuntimeInfo.js', 'RuntimePage.js', From 09d9e8f69430434ea63789fe328e1da8cea9ffe9 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Fri, 17 Aug 2018 09:58:52 +0900 Subject: [PATCH 16/53] Bug 1477602 - Part 2: Implement a mechanism to dispatch workers. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D3571 Depends on D3570 --HG-- extra : rebase_source : 6c8e4f53f323b6f5522cabdbbb374bf440ba72b1 --- .../aboutdebugging-new/src/actions/runtime.js | 30 +++++++++++++++++++ .../src/components/RuntimePage.js | 29 +++++++++++++++++- .../aboutdebugging-new/src/constants.js | 3 ++ .../src/reducers/runtime-state.js | 12 ++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/devtools/client/aboutdebugging-new/src/actions/runtime.js b/devtools/client/aboutdebugging-new/src/actions/runtime.js index e67822bbe065..4da73d0037a9 100644 --- a/devtools/client/aboutdebugging-new/src/actions/runtime.js +++ b/devtools/client/aboutdebugging-new/src/actions/runtime.js @@ -25,6 +25,9 @@ const { REQUEST_TABS_FAILURE, REQUEST_TABS_START, REQUEST_TABS_SUCCESS, + REQUEST_WORKERS_FAILURE, + REQUEST_WORKERS_START, + REQUEST_WORKERS_SUCCESS, } = require("../constants"); let browserToolboxProcess = null; @@ -43,6 +46,7 @@ function connectRuntime() { dispatch({ type: CONNECT_RUNTIME_SUCCESS, client }); dispatch(requestExtensions()); dispatch(requestTabs()); + dispatch(requestWorkers()); } catch (e) { dispatch({ type: CONNECT_RUNTIME_FAILURE, error: e.message }); } @@ -181,6 +185,31 @@ function requestExtensions() { }; } +function requestWorkers() { + return async (dispatch, getState) => { + dispatch({ type: REQUEST_WORKERS_START }); + + const client = getState().runtime.client; + + try { + const { + other: otherWorkers, + service: serviceWorkers, + shared: sharedWorkers, + } = await client.mainRoot.listAllWorkers(); + + dispatch({ + type: REQUEST_WORKERS_SUCCESS, + otherWorkers, + serviceWorkers, + sharedWorkers, + }); + } catch (e) { + dispatch({ type: REQUEST_WORKERS_FAILURE, error: e.message }); + } + }; +} + module.exports = { connectRuntime, disconnectRuntime, @@ -190,4 +219,5 @@ module.exports = { removeTemporaryExtension, requestTabs, requestExtensions, + requestWorkers, }; diff --git a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js index b320fa27563d..13ff4ed8c24b 100644 --- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js +++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js @@ -21,13 +21,24 @@ class RuntimePage extends PureComponent { return { dispatch: PropTypes.func.isRequired, installedExtensions: PropTypes.arrayOf(PropTypes.object).isRequired, + otherWorkers: PropTypes.arrayOf(PropTypes.object).isRequired, + serviceWorkers: PropTypes.arrayOf(PropTypes.object).isRequired, + sharedWorkers: PropTypes.arrayOf(PropTypes.object).isRequired, tabs: PropTypes.arrayOf(PropTypes.object).isRequired, temporaryExtensions: PropTypes.arrayOf(PropTypes.object).isRequired, }; } render() { - const { dispatch, installedExtensions, tabs, temporaryExtensions } = this.props; + const { + dispatch, + installedExtensions, + otherWorkers, + serviceWorkers, + sharedWorkers, + tabs, + temporaryExtensions, + } = this.props; return dom.article( { @@ -54,6 +65,19 @@ class RuntimePage extends PureComponent { name: "Tabs", targets: tabs }), + // Temporary implementation + dom.ul( + {}, + serviceWorkers.map(e => dom.li({}, e.name)) + ), + dom.ul( + {}, + sharedWorkers.map(e => dom.li({}, e.name)) + ), + dom.ul( + {}, + otherWorkers.map(e => dom.li({}, e.name)) + ), ); } } @@ -61,6 +85,9 @@ class RuntimePage extends PureComponent { const mapStateToProps = state => { return { installedExtensions: state.runtime.installedExtensions, + otherWorkers: state.runtime.otherWorkers, + serviceWorkers: state.runtime.serviceWorkers, + sharedWorkers: state.runtime.sharedWorkers, tabs: state.runtime.tabs, temporaryExtensions: state.runtime.temporaryExtensions, }; diff --git a/devtools/client/aboutdebugging-new/src/constants.js b/devtools/client/aboutdebugging-new/src/constants.js index 5ee610c4958d..11a5a34e3ca3 100644 --- a/devtools/client/aboutdebugging-new/src/constants.js +++ b/devtools/client/aboutdebugging-new/src/constants.js @@ -19,6 +19,9 @@ const actionTypes = { REQUEST_TABS_FAILURE: "REQUEST_TABS_FAILURE", REQUEST_TABS_START: "REQUEST_TABS_START", REQUEST_TABS_SUCCESS: "REQUEST_TABS_SUCCESS", + REQUEST_WORKERS_FAILURE: "REQUEST_WORKERS_FAILURE", + REQUEST_WORKERS_START: "REQUEST_WORKERS_START", + REQUEST_WORKERS_SUCCESS: "REQUEST_WORKERS_SUCCESS", }; const DEBUG_TARGETS = { diff --git a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js index 5ebd59ddc2b4..c0a9a7fe3869 100644 --- a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js +++ b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js @@ -10,12 +10,16 @@ const { DISCONNECT_RUNTIME_SUCCESS, REQUEST_EXTENSIONS_SUCCESS, REQUEST_TABS_SUCCESS, + REQUEST_WORKERS_SUCCESS, } = require("../constants"); function RuntimeState() { return { client: null, installedExtensions: [], + otherWorkers: [], + serviceWorkers: [], + sharedWorkers: [], tabs: [], temporaryExtensions: [], }; @@ -41,6 +45,14 @@ function runtimeReducer(state = RuntimeState(), action) { const { tabs } = action; return Object.assign({}, state, { tabs: toTabComponentData(tabs) }); } + case REQUEST_WORKERS_SUCCESS: { + const { otherWorkers, serviceWorkers, sharedWorkers } = action; + return Object.assign({}, state, { + otherWorkers, + serviceWorkers, + sharedWorkers, + }); + } default: return state; From 480b69fa383afe0dc1b19c552dc7c5f76ab3273a Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Fri, 17 Aug 2018 09:58:52 +0900 Subject: [PATCH 17/53] Bug 1477602 - Part 3: Implement UI for workers. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D3754 Depends on D3571 --HG-- extra : rebase_source : 8536b750330c820d7f00e9567a839235b09ec121 --- .../aboutdebugging-new/aboutdebugging.css | 1 + .../src/components/RuntimePage.js | 28 ++++---- .../components/debugtarget/DebugTargetItem.js | 3 + .../components/debugtarget/WorkerDetail.css | 52 ++++++++++++++ .../components/debugtarget/WorkerDetail.js | 71 +++++++++++++++++++ .../src/components/debugtarget/moz.build | 2 + .../aboutdebugging-new/src/constants.js | 14 ++++ .../src/reducers/runtime-state.js | 46 +++++++++++- 8 files changed, 201 insertions(+), 16 deletions(-) create mode 100644 devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css create mode 100644 devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.js diff --git a/devtools/client/aboutdebugging-new/aboutdebugging.css b/devtools/client/aboutdebugging-new/aboutdebugging.css index 97e87cc9c9de..e677c486cb8f 100644 --- a/devtools/client/aboutdebugging-new/aboutdebugging.css +++ b/devtools/client/aboutdebugging-new/aboutdebugging.css @@ -11,6 +11,7 @@ @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css"; @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css"; @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionDetail.css"; +@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css"; :root { /* Import css variables from common.css */ diff --git a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js index 13ff4ed8c24b..62b972c0ec0f 100644 --- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js +++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js @@ -65,19 +65,21 @@ class RuntimePage extends PureComponent { name: "Tabs", targets: tabs }), - // Temporary implementation - dom.ul( - {}, - serviceWorkers.map(e => dom.li({}, e.name)) - ), - dom.ul( - {}, - sharedWorkers.map(e => dom.li({}, e.name)) - ), - dom.ul( - {}, - otherWorkers.map(e => dom.li({}, e.name)) - ), + DebugTargetPane({ + dispatch, + name: "Service Workers", + targets: serviceWorkers + }), + DebugTargetPane({ + dispatch, + name: "Shared Workers", + targets: sharedWorkers + }), + DebugTargetPane({ + dispatch, + name: "Other Workers", + targets: otherWorkers + }), ); } } diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js index 0b27586008df..345660b08b90 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js @@ -11,6 +11,7 @@ const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const ExtensionDetail = createFactory(require("./ExtensionDetail")); const TabDetail = createFactory(require("./TabDetail")); const TemporaryExtensionAction = createFactory(require("./TemporaryExtensionAction")); +const WorkerDetail = createFactory(require("./WorkerDetail")); const Actions = require("../../actions/index"); const { DEBUG_TARGETS } = require("../../constants"); @@ -56,6 +57,8 @@ class DebugTargetItem extends PureComponent { return ExtensionDetail({ target }); case DEBUG_TARGETS.TAB: return TabDetail({ target }); + case DEBUG_TARGETS.WORKER: + return WorkerDetail({ target }); default: return null; diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css b/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css new file mode 100644 index 000000000000..9543049264f2 --- /dev/null +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css @@ -0,0 +1,52 @@ +/* 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/. */ + +.worker-detail { + --worker-status-font-size: 10px; +} + +/* + * The current layout of worker detail is + * + * +----------------+--------------------+ + * | detail name dt | detail value dd | + * | (60px ) | (auto) | + * +----------------+--------------------+ + * | detail name dt | detail value dd | + * +----------------+--------------------+ + * | detail name dt | detail value dd | + * +----------------+--------------------+ + */ +.worker-detail { + display: grid; + grid-template-columns: 60px auto; + margin-block-start: 4px; +} + +/* + * worker-detail__status has a ui like badge and the color change by the status. + * For now, the background-color of running status is palegreen, stopped is lightgrey + * though, might be changed since this is not Photon color. + */ +.worker-detail__status { + border-style: solid; + border-width: 1px; + box-sizing: border-box; + display: inline-block; + font-size: var(--worker-status-font-size); + margin-block-start: 6px; + padding-block-start: 2px; + padding-block-end: 2px; + text-align: center; +} + +.worker-detail__status--running { + border-color: limegreen; + background-color: palegreen; +} + +.worker-detail__status--stopped { + border-color: grey; + background-color: lightgrey; +} diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.js new file mode 100644 index 000000000000..d64bffd7ab70 --- /dev/null +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.js @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { PureComponent } = require("devtools/client/shared/vendor/react"); +const dom = require("devtools/client/shared/vendor/react-dom-factories"); +const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); + +const { + SERVICE_WORKER_FETCH_STATES, +} = require("../../constants"); + +/** + * This component displays detail information for worker. + */ +class WorkerDetail extends PureComponent { + static get propTypes() { + return { + target: PropTypes.object.isRequired, + }; + } + + renderFetch() { + const { fetch } = this.props.target.details; + const label = fetch === SERVICE_WORKER_FETCH_STATES.LISTENING + ? "Listening for fetch events" + : "Not listening for fetch events"; + return this.renderField("Fetch", label); + } + + renderField(name, value) { + return [ + dom.dt({}, name), + dom.dd( + { + className: "ellipsis-text", + title: value, + }, + value, + ), + ]; + } + + renderStatus() { + const status = this.props.target.details.status.toLowerCase(); + + return dom.div( + { + className: `worker-detail__status worker-detail__status--${ status }`, + }, + status + ); + } + + render() { + const { fetch, scope, status } = this.props.target.details; + + return dom.dl( + { + className: "worker-detail", + }, + fetch ? this.renderFetch() : null, + scope ? this.renderField("Scope", scope) : null, + status ? this.renderStatus() : null, + ); + } +} + +module.exports = WorkerDetail; diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build index 27c84ea6c6b5..7109e97fb30a 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build @@ -13,4 +13,6 @@ DevToolsModules( 'TabDetail.js', 'TemporaryExtensionAction.js', 'TemporaryExtensionInstaller.js', + 'WorkerDetail.css', + 'WorkerDetail.js', ) diff --git a/devtools/client/aboutdebugging-new/src/constants.js b/devtools/client/aboutdebugging-new/src/constants.js index 11a5a34e3ca3..c80d70091973 100644 --- a/devtools/client/aboutdebugging-new/src/constants.js +++ b/devtools/client/aboutdebugging-new/src/constants.js @@ -27,6 +27,7 @@ const actionTypes = { const DEBUG_TARGETS = { EXTENSION: "EXTENSION", TAB: "TAB", + WORKER: "WORKER", }; const PAGES = { @@ -34,8 +35,21 @@ const PAGES = { CONNECT: "connect", }; +const SERVICE_WORKER_FETCH_STATES = { + LISTENING: "LISTENING", + NOT_LISTENING: "NOT_LISTENING", +}; + +const SERVICE_WORKER_STATUSES = { + RUNNING: "RUNNING", + REGISTERING: "REGISTERING", + STOPPED: "STOPPED", +}; + // flatten constants module.exports = Object.assign({}, { DEBUG_TARGETS, PAGES, + SERVICE_WORKER_FETCH_STATES, + SERVICE_WORKER_STATUSES, }, actionTypes); diff --git a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js index c0a9a7fe3869..745c91c22aba 100644 --- a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js +++ b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js @@ -11,6 +11,8 @@ const { REQUEST_EXTENSIONS_SUCCESS, REQUEST_TABS_SUCCESS, REQUEST_WORKERS_SUCCESS, + SERVICE_WORKER_FETCH_STATES, + SERVICE_WORKER_STATUSES, } = require("../constants"); function RuntimeState() { @@ -48,9 +50,9 @@ function runtimeReducer(state = RuntimeState(), action) { case REQUEST_WORKERS_SUCCESS: { const { otherWorkers, serviceWorkers, sharedWorkers } = action; return Object.assign({}, state, { - otherWorkers, - serviceWorkers, - sharedWorkers, + otherWorkers: toWorkerComponentData(otherWorkers), + serviceWorkers: toWorkerComponentData(serviceWorkers, true), + sharedWorkers: toWorkerComponentData(sharedWorkers), }); } @@ -123,6 +125,44 @@ function toTabComponentData(tabs) { }); } +function getServiceWorkerStatus(worker) { + if (worker.active && !!worker.workerTargetActor) { + return SERVICE_WORKER_STATUSES.RUNNING; + } else if (worker.active) { + return SERVICE_WORKER_STATUSES.STOPPED; + } + // We cannot get service worker registrations unless the registration is in + // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we + // display a custom state "registering" for now. See Bug 1153292. + return SERVICE_WORKER_STATUSES.REGISTERING; +} + +function toWorkerComponentData(workers, isServiceWorker) { + return workers.map(worker => { + const type = DEBUG_TARGETS.WORKER; + const icon = "chrome://devtools/skin/images/debugging-workers.svg"; + let { fetch, name, scope } = worker; + let status = null; + + if (isServiceWorker) { + fetch = fetch ? SERVICE_WORKER_FETCH_STATES.LISTENING + : SERVICE_WORKER_FETCH_STATES.NOT_LISTENING; + status = getServiceWorkerStatus(worker); + } + + return { + name, + icon, + type, + details: { + fetch, + scope, + status, + }, + }; + }); +} + module.exports = { RuntimeState, runtimeReducer, From 4b68b7a33fae7a69c732e3c65255904ab2e39e66 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Fri, 17 Aug 2018 09:58:52 +0900 Subject: [PATCH 18/53] Bug 1477602 - Part 4: Implement action buttons for worker. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D3755 Depends on D3754 --HG-- extra : rebase_source : 7485165ce29964e7435c91b5cdfae8ccd80b395a --- .../aboutdebugging-new/src/actions/runtime.js | 76 ++++++++++++++----- .../components/debugtarget/DebugTargetItem.js | 27 +++---- .../components/debugtarget/InspectAction.js | 40 ++++++++++ .../debugtarget/ServiceWorkerAction.js | 70 +++++++++++++++++ .../debugtarget/TemporaryExtensionAction.js | 14 +++- .../src/components/debugtarget/moz.build | 2 + .../src/reducers/runtime-state.js | 19 +++-- 7 files changed, 203 insertions(+), 45 deletions(-) create mode 100644 devtools/client/aboutdebugging-new/src/components/debugtarget/InspectAction.js create mode 100644 devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js diff --git a/devtools/client/aboutdebugging-new/src/actions/runtime.js b/devtools/client/aboutdebugging-new/src/actions/runtime.js index 4da73d0037a9..936a62d8a68e 100644 --- a/devtools/client/aboutdebugging-new/src/actions/runtime.js +++ b/devtools/client/aboutdebugging-new/src/actions/runtime.js @@ -10,6 +10,7 @@ const { BrowserToolboxProcess } = const { Cc, Ci } = require("chrome"); const { DebuggerClient } = require("devtools/shared/client/debugger-client"); const { DebuggerServer } = require("devtools/server/main"); +const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser"); const { CONNECT_RUNTIME_FAILURE, @@ -71,26 +72,39 @@ function disconnectRuntime() { } function inspectDebugTarget(type, id) { - if (type === DEBUG_TARGETS.TAB) { - window.open(`about:devtools-toolbox?type=tab&id=${ id }`); - } else if (type === DEBUG_TARGETS.EXTENSION) { - // Close previous addon debugging toolbox. - if (browserToolboxProcess) { - browserToolboxProcess.close(); - } - - browserToolboxProcess = BrowserToolboxProcess.init({ - addonID: id, - onClose: () => { - browserToolboxProcess = null; + return async (_, getState) => { + switch (type) { + case DEBUG_TARGETS.TAB: { + // Open tab debugger in new window. + window.open(`about:devtools-toolbox?type=tab&id=${ id }`); + break; } - }); - } else { - console.error(`Failed to inspect the debug target of type: ${ type } id: ${ id }`); - } + case DEBUG_TARGETS.EXTENSION: { + // Close current debugging toolbox and open a new one. + if (browserToolboxProcess) { + browserToolboxProcess.close(); + } - // We cancel the redux flow here since the inspection does not need to update the state. - return () => {}; + browserToolboxProcess = BrowserToolboxProcess.init({ + addonID: id, + onClose: () => { + browserToolboxProcess = null; + } + }); + break; + } + case DEBUG_TARGETS.WORKER: { + // Open worker toolbox in new window. + gDevToolsBrowser.openWorkerToolbox(getState().runtime.client, id); + break; + } + + default: { + console.error("Failed to inspect the debug target of " + + `type: ${ type } id: ${ id }`); + } + } + }; } function installTemporaryExtension() { @@ -120,6 +134,18 @@ function installTemporaryExtension() { return () => {}; } +function pushServiceWorker(actor) { + return async (_, getState) => { + const client = getState().runtime.client; + + try { + await client.request({ to: actor, type: "push" }); + } catch (e) { + console.error(e); + } + }; +} + function reloadTemporaryExtension(actor) { return async (_, getState) => { const client = getState().runtime.client; @@ -210,14 +236,28 @@ function requestWorkers() { }; } +function startServiceWorker(actor) { + return async (_, getState) => { + const client = getState().runtime.client; + + try { + await client.request({ to: actor, type: "start" }); + } catch (e) { + console.error(e); + } + }; +} + module.exports = { connectRuntime, disconnectRuntime, inspectDebugTarget, installTemporaryExtension, + pushServiceWorker, reloadTemporaryExtension, removeTemporaryExtension, requestTabs, requestExtensions, requestWorkers, + startServiceWorker, }; diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js index 345660b08b90..e1161e103bcd 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js @@ -9,11 +9,12 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const ExtensionDetail = createFactory(require("./ExtensionDetail")); +const InspectAction = createFactory(require("./InspectAction")); +const ServiceWorkerAction = createFactory(require("./ServiceWorkerAction")); const TabDetail = createFactory(require("./TabDetail")); const TemporaryExtensionAction = createFactory(require("./TemporaryExtensionAction")); const WorkerDetail = createFactory(require("./WorkerDetail")); -const Actions = require("../../actions/index"); const { DEBUG_TARGETS } = require("../../constants"); /** @@ -27,26 +28,16 @@ class DebugTargetItem extends PureComponent { }; } - inspect() { - const { dispatch, target } = this.props; - dispatch(Actions.inspectDebugTarget(target.type, target.id)); - } - renderAction() { const { dispatch, target } = this.props; - return dom.div( - {}, - dom.button( - { - onClick: e => this.inspect(), - className: "aboutdebugging-button", - }, - "Inspect" - ), - target.details.temporarilyInstalled - ? TemporaryExtensionAction({ dispatch, target }) : null, - ); + if (target.details.temporarilyInstalled) { + return TemporaryExtensionAction({ dispatch, target }); + } else if (target.details.status) { + return ServiceWorkerAction({ dispatch, target }); + } + + return InspectAction({ dispatch, target }); } renderDetail() { diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/InspectAction.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/InspectAction.js new file mode 100644 index 000000000000..cb82380f49c0 --- /dev/null +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/InspectAction.js @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { PureComponent } = require("devtools/client/shared/vendor/react"); +const dom = require("devtools/client/shared/vendor/react-dom-factories"); +const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); + +const Actions = require("../../actions/index"); + +/** + * This component provides inspect button. + */ +class InspectAction extends PureComponent { + static get propTypes() { + return { + dispatch: PropTypes.func.isRequired, + target: PropTypes.object.isRequired, + }; + } + + inspect() { + const { dispatch, target } = this.props; + dispatch(Actions.inspectDebugTarget(target.type, target.id)); + } + + render() { + return dom.button( + { + onClick: e => this.inspect(), + className: "aboutdebugging-button", + }, + "Inspect" + ); + } +} + +module.exports = InspectAction; diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js new file mode 100644 index 000000000000..0f8134a626e4 --- /dev/null +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); +const dom = require("devtools/client/shared/vendor/react-dom-factories"); +const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); + +const InspectAction = createFactory(require("./InspectAction")); + +const Actions = require("../../actions/index"); + +/** + * This component displays buttons for service worker. + */ +class ServiceWorkerAction extends PureComponent { + static get propTypes() { + return { + dispatch: PropTypes.func.isRequired, + target: PropTypes.object.isRequired, + }; + } + + push() { + const { dispatch, target } = this.props; + dispatch(Actions.pushServiceWorker(target.id)); + } + + start() { + const { dispatch, target } = this.props; + dispatch(Actions.startServiceWorker(target.details.registrationActor)); + } + + _renderAction() { + const { dispatch, target } = this.props; + const { isActive, isRunning } = target.details; + + if (!isRunning) { + return this._renderButton("Start", this.start.bind(this)); + } + + if (!isActive) { + // Only debug button is available if the service worker is not active. + return InspectAction({ dispatch, target }); + } + + return [ + this._renderButton("Push", this.push.bind(this)), + InspectAction({ dispatch, target }), + ]; + } + + _renderButton(label, onClick) { + return dom.button( + { + className: "aboutdebugging-button", + onClick: e => onClick(), + }, + label, + ); + } + + render() { + return dom.div({}, this._renderAction()); + } +} + +module.exports = ServiceWorkerAction; diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/TemporaryExtensionAction.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/TemporaryExtensionAction.js index 4346c3b232ab..e1a324b7f5df 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/TemporaryExtensionAction.js +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/TemporaryExtensionAction.js @@ -4,14 +4,16 @@ "use strict"; -const { PureComponent } = require("devtools/client/shared/vendor/react"); +const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const InspectAction = createFactory(require("./InspectAction")); + const Actions = require("../../actions/index"); /** - * This component provides components that reload/remove temporary extension. + * This component provides components that inspect/reload/remove temporary extension. */ class TemporaryExtensionAction extends PureComponent { static get propTypes() { @@ -32,7 +34,11 @@ class TemporaryExtensionAction extends PureComponent { } render() { - return [ + const { dispatch, target } = this.props; + + return dom.div( + {}, + InspectAction({ dispatch, target }), dom.button( { className: "aboutdebugging-button", @@ -47,7 +53,7 @@ class TemporaryExtensionAction extends PureComponent { }, "Remove", ), - ]; + ); } } diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build index 7109e97fb30a..4672eb0076a9 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build @@ -10,6 +10,8 @@ DevToolsModules( 'DebugTargetPane.js', 'ExtensionDetail.css', 'ExtensionDetail.js', + 'InspectAction.js', + 'ServiceWorkerAction.js', 'TabDetail.js', 'TemporaryExtensionAction.js', 'TemporaryExtensionInstaller.js', diff --git a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js index 745c91c22aba..1d5aeb33889c 100644 --- a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js +++ b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js @@ -125,10 +125,10 @@ function toTabComponentData(tabs) { }); } -function getServiceWorkerStatus(worker) { - if (worker.active && !!worker.workerTargetActor) { +function getServiceWorkerStatus(isActive, isRunning) { + if (isActive && isRunning) { return SERVICE_WORKER_STATUSES.RUNNING; - } else if (worker.active) { + } else if (isActive) { return SERVICE_WORKER_STATUSES.STOPPED; } // We cannot get service worker registrations unless the registration is in @@ -140,22 +140,31 @@ function getServiceWorkerStatus(worker) { function toWorkerComponentData(workers, isServiceWorker) { return workers.map(worker => { const type = DEBUG_TARGETS.WORKER; + const id = worker.workerTargetActor; const icon = "chrome://devtools/skin/images/debugging-workers.svg"; - let { fetch, name, scope } = worker; + let { fetch, name, registrationActor, scope } = worker; + let isActive = false; + let isRunning = false; let status = null; if (isServiceWorker) { fetch = fetch ? SERVICE_WORKER_FETCH_STATES.LISTENING : SERVICE_WORKER_FETCH_STATES.NOT_LISTENING; - status = getServiceWorkerStatus(worker); + isActive = worker.active; + isRunning = !!worker.workerTargetActor; + status = getServiceWorkerStatus(isActive, isRunning); } return { name, icon, + id, type, details: { fetch, + isActive, + isRunning, + registrationActor, scope, status, }, From 201d180ecd58bbdcbfcdfc8ba042f8debe17be0f Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Fri, 17 Aug 2018 09:58:52 +0900 Subject: [PATCH 19/53] Bug 1477602 - Part 5: Add a listener to detect updating the workers. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D3756 Depends on D3755 --HG-- extra : rebase_source : f833f93d2e18a220f44d663c9feb37fb0a7ed0f1 --- .../src/middleware/debug-target-listener.js | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js index d04bad392504..2f588c14113a 100644 --- a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js +++ b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js @@ -47,16 +47,32 @@ function debugTargetListenerMiddleware(state) { }, }; + const onWorkersUpdated = () => { + state.dispatch(Actions.requestWorkers()); + }; + return next => action => { switch (action.type) { case CONNECT_RUNTIME_SUCCESS: { - action.client.addListener("tabListChanged", onTabsUpdated); + const { client } = action; + client.addListener("tabListChanged", onTabsUpdated); AddonManager.addAddonListener(extensionsListener); + client.addListener("workerListChanged", onWorkersUpdated); + client.addListener("serviceWorkerRegistrationListChanged", onWorkersUpdated); + client.addListener("processListChanged", onWorkersUpdated); + client.addListener("registration-changed", onWorkersUpdated); + client.addListener("push-subscription-modified", onWorkersUpdated); break; } case DISCONNECT_RUNTIME_START: { - state.getState().runtime.client.removeListener("tabListChanged", onTabsUpdated); + const { client } = state.getState().runtime; + client.removeListener("tabListChanged", onTabsUpdated); AddonManager.removeAddonListener(extensionsListener); + client.removeListener("workerListChanged", onWorkersUpdated); + client.removeListener("serviceWorkerRegistrationListChanged", onWorkersUpdated); + client.removeListener("processListChanged", onWorkersUpdated); + client.removeListener("registration-changed", onWorkersUpdated); + client.removeListener("push-subscription-modified", onWorkersUpdated); break; } } From 44f5dcf0fe2e204a120df93c6ce486b2a1e2e067 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Fri, 17 Aug 2018 11:02:40 +0900 Subject: [PATCH 20/53] Bug 1477602 - Part 6: Give custom detail and action view component from RuntimePage. r=ladybenko The custom components define from RuntimePage. Because removes extra conditional branchings and information to determin the custom component. Differential Revision: https://phabricator.services.mozilla.com/D3757 Depends on D3756 --HG-- extra : rebase_source : f697a51d4eb325cb4cfa5010270c36fcf0cf03f1 --- .../src/components/RuntimePage.js | 18 ++++++++ .../components/debugtarget/DebugTargetItem.js | 45 ++++--------------- .../components/debugtarget/DebugTargetList.js | 7 ++- .../components/debugtarget/DebugTargetPane.js | 6 ++- .../src/reducers/runtime-state.js | 3 +- 5 files changed, 37 insertions(+), 42 deletions(-) diff --git a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js index 62b972c0ec0f..bc3adcf32d91 100644 --- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js +++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js @@ -10,9 +10,15 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); const DebugTargetPane = createFactory(require("./debugtarget/DebugTargetPane")); +const ExtensionDetail = createFactory(require("./debugtarget/ExtensionDetail")); +const InspectAction = createFactory(require("./debugtarget/InspectAction")); const RuntimeInfo = createFactory(require("./RuntimeInfo")); +const ServiceWorkerAction = createFactory(require("./debugtarget/ServiceWorkerAction")); +const TabDetail = createFactory(require("./debugtarget/TabDetail")); +const TemporaryExtensionAction = createFactory(require("./debugtarget/TemporaryExtensionAction")); const TemporaryExtensionInstaller = createFactory(require("./debugtarget/TemporaryExtensionInstaller")); +const WorkerDetail = createFactory(require("./debugtarget/WorkerDetail")); const Services = require("Services"); @@ -51,31 +57,43 @@ class RuntimePage extends PureComponent { }), TemporaryExtensionInstaller({ dispatch }), DebugTargetPane({ + actionComponent: TemporaryExtensionAction, + detailComponent: ExtensionDetail, dispatch, name: "Temporary Extensions", targets: temporaryExtensions, }), DebugTargetPane({ + actionComponent: InspectAction, + detailComponent: ExtensionDetail, dispatch, name: "Extensions", targets: installedExtensions, }), DebugTargetPane({ + actionComponent: InspectAction, + detailComponent: TabDetail, dispatch, name: "Tabs", targets: tabs }), DebugTargetPane({ + actionComponent: ServiceWorkerAction, + detailComponent: WorkerDetail, dispatch, name: "Service Workers", targets: serviceWorkers }), DebugTargetPane({ + actionComponent: InspectAction, + detailComponent: WorkerDetail, dispatch, name: "Shared Workers", targets: sharedWorkers }), DebugTargetPane({ + actionComponent: InspectAction, + detailComponent: WorkerDetail, dispatch, name: "Other Workers", targets: otherWorkers diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js index e1161e103bcd..5050bacdbfc1 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.js @@ -4,56 +4,31 @@ "use strict"; -const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); +const { PureComponent } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); -const ExtensionDetail = createFactory(require("./ExtensionDetail")); -const InspectAction = createFactory(require("./InspectAction")); -const ServiceWorkerAction = createFactory(require("./ServiceWorkerAction")); -const TabDetail = createFactory(require("./TabDetail")); -const TemporaryExtensionAction = createFactory(require("./TemporaryExtensionAction")); -const WorkerDetail = createFactory(require("./WorkerDetail")); - -const { DEBUG_TARGETS } = require("../../constants"); - /** * This component displays debug target. */ class DebugTargetItem extends PureComponent { static get propTypes() { return { + actionComponent: PropTypes.any.isRequired, + detailComponent: PropTypes.any.isRequired, dispatch: PropTypes.func.isRequired, target: PropTypes.object.isRequired, }; } renderAction() { - const { dispatch, target } = this.props; - - if (target.details.temporarilyInstalled) { - return TemporaryExtensionAction({ dispatch, target }); - } else if (target.details.status) { - return ServiceWorkerAction({ dispatch, target }); - } - - return InspectAction({ dispatch, target }); + const { actionComponent, dispatch, target } = this.props; + return actionComponent({ dispatch, target }); } renderDetail() { - const { target } = this.props; - - switch (target.type) { - case DEBUG_TARGETS.EXTENSION: - return ExtensionDetail({ target }); - case DEBUG_TARGETS.TAB: - return TabDetail({ target }); - case DEBUG_TARGETS.WORKER: - return WorkerDetail({ target }); - - default: - return null; - } + const { detailComponent, target } = this.props; + return detailComponent({ target }); } renderIcon() { @@ -64,8 +39,6 @@ class DebugTargetItem extends PureComponent { } renderInfo() { - const { target } = this.props; - return dom.div( { className: "debug-target-item__info", @@ -73,9 +46,9 @@ class DebugTargetItem extends PureComponent { dom.div( { className: "debug-target-item__info__name ellipsis-text", - title: target.name, + title: this.props.target.name, }, - target.name + this.props.target.name ), this.renderDetail(), ); diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.js index bda2d3e48c5c..4d7467f697a1 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.js +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.js @@ -16,19 +16,22 @@ const DebugTargetItem = createFactory(require("./DebugTargetItem")); class DebugTargetList extends PureComponent { static get propTypes() { return { + actionComponent: PropTypes.any.isRequired, + detailComponent: PropTypes.any.isRequired, dispatch: PropTypes.func.isRequired, targets: PropTypes.arrayOf(PropTypes.object).isRequired, }; } render() { - const { dispatch, targets } = this.props; + const { actionComponent, detailComponent, dispatch, targets } = this.props; return dom.ul( { className: "debug-target-list", }, - targets.map(target => DebugTargetItem({ dispatch, target })), + targets.map(target => + DebugTargetItem({ actionComponent, detailComponent, dispatch, target })), ); } } diff --git a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js index bc47db7c8585..87d2d8efc6ec 100644 --- a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js +++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js @@ -16,6 +16,8 @@ const DebugTargetList = createFactory(require("./DebugTargetList")); class DebugTargetPane extends PureComponent { static get propTypes() { return { + actionComponent: PropTypes.any.isRequired, + detailComponent: PropTypes.any.isRequired, dispatch: PropTypes.func.isRequired, name: PropTypes.string.isRequired, targets: PropTypes.arrayOf(PropTypes.Object).isRequired, @@ -23,12 +25,12 @@ class DebugTargetPane extends PureComponent { } render() { - const { dispatch, name, targets } = this.props; + const { actionComponent, detailComponent, dispatch, name, targets } = this.props; return dom.section( {}, dom.h2({}, name), - DebugTargetList({ dispatch, targets }), + DebugTargetList({ actionComponent, detailComponent, dispatch, targets }), ); } } diff --git a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js index 1d5aeb33889c..478e5489b095 100644 --- a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js +++ b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js @@ -84,7 +84,7 @@ function getExtensionFilePath(extension) { function toExtensionComponentData(extensions) { return extensions.map(extension => { const type = DEBUG_TARGETS.EXTENSION; - const { actor, iconURL, id, manifestURL, name, temporarilyInstalled } = extension; + const { actor, iconURL, id, manifestURL, name } = extension; const icon = iconURL || "chrome://mozapps/skin/extensions/extensionGeneric.svg"; const location = getExtensionFilePath(extension); const uuid = manifestURL ? /moz-extension:\/\/([^/]*)/.exec(manifestURL)[1] : null; @@ -97,7 +97,6 @@ function toExtensionComponentData(extensions) { actor, location, manifestURL, - temporarilyInstalled, uuid, }, }; From 178cce81b97b4a04d8a7e104cf1500eb9aea7298 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Fri, 17 Aug 2018 11:02:45 +0900 Subject: [PATCH 21/53] Bug 1477602 - Part 7: Move tab related logic which convert for component to middleware. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D3758 Depends on D3757 --HG-- extra : rebase_source : 3f0d8b08eb593551106b7ba5cfe46aa3e6ced2ad --- .../aboutdebugging-new/src/create-store.js | 5 +- .../src/middleware/debug-target-listener.js | 10 ++-- .../src/middleware/moz.build | 1 + .../src/middleware/tab-component-data.js | 48 +++++++++++++++++++ .../src/reducers/runtime-state.js | 23 +-------- 5 files changed, 59 insertions(+), 28 deletions(-) create mode 100644 devtools/client/aboutdebugging-new/src/middleware/tab-component-data.js diff --git a/devtools/client/aboutdebugging-new/src/create-store.js b/devtools/client/aboutdebugging-new/src/create-store.js index f98805549f41..3ee93c954a81 100644 --- a/devtools/client/aboutdebugging-new/src/create-store.js +++ b/devtools/client/aboutdebugging-new/src/create-store.js @@ -11,6 +11,7 @@ const rootReducer = require("./reducers/index"); const { RuntimeState } = require("./reducers/runtime-state"); const { UiState } = require("./reducers/ui-state"); const debugTargetListenerMiddleware = require("./middleware/debug-target-listener"); +const tabComponentDataMiddleware = require("./middleware/tab-component-data"); const { getNetworkLocations } = require("./modules/network-locations"); function configureStore() { @@ -19,7 +20,9 @@ function configureStore() { ui: getUiState() }; - const middleware = applyMiddleware(thunk, debugTargetListenerMiddleware); + const middleware = applyMiddleware(thunk, + debugTargetListenerMiddleware, + tabComponentDataMiddleware); return createStore(rootReducer, initialState, middleware); } diff --git a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js index 2f588c14113a..4071f5c4a8aa 100644 --- a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js +++ b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js @@ -12,13 +12,13 @@ const { } = require("../constants"); const Actions = require("../actions/index"); -function debugTargetListenerMiddleware(state) { +function debugTargetListenerMiddleware(store) { const onExtensionsUpdated = () => { - state.dispatch(Actions.requestExtensions()); + store.dispatch(Actions.requestExtensions()); }; const onTabsUpdated = () => { - state.dispatch(Actions.requestTabs()); + store.dispatch(Actions.requestTabs()); }; const extensionsListener = { @@ -48,7 +48,7 @@ function debugTargetListenerMiddleware(state) { }; const onWorkersUpdated = () => { - state.dispatch(Actions.requestWorkers()); + store.dispatch(Actions.requestWorkers()); }; return next => action => { @@ -65,7 +65,7 @@ function debugTargetListenerMiddleware(state) { break; } case DISCONNECT_RUNTIME_START: { - const { client } = state.getState().runtime; + const { client } = store.getState().runtime; client.removeListener("tabListChanged", onTabsUpdated); AddonManager.removeAddonListener(extensionsListener); client.removeListener("workerListChanged", onWorkersUpdated); diff --git a/devtools/client/aboutdebugging-new/src/middleware/moz.build b/devtools/client/aboutdebugging-new/src/middleware/moz.build index c2f07da5fb1b..a9268fe94bb9 100644 --- a/devtools/client/aboutdebugging-new/src/middleware/moz.build +++ b/devtools/client/aboutdebugging-new/src/middleware/moz.build @@ -4,4 +4,5 @@ DevToolsModules( 'debug-target-listener.js', + 'tab-component-data.js', ) diff --git a/devtools/client/aboutdebugging-new/src/middleware/tab-component-data.js b/devtools/client/aboutdebugging-new/src/middleware/tab-component-data.js new file mode 100644 index 000000000000..0b4835d6cec0 --- /dev/null +++ b/devtools/client/aboutdebugging-new/src/middleware/tab-component-data.js @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { + DEBUG_TARGETS, + REQUEST_TABS_SUCCESS, +} = require("../constants"); + +/** + * This middleware converts tabs object that get from DebuggerClient.listTabs() to data + * which is used in DebugTargetItem. + */ +const tabComponentDataMiddleware = store => next => action => { + switch (action.type) { + case REQUEST_TABS_SUCCESS: { + action.tabs = toComponentData(action.tabs); + break; + } + } + + return next(action); +}; + +function toComponentData(tabs) { + return tabs.map(tab => { + const type = DEBUG_TARGETS.TAB; + const id = tab.outerWindowID; + const icon = tab.favicon + ? `data:image/png;base64,${ btoa(String.fromCharCode.apply(String, tab.favicon)) }` + : "chrome://devtools/skin/images/globe.svg"; + const name = tab.title || tab.url; + const url = tab.url; + return { + name, + icon, + id, + type, + details: { + url, + }, + }; + }); +} + +module.exports = tabComponentDataMiddleware; diff --git a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js index 478e5489b095..3a0325745050 100644 --- a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js +++ b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js @@ -45,7 +45,7 @@ function runtimeReducer(state = RuntimeState(), action) { } case REQUEST_TABS_SUCCESS: { const { tabs } = action; - return Object.assign({}, state, { tabs: toTabComponentData(tabs) }); + return Object.assign({}, state, { tabs }); } case REQUEST_WORKERS_SUCCESS: { const { otherWorkers, serviceWorkers, sharedWorkers } = action; @@ -103,27 +103,6 @@ function toExtensionComponentData(extensions) { }); } -function toTabComponentData(tabs) { - return tabs.map(tab => { - const type = DEBUG_TARGETS.TAB; - const id = tab.outerWindowID; - const icon = tab.favicon - ? `data:image/png;base64,${ btoa(String.fromCharCode.apply(String, tab.favicon)) }` - : "chrome://devtools/skin/images/globe.svg"; - const name = tab.title || tab.url; - const url = tab.url; - return { - name, - icon, - id, - type, - details: { - url, - }, - }; - }); -} - function getServiceWorkerStatus(isActive, isRunning) { if (isActive && isRunning) { return SERVICE_WORKER_STATUSES.RUNNING; From 49238b1f18f1b1eceaa3a6bb81824dc432676834 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Thu, 23 Aug 2018 10:19:41 +0900 Subject: [PATCH 22/53] Bug 1477602 - Part 8: Move extension related logic which convert for component to middleware. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D4033 Depends on D3758 --HG-- extra : rebase_source : 0d31e1fff75097f7c1f388c66108e9ae53e97eae --- .../aboutdebugging-new/src/create-store.js | 2 + .../middleware/extension-component-data.js | 70 +++++++++++++++++++ .../src/middleware/moz.build | 1 + .../src/reducers/runtime-state.js | 47 +------------ 4 files changed, 74 insertions(+), 46 deletions(-) create mode 100644 devtools/client/aboutdebugging-new/src/middleware/extension-component-data.js diff --git a/devtools/client/aboutdebugging-new/src/create-store.js b/devtools/client/aboutdebugging-new/src/create-store.js index 3ee93c954a81..938bd6cf4106 100644 --- a/devtools/client/aboutdebugging-new/src/create-store.js +++ b/devtools/client/aboutdebugging-new/src/create-store.js @@ -11,6 +11,7 @@ const rootReducer = require("./reducers/index"); const { RuntimeState } = require("./reducers/runtime-state"); const { UiState } = require("./reducers/ui-state"); const debugTargetListenerMiddleware = require("./middleware/debug-target-listener"); +const extensionComponentDataMiddleware = require("./middleware/extension-component-data"); const tabComponentDataMiddleware = require("./middleware/tab-component-data"); const { getNetworkLocations } = require("./modules/network-locations"); @@ -22,6 +23,7 @@ function configureStore() { const middleware = applyMiddleware(thunk, debugTargetListenerMiddleware, + extensionComponentDataMiddleware, tabComponentDataMiddleware); return createStore(rootReducer, initialState, middleware); diff --git a/devtools/client/aboutdebugging-new/src/middleware/extension-component-data.js b/devtools/client/aboutdebugging-new/src/middleware/extension-component-data.js new file mode 100644 index 000000000000..45b1f1572ef7 --- /dev/null +++ b/devtools/client/aboutdebugging-new/src/middleware/extension-component-data.js @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { + DEBUG_TARGETS, + REQUEST_EXTENSIONS_SUCCESS, +} = require("../constants"); + +/** + * This middleware converts extensions object that get from DebuggerClient.listAddons() + * to data which is used in DebugTargetItem. + */ +const extensionComponentDataMiddleware = store => next => action => { + switch (action.type) { + case REQUEST_EXTENSIONS_SUCCESS: { + action.installedExtensions = toComponentData(action.installedExtensions); + action.temporaryExtensions = toComponentData(action.temporaryExtensions); + break; + } + } + + return next(action); +}; + +function getFilePath(extension) { + // Only show file system paths, and only for temporarily installed add-ons. + if (!extension.temporarilyInstalled || + !extension.url || + !extension.url.startsWith("file://")) { + return null; + } + + // Strip a leading slash from Windows drive letter URIs. + // file:///home/foo ~> /home/foo + // file:///C:/foo ~> C:/foo + const windowsRegex = /^file:\/\/\/([a-zA-Z]:\/.*)/; + + if (windowsRegex.test(extension.url)) { + return windowsRegex.exec(extension.url)[1]; + } + + return extension.url.slice("file://".length); +} + +function toComponentData(extensions) { + return extensions.map(extension => { + const type = DEBUG_TARGETS.EXTENSION; + const { actor, iconURL, id, manifestURL, name } = extension; + const icon = iconURL || "chrome://mozapps/skin/extensions/extensionGeneric.svg"; + const location = getFilePath(extension); + const uuid = manifestURL ? /moz-extension:\/\/([^/]*)/.exec(manifestURL)[1] : null; + return { + name, + icon, + id, + type, + details: { + actor, + location, + manifestURL, + uuid, + }, + }; + }); +} + +module.exports = extensionComponentDataMiddleware; diff --git a/devtools/client/aboutdebugging-new/src/middleware/moz.build b/devtools/client/aboutdebugging-new/src/middleware/moz.build index a9268fe94bb9..eb72e4141701 100644 --- a/devtools/client/aboutdebugging-new/src/middleware/moz.build +++ b/devtools/client/aboutdebugging-new/src/middleware/moz.build @@ -4,5 +4,6 @@ DevToolsModules( 'debug-target-listener.js', + 'extension-component-data.js', 'tab-component-data.js', ) diff --git a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js index 3a0325745050..7e7a562acece 100644 --- a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js +++ b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js @@ -38,10 +38,7 @@ function runtimeReducer(state = RuntimeState(), action) { } case REQUEST_EXTENSIONS_SUCCESS: { const { installedExtensions, temporaryExtensions } = action; - return Object.assign({}, state, { - installedExtensions: toExtensionComponentData(installedExtensions), - temporaryExtensions: toExtensionComponentData(temporaryExtensions), - }); + return Object.assign({}, state, { installedExtensions, temporaryExtensions }); } case REQUEST_TABS_SUCCESS: { const { tabs } = action; @@ -61,48 +58,6 @@ function runtimeReducer(state = RuntimeState(), action) { } } -function getExtensionFilePath(extension) { - // Only show file system paths, and only for temporarily installed add-ons. - if (!extension.temporarilyInstalled || - !extension.url || - !extension.url.startsWith("file://")) { - return null; - } - - // Strip a leading slash from Windows drive letter URIs. - // file:///home/foo ~> /home/foo - // file:///C:/foo ~> C:/foo - const windowsRegex = /^file:\/\/\/([a-zA-Z]:\/.*)/; - - if (windowsRegex.test(extension.url)) { - return windowsRegex.exec(extension.url)[1]; - } - - return extension.url.slice("file://".length); -} - -function toExtensionComponentData(extensions) { - return extensions.map(extension => { - const type = DEBUG_TARGETS.EXTENSION; - const { actor, iconURL, id, manifestURL, name } = extension; - const icon = iconURL || "chrome://mozapps/skin/extensions/extensionGeneric.svg"; - const location = getExtensionFilePath(extension); - const uuid = manifestURL ? /moz-extension:\/\/([^/]*)/.exec(manifestURL)[1] : null; - return { - name, - icon, - id, - type, - details: { - actor, - location, - manifestURL, - uuid, - }, - }; - }); -} - function getServiceWorkerStatus(isActive, isRunning) { if (isActive && isRunning) { return SERVICE_WORKER_STATUSES.RUNNING; From 0c7ac8d8231c9e67febbb4cf6c9f7a44f469149b Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Thu, 23 Aug 2018 10:42:45 +0900 Subject: [PATCH 23/53] Bug 1477602 - Part 9: Move worker related logic which convert for component to middleware. r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D4034 Depends on D4033 --HG-- extra : rebase_source : 119927d70f37631b474b463634bfff007b6e40f7 --- .../aboutdebugging-new/src/create-store.js | 4 +- .../src/middleware/moz.build | 1 + .../src/middleware/worker-component-data.js | 78 +++++++++++++++++++ .../src/reducers/runtime-state.js | 56 +------------ 4 files changed, 83 insertions(+), 56 deletions(-) create mode 100644 devtools/client/aboutdebugging-new/src/middleware/worker-component-data.js diff --git a/devtools/client/aboutdebugging-new/src/create-store.js b/devtools/client/aboutdebugging-new/src/create-store.js index 938bd6cf4106..9ab6e0dcfd59 100644 --- a/devtools/client/aboutdebugging-new/src/create-store.js +++ b/devtools/client/aboutdebugging-new/src/create-store.js @@ -13,6 +13,7 @@ const { UiState } = require("./reducers/ui-state"); const debugTargetListenerMiddleware = require("./middleware/debug-target-listener"); const extensionComponentDataMiddleware = require("./middleware/extension-component-data"); const tabComponentDataMiddleware = require("./middleware/tab-component-data"); +const workerComponentDataMiddleware = require("./middleware/worker-component-data"); const { getNetworkLocations } = require("./modules/network-locations"); function configureStore() { @@ -24,7 +25,8 @@ function configureStore() { const middleware = applyMiddleware(thunk, debugTargetListenerMiddleware, extensionComponentDataMiddleware, - tabComponentDataMiddleware); + tabComponentDataMiddleware, + workerComponentDataMiddleware); return createStore(rootReducer, initialState, middleware); } diff --git a/devtools/client/aboutdebugging-new/src/middleware/moz.build b/devtools/client/aboutdebugging-new/src/middleware/moz.build index eb72e4141701..f06967a8bc73 100644 --- a/devtools/client/aboutdebugging-new/src/middleware/moz.build +++ b/devtools/client/aboutdebugging-new/src/middleware/moz.build @@ -6,4 +6,5 @@ DevToolsModules( 'debug-target-listener.js', 'extension-component-data.js', 'tab-component-data.js', + 'worker-component-data.js', ) diff --git a/devtools/client/aboutdebugging-new/src/middleware/worker-component-data.js b/devtools/client/aboutdebugging-new/src/middleware/worker-component-data.js new file mode 100644 index 000000000000..42c886b41d52 --- /dev/null +++ b/devtools/client/aboutdebugging-new/src/middleware/worker-component-data.js @@ -0,0 +1,78 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { + DEBUG_TARGETS, + REQUEST_WORKERS_SUCCESS, + SERVICE_WORKER_FETCH_STATES, + SERVICE_WORKER_STATUSES, +} = require("../constants"); + +/** + * This middleware converts workers object that get from DebuggerClient.listAllWorkers() + * to data which is used in DebugTargetItem. + */ +const workerComponentDataMiddleware = store => next => action => { + switch (action.type) { + case REQUEST_WORKERS_SUCCESS: { + action.otherWorkers = toComponentData(action.otherWorkers); + action.serviceWorkers = toComponentData(action.serviceWorkers, true); + action.sharedWorkers = toComponentData(action.sharedWorkers); + break; + } + } + + return next(action); +}; + +function getServiceWorkerStatus(isActive, isRunning) { + if (isActive && isRunning) { + return SERVICE_WORKER_STATUSES.RUNNING; + } else if (isActive) { + return SERVICE_WORKER_STATUSES.STOPPED; + } + // We cannot get service worker registrations unless the registration is in + // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we + // display a custom state "registering" for now. See Bug 1153292. + return SERVICE_WORKER_STATUSES.REGISTERING; +} + +function toComponentData(workers, isServiceWorker) { + return workers.map(worker => { + const type = DEBUG_TARGETS.WORKER; + const id = worker.workerTargetActor; + const icon = "chrome://devtools/skin/images/debugging-workers.svg"; + let { fetch, name, registrationActor, scope } = worker; + let isActive = false; + let isRunning = false; + let status = null; + + if (isServiceWorker) { + fetch = fetch ? SERVICE_WORKER_FETCH_STATES.LISTENING + : SERVICE_WORKER_FETCH_STATES.NOT_LISTENING; + isActive = worker.active; + isRunning = !!worker.workerTargetActor; + status = getServiceWorkerStatus(isActive, isRunning); + } + + return { + name, + icon, + id, + type, + details: { + fetch, + isActive, + isRunning, + registrationActor, + scope, + status, + }, + }; + }); +} + +module.exports = workerComponentDataMiddleware; diff --git a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js index 7e7a562acece..e5c612d1f4d1 100644 --- a/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js +++ b/devtools/client/aboutdebugging-new/src/reducers/runtime-state.js @@ -6,13 +6,10 @@ const { CONNECT_RUNTIME_SUCCESS, - DEBUG_TARGETS, DISCONNECT_RUNTIME_SUCCESS, REQUEST_EXTENSIONS_SUCCESS, REQUEST_TABS_SUCCESS, REQUEST_WORKERS_SUCCESS, - SERVICE_WORKER_FETCH_STATES, - SERVICE_WORKER_STATUSES, } = require("../constants"); function RuntimeState() { @@ -46,11 +43,7 @@ function runtimeReducer(state = RuntimeState(), action) { } case REQUEST_WORKERS_SUCCESS: { const { otherWorkers, serviceWorkers, sharedWorkers } = action; - return Object.assign({}, state, { - otherWorkers: toWorkerComponentData(otherWorkers), - serviceWorkers: toWorkerComponentData(serviceWorkers, true), - sharedWorkers: toWorkerComponentData(sharedWorkers), - }); + return Object.assign({}, state, { otherWorkers, serviceWorkers, sharedWorkers }); } default: @@ -58,53 +51,6 @@ function runtimeReducer(state = RuntimeState(), action) { } } -function getServiceWorkerStatus(isActive, isRunning) { - if (isActive && isRunning) { - return SERVICE_WORKER_STATUSES.RUNNING; - } else if (isActive) { - return SERVICE_WORKER_STATUSES.STOPPED; - } - // We cannot get service worker registrations unless the registration is in - // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we - // display a custom state "registering" for now. See Bug 1153292. - return SERVICE_WORKER_STATUSES.REGISTERING; -} - -function toWorkerComponentData(workers, isServiceWorker) { - return workers.map(worker => { - const type = DEBUG_TARGETS.WORKER; - const id = worker.workerTargetActor; - const icon = "chrome://devtools/skin/images/debugging-workers.svg"; - let { fetch, name, registrationActor, scope } = worker; - let isActive = false; - let isRunning = false; - let status = null; - - if (isServiceWorker) { - fetch = fetch ? SERVICE_WORKER_FETCH_STATES.LISTENING - : SERVICE_WORKER_FETCH_STATES.NOT_LISTENING; - isActive = worker.active; - isRunning = !!worker.workerTargetActor; - status = getServiceWorkerStatus(isActive, isRunning); - } - - return { - name, - icon, - id, - type, - details: { - fetch, - isActive, - isRunning, - registrationActor, - scope, - status, - }, - }; - }); -} - module.exports = { RuntimeState, runtimeReducer, From 1948bdb6bcd8bb3103f8d58024fd443b471404af Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 23 Aug 2018 11:55:50 +0200 Subject: [PATCH 24/53] Bug 1485396: Handle non-unified builds for CompileDB too; r=froydnj --HG-- extra : rebase_source : 98c4d0c6a4f6137ba05c7e13b7a69ba07bd30bf6 extra : amend_source : 863740c6e5e7e24d6ba77c1a107ac1281549ef47 --- python/mozbuild/mozbuild/compilation/database.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/mozbuild/mozbuild/compilation/database.py b/python/mozbuild/mozbuild/compilation/database.py index 8277daa9b2ba..bbb634f712aa 100644 --- a/python/mozbuild/mozbuild/compilation/database.py +++ b/python/mozbuild/mozbuild/compilation/database.py @@ -126,6 +126,12 @@ class CompileDBBackend(CommonBackend): json.dump(db, jsonout, indent=0) def _process_unified_sources(self, obj): + if not obj.have_unified_mapping: + for f in list(sorted(obj.files)): + self._build_db_line(obj.objdir, obj.relsrcdir, obj.config, f, + obj.canonical_suffix) + return + # For unified sources, only include the unified source file. # Note that unified sources are never used for host sources. for f in obj.unified_source_mapping: From 35f7627ddbbd0234f9d97d352dc5404590672705 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 23 Aug 2018 23:24:16 -0400 Subject: [PATCH 25/53] Bug 1485871 - Remove some unused XPCOM registrations for editor; r=masayuki --- dom/html/nsTextEditorState.cpp | 1 - editor/moz.build | 4 ---- editor/nsEditorCID.h | 25 ------------------------- layout/build/nsLayoutModule.cpp | 12 ------------ 4 files changed, 42 deletions(-) delete mode 100644 editor/nsEditorCID.h diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp index 86588889e998..6a52b443aa9b 100644 --- a/dom/html/nsTextEditorState.cpp +++ b/dom/html/nsTextEditorState.cpp @@ -11,7 +11,6 @@ #include "nsIPresShell.h" #include "nsView.h" #include "nsCaret.h" -#include "nsEditorCID.h" #include "nsLayoutCID.h" #include "nsITextControlFrame.h" #include "nsContentCreatorFunctions.h" diff --git a/editor/moz.build b/editor/moz.build index 35f5100f9fcf..9423893ccdfb 100644 --- a/editor/moz.build +++ b/editor/moz.build @@ -31,10 +31,6 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'editor' -EXPORTS += [ - 'nsEditorCID.h', -] - TESTING_JS_MODULES += [ 'AsyncSpellCheckTestHelper.jsm', ] diff --git a/editor/nsEditorCID.h b/editor/nsEditorCID.h deleted file mode 100644 index 4b288771ee4a..000000000000 --- a/editor/nsEditorCID.h +++ /dev/null @@ -1,25 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef NSEDITORCID_H___ - -#define NS_EDITOR_CID \ -{/* {D3DE3431-8A75-11d2-918C-0080C8E44DB5}*/ \ -0xd3de3431, 0x8a75, 0x11d2, \ -{ 0x91, 0x8c, 0x0, 0x80, 0xc8, 0xe4, 0x4d, 0xb5 } } - -#define NS_TEXTEDITOR_CID \ -{/* {e197cc01-cfe1-11d4-8eb0-87ae406dfd3f}*/ \ -0xe197cc01, 0xcfe1, 0x11d4, \ -{ 0x8e, 0xb0, 0x87, 0xae, 0x40, 0x6d, 0xfd, 0x3f } } - -#define NS_HTMLEDITOR_CID \ -{/* {ed0244e0-c144-11d2-8f4c-006008159b0c}*/ \ -0xed0244e0, 0xc144, 0x11d2, \ -{ 0x8f, 0x4c, 0x0, 0x60, 0x08, 0x15, 0x9b, 0x0c } } - -#endif //NSEDITORCID_H___ - - - diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index a8ec89d1b65d..054bff18624a 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -94,9 +94,7 @@ using mozilla::dom::PushNotifier; using mozilla::dom::AudioChannelAgent; // Editor stuff -#include "nsEditorCID.h" #include "mozilla/EditorController.h" //CID -#include "mozilla/HTMLEditor.h" #include "nsScriptSecurityManager.h" #include "ExpandedPrincipal.h" @@ -179,12 +177,8 @@ using mozilla::gmp::GeckoMediaPluginService; { 0x1f15dbc8, 0xbfaa, 0x45de, \ { 0x8a, 0x46, 0x08, 0xe2, 0xe2, 0x63, 0x26, 0xb0 } } -NS_GENERIC_FACTORY_CONSTRUCTOR(TextEditor) - NS_GENERIC_FACTORY_CONSTRUCTOR(nsParserUtils) -NS_GENERIC_FACTORY_CONSTRUCTOR(HTMLEditor) - // PresentationDeviceManager /* e1e79dec-4085-4994-ac5b-744b016697e6 */ #define PRESENTATION_DEVICE_MANAGER_CID \ @@ -533,7 +527,6 @@ NS_DEFINE_NAMED_CID(NS_HOSTOBJECTURIMUTATOR_CID); NS_DEFINE_NAMED_CID(NS_SDBCONNECTION_CID); NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID); NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID); -NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID); NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID); NS_DEFINE_NAMED_CID(QUOTAMANAGER_SERVICE_CID); NS_DEFINE_NAMED_CID(SERVICEWORKERMANAGER_CID); @@ -542,7 +535,6 @@ NS_DEFINE_NAMED_CID(NOTIFICATIONTELEMETRYSERVICE_CID); NS_DEFINE_NAMED_CID(PUSHNOTIFIER_CID); NS_DEFINE_NAMED_CID(WORKERDEBUGGERMANAGER_CID); NS_DEFINE_NAMED_CID(NS_AUDIOCHANNELAGENT_CID); -NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID); NS_DEFINE_NAMED_CID(NS_EDITORCONTROLLER_CID); NS_DEFINE_NAMED_CID(NS_EDITINGCONTROLLER_CID); NS_DEFINE_NAMED_CID(NS_EDITORCOMMANDTABLE_CID); @@ -770,7 +762,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kNS_SDBCONNECTION_CID, false, nullptr, SDBConnection::Create }, { &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, nullptr, SessionStorageManagerConstructor }, { &kNS_DOMLOCALSTORAGEMANAGER_CID, false, nullptr, LocalStorageManagerConstructor }, - { &kNS_TEXTEDITOR_CID, false, nullptr, TextEditorConstructor }, { &kDOMREQUEST_SERVICE_CID, false, nullptr, DOMRequestServiceConstructor }, { &kQUOTAMANAGER_SERVICE_CID, false, nullptr, QuotaManagerServiceConstructor }, { &kSERVICEWORKERMANAGER_CID, false, nullptr, ServiceWorkerManagerConstructor }, @@ -779,7 +770,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kPUSHNOTIFIER_CID, false, nullptr, PushNotifierConstructor }, { &kWORKERDEBUGGERMANAGER_CID, true, nullptr, WorkerDebuggerManagerConstructor }, { &kNS_AUDIOCHANNELAGENT_CID, true, nullptr, AudioChannelAgentConstructor }, - { &kNS_HTMLEDITOR_CID, false, nullptr, HTMLEditorConstructor }, { &kNS_EDITORCONTROLLER_CID, false, nullptr, EditorControllerConstructor }, { &kNS_EDITINGCONTROLLER_CID, false, nullptr, nsEditingControllerConstructor }, { &kNS_EDITORCOMMANDTABLE_CID, false, nullptr, nsEditorCommandTableConstructor }, @@ -872,7 +862,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { // Keeping the old ContractID for backward compatibility { "@mozilla.org/dom/storagemanager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID }, { "@mozilla.org/dom/sessionStorage-manager;1", &kNS_DOMSESSIONSTORAGEMANAGER_CID }, - { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID }, { DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID }, { QUOTAMANAGER_SERVICE_CONTRACTID, &kQUOTAMANAGER_SERVICE_CID }, { SERVICEWORKERMANAGER_CONTRACTID, &kSERVICEWORKERMANAGER_CID }, @@ -881,7 +870,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { PUSHNOTIFIER_CONTRACTID, &kPUSHNOTIFIER_CID }, { WORKERDEBUGGERMANAGER_CONTRACTID, &kWORKERDEBUGGERMANAGER_CID }, { NS_AUDIOCHANNELAGENT_CONTRACTID, &kNS_AUDIOCHANNELAGENT_CID }, - { "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID }, { "@mozilla.org/editor/editorcontroller;1", &kNS_EDITORCONTROLLER_CID }, { "@mozilla.org/editor/editingcontroller;1", &kNS_EDITINGCONTROLLER_CID }, { "@mozilla.org/geolocation;1", &kNS_GEOLOCATION_CID }, From 55802c7e10f76ad27fe87001c9a1dfa6ef48e40a Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Fri, 24 Aug 2018 10:24:52 +0200 Subject: [PATCH 26/53] Bug 1485580 - [wdspec] Improve timeouts tests for "Set Timeout" and "New Session". r=jgraham --HG-- extra : rebase_source : 6d09292c3955f36705665340820f30f81ea80ab7 --- testing/web-platform/meta/MANIFEST.json | 18 ++- .../tests/new_session/invalid_capabilities.py | 52 +------ .../tests/new_session/support/create.py | 139 ++++++++++++++++-- .../tests/webdriver/tests/set_timeouts/set.py | 62 +++++++- .../tests/set_timeouts/user_prompts.py | 62 ++++++++ 5 files changed, 262 insertions(+), 71 deletions(-) create mode 100644 testing/web-platform/tests/webdriver/tests/set_timeouts/user_prompts.py diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index a1a7377f1fc1..ba33055c15d3 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -423114,6 +423114,14 @@ {} ] ], + "webdriver/tests/set_timeouts/user_prompts.py": [ + [ + "/webdriver/tests/set_timeouts/user_prompts.py", + { + "timeout": "long" + } + ] + ], "webdriver/tests/set_window_rect/set.py": [ [ "/webdriver/tests/set_window_rect/set.py", @@ -648594,7 +648602,7 @@ "wdspec" ], "webdriver/tests/new_session/invalid_capabilities.py": [ - "83f93ea22f7ed28fa28ab05d36387df828716026", + "f31ce3b8b6fd5f8e4a9ff4d0137debdb7dacdea4", "wdspec" ], "webdriver/tests/new_session/merge.py": [ @@ -648618,7 +648626,7 @@ "support" ], "webdriver/tests/new_session/support/create.py": [ - "85ae1cd4ea85e0a1e0d712b1a7803d6066ab8739", + "475fe5a424fe609f0a7e55164e56378e229e4885", "support" ], "webdriver/tests/new_session/timeouts.py": [ @@ -648658,7 +648666,11 @@ "support" ], "webdriver/tests/set_timeouts/set.py": [ - "e603e217ec7d73bf7bc59f1d2e8687a89c818c47", + "a78ab2e68e82ba28c15748bb98239b3d232dc9f1", + "wdspec" + ], + "webdriver/tests/set_timeouts/user_prompts.py": [ + "a98d87e9b2e2ca252a3ed7cf215a20bd1c299818", "wdspec" ], "webdriver/tests/set_window_rect/__init__.py": [ diff --git a/testing/web-platform/tests/webdriver/tests/new_session/invalid_capabilities.py b/testing/web-platform/tests/webdriver/tests/new_session/invalid_capabilities.py index 83f93ea22f7e..f31ce3b8b6fd 100644 --- a/testing/web-platform/tests/webdriver/tests/new_session/invalid_capabilities.py +++ b/testing/web-platform/tests/webdriver/tests/new_session/invalid_capabilities.py @@ -1,8 +1,10 @@ import pytest -from tests.support.asserts import assert_error from conftest import product, flatten +from tests.new_session.support.create import invalid_data, invalid_extensions +from tests.support.asserts import assert_error + @pytest.mark.parametrize("value", [None, 1, "{}", []]) def test_invalid_capabilites(new_session, value): @@ -26,29 +28,6 @@ def test_invalid_first_match(new_session, add_browser_capabilities, value): assert_error(response, "invalid argument") -invalid_data = [ - ("acceptInsecureCerts", [1, [], {}, "false"]), - ("browserName", [1, [], {}, False]), - ("browserVersion", [1, [], {}, False]), - ("platformName", [1, [], {}, False]), - ("pageLoadStrategy", [1, [], {}, False, "invalid", "NONE", "Eager", "eagerblah", "interactive", - " eager", "eager "]), - ("proxy", [1, [], "{}", {"proxyType": "SYSTEM"}, {"proxyType": "systemSomething"}, - {"proxy type": "pac"}, {"proxy-Type": "system"}, {"proxy_type": "system"}, - {"proxytype": "system"}, {"PROXYTYPE": "system"}, {"proxyType": None}, - {"proxyType": 1}, {"proxyType": []}, {"proxyType": {"value": "system"}}, - {" proxyType": "system"}, {"proxyType ": "system"}, {"proxyType ": " system"}, - {"proxyType": "system "}]), - ("timeouts", [1, [], "{}", False, {"pageLOAD": 10}, {"page load": 10}, - {"page load": 10}, {"pageLoad": "10"}, {"pageLoad": {"value": 10}}, - {"invalid": 10}, {"pageLoad": -1}, {"pageLoad": 2**64}, - {"pageLoad": None}, {"pageLoad": 1.1}, {"pageLoad": 10, "invalid": 10}, - {" pageLoad": 10}, {"pageLoad ": 10}]), - ("unhandledPromptBehavior", [1, [], {}, False, "DISMISS", "dismissABC", "Accept", - " dismiss", "dismiss "]) -] - - @pytest.mark.parametrize("body", [lambda key, value: {"alwaysMatch": {key: value}}, lambda key, value: {"firstMatch": [{key: value}]}]) @pytest.mark.parametrize("key,value", flatten(product(*item) for item in invalid_data)) @@ -63,31 +42,6 @@ def test_invalid_values(new_session, add_browser_capabilities, body, key, value) assert_error(response, "invalid argument") -invalid_extensions = [ - "firefox", - "firefox_binary", - "firefoxOptions", - "chromeOptions", - "automaticInspection", - "automaticProfiling", - "platform", - "version", - "browser", - "platformVersion", - "javascriptEnabled", - "nativeEvents", - "seleniumProtocol", - "profile", - "trustAllSSLCertificates", - "initialBrowserUrl", - "requireWindowFocus", - "logFile", - "logLevel", - "safari.options", - "ensureCleanSession", -] - - @pytest.mark.parametrize("body", [lambda key, value: {"alwaysMatch": {key: value}}, lambda key, value: {"firstMatch": [{key: value}]}]) @pytest.mark.parametrize("key", invalid_extensions) diff --git a/testing/web-platform/tests/webdriver/tests/new_session/support/create.py b/testing/web-platform/tests/webdriver/tests/new_session/support/create.py index 85ae1cd4ea85..475fe5a424fe 100644 --- a/testing/web-platform/tests/webdriver/tests/new_session/support/create.py +++ b/testing/web-platform/tests/webdriver/tests/new_session/support/create.py @@ -1,15 +1,130 @@ # Note that we can only test things here all implementations must support valid_data = [ - ("acceptInsecureCerts", [False, None]), - ("browserName", [None]), - ("browserVersion", [None]), - ("platformName", [None]), - ("pageLoadStrategy", ["none", "eager", "normal", None]), - ("proxy", [None]), - ("timeouts", [{"script": 0, "pageLoad": 2.0, "implicit": 2**53 - 1}, - {"script": 50, "pageLoad": 25}, - {"script": 500}, - {}]), - ("unhandledPromptBehavior", ["dismiss", "accept", None]), - ("test:extension", [True, "abc", 123, [], {"key": "value"}, None]), + ("acceptInsecureCerts", [ + False, None, + ]), + ("browserName", [ + None, + ]), + ("browserVersion", [ + None, + ]), + ("platformName", [ + None, + ]), + ("pageLoadStrategy", [ + None, + "none", + "eager", + "normal", + ]), + ("proxy", [ + None, + ]), + ("timeouts", [ + None, {}, + {"script": 0, "pageLoad": 2.0, "implicit": 2**53 - 1}, + {"script": 50, "pageLoad": 25}, + {"script": 500}, + ]), + ("unhandledPromptBehavior", [ + "dismiss", + "accept", + None, + ]), + ("test:extension", [ + None, False, "abc", 123, [], + {"key": "value"}, + ]), +] + +invalid_data = [ + ("acceptInsecureCerts", [ + 1, [], {}, "false", + ]), + ("browserName", [ + 1, [], {}, False, + ]), + ("browserVersion", [ + 1, [], {}, False, + ]), + ("platformName", [ + 1, [], {}, False, + ]), + ("pageLoadStrategy", [ + 1, [], {}, False, + "invalid", + "NONE", + "Eager", + "eagerblah", + "interactive", + " eager", + "eager "]), + ("proxy", [ + 1, [], "{}", + {"proxyType": "SYSTEM"}, + {"proxyType": "systemSomething"}, + {"proxy type": "pac"}, + {"proxy-Type": "system"}, + {"proxy_type": "system"}, + {"proxytype": "system"}, + {"PROXYTYPE": "system"}, + {"proxyType": None}, + {"proxyType": 1}, + {"proxyType": []}, + {"proxyType": {"value": "system"}}, + {" proxyType": "system"}, + {"proxyType ": "system"}, + {"proxyType ": " system"}, + {"proxyType": "system "}, + ]), + ("timeouts", [ + 1, [], "{}", False, + {"invalid": 10}, + {"PAGELOAD": 10}, + {"page load": 10}, + {" pageLoad": 10}, + {"pageLoad ": 10}, + {"pageLoad": None}, + {"pageLoad": False}, + {"pageLoad": []}, + {"pageLoad": "10"}, + {"pageLoad": 2.5}, + {"pageLoad": -1}, + {"pageLoad": 2**53}, + {"pageLoad": {"value": 10}}, + {"pageLoad": 10, "invalid": 10}, + ]), + ("unhandledPromptBehavior", [ + 1, [], {}, False, + "DISMISS", + "dismissABC", + "Accept", + " dismiss", + "dismiss ", + ]) +] + +invalid_extensions = [ + "firefox", + "firefox_binary", + "firefoxOptions", + "chromeOptions", + "automaticInspection", + "automaticProfiling", + "platform", + "version", + "browser", + "platformVersion", + "javascriptEnabled", + "nativeEvents", + "seleniumProtocol", + "profile", + "trustAllSSLCertificates", + "initialBrowserUrl", + "requireWindowFocus", + "logFile", + "logLevel", + "safari.options", + "ensureCleanSession", ] diff --git a/testing/web-platform/tests/webdriver/tests/set_timeouts/set.py b/testing/web-platform/tests/webdriver/tests/set_timeouts/set.py index e603e217ec7d..a78ab2e68e82 100644 --- a/testing/web-platform/tests/webdriver/tests/set_timeouts/set.py +++ b/testing/web-platform/tests/webdriver/tests/set_timeouts/set.py @@ -1,3 +1,5 @@ +import pytest + from webdriver.transport import Response from tests.support.asserts import assert_error, assert_success @@ -16,14 +18,60 @@ def test_null_parameter_value(session, http): def test_null_response_value(session): - response = set_timeouts(session, {"implicit": 1000}) + timeouts = {"implicit": 10, "pageLoad": 10, "script": 10} + response = set_timeouts(session, timeouts) value = assert_success(response) assert value is None - response = set_timeouts(session, {"pageLoad": 1000}) - value = assert_success(response) - assert value is None - response = set_timeouts(session, {"script": 1000}) - value = assert_success(response) - assert value is None +@pytest.mark.parametrize("value", [1, "{}", False, []]) +def test_parameters_invalid(session, value): + response = set_timeouts(session, value) + assert_error(response, "invalid argument") + + +def test_parameters_empty_no_change(session): + original = session.timeouts._get() + + response = set_timeouts(session, {}) + assert_success(response) + + assert session.timeouts._get() == original + + +def test_key_invalid(session): + response = set_timeouts(session, {"foo": 1000}) + assert_error(response, "invalid argument") + + +@pytest.mark.parametrize("typ", ["implicit", "pageLoad", "script"]) +@pytest.mark.parametrize("value", [0, 2.0, 2**53 - 1]) +def test_positive_integer(session, typ, value): + response = set_timeouts(session, {typ: value}) + assert_success(response) + + assert session.timeouts._get(typ) == value + + +@pytest.mark.parametrize("typ", ["implicit", "pageLoad", "script"]) +@pytest.mark.parametrize("value", [None, [], {}, False, "10"]) +def test_value_invalid_types(session, typ, value): + response = set_timeouts(session, {typ: value}) + assert_error(response, "invalid argument") + + +@pytest.mark.parametrize("typ", ["implicit", "pageLoad", "script"]) +@pytest.mark.parametrize("value", [-1, 2.5, 2**53]) +def test_value_positive_integer(session, typ, value): + response = set_timeouts(session, {typ: value}) + assert_error(response, "invalid argument") + + +def test_set_all_fields(session): + timeouts = {"implicit": 10, "pageLoad": 20, "script": 30} + response = set_timeouts(session, timeouts) + assert_success(response) + + assert session.timeouts.implicit == 10 + assert session.timeouts.page_load == 20 + assert session.timeouts.script == 30 diff --git a/testing/web-platform/tests/webdriver/tests/set_timeouts/user_prompts.py b/testing/web-platform/tests/webdriver/tests/set_timeouts/user_prompts.py new file mode 100644 index 000000000000..a98d87e9b2e2 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/set_timeouts/user_prompts.py @@ -0,0 +1,62 @@ +# META: timeout=long + +import pytest + +from tests.support.asserts import assert_success + + +def set_timeouts(session, timeouts): + return session.transport.send( + "POST", "session/{session_id}/timeouts".format(**vars(session)), + timeouts) + + +@pytest.fixture +def check_user_prompt_not_closed(session, create_dialog): + def check_user_prompt_not_closed(dialog_type): + create_dialog(dialog_type, text=dialog_type) + + response = set_timeouts(session, {"script": 100}) + assert_success(response) + + assert session.alert.text == dialog_type + session.alert.dismiss() + + assert session.timeouts.script == 100 + + return check_user_prompt_not_closed + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "accept"}) +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_accept(check_user_prompt_not_closed, dialog_type): + check_user_prompt_not_closed(dialog_type) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "accept and notify"}) +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_accept_and_notify(check_user_prompt_not_closed, dialog_type): + check_user_prompt_not_closed(dialog_type) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss"}) +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_dismiss(check_user_prompt_not_closed, dialog_type): + check_user_prompt_not_closed(dialog_type) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss and notify"}) +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_dismiss_and_notify(check_user_prompt_not_closed, dialog_type): + check_user_prompt_not_closed(dialog_type) + + +@pytest.mark.capabilities({"unhandledPromptBehavior": "ignore"}) +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_ignore(check_user_prompt_not_closed, dialog_type): + check_user_prompt_not_closed(dialog_type) + + +@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) +def test_default(check_user_prompt_not_closed, dialog_type): + check_user_prompt_not_closed(dialog_type) From e78d02deeed4ce03d4348757489f52d3125707d0 Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Thu, 23 Aug 2018 15:58:27 +0200 Subject: [PATCH 27/53] Bug 1485580 - [marionette] Update unit tests for invalid timeout values. r=jgraham --HG-- extra : rebase_source : a2e43c040f05673437b34114193e5c8997452d4e --- .../tests/unit/test_capabilities.py | 12 +++++++--- .../marionette/test/unit/test_capabilities.js | 23 +++++++------------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py index 343f950be20f..a02eb5eb318d 100644 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py +++ b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py @@ -167,9 +167,15 @@ class TestCapabilityMatching(MarionetteTestCase): self.marionette.start_session({"setWindowRect": True}) def test_timeouts(self): - timeouts = {u"implicit": 123, u"pageLoad": 456, u"script": 789} - caps = {"timeouts": timeouts} - self.marionette.start_session(caps) + for value in ["", 2.5, {}, []]: + print(" type {}".format(type(value))) + with self.assertRaises(SessionNotCreatedException): + self.marionette.start_session({"timeouts": {"pageLoad": value}}) + + self.delete_session() + + timeouts = {"implicit": 0, "pageLoad": 2.0, "script": 2**53 - 1} + self.marionette.start_session({"timeouts": timeouts}) self.assertIn("timeouts", self.marionette.session_capabilities) self.assertDictEqual(self.marionette.session_capabilities["timeouts"], timeouts) self.assertDictEqual(self.marionette._send_message("WebDriver:GetTimeouts"), timeouts) diff --git a/testing/marionette/test/unit/test_capabilities.js b/testing/marionette/test/unit/test_capabilities.js index 8d8422753486..082f55aee25f 100644 --- a/testing/marionette/test/unit/test_capabilities.js +++ b/testing/marionette/test/unit/test_capabilities.js @@ -40,9 +40,9 @@ add_test(function test_Timeouts_toJSON() { add_test(function test_Timeouts_fromJSON() { let json = { - implicit: 10, - pageLoad: 20, - script: 30, + implicit: 0, + pageLoad: 2.0, + script: Number.MAX_SAFE_INTEGER, }; let ts = Timeouts.fromJSON(json); equal(ts.implicit, json.implicit); @@ -55,7 +55,6 @@ add_test(function test_Timeouts_fromJSON() { add_test(function test_Timeouts_fromJSON_unrecognised_field() { let json = { sessionId: "foobar", - script: 42, }; try { Timeouts.fromJSON(json); @@ -67,23 +66,17 @@ add_test(function test_Timeouts_fromJSON_unrecognised_field() { run_next_test(); }); -add_test(function test_Timeouts_fromJSON_invalid_type() { - try { - Timeouts.fromJSON({script: "foobar"}); - } catch (e) { - equal(e.name, InvalidArgumentError.name); - equal(e.message, "Expected [object String] \"script\" to be a positive integer, got [object String] \"foobar\""); +add_test(function test_Timeouts_fromJSON_invalid_types() { + for (let value of [null, [], {}, false, "10", 2.5]) { + Assert.throws(() => Timeouts.fromJSON({"script": value}), /InvalidArgumentError/); } run_next_test(); }); add_test(function test_Timeouts_fromJSON_bounds() { - try { - Timeouts.fromJSON({script: -42}); - } catch (e) { - equal(e.name, InvalidArgumentError.name); - equal(e.message, "Expected [object String] \"script\" to be a positive integer, got [object Number] -42"); + for (let value of [-1, Number.MAX_SAFE_INTEGER + 1]) { + Assert.throws(() => Timeouts.fromJSON({"script": value}), /InvalidArgumentError/); } run_next_test(); From ff8893e19a3c6cc468a7f37ff0a02567830100c2 Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Thu, 23 Aug 2018 09:59:39 +0200 Subject: [PATCH 28/53] Bug 1485580 - [geckodriver] Make parsing of float timeout values spec conforming. r=jgraham --HG-- extra : rebase_source : 5efb1c8db234cecbd07f478e95bb9f3b8bb0b785 --- .../new_session/create_alwaysMatch.py.ini | 4 - .../new_session/create_firstMatch.py.ini | 4 - testing/webdriver/src/capabilities.rs | 21 ++++- testing/webdriver/src/command.rs | 86 ++++++++++++------- testing/webdriver/src/common.rs | 2 + 5 files changed, 74 insertions(+), 43 deletions(-) delete mode 100644 testing/web-platform/meta/webdriver/tests/new_session/create_alwaysMatch.py.ini delete mode 100644 testing/web-platform/meta/webdriver/tests/new_session/create_firstMatch.py.ini diff --git a/testing/web-platform/meta/webdriver/tests/new_session/create_alwaysMatch.py.ini b/testing/web-platform/meta/webdriver/tests/new_session/create_alwaysMatch.py.ini deleted file mode 100644 index 8a8f63934dce..000000000000 --- a/testing/web-platform/meta/webdriver/tests/new_session/create_alwaysMatch.py.ini +++ /dev/null @@ -1,4 +0,0 @@ -[create_alwaysMatch.py] - [test_valid[timeouts-value10\]] - expected: FAIL - diff --git a/testing/web-platform/meta/webdriver/tests/new_session/create_firstMatch.py.ini b/testing/web-platform/meta/webdriver/tests/new_session/create_firstMatch.py.ini deleted file mode 100644 index afc8153db3f3..000000000000 --- a/testing/web-platform/meta/webdriver/tests/new_session/create_firstMatch.py.ini +++ /dev/null @@ -1,4 +0,0 @@ -[create_firstMatch.py] - [test_valid[timeouts-value10\]] - expected: FAIL - diff --git a/testing/webdriver/src/capabilities.rs b/testing/webdriver/src/capabilities.rs index 92813568cd28..eeea7eb1c002 100644 --- a/testing/webdriver/src/capabilities.rs +++ b/testing/webdriver/src/capabilities.rs @@ -1,3 +1,4 @@ +use common::MAX_SAFE_INTEGER; use error::{ErrorStatus, WebDriverError, WebDriverResult}; use serde_json::{Map, Value}; use std::convert::From; @@ -324,14 +325,26 @@ impl SpecNewSessionParameters { match &**key { x @ "script" | x @ "pageLoad" | x @ "implicit" => { let timeout = try_opt!( - value.as_i64(), + value.as_f64(), ErrorStatus::InvalidArgument, - format!("{} timeouts value is not an integer: {}", x, value) + format!("{} timeouts value is not a number: {}", x, value) ); - if timeout < 0 { + if timeout < 0.0 || timeout.fract() != 0.0 { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, - format!("{} timeouts value is negative: {}", x, timeout), + format!( + "'{}' timeouts value is not a positive Integer: {}", + x, timeout + ), + )); + } + if (timeout as u64) > MAX_SAFE_INTEGER { + return Err(WebDriverError::new( + ErrorStatus::InvalidArgument, + format!( + "'{}' timeouts value is greater than maximum safe integer: {}", + x, timeout + ), )); } } diff --git a/testing/webdriver/src/command.rs b/testing/webdriver/src/command.rs index 64874292daef..3592f0695ce6 100644 --- a/testing/webdriver/src/command.rs +++ b/testing/webdriver/src/command.rs @@ -1,7 +1,7 @@ use actions::ActionSequence; use capabilities::{BrowserCapabilities, Capabilities, CapabilitiesMatching, LegacyNewSessionParameters, SpecNewSessionParameters}; -use common::{Date, FrameId, LocatorStrategy, WebElement}; +use common::{Date, FrameId, LocatorStrategy, WebElement, MAX_SAFE_INTEGER}; use error::{ErrorStatus, WebDriverError, WebDriverResult}; use httpapi::{Route, VoidWebDriverExtensionRoute, WebDriverExtensionRoute}; use regex::Captures; @@ -492,15 +492,56 @@ pub struct TakeScreenshotParameters { } #[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct TimeoutsParameters { - #[serde(skip_serializing_if = "Option::is_none")] + #[serde( + default, skip_serializing_if = "Option::is_none", deserialize_with = "deserialize_to_u64" + )] pub implicit: Option, - #[serde(rename = "pageLoad", skip_serializing_if = "Option::is_none")] + #[serde( + default, + rename = "pageLoad", + skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_to_u64" + )] pub page_load: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde( + default, skip_serializing_if = "Option::is_none", deserialize_with = "deserialize_to_u64" + )] pub script: Option, } +fn deserialize_to_u64<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let opt = Option::deserialize(deserializer)?.map(|value: f64| value); + let value = match opt { + Some(n) => { + if n < 0.0 || n.fract() != 0.0 { + return Err(de::Error::custom(format!( + "'{}' is not a positive Integer", + n + ))); + } + if (n as u64) > MAX_SAFE_INTEGER { + return Err(de::Error::custom(format!( + "'{}' is greater than maximum safe integer", + n + ))); + } + Some(n as u64) + } + None => { + return Err(de::Error::custom(format!( + "'null' is not a positive Integer" + ))); + } + }; + + Ok(value) +} + /// A top-level browsing context’s window rect is a dictionary of the /// [`screenX`], [`screenY`], `width`, and `height` attributes of the /// `WindowProxy`. @@ -972,26 +1013,27 @@ mod tests { #[test] fn test_json_timeout_parameters_with_values() { - let json = r#"{"implicit":1,"pageLoad":2,"script":3}"#; + let json = r#"{"implicit":0,"pageLoad":2.0,"script":9007199254740991}"#; let data = TimeoutsParameters { - implicit: Some(1u64), + implicit: Some(0u64), page_load: Some(2u64), - script: Some(3u64), + script: Some(9007199254740991u64), }; check_deserialize(&json, &data); } + #[test] + fn test_json_timeout_parameters_with_invalid_values() { + let json = r#"{"implicit":-1,"pageLoad":2.5,"script":9007199254740992}"#; + assert!(serde_json::from_str::(&json).is_err()); + } + #[test] fn test_json_timeout_parameters_with_optional_null_field() { let json = r#"{"implicit":null,"pageLoad":null,"script":null}"#; - let data = TimeoutsParameters { - implicit: None, - page_load: None, - script: None, - }; - check_deserialize(&json, &data); + assert!(serde_json::from_str::(&json).is_err()); } #[test] @@ -1006,24 +1048,6 @@ mod tests { check_deserialize(&json, &data); } - #[test] - fn test_json_timeout_parameters_with_invalid_implicit_value() { - let json = r#"{"implicit":1.1}"#; - assert!(serde_json::from_str::(&json).is_err()); - } - - #[test] - fn test_json_timeout_parameters_with_invalid_page_load_value() { - let json = r#"{"pageLoad":1.2}"#; - assert!(serde_json::from_str::(&json).is_err()); - } - - #[test] - fn test_json_timeout_parameters_with_invalid_script_value() { - let json = r#"{"script":1.3}"#; - assert!(serde_json::from_str::(&json).is_err()); - } - #[test] fn test_json_window_rect_parameters_with_values() { let json = r#"{"x":0,"y":1,"width":2,"height":3}"#; diff --git a/testing/webdriver/src/common.rs b/testing/webdriver/src/common.rs index ae1286c8eb81..09af2b2ae3e3 100644 --- a/testing/webdriver/src/common.rs +++ b/testing/webdriver/src/common.rs @@ -4,6 +4,8 @@ pub static ELEMENT_KEY: &'static str = "element-6066-11e4-a52e-4f735466cecf"; pub static FRAME_KEY: &'static str = "frame-075b-4da1-b6ba-e579c2d3230a"; pub static WINDOW_KEY: &'static str = "window-fcc6-11e5-b4f8-330a88ab9d7f"; +pub static MAX_SAFE_INTEGER: u64 = 9007199254740991; + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Cookie { pub name: String, From 0b2817dd4edd59ec91e2e8bbdd43044eee31841d Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Fri, 24 Aug 2018 10:19:15 -0400 Subject: [PATCH 29/53] Bug 1485384 - Fix typo in open_allowCallsWhileClosed; r=me --- .../java/org/mozilla/geckoview/test/SessionLifecycleTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt index c5d620d0db7f..288d51b18563 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt @@ -47,7 +47,7 @@ class SessionLifecycleTest : BaseSessionTest() { @Test fun open_allowCallsWhileClosed() { sessionRule.session.close() - sessionRule.session.loadUri(HELLO_HTML_PATH) + sessionRule.session.loadTestPath(HELLO_HTML_PATH) sessionRule.session.reload() sessionRule.session.open() From 812da7fa44a84ca9858ad7af0abe1fbd53004e25 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 24 Aug 2018 10:24:28 -0400 Subject: [PATCH 30/53] Bug 1472303 - Backed out changeset 8a40d04dfcbb. r=asuth The logic here to move our check was right, but our check was wrong. Also, we landed a test that checked for our wrong implementation. We need to correct our implementation and re-think the test. The right test might just be a mochitest, possibly with some Firefox-only hooks involved. --HG-- extra : rebase_source : 4d6b9a120adcee835f626098e8547c440a39f595 --- .../ServiceWorkerRegistration.cpp | 11 ------- .../ServiceWorkerRegistrationImpl.cpp | 9 ++++-- dom/workers/WorkerScope.cpp | 1 - dom/workers/WorkerScope.h | 6 ---- testing/web-platform/meta/MANIFEST.json | 19 ----------- .../resources/update-top-level-worker.py | 18 ----------- .../update-top-level.https.html | 32 ------------------- 7 files changed, 7 insertions(+), 89 deletions(-) delete mode 100644 testing/web-platform/tests/service-workers/service-worker/resources/update-top-level-worker.py delete mode 100644 testing/web-platform/tests/service-workers/service-worker/update-top-level.https.html diff --git a/dom/serviceworkers/ServiceWorkerRegistration.cpp b/dom/serviceworkers/ServiceWorkerRegistration.cpp index 16d1c8e3b0db..14ab1e4e4a0a 100644 --- a/dom/serviceworkers/ServiceWorkerRegistration.cpp +++ b/dom/serviceworkers/ServiceWorkerRegistration.cpp @@ -222,17 +222,6 @@ ServiceWorkerRegistration::Update(ErrorResult& aRv) return nullptr; } - if (RefPtr serviceWorkerGlobal = - do_QueryObject(global)) { - WorkerPrivate* wp; - if (serviceWorkerGlobal->Registration() == this && - (wp = GetCurrentThreadWorkerPrivate()) && - wp->IsLoadingWorkerScript()) { - outer->MaybeResolve(*this); - return outer.forget(); - } - } - RefPtr self = this; mPendingUpdatePromises += 1; diff --git a/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp b/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp index d9f5a5b92507..78ec762daf21 100644 --- a/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp +++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp @@ -826,8 +826,13 @@ ServiceWorkerRegistrationWorkerThread::Update(ServiceWorkerRegistrationCallback& return; } - // This is ensured by the binding layer. - MOZ_ASSERT(!workerRef->Private()->IsLoadingWorkerScript()); + // Avoid infinite update loops by ignoring update() calls during top + // level script evaluation. See: + // https://github.com/slightlyoff/ServiceWorker/issues/800 + if (workerRef->Private()->IsLoadingWorkerScript()) { + aSuccessCB(mDescriptor); + return; + } auto promise = MakeRefPtr(__func__); auto holder = diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 93aa66e7ba01..c46fd01cd3f3 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -677,7 +677,6 @@ SharedWorkerGlobalScope::Close() NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope, mClients, mRegistration) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerGlobalScope) - NS_INTERFACE_MAP_ENTRY_CONCRETE(ServiceWorkerGlobalScope) NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope) NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 15ae99bcad05..c0712ed6ac4d 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -302,9 +302,6 @@ public: IMPL_EVENT_HANDLER(connect) }; -#define NS_DOM_SERVICEWORKERGLOBALSCOPE_IID \ - {0x552bfa7e, 0x0dd5, 0x4e94, {0xa0, 0x43, 0xff, 0x34, 0x6b, 0x6e, 0x04, 0x46}} - class ServiceWorkerGlobalScope final : public WorkerGlobalScope { const nsString mScope; @@ -314,7 +311,6 @@ class ServiceWorkerGlobalScope final : public WorkerGlobalScope ~ServiceWorkerGlobalScope(); public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_SERVICEWORKERGLOBALSCOPE_IID) NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) @@ -359,8 +355,6 @@ public: void EventListenerAdded(nsAtom* aType) override; }; -NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerGlobalScope, NS_DOM_SERVICEWORKERGLOBALSCOPE_IID) - class WorkerDebuggerGlobalScope final : public DOMEventTargetHelper, public nsIGlobalObject { diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index ba33055c15d3..709ea158aef3 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -300278,11 +300278,6 @@ {} ] ], - "service-workers/service-worker/resources/update-top-level-worker.py": [ - [ - {} - ] - ], "service-workers/service-worker/resources/update-worker.py": [ [ {} @@ -390576,12 +390571,6 @@ {} ] ], - "service-workers/service-worker/update-top-level.https.html": [ - [ - "/service-workers/service-worker/update-top-level.https.html", - {} - ] - ], "service-workers/service-worker/update.https.html": [ [ "/service-workers/service-worker/update.https.html", @@ -632493,10 +632482,6 @@ "8aaa5ca934457714ee0e529ad4b2b1740d9758dd", "support" ], - "service-workers/service-worker/resources/update-top-level-worker.py": [ - "f77ef284ac0745bd6d31e642742438766f14e32e", - "support" - ], "service-workers/service-worker/resources/update-worker.py": [ "bc9b32ad3e68870d9f540524e70cd7947346e5c8", "support" @@ -632677,10 +632662,6 @@ "d8ed94f776650c8a40ba82df9ca5e909b460bb79", "testharness" ], - "service-workers/service-worker/update-top-level.https.html": [ - "e382028b44a9d19b26b3c15a3bba17fa6a0d9bcb", - "testharness" - ], "service-workers/service-worker/update.https.html": [ "6717d4d7ac289c8a18b1500e21795fd16c5321e7", "testharness" diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/update-top-level-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/update-top-level-worker.py deleted file mode 100644 index f77ef284ac07..000000000000 --- a/testing/web-platform/tests/service-workers/service-worker/resources/update-top-level-worker.py +++ /dev/null @@ -1,18 +0,0 @@ -import time - -def main(request, response): - # no-cache itself to ensure the user agent finds a new version for each update. - headers = [('Cache-Control', 'no-cache, must-revalidate'), - ('Pragma', 'no-cache')] - content_type = 'application/javascript' - - headers.append(('Content-Type', content_type)) - - body = ''' -let promise = self.registration.update() -onmessage = (evt) => { - promise.then(r => { - evt.source.postMessage(self.registration === r ? 'PASS' : 'FAIL'); - }); -};''' - return headers, '/* %s %s */ %s' % (time.time(), time.clock(), body) diff --git a/testing/web-platform/tests/service-workers/service-worker/update-top-level.https.html b/testing/web-platform/tests/service-workers/service-worker/update-top-level.https.html deleted file mode 100644 index e382028b44a9..000000000000 --- a/testing/web-platform/tests/service-workers/service-worker/update-top-level.https.html +++ /dev/null @@ -1,32 +0,0 @@ - -Service Worker: Registration update() - - - - From 1f9d6d9b78428d12de46b990e85aeb1284e5cfc4 Mon Sep 17 00:00:00 2001 From: Diego Pino Garcia Date: Thu, 23 Aug 2018 17:25:12 -0500 Subject: [PATCH 31/53] Bug 1481199 - Drop the obsolete dataType parameter for nsIBrowserSearchService.addEngine. r=mkaply --- browser/base/content/browser-pageActions.js | 2 +- .../content/test/about/browser_aboutHome_search_POST.js | 2 +- browser/base/content/test/about/head.js | 2 +- .../content/test/general/browser_keywordSearch_postData.js | 2 +- .../browser_ext_webNavigation_urlbar_transitions.js | 2 +- browser/components/search/test/SearchTestUtils.jsm | 2 +- browser/components/search/test/browser_426329.js | 2 +- browser/components/search/test/browser_483086.js | 4 ++-- browser/components/search/test/browser_addEngine.js | 2 +- browser/components/search/test/browser_contextmenu.js | 2 +- browser/components/search/test/browser_healthreport.js | 2 +- .../search/test/browser_hiddenOneOffs_cleanup.js | 2 +- browser/components/search/test/head.js | 2 +- browser/modules/test/browser/browser_ContentSearch.js | 2 +- .../test/browser/browser_UsageTelemetry_searchbar.js | 6 +++--- .../modules/test/browser/browser_UsageTelemetry_urlbar.js | 2 +- mobile/android/chrome/content/browser.js | 2 +- netwerk/base/nsIBrowserSearchService.idl | 7 ++----- .../places/tests/unifiedcomplete/head_autocomplete.js | 2 +- .../components/processsingleton/MainProcessSingleton.js | 2 +- toolkit/components/search/nsSearchService.js | 4 ++-- toolkit/components/search/tests/xpcshell/head_search.js | 3 +-- .../search/tests/xpcshell/test_addEngineWithExtensionID.js | 2 +- .../search/tests/xpcshell/test_addEngine_callback.js | 6 +++--- .../components/search/tests/xpcshell/test_multipleIcons.js | 2 +- .../components/search/tests/xpcshell/test_notifications.js | 2 +- .../telemetry/tests/unit/test_TelemetryEnvironment.js | 2 +- 27 files changed, 34 insertions(+), 38 deletions(-) diff --git a/browser/base/content/browser-pageActions.js b/browser/base/content/browser-pageActions.js index f190c62109e2..57ec2811df2f 100644 --- a/browser/base/content/browser-pageActions.js +++ b/browser/base/content/browser-pageActions.js @@ -1162,7 +1162,7 @@ BrowserPageActions.addSearchEngine = { }, _installEngine(uri, image) { - Services.search.addEngine(uri, null, image, false, { + Services.search.addEngine(uri, image, false, { onSuccess: engine => { showBrowserPageActionFeedback(this.action); }, diff --git a/browser/base/content/test/about/browser_aboutHome_search_POST.js b/browser/base/content/test/about/browser_aboutHome_search_POST.js index 7acc733f6385..a8e99ade4a18 100644 --- a/browser/base/content/test/about/browser_aboutHome_search_POST.js +++ b/browser/base/content/test/about/browser_aboutHome_search_POST.js @@ -54,7 +54,7 @@ add_task(async function() { }; Services.obs.addObserver(searchObserver, "browser-search-engine-modified"); Services.search.addEngine("http://test:80/browser/browser/base/content/test/about/POSTSearchEngine.xml", - null, null, false); + null, false); }); }); }); diff --git a/browser/base/content/test/about/head.js b/browser/base/content/test/about/head.js index ca3fdf5ff500..f79a4d356a00 100644 --- a/browser/base/content/test/about/head.js +++ b/browser/base/content/test/about/head.js @@ -167,7 +167,7 @@ function promiseNewEngine(basename) { info("Waiting for engine to be added: " + basename); return new Promise((resolve, reject) => { let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess(engine) { info("Search engine added: " + basename); registerCleanupFunction(() => { diff --git a/browser/base/content/test/general/browser_keywordSearch_postData.js b/browser/base/content/test/general/browser_keywordSearch_postData.js index 79e5bae4a6c2..0a467c10fd20 100644 --- a/browser/base/content/test/general/browser_keywordSearch_postData.js +++ b/browser/base/content/test/general/browser_keywordSearch_postData.js @@ -50,7 +50,7 @@ function test() { }); Services.search.addEngine("http://test:80/browser/browser/base/content/test/general/POSTSearchEngine.xml", - null, null, false); + null, false); } var gCurrTest; diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js index 2df39f0a2639..b92820dfea67 100644 --- a/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js +++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js @@ -42,7 +42,7 @@ function addSearchEngine(basename) { return new Promise((resolve, reject) => { info("Waiting for engine to be added: " + basename); let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess: (engine) => { info(`Search engine added: ${basename}`); registerCleanupFunction(() => Services.search.removeEngine(engine)); diff --git a/browser/components/search/test/SearchTestUtils.jsm b/browser/components/search/test/SearchTestUtils.jsm index 180c6a90ec34..a7629fc506c6 100644 --- a/browser/components/search/test/SearchTestUtils.jsm +++ b/browser/components/search/test/SearchTestUtils.jsm @@ -28,7 +28,7 @@ var SearchTestUtils = Object.freeze({ */ promiseNewSearchEngine(url) { return new Promise((resolve, reject) => { - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess(engine) { gTestGlobals.registerCleanupFunction(() => Services.search.removeEngine(engine)); resolve(engine); diff --git a/browser/components/search/test/browser_426329.js b/browser/components/search/test/browser_426329.js index d87a4cb56269..e257f8992ff0 100644 --- a/browser/components/search/test/browser_426329.js +++ b/browser/components/search/test/browser_426329.js @@ -83,7 +83,7 @@ function promiseSetEngine() { Services.obs.addObserver(observer, "browser-search-engine-modified"); ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml", - null, "data:image/x-icon,%00", false); + "data:image/x-icon,%00", false); }); } diff --git a/browser/components/search/test/browser_483086.js b/browser/components/search/test/browser_483086.js index a606c8879aa6..af6fdcb66c91 100644 --- a/browser/components/search/test/browser_483086.js +++ b/browser/components/search/test/browser_483086.js @@ -24,7 +24,7 @@ function test() { Services.obs.addObserver(observer, "browser-search-engine-modified"); gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/483086-1.xml", - null, "data:image/x-icon;%00", false); + "data:image/x-icon;%00", false); } function test2() { @@ -45,5 +45,5 @@ function test2() { Services.obs.addObserver(observer, "browser-search-engine-modified"); gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/483086-2.xml", - null, "data:image/x-icon;%00", false); + "data:image/x-icon;%00", false); } diff --git a/browser/components/search/test/browser_addEngine.js b/browser/components/search/test/browser_addEngine.js index e72e4c9110fa..cf683afb8c79 100644 --- a/browser/components/search/test/browser_addEngine.js +++ b/browser/components/search/test/browser_addEngine.js @@ -50,7 +50,7 @@ var gTests = [ Services.obs.addObserver(observer, "browser-search-engine-modified"); gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.xml", - null, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC", + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC", false); }, added(engine) { diff --git a/browser/components/search/test/browser_contextmenu.js b/browser/components/search/test/browser_contextmenu.js index 7f385501339d..845043ef817e 100644 --- a/browser/components/search/test/browser_contextmenu.js +++ b/browser/components/search/test/browser_contextmenu.js @@ -49,7 +49,7 @@ add_task(async function() { Services.obs.addObserver(observer, "browser-search-engine-modified"); ss.addEngine("resource://search-plugins/testEngine_mozsearch.xml", - null, "data:image/x-icon,%00", false); + "data:image/x-icon,%00", false); }); contextMenu = document.getElementById("contentAreaContextMenu"); diff --git a/browser/components/search/test/browser_healthreport.js b/browser/components/search/test/browser_healthreport.js index a1b514754e23..daea794f32f6 100644 --- a/browser/components/search/test/browser_healthreport.js +++ b/browser/components/search/test/browser_healthreport.js @@ -78,7 +78,7 @@ function test() { Services.obs.addObserver(observer, "browser-search-engine-modified"); gCUITestUtils.addSearchBar().then(function() { Services.search.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.xml", - null, "data:image/x-icon,%00", false); + "data:image/x-icon,%00", false); }); } diff --git a/browser/components/search/test/browser_hiddenOneOffs_cleanup.js b/browser/components/search/test/browser_hiddenOneOffs_cleanup.js index d1e9907a01a1..aba2a46951e9 100644 --- a/browser/components/search/test/browser_hiddenOneOffs_cleanup.js +++ b/browser/components/search/test/browser_hiddenOneOffs_cleanup.js @@ -9,7 +9,7 @@ function promiseNewEngine(basename) { Services.search.init({ onInitComplete() { let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess(engine) { info("Search engine added: " + basename); resolve(engine); diff --git a/browser/components/search/test/head.js b/browser/components/search/test/head.js index 1613336f9823..61663bb23a30 100644 --- a/browser/components/search/test/head.js +++ b/browser/components/search/test/head.js @@ -61,7 +61,7 @@ function promiseNewEngine(basename, options = {}) { onInitComplete() { let url = getRootDirectory(options.testPath || gTestPath) + basename; let current = Services.search.currentEngine; - Services.search.addEngine(url, null, options.iconURL || "", false, { + Services.search.addEngine(url, options.iconURL || "", false, { onSuccess(engine) { info("Search engine added: " + basename); if (setAsCurrent) { diff --git a/browser/modules/test/browser/browser_ContentSearch.js b/browser/modules/test/browser/browser_ContentSearch.js index 575f609adc49..501db3a44a46 100644 --- a/browser/modules/test/browser/browser_ContentSearch.js +++ b/browser/modules/test/browser/browser_ContentSearch.js @@ -335,7 +335,7 @@ function waitForNewEngine(basename, numImages) { // Wait for addEngine(). let addDeferred = PromiseUtils.defer(); let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess(engine) { info("Search engine added: " + basename); addDeferred.resolve(engine); diff --git a/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js b/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js index 6440881de906..3b62ce0dd9e8 100644 --- a/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js +++ b/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js @@ -186,7 +186,7 @@ add_task(async function test_oneOff_enterSelection() { // for this test. const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = await new Promise((resolve, reject) => { - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess(engine) { resolve(engine); }, onError() { reject(); } }); @@ -254,7 +254,7 @@ add_task(async function test_suggestion_click() { // for this test. const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = await new Promise((resolve, reject) => { - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess(engine) { resolve(engine); }, onError() { reject(); } }); @@ -311,7 +311,7 @@ add_task(async function test_suggestion_enterSelection() { // for this test. const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = await new Promise((resolve, reject) => { - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess(engine) { resolve(engine); }, onError() { reject(); } }); diff --git a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js index 2c0250fc3cc1..b4cc17ca73e1 100644 --- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js +++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js @@ -82,7 +82,7 @@ function clickURLBarSuggestion(entryName, button = 1) { async function withNewSearchEngine(taskFn) { const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = await new Promise((resolve, reject) => { - Services.search.addEngine(url, null, "", false, { + Services.search.addEngine(url, "", false, { onSuccess(engine) { resolve(engine); }, onError() { reject(); } }); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 5ad5d9838d05..76bfd0e6256b 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -5990,7 +5990,7 @@ var SearchEngines = { }, addOpenSearchEngine: function addOpenSearchEngine(engine) { - Services.search.addEngine(engine.url, Ci.nsISearchEngine.DATA_XML, engine.iconURL, false, { + Services.search.addEngine(engine.url, engine.iconURL, false, { onSuccess: function() { // Display a toast confirming addition of new search engine. Snackbars.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [engine.title], 1), Snackbars.LENGTH_LONG); diff --git a/netwerk/base/nsIBrowserSearchService.idl b/netwerk/base/nsIBrowserSearchService.idl index 8bc9bebb6395..58761689a439 100644 --- a/netwerk/base/nsIBrowserSearchService.idl +++ b/netwerk/base/nsIBrowserSearchService.idl @@ -295,9 +295,6 @@ interface nsIBrowserSearchService : nsISupports * @param engineURL * The URL to the search engine's description file. * - * @param dataType - * Obsolete, the value is ignored. - * * @param iconURL * A URL string to an icon file to be used as the search engine's * icon. This value may be overridden by an icon specified in the @@ -320,8 +317,8 @@ interface nsIBrowserSearchService : nsISupports * @throws NS_ERROR_FAILURE if the description file cannot be successfully * loaded. */ - void addEngine(in AString engineURL, in long dataType, in AString iconURL, - in boolean confirm, [optional] in nsISearchInstallCallback callback, + void addEngine(in AString engineURL, in AString iconURL, in boolean confirm, + [optional] in nsISearchInstallCallback callback, [optional] in AString extensionID); /** diff --git a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js index ee17fd58e4d5..f7c9b6c204d9 100644 --- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js +++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js @@ -462,7 +462,7 @@ function addTestEngine(basename, httpServer = undefined) { }, "browser-search-engine-modified"); info("Adding engine from URL: " + dataUrl + basename); - Services.search.addEngine(dataUrl + basename, null, null, false); + Services.search.addEngine(dataUrl + basename, null, false); }); } diff --git a/toolkit/components/processsingleton/MainProcessSingleton.js b/toolkit/components/processsingleton/MainProcessSingleton.js index dc71dc4ff12c..61dbd0895b7e 100644 --- a/toolkit/components/processsingleton/MainProcessSingleton.js +++ b/toolkit/components/processsingleton/MainProcessSingleton.js @@ -53,7 +53,7 @@ MainProcessSingleton.prototype = { if (status != Cr.NS_OK) return; - Services.search.addEngine(engineURL.spec, null, iconURL ? iconURL.spec : null, true); + Services.search.addEngine(engineURL.spec, iconURL ? iconURL.spec : null, true); }); }, diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index 56a8e6d5ccd8..d95d9077a460 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -3848,8 +3848,8 @@ SearchService.prototype = { } }, - addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL, - aConfirm, aCallback, aExtensionID) { + addEngine: function SRCH_SVC_addEngine(aEngineURL, aIconURL, aConfirm, + aCallback, aExtensionID) { LOG("addEngine: Adding \"" + aEngineURL + "\"."); this._ensureInitialized(); try { diff --git a/toolkit/components/search/tests/xpcshell/head_search.js b/toolkit/components/search/tests/xpcshell/head_search.js index b9f73eec5786..c7c93281cae7 100644 --- a/toolkit/components/search/tests/xpcshell/head_search.js +++ b/toolkit/components/search/tests/xpcshell/head_search.js @@ -415,8 +415,7 @@ var addTestEngines = async function(aItems) { }, "browser-search-engine-modified"); if (item.xmlFileName) { - Services.search.addEngine(gDataUrl + item.xmlFileName, - null, null, false); + Services.search.addEngine(gDataUrl + item.xmlFileName, null, false); } else { Services.search.addEngineWithDetails(item.name, ...item.details); } diff --git a/toolkit/components/search/tests/xpcshell/test_addEngineWithExtensionID.js b/toolkit/components/search/tests/xpcshell/test_addEngineWithExtensionID.js index 015ac2bfaf8d..4019666e9b44 100644 --- a/toolkit/components/search/tests/xpcshell/test_addEngineWithExtensionID.js +++ b/toolkit/components/search/tests/xpcshell/test_addEngineWithExtensionID.js @@ -40,7 +40,7 @@ add_test(function test_addEngineWithExtensionID() { } }; Services.search.addEngine(gDataUrl + "engine.xml", null, - null, false, searchCallback, kExtensionID2); + false, searchCallback, kExtensionID2); }); function run_test() { diff --git a/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js b/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js index 256e949516a7..d15986147f8f 100644 --- a/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js +++ b/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js @@ -50,7 +50,7 @@ add_test(function simple_callback_test() { } }; Services.search.addEngine(gDataUrl + "engine.xml", null, - null, false, searchCallback); + false, searchCallback); }); // Test of the search callback on duplicate engine failures @@ -67,7 +67,7 @@ add_test(function duplicate_failure_test() { }; // Re-add the same engine added in the previous test Services.search.addEngine(gDataUrl + "engine.xml", null, - null, false, searchCallback); + false, searchCallback); }); // Test of the search callback on failure to load the engine failures @@ -84,7 +84,7 @@ add_test(function load_failure_test() { }; // Try adding an engine that doesn't exist Services.search.addEngine("http://invalid/data/engine.xml", null, - null, false, searchCallback); + false, searchCallback); }); function run_test() { diff --git a/toolkit/components/search/tests/xpcshell/test_multipleIcons.js b/toolkit/components/search/tests/xpcshell/test_multipleIcons.js index 04ae7af02254..1190c035f639 100644 --- a/toolkit/components/search/tests/xpcshell/test_multipleIcons.js +++ b/toolkit/components/search/tests/xpcshell/test_multipleIcons.js @@ -48,7 +48,7 @@ add_task(async function test_multipleIcons() { add_task(async function test_icon_not_in_file() { let engineUrl = gDataUrl + "engine-fr.xml"; let engine = await new Promise((resolve, reject) => { - Services.search.addEngine(engineUrl, null, "data:image/x-icon;base64,ico16", + Services.search.addEngine(engineUrl, "data:image/x-icon;base64,ico16", false, {onSuccess: resolve, onError: reject}); }); diff --git a/toolkit/components/search/tests/xpcshell/test_notifications.js b/toolkit/components/search/tests/xpcshell/test_notifications.js index 3ae78a27b2a7..1b325d0edacc 100644 --- a/toolkit/components/search/tests/xpcshell/test_notifications.js +++ b/toolkit/components/search/tests/xpcshell/test_notifications.js @@ -66,5 +66,5 @@ function run_test() { Services.obs.addObserver(search_observer, "browser-search-engine-modified"); - Services.search.addEngine(gDataUrl + "engine.xml", null, null, false); + Services.search.addEngine(gDataUrl + "engine.xml", null, false); } diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js index 340bfd851be7..f70e551be3e8 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js @@ -1610,7 +1610,7 @@ add_task(async function test_defaultSearchEngine() { } }, "browser-search-engine-modified"); Services.search.addEngine("file://" + do_get_cwd().path + "/engine.xml", - null, null, false); + null, false); }); Services.search.defaultEngine = engine; await promise; From 5306437473c48733e4726b6b7745ebf35e248636 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 23 Aug 2018 10:05:35 -0400 Subject: [PATCH 32/53] Bug 1485673 - Allow tracking channels being redirected from http-on-modify-request observer notifications when tracking protection is turned on; r=mayhemer --- netwerk/protocol/http/nsHttpChannel.cpp | 13 ++++++++++ ...RequestNotificationForTrackingResources.js | 24 +++++++++++++++---- .../antitracking/test/browser/embedder.html | 2 ++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index ca77764042a2..593129597058 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -6062,6 +6062,12 @@ nsHttpChannel::CancelForTrackingProtection() return NS_OK; } + // Check to see if we should redirect this channel elsewhere by + // nsIHttpChannel.redirectTo API request + if (mAPIRedirectToURI) { + return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect); + } + return CancelInternal(NS_ERROR_TRACKING_URI); } @@ -6079,6 +6085,13 @@ nsHttpChannel::ContinueCancelledByTrackingProtection() return; } + // Check to see if we should redirect this channel elsewhere by + // nsIHttpChannel.redirectTo API request + if (mAPIRedirectToURI) { + Unused << AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect); + return; + } + Unused << CancelInternal(NS_ERROR_TRACKING_URI); } diff --git a/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js b/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js index 9c4d340509d4..ccaffa3a95f0 100644 --- a/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js +++ b/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js @@ -9,6 +9,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm"); * canceled. */ +let gExpectedResourcesSeen = 0; async function onModifyRequest() { return new Promise((resolve, reject) => { Services.obs.addObserver(function observer(subject, topic, data) { @@ -18,10 +19,25 @@ async function onModifyRequest() { if (httpChannel.URI.prePath + "/" != TEST_3RD_PARTY_DOMAIN_TP) { return; } - ok(spec.endsWith("empty.js"), "Correct resource observed"); - Services.obs.removeObserver(observer, "http-on-modify-request"); - resolve(); - }, "http-on-modify-request"); + if (spec.endsWith("empty.js")) { + ok(true, "Correct resource observed"); + ++gExpectedResourcesSeen; + } else if (spec.endsWith("empty.js?redirect")) { + httpChannel.redirectTo(Services.io.newURI(spec.replace("empty.js?redirect", "head.js"))); + } else if (spec.endsWith("empty.js?redirect2")) { + httpChannel.suspend(); + setTimeout(() => { + httpChannel.redirectTo(Services.io.newURI(spec.replace("empty.js?redirect2", "head.js"))); + httpChannel.resume(); + }, 100); + } else if (spec.endsWith("head.js")) { + ++gExpectedResourcesSeen; + } + if (gExpectedResourcesSeen == 3) { + Services.obs.removeObserver(observer, "http-on-modify-request"); + resolve(); + } + }, "http-on-modify-request", false); }); } diff --git a/toolkit/components/antitracking/test/browser/embedder.html b/toolkit/components/antitracking/test/browser/embedder.html index 33d713794fdd..1a517079e0ae 100644 --- a/toolkit/components/antitracking/test/browser/embedder.html +++ b/toolkit/components/antitracking/test/browser/embedder.html @@ -1,2 +1,4 @@ + + From cdbb2e05861f6e8b776ad5b693eec1cba2239371 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 23 Aug 2018 11:01:40 -0400 Subject: [PATCH 33/53] Bug 1485532 - Part 1: Add a test case for the interaction of tracking protection and the webRequest onBeforeRequest API; r=mayhemer --- .../antitracking/test/browser/browser.ini | 1 + ...RequestNotificationForTrackingResources.js | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js diff --git a/toolkit/components/antitracking/test/browser/browser.ini b/toolkit/components/antitracking/test/browser/browser.ini index 1cf1cde38780..35fd653382de 100644 --- a/toolkit/components/antitracking/test/browser/browser.ini +++ b/toolkit/components/antitracking/test/browser/browser.ini @@ -23,6 +23,7 @@ support-files = server.sjs [browser_existingCookiesForSubresources.js] [browser_imageCache.js] support-files = image.sjs +[browser_onBeforeRequestNotificationForTrackingResources.js] [browser_onModifyRequestNotificationForTrackingResources.js] [browser_subResources.js] support-files = subResources.sjs diff --git a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js new file mode 100644 index 000000000000..36986a7a4025 --- /dev/null +++ b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js @@ -0,0 +1,84 @@ +ChromeUtils.import("resource://gre/modules/Services.jsm"); + +/** + * This test ensures that http-on-modify-request is dispatched for channels that + * are blocked by tracking protection. It sets up a page with a third-party script + * resource on it that is blocked by TP, and sets up an http-on-modify-request + * observer which waits to be notified about that resource. The test would time out + * if the http-on-modify-request notification isn't dispatched before the channel is + * canceled. + */ + +let extension; +add_task(async function() { + extension = ExtensionTestUtils.loadExtension({ + manifest: {permissions: ["webRequest", "webRequestBlocking", "*://*/*"]}, + async background() { + let gExpectedResourcesSeen = 0; + function onBeforeRequest(details) { + let spec = details.url; + browser.test.log("Observed channel for " + spec); + // We would use TEST_3RD_PARTY_DOMAIN_TP here, but the variable is inaccessible + // since it is defined in head.js! + if (!spec.startsWith("https://tracking.example.com/")) { + return; + } + if (spec.endsWith("empty.js")) { + browser.test.succeed("Correct resource observed"); + ++gExpectedResourcesSeen; + } else if (spec.endsWith("empty.js?redirect")) { + return {redirectUrl: spec.replace("empty.js?redirect", "head.js")}; + } else if (spec.endsWith("head.js")) { + ++gExpectedResourcesSeen; + } + if (gExpectedResourcesSeen == 2) { + browser.webRequest.onBeforeRequest.removeListener(onBeforeRequest); + browser.test.sendMessage("finish"); + } + return undefined; + } + + browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, + {urls: ["*://*/*"]}, + ["blocking"]); + browser.test.sendMessage("ready"); + }, + }); + await extension.startup(); + await extension.awaitMessage("ready"); +}); + +add_task(async function() { + info("Starting subResources test"); + + await SpecialPowers.flushPrefEnv(); + await SpecialPowers.pushPrefEnv({"set": [ + ["browser.contentblocking.enabled", true], + ["privacy.trackingprotection.enabled", true], + // the test doesn't open a private window, so we don't care about this pref's value + ["privacy.trackingprotection.pbmode.enabled", false], + // tracking annotations aren't needed in this test, only TP is needed + ["privacy.trackingprotection.annotate_channels", false], + // prevent the content blocking on-boarding UI to start mid-way through the test! + ["privacy.trackingprotection.introCount", ContentBlocking.MAX_INTROS], + ]}); + + await UrlClassifierTestUtils.addTestTrackers(); + + let promise = extension.awaitMessage("finish"); + + info("Creating a new tab"); + let tab = BrowserTestUtils.addTab(gBrowser, TEST_EMBEDDER_PAGE); + gBrowser.selectedTab = tab; + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + await promise; + + info("Removing the tab"); + BrowserTestUtils.removeTab(tab); + + UrlClassifierTestUtils.cleanupTestTrackers(); + await extension.unload(); +}); From 50793958bfb8453c0603e005c2bbe8721f0f207a Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 23 Aug 2018 14:56:41 -0400 Subject: [PATCH 34/53] Bug 1485532 - Part 2: Make sure that tracking requests that webRequest.onBeforeRequest handlers do not handle will still get blocked by tracking protection; r=mayhemer --- netwerk/protocol/http/nsHttpChannel.cpp | 21 +++++++++++++++++++ netwerk/protocol/http/nsHttpChannel.h | 5 +++++ ...RequestNotificationForTrackingResources.js | 8 +++++++ ...RequestNotificationForTrackingResources.js | 8 +++++++ 4 files changed, 42 insertions(+) diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 593129597058..5cff8c365d66 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -336,6 +336,7 @@ nsHttpChannel::nsHttpChannel() , mStronglyFramed(false) , mUsedNetwork(0) , mAuthConnectionRestartable(0) + , mTrackingProtectionCancellationPending(0) , mPushedStream(nullptr) , mLocalBlocklist(false) , mOnTailUnblock(nullptr) @@ -6058,6 +6059,7 @@ nsHttpChannel::CancelForTrackingProtection() if (mSuspendCount) { LOG(("Waiting until resume in Cancel [this=%p]\n", this)); MOZ_ASSERT(!mCallOnResume); + mTrackingProtectionCancellationPending = 1; mCallOnResume = &nsHttpChannel::HandleContinueCancelledByTrackingProtection; return NS_OK; } @@ -6065,6 +6067,7 @@ nsHttpChannel::CancelForTrackingProtection() // Check to see if we should redirect this channel elsewhere by // nsIHttpChannel.redirectTo API request if (mAPIRedirectToURI) { + mTrackingProtectionCancellationPending = 1; return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect); } @@ -6098,6 +6101,12 @@ nsHttpChannel::ContinueCancelledByTrackingProtection() nsresult nsHttpChannel::CancelInternal(nsresult status) { + bool trackingProtectionCancellationPending = + !!mTrackingProtectionCancellationPending; + if (status == NS_ERROR_TRACKING_URI) { + mTrackingProtectionCancellationPending = 0; + } + mCanceled = true; mStatus = status; if (mProxyRequest) @@ -6115,6 +6124,10 @@ nsHttpChannel::CancelInternal(nsresult status) mRequestContext->CancelTailedRequest(this); CloseCacheEntry(false); Unused << AsyncAbort(status); + } else if (trackingProtectionCancellationPending) { + // If we're coming from an asynchronous path when canceling a channel due + // to tracking protection, we need to AsyncAbort the channel now. + Unused << AsyncAbort(status); } return NS_OK; } @@ -6576,6 +6589,14 @@ nsHttpChannel::BeginConnectActual() return mStatus; } + if (mTrackingProtectionCancellationPending) { + LOG(("Waiting for tracking protection cancellation in BeginConnectActual [this=%p]\n", this)); + MOZ_ASSERT(!mCallOnResume || + mCallOnResume == &nsHttpChannel::HandleContinueCancelledByTrackingProtection, + "We should be paused waiting for cancellation from tracking protection"); + return NS_OK; + } + if (!mConnectionInfo->UsingHttpProxy() && !(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) { // Start a DNS lookup very early in case the real open is queued the DNS can diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 2f04fc9b74cc..32eadd0bbd64 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -628,6 +628,11 @@ private: // the next authentication request can be sent on a whole new connection uint32_t mAuthConnectionRestartable : 1; + // True if the channel classifier has marked the channel to be cancelled + // due to the tracking protection rules, but the asynchronous cancellation + // process hasn't finished yet. + uint32_t mTrackingProtectionCancellationPending : 1; + nsTArray mRedirectFuncStack; // Needed for accurate DNS timing diff --git a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js index 36986a7a4025..a4e0527eb829 100644 --- a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js +++ b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js @@ -76,6 +76,14 @@ add_task(async function() { await promise; + info("Verify the number of tracking nodes found"); + await ContentTask.spawn(browser, + { expected: 3, + }, + async function(obj) { + is(content.document.blockedTrackingNodeCount, obj.expected, "Expected tracking nodes found"); + }); + info("Removing the tab"); BrowserTestUtils.removeTab(tab); diff --git a/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js b/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js index ccaffa3a95f0..c361d513535f 100644 --- a/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js +++ b/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js @@ -69,6 +69,14 @@ add_task(async function() { await promise; + info("Verify the number of tracking nodes found"); + await ContentTask.spawn(browser, + { expected: gExpectedResourcesSeen, + }, + async function(obj) { + is(content.document.blockedTrackingNodeCount, obj.expected, "Expected tracking nodes found"); + }); + info("Removing the tab"); BrowserTestUtils.removeTab(tab); From e1dbdd0fcb5c9bee2b197686f4a023c1125d8fb1 Mon Sep 17 00:00:00 2001 From: Devika Sugathan Date: Wed, 22 Aug 2018 23:40:45 +0530 Subject: [PATCH 35/53] Bug 1483810 - Remove obsolete .focusring styling r=dao --- toolkit/themes/osx/global/tree.css | 9 --------- toolkit/themes/shared/tree.inc.css | 11 ----------- 2 files changed, 20 deletions(-) diff --git a/toolkit/themes/osx/global/tree.css b/toolkit/themes/osx/global/tree.css index 24790a1f61ba..d2ec96798d32 100644 --- a/toolkit/themes/osx/global/tree.css +++ b/toolkit/themes/osx/global/tree.css @@ -4,15 +4,6 @@ %include ../../shared/tree.inc.css -/* ::::: tree focusring ::::: */ - -.focusring > .tree-stack > .tree-rows > .tree-bodybox { - border: 1px solid transparent; -} - -.focusring:focus > .tree-stack > .tree-rows > .tree-bodybox { - border: 1px solid -moz-mac-focusring; -} /* ::::: editable tree ::::: */ diff --git a/toolkit/themes/shared/tree.inc.css b/toolkit/themes/shared/tree.inc.css index de5027e3ba12..3e425a4c2061 100644 --- a/toolkit/themes/shared/tree.inc.css +++ b/toolkit/themes/shared/tree.inc.css @@ -17,17 +17,6 @@ tree { -moz-appearance: listbox; } -/* ::::: tree focusring ::::: */ - -.focusring > .tree-stack > .tree-rows > .tree-bodybox { - border: 1px solid transparent; -} - -.focusring:focus > .tree-stack > .tree-rows > .tree-bodybox { - border: 1px solid #000000; -} - - /* ::::: tree rows ::::: */ treechildren::-moz-tree-row { From 65a3db675b1f793fbcd5cf3395bab05b12fe4a01 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 Aug 2018 11:51:30 -0400 Subject: [PATCH 36/53] Bug 1485532 follow-up: Fix lint failures on a CLOSED TREE --- toolkit/components/antitracking/test/browser/.eslintrc.js | 6 +++++- ...owser_onBeforeRequestNotificationForTrackingResources.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/toolkit/components/antitracking/test/browser/.eslintrc.js b/toolkit/components/antitracking/test/browser/.eslintrc.js index b1c842d8a6db..6594a5079b66 100644 --- a/toolkit/components/antitracking/test/browser/.eslintrc.js +++ b/toolkit/components/antitracking/test/browser/.eslintrc.js @@ -3,5 +3,9 @@ module.exports = { "extends": [ "plugin:mozilla/browser-test" - ] + ], + + "env": { + "webextensions": true, + }, }; diff --git a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js index a4e0527eb829..ebb107541430 100644 --- a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js +++ b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js @@ -21,7 +21,7 @@ add_task(async function() { // We would use TEST_3RD_PARTY_DOMAIN_TP here, but the variable is inaccessible // since it is defined in head.js! if (!spec.startsWith("https://tracking.example.com/")) { - return; + return undefined; } if (spec.endsWith("empty.js")) { browser.test.succeed("Correct resource observed"); From 82cd96cb5f9b1944a0ca573d6ef7c1b791f3d230 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 Aug 2018 11:51:58 -0400 Subject: [PATCH 37/53] Bug 1485673 follow-up: Fix lint failures on a CLOSED TREE --- .../browser_onModifyRequestNotificationForTrackingResources.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js b/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js index c361d513535f..f153befc9f3b 100644 --- a/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js +++ b/toolkit/components/antitracking/test/browser/browser_onModifyRequestNotificationForTrackingResources.js @@ -1,3 +1,4 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ ChromeUtils.import("resource://gre/modules/Services.jsm"); /** @@ -37,7 +38,7 @@ async function onModifyRequest() { Services.obs.removeObserver(observer, "http-on-modify-request"); resolve(); } - }, "http-on-modify-request", false); + }, "http-on-modify-request"); }); } From 3f63726aa1d41dca8544aad1e15aef4b964808b6 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 24 Aug 2018 11:55:45 -0400 Subject: [PATCH 38/53] Bug 1485532 follow-up: Update the comments in the test, DONTBUILD --- ...er_onBeforeRequestNotificationForTrackingResources.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js index ebb107541430..8fa8bb6a6b58 100644 --- a/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js +++ b/toolkit/components/antitracking/test/browser/browser_onBeforeRequestNotificationForTrackingResources.js @@ -1,12 +1,11 @@ ChromeUtils.import("resource://gre/modules/Services.jsm"); /** - * This test ensures that http-on-modify-request is dispatched for channels that + * This test ensures that onBeforeRequest is dispatched for webRequest loads that * are blocked by tracking protection. It sets up a page with a third-party script - * resource on it that is blocked by TP, and sets up an http-on-modify-request - * observer which waits to be notified about that resource. The test would time out - * if the http-on-modify-request notification isn't dispatched before the channel is - * canceled. + * resource on it that is blocked by TP, and sets up an onBeforeRequest listener + * which waits to be notified about that resource. The test would time out if the + * onBeforeRequest listener isn't called dispatched before the load is canceled. */ let extension; From a4496c818e1fe78988aacf1a39d8330bc2a24d56 Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Thu, 23 Aug 2018 12:16:49 +0200 Subject: [PATCH 39/53] Bug 1399633 - [wdspec] Re-enable maximize window tests. --HG-- extra : rebase_source : 260828c9cb0604dd0456ed0bbee235d6532e586e --- .../meta/webdriver/tests/maximize_window/user_prompts.py.ini | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 testing/web-platform/meta/webdriver/tests/maximize_window/user_prompts.py.ini diff --git a/testing/web-platform/meta/webdriver/tests/maximize_window/user_prompts.py.ini b/testing/web-platform/meta/webdriver/tests/maximize_window/user_prompts.py.ini deleted file mode 100644 index 4ec512f4915c..000000000000 --- a/testing/web-platform/meta/webdriver/tests/maximize_window/user_prompts.py.ini +++ /dev/null @@ -1,3 +0,0 @@ -[user_prompts.py] - disabled: - if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1399633 From 7daebb448d410eb62af37da4553636a33b500ffe Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Fri, 24 Aug 2018 19:38:08 +0300 Subject: [PATCH 40/53] Bug 1485972 - Set legend-position-relative.html as failing on webrender. r=me NPOTB --- testing/web-platform/meta/MANIFEST.json | 6 +++--- .../legend-position-relative.html.ini | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 709ea158aef3..f560671ff8dc 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -559711,7 +559711,7 @@ "support" ], "css/css-values/reference/vh_not_refreshing_on_chrome-ref.html": [ - "32ce9ada155649e9a4935e1abdddc3d3aadfca73", + "279d1c69b9f7fbe60edb55b7e8a5d507eda24936", "support" ], "css/css-values/reference/vh_not_refreshing_on_chrome_iframe-ref.html": [ @@ -559903,7 +559903,7 @@ "support" ], "css/css-values/support/vh_not_refreshing_on_chrome_iframe.html": [ - "c58ec57a58f29d4774b199ca82dd6d16607d4735", + "8d8e9b49d4aa3d9804a176852288e32ccaaa47d8", "support" ], "css/css-values/unset-value-storage.html": [ @@ -559971,7 +559971,7 @@ "support" ], "css/css-values/vh_not_refreshing_on_chrome.html": [ - "b4e0a413ab975dea429ca35642dc075a54127117", + "52a45a114c85bf96a175ca583d8a2b6f54b9ab6c", "reftest" ], "css/css-values/viewport-relative-lengths-scaled-viewport.html": [ diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini new file mode 100644 index 000000000000..382d4b71fe7c --- /dev/null +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini @@ -0,0 +1,2 @@ +[legend-position-relative.html] + if webrender: FAIL From 9172f9d6167b6d8afa85a218bceead485a3d296d Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 22 Aug 2018 00:37:28 -0400 Subject: [PATCH 41/53] Bug 1485494 - Remove the XPCOM registration for RedirectChannelRegistrar; r=mayhemer --- netwerk/base/RedirectChannelRegistrar.cpp | 25 +++++++++++++++++++ netwerk/base/RedirectChannelRegistrar.h | 7 ++++++ netwerk/base/moz.build | 1 + netwerk/base/nsNetUtil.cpp | 8 +++--- netwerk/build/nsNetCID.h | 11 -------- netwerk/build/nsNetModule.cpp | 10 +++----- netwerk/protocol/http/HttpChannelParent.cpp | 4 +-- .../http/HttpChannelParentListener.cpp | 10 ++++---- 8 files changed, 46 insertions(+), 30 deletions(-) diff --git a/netwerk/base/RedirectChannelRegistrar.cpp b/netwerk/base/RedirectChannelRegistrar.cpp index 17e26603a915..2cbe6f92a809 100644 --- a/netwerk/base/RedirectChannelRegistrar.cpp +++ b/netwerk/base/RedirectChannelRegistrar.cpp @@ -3,10 +3,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "RedirectChannelRegistrar.h" +#include "mozilla/StaticPtr.h" namespace mozilla { namespace net { +namespace { +StaticRefPtr gSingleton; +} + NS_IMPL_ISUPPORTS(RedirectChannelRegistrar, nsIRedirectChannelRegistrar) RedirectChannelRegistrar::RedirectChannelRegistrar() @@ -15,6 +20,26 @@ RedirectChannelRegistrar::RedirectChannelRegistrar() , mId(1) , mLock("RedirectChannelRegistrar") { + MOZ_ASSERT(!gSingleton); +} + +// static +already_AddRefed +RedirectChannelRegistrar::GetOrCreate() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!gSingleton) { + gSingleton = new RedirectChannelRegistrar(); + } + return do_AddRef(gSingleton); +} + +// static +void +RedirectChannelRegistrar::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + gSingleton = nullptr; } NS_IMETHODIMP diff --git a/netwerk/base/RedirectChannelRegistrar.h b/netwerk/base/RedirectChannelRegistrar.h index 2bf8c5a6c227..438123b5e00e 100644 --- a/netwerk/base/RedirectChannelRegistrar.h +++ b/netwerk/base/RedirectChannelRegistrar.h @@ -26,6 +26,13 @@ class RedirectChannelRegistrar final : public nsIRedirectChannelRegistrar private: ~RedirectChannelRegistrar() = default; +public: + // Singleton accessor + static already_AddRefed GetOrCreate(); + + // Cleanup the singleton instance on shutdown + static void Shutdown(); + protected: typedef nsInterfaceHashtable ChannelHashtable; diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index f15746302ea0..5e972b076929 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -176,6 +176,7 @@ EXPORTS.mozilla.net += [ 'MemoryDownloader.h', 'PartiallySeekableInputStream.h', 'Predictor.h', + 'RedirectChannelRegistrar.h', 'ReferrerPolicy.h', 'SimpleChannelParent.h', 'TCPFastOpen.h', diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index 98cbd6b3b412..76a8b281a624 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -47,7 +47,7 @@ #include "nsIPrivateBrowsingChannel.h" #include "nsIPropertyBag2.h" #include "nsIProtocolProxyService.h" -#include "nsIRedirectChannelRegistrar.h" +#include "mozilla/net/RedirectChannelRegistrar.h" #include "nsIRequestObserverProxy.h" #include "nsIScriptSecurityManager.h" #include "nsISensitiveInfoHiddenURI.h" @@ -2757,11 +2757,9 @@ NS_LinkRedirectChannels(uint32_t channelId, nsIParentChannel *parentChannel, nsIChannel **_result) { - nsresult rv; - nsCOMPtr registrar = - do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); + RedirectChannelRegistrar::GetOrCreate(); + MOZ_ASSERT(registrar); return registrar->LinkChannels(channelId, parentChannel, diff --git a/netwerk/build/nsNetCID.h b/netwerk/build/nsNetCID.h index dfdad224afe3..fed218d5a04c 100644 --- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -474,17 +474,6 @@ #define NS_URICLASSIFIERSERVICE_CONTRACTID \ "@mozilla.org/uriclassifierservice" -// Redirect channel registrar used for redirect to various protocols -#define NS_REDIRECTCHANNELREGISTRAR_CONTRACTID \ - "@mozilla.org/redirectchannelregistrar;1" -#define NS_REDIRECTCHANNELREGISTRAR_CID \ -{ /* {b69043a6-8929-4d60-8d17-a27e44a8393e} */ \ - 0xb69043a6, \ - 0x8929, \ - 0x4d60, \ - { 0x8d, 0x17, 0xa2, 0x7e, 0x44, 0xa8, 0x39, 0x3e } \ -} - // service implementing nsINetworkPredictor #define NS_NETWORKPREDICTOR_CONTRACTID \ "@mozilla.org/network/predictor;1" diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 9aac5c67891b..f0e1cab622bf 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -39,6 +39,7 @@ #include "Predictor.h" #include "nsIThreadPool.h" #include "mozilla/net/NeckoChild.h" +#include "RedirectChannelRegistrar.h" #include "nsNetCID.h" @@ -131,10 +132,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsEffectiveTLDService, Init) #include "nsSerializationHelper.h" NS_GENERIC_FACTORY_CONSTRUCTOR(nsSerializationHelper) -#include "RedirectChannelRegistrar.h" -typedef mozilla::net::RedirectChannelRegistrar RedirectChannelRegistrar; -NS_GENERIC_FACTORY_CONSTRUCTOR(RedirectChannelRegistrar) - #include "CacheStorageService.h" typedef mozilla::net::CacheStorageService CacheStorageService; NS_GENERIC_FACTORY_CONSTRUCTOR(CacheStorageService) @@ -652,6 +649,8 @@ static void nsNetShutdown() mozilla::net::Http2CompressionCleanup(); + mozilla::net::RedirectChannelRegistrar::Shutdown(); + delete gNetSniffers; gNetSniffers = nullptr; delete gDataSniffers; @@ -768,7 +767,6 @@ NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID); #endif NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID); -NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID); NS_DEFINE_NAMED_CID(NS_CACHE_STORAGE_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_NSILOADCONTEXTINFOFACTORY_CID); NS_DEFINE_NAMED_CID(NS_NETWORKPREDICTOR_CID); @@ -893,7 +891,6 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = { { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsNotifyAddrListenerConstructor }, #endif { &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor }, - { &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor }, { &kNS_CACHE_STORAGE_SERVICE_CID, false, nullptr, CacheStorageServiceConstructor }, { &kNS_NSILOADCONTEXTINFOFACTORY_CID, false, nullptr, LoadContextInfoFactoryConstructor }, { &kNS_NETWORKPREDICTOR_CID, false, nullptr, mozilla::net::Predictor::Create }, @@ -1017,7 +1014,6 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = { { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID }, #endif { NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID }, - { NS_REDIRECTCHANNELREGISTRAR_CONTRACTID, &kNS_REDIRECTCHANNELREGISTRAR_CID }, { NS_CACHE_STORAGE_SERVICE_CONTRACTID, &kNS_CACHE_STORAGE_SERVICE_CID }, { NS_CACHE_STORAGE_SERVICE_CONTRACTID2, &kNS_CACHE_STORAGE_SERVICE_CID }, { NS_NSILOADCONTEXTINFOFACTORY_CONTRACTID, &kNS_NSILOADCONTEXTINFOFACTORY_CID }, diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 347570deb112..21b2ace5dff2 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -45,7 +45,7 @@ #include "nsCORSListenerProxy.h" #include "nsIIPCSerializableInputStream.h" #include "nsIPrompt.h" -#include "nsIRedirectChannelRegistrar.h" +#include "mozilla/net/RedirectChannelRegistrar.h" #include "nsIWindowWatcher.h" #include "nsIDocument.h" #include "nsStreamUtils.h" @@ -972,7 +972,7 @@ HttpChannelParent::RecvRedirect2Verify(const nsresult& aResult, // Wait for background channel ready on target channel nsCOMPtr redirectReg = - do_GetService(NS_REDIRECTCHANNELREGISTRAR_CONTRACTID); + RedirectChannelRegistrar::GetOrCreate(); MOZ_ASSERT(redirectReg); nsCOMPtr redirectParentChannel; diff --git a/netwerk/protocol/http/HttpChannelParentListener.cpp b/netwerk/protocol/http/HttpChannelParentListener.cpp index 7be910841fb6..0ab01cb98887 100644 --- a/netwerk/protocol/http/HttpChannelParentListener.cpp +++ b/netwerk/protocol/http/HttpChannelParentListener.cpp @@ -15,7 +15,7 @@ #include "nsIAuthPrompt.h" #include "nsIAuthPrompt2.h" #include "nsIHttpHeaderVisitor.h" -#include "nsIRedirectChannelRegistrar.h" +#include "mozilla/net/RedirectChannelRegistrar.h" #include "nsIPromptFactory.h" #include "nsIWindowWatcher.h" #include "nsQueryObject.h" @@ -179,8 +179,8 @@ HttpChannelParentListener::AsyncOnChannelRedirect( // Register the new channel and obtain id for it nsCOMPtr registrar = - do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); + RedirectChannelRegistrar::GetOrCreate(); + MOZ_ASSERT(registrar); rv = registrar->RegisterChannel(newChannel, &mRedirectChannelId); NS_ENSURE_SUCCESS(rv, rv); @@ -208,8 +208,8 @@ HttpChannelParentListener::OnRedirectResult(bool succeeded) nsCOMPtr redirectChannel; if (mRedirectChannelId) { nsCOMPtr registrar = - do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); + RedirectChannelRegistrar::GetOrCreate(); + MOZ_ASSERT(registrar); rv = registrar->GetParentChannel(mRedirectChannelId, getter_AddRefs(redirectChannel)); From f672f202ae4903fda5c009b726f15beac5aee3ec Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 21 Aug 2018 16:38:03 -0400 Subject: [PATCH 42/53] Bug 1485005 - Part 1: Fall back to the top-level principal when computing the parent principal on the channel code path for IsFirstPartyStorageAccessGrantedFor(); r=baku This is the fix for the bug. In this case, the top-level principal is picked from the loading principal of the load info object, and it is different from the triggering principal. Since we already fall back to the triggering principal while computing the top-level principal, there is no need to do that explicitly here. In fact we need all of the same rules as previously implemented above in the same function, so we may as well use the toplevelPrincipal variable. I couldn't think of a good way to write a test case for this, sadly. --- toolkit/components/antitracking/AntiTrackingCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/antitracking/AntiTrackingCommon.cpp b/toolkit/components/antitracking/AntiTrackingCommon.cpp index 8632f9b618c3..334e361fe868 100644 --- a/toolkit/components/antitracking/AntiTrackingCommon.cpp +++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp @@ -495,7 +495,7 @@ AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* aChannel return false; } - parentPrincipal = loadInfo->TriggeringPrincipal(); + parentPrincipal = toplevelPrincipal; if (NS_WARN_IF(!parentPrincipal)) { LOG(("No triggering principal, this shouldn't be happening! Bail out early")); // Why we are here?!? From 17741f0004bdf33be88dc288c2a291ca9f3610d4 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 21 Aug 2018 16:39:27 -0400 Subject: [PATCH 43/53] Bug 1485005 - Part 2: Get the cookie behavior from the top-level principal on the channel code path for IsFirstPartyStorageAccessGrantedFor(); r=baku This isn't related to this bug report, but while reading this code, I noticed that we are using the wrong principal object here too. --- toolkit/components/antitracking/AntiTrackingCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/components/antitracking/AntiTrackingCommon.cpp b/toolkit/components/antitracking/AntiTrackingCommon.cpp index 334e361fe868..f0a69331acb0 100644 --- a/toolkit/components/antitracking/AntiTrackingCommon.cpp +++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp @@ -445,7 +445,7 @@ AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* aChannel return false; } - int32_t behavior = CookiesBehavior(channelPrincipal); + int32_t behavior = CookiesBehavior(toplevelPrincipal); if (behavior == nsICookieService::BEHAVIOR_ACCEPT) { LOG(("The cookie behavior pref mandates accepting all cookies!")); return true; From d9cb8a1d4aa3ec3779ef081312a1c0a2ba31af74 Mon Sep 17 00:00:00 2001 From: Noemi Erli Date: Fri, 24 Aug 2018 20:34:36 +0300 Subject: [PATCH 44/53] Backed out changeset b24238e8e714 (bug 1481199) for failures in mobile/android/tests/browser/chrome/test_device_search_engine.html --- browser/base/content/browser-pageActions.js | 2 +- .../content/test/about/browser_aboutHome_search_POST.js | 2 +- browser/base/content/test/about/head.js | 2 +- .../content/test/general/browser_keywordSearch_postData.js | 2 +- .../browser_ext_webNavigation_urlbar_transitions.js | 2 +- browser/components/search/test/SearchTestUtils.jsm | 2 +- browser/components/search/test/browser_426329.js | 2 +- browser/components/search/test/browser_483086.js | 4 ++-- browser/components/search/test/browser_addEngine.js | 2 +- browser/components/search/test/browser_contextmenu.js | 2 +- browser/components/search/test/browser_healthreport.js | 2 +- .../search/test/browser_hiddenOneOffs_cleanup.js | 2 +- browser/components/search/test/head.js | 2 +- browser/modules/test/browser/browser_ContentSearch.js | 2 +- .../test/browser/browser_UsageTelemetry_searchbar.js | 6 +++--- .../modules/test/browser/browser_UsageTelemetry_urlbar.js | 2 +- mobile/android/chrome/content/browser.js | 2 +- netwerk/base/nsIBrowserSearchService.idl | 7 +++++-- .../places/tests/unifiedcomplete/head_autocomplete.js | 2 +- .../components/processsingleton/MainProcessSingleton.js | 2 +- toolkit/components/search/nsSearchService.js | 4 ++-- toolkit/components/search/tests/xpcshell/head_search.js | 3 ++- .../search/tests/xpcshell/test_addEngineWithExtensionID.js | 2 +- .../search/tests/xpcshell/test_addEngine_callback.js | 6 +++--- .../components/search/tests/xpcshell/test_multipleIcons.js | 2 +- .../components/search/tests/xpcshell/test_notifications.js | 2 +- .../telemetry/tests/unit/test_TelemetryEnvironment.js | 2 +- 27 files changed, 38 insertions(+), 34 deletions(-) diff --git a/browser/base/content/browser-pageActions.js b/browser/base/content/browser-pageActions.js index 57ec2811df2f..f190c62109e2 100644 --- a/browser/base/content/browser-pageActions.js +++ b/browser/base/content/browser-pageActions.js @@ -1162,7 +1162,7 @@ BrowserPageActions.addSearchEngine = { }, _installEngine(uri, image) { - Services.search.addEngine(uri, image, false, { + Services.search.addEngine(uri, null, image, false, { onSuccess: engine => { showBrowserPageActionFeedback(this.action); }, diff --git a/browser/base/content/test/about/browser_aboutHome_search_POST.js b/browser/base/content/test/about/browser_aboutHome_search_POST.js index a8e99ade4a18..7acc733f6385 100644 --- a/browser/base/content/test/about/browser_aboutHome_search_POST.js +++ b/browser/base/content/test/about/browser_aboutHome_search_POST.js @@ -54,7 +54,7 @@ add_task(async function() { }; Services.obs.addObserver(searchObserver, "browser-search-engine-modified"); Services.search.addEngine("http://test:80/browser/browser/base/content/test/about/POSTSearchEngine.xml", - null, false); + null, null, false); }); }); }); diff --git a/browser/base/content/test/about/head.js b/browser/base/content/test/about/head.js index f79a4d356a00..ca3fdf5ff500 100644 --- a/browser/base/content/test/about/head.js +++ b/browser/base/content/test/about/head.js @@ -167,7 +167,7 @@ function promiseNewEngine(basename) { info("Waiting for engine to be added: " + basename); return new Promise((resolve, reject) => { let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess(engine) { info("Search engine added: " + basename); registerCleanupFunction(() => { diff --git a/browser/base/content/test/general/browser_keywordSearch_postData.js b/browser/base/content/test/general/browser_keywordSearch_postData.js index 0a467c10fd20..79e5bae4a6c2 100644 --- a/browser/base/content/test/general/browser_keywordSearch_postData.js +++ b/browser/base/content/test/general/browser_keywordSearch_postData.js @@ -50,7 +50,7 @@ function test() { }); Services.search.addEngine("http://test:80/browser/browser/base/content/test/general/POSTSearchEngine.xml", - null, false); + null, null, false); } var gCurrTest; diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js index b92820dfea67..2df39f0a2639 100644 --- a/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js +++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js @@ -42,7 +42,7 @@ function addSearchEngine(basename) { return new Promise((resolve, reject) => { info("Waiting for engine to be added: " + basename); let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess: (engine) => { info(`Search engine added: ${basename}`); registerCleanupFunction(() => Services.search.removeEngine(engine)); diff --git a/browser/components/search/test/SearchTestUtils.jsm b/browser/components/search/test/SearchTestUtils.jsm index a7629fc506c6..180c6a90ec34 100644 --- a/browser/components/search/test/SearchTestUtils.jsm +++ b/browser/components/search/test/SearchTestUtils.jsm @@ -28,7 +28,7 @@ var SearchTestUtils = Object.freeze({ */ promiseNewSearchEngine(url) { return new Promise((resolve, reject) => { - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess(engine) { gTestGlobals.registerCleanupFunction(() => Services.search.removeEngine(engine)); resolve(engine); diff --git a/browser/components/search/test/browser_426329.js b/browser/components/search/test/browser_426329.js index e257f8992ff0..d87a4cb56269 100644 --- a/browser/components/search/test/browser_426329.js +++ b/browser/components/search/test/browser_426329.js @@ -83,7 +83,7 @@ function promiseSetEngine() { Services.obs.addObserver(observer, "browser-search-engine-modified"); ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml", - "data:image/x-icon,%00", false); + null, "data:image/x-icon,%00", false); }); } diff --git a/browser/components/search/test/browser_483086.js b/browser/components/search/test/browser_483086.js index af6fdcb66c91..a606c8879aa6 100644 --- a/browser/components/search/test/browser_483086.js +++ b/browser/components/search/test/browser_483086.js @@ -24,7 +24,7 @@ function test() { Services.obs.addObserver(observer, "browser-search-engine-modified"); gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/483086-1.xml", - "data:image/x-icon;%00", false); + null, "data:image/x-icon;%00", false); } function test2() { @@ -45,5 +45,5 @@ function test2() { Services.obs.addObserver(observer, "browser-search-engine-modified"); gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/483086-2.xml", - "data:image/x-icon;%00", false); + null, "data:image/x-icon;%00", false); } diff --git a/browser/components/search/test/browser_addEngine.js b/browser/components/search/test/browser_addEngine.js index cf683afb8c79..e72e4c9110fa 100644 --- a/browser/components/search/test/browser_addEngine.js +++ b/browser/components/search/test/browser_addEngine.js @@ -50,7 +50,7 @@ var gTests = [ Services.obs.addObserver(observer, "browser-search-engine-modified"); gSS.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.xml", - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC", + null, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC", false); }, added(engine) { diff --git a/browser/components/search/test/browser_contextmenu.js b/browser/components/search/test/browser_contextmenu.js index 845043ef817e..7f385501339d 100644 --- a/browser/components/search/test/browser_contextmenu.js +++ b/browser/components/search/test/browser_contextmenu.js @@ -49,7 +49,7 @@ add_task(async function() { Services.obs.addObserver(observer, "browser-search-engine-modified"); ss.addEngine("resource://search-plugins/testEngine_mozsearch.xml", - "data:image/x-icon,%00", false); + null, "data:image/x-icon,%00", false); }); contextMenu = document.getElementById("contentAreaContextMenu"); diff --git a/browser/components/search/test/browser_healthreport.js b/browser/components/search/test/browser_healthreport.js index daea794f32f6..a1b514754e23 100644 --- a/browser/components/search/test/browser_healthreport.js +++ b/browser/components/search/test/browser_healthreport.js @@ -78,7 +78,7 @@ function test() { Services.obs.addObserver(observer, "browser-search-engine-modified"); gCUITestUtils.addSearchBar().then(function() { Services.search.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine.xml", - "data:image/x-icon,%00", false); + null, "data:image/x-icon,%00", false); }); } diff --git a/browser/components/search/test/browser_hiddenOneOffs_cleanup.js b/browser/components/search/test/browser_hiddenOneOffs_cleanup.js index aba2a46951e9..d1e9907a01a1 100644 --- a/browser/components/search/test/browser_hiddenOneOffs_cleanup.js +++ b/browser/components/search/test/browser_hiddenOneOffs_cleanup.js @@ -9,7 +9,7 @@ function promiseNewEngine(basename) { Services.search.init({ onInitComplete() { let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess(engine) { info("Search engine added: " + basename); resolve(engine); diff --git a/browser/components/search/test/head.js b/browser/components/search/test/head.js index 61663bb23a30..1613336f9823 100644 --- a/browser/components/search/test/head.js +++ b/browser/components/search/test/head.js @@ -61,7 +61,7 @@ function promiseNewEngine(basename, options = {}) { onInitComplete() { let url = getRootDirectory(options.testPath || gTestPath) + basename; let current = Services.search.currentEngine; - Services.search.addEngine(url, options.iconURL || "", false, { + Services.search.addEngine(url, null, options.iconURL || "", false, { onSuccess(engine) { info("Search engine added: " + basename); if (setAsCurrent) { diff --git a/browser/modules/test/browser/browser_ContentSearch.js b/browser/modules/test/browser/browser_ContentSearch.js index 501db3a44a46..575f609adc49 100644 --- a/browser/modules/test/browser/browser_ContentSearch.js +++ b/browser/modules/test/browser/browser_ContentSearch.js @@ -335,7 +335,7 @@ function waitForNewEngine(basename, numImages) { // Wait for addEngine(). let addDeferred = PromiseUtils.defer(); let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess(engine) { info("Search engine added: " + basename); addDeferred.resolve(engine); diff --git a/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js b/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js index 3b62ce0dd9e8..6440881de906 100644 --- a/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js +++ b/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js @@ -186,7 +186,7 @@ add_task(async function test_oneOff_enterSelection() { // for this test. const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = await new Promise((resolve, reject) => { - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess(engine) { resolve(engine); }, onError() { reject(); } }); @@ -254,7 +254,7 @@ add_task(async function test_suggestion_click() { // for this test. const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = await new Promise((resolve, reject) => { - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess(engine) { resolve(engine); }, onError() { reject(); } }); @@ -311,7 +311,7 @@ add_task(async function test_suggestion_enterSelection() { // for this test. const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = await new Promise((resolve, reject) => { - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess(engine) { resolve(engine); }, onError() { reject(); } }); diff --git a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js index b4cc17ca73e1..2c0250fc3cc1 100644 --- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js +++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js @@ -82,7 +82,7 @@ function clickURLBarSuggestion(entryName, button = 1) { async function withNewSearchEngine(taskFn) { const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml"; let suggestionEngine = await new Promise((resolve, reject) => { - Services.search.addEngine(url, "", false, { + Services.search.addEngine(url, null, "", false, { onSuccess(engine) { resolve(engine); }, onError() { reject(); } }); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 76bfd0e6256b..5ad5d9838d05 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -5990,7 +5990,7 @@ var SearchEngines = { }, addOpenSearchEngine: function addOpenSearchEngine(engine) { - Services.search.addEngine(engine.url, engine.iconURL, false, { + Services.search.addEngine(engine.url, Ci.nsISearchEngine.DATA_XML, engine.iconURL, false, { onSuccess: function() { // Display a toast confirming addition of new search engine. Snackbars.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [engine.title], 1), Snackbars.LENGTH_LONG); diff --git a/netwerk/base/nsIBrowserSearchService.idl b/netwerk/base/nsIBrowserSearchService.idl index 58761689a439..8bc9bebb6395 100644 --- a/netwerk/base/nsIBrowserSearchService.idl +++ b/netwerk/base/nsIBrowserSearchService.idl @@ -295,6 +295,9 @@ interface nsIBrowserSearchService : nsISupports * @param engineURL * The URL to the search engine's description file. * + * @param dataType + * Obsolete, the value is ignored. + * * @param iconURL * A URL string to an icon file to be used as the search engine's * icon. This value may be overridden by an icon specified in the @@ -317,8 +320,8 @@ interface nsIBrowserSearchService : nsISupports * @throws NS_ERROR_FAILURE if the description file cannot be successfully * loaded. */ - void addEngine(in AString engineURL, in AString iconURL, in boolean confirm, - [optional] in nsISearchInstallCallback callback, + void addEngine(in AString engineURL, in long dataType, in AString iconURL, + in boolean confirm, [optional] in nsISearchInstallCallback callback, [optional] in AString extensionID); /** diff --git a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js index f7c9b6c204d9..ee17fd58e4d5 100644 --- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js +++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js @@ -462,7 +462,7 @@ function addTestEngine(basename, httpServer = undefined) { }, "browser-search-engine-modified"); info("Adding engine from URL: " + dataUrl + basename); - Services.search.addEngine(dataUrl + basename, null, false); + Services.search.addEngine(dataUrl + basename, null, null, false); }); } diff --git a/toolkit/components/processsingleton/MainProcessSingleton.js b/toolkit/components/processsingleton/MainProcessSingleton.js index 61dbd0895b7e..dc71dc4ff12c 100644 --- a/toolkit/components/processsingleton/MainProcessSingleton.js +++ b/toolkit/components/processsingleton/MainProcessSingleton.js @@ -53,7 +53,7 @@ MainProcessSingleton.prototype = { if (status != Cr.NS_OK) return; - Services.search.addEngine(engineURL.spec, iconURL ? iconURL.spec : null, true); + Services.search.addEngine(engineURL.spec, null, iconURL ? iconURL.spec : null, true); }); }, diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index d95d9077a460..56a8e6d5ccd8 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -3848,8 +3848,8 @@ SearchService.prototype = { } }, - addEngine: function SRCH_SVC_addEngine(aEngineURL, aIconURL, aConfirm, - aCallback, aExtensionID) { + addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL, + aConfirm, aCallback, aExtensionID) { LOG("addEngine: Adding \"" + aEngineURL + "\"."); this._ensureInitialized(); try { diff --git a/toolkit/components/search/tests/xpcshell/head_search.js b/toolkit/components/search/tests/xpcshell/head_search.js index c7c93281cae7..b9f73eec5786 100644 --- a/toolkit/components/search/tests/xpcshell/head_search.js +++ b/toolkit/components/search/tests/xpcshell/head_search.js @@ -415,7 +415,8 @@ var addTestEngines = async function(aItems) { }, "browser-search-engine-modified"); if (item.xmlFileName) { - Services.search.addEngine(gDataUrl + item.xmlFileName, null, false); + Services.search.addEngine(gDataUrl + item.xmlFileName, + null, null, false); } else { Services.search.addEngineWithDetails(item.name, ...item.details); } diff --git a/toolkit/components/search/tests/xpcshell/test_addEngineWithExtensionID.js b/toolkit/components/search/tests/xpcshell/test_addEngineWithExtensionID.js index 4019666e9b44..015ac2bfaf8d 100644 --- a/toolkit/components/search/tests/xpcshell/test_addEngineWithExtensionID.js +++ b/toolkit/components/search/tests/xpcshell/test_addEngineWithExtensionID.js @@ -40,7 +40,7 @@ add_test(function test_addEngineWithExtensionID() { } }; Services.search.addEngine(gDataUrl + "engine.xml", null, - false, searchCallback, kExtensionID2); + null, false, searchCallback, kExtensionID2); }); function run_test() { diff --git a/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js b/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js index d15986147f8f..256e949516a7 100644 --- a/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js +++ b/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js @@ -50,7 +50,7 @@ add_test(function simple_callback_test() { } }; Services.search.addEngine(gDataUrl + "engine.xml", null, - false, searchCallback); + null, false, searchCallback); }); // Test of the search callback on duplicate engine failures @@ -67,7 +67,7 @@ add_test(function duplicate_failure_test() { }; // Re-add the same engine added in the previous test Services.search.addEngine(gDataUrl + "engine.xml", null, - false, searchCallback); + null, false, searchCallback); }); // Test of the search callback on failure to load the engine failures @@ -84,7 +84,7 @@ add_test(function load_failure_test() { }; // Try adding an engine that doesn't exist Services.search.addEngine("http://invalid/data/engine.xml", null, - false, searchCallback); + null, false, searchCallback); }); function run_test() { diff --git a/toolkit/components/search/tests/xpcshell/test_multipleIcons.js b/toolkit/components/search/tests/xpcshell/test_multipleIcons.js index 1190c035f639..04ae7af02254 100644 --- a/toolkit/components/search/tests/xpcshell/test_multipleIcons.js +++ b/toolkit/components/search/tests/xpcshell/test_multipleIcons.js @@ -48,7 +48,7 @@ add_task(async function test_multipleIcons() { add_task(async function test_icon_not_in_file() { let engineUrl = gDataUrl + "engine-fr.xml"; let engine = await new Promise((resolve, reject) => { - Services.search.addEngine(engineUrl, "data:image/x-icon;base64,ico16", + Services.search.addEngine(engineUrl, null, "data:image/x-icon;base64,ico16", false, {onSuccess: resolve, onError: reject}); }); diff --git a/toolkit/components/search/tests/xpcshell/test_notifications.js b/toolkit/components/search/tests/xpcshell/test_notifications.js index 1b325d0edacc..3ae78a27b2a7 100644 --- a/toolkit/components/search/tests/xpcshell/test_notifications.js +++ b/toolkit/components/search/tests/xpcshell/test_notifications.js @@ -66,5 +66,5 @@ function run_test() { Services.obs.addObserver(search_observer, "browser-search-engine-modified"); - Services.search.addEngine(gDataUrl + "engine.xml", null, false); + Services.search.addEngine(gDataUrl + "engine.xml", null, null, false); } diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js index f70e551be3e8..340bfd851be7 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js @@ -1610,7 +1610,7 @@ add_task(async function test_defaultSearchEngine() { } }, "browser-search-engine-modified"); Services.search.addEngine("file://" + do_get_cwd().path + "/engine.xml", - null, false); + null, null, false); }); Services.search.defaultEngine = engine; await promise; From 9eedb4045d4a1c56ad7e8aadff5cd9a9d88405ef Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 24 Aug 2018 15:27:20 +0200 Subject: [PATCH 45/53] Bug 1437065: Inline monomorphic calls to wasm; r=luke, r=jandem --HG-- extra : rebase_source : fdf0feee57bbde518368beba2d6056680fe72207 --- js/src/jit/Bailouts.h | 2 +- js/src/jit/CodeGenerator.cpp | 128 +++++++++++++++++++++++++ js/src/jit/CodeGenerator.h | 3 + js/src/jit/CompileWrappers.cpp | 6 ++ js/src/jit/CompileWrappers.h | 9 ++ js/src/jit/IonBuilder.h | 1 + js/src/jit/JSJitFrameIter.cpp | 5 +- js/src/jit/JSJitFrameIter.h | 2 +- js/src/jit/JitFrames.cpp | 6 ++ js/src/jit/JitFrames.h | 40 +++++--- js/src/jit/Lowering.cpp | 53 +++++++++++ js/src/jit/MCallOptimize.cpp | 88 ++++++++++++++++- js/src/jit/MIR.cpp | 31 ++++++ js/src/jit/MIR.h | 45 +++++++++ js/src/jit/MacroAssembler-inl.h | 8 ++ js/src/jit/MacroAssembler.h | 1 + js/src/jit/RegisterAllocator.h | 19 ++-- js/src/jit/shared/LIR-shared.h | 39 ++++++++ js/src/jsdate.cpp | 1 + js/src/vm/Stack.cpp | 3 +- js/src/vm/Stack.h | 19 ++-- js/src/wasm/WasmFrameIter.cpp | 149 +++++++++++++++++++++++++---- js/src/wasm/WasmFrameIter.h | 16 ++++ js/src/wasm/WasmStubs.cpp | 162 +++++++++++++++++++++++++++++++- js/src/wasm/WasmStubs.h | 81 ++++++++++++++++ 25 files changed, 860 insertions(+), 57 deletions(-) diff --git a/js/src/jit/Bailouts.h b/js/src/jit/Bailouts.h index dc5a3d1e56a8..eee583b920c9 100644 --- a/js/src/jit/Bailouts.h +++ b/js/src/jit/Bailouts.h @@ -108,7 +108,7 @@ static const uint32_t FAKE_EXITFP_FOR_BAILOUT_ADDR = 0xba2; static uint8_t* const FAKE_EXITFP_FOR_BAILOUT = reinterpret_cast(FAKE_EXITFP_FOR_BAILOUT_ADDR); -static_assert(!(FAKE_EXITFP_FOR_BAILOUT_ADDR & JitActivation::ExitFpWasmBit), +static_assert(!(FAKE_EXITFP_FOR_BAILOUT_ADDR & wasm::ExitOrJitEntryFPTag), "FAKE_EXITFP_FOR_BAILOUT could be mistaken as a low-bit tagged wasm exit fp"); // BailoutStack is an architecture specific pointer to the stack, given by the diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 0d3c3a9a7783..00f39ca17cb1 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -54,6 +54,7 @@ #include "vm/TraceLogging.h" #include "vm/TypedArrayObject.h" #include "vtune/VTuneWrapper.h" +#include "wasm/WasmStubs.h" #include "builtin/Boolean-inl.h" #include "jit/MacroAssembler-inl.h" @@ -13556,6 +13557,133 @@ CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) masm.bind(ool->rejoin()); } +template +void +CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase* lir) +{ + wasm::JitCallStackArgVector stackArgs; + masm.propagateOOM(stackArgs.reserve(lir->numOperands())); + if (masm.oom()) + return; + + const wasm::FuncExport& funcExport = lir->mir()->funcExport(); + const wasm::FuncType& sig = funcExport.funcType(); + + ABIArgGenerator abi; + for (size_t i = 0; i < lir->numOperands(); i++) { + MIRType argMir; + switch (sig.args()[i].code()) { + case wasm::ValType::I32: + case wasm::ValType::F32: + case wasm::ValType::F64: + argMir = ToMIRType(sig.args()[i]); + break; + case wasm::ValType::I64: + case wasm::ValType::Ref: + case wasm::ValType::AnyRef: + // Don't forget to trace GC type arguments in TraceJitExitFrames + // when they're enabled. + MOZ_CRASH("unexpected argument type when calling from ion to wasm"); + } + + ABIArg arg = abi.next(argMir); + switch (arg.kind()) { + case ABIArg::GPR: + case ABIArg::FPU: { + MOZ_ASSERT(ToAnyRegister(lir->getOperand(i)) == arg.reg()); + stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg()); + break; + } + case ABIArg::Stack: { + const LAllocation* larg = lir->getOperand(i); + if (larg->isConstant()) + stackArgs.infallibleEmplaceBack(ToInt32(larg)); + else if (larg->isGeneralReg()) + stackArgs.infallibleEmplaceBack(ToRegister(larg)); + else if (larg->isFloatReg()) + stackArgs.infallibleEmplaceBack(ToFloatRegister(larg)); + else + stackArgs.infallibleEmplaceBack(ToAddress(larg)); + break; + } +#ifdef JS_CODEGEN_REGISTER_PAIR + case ABIArg::GPR_PAIR: { + MOZ_CRASH("no way to pass i64, and wasm uses hardfp for function calls"); + } +#endif + case ABIArg::Uninitialized: { + MOZ_CRASH("Uninitialized ABIArg kind"); + } + } + } + + switch (sig.ret().code()) { + case wasm::ExprType::Void: + MOZ_ASSERT(lir->mir()->type() == MIRType::Value); + break; + case wasm::ExprType::I32: + MOZ_ASSERT(lir->mir()->type() == MIRType::Int32); + MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg); + break; + case wasm::ExprType::F32: + MOZ_ASSERT(lir->mir()->type() == MIRType::Float32); + MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnFloat32Reg); + break; + case wasm::ExprType::F64: + MOZ_ASSERT(lir->mir()->type() == MIRType::Double); + MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg); + break; + case wasm::ExprType::Ref: + case wasm::ExprType::AnyRef: + case wasm::ExprType::I64: + // Don't forget to trace GC type return value in TraceJitExitFrames + // when they're enabled. + MOZ_CRASH("unexpected return type when calling from ion to wasm"); + case wasm::ExprType::Limit: + MOZ_CRASH("Limit"); + } + + bool profilingEnabled = isProfilerInstrumentationEnabled(); + WasmInstanceObject* instObj = lir->mir()->instanceObject(); + + bool wasmGcEnabled = false; +#ifdef ENABLE_WASM_GC + wasmGcEnabled = gen->options.wasmGcEnabled(); +#endif + + Register scratch = ToRegister(lir->temp()); + + uint32_t callOffset; + GenerateDirectCallFromJit(masm, + funcExport, + instObj->instance(), + stackArgs, + profilingEnabled, + wasmGcEnabled, + scratch, + &callOffset); + + // Add the instance object to the constant pool, so it is transferred to + // the owning IonScript and so that it gets traced as long as the IonScript + // lives. + + uint32_t unused; + masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused)); + + markSafepointAt(callOffset, lir); +} + +void +CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) +{ + emitIonToWasmCallBase(lir); +} +void +CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) +{ + emitIonToWasmCallBase(lir); +} + static_assert(!std::is_polymorphic::value, "CodeGenerator should not have any virtual methods"); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 561a1b93baf7..6de8676900bf 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -219,6 +219,9 @@ class CodeGenerator final : public CodeGeneratorSpecific void emitWasmCallBase(MWasmCall* mir, bool needsBoundsCheck); + template + void emitIonToWasmCallBase(LIonToWasmCallBase* lir); + IonScriptCounts* maybeCreateScriptCounts(); // This function behaves like testValueTruthy with the exception that it can diff --git a/js/src/jit/CompileWrappers.cpp b/js/src/jit/CompileWrappers.cpp index 478410bba9a7..0dac97b67805 100644 --- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -322,6 +322,9 @@ JitCompileOptions::JitCompileOptions() : cloneSingletons_(false), profilerSlowAssertionsEnabled_(false), offThreadCompilationAvailable_(false) +#ifdef ENABLE_WASM_GC + , wasmGcEnabled_(false) +#endif { } @@ -331,4 +334,7 @@ JitCompileOptions::JitCompileOptions(JSContext* cx) profilerSlowAssertionsEnabled_ = cx->runtime()->geckoProfiler().enabled() && cx->runtime()->geckoProfiler().slowAssertionsEnabled(); offThreadCompilationAvailable_ = OffThreadCompilationAvailable(cx); +#ifdef ENABLE_WASM_GC + wasmGcEnabled_ = cx->options().wasmGc(); +#endif } diff --git a/js/src/jit/CompileWrappers.h b/js/src/jit/CompileWrappers.h index af3b1bafff4d..a1f13e5a885e 100644 --- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -138,10 +138,19 @@ class JitCompileOptions return offThreadCompilationAvailable_; } +#ifdef ENABLE_WASM_GC + bool wasmGcEnabled() const { + return wasmGcEnabled_; + } +#endif + private: bool cloneSingletons_; bool profilerSlowAssertionsEnabled_; bool offThreadCompilationAvailable_; +#ifdef ENABLE_WASM_GC + bool wasmGcEnabled_; +#endif }; } // namespace jit diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 91ba2e4f4d14..cca90ee6a58a 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -750,6 +750,7 @@ class IonBuilder InliningResult inlineObjectHasPrototype(CallInfo& callInfo); InliningResult inlineFinishBoundFunctionInit(CallInfo& callInfo); InliningResult inlineIsPackedArray(CallInfo& callInfo); + InliningResult inlineWasmCall(CallInfo& callInfo, JSFunction* target); // Testing functions. InliningResult inlineBailout(CallInfo& callInfo); diff --git a/js/src/jit/JSJitFrameIter.cpp b/js/src/jit/JSJitFrameIter.cpp index b1182f1da64c..2a3a72aa48a8 100644 --- a/js/src/jit/JSJitFrameIter.cpp +++ b/js/src/jit/JSJitFrameIter.cpp @@ -32,14 +32,15 @@ JSJitFrameIter::JSJitFrameIter(const JitActivation* activation) } } -JSJitFrameIter::JSJitFrameIter(const JitActivation* activation, uint8_t* fp) +JSJitFrameIter::JSJitFrameIter(const JitActivation* activation, FrameType frameType, uint8_t* fp) : current_(fp), - type_(JitFrame_JSJitToWasm), + type_(frameType), returnAddressToFp_(nullptr), frameSize_(0), cachedSafepointIndex_(nullptr), activation_(activation) { + MOZ_ASSERT(type_ == JitFrame_JSJitToWasm || type_ == JitFrame_Exit); MOZ_ASSERT(!activation_->bailoutData()); MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI); } diff --git a/js/src/jit/JSJitFrameIter.h b/js/src/jit/JSJitFrameIter.h index 07b1013fbe9b..e222b3e1a832 100644 --- a/js/src/jit/JSJitFrameIter.h +++ b/js/src/jit/JSJitFrameIter.h @@ -119,7 +119,7 @@ class JSJitFrameIter // A constructor specialized for jit->wasm frames, which starts at a // specific FP. - JSJitFrameIter(const JitActivation* activation, uint8_t* fp); + JSJitFrameIter(const JitActivation* activation, FrameType frameType, uint8_t* fp); // Used only by DebugModeOSRVolatileJitFrameIter. void exchangeReturnAddressIfMatch(uint8_t* oldAddr, uint8_t* newAddr) { diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 7e9f8778ee42..eff16406be10 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -1170,6 +1170,12 @@ TraceJitExitFrame(JSTracer* trc, const JSJitFrameIter& frame) return; } + if (frame.isExitFrameLayout()) { + // Nothing to do: we can't have object arguments (yet!) and the callee + // is traced elsewhere. + return; + } + if (frame.isBareExit()) { // Nothing to trace. Fake exit frame pushed for VM functions with // nothing to trace on the stack. diff --git a/js/src/jit/JitFrames.h b/js/src/jit/JitFrames.h index 5aa8fecbf037..20e1b1731f0a 100644 --- a/js/src/jit/JitFrames.h +++ b/js/src/jit/JitFrames.h @@ -471,18 +471,19 @@ class IonICCallFrameLayout : public CommonFrameLayout enum class ExitFrameType : uint8_t { - CallNative = 0x0, - ConstructNative = 0x1, - IonDOMGetter = 0x2, - IonDOMSetter = 0x3, - IonDOMMethod = 0x4, - IonOOLNative = 0x5, - IonOOLProxy = 0x6, - WasmJitEntry = 0x7, - InterpreterStub = 0xFC, - VMFunction = 0xFD, - LazyLink = 0xFE, - Bare = 0xFF, + CallNative = 0x0, + ConstructNative = 0x1, + IonDOMGetter = 0x2, + IonDOMSetter = 0x3, + IonDOMMethod = 0x4, + IonOOLNative = 0x5, + IonOOLProxy = 0x6, + WasmGenericJitEntry = 0x7, + DirectWasmJitCall = 0x8, + InterpreterStub = 0xFC, + VMFunction = 0xFD, + LazyLink = 0xFE, + Bare = 0xFF, }; // GC related data used to keep alive data surrounding the Exit frame. @@ -855,10 +856,10 @@ class InterpreterStubExitFrameLayout : public CalledFromJitExitFrameLayout static ExitFrameType Type() { return ExitFrameType::InterpreterStub; } }; -class WasmExitFrameLayout : CalledFromJitExitFrameLayout +class WasmGenericJitEntryFrameLayout : CalledFromJitExitFrameLayout { public: - static ExitFrameType Type() { return ExitFrameType::WasmJitEntry; } + static ExitFrameType Type() { return ExitFrameType::WasmGenericJitEntry; } }; template<> @@ -867,7 +868,7 @@ ExitFrameLayout::is() { return is() || is() || - is(); + is(); } template <> @@ -880,6 +881,15 @@ ExitFrameLayout::as() return reinterpret_cast(sp); } +class DirectWasmJitCallFrameLayout +{ + protected: // silence clang warning about unused private fields + ExitFooterFrame footer_; + ExitFrameLayout exit_; + public: + static ExitFrameType Type() { return ExitFrameType::DirectWasmJitCall; } +}; + class ICStub; class JitStubFrameLayout : public CommonFrameLayout diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index ff35b60b0d05..d02379fa0abf 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -5343,5 +5343,58 @@ LIRGenerator::visitUnknownValue(MUnknownValue* ins) MOZ_CRASH("Can not lower unknown value."); } +void +LIRGenerator::visitIonToWasmCall(MIonToWasmCall* ins) +{ + // The instruction needs a temp register: + // - that's not the FramePointer, since wasm is going to use it in the + // function. + // - that's not aliasing an input register. + LDefinition scratch = tempFixed(ABINonArgReg0); + + // Also prevent register allocation from using wasm's FramePointer, in + // non-profiling mode. + LDefinition fp = gen->isProfilerInstrumentationEnabled() + ? LDefinition::BogusTemp() + : tempFixed(FramePointer); + + // Note that since this is a LIR call instruction, regalloc will prevent + // the use*AtStart below from reusing any of the temporaries. + + LInstruction* lir; + if (ins->type() == MIRType::Value) + lir = allocateVariadic(ins->numOperands(), scratch, fp); + else + lir = allocateVariadic(ins->numOperands(), scratch, fp); + if (!lir) { + abort(AbortReason::Alloc, "Couldn't allocate for LIonToWasmCallBase"); + return; + } + + ABIArgGenerator abi; + for (unsigned i = 0; i < ins->numOperands(); i++) { + MDefinition* argDef = ins->getOperand(i); + ABIArg arg = abi.next(ToMIRType(argDef->type())); + switch (arg.kind()) { + case ABIArg::GPR: + case ABIArg::FPU: + lir->setOperand(i, useFixedAtStart(argDef, arg.reg())); + break; + case ABIArg::Stack: + lir->setOperand(i, useAtStart(argDef)); + break; +#ifdef JS_CODEGEN_REGISTER_PAIR + case ABIArg::GPR_PAIR: + MOZ_CRASH("no way to pass i64, and wasm uses hardfp for function calls"); +#endif + case ABIArg::Uninitialized: + MOZ_CRASH("Uninitialized ABIArg kind"); + } + } + + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + static_assert(!std::is_polymorphic::value, "LIRGenerator should not have any virtual methods"); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index ae317ed4a0d7..bb7418918499 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -31,6 +31,7 @@ #include "vm/SelfHosting.h" #include "vm/SharedArrayObject.h" #include "vm/TypedArrayObject.h" +#include "wasm/WasmInstance.h" #include "jit/shared/Lowering-shared-inl.h" #include "vm/JSScript-inl.h" @@ -40,6 +41,7 @@ using mozilla::ArrayLength; using mozilla::AssertedCast; +using mozilla::Maybe; using JS::DoubleNaNValue; using JS::TrackedOutcome; @@ -57,7 +59,11 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return InliningStatus_NotInlined; } - if (!target->hasJitInfo() || target->jitInfo()->type() != JSJitInfo::InlinableNative) { + bool isWasmCall = target->isWasmOptimized(); + if (!isWasmCall && + (!target->hasJitInfo() || + target->jitInfo()->type() != JSJitInfo::InlinableNative)) + { // Reaching here means we tried to inline a native for which there is no // Ion specialization. trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization); @@ -81,6 +87,9 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return InliningStatus_NotInlined; } + if (isWasmCall) + return inlineWasmCall(callInfo, target); + switch (InlinableNative inlNative = target->jitInfo()->inlinableNative) { // Array natives. case InlinableNative::Array: @@ -3773,6 +3782,83 @@ IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr) return InliningStatus_Inlined; } +IonBuilder::InliningResult +IonBuilder::inlineWasmCall(CallInfo& callInfo, JSFunction* target) +{ + MOZ_ASSERT(target->isWasmOptimized()); + MOZ_ASSERT(target->realm() == script()->realm()); + + // Don't inline wasm constructors. + if (callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); + return InliningStatus_NotInlined; + } + + wasm::Instance& inst = wasm::ExportedFunctionToInstance(target); + uint32_t funcIndex = inst.code().getFuncIndex(target); + + auto bestTier = inst.code().bestTier(); + const wasm::FuncExport& funcExport = inst.metadata(bestTier).lookupFuncExport(funcIndex); + const wasm::FuncType& sig = funcExport.funcType(); + + // Check that the function doesn't take or return non-compatible JS + // argument types before adding nodes to the MIR graph, otherwise they'd be + // dead code. + if (sig.hasI64ArgOrRet() || sig.temporarilyUnsupportedAnyRef()) + return InliningStatus_NotInlined; + + // If there are too many arguments, don't inline (we won't be able to store + // the arguments in the LIR node). + static constexpr size_t MaxNumInlinedArgs = 8; + static_assert(MaxNumInlinedArgs <= MaxNumLInstructionOperands, + "inlined arguments can all be LIR operands"); + if (sig.args().length() > MaxNumInlinedArgs) + return InliningStatus_NotInlined; + + auto* call = MIonToWasmCall::New(alloc(), inst.object(), funcExport); + if (!call) + return abort(AbortReason::Alloc); + + Maybe undefined; + for (size_t i = 0; i < sig.args().length(); i++) { + if (!alloc().ensureBallast()) + return abort(AbortReason::Alloc); + + // Add undefined if an argument is missing. + if (i >= callInfo.argc() && !undefined) + undefined.emplace(constant(UndefinedValue())); + + MDefinition* arg = i >= callInfo.argc() ? *undefined : callInfo.getArg(i); + + MInstruction* conversion = nullptr; + switch (sig.args()[i].code()) { + case wasm::ValType::I32: + conversion = MTruncateToInt32::New(alloc(), arg); + break; + case wasm::ValType::F32: + conversion = MToFloat32::New(alloc(), arg); + break; + case wasm::ValType::F64: + conversion = MToDouble::New(alloc(), arg); + break; + case wasm::ValType::I64: + case wasm::ValType::AnyRef: + case wasm::ValType::Ref: + MOZ_CRASH("impossible per above check"); + } + + current->add(conversion); + call->initArg(i, conversion); + } + + current->push(call); + current->add(call); + + callInfo.setImplicitlyUsedUnchecked(); + + return InliningStatus_Inlined; +} + #define ADD_NATIVE(native) const JSJitInfo JitInfo_##native { \ { nullptr }, { uint16_t(InlinableNative::native) }, { 0 }, JSJitInfo::InlinableNative }; INLINABLE_NATIVE_LIST(ADD_NATIVE) diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 479dbce18713..48f08d490614 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -26,6 +26,7 @@ #include "jit/RangeAnalysis.h" #include "js/Conversions.h" #include "util/Text.h" +#include "wasm/WasmCode.h" #include "builtin/Boolean-inl.h" @@ -6305,3 +6306,33 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); return false; } + +MIonToWasmCall* +MIonToWasmCall::New(TempAllocator& alloc, WasmInstanceObject* instanceObj, + const wasm::FuncExport& funcExport) +{ + wasm::ExprType retType = funcExport.funcType().ret(); + + MIRType resultType = retType.code() == wasm::ExprType::Void + ? MIRType::Value + : ToMIRType(retType); + + auto* ins = new(alloc) MIonToWasmCall(instanceObj, resultType, funcExport); + if (!ins->init(alloc, funcExport.funcType().args().length())) + return nullptr; + return ins; +} + +bool +MIonToWasmCall::appendRoots(MRootList& roots) const +{ + return roots.append(instanceObj_); +} + +#ifdef DEBUG +bool +MIonToWasmCall::isConsistentFloat32Use(MUse* use) const +{ + return funcExport_.funcType().args()[use->index()].code() == wasm::ValType::F32; +} +#endif diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index e424a98fa234..6981e1abc935 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -36,6 +36,10 @@ namespace js { +namespace wasm { +class FuncExport; +} + class StringObject; namespace jit { @@ -13966,6 +13970,47 @@ class MUnknownValue : public MNullaryInstruction TRIVIAL_NEW_WRAPPERS }; +class MIonToWasmCall final : + public MVariadicInstruction, + public NoTypePolicy::Data +{ + CompilerGCPointer instanceObj_; + const wasm::FuncExport& funcExport_; + + MIonToWasmCall(WasmInstanceObject* instanceObj, MIRType resultType, + const wasm::FuncExport& funcExport) + : MVariadicInstruction(classOpcode), + instanceObj_(instanceObj), + funcExport_(funcExport) + { + setResultType(resultType); + } + + public: + INSTRUCTION_HEADER(IonToWasmCall); + + static MIonToWasmCall* New(TempAllocator& alloc, WasmInstanceObject* instanceObj, + const wasm::FuncExport& funcExport); + + void initArg(size_t i, MDefinition* arg) { + initOperand(i, arg); + } + + WasmInstanceObject* instanceObject() const { + return instanceObj_; + } + const wasm::FuncExport& funcExport() const { + return funcExport_; + } + bool possiblyCalls() const override { + return true; + } + bool appendRoots(MRootList& roots) const override; +#ifdef DEBUG + bool isConsistentFloat32Use(MUse* use) const override; +#endif +}; + #undef INSTRUCTION_HEADER void MUse::init(MDefinition* producer, MNode* consumer) diff --git a/js/src/jit/MacroAssembler-inl.h b/js/src/jit/MacroAssembler-inl.h index 004069842c89..f8fdee4b1841 100644 --- a/js/src/jit/MacroAssembler-inl.h +++ b/js/src/jit/MacroAssembler-inl.h @@ -218,6 +218,14 @@ MacroAssembler::callJit(TrampolinePtr code) return currentOffset(); } +uint32_t +MacroAssembler::callJit(ImmPtr callee) +{ + AutoProfilerCallInstrumentation profiler(*this); + call(callee); + return currentOffset(); +} + void MacroAssembler::makeFrameDescriptor(Register frameSizeReg, FrameType type, uint32_t headerSize) { diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index d75320703c42..b4630371f8d3 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -597,6 +597,7 @@ class MacroAssembler : public MacroAssemblerSpecific inline uint32_t callJit(Register callee); inline uint32_t callJit(JitCode* code); inline uint32_t callJit(TrampolinePtr code); + inline uint32_t callJit(ImmPtr callee); // The frame descriptor is the second field of all Jit frames, pushed before // calling the Jit function. It is a composite value defined in JitFrames.h diff --git a/js/src/jit/RegisterAllocator.h b/js/src/jit/RegisterAllocator.h index 092c932d320e..c4e41eb23e77 100644 --- a/js/src/jit/RegisterAllocator.h +++ b/js/src/jit/RegisterAllocator.h @@ -254,6 +254,15 @@ class InstructionDataMap } }; +inline void +TakeJitRegisters(bool isProfiling, AllocatableRegisterSet* set) +{ +#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64) + if (isProfiling) + set->take(AnyRegister(FramePointer)); +#endif +} + // Common superclass for register allocators. class RegisterAllocator { @@ -280,14 +289,10 @@ class RegisterAllocator graph(graph), allRegisters_(RegisterSet::All()) { - if (mir->compilingWasm()) { + if (mir->compilingWasm()) takeWasmRegisters(allRegisters_); - } else { -#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64) - if (mir->instrumentedProfiling()) - allRegisters_.take(AnyRegister(FramePointer)); -#endif - } + else + TakeJitRegisters(mir->instrumentedProfiling(), &allRegisters_); } MOZ_MUST_USE bool init(); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 8b87e71f3cfd..6d22640d6e26 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -9835,6 +9835,45 @@ class LGetPrototypeOf : public LInstructionHelper } }; +template +class LIonToWasmCallBase : public LVariadicInstruction +{ + using Base = LVariadicInstruction; + public: + explicit LIonToWasmCallBase(LNode::Opcode classOpcode, uint32_t numOperands, + const LDefinition& temp, const LDefinition& fp) + : Base(classOpcode, numOperands) + { + this->setIsCall(); + this->setTemp(0, temp); + this->setTemp(1, fp); + } + MIonToWasmCall* mir() const { + return this->mir_->toIonToWasmCall(); + } + const LDefinition* temp() { + return this->getTemp(0); + } +}; + +class LIonToWasmCall : public LIonToWasmCallBase<1> +{ + public: + LIR_HEADER(IonToWasmCall); + LIonToWasmCall(uint32_t numOperands, const LDefinition& temp, const LDefinition& fp) + : LIonToWasmCallBase<1>(classOpcode, numOperands, temp, fp) + {} +}; + +class LIonToWasmCallV : public LIonToWasmCallBase +{ + public: + LIR_HEADER(IonToWasmCallV); + LIonToWasmCallV(uint32_t numOperands, const LDefinition& temp, const LDefinition& fp) + : LIonToWasmCallBase(classOpcode, numOperands, temp, fp) + {} +}; + } // namespace jit } // namespace js diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index ce0f1ac705b6..bc7851fa7626 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -36,6 +36,7 @@ #include "builtin/String.h" #include "js/Conversions.h" #include "js/Date.h" +#include "js/LocaleSensitive.h" #include "js/Wrapper.h" #include "util/StringBuffer.h" #include "util/Text.h" diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 8ff311c69b20..365022479f64 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -614,12 +614,13 @@ JitFrameIter::settle() MOZ_ASSERT(wasmFrame.done()); uint8_t* prevFP = wasmFrame.unwoundIonCallerFP(); + jit::FrameType prevFrameType = wasmFrame.unwoundIonFrameType(); if (mustUnwindActivation_) act_->setJSExitFP(prevFP); iter_.destroy(); - iter_.construct(act_, prevFP); + iter_.construct(act_, prevFrameType, prevFP); MOZ_ASSERT(!asJSJit().done()); return; } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index ddc2cd2b210d..038bb8b6fbf3 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -1622,13 +1622,10 @@ class BailoutFrameInfo; // A JitActivation is used for frames running in Baseline or Ion. class JitActivation : public Activation { - public: - static const uintptr_t ExitFpWasmBit = 0x1; - - private: // If Baseline, Ion or Wasm code is on the stack, and has called into C++, // this will be aligned to an ExitFrame. The last bit indicates if it's a - // wasm frame (bit set to ExitFpWasmBit) or not (bit set to !ExitFpWasmBit). + // wasm frame (bit set to wasm::ExitOrJitEntryFPTag) or not + // (bit set to ~wasm::ExitOrJitEntryFPTag). uint8_t* packedExitFP_; // When hasWasmExitFP(), encodedWasmExitReason_ holds ExitReason. @@ -1704,14 +1701,14 @@ class JitActivation : public Activation return !!packedExitFP_; } uint8_t* jsOrWasmExitFP() const { - return (uint8_t*)(uintptr_t(packedExitFP_) & ~ExitFpWasmBit); + return (uint8_t*)(uintptr_t(packedExitFP_) & ~wasm::ExitOrJitEntryFPTag); } static size_t offsetOfPackedExitFP() { return offsetof(JitActivation, packedExitFP_); } bool hasJSExitFP() const { - return !(uintptr_t(packedExitFP_) & ExitFpWasmBit); + return !(uintptr_t(packedExitFP_) & wasm::ExitOrJitEntryFPTag); } uint8_t* jsExitFP() const { MOZ_ASSERT(hasJSExitFP()); @@ -1802,16 +1799,16 @@ class JitActivation : public Activation // WebAssembly specific attributes. bool hasWasmExitFP() const { - return uintptr_t(packedExitFP_) & ExitFpWasmBit; + return uintptr_t(packedExitFP_) & wasm::ExitOrJitEntryFPTag; } wasm::Frame* wasmExitFP() const { MOZ_ASSERT(hasWasmExitFP()); - return (wasm::Frame*)(uintptr_t(packedExitFP_) & ~ExitFpWasmBit); + return (wasm::Frame*)(uintptr_t(packedExitFP_) & ~wasm::ExitOrJitEntryFPTag); } void setWasmExitFP(const wasm::Frame* fp) { if (fp) { - MOZ_ASSERT(!(uintptr_t(fp) & ExitFpWasmBit)); - packedExitFP_ = (uint8_t*)(uintptr_t(fp) | ExitFpWasmBit); + MOZ_ASSERT(!(uintptr_t(fp) & wasm::ExitOrJitEntryFPTag)); + packedExitFP_ = (uint8_t*)(uintptr_t(fp) | wasm::ExitOrJitEntryFPTag); MOZ_ASSERT(hasWasmExitFP()); } else { packedExitFP_ = nullptr; diff --git a/js/src/wasm/WasmFrameIter.cpp b/js/src/wasm/WasmFrameIter.cpp index b76eeafc453d..49ed3a3a1592 100644 --- a/js/src/wasm/WasmFrameIter.cpp +++ b/js/src/wasm/WasmFrameIter.cpp @@ -40,6 +40,7 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp) lineOrBytecode_(0), fp_(fp ? fp : activation->wasmExitFP()), unwoundIonCallerFP_(nullptr), + unwoundIonFrameType_(jit::FrameType(-1)), unwind_(Unwind::False), unwoundAddressOfReturnAddress_(nullptr) { @@ -112,7 +113,39 @@ WasmFrameIter::popFrame() { Frame* prevFP = fp_; fp_ = prevFP->callerFP; - MOZ_ASSERT(!(uintptr_t(fp_) & JitActivation::ExitFpWasmBit)); + + if (uintptr_t(fp_) & ExitOrJitEntryFPTag) { + // We just unwound a frame pointer which has the low bit set, + // indicating this is a direct call from the jit into the wasm + // function's body. The call stack resembles this at this point: + // + // |---------------------| + // | JIT FRAME | + // | JIT FAKE EXIT FRAME | <-- tagged fp_ + // | WASM FRAME | <-- prevFP (already unwound) + // |---------------------| + // + // fp_ points to the fake exit frame set up by the jit caller, and the + // return-address-to-fp is in JIT code, thus doesn't belong to any wasm + // instance's code (in particular, there's no associated CodeRange). + // Mark the frame as such and untag FP. + MOZ_ASSERT(!LookupCode(prevFP->returnAddress)); + + unwoundIonCallerFP_ = (uint8_t*)(uintptr_t(fp_) & ~uintptr_t(ExitOrJitEntryFPTag)); + unwoundIonFrameType_ = JitFrame_Exit; + + fp_ = nullptr; + code_ = nullptr; + codeRange_ = nullptr; + + if (unwind_ == Unwind::True) { + activation_->setJSExitFP(unwoundIonCallerFP_); + unwoundAddressOfReturnAddress_ = &prevFP->returnAddress; + } + + MOZ_ASSERT(done()); + return; + } if (!fp_) { code_ = nullptr; @@ -135,7 +168,20 @@ WasmFrameIter::popFrame() MOZ_ASSERT(codeRange_); if (codeRange_->isJitEntry()) { + // This wasm function has been called through the generic JIT entry by + // a JIT caller, so the call stack resembles this: + // + // |---------------------| + // | JIT FRAME | + // | JSJIT TO WASM EXIT | <-- fp_ + // | WASM JIT ENTRY | <-- prevFP (already unwound) + // | WASM FRAME | (already unwound) + // |---------------------| + // + // The next value of FP is just a regular jit frame used marked to + // know that we should transition to a JSJit frame iterator. unwoundIonCallerFP_ = (uint8_t*) fp_; + unwoundIonFrameType_ = JitFrame_JSJitToWasm; fp_ = nullptr; code_ = nullptr; @@ -275,6 +321,14 @@ WasmFrameIter::debugFrame() const return DebugFrame::from(fp_); } +jit::FrameType +WasmFrameIter::unwoundIonFrameType() const +{ + MOZ_ASSERT(unwoundIonCallerFP_); + MOZ_ASSERT(unwoundIonFrameType_ != jit::FrameType(-1)); + return unwoundIonFrameType_; +} + /*****************************************************************************/ // Prologue/epilogue code generation @@ -358,9 +412,9 @@ wasm::SetExitFP(MacroAssembler& masm, ExitReason reason, Register scratch) masm.store32(Imm32(reason.encode()), Address(scratch, JitActivation::offsetOfEncodedWasmExitReason())); - masm.orPtr(Imm32(JitActivation::ExitFpWasmBit), FramePointer); + masm.orPtr(Imm32(ExitOrJitEntryFPTag), FramePointer); masm.storePtr(FramePointer, Address(scratch, JitActivation::offsetOfPackedExitFP())); - masm.andPtr(Imm32(int32_t(~JitActivation::ExitFpWasmBit)), FramePointer); + masm.andPtr(Imm32(int32_t(~ExitOrJitEntryFPTag)), FramePointer); } void @@ -629,7 +683,7 @@ AssertNoWasmExitFPInJitExit(MacroAssembler& masm) Label ok; masm.branchTestPtr(Assembler::Zero, Address(scratch, JitActivation::offsetOfPackedExitFP()), - Imm32(uintptr_t(JitActivation::ExitFpWasmBit)), + Imm32(uintptr_t(ExitOrJitEntryFPTag)), &ok); masm.breakpoint(); masm.bind(&ok); @@ -734,6 +788,18 @@ ProfilingFrameIterator::ProfilingFrameIterator(const JitActivation& activation, initFromExitFP(fp); } +static inline void +AssertDirectJitCall(const void* fp) +{ + // Called via an inlined fast JIT to wasm call: in this case, FP is + // pointing in the middle of the exit frame, right before the exit + // footer; ensure the exit frame type is the expected one. +#ifdef DEBUG + auto* jitCaller = (ExitFrameLayout*)(uintptr_t(fp) & ~ExitOrJitEntryFPTag); + MOZ_ASSERT(jitCaller->footer()->type() == jit::ExitFrameType::DirectWasmJitCall); +#endif +} + static inline void AssertMatchesCallSite(void* callerPC, Frame* callerFP) { @@ -741,7 +807,11 @@ AssertMatchesCallSite(void* callerPC, Frame* callerFP) const CodeRange* callerCodeRange; const Code* code = LookupCode(callerPC, &callerCodeRange); - MOZ_ASSERT(code); + if (!code) { + AssertDirectJitCall(callerFP); + return; + } + MOZ_ASSERT(callerCodeRange); if (callerCodeRange->isInterpEntry()) { @@ -768,7 +838,18 @@ ProfilingFrameIterator::initFromExitFP(const Frame* fp) void* pc = fp->returnAddress; code_ = LookupCode(pc, &codeRange_); - MOZ_ASSERT(code_); + + if (!code_) { + // This is a direct call from the JIT, the caller FP is pointing to a + // tagged JIT caller's frame. + MOZ_ASSERT(uintptr_t(fp->callerFP) & ExitOrJitEntryFPTag); + AssertDirectJitCall(fp->callerFP); + + unwoundIonCallerFP_ = (uint8_t*)(uintptr_t(fp->callerFP) & ~ExitOrJitEntryFPTag); + MOZ_ASSERT(done()); + return; + } + MOZ_ASSERT(codeRange_); // Since we don't have the pc for fp, start unwinding at the caller of fp. @@ -808,6 +889,15 @@ ProfilingFrameIterator::initFromExitFP(const Frame* fp) MOZ_ASSERT(!done()); } +static void +AssertCallerFP(DebugOnly fpWasTagged, Frame* const fp, void** const sp) +{ + MOZ_ASSERT_IF(!fpWasTagged.value, + fp == reinterpret_cast(sp)->callerFP); + MOZ_ASSERT_IF(fpWasTagged.value, + (Frame*)(uintptr_t(fp) | 0x1) == reinterpret_cast(sp)->callerFP); +} + bool js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindState, bool* unwoundCaller) @@ -816,9 +906,12 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat uint8_t* const pc = (uint8_t*) registers.pc; void** const sp = (void**) registers.sp; - // The frame pointer might be in the process of tagging/untagging; make - // sure it's untagged. - Frame* const fp = (Frame*) (intptr_t(registers.fp) & ~JitActivation::ExitFpWasmBit); + // The frame pointer might be: + // - in the process of tagging/untagging when calling into the JITs; + // make sure it's untagged. + // - tagged by an direct JIT call. + DebugOnly fpWasTagged = uintptr_t(registers.fp) & ExitOrJitEntryFPTag; + Frame* const fp = (Frame*) (intptr_t(registers.fp) & ~ExitOrJitEntryFPTag); // Get the CodeRange describing pc and the base address to which the // CodeRange is relative. If the pc is not in a wasm module or a builtin @@ -927,7 +1020,7 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat AssertMatchesCallSite(fixedPC, fixedFP); } else if (offsetFromEntry == PushedFP) { // The full Frame has been pushed; fp is still the caller's fp. - MOZ_ASSERT(fp == reinterpret_cast(sp)->callerFP); + AssertCallerFP(fpWasTagged, fp, sp); fixedPC = reinterpret_cast(sp)->returnAddress; fixedFP = fp; AssertMatchesCallSite(fixedPC, fixedFP); @@ -939,7 +1032,7 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat // The fixedFP field of the Frame has been loaded into fp. // The ra and TLS might also be loaded, but the Frame structure is // still on stack, so we can acess the ra form there. - MOZ_ASSERT(fp == reinterpret_cast(sp)->callerFP); + AssertCallerFP(fpWasTagged, fp, sp); fixedPC = reinterpret_cast(sp)->returnAddress; fixedFP = fp; AssertMatchesCallSite(fixedPC, fixedFP); @@ -1017,7 +1110,7 @@ js::wasm::StartUnwinding(const RegisterState& registers, UnwindState* unwindStat fixedPC = nullptr; // On the error return path, FP might be set to FailFP. Ignore these transient frames. - if (intptr_t(fixedFP) == (FailFP & ~JitActivation::ExitFpWasmBit)) + if (intptr_t(fixedFP) == (FailFP & ~ExitOrJitEntryFPTag)) return false; break; case CodeRange::Throw: @@ -1063,13 +1156,24 @@ ProfilingFrameIterator::ProfilingFrameIterator(const JitActivation& activation, if (unwoundCaller) { callerFP_ = unwindState.fp; callerPC_ = unwindState.pc; + // If the original FP value is tagged, then we're being called through + // a direct JIT call. We can't observe transient tagged values of FP + // (during wasm::SetExitFP) here because StartUnwinding would not have + // unwound for us in this case. + if ((uintptr_t(state.fp) & ExitOrJitEntryFPTag)) + unwoundIonCallerFP_ = (uint8_t*) callerFP_; } else { callerFP_ = unwindState.fp->callerFP; callerPC_ = unwindState.fp->returnAddress; + // See comment above. + if ((uintptr_t(callerFP_) & ExitOrJitEntryFPTag)) + unwoundIonCallerFP_ = (uint8_t*)(uintptr_t(callerFP_) & ~ExitOrJitEntryFPTag); } - if (unwindState.codeRange->isJitEntry()) + if (unwindState.codeRange->isJitEntry()) { + MOZ_ASSERT(!unwoundIonCallerFP_); unwoundIonCallerFP_ = (uint8_t*) callerFP_; + } if (unwindState.codeRange->isInterpEntry()) { unwindState.codeRange = nullptr; @@ -1086,15 +1190,15 @@ void ProfilingFrameIterator::operator++() { if (!exitReason_.isNone()) { - DebugOnly prevExitReason = exitReason_; + DebugOnly wasInterpEntry = exitReason_.isInterpEntry(); exitReason_ = ExitReason::None(); - MOZ_ASSERT(!codeRange_ == prevExitReason.value.isInterpEntry()); - MOZ_ASSERT(done() == prevExitReason.value.isInterpEntry()); + MOZ_ASSERT((!codeRange_) == wasInterpEntry); + MOZ_ASSERT(done() == wasInterpEntry); return; } if (unwoundIonCallerFP_) { - MOZ_ASSERT(codeRange_->isJitEntry()); + MOZ_ASSERT(codeRange_->isFunction() || codeRange_->isJitEntry()); callerPC_ = nullptr; callerFP_ = nullptr; codeRange_ = nullptr; @@ -1120,6 +1224,17 @@ ProfilingFrameIterator::operator++() } code_ = LookupCode(callerPC_, &codeRange_); + + if (!code_ && uintptr_t(callerFP_) & ExitOrJitEntryFPTag) { + // The parent frame is an inlined wasm call, the tagged FP points to + // the fake exit frame. + MOZ_ASSERT(!codeRange_); + AssertDirectJitCall(callerFP_); + unwoundIonCallerFP_ = (uint8_t*) (uintptr_t(callerFP_) & ~uintptr_t(ExitOrJitEntryFPTag)); + MOZ_ASSERT(done()); + return; + } + MOZ_ASSERT(codeRange_); if (codeRange_->isJitEntry()) { diff --git a/js/src/wasm/WasmFrameIter.h b/js/src/wasm/WasmFrameIter.h index 7693261e2914..844204d01415 100644 --- a/js/src/wasm/WasmFrameIter.h +++ b/js/src/wasm/WasmFrameIter.h @@ -19,6 +19,7 @@ #ifndef wasm_frame_iter_h #define wasm_frame_iter_h +#include "jit/JSJitFrameIter.h" #include "js/ProfilingFrameIterator.h" #include "js/TypeDecls.h" #include "wasm/WasmTypes.h" @@ -64,6 +65,7 @@ class WasmFrameIter unsigned lineOrBytecode_; Frame* fp_; uint8_t* unwoundIonCallerFP_; + jit::FrameType unwoundIonFrameType_; Unwind unwind_; void** unwoundAddressOfReturnAddress_; @@ -88,6 +90,7 @@ class WasmFrameIter void** unwoundAddressOfReturnAddress() const; bool debugEnabled() const; DebugFrame* debugFrame() const; + jit::FrameType unwoundIonFrameType() const; uint8_t* unwoundIonCallerFP() const { return unwoundIonCallerFP_; } }; @@ -264,6 +267,19 @@ bool StartUnwinding(const RegisterState& registers, UnwindState* unwindState, bool* unwoundCaller); +// Bit set as the lowest bit of a frame pointer, used in two different mutually +// exclusive situations: +// - either it's a low bit tag in a FramePointer value read from the +// Frame::callerFP of an inner wasm frame. This indicates the previous call +// frame has been set up by a JIT caller that directly called into a wasm +// function's body. This is only stored in Frame::callerFP for a wasm frame +// called from JIT code, and thus it can not appear in a JitActivation's +// exitFP. +// - or it's the low big tag set when exiting wasm code in JitActivation's +// exitFP. + +constexpr uintptr_t ExitOrJitEntryFPTag = 0x1; + } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp index 045fe453f945..d4454c9aff7e 100644 --- a/js/src/wasm/WasmStubs.cpp +++ b/js/src/wasm/WasmStubs.cpp @@ -20,6 +20,7 @@ #include "mozilla/ArrayUtils.h" +#include "jit/RegisterAllocator.h" #include "wasm/WasmCode.h" #include "wasm/WasmGenerator.h" #include "wasm/WasmInstance.h" @@ -501,7 +502,8 @@ GenerateJitEntryThrow(MacroAssembler& masm, unsigned frameSize) MoveSPForJitABI(masm); masm.loadPtr(Address(WasmTlsReg, offsetof(TlsData, cx)), ScratchIonEntry); - masm.enterFakeExitFrameForWasm(ScratchIonEntry, ScratchIonEntry, ExitFrameType::WasmJitEntry); + masm.enterFakeExitFrameForWasm(ScratchIonEntry, ScratchIonEntry, + ExitFrameType::WasmGenericJitEntry); masm.loadPtr(Address(WasmTlsReg, offsetof(TlsData, instance)), ScratchIonEntry); masm.loadPtr(Address(ScratchIonEntry, Instance::offsetOfJSJitExceptionHandler()), @@ -844,6 +846,164 @@ GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex, const FuncExport& return FinishOffsets(masm, offsets); } +void +wasm::GenerateDirectCallFromJit(MacroAssembler& masm, + const FuncExport& fe, + const Instance& inst, + const JitCallStackArgVector& stackArgs, + bool profilingEnabled, + bool wasmGcEnabled, + Register scratch, + uint32_t* callOffset) +{ + MOZ_ASSERT(!IsCompilingWasm()); + + size_t framePushedAtStart = masm.framePushed(); + + if (profilingEnabled) { + // FramePointer isn't volatile, manually preserve it because it will be + // clobbered below. + masm.Push(FramePointer); + } else { +#ifdef DEBUG + // Ensure that the FramePointer is actually Ion-volatile. This might + // assert when bug 1426134 lands. + AllocatableRegisterSet set(RegisterSet::All()); + TakeJitRegisters(/* profiling */ false, &set); + MOZ_ASSERT(set.has(FramePointer), + "replace the whole if branch by the then body when this fails"); +#endif + } + + // Push a special frame descriptor that indicates the frame size so we can + // directly iterate from the current JIT frame without an extra call. + *callOffset = masm.buildFakeExitFrame(scratch); + masm.loadJSContext(scratch); + + masm.moveStackPtrTo(FramePointer); + masm.enterFakeExitFrame(scratch, scratch, ExitFrameType::DirectWasmJitCall); + masm.orPtr(Imm32(ExitOrJitEntryFPTag), FramePointer); + + // Move stack arguments to their final locations. + unsigned bytesNeeded = StackArgBytes(fe.funcType().args()); + bytesNeeded = StackDecrementForCall(WasmStackAlignment, masm.framePushed(), bytesNeeded); + if (bytesNeeded) + masm.reserveStack(bytesNeeded); + + for (ABIArgValTypeIter iter(fe.funcType().args()); !iter.done(); iter++) { + MOZ_ASSERT_IF(iter->kind() == ABIArg::GPR, iter->gpr() != scratch); + MOZ_ASSERT_IF(iter->kind() == ABIArg::GPR, iter->gpr() != FramePointer); + if (iter->kind() != ABIArg::Stack) + continue; + + Address dst(masm.getStackPointer(), iter->offsetFromArgBase()); + + const JitCallStackArg& stackArg = stackArgs[iter.index()]; + switch (stackArg.tag()) { + case JitCallStackArg::Tag::Imm32: + masm.storePtr(ImmWord(stackArg.imm32()), dst); + break; + case JitCallStackArg::Tag::GPR: + MOZ_ASSERT(stackArg.gpr() != scratch); + MOZ_ASSERT(stackArg.gpr() != FramePointer); + masm.storePtr(stackArg.gpr(), dst); + break; + case JitCallStackArg::Tag::FPU: + switch (iter.mirType()) { + case MIRType::Double: + masm.storeDouble(stackArg.fpu(), dst); + break; + case MIRType::Float32: + masm.storeFloat32(stackArg.fpu(), dst); + break; + default: + MOZ_CRASH("unexpected MIR type for a float register in wasm fast call"); + } + break; + case JitCallStackArg::Tag::Address: { + // The address offsets were valid *before* we pushed our frame. + Address src = stackArg.addr(); + src.offset += masm.framePushed() - framePushedAtStart; + switch (iter.mirType()) { + case MIRType::Double: + masm.loadDouble(src, ScratchDoubleReg); + masm.storeDouble(ScratchDoubleReg, dst); + break; + case MIRType::Float32: + masm.loadFloat32(src, ScratchFloat32Reg); + masm.storeFloat32(ScratchFloat32Reg, dst); + break; + case MIRType::Int32: + masm.loadPtr(src, scratch); + masm.storePtr(scratch, dst); + break; + default: + MOZ_CRASH("unexpected MIR type for a stack slot in wasm fast call"); + } + break; + } + case JitCallStackArg::Tag::Undefined: { + MOZ_CRASH("can't happen because of arg.kind() check"); + } + } + } + + // Load tls; from now on, WasmTlsReg is live. + masm.movePtr(ImmPtr(inst.tlsData()), WasmTlsReg); + masm.loadWasmPinnedRegsFromTls(); + +#ifdef ENABLE_WASM_GC + if (wasmGcEnabled) + SuppressGC(masm, 1, ABINonArgReg0); +#endif + + // Actual call. + const wasm::CodeTier& codeTier = inst.code().codeTier(inst.code().bestTier()); + uint32_t offset = codeTier.metadata().codeRanges[fe.funcCodeRangeIndex()].funcNormalEntry(); + void* callee = codeTier.segment().base() + offset; + + masm.assertStackAlignment(WasmStackAlignment); + masm.callJit(ImmPtr(callee)); + masm.assertStackAlignment(WasmStackAlignment); + +#ifdef ENABLE_WASM_GC + if (wasmGcEnabled) + SuppressGC(masm, -1, WasmTlsReg); +#endif + + masm.branchPtr(Assembler::Equal, FramePointer, Imm32(wasm::FailFP), masm.exceptionLabel()); + + // Store the return value in the appropriate place. + switch (fe.funcType().ret().code()) { + case wasm::ExprType::Void: + masm.moveValue(UndefinedValue(), JSReturnOperand); + break; + case wasm::ExprType::I32: + break; + case wasm::ExprType::F32: + masm.canonicalizeFloat(ReturnFloat32Reg); + break; + case wasm::ExprType::F64: + masm.canonicalizeDouble(ReturnDoubleReg); + break; + case wasm::ExprType::Ref: + case wasm::ExprType::AnyRef: + case wasm::ExprType::I64: + MOZ_CRASH("unexpected return type when calling from ion to wasm"); + case wasm::ExprType::Limit: + MOZ_CRASH("Limit"); + } + + // Free args + frame descriptor. + masm.leaveExitFrame(bytesNeeded + ExitFrameLayout::Size()); + + // If we pushed it, free FramePointer. + if (profilingEnabled) + masm.Pop(FramePointer); + + MOZ_ASSERT(framePushedAtStart == masm.framePushed()); +} + static void StackCopy(MacroAssembler& masm, MIRType type, Register scratch, Address src, Address dst) { diff --git a/js/src/wasm/WasmStubs.h b/js/src/wasm/WasmStubs.h index 6dc3d0b20a11..20655577ec82 100644 --- a/js/src/wasm/WasmStubs.h +++ b/js/src/wasm/WasmStubs.h @@ -41,6 +41,87 @@ GenerateEntryStubs(jit::MacroAssembler& masm, size_t funcExportIndex, const FuncExport& funcExport, const Maybe& callee, bool isAsmJS, HasGcTypes gcTypesEnabled, CodeRangeVector* codeRanges); +// An argument that will end up on the stack according to the system ABI, to be +// passed to GenerateDirectCallFromJit. Since the direct JIT call creates its +// own frame, it is its responsibility to put stack arguments to their expected +// locations; so the caller of GenerateDirectCallFromJit can put them anywhere. + +class JitCallStackArg +{ + public: + enum class Tag { + Imm32, + GPR, + FPU, + Address, + Undefined, + }; + + private: + Tag tag_; + union U { + int32_t imm32_; + jit::Register gpr_; + jit::FloatRegister fpu_; + jit::Address addr_; + U() {} + } arg; + + public: + JitCallStackArg() + : tag_(Tag::Undefined) + {} + explicit JitCallStackArg(int32_t imm32) + : tag_(Tag::Imm32) + { + arg.imm32_ = imm32; + } + explicit JitCallStackArg(jit::Register gpr) + : tag_(Tag::GPR) + { + arg.gpr_ = gpr; + } + explicit JitCallStackArg(jit::FloatRegister fpu) + : tag_(Tag::FPU) + { + new (&arg) jit::FloatRegister(fpu); + } + explicit JitCallStackArg(const jit::Address& addr) + : tag_(Tag::Address) + { + new (&arg) jit::Address(addr); + } + + Tag tag() const { return tag_; } + int32_t imm32() const { MOZ_ASSERT(tag_ == Tag::Imm32); return arg.imm32_; } + jit::Register gpr() const { MOZ_ASSERT(tag_ == Tag::GPR); return arg.gpr_; } + jit::FloatRegister fpu() const { MOZ_ASSERT(tag_ == Tag::FPU); return arg.fpu_; } + const jit::Address& addr() const { MOZ_ASSERT(tag_ == Tag::Address); return arg.addr_; } +}; + +using JitCallStackArgVector = Vector; + +// Generates an inline wasm call (during jit compilation) to a specific wasm +// function (as specifed by the given FuncExport). +// This call doesn't go through a wasm entry, but rather creates its own +// inlined exit frame. +// Assumes: +// - all the registers have been preserved by the caller, +// - all arguments passed in registers have been set up at the expected +// locations, +// - all arguments passed on stack slot are alive as defined by a corresponding +// JitCallStackArg. + +extern void +GenerateDirectCallFromJit(jit::MacroAssembler& masm, + const FuncExport& fe, + const Instance& inst, + const JitCallStackArgVector& stackArgs, + bool profilingEnabled, + bool wasmGcEnabled, + jit::Register scratch, + uint32_t* callOffset); + } // namespace wasm } // namespace js From 5596e0df49d51978760a939112d418bb6b1a3cab Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 23 Aug 2018 15:28:16 +0200 Subject: [PATCH 46/53] Bug 1437065: Tests; r=luke --HG-- extra : rebase_source : ad3365a9951f31d1bb7df60fff9bb76eb70ccee5 --- js/src/jit-test/tests/wasm/ion-args.js | 113 +++++++++++++++--- js/src/jit-test/tests/wasm/ion-error-i64.js | 18 ++- js/src/jit-test/tests/wasm/ion-error-ool.js | 3 +- js/src/jit-test/tests/wasm/ion-error-throw.js | 3 +- js/src/jit-test/tests/wasm/ion-lazy-tables.js | 8 +- js/src/jit-test/tests/wasm/profiling.js | 37 +++++- 6 files changed, 156 insertions(+), 26 deletions(-) diff --git a/js/src/jit-test/tests/wasm/ion-args.js b/js/src/jit-test/tests/wasm/ion-args.js index 2bfa17468995..194de924840c 100644 --- a/js/src/jit-test/tests/wasm/ion-args.js +++ b/js/src/jit-test/tests/wasm/ion-args.js @@ -10,10 +10,17 @@ let { exports } = wasmEvalText(`(module (func (export "f64") (result f64) (param f64) get_local 0 ) + + (func (export "mixed_args") (result f64) + (param i32) (param i32) (param i32) (param i32) (param i32) ;; 5 i32 + (param $f64 f64) ;; 1 f64 + (param i32) + get_local $f64 + ) )`); const options = getJitCompilerOptions(); -const jitThreshold = options['ion.warmup.trigger'] * 2; +const jitThreshold = options['ion.warmup.trigger'] * 2 + 2; let coercions = { i32(x) { return x|0; }, @@ -38,23 +45,93 @@ function call(func, coercion, arg) { } } -const inputs = [ - 42, - 3.5, - -0, - -Infinity, - 2**32, - true, - Symbol(), - undefined, - null, - {}, - { valueOf() { return 13.37; } }, - "bonjour" -]; +// Test misc kinds of arguments. +(function() { + const inputs = [ + 42, + 3.5, + -0, + -Infinity, + 2**32, + true, + Symbol(), + undefined, + null, + {}, + { valueOf() { return 13.37; } }, + "bonjour" + ]; -for (let arg of inputs) { - for (let func of ['i32', 'f32', 'f64']) { - call(exports[func], coercions[func], arg); + for (let arg of inputs) { + for (let func of ['i32', 'f32', 'f64']) { + call(exports[func], coercions[func], arg); + } } +})(); + +// Test mixup of float and int arguments. +(function() { + for (let i = 0; i < 10; i++) { + assertEq(exports.mixed_args(i, i+1, i+2, i+3, i+4, i+0.5, i+5), i+0.5); + } +})(); + +// Test high number of arguments. +// All integers. +let {func} = wasmEvalText(`(module + (func (export "func") (result i32) + ${Array(32).join('(param i32)')} + (param $last i32) + get_local $last + ) +)`).exports; + +(function() { + for (let i = 0; i < 10; i++) { + assertEq(func(i, i+1, i+2, i+3, i+4, i+5, i+6, i+7, i+8, i+9, i+10, i+11, i+12, i+13, i+14, i+15, + i+16, i+17, i+18, i+19, i+20, i+21, i+22, i+23, i+24, i+25, i+26, i+27, i+28, i+29, i+30, i+31 + ), i+31); + } +})(); + +// All floats. +func = wasmEvalText(`(module + (func (export "func") (result i32) + ${Array(32).join('(param f64)')} + (param $last i32) + get_local $last + ) +)`).exports.func; + +(function() { + for (let i = 0; i < 10; i++) { + assertEq(func(i, i+1, i+2, i+3, i+4, i+5, i+6, i+7, i+8, i+9, i+10, i+11, i+12, i+13, i+14, i+15, + i+16, i+17, i+18, i+19, i+20, i+21, i+22, i+23, i+24, i+25, i+26, i+27, i+28, i+29, i+30, i+31 + ), i+31); + } +})(); + +// Mix em up! 1 i32, then 1 f32, then 1 f64, and again up to 32 args. +let params = []; +for (let i = 0; i < 32; i++) { + params.push((i % 3 == 0) ? 'i32' : + (i % 3 == 1) ? 'f32' : + 'f64' + ); } + +func = wasmEvalText(`(module + (func (export "func") (result i32) + ${Array(32).join('(param f64)')} + (param $last i32) + get_local $last + ) +)`).exports.func; + +(function() { + for (let i = 0; i < 10; i++) { + assertEq(func(i, i+1, i+2, i+3, i+4, i+5, i+6, i+7, i+8, i+9, i+10, i+11, i+12, i+13, i+14, i+15, + i+16, i+17, i+18, i+19, i+20, i+21, i+22, i+23, i+24, i+25, i+26, i+27, i+28, i+29, i+30, i+31 + ), i+31); + } +})(); diff --git a/js/src/jit-test/tests/wasm/ion-error-i64.js b/js/src/jit-test/tests/wasm/ion-error-i64.js index 867930f70f27..831bf47a9ef7 100644 --- a/js/src/jit-test/tests/wasm/ion-error-i64.js +++ b/js/src/jit-test/tests/wasm/ion-error-i64.js @@ -10,8 +10,6 @@ const TRIGGER = options['ion.warmup.trigger'] + 10; const ITER = 2 * TRIGGER; const EXCEPTION_ITER = ITER - 2; -enableGeckoProfiling(); - var instance = wasmEvalText(`(module (func $add (export "add") (result i32) (param i32) (param i32) get_local 0 @@ -25,8 +23,22 @@ var instance = wasmEvalText(`(module call $add i64.extend_s/i32 ) + + (func $add_two_i64 (export "add_two_i64") (result i64) (param i64) (param i64) + get_local 0 + get_local 1 + i64.add + ) )`).exports; +(function() { + // In ion-eager mode, make sure we don't try to inline a function that + // takes or returns i64 arguments. + assertErrorMessage(() => instance.add_two_i64(0, 1), TypeError, /cannot pass i64 to or from JS/); +})(); + +enableGeckoProfiling(); + var callToMain; function main() { @@ -73,7 +85,9 @@ function main() { assertEq(+lines[0], arrayCallLine); assertEq(+lines[1], callToMain); } else if ((i % 2) == 0) { + // Regular call to wasm add on 32 bits integers. assertEqPreciseStacks(profilingStack, [ + ['', '0', ''], // supa-dupa fast path ['', '>', '0,>', '>', ''], // fast path ['', '!>', '0,!>', '!>', ''], // slow path ]); diff --git a/js/src/jit-test/tests/wasm/ion-error-ool.js b/js/src/jit-test/tests/wasm/ion-error-ool.js index 1316d4f4c116..6b8162f7289b 100644 --- a/js/src/jit-test/tests/wasm/ion-error-ool.js +++ b/js/src/jit-test/tests/wasm/ion-error-ool.js @@ -12,6 +12,7 @@ const EXCEPTION_ITER = TRIGGER + 5; const SLOW_ENTRY_STACK = ['', '!>', '0,!>', '!>', '']; const FAST_ENTRY_STACK = ['', '>', '0,>', '>', '']; +const INLINED_CALL_STACK = ['', '0', '']; const FAST_OOL_ENTRY_STACK = ['', '>', '<,>', 'ool>,>', '<,>', '>', '0,>', '>', '']; const EXCEPTION_ENTRY_STACK = ['', '>', '<,>', 'ool>,>', '<,>', '>', '']; @@ -44,7 +45,7 @@ for (let type of ['i32', 'f32', 'f64']) { for (var i = 0; i < ITER; i++) { startProfiling(); loopBody(i + 1, i + EXCEPTION_ITER + 1); - assertEqPreciseStacks(endProfiling(), [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]); + assertEqPreciseStacks(endProfiling(), [INLINED_CALL_STACK, FAST_ENTRY_STACK, SLOW_ENTRY_STACK]); if (i === EXCEPTION_ITER) { x = { valueOf: function innerValueOf() { throw new Error("ph34r"); }}; diff --git a/js/src/jit-test/tests/wasm/ion-error-throw.js b/js/src/jit-test/tests/wasm/ion-error-throw.js index fe6c6ec553e5..1ff9e16bf945 100644 --- a/js/src/jit-test/tests/wasm/ion-error-throw.js +++ b/js/src/jit-test/tests/wasm/ion-error-throw.js @@ -25,6 +25,7 @@ let { add } = wasmEvalText(`(module const SLOW_ENTRY_STACK = ['', '!>', '0,!>', '!>', '']; const FAST_ENTRY_STACK = ['', '>', '0,>', '>', '']; +const INLINED_CALL_STACK = ['', '0', '']; function main() { for (let i = 0; i < 50; i++) { @@ -37,7 +38,7 @@ function main() { assertStackTrace(e, ['wasm-function[0]', 'main', '']); } let stack = endProfiling(); - assertEqPreciseStacks(stack, [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]); + assertEqPreciseStacks(stack, [INLINED_CALL_STACK, FAST_ENTRY_STACK, SLOW_ENTRY_STACK]); } } diff --git a/js/src/jit-test/tests/wasm/ion-lazy-tables.js b/js/src/jit-test/tests/wasm/ion-lazy-tables.js index c37d7051ac91..c6bbb7d1e7d6 100644 --- a/js/src/jit-test/tests/wasm/ion-lazy-tables.js +++ b/js/src/jit-test/tests/wasm/ion-lazy-tables.js @@ -12,6 +12,8 @@ const EXCEPTION_ITER = TRIGGER + 5; const SLOW_ENTRY_STACK = ['', '!>', '0,!>', '!>', '']; const FAST_ENTRY_STACK = ['', '>', '0,>', '>', '']; +const INLINED_CALL_STACK = ['', '0', '']; +const EXPECTED_STACKS = [SLOW_ENTRY_STACK, FAST_ENTRY_STACK, INLINED_CALL_STACK]; function main() { var { table } = wasmEvalText(`(module @@ -27,7 +29,7 @@ function main() { for (var i = 0; i < ITER; i++) { startProfiling(); assertEq(table.get(0)(i, i+1), i*2+1); - assertEqPreciseStacks(endProfiling(), [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]); + assertEqPreciseStacks(endProfiling(), EXPECTED_STACKS); } } @@ -50,13 +52,13 @@ function withTier2() { i++; startProfiling(); assertEq(table.get(0)(i, i+1), i*2+1); - assertEqPreciseStacks(endProfiling(), [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]); + assertEqPreciseStacks(endProfiling(), EXPECTED_STACKS); } while (!wasmHasTier2CompilationCompleted(module)); for (i = 0; i < ITER; i++) { startProfiling(); assertEq(table.get(0)(i, i+1), i*2+1); - assertEqPreciseStacks(endProfiling(), [FAST_ENTRY_STACK, SLOW_ENTRY_STACK]); + assertEqPreciseStacks(endProfiling(), EXPECTED_STACKS); } setJitCompilerOption('wasm.delay-tier2', 0); diff --git a/js/src/jit-test/tests/wasm/profiling.js b/js/src/jit-test/tests/wasm/profiling.js index c37964600966..dbb54e739218 100644 --- a/js/src/jit-test/tests/wasm/profiling.js +++ b/js/src/jit-test/tests/wasm/profiling.js @@ -5,7 +5,12 @@ const Module = WebAssembly.Module; const Instance = WebAssembly.Instance; const Table = WebAssembly.Table; -const { assertEqImpreciseStacks, startProfiling, endProfiling } = WasmHelpers; +const { + assertEqImpreciseStacks, + assertEqPreciseStacks, + startProfiling, + endProfiling +} = WasmHelpers; function test(code, importObj, expectedStacks) { @@ -380,3 +385,33 @@ for (let type of ['f32', 'f64']) { disableSingleStepProfiling(); disableGeckoProfiling(); })(); + +// Ion->wasm calls. +let func = wasmEvalText(`(module + (func $inner (result i32) (param i32) (param i32) + get_local 0 + get_local 1 + i32.add + ) + (func (export "add") (result i32) (param i32) (param i32) + get_local 0 + get_local 1 + call $inner + ) +)`).exports.add; + +(function() { + enableGeckoProfiling(); + // 10 is enough in ion eager mode. + for (let i = 0; i < 10; i++) { + enableSingleStepProfiling(); + let res = func(i - 1, i + 1); + assertEqPreciseStacks(disableSingleStepProfiling(), [ + ['', '>', '1,>', '0,1,>' , '1,>', '>', ''], // slow entry + ['', '!>', '1,!>', '0,1,!>' , '1,!>', '!>', ''], // fast entry + ['', '1', '0,1' , '1', ''], // inlined jit call + ]); + assertEq(res, i+i); + } + disableGeckoProfiling(); +})(); From 618a191589df64366a0db7497a7b6b7a691aefb7 Mon Sep 17 00:00:00 2001 From: Noemi Erli Date: Fri, 24 Aug 2018 20:56:31 +0300 Subject: [PATCH 47/53] Backed out changeset 001816b621d8 (bug 1485972) for permafailing bug1400716 CLOSED TREE --- testing/web-platform/meta/MANIFEST.json | 6 +++--- .../legend-position-relative.html.ini | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index f560671ff8dc..709ea158aef3 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -559711,7 +559711,7 @@ "support" ], "css/css-values/reference/vh_not_refreshing_on_chrome-ref.html": [ - "279d1c69b9f7fbe60edb55b7e8a5d507eda24936", + "32ce9ada155649e9a4935e1abdddc3d3aadfca73", "support" ], "css/css-values/reference/vh_not_refreshing_on_chrome_iframe-ref.html": [ @@ -559903,7 +559903,7 @@ "support" ], "css/css-values/support/vh_not_refreshing_on_chrome_iframe.html": [ - "8d8e9b49d4aa3d9804a176852288e32ccaaa47d8", + "c58ec57a58f29d4774b199ca82dd6d16607d4735", "support" ], "css/css-values/unset-value-storage.html": [ @@ -559971,7 +559971,7 @@ "support" ], "css/css-values/vh_not_refreshing_on_chrome.html": [ - "52a45a114c85bf96a175ca583d8a2b6f54b9ab6c", + "b4e0a413ab975dea429ca35642dc075a54127117", "reftest" ], "css/css-values/viewport-relative-lengths-scaled-viewport.html": [ diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini deleted file mode 100644 index 382d4b71fe7c..000000000000 --- a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[legend-position-relative.html] - if webrender: FAIL From 3e4bb11f4444694e346a67aca4d5116a46210f56 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Fri, 24 Aug 2018 19:38:08 +0300 Subject: [PATCH 48/53] Bug 1485972 - Set legend-position-relative.html as failing on webrender. r=me NPOTB --HG-- extra : source : 001816b621d8bd754aa3e6f602f721f1add6d48a extra : amend_source : a0b20782d80c684ef4d938a8f3a6af237b82c087 --- testing/web-platform/meta/MANIFEST.json | 6 +++--- .../legend-position-relative.html.ini | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 709ea158aef3..f560671ff8dc 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -559711,7 +559711,7 @@ "support" ], "css/css-values/reference/vh_not_refreshing_on_chrome-ref.html": [ - "32ce9ada155649e9a4935e1abdddc3d3aadfca73", + "279d1c69b9f7fbe60edb55b7e8a5d507eda24936", "support" ], "css/css-values/reference/vh_not_refreshing_on_chrome_iframe-ref.html": [ @@ -559903,7 +559903,7 @@ "support" ], "css/css-values/support/vh_not_refreshing_on_chrome_iframe.html": [ - "c58ec57a58f29d4774b199ca82dd6d16607d4735", + "8d8e9b49d4aa3d9804a176852288e32ccaaa47d8", "support" ], "css/css-values/unset-value-storage.html": [ @@ -559971,7 +559971,7 @@ "support" ], "css/css-values/vh_not_refreshing_on_chrome.html": [ - "b4e0a413ab975dea429ca35642dc075a54127117", + "52a45a114c85bf96a175ca583d8a2b6f54b9ab6c", "reftest" ], "css/css-values/viewport-relative-lengths-scaled-viewport.html": [ diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini new file mode 100644 index 000000000000..1b8ab9c5dc98 --- /dev/null +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html.ini @@ -0,0 +1,3 @@ +[legend-position-relative.html] + expected: + if webrender: FAIL From 2e062cc88cd0fd646c50a0000da3283ad624bbc9 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Thu, 16 Aug 2018 11:19:59 -0700 Subject: [PATCH 49/53] Bug 1246764 - Part 1: Move SVGPathData and its parser into svg_path.rs. r=emilio SVGPathData will be used by clip-path and offset-path (and/or more on the properties which support ). Therefore, let's move SVGPathData out of motion.rs. Differential Revision: https://phabricator.services.mozilla.com/D3631 --- servo/components/style/gecko/conversions.rs | 2 +- .../components/style/values/specified/mod.rs | 2 + .../style/values/specified/motion.rs | 501 +---------------- .../style/values/specified/svg_path.rs | 517 ++++++++++++++++++ 4 files changed, 522 insertions(+), 500 deletions(-) create mode 100644 servo/components/style/values/specified/svg_path.rs diff --git a/servo/components/style/gecko/conversions.rs b/servo/components/style/gecko/conversions.rs index 4dafe457371c..a3109a1f2264 100644 --- a/servo/components/style/gecko/conversions.rs +++ b/servo/components/style/gecko/conversions.rs @@ -743,7 +743,7 @@ pub mod basic_shape { impl<'a> From<&'a StyleShapeSource> for OffsetPath { fn from(other: &'a StyleShapeSource) -> Self { use gecko_bindings::structs::StylePathCommand; - use values::specified::motion::{SVGPathData, PathCommand}; + use values::specified::svg_path::{SVGPathData, PathCommand}; match other.mType { StyleShapeSourceType::Path => { let gecko_path = unsafe { &*other.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 1bd52e916d07..31e37b3041d7 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -68,6 +68,7 @@ pub use self::position::{PositionComponent, ZIndex}; pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg::MozContextProperties; +pub use self::svg_path::SVGPathData; pub use self::table::XSpan; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, MozTabSize, TextAlign}; pub use self::text::{TextEmphasisPosition, TextEmphasisStyle}; @@ -109,6 +110,7 @@ pub mod rect; pub mod resolution; pub mod source_size_list; pub mod svg; +pub mod svg_path; pub mod table; pub mod text; pub mod time; diff --git a/servo/components/style/values/specified/motion.rs b/servo/components/style/values/specified/motion.rs index ffaaf7cff17b..87ff39d9a3ae 100644 --- a/servo/components/style/values/specified/motion.rs +++ b/servo/components/style/values/specified/motion.rs @@ -6,12 +6,8 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; -use std::fmt::{self, Write}; -use std::iter::Peekable; -use std::str::Chars; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; -use style_traits::values::SequenceWriter; -use values::CSSFloat; +use style_traits::{ParseError, StyleParseErrorKind}; +use values::specified::SVGPathData; /// The offset-path value. /// @@ -63,496 +59,3 @@ impl Parse for OffsetPath { }) } } - -/// SVG Path parser. -struct PathParser<'a> { - chars: Peekable>, - path: Vec, -} - -macro_rules! parse_arguments { - ( - $parser:ident, - $abs:ident, - $enum:ident, - [ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ] - ) => { - { - loop { - let $para = $func(&mut $parser.chars)?; - $( - skip_comma_wsp(&mut $parser.chars); - let $other_para = $other_func(&mut $parser.chars)?; - )* - $parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut $parser.chars) || - $parser.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut $parser.chars); - } - Ok(()) - } - } -} - -impl<'a> PathParser<'a> { - /// Parse a sub-path. - fn parse_subpath(&mut self) -> Result<(), ()> { - // Handle "moveto" Command first. If there is no "moveto", this is not a valid sub-path - // (i.e. not a valid moveto-drawto-command-group). - self.parse_moveto()?; - - // Handle other commands. - loop { - skip_wsp(&mut self.chars); - if self.chars.peek().map_or(true, |m| *m == 'M' || *m == 'm') { - break; - } - - match self.chars.next() { - Some(command) => { - let abs = command.is_uppercase(); - macro_rules! parse_command { - ( $($($p:pat)|+ => $parse_func:ident,)* ) => { - match command { - $( - $($p)|+ => { - skip_wsp(&mut self.chars); - self.$parse_func(abs)?; - }, - )* - _ => return Err(()), - } - } - } - parse_command!( - 'Z' | 'z' => parse_closepath, - 'L' | 'l' => parse_lineto, - 'H' | 'h' => parse_h_lineto, - 'V' | 'v' => parse_v_lineto, - 'C' | 'c' => parse_curveto, - 'S' | 's' => parse_smooth_curveto, - 'Q' | 'q' => parse_quadratic_bezier_curveto, - 'T' | 't' => parse_smooth_quadratic_bezier_curveto, - 'A' | 'a' => parse_elliprical_arc, - ); - }, - _ => break, // no more commands. - } - } - Ok(()) - } - - /// Parse "moveto" command. - fn parse_moveto(&mut self) -> Result<(), ()> { - let command = match self.chars.next() { - Some(c) if c == 'M' || c == 'm' => c, - _ => return Err(()), - }; - - skip_wsp(&mut self.chars); - let point = parse_coord(&mut self.chars)?; - let absolute = command == 'M'; - self.path.push(PathCommand::MoveTo { point, absolute } ); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - return Ok(()); - } - skip_comma_wsp(&mut self.chars); - - // If a moveto is followed by multiple pairs of coordinates, the subsequent - // pairs are treated as implicit lineto commands. - self.parse_lineto(absolute) - } - - /// Parse "closepath" command. - fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> { - self.path.push(PathCommand::ClosePath); - Ok(()) - } - - /// Parse "lineto" command. - fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, LineTo, [ point => parse_coord ]) - } - - /// Parse horizontal "lineto" command. - fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ]) - } - - /// Parse vertical "lineto" command. - fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ]) - } - - /// Parse cubic Bézier curve command. - fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, CurveTo, [ - control1 => parse_coord, control2 => parse_coord, point => parse_coord - ]) - } - - /// Parse smooth "curveto" command. - fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, SmoothCurveTo, [ - control2 => parse_coord, point => parse_coord - ]) - } - - /// Parse quadratic Bézier curve command. - fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, QuadBezierCurveTo, [ - control1 => parse_coord, point => parse_coord - ]) - } - - /// Parse smooth quadratic Bézier curveto command. - fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ]) - } - - /// Parse elliptical arc curve command. - fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> { - // Parse a flag whose value is '0' or '1'; otherwise, return Err(()). - let parse_flag = |iter: &mut Peekable| -> Result { - let value = match iter.peek() { - Some(c) if *c == '0' || *c == '1' => *c == '1', - _ => return Err(()), - }; - iter.next(); - Ok(value) - }; - parse_arguments!(self, absolute, EllipticalArc, [ - rx => parse_number, - ry => parse_number, - angle => parse_number, - large_arc_flag => parse_flag, - sweep_flag => parse_flag, - point => parse_coord - ]) - } -} - -/// The SVG path data. -/// -/// https://www.w3.org/TR/SVG11/paths.html#PathData -#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] -pub struct SVGPathData(Box<[PathCommand]>); - -impl SVGPathData { - /// Return SVGPathData by a slice of PathCommand. - #[inline] - pub fn new(cmd: Box<[PathCommand]>) -> Self { - debug_assert!(!cmd.is_empty()); - SVGPathData(cmd) - } - - /// Get the array of PathCommand. - #[inline] - pub fn commands(&self) -> &[PathCommand] { - debug_assert!(!self.0.is_empty()); - &self.0 - } -} - -impl ToCss for SVGPathData { - #[inline] - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write - { - dest.write_char('"')?; - { - let mut writer = SequenceWriter::new(dest, " "); - for command in self.0.iter() { - writer.item(command)?; - } - } - dest.write_char('"') - } -} - -impl Parse for SVGPathData { - // We cannot use cssparser::Parser to parse a SVG path string because the spec wants to make - // the SVG path string as compact as possible. (i.e. The whitespaces may be dropped.) - // e.g. "M100 200L100 200" is a valid SVG path string. If we use tokenizer, the first ident - // is "M100", instead of "M", and this is not correct. Therefore, we use a Peekable - // str::Char iterator to check each character. - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't> - ) -> Result> { - let location = input.current_source_location(); - let path_string = input.expect_string()?.as_ref(); - if path_string.is_empty() { - // Treat an empty string as invalid, so we will not set it. - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - - // Parse the svg path string as multiple sub-paths. - let mut path_parser = PathParser { - chars: path_string.chars().peekable(), - path: Vec::new(), - }; - while skip_wsp(&mut path_parser.chars) { - if path_parser.parse_subpath().is_err() { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - } - - Ok(SVGPathData::new(path_parser.path.into_boxed_slice())) - } -} - - -/// The SVG path command. -/// The fields of these commands are self-explanatory, so we skip the documents. -/// Note: the index of the control points, e.g. control1, control2, are mapping to the control -/// points of the Bézier curve in the spec. -/// -/// https://www.w3.org/TR/SVG11/paths.html#PathData -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)] -#[allow(missing_docs)] -#[repr(C, u8)] -pub enum PathCommand { - /// The unknown type. - /// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN - Unknown, - /// The "moveto" command. - MoveTo { point: CoordPair, absolute: bool }, - /// The "lineto" command. - LineTo { point: CoordPair, absolute: bool }, - /// The horizontal "lineto" command. - HorizontalLineTo { x: CSSFloat, absolute: bool }, - /// The vertical "lineto" command. - VerticalLineTo { y: CSSFloat, absolute: bool }, - /// The cubic Bézier curve command. - CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: bool }, - /// The smooth curve command. - SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: bool }, - /// The quadratic Bézier curve command. - QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: bool }, - /// The smooth quadratic Bézier curve command. - SmoothQuadBezierCurveTo { point: CoordPair, absolute: bool }, - /// The elliptical arc curve command. - EllipticalArc { - rx: CSSFloat, - ry: CSSFloat, - angle: CSSFloat, - large_arc_flag: bool, - sweep_flag: bool, - point: CoordPair, - absolute: bool - }, - /// The "closepath" command. - ClosePath, -} - -impl ToCss for PathCommand { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write - { - use self::PathCommand::*; - match *self { - Unknown => dest.write_str("X"), - ClosePath => dest.write_str("Z"), - MoveTo { point, absolute } => { - dest.write_char(if absolute { 'M' } else { 'm' })?; - dest.write_char(' ')?; - point.to_css(dest) - } - LineTo { point, absolute } => { - dest.write_char(if absolute { 'L' } else { 'l' })?; - dest.write_char(' ')?; - point.to_css(dest) - } - CurveTo { control1, control2, point, absolute } => { - dest.write_char(if absolute { 'C' } else { 'c' })?; - dest.write_char(' ')?; - control1.to_css(dest)?; - dest.write_char(' ')?; - control2.to_css(dest)?; - dest.write_char(' ')?; - point.to_css(dest) - }, - QuadBezierCurveTo { control1, point, absolute } => { - dest.write_char(if absolute { 'Q' } else { 'q' })?; - dest.write_char(' ')?; - control1.to_css(dest)?; - dest.write_char(' ')?; - point.to_css(dest) - }, - EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, point, absolute } => { - dest.write_char(if absolute { 'A' } else { 'a' })?; - dest.write_char(' ')?; - rx.to_css(dest)?; - dest.write_char(' ')?; - ry.to_css(dest)?; - dest.write_char(' ')?; - angle.to_css(dest)?; - dest.write_char(' ')?; - (large_arc_flag as i32).to_css(dest)?; - dest.write_char(' ')?; - (sweep_flag as i32).to_css(dest)?; - dest.write_char(' ')?; - point.to_css(dest) - }, - HorizontalLineTo { x, absolute } => { - dest.write_char(if absolute { 'H' } else { 'h' })?; - dest.write_char(' ')?; - x.to_css(dest) - }, - VerticalLineTo { y, absolute } => { - dest.write_char(if absolute { 'V' } else { 'v' })?; - dest.write_char(' ')?; - y.to_css(dest) - }, - SmoothCurveTo { control2, point, absolute } => { - dest.write_char(if absolute { 'S' } else { 's' })?; - dest.write_char(' ')?; - control2.to_css(dest)?; - dest.write_char(' ')?; - point.to_css(dest) - }, - SmoothQuadBezierCurveTo { point, absolute } => { - dest.write_char(if absolute { 'T' } else { 't' })?; - dest.write_char(' ')?; - point.to_css(dest) - }, - } - } -} - -/// The path coord type. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] -#[repr(C)] -pub struct CoordPair(CSSFloat, CSSFloat); - -impl CoordPair { - /// Create a CoordPair. - #[inline] - pub fn new(x: CSSFloat, y: CSSFloat) -> Self { - CoordPair(x, y) - } -} - -/// Parse a pair of numbers into CoordPair. -fn parse_coord(iter: &mut Peekable) -> Result { - let x = parse_number(iter)?; - skip_comma_wsp(iter); - let y = parse_number(iter)?; - Ok(CoordPair::new(x, y)) -} - -/// This is a special version which parses the number for SVG Path. e.g. "M 0.6.5" should be parsed -/// as MoveTo with a coordinate of ("0.6", ".5"), instead of treating 0.6.5 as a non-valid floating -/// point number. In other words, the logic here is similar with that of -/// tokenizer::consume_numeric, which also consumes the number as many as possible, but here the -/// input is a Peekable and we only accept an integer of a floating point number. -/// -/// The "number" syntax in https://www.w3.org/TR/SVG/paths.html#PathDataBNF -fn parse_number(iter: &mut Peekable) -> Result { - // 1. Check optional sign. - let sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { - if iter.next().unwrap() == '-' { -1. } else { 1. } - } else { - 1. - }; - - // 2. Check integer part. - let mut integral_part: f64 = 0.; - let got_dot = if !iter.peek().map_or(false, |&n: &char| n == '.') { - // If the first digit in integer part is neither a dot nor a digit, this is not a number. - if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { - return Err(()); - } - - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { - integral_part = - integral_part * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; - } - - iter.peek().map_or(false, |&n: &char| n == '.') - } else { - true - }; - - // 3. Check fractional part. - let mut fractional_part: f64 = 0.; - if got_dot { - // Consume '.'. - iter.next(); - // If the first digit in fractional part is not a digit, this is not a number. - if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { - return Err(()); - } - - let mut factor = 0.1; - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { - fractional_part += iter.next().unwrap().to_digit(10).unwrap() as f64 * factor; - factor *= 0.1; - } - } - - let mut value = sign * (integral_part + fractional_part); - - // 4. Check exp part. The segment name of SVG Path doesn't include 'E' or 'e', so it's ok to - // treat the numbers after 'E' or 'e' are in the exponential part. - if iter.peek().map_or(false, |&exp: &char| exp == 'E' || exp == 'e') { - // Consume 'E' or 'e'. - iter.next(); - let exp_sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { - if iter.next().unwrap() == '-' { -1. } else { 1. } - } else { - 1. - }; - - let mut exp: f64 = 0.; - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { - exp = exp * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; - } - - value *= f64::powf(10., exp * exp_sign); - } - - if value.is_finite() { - Ok(value.min(::std::f32::MAX as f64).max(::std::f32::MIN as f64) as CSSFloat) - } else { - Err(()) - } -} - -/// Skip all svg whitespaces, and return true if |iter| hasn't finished. -#[inline] -fn skip_wsp(iter: &mut Peekable) -> bool { - // Note: SVG 1.1 defines the whitespaces as \u{9}, \u{20}, \u{A}, \u{D}. - // However, SVG 2 has one extra whitespace: \u{C}. - // Therefore, we follow the newest spec for the definition of whitespace, - // i.e. \u{9}, \u{20}, \u{A}, \u{C}, \u{D}, by is_ascii_whitespace(). - while iter.peek().map_or(false, |c: &char| c.is_ascii_whitespace()) { - iter.next(); - } - iter.peek().is_some() -} - -/// Skip all svg whitespaces and one comma, and return true if |iter| hasn't finished. -#[inline] -fn skip_comma_wsp(iter: &mut Peekable) -> bool { - if !skip_wsp(iter) { - return false; - } - - if *iter.peek().unwrap() != ',' { - return true; - } - iter.next(); - - skip_wsp(iter) -} diff --git a/servo/components/style/values/specified/svg_path.rs b/servo/components/style/values/specified/svg_path.rs new file mode 100644 index 000000000000..8d92e94aee0c --- /dev/null +++ b/servo/components/style/values/specified/svg_path.rs @@ -0,0 +1,517 @@ +/* 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/. */ + +//! Specified types for SVG Path. + +use cssparser::Parser; +use parser::{Parse, ParserContext}; +use std::fmt::{self, Write}; +use std::iter::Peekable; +use std::str::Chars; +use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; +use style_traits::values::SequenceWriter; +use values::CSSFloat; + + +/// The SVG path data. +/// +/// https://www.w3.org/TR/SVG11/paths.html#PathData +#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] +pub struct SVGPathData(Box<[PathCommand]>); + +impl SVGPathData { + /// Return SVGPathData by a slice of PathCommand. + #[inline] + pub fn new(cmd: Box<[PathCommand]>) -> Self { + debug_assert!(!cmd.is_empty()); + SVGPathData(cmd) + } + + /// Get the array of PathCommand. + #[inline] + pub fn commands(&self) -> &[PathCommand] { + debug_assert!(!self.0.is_empty()); + &self.0 + } +} + +impl ToCss for SVGPathData { + #[inline] + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write + { + dest.write_char('"')?; + { + let mut writer = SequenceWriter::new(dest, " "); + for command in self.0.iter() { + writer.item(command)?; + } + } + dest.write_char('"') + } +} + +impl Parse for SVGPathData { + // We cannot use cssparser::Parser to parse a SVG path string because the spec wants to make + // the SVG path string as compact as possible. (i.e. The whitespaces may be dropped.) + // e.g. "M100 200L100 200" is a valid SVG path string. If we use tokenizer, the first ident + // is "M100", instead of "M", and this is not correct. Therefore, we use a Peekable + // str::Char iterator to check each character. + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result> { + let location = input.current_source_location(); + let path_string = input.expect_string()?.as_ref(); + if path_string.is_empty() { + // Treat an empty string as invalid, so we will not set it. + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + // Parse the svg path string as multiple sub-paths. + let mut path_parser = PathParser::new(path_string); + while skip_wsp(&mut path_parser.chars) { + if path_parser.parse_subpath().is_err() { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + } + + Ok(SVGPathData::new(path_parser.path.into_boxed_slice())) + } +} + + +/// The SVG path command. +/// The fields of these commands are self-explanatory, so we skip the documents. +/// Note: the index of the control points, e.g. control1, control2, are mapping to the control +/// points of the Bézier curve in the spec. +/// +/// https://www.w3.org/TR/SVG11/paths.html#PathData +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)] +#[allow(missing_docs)] +#[repr(C, u8)] +pub enum PathCommand { + /// The unknown type. + /// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN + Unknown, + /// The "moveto" command. + MoveTo { point: CoordPair, absolute: bool }, + /// The "lineto" command. + LineTo { point: CoordPair, absolute: bool }, + /// The horizontal "lineto" command. + HorizontalLineTo { x: CSSFloat, absolute: bool }, + /// The vertical "lineto" command. + VerticalLineTo { y: CSSFloat, absolute: bool }, + /// The cubic Bézier curve command. + CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: bool }, + /// The smooth curve command. + SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: bool }, + /// The quadratic Bézier curve command. + QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: bool }, + /// The smooth quadratic Bézier curve command. + SmoothQuadBezierCurveTo { point: CoordPair, absolute: bool }, + /// The elliptical arc curve command. + EllipticalArc { + rx: CSSFloat, + ry: CSSFloat, + angle: CSSFloat, + large_arc_flag: bool, + sweep_flag: bool, + point: CoordPair, + absolute: bool + }, + /// The "closepath" command. + ClosePath, +} + +impl ToCss for PathCommand { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write + { + use self::PathCommand::*; + match *self { + Unknown => dest.write_str("X"), + ClosePath => dest.write_str("Z"), + MoveTo { point, absolute } => { + dest.write_char(if absolute { 'M' } else { 'm' })?; + dest.write_char(' ')?; + point.to_css(dest) + } + LineTo { point, absolute } => { + dest.write_char(if absolute { 'L' } else { 'l' })?; + dest.write_char(' ')?; + point.to_css(dest) + } + CurveTo { control1, control2, point, absolute } => { + dest.write_char(if absolute { 'C' } else { 'c' })?; + dest.write_char(' ')?; + control1.to_css(dest)?; + dest.write_char(' ')?; + control2.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + QuadBezierCurveTo { control1, point, absolute } => { + dest.write_char(if absolute { 'Q' } else { 'q' })?; + dest.write_char(' ')?; + control1.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, point, absolute } => { + dest.write_char(if absolute { 'A' } else { 'a' })?; + dest.write_char(' ')?; + rx.to_css(dest)?; + dest.write_char(' ')?; + ry.to_css(dest)?; + dest.write_char(' ')?; + angle.to_css(dest)?; + dest.write_char(' ')?; + (large_arc_flag as i32).to_css(dest)?; + dest.write_char(' ')?; + (sweep_flag as i32).to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + HorizontalLineTo { x, absolute } => { + dest.write_char(if absolute { 'H' } else { 'h' })?; + dest.write_char(' ')?; + x.to_css(dest) + }, + VerticalLineTo { y, absolute } => { + dest.write_char(if absolute { 'V' } else { 'v' })?; + dest.write_char(' ')?; + y.to_css(dest) + }, + SmoothCurveTo { control2, point, absolute } => { + dest.write_char(if absolute { 'S' } else { 's' })?; + dest.write_char(' ')?; + control2.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + SmoothQuadBezierCurveTo { point, absolute } => { + dest.write_char(if absolute { 'T' } else { 't' })?; + dest.write_char(' ')?; + point.to_css(dest) + }, + } + } +} + + +/// The path coord type. +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] +#[repr(C)] +pub struct CoordPair(CSSFloat, CSSFloat); + +impl CoordPair { + /// Create a CoordPair. + #[inline] + pub fn new(x: CSSFloat, y: CSSFloat) -> Self { + CoordPair(x, y) + } +} + + +/// SVG Path parser. +struct PathParser<'a> { + chars: Peekable>, + path: Vec, +} + +macro_rules! parse_arguments { + ( + $parser:ident, + $abs:ident, + $enum:ident, + [ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ] + ) => { + { + loop { + let $para = $func(&mut $parser.chars)?; + $( + skip_comma_wsp(&mut $parser.chars); + let $other_para = $other_func(&mut $parser.chars)?; + )* + $parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut $parser.chars) || + $parser.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut $parser.chars); + } + Ok(()) + } + } +} + +impl<'a> PathParser<'a> { + /// Return a PathParser. + #[inline] + fn new(string: &'a str) -> Self { + PathParser { + chars: string.chars().peekable(), + path: Vec::new(), + } + } + + /// Parse a sub-path. + fn parse_subpath(&mut self) -> Result<(), ()> { + // Handle "moveto" Command first. If there is no "moveto", this is not a valid sub-path + // (i.e. not a valid moveto-drawto-command-group). + self.parse_moveto()?; + + // Handle other commands. + loop { + skip_wsp(&mut self.chars); + if self.chars.peek().map_or(true, |m| *m == 'M' || *m == 'm') { + break; + } + + match self.chars.next() { + Some(command) => { + let abs = command.is_uppercase(); + macro_rules! parse_command { + ( $($($p:pat)|+ => $parse_func:ident,)* ) => { + match command { + $( + $($p)|+ => { + skip_wsp(&mut self.chars); + self.$parse_func(abs)?; + }, + )* + _ => return Err(()), + } + } + } + parse_command!( + 'Z' | 'z' => parse_closepath, + 'L' | 'l' => parse_lineto, + 'H' | 'h' => parse_h_lineto, + 'V' | 'v' => parse_v_lineto, + 'C' | 'c' => parse_curveto, + 'S' | 's' => parse_smooth_curveto, + 'Q' | 'q' => parse_quadratic_bezier_curveto, + 'T' | 't' => parse_smooth_quadratic_bezier_curveto, + 'A' | 'a' => parse_elliprical_arc, + ); + }, + _ => break, // no more commands. + } + } + Ok(()) + } + + /// Parse "moveto" command. + fn parse_moveto(&mut self) -> Result<(), ()> { + let command = match self.chars.next() { + Some(c) if c == 'M' || c == 'm' => c, + _ => return Err(()), + }; + + skip_wsp(&mut self.chars); + let point = parse_coord(&mut self.chars)?; + let absolute = command == 'M'; + self.path.push(PathCommand::MoveTo { point, absolute } ); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + return Ok(()); + } + skip_comma_wsp(&mut self.chars); + + // If a moveto is followed by multiple pairs of coordinates, the subsequent + // pairs are treated as implicit lineto commands. + self.parse_lineto(absolute) + } + + /// Parse "closepath" command. + fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> { + self.path.push(PathCommand::ClosePath); + Ok(()) + } + + /// Parse "lineto" command. + fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, LineTo, [ point => parse_coord ]) + } + + /// Parse horizontal "lineto" command. + fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ]) + } + + /// Parse vertical "lineto" command. + fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ]) + } + + /// Parse cubic Bézier curve command. + fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, CurveTo, [ + control1 => parse_coord, control2 => parse_coord, point => parse_coord + ]) + } + + /// Parse smooth "curveto" command. + fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, SmoothCurveTo, [ + control2 => parse_coord, point => parse_coord + ]) + } + + /// Parse quadratic Bézier curve command. + fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, QuadBezierCurveTo, [ + control1 => parse_coord, point => parse_coord + ]) + } + + /// Parse smooth quadratic Bézier curveto command. + fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ]) + } + + /// Parse elliptical arc curve command. + fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> { + // Parse a flag whose value is '0' or '1'; otherwise, return Err(()). + let parse_flag = |iter: &mut Peekable| -> Result { + let value = match iter.peek() { + Some(c) if *c == '0' || *c == '1' => *c == '1', + _ => return Err(()), + }; + iter.next(); + Ok(value) + }; + parse_arguments!(self, absolute, EllipticalArc, [ + rx => parse_number, + ry => parse_number, + angle => parse_number, + large_arc_flag => parse_flag, + sweep_flag => parse_flag, + point => parse_coord + ]) + } +} + + +/// Parse a pair of numbers into CoordPair. +fn parse_coord(iter: &mut Peekable) -> Result { + let x = parse_number(iter)?; + skip_comma_wsp(iter); + let y = parse_number(iter)?; + Ok(CoordPair::new(x, y)) +} + +/// This is a special version which parses the number for SVG Path. e.g. "M 0.6.5" should be parsed +/// as MoveTo with a coordinate of ("0.6", ".5"), instead of treating 0.6.5 as a non-valid floating +/// point number. In other words, the logic here is similar with that of +/// tokenizer::consume_numeric, which also consumes the number as many as possible, but here the +/// input is a Peekable and we only accept an integer of a floating point number. +/// +/// The "number" syntax in https://www.w3.org/TR/SVG/paths.html#PathDataBNF +fn parse_number(iter: &mut Peekable) -> Result { + // 1. Check optional sign. + let sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { + if iter.next().unwrap() == '-' { -1. } else { 1. } + } else { + 1. + }; + + // 2. Check integer part. + let mut integral_part: f64 = 0.; + let got_dot = if !iter.peek().map_or(false, |&n: &char| n == '.') { + // If the first digit in integer part is neither a dot nor a digit, this is not a number. + if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { + return Err(()); + } + + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + integral_part = + integral_part * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; + } + + iter.peek().map_or(false, |&n: &char| n == '.') + } else { + true + }; + + // 3. Check fractional part. + let mut fractional_part: f64 = 0.; + if got_dot { + // Consume '.'. + iter.next(); + // If the first digit in fractional part is not a digit, this is not a number. + if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { + return Err(()); + } + + let mut factor = 0.1; + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + fractional_part += iter.next().unwrap().to_digit(10).unwrap() as f64 * factor; + factor *= 0.1; + } + } + + let mut value = sign * (integral_part + fractional_part); + + // 4. Check exp part. The segment name of SVG Path doesn't include 'E' or 'e', so it's ok to + // treat the numbers after 'E' or 'e' are in the exponential part. + if iter.peek().map_or(false, |&exp: &char| exp == 'E' || exp == 'e') { + // Consume 'E' or 'e'. + iter.next(); + let exp_sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { + if iter.next().unwrap() == '-' { -1. } else { 1. } + } else { + 1. + }; + + let mut exp: f64 = 0.; + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + exp = exp * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; + } + + value *= f64::powf(10., exp * exp_sign); + } + + if value.is_finite() { + Ok(value.min(::std::f32::MAX as f64).max(::std::f32::MIN as f64) as CSSFloat) + } else { + Err(()) + } +} + +/// Skip all svg whitespaces, and return true if |iter| hasn't finished. +#[inline] +fn skip_wsp(iter: &mut Peekable) -> bool { + // Note: SVG 1.1 defines the whitespaces as \u{9}, \u{20}, \u{A}, \u{D}. + // However, SVG 2 has one extra whitespace: \u{C}. + // Therefore, we follow the newest spec for the definition of whitespace, + // i.e. \u{9}, \u{20}, \u{A}, \u{C}, \u{D}, by is_ascii_whitespace(). + while iter.peek().map_or(false, |c: &char| c.is_ascii_whitespace()) { + iter.next(); + } + iter.peek().is_some() +} + +/// Skip all svg whitespaces and one comma, and return true if |iter| hasn't finished. +#[inline] +fn skip_comma_wsp(iter: &mut Peekable) -> bool { + if !skip_wsp(iter) { + return false; + } + + if *iter.peek().unwrap() != ',' { + return true; + } + iter.next(); + + skip_wsp(iter) +} From a1909a88ffe34b3d552ef5d59fb4954c26529085 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Tue, 14 Aug 2018 18:58:18 -0700 Subject: [PATCH 50/53] Bug 1246764 - Part 2: Define path() for clip-path. r=emilio For now, |clip-path: path()| is chrome-only, and not for shape-outside, so we only implement the parser for clip-path. Besides, I didn't put path() in BasicShape because path() doesn't use the reference box to resolve the percentage or keywords (i.e. SVG path only accept floating point or integer number as the css pixel value). Therefore, I add it into ShapeSource, instead of BasicShape. Differential Revision: https://phabricator.services.mozilla.com/D3633 --- .../shared/css/generated/properties-db.js | 2 + layout/inspector/tests/test_bug877690.html | 2 +- layout/style/nsStyleStruct.h | 8 ++- layout/style/test/property_database.js | 13 ++++ servo/components/style/gecko/conversions.rs | 42 ++++++++---- .../components/style/properties/gecko.mako.rs | 49 +++++++++----- .../style/values/generics/basic_shape.rs | 17 +++++ .../style/values/specified/basic_shape.rs | 64 ++++++++++++++++++- 8 files changed, 166 insertions(+), 31 deletions(-) diff --git a/devtools/shared/css/generated/properties-db.js b/devtools/shared/css/generated/properties-db.js index 50499051695b..d4d08f10fe3c 100644 --- a/devtools/shared/css/generated/properties-db.js +++ b/devtools/shared/css/generated/properties-db.js @@ -4884,6 +4884,7 @@ exports.CSS_PROPERTIES = { "margin-box", "none", "padding-box", + "path", "polygon", "stroke-box", "unset", @@ -8269,6 +8270,7 @@ exports.CSS_PROPERTIES = { "margin-box", "none", "padding-box", + "path", "polygon", "radial-gradient", "repeating-linear-gradient", diff --git a/layout/inspector/tests/test_bug877690.html b/layout/inspector/tests/test_bug877690.html index 52da3d8aaa33..8fd8399657b1 100644 --- a/layout/inspector/tests/test_bug877690.html +++ b/layout/inspector/tests/test_bug877690.html @@ -190,7 +190,7 @@ function do_test() { // Regression test for bug 1255379. var expected = [ "inherit", "initial", "unset", "none", "url", - "polygon", "circle", "ellipse", "inset", + "polygon", "circle", "ellipse", "inset", "path", "fill-box", "stroke-box", "view-box", "margin-box", "border-box", "padding-box", "content-box" ]; var values = InspectorUtils.getCSSValuesForProperty("clip-path"); diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index d1c7b8142cce..662de8297f39 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1978,9 +1978,14 @@ struct StyleSVGPath final return mPath; } + StyleFillRule FillRule() const + { + return mFillRule; + } + bool operator==(const StyleSVGPath& aOther) const { - return mPath == aOther.mPath; + return mPath == aOther.mPath && mFillRule == aOther.mFillRule; } bool operator!=(const StyleSVGPath& aOther) const @@ -1990,6 +1995,7 @@ struct StyleSVGPath final private: nsTArray mPath; + StyleFillRule mFillRule = StyleFillRule::Nonzero; }; struct StyleShapeSource final diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index e38f81cf8e9a..5d48a338b4ac 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -8129,6 +8129,19 @@ if (false) { other_values: [ "green", "#fc3" ], invalid_values: [ "000000", "ff00ff" ] }; + + // |clip-path: path()| is chrome-only. + gCSSProperties["clip-path"].other_values.push( + "path(nonzero, 'M 10 10 h 100 v 100 h-100 v-100 z')", + "path(evenodd, 'M 10 10 h 100 v 100 h-100 v-100 z')", + "path('M10,30A20,20 0,0,1 50,30A20,20 0,0,1 90,30Q90,60 50,90Q10,60 10,30z')", + ); + + gCSSProperties["clip-path"].invalid_values.push( + "path(nonzero)", + "path(evenodd, '')", + "path(abs, 'M 10 10 L 10 10 z')", + ); } if (IsCSSPropertyPrefEnabled("layout.css.filters.enabled")) { diff --git a/servo/components/style/gecko/conversions.rs b/servo/components/style/gecko/conversions.rs index a3109a1f2264..e85bc8ec3103 100644 --- a/servo/components/style/gecko/conversions.rs +++ b/servo/components/style/gecko/conversions.rs @@ -670,10 +670,11 @@ pub mod basic_shape { use values::computed::position; use values::computed::url::ComputedUrl; use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon}; - use values::generics::basic_shape::{Circle, Ellipse, FillRule, PolygonCoord}; + use values::generics::basic_shape::{Circle, Ellipse, FillRule, Path, PolygonCoord}; use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource}; use values::generics::border::BorderRadius as GenericBorderRadius; use values::generics::rect::Rect; + use values::specified::SVGPathData; impl StyleShapeSource { /// Convert StyleShapeSource to ShapeSource except URL and Image @@ -698,7 +699,34 @@ pub mod basic_shape { Some(ShapeSource::Shape(shape, reference_box)) }, StyleShapeSourceType::URL | StyleShapeSourceType::Image => None, - StyleShapeSourceType::Path => None, + StyleShapeSourceType::Path => { + let path = self.to_svg_path().expect("expect an SVGPathData"); + let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; + let fill = if gecko_path.mFillRule == StyleFillRule::Evenodd { + FillRule::Evenodd + } else { + FillRule::Nonzero + }; + Some(ShapeSource::Path(Path { fill, path })) + }, + } + } + + /// Generate a SVGPathData from StyleShapeSource if possible. + fn to_svg_path(&self) -> Option { + use gecko_bindings::structs::StylePathCommand; + use values::specified::svg_path::PathCommand; + match self.mType { + StyleShapeSourceType::Path => { + let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; + let result: Vec = + gecko_path.mPath.iter().map(|gecko: &StylePathCommand| { + // unsafe: cbindgen ensures the representation is the same. + unsafe{ ::std::mem::transmute(*gecko) } + }).collect(); + Some(SVGPathData::new(result.into_boxed_slice())) + }, + _ => None, } } } @@ -742,17 +770,9 @@ pub mod basic_shape { impl<'a> From<&'a StyleShapeSource> for OffsetPath { fn from(other: &'a StyleShapeSource) -> Self { - use gecko_bindings::structs::StylePathCommand; - use values::specified::svg_path::{SVGPathData, PathCommand}; match other.mType { StyleShapeSourceType::Path => { - let gecko_path = unsafe { &*other.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; - let result: Vec = - gecko_path.mPath.iter().map(|gecko: &StylePathCommand| { - // unsafe: cbindgen ensures the representation is the same. - unsafe{ ::std::mem::transmute(*gecko) } - }).collect(); - OffsetPath::Path(SVGPathData::new(result.into_boxed_slice())) + OffsetPath::Path(other.to_svg_path().expect("Cannot convert to SVGPathData")) }, StyleShapeSourceType::None => OffsetPath::none(), StyleShapeSourceType::Shape | diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 76d61838708a..9e9a6835a426 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -3683,27 +3683,16 @@ fn static_assert() { ${impl_simple_type_with_conversion("touch_action")} pub fn set_offset_path(&mut self, v: longhands::offset_path::computed_value::T) { - use gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_NewStyleSVGPath}; - use gecko_bindings::bindings::Gecko_SetStyleMotion; + use gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_SetStyleMotion}; use gecko_bindings::structs::StyleShapeSourceType; + use values::generics::basic_shape::FillRule; use values::specified::OffsetPath; let motion = unsafe { Gecko_NewStyleMotion().as_mut().unwrap() }; match v { OffsetPath::None => motion.mOffsetPath.mType = StyleShapeSourceType::None, - OffsetPath::Path(servo_path) => { - motion.mOffsetPath.mType = StyleShapeSourceType::Path; - let gecko_path = unsafe { - let ref mut source = motion.mOffsetPath; - Gecko_NewStyleSVGPath(source); - &mut source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap().mPath - }; - unsafe { gecko_path.set_len(servo_path.commands().len() as u32) }; - debug_assert_eq!(gecko_path.len(), servo_path.commands().len()); - for (servo, gecko) in servo_path.commands().iter().zip(gecko_path.iter_mut()) { - // unsafe: cbindgen ensures the representation is the same. - *gecko = unsafe { transmute(*servo) }; - } + OffsetPath::Path(p) => { + set_style_svg_path(&mut motion.mOffsetPath, &p, FillRule::Nonzero) }, } unsafe { Gecko_SetStyleMotion(&mut self.gecko.mMotion, motion) }; @@ -4982,6 +4971,35 @@ fn static_assert() { } +// Set SVGPathData to StyleShapeSource. +fn set_style_svg_path( + shape_source: &mut structs::mozilla::StyleShapeSource, + servo_path: &values::specified::svg_path::SVGPathData, + fill: values::generics::basic_shape::FillRule, +) { + use gecko_bindings::bindings::Gecko_NewStyleSVGPath; + use gecko_bindings::structs::StyleShapeSourceType; + + // Setup type. + shape_source.mType = StyleShapeSourceType::Path; + + // Setup path. + let gecko_path = unsafe { + Gecko_NewStyleSVGPath(shape_source); + &mut shape_source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap() + }; + unsafe { gecko_path.mPath.set_len(servo_path.commands().len() as u32) }; + debug_assert_eq!(gecko_path.mPath.len(), servo_path.commands().len()); + for (servo, gecko) in servo_path.commands().iter().zip(gecko_path.mPath.iter_mut()) { + // unsafe: cbindgen ensures the representation is the same. + *gecko = unsafe { transmute(*servo) }; + } + + // Setup fill-rule. + // unsafe: cbindgen ensures the representation is the same. + gecko_path.mFillRule = unsafe { transmute(fill) }; +} + <%def name="impl_shape_source(ident, gecko_ffi_name)"> pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource}; @@ -5021,6 +5039,7 @@ fn static_assert() { ${ident}.mReferenceBox = reference.into(); ${ident}.mType = StyleShapeSourceType::Box; } + ShapeSource::Path(p) => set_style_svg_path(${ident}, &p.path, p.fill), ShapeSource::Shape(servo_shape, maybe_box) => { fn init_shape(${ident}: &mut StyleShapeSource, basic_shape_type: StyleBasicShapeType) -> &mut StyleBasicShape { diff --git a/servo/components/style/values/generics/basic_shape.rs b/servo/components/style/values/generics/basic_shape.rs index ee7455e777de..0eccf011c716 100644 --- a/servo/components/style/values/generics/basic_shape.rs +++ b/servo/components/style/values/generics/basic_shape.rs @@ -12,6 +12,7 @@ use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::border::BorderRadius; use values::generics::position::Position; use values::generics::rect::Rect; +use values::specified::SVGPathData; /// A clipping shape, for `clip-path`. pub type ClippingShape = ShapeSource; @@ -54,6 +55,9 @@ pub enum ShapeSource { #[animation(error)] Box(ReferenceBox), #[animation(error)] + #[css(function)] + Path(Path), + #[animation(error)] None, } @@ -144,6 +148,19 @@ pub enum FillRule { Evenodd, } +/// The path function defined in css-shape-2. +/// +/// https://drafts.csswg.org/css-shapes-2/#funcdef-path +#[css(comma)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] +pub struct Path { + /// The filling rule for the svg path. + #[css(skip_if = "fill_is_default")] + pub fill: FillRule, + /// The svg path data. + pub path: SVGPathData, +} + // FIXME(nox): Implement ComputeSquaredDistance for T types and stop // using PartialEq here, this will let us derive this impl. impl ComputeSquaredDistance for ShapeSource diff --git a/servo/components/style/values/specified/basic_shape.rs b/servo/components/style/values/specified/basic_shape.rs index 2fa693a1978a..824c13f1f014 100644 --- a/servo/components/style/values/specified/basic_shape.rs +++ b/servo/components/style/values/specified/basic_shape.rs @@ -14,9 +14,11 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use values::computed::Percentage; use values::generics::basic_shape as generic; -use values::generics::basic_shape::{FillRule, GeometryBox, PolygonCoord, ShapeBox, ShapeSource}; +use values::generics::basic_shape::{FillRule, GeometryBox, Path, PolygonCoord}; +use values::generics::basic_shape::{ShapeBox, ShapeSource}; use values::generics::rect::Rect; use values::specified::LengthOrPercentage; +use values::specified::SVGPathData; use values::specified::border::BorderRadius; use values::specified::image::Image; use values::specified::position::{HorizontalPosition, Position, PositionComponent}; @@ -47,12 +49,42 @@ pub type ShapeRadius = generic::ShapeRadius; /// The specified value of `Polygon` pub type Polygon = generic::Polygon; -impl Parse for ShapeSource +impl Parse for ClippingShape { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + // |clip-path:path()| is a chrome-only property value support for now. `path()` is + // defined in css-shape-2, but the spec is not stable enough, and we haven't decided + // to make it public yet. However, it has some benefits for the front-end, so we + // implement it. + if context.chrome_rules_enabled() { + if let Ok(p) = input.try(|i| Path::parse(context, i)) { + return Ok(ShapeSource::Path(p)); + } + } + Self::parse_internal(context, input) + } +} + +impl Parse for FloatAreaShape { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + Self::parse_internal(context, input) + } +} + +impl ShapeSource where ReferenceBox: Parse, ImageOrUrl: Parse, { - fn parse<'i, 't>( + /// The internal parser for ShapeSource. + fn parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { @@ -393,3 +425,29 @@ impl Polygon { }) } } + +impl Parse for Path { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + input.expect_function_matching("path")?; + input.parse_nested_block(|i| Self::parse_function_arguments(context, i)) + } +} + +impl Path { + /// Parse the inner arguments of a `path` function. + fn parse_function_arguments<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let fill = input.try(|i| -> Result<_, ParseError> { + let fill = FillRule::parse(i)?; + i.expect_comma()?; + Ok(fill) + }).unwrap_or_default(); + let path = SVGPathData::parse(context, input)?; + Ok(Path { fill, path }) + } +} From 7ba34b6df659131ed872c9a7338acaba37ac4d2a Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Wed, 15 Aug 2018 12:27:38 -0700 Subject: [PATCH 51/53] Bug 1246764 - Part 3: Layout part for |clip-path: path()|. r=jwatt Create clip-path for the path function and reuse some APIs in nsCSSClipPathInstance, so we don't have to update the code flow. Differential Revision: https://phabricator.services.mozilla.com/D3635 --- dom/svg/SVGPathData.cpp | 27 +++++++++++++---------- dom/svg/SVGPathData.h | 3 ++- layout/svg/nsCSSClipPathInstance.cpp | 33 +++++++++++++++++++++++----- layout/svg/nsCSSClipPathInstance.h | 7 ++++-- layout/svg/nsSVGUtils.cpp | 1 + 5 files changed, 52 insertions(+), 19 deletions(-) diff --git a/dom/svg/SVGPathData.cpp b/dom/svg/SVGPathData.cpp index c56182400494..79b76a43961e 100644 --- a/dom/svg/SVGPathData.cpp +++ b/dom/svg/SVGPathData.cpp @@ -560,7 +560,8 @@ SVGPathData::BuildPathForMeasuring() const SVGPathData::BuildPath(const nsTArray& aPath, PathBuilder* aBuilder, uint8_t aStrokeLineCap, - Float aStrokeWidth) + Float aStrokeWidth, + float aZoomFactor) { if (aPath.IsEmpty() || !aPath[0].IsMoveTo()) { return nullptr; // paths without an initial moveto are invalid @@ -592,6 +593,10 @@ SVGPathData::BuildPath(const nsTArray& aPath, Point cp1, cp2; // previous bezier's control points Point tcp1, tcp2; // temporaries + auto scale = [aZoomFactor](const Point& p) { + return Point(p.x * aZoomFactor, p.y * aZoomFactor); + }; + // Regarding cp1 and cp2: If the previous segment was a cubic bezier curve, // then cp2 is its second control point. If the previous segment was a // quadratic curve, then cp1 is its (only) control point. @@ -610,7 +615,7 @@ SVGPathData::BuildPath(const nsTArray& aPath, MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT; const Point& p = toGfxPoint(cmd.move_to.point); pathStart = segEnd = cmd.move_to.absolute ? p : segStart + p; - aBuilder->MoveTo(segEnd); + aBuilder->MoveTo(scale(segEnd)); subpathHasLength = false; break; } @@ -619,7 +624,7 @@ SVGPathData::BuildPath(const nsTArray& aPath, segEnd = cmd.line_to.absolute ? p : segStart + p; if (segEnd != segStart) { subpathHasLength = true; - aBuilder->LineTo(segEnd); + aBuilder->LineTo(scale(segEnd)); } break; } @@ -636,7 +641,7 @@ SVGPathData::BuildPath(const nsTArray& aPath, if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) { subpathHasLength = true; - aBuilder->BezierTo(cp1, cp2, segEnd); + aBuilder->BezierTo(scale(cp1), scale(cp2), scale(segEnd)); } break; @@ -655,7 +660,7 @@ SVGPathData::BuildPath(const nsTArray& aPath, if (segEnd != segStart || segEnd != cp1) { subpathHasLength = true; - aBuilder->BezierTo(tcp1, tcp2, segEnd); + aBuilder->BezierTo(scale(tcp1), scale(tcp2), scale(segEnd)); } break; @@ -669,12 +674,12 @@ SVGPathData::BuildPath(const nsTArray& aPath, if (segEnd != segStart) { subpathHasLength = true; if (radii.x == 0.0f || radii.y == 0.0f) { - aBuilder->LineTo(segEnd); + aBuilder->LineTo(scale(segEnd)); } else { nsSVGArcConverter converter(segStart, segEnd, radii, arc.angle, arc.large_arc_flag, arc.sweep_flag); while (converter.GetNextSegment(&cp1, &cp2, &segEnd)) { - aBuilder->BezierTo(cp1, cp2, segEnd); + aBuilder->BezierTo(scale(cp1), scale(cp2), scale(segEnd)); } } } @@ -689,7 +694,7 @@ SVGPathData::BuildPath(const nsTArray& aPath, if (segEnd != segStart) { subpathHasLength = true; - aBuilder->LineTo(segEnd); + aBuilder->LineTo(scale(segEnd)); } break; @@ -702,7 +707,7 @@ SVGPathData::BuildPath(const nsTArray& aPath, if (segEnd != segStart) { subpathHasLength = true; - aBuilder->LineTo(segEnd); + aBuilder->LineTo(scale(segEnd)); } break; @@ -718,7 +723,7 @@ SVGPathData::BuildPath(const nsTArray& aPath, if (segEnd != segStart || segEnd != cp1 || segEnd != cp2) { subpathHasLength = true; - aBuilder->BezierTo(cp1, cp2, segEnd); + aBuilder->BezierTo(scale(cp1), scale(cp2), scale(segEnd)); } break; @@ -734,7 +739,7 @@ SVGPathData::BuildPath(const nsTArray& aPath, if (segEnd != segStart || segEnd != cp1) { subpathHasLength = true; - aBuilder->BezierTo(tcp1, tcp2, segEnd); + aBuilder->BezierTo(scale(tcp1), scale(tcp2), scale(segEnd)); } break; } diff --git a/dom/svg/SVGPathData.h b/dom/svg/SVGPathData.h index 25a2cdb88498..45e8bc86c24f 100644 --- a/dom/svg/SVGPathData.h +++ b/dom/svg/SVGPathData.h @@ -178,7 +178,8 @@ public: BuildPath(const nsTArray& aPath, PathBuilder* aBuilder, uint8_t aCapStyle, - Float aStrokeWidth); + Float aStrokeWidth, + float aZoomFactor = 1.0); const_iterator begin() const { return mData.Elements(); } const_iterator end() const { return mData.Elements() + mData.Length(); } diff --git a/layout/svg/nsCSSClipPathInstance.cpp b/layout/svg/nsCSSClipPathInstance.cpp index 4edcbba21fa7..5846cc807a96 100644 --- a/layout/svg/nsCSSClipPathInstance.cpp +++ b/layout/svg/nsCSSClipPathInstance.cpp @@ -10,6 +10,7 @@ #include "gfx2DGlue.h" #include "gfxContext.h" #include "gfxPlatform.h" +#include "mozilla/dom/SVGPathData.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/PathHelpers.h" #include "mozilla/ShapeUtils.h" @@ -33,8 +34,9 @@ nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext, #ifdef DEBUG StyleShapeSourceType type = clipPathStyle.GetType(); MOZ_ASSERT(type == StyleShapeSourceType::Shape || - type == StyleShapeSourceType::Box, - "This function is used with basic-shape and geometry-box only."); + type == StyleShapeSourceType::Box || + type == StyleShapeSourceType::Path, + "This is used with basic-shape, geometry-box, and path() only"); #endif nsCSSClipPathInstance instance(aFrame, clipPathStyle); @@ -69,11 +71,13 @@ nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame, } /* static */ Rect -nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip(nsIFrame* aFrame, - const StyleShapeSource& aClipPathStyle) +nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip( + nsIFrame* aFrame, + const StyleShapeSource& aClipPathStyle) { MOZ_ASSERT(aClipPathStyle.GetType() == StyleShapeSourceType::Shape || - aClipPathStyle.GetType() == StyleShapeSourceType::Box); + aClipPathStyle.GetType() == StyleShapeSourceType::Box || + aClipPathStyle.GetType() == StyleShapeSourceType::Path); nsCSSClipPathInstance instance(aFrame, aClipPathStyle); @@ -86,6 +90,10 @@ nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip(nsIFrame* aFrame, already_AddRefed nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget) { + if (mClipPathStyle.GetType() == StyleShapeSourceType::Path) { + return CreateClipPathPath(aDrawTarget); + } + nsRect r = nsLayoutUtils::ComputeGeometryBox(mTargetFrame, mClipPathStyle.GetReferenceBox()); @@ -213,3 +221,18 @@ nsCSSClipPathInstance::CreateClipPathInset(DrawTarget* aDrawTarget, } return builder->Finish(); } + +already_AddRefed +nsCSSClipPathInstance::CreateClipPathPath(DrawTarget* aDrawTarget) +{ + const StyleSVGPath* path = mClipPathStyle.GetPath(); + MOZ_ASSERT(path); + + RefPtr builder = aDrawTarget->CreatePathBuilder( + path->FillRule() == StyleFillRule::Nonzero ? FillRule::FILL_WINDING + : FillRule::FILL_EVEN_ODD); + float scale = float(AppUnitsPerCSSPixel()) / + mTargetFrame->PresContext()->AppUnitsPerDevPixel(); + return SVGPathData::BuildPath( + path->Path(), builder, NS_STYLE_STROKE_LINECAP_BUTT, 0.0, scale); +} diff --git a/layout/svg/nsCSSClipPathInstance.h b/layout/svg/nsCSSClipPathInstance.h index bfe1ad136e5c..1327d70a8114 100644 --- a/layout/svg/nsCSSClipPathInstance.h +++ b/layout/svg/nsCSSClipPathInstance.h @@ -29,8 +29,10 @@ public: static bool HitTestBasicShapeClip(nsIFrame* aFrame, const gfxPoint& aPoint); - static Rect GetBoundingRectForBasicShapeClip(nsIFrame* aFrame, - const StyleShapeSource& aClipPathStyle); + static Rect GetBoundingRectForBasicShapeClip( + nsIFrame* aFrame, + const StyleShapeSource& aClipPathStyle); + private: explicit nsCSSClipPathInstance(nsIFrame* aFrame, const StyleShapeSource aClipPathStyle) @@ -53,6 +55,7 @@ private: already_AddRefed CreateClipPathInset(DrawTarget* aDrawTarget, const nsRect& aRefBox); + already_AddRefed CreateClipPathPath(DrawTarget* aDrawTarget); /** * The frame for the element that is currently being clipped. diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 151af89a2c51..81fa5aaef7e6 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -523,6 +523,7 @@ nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity, break; case StyleShapeSourceType::Shape: case StyleShapeSourceType::Box: + case StyleShapeSourceType::Path: aUsage.shouldApplyBasicShape = true; break; case StyleShapeSourceType::None: From 63d06d801741f35f3349314152b3210b43dcf9ee Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Fri, 17 Aug 2018 15:48:02 -0700 Subject: [PATCH 52/53] Bug 1246764 - Part 4: Rename mask flag and function name of xxxBasicShape to xxxBasicShapeOrPath. r=jwatt This flag and function name are used for both basic shapes and path function, so rename it. For now, we treat path() and other basic-shapes as the different object (i.e. StyleSVGPath and StyleBasicShape), so I rename these functions and mask flag. Differential Revision: https://phabricator.services.mozilla.com/D3636 --- layout/generic/nsFrame.cpp | 4 ++-- layout/svg/nsCSSClipPathInstance.cpp | 10 +++++----- layout/svg/nsCSSClipPathInstance.h | 10 +++++----- layout/svg/nsSVGIntegrationUtils.cpp | 14 +++++++------- layout/svg/nsSVGUtils.cpp | 13 +++++++------ layout/svg/nsSVGUtils.h | 11 +++++++---- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 984f75e93ac7..e3d26d0f888a 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2713,8 +2713,8 @@ ComputeClipForMaskItem(nsDisplayListBuilder* aBuilder, nsIFrame* aMaskedFrame, aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame); Maybe combinedClip; - if (maskUsage.shouldApplyBasicShape) { - Rect result = nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip( + if (maskUsage.shouldApplyBasicShapeOrPath) { + Rect result = nsCSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip( aMaskedFrame, svgReset->mClipPath); combinedClip = Some(ThebesRect(result)); } else if (maskUsage.shouldApplyClipPath) { diff --git a/layout/svg/nsCSSClipPathInstance.cpp b/layout/svg/nsCSSClipPathInstance.cpp index 5846cc807a96..2c97aa5d4990 100644 --- a/layout/svg/nsCSSClipPathInstance.cpp +++ b/layout/svg/nsCSSClipPathInstance.cpp @@ -26,8 +26,8 @@ using namespace mozilla::dom; using namespace mozilla::gfx; /* static*/ void -nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext, - nsIFrame* aFrame) +nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(gfxContext& aContext, + nsIFrame* aFrame) { auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath; @@ -48,8 +48,8 @@ nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext, } /* static*/ bool -nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame, - const gfxPoint& aPoint) +nsCSSClipPathInstance::HitTestBasicShapeOrPathClip(nsIFrame* aFrame, + const gfxPoint& aPoint) { auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath; StyleShapeSourceType type = clipPathStyle.GetType(); @@ -71,7 +71,7 @@ nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame, } /* static */ Rect -nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip( +nsCSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip( nsIFrame* aFrame, const StyleShapeSource& aClipPathStyle) { diff --git a/layout/svg/nsCSSClipPathInstance.h b/layout/svg/nsCSSClipPathInstance.h index 1327d70a8114..bc7381d84c8f 100644 --- a/layout/svg/nsCSSClipPathInstance.h +++ b/layout/svg/nsCSSClipPathInstance.h @@ -23,13 +23,13 @@ class nsCSSClipPathInstance typedef mozilla::gfx::Rect Rect; public: - static void ApplyBasicShapeClip(gfxContext& aContext, - nsIFrame* aFrame); + static void ApplyBasicShapeOrPathClip(gfxContext& aContext, + nsIFrame* aFrame); // aPoint is in CSS pixels. - static bool HitTestBasicShapeClip(nsIFrame* aFrame, - const gfxPoint& aPoint); + static bool HitTestBasicShapeOrPathClip(nsIFrame* aFrame, + const gfxPoint& aPoint); - static Rect GetBoundingRectForBasicShapeClip( + static Rect GetBoundingRectForBasicShapeOrPathClip( nsIFrame* aFrame, const StyleShapeSource& aClipPathStyle); diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 328f111bc1ed..051059822e54 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -809,13 +809,13 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams) // Paint clip-path-basic-shape onto ctx gfxContextAutoSaveRestore basicShapeSR; - if (maskUsage.shouldApplyBasicShape) { + if (maskUsage.shouldApplyBasicShapeOrPath) { matSR.SetContext(&ctx); MoveContextOriginToUserSpace(firstFrame, aParams); basicShapeSR.SetContext(&ctx); - nsCSSClipPathInstance::ApplyBasicShapeClip(ctx, frame); + nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(ctx, frame); if (!maskUsage.shouldGenerateMaskLayer) { // Only have basic-shape clip-path effect. Fill clipped region by // opaque white. @@ -997,17 +997,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) /* If this frame has only a trivial clipPath, set up cairo's clipping now so * we can just do normal painting and get it clipped appropriately. */ - if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) { + if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) { gfxContextMatrixAutoSaveRestore matSR(&context); MoveContextOriginToUserSpace(firstFrame, aParams); MOZ_ASSERT(!maskUsage.shouldApplyClipPath || - !maskUsage.shouldApplyBasicShape); + !maskUsage.shouldApplyBasicShapeOrPath); if (maskUsage.shouldApplyClipPath) { clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix); } else { - nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame); + nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(context, frame); } } @@ -1036,7 +1036,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) maskUsage.shouldGenerateClipMaskLayer) { overlayColor.g = 1.0f; // green represents clip-path:. } - if (maskUsage.shouldApplyBasicShape) { + if (maskUsage.shouldApplyBasicShapeOrPath) { overlayColor.b = 1.0f; // blue represents // clip-path:||. } @@ -1045,7 +1045,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) context.Fill(); } - if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) { + if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) { context.PopClip(); } diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 81fa5aaef7e6..b94316d69772 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -524,11 +524,12 @@ nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity, case StyleShapeSourceType::Shape: case StyleShapeSourceType::Box: case StyleShapeSourceType::Path: - aUsage.shouldApplyBasicShape = true; + aUsage.shouldApplyBasicShapeOrPath = true; break; case StyleShapeSourceType::None: MOZ_ASSERT(!aUsage.shouldGenerateClipMaskLayer && - !aUsage.shouldApplyClipPath && !aUsage.shouldApplyBasicShape); + !aUsage.shouldApplyClipPath && + !aUsage.shouldApplyBasicShapeOrPath); break; default: MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type."); @@ -808,11 +809,11 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, /* If this frame has only a trivial clipPath, set up cairo's clipping now so * we can just do normal painting and get it clipped appropriately. */ - if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) { + if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) { if (maskUsage.shouldApplyClipPath) { clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform); } else { - nsCSSClipPathInstance::ApplyBasicShapeClip(aContext, aFrame); + nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(aContext, aFrame); } } @@ -857,7 +858,7 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, svgFrame->PaintSVG(*target, aTransform, aImgParams, aDirtyRect); } - if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) { + if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) { aContext.PopClip(); } @@ -879,7 +880,7 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint) if (!props.mClipPath) { const nsStyleSVGReset *style = aFrame->StyleSVGReset(); if (style->HasClipPath()) { - return nsCSSClipPathInstance::HitTestBasicShapeClip(aFrame, aPoint); + return nsCSSClipPathInstance::HitTestBasicShapeOrPathClip(aFrame, aPoint); } return true; } diff --git a/layout/svg/nsSVGUtils.h b/layout/svg/nsSVGUtils.h index 44784ce5d63d..d42849a8f81a 100644 --- a/layout/svg/nsSVGUtils.h +++ b/layout/svg/nsSVGUtils.h @@ -606,19 +606,22 @@ public: bool shouldGenerateMaskLayer; bool shouldGenerateClipMaskLayer; bool shouldApplyClipPath; - bool shouldApplyBasicShape; + bool shouldApplyBasicShapeOrPath; float opacity; MaskUsage() - : shouldGenerateMaskLayer(false), shouldGenerateClipMaskLayer(false), - shouldApplyClipPath(false), shouldApplyBasicShape(false), opacity(0.0) + : shouldGenerateMaskLayer(false) + , shouldGenerateClipMaskLayer(false) + , shouldApplyClipPath(false) + , shouldApplyBasicShapeOrPath(false) + , opacity(0.0) { } bool shouldDoSomething() { return shouldGenerateMaskLayer || shouldGenerateClipMaskLayer || shouldApplyClipPath - || shouldApplyBasicShape + || shouldApplyBasicShapeOrPath || opacity != 1.0; } }; From 06a4323ee035c99576ecfc9fbc1ee79007d302dd Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Thu, 16 Aug 2018 16:07:13 -0700 Subject: [PATCH 53/53] Bug 1246764 - Part 5: Tests. r=jwatt Add some chrome-only reftests. Differential Revision: https://phabricator.services.mozilla.com/D3637 --- .../clip-path/clip-path-path-001-ref.html | 29 +++++++++++++++++++ .../clip-path/clip-path-path-001.html | 24 +++++++++++++++ .../clip-path/clip-path-path-002-ref.html | 29 +++++++++++++++++++ .../clip-path/clip-path-path-002.html | 24 +++++++++++++++ .../svg/svg-integration/clip-path/path.css | 7 +++++ .../svg-integration/clip-path/reftest.list | 3 ++ layout/tools/reftest/jar.mn | 1 + 7 files changed, 117 insertions(+) create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-path-001-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-path-001.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-path-002-ref.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/clip-path-path-002.html create mode 100644 layout/reftests/svg/svg-integration/clip-path/path.css diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-path-001-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-path-001-ref.html new file mode 100644 index 000000000000..afc1d18f204e --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-path-001-ref.html @@ -0,0 +1,29 @@ + + + + + CSS Masking: Reference for clip-path's path function with nonzero + + + +

The test passes if there are a green filled rect.

+
+ + + + + + + + + diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-path-001.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-path-001.html new file mode 100644 index 000000000000..4f9dafefb517 --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-path-001.html @@ -0,0 +1,24 @@ + + + + + CSS Masking: Test clip-path property and path function with nonzero + + + + + + +

The test passes if there are a green filled rect.

+
+ + diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-path-002-ref.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-path-002-ref.html new file mode 100644 index 000000000000..d9ea5183fb1e --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-path-002-ref.html @@ -0,0 +1,29 @@ + + + + + CSS Masking: Reference for clip-path's path function with evenodd + + + +

The test passes if there are a green hollow rect.

+
+ + + + + + + + + diff --git a/layout/reftests/svg/svg-integration/clip-path/clip-path-path-002.html b/layout/reftests/svg/svg-integration/clip-path/clip-path-path-002.html new file mode 100644 index 000000000000..fc2b5a0b4a6c --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/clip-path-path-002.html @@ -0,0 +1,24 @@ + + + + + CSS Masking: Test clip-path property and path function with evenodd + + + + + + +

The test passes if there are a green hollow rect.

+
+ + diff --git a/layout/reftests/svg/svg-integration/clip-path/path.css b/layout/reftests/svg/svg-integration/clip-path/path.css new file mode 100644 index 000000000000..190a6f69435e --- /dev/null +++ b/layout/reftests/svg/svg-integration/clip-path/path.css @@ -0,0 +1,7 @@ +.path_nonzero_rect { + clip-path: path(nonzero, 'M10,10h80v80h-80zM25,25h50v50h-50z'); +} + +.path_evenodd_rect { + clip-path: path(evenodd, 'M10,10h80v80h-80zM25,25h50v50h-50z'); +} diff --git a/layout/reftests/svg/svg-integration/clip-path/reftest.list b/layout/reftests/svg/svg-integration/clip-path/reftest.list index f28ec5cc354c..ca4245336ff7 100644 --- a/layout/reftests/svg/svg-integration/clip-path/reftest.list +++ b/layout/reftests/svg/svg-integration/clip-path/reftest.list @@ -59,3 +59,6 @@ fuzzy(0-64,0-340) fuzzy-if(webrender,104-104,311-311) == clip-path-inset-003.htm == clip-path-stroke-001.html clip-path-stroke-001-ref.html == clip-path-transform-001.html clip-path-transform-001-ref.html + +== clip-path-path-001.html clip-path-path-001-ref.html +== clip-path-path-002.html clip-path-path-002-ref.html diff --git a/layout/tools/reftest/jar.mn b/layout/tools/reftest/jar.mn index c5431eac5ea1..1f2dd0bf3d76 100644 --- a/layout/tools/reftest/jar.mn +++ b/layout/tools/reftest/jar.mn @@ -8,6 +8,7 @@ reftest.jar: content/PerTestCoverageUtils.jsm (../../../tools/code-coverage/PerTestCoverageUtils.jsm) content/input.css (../../../editor/reftests/xul/input.css) content/moz-bool-pref.css (../../../layout/reftests/css-parsing/moz-bool-pref.css) + content/path.css (../../../layout/reftests/svg/svg-integration/clip-path/path.css) content/progress.css (../../../layout/reftests/forms/progress/style.css) * content/manifest.jsm (manifest.jsm) * content/reftest.jsm (reftest.jsm)