|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Mason Freed <masonfreed@chromium.org> |
| 3 | +Date: Mon, 20 Apr 2020 21:57:52 +0000 |
| 4 | +Subject: Fix customized built-in element constructor behavior |
| 5 | + |
| 6 | +This CL implements two changes: |
| 7 | + 1. It fixes the implementation to better match the spec for the |
| 8 | + "create an element for the token" [1] algorithm. Prior to this CL, |
| 9 | + step 7 of that algorithm was skipping directly to step 6 of the |
| 10 | + "create an element" [2] algorithm, skipping over step 5 for |
| 11 | + customized built-in elements. This is now fixed. This case is |
| 12 | + illustrated by the issue and example at [3] and [4]. This becomes |
| 13 | + the first test in customized-built-in-constructor-exceptions.html. |
| 14 | + |
| 15 | + 2. It updates the comments to match the new behavior discussed in [3] |
| 16 | + and the [5] spec PR, which changes the return value in the case |
| 17 | + that a customized built-in element constructor throws an exception. |
| 18 | + With the change above, that is actually already the behavior. So |
| 19 | + this is just a comment change. Two new tests are added to |
| 20 | + customized-built-in-constructor-exceptions.html. |
| 21 | + |
| 22 | +[1] https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token |
| 23 | +[2] https://dom.spec.whatwg.org/#concept-create-element |
| 24 | +[3] https://github.com/whatwg/html/issues/5084 |
| 25 | +[4] https://crbug.com/1024866 |
| 26 | +[5] https://github.com/whatwg/dom/pull/797 |
| 27 | + |
| 28 | +Bug: 1071059, 1024866 |
| 29 | +Change-Id: I814c81991eb5e83501304bcb3d2da476743aef52 |
| 30 | +Cq-Do-Not-Cancel-Tryjobs: true |
| 31 | +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2152986 |
| 32 | +Commit-Queue: Mason Freed <masonfreed@chromium.org> |
| 33 | +Auto-Submit: Mason Freed <masonfreed@chromium.org> |
| 34 | +Reviewed-by: Kent Tamura <tkent@chromium.org> |
| 35 | +Cr-Commit-Position: refs/heads/master@{#760705} |
| 36 | + |
| 37 | +diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc |
| 38 | +index dfbfed1ca13e2182dfde470fb9a64bd0a0fd0cd9..89c54511faf05ee401900d4e33c2abaa2fa86405 100644 |
| 39 | +--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc |
| 40 | ++++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc |
| 41 | +@@ -146,6 +146,7 @@ HTMLElement* ScriptCustomElementDefinition::HandleCreateElementSyncException( |
| 42 | + HTMLElement* ScriptCustomElementDefinition::CreateAutonomousCustomElementSync( |
| 43 | + Document& document, |
| 44 | + const QualifiedName& tag_name) { |
| 45 | ++ DCHECK(CustomElement::ShouldCreateCustomElement(tag_name)) << tag_name; |
| 46 | + if (!script_state_->ContextIsValid()) |
| 47 | + return CustomElement::CreateFailedElement(document, tag_name); |
| 48 | + ScriptState::Scope scope(script_state_); |
| 49 | +diff --git a/third_party/blink/renderer/core/html/custom/custom_element.cc b/third_party/blink/renderer/core/html/custom/custom_element.cc |
| 50 | +index d21c582052cbf861b792ec6f44e418cc25f3a8cc..1edcd4e60009c9f994c0711a8d373d771b4e8965 100644 |
| 51 | +--- a/third_party/blink/renderer/core/html/custom/custom_element.cc |
| 52 | ++++ b/third_party/blink/renderer/core/html/custom/custom_element.cc |
| 53 | +@@ -205,7 +205,8 @@ Element* CustomElement::CreateUncustomizedOrUndefinedElement( |
| 54 | + |
| 55 | + HTMLElement* CustomElement::CreateFailedElement(Document& document, |
| 56 | + const QualifiedName& tag_name) { |
| 57 | +- DCHECK(ShouldCreateCustomElement(tag_name)); |
| 58 | ++ CHECK(ShouldCreateCustomElement(tag_name)) |
| 59 | ++ << "HTMLUnknownElement with built-in tag name: " << tag_name; |
| 60 | + |
| 61 | + // "create an element for a token": |
| 62 | + // https://html.spec.whatwg.org/C/#create-an-element-for-the-token |
| 63 | +diff --git a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc |
| 64 | +index 6126e0d04c48e8f7cebe8dbf39418599fcfcb84e..3341f49f48de367cbe4f736c4acac09c5d59ddbd 100644 |
| 65 | +--- a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc |
| 66 | ++++ b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc |
| 67 | +@@ -142,14 +142,19 @@ HTMLElement* CustomElementDefinition::CreateElement( |
| 68 | + result->SetCustomElementState(CustomElementState::kUndefined); |
| 69 | + result->SetIsValue(Descriptor().GetName()); |
| 70 | + |
| 71 | +- // 5.3. If the synchronous custom elements flag is set, upgrade |
| 72 | +- // element using definition. |
| 73 | +- // 5.4. Otherwise, enqueue a custom element upgrade reaction given |
| 74 | +- // result and definition. |
| 75 | +- if (!flags.IsAsyncCustomElements()) |
| 76 | ++ if (!flags.IsAsyncCustomElements()) { |
| 77 | ++ // 5.3 If the synchronous custom elements flag is set, then run this step |
| 78 | ++ // while catching any exceptions: |
| 79 | ++ // 1. Upgrade element using definition. |
| 80 | ++ // If this step threw an exception, then: |
| 81 | ++ // 1. Report the exception. |
| 82 | ++ // 2. Set result's custom element state to "failed". |
| 83 | + Upgrade(*result); |
| 84 | +- else |
| 85 | ++ } else { |
| 86 | ++ // 5.4. Otherwise, enqueue a custom element upgrade reaction given |
| 87 | ++ // result and definition. |
| 88 | + EnqueueUpgradeReaction(*result); |
| 89 | ++ } |
| 90 | + return To<HTMLElement>(result); |
| 91 | + } |
| 92 | + |
| 93 | +diff --git a/third_party/blink/renderer/core/html/parser/html_construction_site.cc b/third_party/blink/renderer/core/html/parser/html_construction_site.cc |
| 94 | +index 1435bcf39f86e3e6811385e61c803d8ca8eb97e4..33d3b37cb9e4f38dca4c66b3540b087db54d02fa 100644 |
| 95 | +--- a/third_party/blink/renderer/core/html/parser/html_construction_site.cc |
| 96 | ++++ b/third_party/blink/renderer/core/html/parser/html_construction_site.cc |
| 97 | +@@ -914,8 +914,11 @@ Element* HTMLConstructionSite::CreateElement( |
| 98 | + // reactions stack." |
| 99 | + CEReactionsScope reactions; |
| 100 | + |
| 101 | +- // 7. |
| 102 | +- element = definition->CreateAutonomousCustomElementSync(document, tag_name); |
| 103 | ++ // 7. Let element be the result of creating an element given document, |
| 104 | ++ // localName, given namespace, null, and is. If will execute script is true, |
| 105 | ++ // set the synchronous custom elements flag; otherwise, leave it unset. |
| 106 | ++ element = |
| 107 | ++ definition->CreateElement(document, tag_name, GetCreateElementFlags()); |
| 108 | + |
| 109 | + // "8. Append each attribute in the given token to element." We don't use |
| 110 | + // setAttributes here because the custom element constructor may have |
| 111 | +diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html b/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html |
| 112 | +new file mode 100644 |
| 113 | +index 0000000000000000000000000000000000000000..32729bdb6bb2c82f54c074c7609ff5c79883a37a |
| 114 | +--- /dev/null |
| 115 | ++++ b/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html |
| 116 | +@@ -0,0 +1,75 @@ |
| 117 | ++<!DOCTYPE html> |
| 118 | ++<title>Customized built-in element constructor behavior</title> |
| 119 | ++<meta name='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'> |
| 120 | ++<link rel='help' href='https://dom.spec.whatwg.org/#concept-create-element'> |
| 121 | ++<script src='/resources/testharness.js'></script> |
| 122 | ++<script src='/resources/testharnessreport.js'></script> |
| 123 | ++ |
| 124 | ++<script> |
| 125 | ++setup({allow_uncaught_exception : true}); |
| 126 | ++ |
| 127 | ++class MyCustomParagraph extends HTMLParagraphElement { |
| 128 | ++ constructor() { |
| 129 | ++ super(); |
| 130 | ++ this.textContent = 'PASS'; |
| 131 | ++ } |
| 132 | ++} |
| 133 | ++customElements.define('custom-p', MyCustomParagraph, { extends: 'p' }); |
| 134 | ++</script> |
| 135 | ++<p id=targetp is='custom-p'></p> |
| 136 | ++<script> |
| 137 | ++test(t => { |
| 138 | ++ let target = document.getElementById('targetp'); |
| 139 | ++ assert_true(!!target); |
| 140 | ++ assert_equals(target.localName, 'p'); |
| 141 | ++ assert_true(target instanceof MyCustomParagraph); |
| 142 | ++ assert_true(target instanceof HTMLParagraphElement); |
| 143 | ++ assert_equals(target.childNodes.length, 1); |
| 144 | ++ assert_equals(target.textContent, 'PASS'); |
| 145 | ++}, 'Appending children in customized built-in constructor should work'); |
| 146 | ++</script> |
| 147 | ++ |
| 148 | ++ |
| 149 | ++<script> |
| 150 | ++class MyCustomVideo extends HTMLVideoElement { |
| 151 | ++ constructor() { |
| 152 | ++ super(); |
| 153 | ++ throw new Error(); |
| 154 | ++ } |
| 155 | ++} |
| 156 | ++customElements.define('custom-video', MyCustomVideo, { extends: 'video' }); |
| 157 | ++</script> |
| 158 | ++<video id=targetvideo is='custom-video'> <source></source> </video> |
| 159 | ++<script> |
| 160 | ++test(t => { |
| 161 | ++ let target = document.getElementById('targetvideo'); |
| 162 | ++ assert_true(!!target); |
| 163 | ++ assert_equals(target.localName, 'video'); |
| 164 | ++ assert_true(target instanceof MyCustomVideo); |
| 165 | ++ assert_true(target instanceof HTMLVideoElement); |
| 166 | ++ assert_equals(target.children.length, 1); |
| 167 | ++}, 'Throwing exception in customized built-in constructor should not crash and should return correct element type (video)'); |
| 168 | ++</script> |
| 169 | ++ |
| 170 | ++ |
| 171 | ++<script> |
| 172 | ++class MyCustomForm extends HTMLFormElement { |
| 173 | ++ constructor() { |
| 174 | ++ super(); |
| 175 | ++ throw new Error(); |
| 176 | ++ } |
| 177 | ++} |
| 178 | ++customElements.define('custom-form', MyCustomForm, { extends: 'form' }); |
| 179 | ++</script> |
| 180 | ++<form id=targetform is='custom-form'> <label></label><input> </form> |
| 181 | ++<script> |
| 182 | ++test(t => { |
| 183 | ++ let target = document.getElementById('targetform'); |
| 184 | ++ assert_true(!!target); |
| 185 | ++ assert_equals(target.localName, 'form'); |
| 186 | ++ assert_true(target instanceof MyCustomForm); |
| 187 | ++ assert_true(target instanceof HTMLFormElement); |
| 188 | ++ assert_equals(target.children.length, 2); |
| 189 | ++}, 'Throwing exception in customized built-in constructor should not crash and should return correct element type (form)'); |
| 190 | ++</script> |
| 191 | ++ |
0 commit comments