| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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 "web/RotationViewportAnchor.h" | |
| 6 | |
| 7 #include "core/dom/ContainerNode.h" | |
| 8 #include "core/dom/Node.h" | |
| 9 #include "core/frame/FrameView.h" | |
| 10 #include "core/frame/LocalFrame.h" | |
| 11 #include "core/frame/PageScaleConstraintsSet.h" | |
| 12 #include "core/frame/RootFrameViewport.h" | |
| 13 #include "core/frame/VisualViewport.h" | |
| 14 #include "core/input/EventHandler.h" | |
| 15 #include "core/layout/HitTestResult.h" | |
| 16 #include "platform/geometry/DoubleRect.h" | |
| 17 | |
| 18 namespace blink { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 static const float kViewportAnchorRelativeEpsilon = 0.1f; | |
| 23 static const int kViewportToNodeMaxRelativeArea = 2; | |
| 24 | |
| 25 Node* FindNonEmptyAnchorNode(const IntPoint& point, | |
| 26 const IntRect& view_rect, | |
| 27 EventHandler& event_handler) { | |
| 28 Node* node = event_handler | |
| 29 .HitTestResultAtPoint(point, HitTestRequest::kReadOnly | | |
| 30 HitTestRequest::kActive) | |
| 31 .InnerNode(); | |
| 32 | |
| 33 // If the node bounding box is sufficiently large, make a single attempt to | |
| 34 // find a smaller node; the larger the node bounds, the greater the | |
| 35 // variability under resize. | |
| 36 const int max_node_area = | |
| 37 view_rect.Width() * view_rect.Height() * kViewportToNodeMaxRelativeArea; | |
| 38 if (node && node->BoundingBox().Width() * node->BoundingBox().Height() > | |
| 39 max_node_area) { | |
| 40 IntSize point_offset = view_rect.Size(); | |
| 41 point_offset.Scale(kViewportAnchorRelativeEpsilon); | |
| 42 node = event_handler | |
| 43 .HitTestResultAtPoint( | |
| 44 point + point_offset, | |
| 45 HitTestRequest::kReadOnly | HitTestRequest::kActive) | |
| 46 .InnerNode(); | |
| 47 } | |
| 48 | |
| 49 while (node && node->BoundingBox().IsEmpty()) | |
| 50 node = node->parentNode(); | |
| 51 | |
| 52 return node; | |
| 53 } | |
| 54 | |
| 55 void MoveToEncloseRect(IntRect& outer, const FloatRect& inner) { | |
| 56 IntPoint minimum_position = | |
| 57 CeiledIntPoint(inner.Location() + inner.Size() - FloatSize(outer.Size())); | |
| 58 IntPoint maximum_position = FlooredIntPoint(inner.Location()); | |
| 59 | |
| 60 IntPoint outer_origin = outer.Location(); | |
| 61 outer_origin = outer_origin.ExpandedTo(minimum_position); | |
| 62 outer_origin = outer_origin.ShrunkTo(maximum_position); | |
| 63 | |
| 64 outer.SetLocation(outer_origin); | |
| 65 } | |
| 66 | |
| 67 void MoveIntoRect(FloatRect& inner, const IntRect& outer) { | |
| 68 FloatPoint minimum_position = FloatPoint(outer.Location()); | |
| 69 FloatPoint maximum_position = minimum_position + outer.Size() - inner.Size(); | |
| 70 | |
| 71 // Adjust maximumPosition to the nearest lower integer because | |
| 72 // VisualViewport::maximumScrollPosition() does the same. | |
| 73 // The value of minumumPosition is already adjusted since it is | |
| 74 // constructed from an integer point. | |
| 75 maximum_position = FlooredIntPoint(maximum_position); | |
| 76 | |
| 77 FloatPoint inner_origin = inner.Location(); | |
| 78 inner_origin = inner_origin.ExpandedTo(minimum_position); | |
| 79 inner_origin = inner_origin.ShrunkTo(maximum_position); | |
| 80 | |
| 81 inner.SetLocation(inner_origin); | |
| 82 } | |
| 83 | |
| 84 } // namespace | |
| 85 | |
| 86 RotationViewportAnchor::RotationViewportAnchor( | |
| 87 FrameView& root_frame_view, | |
| 88 VisualViewport& visual_viewport, | |
| 89 const FloatSize& anchor_in_inner_view_coords, | |
| 90 PageScaleConstraintsSet& page_scale_constraints_set) | |
| 91 : root_frame_view_(&root_frame_view), | |
| 92 visual_viewport_(&visual_viewport), | |
| 93 anchor_in_inner_view_coords_(anchor_in_inner_view_coords), | |
| 94 page_scale_constraints_set_(page_scale_constraints_set) { | |
| 95 SetAnchor(); | |
| 96 } | |
| 97 | |
| 98 RotationViewportAnchor::~RotationViewportAnchor() { | |
| 99 RestoreToAnchor(); | |
| 100 } | |
| 101 | |
| 102 void RotationViewportAnchor::SetAnchor() { | |
| 103 RootFrameViewport* root_frame_viewport = | |
| 104 root_frame_view_->GetRootFrameViewport(); | |
| 105 DCHECK(root_frame_viewport); | |
| 106 | |
| 107 old_page_scale_factor_ = visual_viewport_->Scale(); | |
| 108 old_minimum_page_scale_factor_ = | |
| 109 page_scale_constraints_set_.FinalConstraints().minimum_scale; | |
| 110 | |
| 111 // Save the absolute location in case we won't find the anchor node, we'll | |
| 112 // fall back to that. | |
| 113 visual_viewport_in_document_ = | |
| 114 FloatPoint(root_frame_viewport->VisibleContentRect().Location()); | |
| 115 | |
| 116 anchor_node_.Clear(); | |
| 117 anchor_node_bounds_ = LayoutRect(); | |
| 118 anchor_in_node_coords_ = FloatSize(); | |
| 119 normalized_visual_viewport_offset_ = FloatSize(); | |
| 120 | |
| 121 IntRect inner_view_rect = root_frame_viewport->VisibleContentRect(); | |
| 122 | |
| 123 // Preserve origins at the absolute screen origin. | |
| 124 if (inner_view_rect.Location() == IntPoint::Zero() || | |
| 125 inner_view_rect.IsEmpty()) | |
| 126 return; | |
| 127 | |
| 128 IntRect outer_view_rect = | |
| 129 LayoutViewport().VisibleContentRect(kIncludeScrollbars); | |
| 130 | |
| 131 // Normalize by the size of the outer rect | |
| 132 DCHECK(!outer_view_rect.IsEmpty()); | |
| 133 normalized_visual_viewport_offset_ = visual_viewport_->GetScrollOffset(); | |
| 134 normalized_visual_viewport_offset_.Scale(1.0 / outer_view_rect.Width(), | |
| 135 1.0 / outer_view_rect.Height()); | |
| 136 | |
| 137 // Note, we specifically use the unscaled visual viewport size here as the | |
| 138 // conversion into content-space below will apply the scale. | |
| 139 FloatPoint anchor_offset(visual_viewport_->Size()); | |
| 140 anchor_offset.Scale(anchor_in_inner_view_coords_.Width(), | |
| 141 anchor_in_inner_view_coords_.Height()); | |
| 142 | |
| 143 // Note, we specifically convert to the rootFrameView contents here, rather | |
| 144 // than the layout viewport. That's because hit testing works from the | |
| 145 // FrameView's content coordinates even if it's not the layout viewport. | |
| 146 const FloatPoint anchor_point_in_contents = root_frame_view_->FrameToContents( | |
| 147 visual_viewport_->ViewportToRootFrame(anchor_offset)); | |
| 148 | |
| 149 Node* node = FindNonEmptyAnchorNode( | |
| 150 FlooredIntPoint(anchor_point_in_contents), inner_view_rect, | |
| 151 root_frame_view_->GetFrame().GetEventHandler()); | |
| 152 if (!node) | |
| 153 return; | |
| 154 | |
| 155 anchor_node_ = node; | |
| 156 anchor_node_bounds_ = node->BoundingBox(); | |
| 157 anchor_in_node_coords_ = | |
| 158 anchor_point_in_contents - FloatPoint(anchor_node_bounds_.Location()); | |
| 159 anchor_in_node_coords_.Scale(1.f / anchor_node_bounds_.Width(), | |
| 160 1.f / anchor_node_bounds_.Height()); | |
| 161 } | |
| 162 | |
| 163 void RotationViewportAnchor::RestoreToAnchor() { | |
| 164 float new_page_scale_factor = | |
| 165 old_page_scale_factor_ / old_minimum_page_scale_factor_ * | |
| 166 page_scale_constraints_set_.FinalConstraints().minimum_scale; | |
| 167 new_page_scale_factor = | |
| 168 page_scale_constraints_set_.FinalConstraints().ClampToConstraints( | |
| 169 new_page_scale_factor); | |
| 170 | |
| 171 FloatSize visual_viewport_size(visual_viewport_->Size()); | |
| 172 visual_viewport_size.Scale(1 / new_page_scale_factor); | |
| 173 | |
| 174 IntPoint main_frame_origin; | |
| 175 FloatPoint visual_viewport_origin; | |
| 176 | |
| 177 ComputeOrigins(visual_viewport_size, main_frame_origin, | |
| 178 visual_viewport_origin); | |
| 179 | |
| 180 LayoutViewport().SetScrollOffset(ToScrollOffset(main_frame_origin), | |
| 181 kProgrammaticScroll); | |
| 182 | |
| 183 // Set scale before location, since location can be clamped on setting scale. | |
| 184 visual_viewport_->SetScale(new_page_scale_factor); | |
| 185 visual_viewport_->SetLocation(visual_viewport_origin); | |
| 186 } | |
| 187 | |
| 188 ScrollableArea& RotationViewportAnchor::LayoutViewport() const { | |
| 189 RootFrameViewport* root_frame_viewport = | |
| 190 root_frame_view_->GetRootFrameViewport(); | |
| 191 DCHECK(root_frame_viewport); | |
| 192 return root_frame_viewport->LayoutViewport(); | |
| 193 } | |
| 194 | |
| 195 void RotationViewportAnchor::ComputeOrigins( | |
| 196 const FloatSize& inner_size, | |
| 197 IntPoint& main_frame_offset, | |
| 198 FloatPoint& visual_viewport_offset) const { | |
| 199 IntSize outer_size = LayoutViewport().VisibleContentRect().Size(); | |
| 200 | |
| 201 // Compute the viewport origins in CSS pixels relative to the document. | |
| 202 FloatSize abs_visual_viewport_offset = normalized_visual_viewport_offset_; | |
| 203 abs_visual_viewport_offset.Scale(outer_size.Width(), outer_size.Height()); | |
| 204 | |
| 205 FloatPoint inner_origin = GetInnerOrigin(inner_size); | |
| 206 FloatPoint outer_origin = inner_origin - abs_visual_viewport_offset; | |
| 207 | |
| 208 IntRect outer_rect = IntRect(FlooredIntPoint(outer_origin), outer_size); | |
| 209 FloatRect inner_rect = FloatRect(inner_origin, inner_size); | |
| 210 | |
| 211 MoveToEncloseRect(outer_rect, inner_rect); | |
| 212 | |
| 213 outer_rect.SetLocation(IntPoint( | |
| 214 LayoutViewport().ClampScrollOffset(ToIntSize(outer_rect.Location())))); | |
| 215 | |
| 216 MoveIntoRect(inner_rect, outer_rect); | |
| 217 | |
| 218 main_frame_offset = outer_rect.Location(); | |
| 219 visual_viewport_offset = | |
| 220 FloatPoint(inner_rect.Location() - outer_rect.Location()); | |
| 221 } | |
| 222 | |
| 223 FloatPoint RotationViewportAnchor::GetInnerOrigin( | |
| 224 const FloatSize& inner_size) const { | |
| 225 if (!anchor_node_ || !anchor_node_->isConnected()) | |
| 226 return visual_viewport_in_document_; | |
| 227 | |
| 228 const LayoutRect current_node_bounds = anchor_node_->BoundingBox(); | |
| 229 if (anchor_node_bounds_ == current_node_bounds) | |
| 230 return visual_viewport_in_document_; | |
| 231 | |
| 232 RootFrameViewport* root_frame_viewport = | |
| 233 root_frame_view_->GetRootFrameViewport(); | |
| 234 const LayoutRect current_node_bounds_in_layout_viewport = | |
| 235 root_frame_viewport->RootContentsToLayoutViewportContents( | |
| 236 *root_frame_view_.Get(), current_node_bounds); | |
| 237 | |
| 238 // Compute the new anchor point relative to the node position | |
| 239 FloatSize anchor_offset_from_node( | |
| 240 current_node_bounds_in_layout_viewport.Size()); | |
| 241 anchor_offset_from_node.Scale(anchor_in_node_coords_.Width(), | |
| 242 anchor_in_node_coords_.Height()); | |
| 243 FloatPoint anchor_point = | |
| 244 FloatPoint(current_node_bounds_in_layout_viewport.Location()) + | |
| 245 anchor_offset_from_node; | |
| 246 | |
| 247 // Compute the new origin point relative to the new anchor point | |
| 248 FloatSize anchor_offset_from_origin = inner_size; | |
| 249 anchor_offset_from_origin.Scale(anchor_in_inner_view_coords_.Width(), | |
| 250 anchor_in_inner_view_coords_.Height()); | |
| 251 return anchor_point - anchor_offset_from_origin; | |
| 252 } | |
| 253 | |
| 254 } // namespace blink | |
| OLD | NEW |