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/resize_observer/resize_observer.h"
6
7 #include "third_party/blink/renderer/bindings/core/v8/v8_resize_observer_callback.h"
8 #include "third_party/blink/renderer/bindings/core/v8/v8_resize_observer_options.h"
9 #include "third_party/blink/renderer/core/dom/element.h"
10 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
11 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
12 #include "third_party/blink/renderer/core/layout/layout_object.h"
13 #include "third_party/blink/renderer/core/resize_observer/resize_observation.h"
14 #include "third_party/blink/renderer/core/resize_observer/resize_observer_controller.h"
15 #include "third_party/blink/renderer/core/resize_observer/resize_observer_entry.h"
16
17 namespace blink {
18
19 constexpr const char* kBoxOptionBorderBox = "border-box";
20 constexpr const char* kBoxOptionContentBox = "content-box";
21 constexpr const char* kBoxOptionDevicePixelContentBox =
22 "device-pixel-content-box";
23
Create(Document & document,V8ResizeObserverCallback * callback)24 ResizeObserver* ResizeObserver::Create(Document& document,
25 V8ResizeObserverCallback* callback) {
26 return MakeGarbageCollected<ResizeObserver>(callback, document);
27 }
28
Create(Document & document,Delegate * delegate)29 ResizeObserver* ResizeObserver::Create(Document& document, Delegate* delegate) {
30 return MakeGarbageCollected<ResizeObserver>(delegate, document);
31 }
32
ResizeObserver(V8ResizeObserverCallback * callback,Document & document)33 ResizeObserver::ResizeObserver(V8ResizeObserverCallback* callback,
34 Document& document)
35 : ExecutionContextClient(document.ToExecutionContext()),
36 callback_(callback),
37 skipped_observations_(false) {
38 DCHECK(callback_);
39 controller_ = &document.EnsureResizeObserverController();
40 controller_->AddObserver(*this);
41 }
42
ResizeObserver(Delegate * delegate,Document & document)43 ResizeObserver::ResizeObserver(Delegate* delegate, Document& document)
44 : ExecutionContextClient(document.ToExecutionContext()),
45 delegate_(delegate),
46 skipped_observations_(false) {
47 DCHECK(delegate_);
48 controller_ = &document.EnsureResizeObserverController();
49 controller_->AddObserver(*this);
50 }
51
ParseBoxOptions(const String & box_options)52 ResizeObserverBoxOptions ResizeObserver::ParseBoxOptions(
53 const String& box_options) {
54 if (box_options == kBoxOptionBorderBox)
55 return ResizeObserverBoxOptions::BorderBox;
56 if (box_options == kBoxOptionContentBox)
57 return ResizeObserverBoxOptions::ContentBox;
58 if (box_options == kBoxOptionDevicePixelContentBox)
59 return ResizeObserverBoxOptions::DevicePixelContentBox;
60 return ResizeObserverBoxOptions::ContentBox;
61 }
62
observeInternal(Element * target,ResizeObserverBoxOptions box_option)63 void ResizeObserver::observeInternal(Element* target,
64 ResizeObserverBoxOptions box_option) {
65 auto& observer_map = target->EnsureResizeObserverData();
66
67 if (observer_map.Contains(this)) {
68 auto observation = observer_map.find(this);
69 if ((*observation).value->observedBox() == box_option)
70 return;
71
72 // Unobserve target if box_option has changed and target already existed. If
73 // there is an existing observation of a different box, this new observation
74 // takes precedence. See:
75 // https://drafts.csswg.org/resize-observer/#processing-model
76 observations_.erase((*observation).value);
77 auto index = active_observations_.Find((*observation).value);
78 if (index != kNotFound) {
79 active_observations_.EraseAt(index);
80 }
81 observer_map.erase(observation);
82 }
83
84 auto* observation =
85 MakeGarbageCollected<ResizeObservation>(target, this, box_option);
86 observations_.insert(observation);
87 observer_map.Set(this, observation);
88
89 if (LocalFrameView* frame_view = target->GetDocument().View())
90 frame_view->ScheduleAnimation();
91 }
92
observe(Element * target,const ResizeObserverOptions * options)93 void ResizeObserver::observe(Element* target,
94 const ResizeObserverOptions* options) {
95 ResizeObserverBoxOptions box_option = ParseBoxOptions(options->box());
96 observeInternal(target, box_option);
97 }
98
observe(Element * target)99 void ResizeObserver::observe(Element* target) {
100 observeInternal(target, ResizeObserverBoxOptions::ContentBox);
101 }
102
unobserve(Element * target)103 void ResizeObserver::unobserve(Element* target) {
104 auto* observer_map = target ? target->ResizeObserverData() : nullptr;
105 if (!observer_map)
106 return;
107 auto observation = observer_map->find(this);
108 if (observation != observer_map->end()) {
109 observations_.erase((*observation).value);
110 auto index = active_observations_.Find((*observation).value);
111 if (index != kNotFound) {
112 active_observations_.EraseAt(index);
113 }
114 observer_map->erase(observation);
115 }
116 }
117
disconnect()118 void ResizeObserver::disconnect() {
119 ObservationList observations;
120 observations_.Swap(observations);
121
122 for (auto& observation : observations) {
123 Element* target = (*observation).Target();
124 if (target)
125 target->EnsureResizeObserverData().erase(this);
126 }
127 ClearObservations();
128 }
129
GatherObservations(size_t deeper_than)130 size_t ResizeObserver::GatherObservations(size_t deeper_than) {
131 DCHECK(active_observations_.IsEmpty());
132
133 size_t min_observed_depth = ResizeObserverController::kDepthBottom;
134 for (auto& observation : observations_) {
135 if (!observation->ObservationSizeOutOfSync())
136 continue;
137 auto depth = observation->TargetDepth();
138 if (depth > deeper_than) {
139 active_observations_.push_back(*observation);
140 min_observed_depth = std::min(min_observed_depth, depth);
141 } else {
142 skipped_observations_ = true;
143 }
144 }
145 return min_observed_depth;
146 }
147
DeliverObservations()148 void ResizeObserver::DeliverObservations() {
149 if (active_observations_.IsEmpty())
150 return;
151
152 HeapVector<Member<ResizeObserverEntry>> entries;
153
154 for (auto& observation : active_observations_) {
155 // In case that the observer and the target belong to different execution
156 // contexts and the target's execution context is already gone, then skip
157 // such a target.
158 ExecutionContext* execution_context =
159 observation->Target()->GetExecutionContext();
160 if (!execution_context || execution_context->IsContextDestroyed())
161 continue;
162
163 observation->SetObservationSize(observation->ComputeTargetSize());
164 auto* entry =
165 MakeGarbageCollected<ResizeObserverEntry>(observation->Target());
166 entries.push_back(entry);
167 }
168
169 if (entries.size() == 0) {
170 // No entry to report.
171 // Note that, if |active_observations_| is not empty but |entries| is empty,
172 // it means that it's possible that no target element is making |callback_|
173 // alive. In this case, we must not touch |callback_|.
174 ClearObservations();
175 return;
176 }
177
178 DCHECK(callback_ || delegate_);
179 if (callback_)
180 callback_->InvokeAndReportException(this, entries, this);
181 if (delegate_)
182 delegate_->OnResize(entries);
183 ClearObservations();
184 }
185
ClearObservations()186 void ResizeObserver::ClearObservations() {
187 active_observations_.clear();
188 skipped_observations_ = false;
189 }
190
HasPendingActivity() const191 bool ResizeObserver::HasPendingActivity() const {
192 return !observations_.IsEmpty();
193 }
194
Trace(Visitor * visitor)195 void ResizeObserver::Trace(Visitor* visitor) {
196 visitor->Trace(callback_);
197 visitor->Trace(delegate_);
198 visitor->Trace(observations_);
199 visitor->Trace(active_observations_);
200 visitor->Trace(controller_);
201 ScriptWrappable::Trace(visitor);
202 ExecutionContextClient::Trace(visitor);
203 }
204
205 } // namespace blink
206