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