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