1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/html/custom/custom_element_reaction_stack.h"
6 
7 #include "third_party/blink/renderer/core/dom/element.h"
8 #include "third_party/blink/renderer/core/html/custom/ce_reactions_scope.h"
9 #include "third_party/blink/renderer/core/html/custom/custom_element_reaction_queue.h"
10 #include "third_party/blink/renderer/platform/bindings/microtask.h"
11 #include "third_party/blink/renderer/platform/heap/persistent.h"
12 
13 namespace blink {
14 
15 namespace {
16 
GetCustomElementReactionStack()17 Persistent<CustomElementReactionStack>& GetCustomElementReactionStack() {
18   DEFINE_STATIC_LOCAL(Persistent<CustomElementReactionStack>,
19                       custom_element_reaction_stack,
20                       (MakeGarbageCollected<CustomElementReactionStack>()));
21   return custom_element_reaction_stack;
22 }
23 
24 }  // namespace
25 
26 // TODO(dominicc): Consider using linked heap structures, avoiding
27 // finalizers, to make short-lived entries fast.
28 
29 CustomElementReactionStack::CustomElementReactionStack() = default;
30 
Trace(Visitor * visitor) const31 void CustomElementReactionStack::Trace(Visitor* visitor) const {
32   visitor->Trace(map_);
33   visitor->Trace(stack_);
34   visitor->Trace(backup_queue_);
35 }
36 
Push()37 void CustomElementReactionStack::Push() {
38   stack_.push_back(nullptr);
39 }
40 
PopInvokingReactions()41 void CustomElementReactionStack::PopInvokingReactions() {
42   ElementQueue* queue = stack_.back();
43   if (queue)
44     InvokeReactions(*queue);
45   stack_.pop_back();
46 }
47 
InvokeReactions(ElementQueue & queue)48 void CustomElementReactionStack::InvokeReactions(ElementQueue& queue) {
49   for (wtf_size_t i = 0; i < queue.size(); ++i) {
50     Element* element = queue[i];
51     if (CustomElementReactionQueue* reactions = map_.at(element)) {
52       reactions->InvokeReactions(*element);
53       CHECK(reactions->IsEmpty());
54       map_.erase(element);
55     }
56   }
57 }
58 
EnqueueToCurrentQueue(Element & element,CustomElementReaction & reaction)59 void CustomElementReactionStack::EnqueueToCurrentQueue(
60     Element& element,
61     CustomElementReaction& reaction) {
62   Enqueue(stack_.back(), element, reaction);
63 }
64 
Enqueue(Member<ElementQueue> & queue,Element & element,CustomElementReaction & reaction)65 void CustomElementReactionStack::Enqueue(Member<ElementQueue>& queue,
66                                          Element& element,
67                                          CustomElementReaction& reaction) {
68   if (!queue)
69     queue = MakeGarbageCollected<ElementQueue>();
70   queue->push_back(&element);
71 
72   CustomElementReactionQueue* reactions = map_.at(&element);
73   if (!reactions) {
74     reactions = MakeGarbageCollected<CustomElementReactionQueue>();
75     map_.insert(&element, reactions);
76   }
77 
78   reactions->Add(reaction);
79 }
80 
EnqueueToBackupQueue(Element & element,CustomElementReaction & reaction)81 void CustomElementReactionStack::EnqueueToBackupQueue(
82     Element& element,
83     CustomElementReaction& reaction) {
84   // https://html.spec.whatwg.org/C/#backup-element-queue
85 
86   DCHECK(!CEReactionsScope::Current());
87   DCHECK(stack_.IsEmpty());
88   DCHECK(IsMainThread());
89 
90   // If the processing the backup element queue is not set:
91   if (!backup_queue_ || backup_queue_->IsEmpty()) {
92     Microtask::EnqueueMicrotask(
93         WTF::Bind(&CustomElementReactionStack::InvokeBackupQueue,
94                   Persistent<CustomElementReactionStack>(this)));
95   }
96 
97   Enqueue(backup_queue_, element, reaction);
98 }
99 
ClearQueue(Element & element)100 void CustomElementReactionStack::ClearQueue(Element& element) {
101   if (CustomElementReactionQueue* reactions = map_.at(&element))
102     reactions->Clear();
103 }
104 
InvokeBackupQueue()105 void CustomElementReactionStack::InvokeBackupQueue() {
106   DCHECK(IsMainThread());
107   InvokeReactions(*backup_queue_);
108   backup_queue_->clear();
109 }
110 
Current()111 CustomElementReactionStack& CustomElementReactionStack::Current() {
112   return *GetCustomElementReactionStack();
113 }
114 
115 CustomElementReactionStack*
SetCurrentForTest(CustomElementReactionStack * new_stack)116 CustomElementReactionStackTestSupport::SetCurrentForTest(
117     CustomElementReactionStack* new_stack) {
118   Persistent<CustomElementReactionStack>& stack =
119       GetCustomElementReactionStack();
120   CustomElementReactionStack* old_stack = stack.Get();
121   stack = new_stack;
122   return old_stack;
123 }
124 
125 }  // namespace blink
126