Skip to content

Commit

Permalink
Deny ElementInternals.shadowRoot for pre-created shadow roots
Browse files Browse the repository at this point in the history
Per the spec issue [1], this change disallows the use of
ElementInternals.shadowRoot for shadow roots created prior
to the custom element constructor being run. This protects
potentially-closed shadow roots, created outside of the
custom element, from being revealed to the custom element
itself. (Use case unclear, but this was the request.)

[1] WICG/webcomponents#871

Bug: 1042130
Change-Id: I25192256e8b1334d09ea587f29d64f35d4f5f949
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2412470
Commit-Queue: Kouhei Ueno <[email protected]>
Auto-Submit: Mason Freed <[email protected]>
Reviewed-by: Kouhei Ueno <[email protected]>
Cr-Commit-Position: refs/heads/master@{#807311}
GitOrigin-RevId: 2372aae3a33f3928c0964defb4f01b9be77ef3ce
  • Loading branch information
mfreed7 authored and copybara-github committed Sep 16, 2020
1 parent 8d1536b commit 0b23c81
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 4 deletions.
16 changes: 16 additions & 0 deletions blink/renderer/core/dom/element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,13 @@ Node* Element::Clone(Document& factory, CloneChildrenFlag flag) const {
// set copy’s shadow root’s "is declarative shadow root" property to true.
cloned_shadow_root.SetIsDeclarativeShadowRoot(
shadow_root->IsDeclarativeShadowRoot());

// 7.NEW If node’s shadow root’s "is available to element internals" is
// true, then set copy’s shadow root’s "is available to element internals"
// property to true.
cloned_shadow_root.SetAvailableToElementInternals(
shadow_root->IsAvailableToElementInternals());

// 7.3 If the clone children flag is set, clone all the children of node’s
// shadow root and append them to copy’s shadow root, with document as
// specified, the clone children flag being set, and the clone shadows
Expand Down Expand Up @@ -3591,6 +3598,15 @@ ShadowRoot& Element::AttachShadowRootInternal(
FocusDelegation::kDelegateFocus);
// 8. Set shadow’s "is declarative shadow root" property to false.
shadow_root.SetIsDeclarativeShadowRoot(false);

// NEW. If shadow host is a custom element, and if custom element state is
// not "precustomized" or "custom", set shadow root's
// IsAvailableToElementInternals flag to false. Otherwise, set it to true.
shadow_root.SetAvailableToElementInternals(
!(IsCustomElement() &&
GetCustomElementState() != CustomElementState::kCustom &&
GetCustomElementState() != CustomElementState::kPreCustomized));

shadow_root.SetSlotAssignmentMode(slot_assignment_mode);
return shadow_root;
}
Expand Down
12 changes: 11 additions & 1 deletion blink/renderer/core/dom/shadow_root.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ class CORE_EXPORT ShadowRoot final : public DocumentFragment, public TreeScope {
}
bool IsDeclarativeShadowRoot() const { return is_declarative_shadow_root_; }

void SetAvailableToElementInternals(bool flag) {
DCHECK(!flag || GetType() == ShadowRootType::kOpen ||
GetType() == ShadowRootType::kClosed);
available_to_element_internals_ = flag;
}
bool IsAvailableToElementInternals() const {
return available_to_element_internals_ || is_declarative_shadow_root_;
}

bool ContainsShadowRoots() const { return child_shadow_root_count_; }

StyleSheetList& StyleSheets();
Expand Down Expand Up @@ -195,8 +204,9 @@ class CORE_EXPORT ShadowRoot final : public DocumentFragment, public TreeScope {
unsigned delegates_focus_ : 1;
unsigned slot_assignment_mode_ : 1;
unsigned is_declarative_shadow_root_ : 1;
unsigned available_to_element_internals_ : 1;
unsigned needs_distribution_recalc_ : 1;
unsigned unused_ : 9;
unsigned unused_ : 8;
};

inline Element* ShadowRoot::ActiveElement() const {
Expand Down
5 changes: 4 additions & 1 deletion blink/renderer/core/html/custom/element_internals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,10 @@ bool ElementInternals::HasState(const AtomicString& state) const {
}

ShadowRoot* ElementInternals::shadowRoot() const {
return Target().AuthorShadowRoot();
if (ShadowRoot* shadow_root = Target().AuthorShadowRoot()) {
return shadow_root->IsAvailableToElementInternals() ? shadow_root : nullptr;
}
return nullptr;
}

const AtomicString& ElementInternals::FastGetAttribute(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@
assert_throws_dom('NotSupportedError', () => element.attachInternals(), 'attachInternals forbidden by disabledFeatures, post-upgrade');
}, 'ElementInternals disabled by disabledFeatures');



test(() => {
let constructed = false;
const element = document.createElement('x-6');
const sr = element.attachShadow({mode: 'closed'});
assert_true(sr instanceof ShadowRoot);
customElements.define('x-6', class extends HTMLElement {
constructor() {
super();
assert_throws_dom('NotSupportedError', () => this.attachShadow({mode:'open'}), 'attachShadow already called');
const elementInternals = this.attachInternals();
assert_equals(elementInternals.shadowRoot, null, 'ElementInternals.shadowRoot should not be available for pre-attached shadow');
constructed = true;
}
});
assert_false(constructed);
customElements.upgrade(element);
assert_true(constructed,'Failed to construct - test failed');
assert_equals(element.shadowRoot, null, 'shadow root is closed');
}, 'ElementInternals.shadowRoot doesn\'t reveal pre-attached closed shadowRoot');
</script>

0 comments on commit 0b23c81

Please sign in to comment.