1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Neither the name of Google Inc. nor the names of its
11  * contributors may be used to endorse or promote products derived from
12  * this software without specific prior written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "third_party/blink/renderer/core/dom/shadow_root.h"
28 
29 #include "third_party/blink/public/platform/platform.h"
30 #include "third_party/blink/renderer/bindings/core/v8/v8_set_inner_html_options.h"
31 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
32 #include "third_party/blink/renderer/core/css/style_change_reason.h"
33 #include "third_party/blink/renderer/core/css/style_engine.h"
34 #include "third_party/blink/renderer/core/css/style_sheet_list.h"
35 #include "third_party/blink/renderer/core/dom/element_traversal.h"
36 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
37 #include "third_party/blink/renderer/core/dom/shadow_root_v0.h"
38 #include "third_party/blink/renderer/core/dom/slot_assignment.h"
39 #include "third_party/blink/renderer/core/dom/slot_assignment_engine.h"
40 #include "third_party/blink/renderer/core/dom/text.h"
41 #include "third_party/blink/renderer/core/dom/v0_insertion_point.h"
42 #include "third_party/blink/renderer/core/dom/whitespace_attacher.h"
43 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
44 #include "third_party/blink/renderer/core/html/html_content_element.h"
45 #include "third_party/blink/renderer/core/html/html_shadow_element.h"
46 #include "third_party/blink/renderer/core/html/html_slot_element.h"
47 #include "third_party/blink/renderer/core/layout/layout_object.h"
48 #include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
49 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
50 #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
51 
52 namespace blink {
53 
Distribute()54 void ShadowRoot::Distribute() {
55   if (!IsV1())
56     V0().Distribute();
57 }
58 
59 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope {
60   Member<void*> member[3];
61   unsigned flags[1];
62 };
63 
64 ASSERT_SIZE(ShadowRoot, SameSizeAsShadowRoot);
65 
ShadowRoot(Document & document,ShadowRootType type)66 ShadowRoot::ShadowRoot(Document& document, ShadowRootType type)
67     : DocumentFragment(nullptr, kCreateShadowRoot),
68       TreeScope(*this, document),
69       style_sheet_list_(nullptr),
70       child_shadow_root_count_(0),
71       type_(static_cast<unsigned>(type)),
72       registered_with_parent_shadow_root_(false),
73       delegates_focus_(false),
74       slot_assignment_mode_(static_cast<unsigned>(SlotAssignmentMode::kAuto)),
75       needs_distribution_recalc_(false),
76       unused_(0) {
77   if (IsV0())
78     shadow_root_v0_ = MakeGarbageCollected<ShadowRootV0>(*this);
79 }
80 
81 ShadowRoot::~ShadowRoot() = default;
82 
EnsureSlotAssignment()83 SlotAssignment& ShadowRoot::EnsureSlotAssignment() {
84   if (!slot_assignment_)
85     slot_assignment_ = MakeGarbageCollected<SlotAssignment>(*this);
86   return *slot_assignment_;
87 }
88 
AssignedSlotFor(const Node & node)89 HTMLSlotElement* ShadowRoot::AssignedSlotFor(const Node& node) {
90   if (!slot_assignment_)
91     return nullptr;
92   return slot_assignment_->FindSlot(node);
93 }
94 
DidAddSlot(HTMLSlotElement & slot)95 void ShadowRoot::DidAddSlot(HTMLSlotElement& slot) {
96   DCHECK(IsV1());
97   EnsureSlotAssignment().DidAddSlot(slot);
98 }
99 
DidChangeHostChildSlotName(const AtomicString & old_value,const AtomicString & new_value)100 void ShadowRoot::DidChangeHostChildSlotName(const AtomicString& old_value,
101                                             const AtomicString& new_value) {
102   if (!slot_assignment_)
103     return;
104   slot_assignment_->DidChangeHostChildSlotName(old_value, new_value);
105 }
106 
Clone(Document &,CloneChildrenFlag) const107 Node* ShadowRoot::Clone(Document&, CloneChildrenFlag) const {
108   NOTREACHED() << "ShadowRoot nodes are not clonable.";
109   return nullptr;
110 }
111 
SetSlotAssignmentMode(SlotAssignmentMode assignment_mode)112 void ShadowRoot::SetSlotAssignmentMode(SlotAssignmentMode assignment_mode) {
113   slot_assignment_mode_ = static_cast<unsigned>(assignment_mode);
114 }
115 
innerHTML() const116 String ShadowRoot::innerHTML() const {
117   return CreateMarkup(this, kChildrenOnly);
118 }
119 
SetInnerHTMLInternal(const String & html,const SetInnerHTMLOptions * options,ExceptionState & exception_state)120 void ShadowRoot::SetInnerHTMLInternal(const String& html,
121                                       const SetInnerHTMLOptions* options,
122                                       ExceptionState& exception_state) {
123   bool allow_shadow_root =
124       options->hasAllowShadowRoot() && options->allowShadowRoot();
125   if (DocumentFragment* fragment = CreateFragmentForInnerOuterHTML(
126           html, &host(), kAllowScriptingContent, "innerHTML", allow_shadow_root,
127           exception_state)) {
128     ReplaceChildrenWithFragment(this, fragment, exception_state);
129   }
130 }
131 
setInnerHTML(const String & html,ExceptionState & exception_state)132 void ShadowRoot::setInnerHTML(const String& html,
133                               ExceptionState& exception_state) {
134   const SetInnerHTMLOptions options;
135   SetInnerHTMLInternal(html, &options, exception_state);
136 }
137 
setInnerHTMLWithOptions(const String & html,const SetInnerHTMLOptions * options,ExceptionState & exception_state)138 void ShadowRoot::setInnerHTMLWithOptions(const String& html,
139                                          const SetInnerHTMLOptions* options,
140                                          ExceptionState& exception_state) {
141   DCHECK(RuntimeEnabledFeatures::DeclarativeShadowDOMEnabled(
142       GetExecutionContext()));
143   SetInnerHTMLInternal(html, options, exception_state);
144 }
145 
RebuildLayoutTree(WhitespaceAttacher & whitespace_attacher)146 void ShadowRoot::RebuildLayoutTree(WhitespaceAttacher& whitespace_attacher) {
147   DCHECK(!NeedsReattachLayoutTree());
148   DCHECK(!ChildNeedsReattachLayoutTree());
149   RebuildChildrenLayoutTrees(whitespace_attacher);
150 }
151 
InsertedInto(ContainerNode & insertion_point)152 Node::InsertionNotificationRequest ShadowRoot::InsertedInto(
153     ContainerNode& insertion_point) {
154   DocumentFragment::InsertedInto(insertion_point);
155 
156   if (!insertion_point.isConnected())
157     return kInsertionDone;
158 
159   GetDocument().GetStyleEngine().ShadowRootInsertedToDocument(*this);
160 
161   GetDocument().GetSlotAssignmentEngine().Connected(*this);
162 
163   // FIXME: When parsing <video controls>, InsertedInto() is called many times
164   // without invoking RemovedFrom().  For now, we check
165   // registered_with_parent_shadow_root. We would like to
166   // DCHECK(!registered_with_parent_shadow_root) here.
167   // https://bugs.webkit.org/show_bug.cig?id=101316
168   if (registered_with_parent_shadow_root_)
169     return kInsertionDone;
170 
171   if (ShadowRoot* root = host().ContainingShadowRoot()) {
172     root->AddChildShadowRoot();
173     registered_with_parent_shadow_root_ = true;
174   }
175 
176   return kInsertionDone;
177 }
178 
RemovedFrom(ContainerNode & insertion_point)179 void ShadowRoot::RemovedFrom(ContainerNode& insertion_point) {
180   if (insertion_point.isConnected()) {
181     if (NeedsSlotAssignmentRecalc())
182       GetDocument().GetSlotAssignmentEngine().Disconnected(*this);
183     GetDocument().GetStyleEngine().ShadowRootRemovedFromDocument(this);
184     if (registered_with_parent_shadow_root_) {
185       ShadowRoot* root = host().ContainingShadowRoot();
186       if (!root)
187         root = insertion_point.ContainingShadowRoot();
188       if (root)
189         root->RemoveChildShadowRoot();
190       registered_with_parent_shadow_root_ = false;
191     }
192   }
193 
194   DocumentFragment::RemovedFrom(insertion_point);
195 }
196 
SetNeedsAssignmentRecalc()197 void ShadowRoot::SetNeedsAssignmentRecalc() {
198   DCHECK(IsV1());
199   if (!slot_assignment_)
200     return;
201   return slot_assignment_->SetNeedsAssignmentRecalc();
202 }
203 
NeedsSlotAssignmentRecalc() const204 bool ShadowRoot::NeedsSlotAssignmentRecalc() const {
205   return slot_assignment_ && slot_assignment_->NeedsAssignmentRecalc();
206 }
207 
ChildrenChanged(const ChildrenChange & change)208 void ShadowRoot::ChildrenChanged(const ChildrenChange& change) {
209   ContainerNode::ChildrenChanged(change);
210 
211   if (change.IsChildElementChange()) {
212     CheckForSiblingStyleChanges(
213         change.type == ChildrenChangeType::kElementRemoved
214             ? kSiblingElementRemoved
215             : kSiblingElementInserted,
216         To<Element>(change.sibling_changed), change.sibling_before_change,
217         change.sibling_after_change);
218   }
219 }
220 
StyleSheets()221 StyleSheetList& ShadowRoot::StyleSheets() {
222   if (!style_sheet_list_)
223     SetStyleSheets(MakeGarbageCollected<StyleSheetList>(this));
224   return *style_sheet_list_;
225 }
226 
SetNeedsDistributionRecalcWillBeSetNeedsAssignmentRecalc()227 void ShadowRoot::SetNeedsDistributionRecalcWillBeSetNeedsAssignmentRecalc() {
228   if (IsV1())
229     SetNeedsAssignmentRecalc();
230   else
231     SetNeedsDistributionRecalc();
232 }
233 
SetNeedsDistributionRecalc()234 void ShadowRoot::SetNeedsDistributionRecalc() {
235   DCHECK(!IsV1());
236   if (needs_distribution_recalc_)
237     return;
238   needs_distribution_recalc_ = true;
239   host().MarkAncestorsWithChildNeedsDistributionRecalc();
240 }
241 
Trace(Visitor * visitor) const242 void ShadowRoot::Trace(Visitor* visitor) const {
243   visitor->Trace(style_sheet_list_);
244   visitor->Trace(slot_assignment_);
245   visitor->Trace(shadow_root_v0_);
246   TreeScope::Trace(visitor);
247   DocumentFragment::Trace(visitor);
248 }
249 
operator <<(std::ostream & ostream,const ShadowRootType & type)250 std::ostream& operator<<(std::ostream& ostream, const ShadowRootType& type) {
251   switch (type) {
252     case ShadowRootType::kUserAgent:
253       ostream << "UserAgent";
254       break;
255     case ShadowRootType::V0:
256       ostream << "V0";
257       break;
258     case ShadowRootType::kOpen:
259       ostream << "Open";
260       break;
261     case ShadowRootType::kClosed:
262       ostream << "Closed";
263       break;
264   }
265   return ostream;
266 }
267 
268 }  // namespace blink
269