OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights | 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights |
4 * reserved. | 4 * reserved. |
5 * | 5 * |
6 * This library is free software; you can redistribute it and/or | 6 * This library is free software; you can redistribute it and/or |
7 * modify it under the terms of the GNU Library General Public | 7 * modify it under the terms of the GNU Library General Public |
8 * License as published by the Free Software Foundation; either | 8 * License as published by the Free Software Foundation; either |
9 * version 2 of the License, or (at your option) any later version. | 9 * version 2 of the License, or (at your option) any later version. |
10 * | 10 * |
(...skipping 10 matching lines...) Expand all Loading... |
21 | 21 |
22 #include "core/editing/LayoutSelection.h" | 22 #include "core/editing/LayoutSelection.h" |
23 | 23 |
24 #include "core/dom/Document.h" | 24 #include "core/dom/Document.h" |
25 #include "core/editing/EditingUtilities.h" | 25 #include "core/editing/EditingUtilities.h" |
26 #include "core/editing/FrameSelection.h" | 26 #include "core/editing/FrameSelection.h" |
27 #include "core/editing/VisiblePosition.h" | 27 #include "core/editing/VisiblePosition.h" |
28 #include "core/editing/VisibleUnits.h" | 28 #include "core/editing/VisibleUnits.h" |
29 #include "core/html/TextControlElement.h" | 29 #include "core/html/TextControlElement.h" |
30 #include "core/layout/LayoutView.h" | 30 #include "core/layout/LayoutView.h" |
| 31 #include "core/paint/PaintLayer.h" |
31 | 32 |
32 namespace blink { | 33 namespace blink { |
33 | 34 |
34 LayoutSelection::LayoutSelection(FrameSelection& frame_selection) | 35 LayoutSelection::LayoutSelection(FrameSelection& frame_selection) |
35 : frame_selection_(&frame_selection), | 36 : frame_selection_(&frame_selection), |
36 has_pending_selection_(false), | 37 has_pending_selection_(false), |
37 selection_start_(nullptr), | 38 selection_start_(nullptr), |
38 selection_end_(nullptr), | 39 selection_end_(nullptr), |
39 selection_start_pos_(-1), | 40 selection_start_pos_(-1), |
40 selection_end_pos_(-1) {} | 41 selection_end_pos_(-1) {} |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 ? TextAffinity::kUpstream | 105 ? TextAffinity::kUpstream |
105 : affinity); | 106 : affinity); |
106 if (visible_end.IsNull()) | 107 if (visible_end.IsNull()) |
107 return SelectionInFlatTree(); | 108 return SelectionInFlatTree(); |
108 SelectionInFlatTree::Builder builder; | 109 SelectionInFlatTree::Builder builder; |
109 builder.Collapse(visible_start.ToPositionWithAffinity()); | 110 builder.Collapse(visible_start.ToPositionWithAffinity()); |
110 builder.Extend(visible_end.DeepEquivalent()); | 111 builder.Extend(visible_end.DeepEquivalent()); |
111 return builder.Build(); | 112 return builder.Build(); |
112 } | 113 } |
113 | 114 |
| 115 static LayoutObject* LayoutObjectAfterPosition(LayoutObject* object, |
| 116 unsigned offset) { |
| 117 if (!object) |
| 118 return nullptr; |
| 119 |
| 120 LayoutObject* child = object->ChildAt(offset); |
| 121 return child ? child : object->NextInPreOrderAfterChildren(); |
| 122 } |
| 123 |
| 124 // When exploring the LayoutTree looking for the nodes involved in the |
| 125 // Selection, sometimes it's required to change the traversing direction because |
| 126 // the "start" position is below the "end" one. |
| 127 static inline LayoutObject* GetNextOrPrevLayoutObjectBasedOnDirection( |
| 128 const LayoutObject* o, |
| 129 const LayoutObject* stop, |
| 130 bool& continue_exploring, |
| 131 bool& exploring_backwards) { |
| 132 LayoutObject* next; |
| 133 if (exploring_backwards) { |
| 134 next = o->PreviousInPreOrder(); |
| 135 continue_exploring = next && !(next)->IsLayoutView(); |
| 136 } else { |
| 137 next = o->NextInPreOrder(); |
| 138 continue_exploring = next && next != stop; |
| 139 exploring_backwards = !next && (next != stop); |
| 140 if (exploring_backwards) { |
| 141 next = stop->PreviousInPreOrder(); |
| 142 continue_exploring = next && !next->IsLayoutView(); |
| 143 } |
| 144 } |
| 145 |
| 146 return next; |
| 147 } |
| 148 |
| 149 void LayoutSelection::SetSelection( |
| 150 LayoutObject* start, |
| 151 int start_pos, |
| 152 LayoutObject* end, |
| 153 int end_pos, |
| 154 SelectionPaintInvalidationMode block_paint_invalidation_mode) { |
| 155 // This code makes no assumptions as to if the layout tree is up to date or |
| 156 // not and will not try to update it. Currently clearSelection calls this |
| 157 // (intentionally) without updating the layout tree as it doesn't care. |
| 158 // Other callers may want to force recalc style before calling this. |
| 159 |
| 160 // Make sure both our start and end objects are defined. |
| 161 // Check www.msnbc.com and try clicking around to find the case where this |
| 162 // happened. |
| 163 if ((start && !end) || (end && !start)) |
| 164 return; |
| 165 |
| 166 // Just return if the selection hasn't changed. |
| 167 if (selection_start_ == start && selection_start_pos_ == start_pos && |
| 168 selection_end_ == end && selection_end_pos_ == end_pos) |
| 169 return; |
| 170 |
| 171 // Record the old selected objects. These will be used later when we compare |
| 172 // against the new selected objects. |
| 173 int old_start_pos = selection_start_pos_; |
| 174 int old_end_pos = selection_end_pos_; |
| 175 |
| 176 // Objects each have a single selection rect to examine. |
| 177 typedef HashMap<LayoutObject*, SelectionState> SelectedObjectMap; |
| 178 SelectedObjectMap old_selected_objects; |
| 179 // FIXME: |newSelectedObjects| doesn't really need to store the |
| 180 // SelectionState, it's just more convenient to have it use the same data |
| 181 // structure as |oldSelectedObjects|. |
| 182 SelectedObjectMap new_selected_objects; |
| 183 |
| 184 // Blocks contain selected objects and fill gaps between them, either on the |
| 185 // left, right, or in between lines and blocks. |
| 186 // In order to get the visual rect right, we have to examine left, middle, and |
| 187 // right rects individually, since otherwise the union of those rects might |
| 188 // remain the same even when changes have occurred. |
| 189 typedef HashMap<LayoutBlock*, SelectionState> SelectedBlockMap; |
| 190 SelectedBlockMap old_selected_blocks; |
| 191 // FIXME: |newSelectedBlocks| doesn't really need to store the SelectionState, |
| 192 // it's just more convenient to have it use the same data structure as |
| 193 // |oldSelectedBlocks|. |
| 194 SelectedBlockMap new_selected_blocks; |
| 195 |
| 196 LayoutObject* os = selection_start_; |
| 197 LayoutObject* stop = |
| 198 LayoutObjectAfterPosition(selection_end_, selection_end_pos_); |
| 199 bool exploring_backwards = false; |
| 200 bool continue_exploring = os && (os != stop); |
| 201 while (continue_exploring) { |
| 202 if ((os->CanBeSelectionLeaf() || os == selection_start_ || |
| 203 os == selection_end_) && |
| 204 os->GetSelectionState() != SelectionNone) { |
| 205 // Blocks are responsible for painting line gaps and margin gaps. They |
| 206 // must be examined as well. |
| 207 old_selected_objects.Set(os, os->GetSelectionState()); |
| 208 if (block_paint_invalidation_mode == kPaintInvalidationNewXOROld) { |
| 209 LayoutBlock* cb = os->ContainingBlock(); |
| 210 while (cb && !cb->IsLayoutView()) { |
| 211 SelectedBlockMap::AddResult result = |
| 212 old_selected_blocks.insert(cb, cb->GetSelectionState()); |
| 213 if (!result.is_new_entry) |
| 214 break; |
| 215 cb = cb->ContainingBlock(); |
| 216 } |
| 217 } |
| 218 } |
| 219 |
| 220 os = GetNextOrPrevLayoutObjectBasedOnDirection(os, stop, continue_exploring, |
| 221 exploring_backwards); |
| 222 } |
| 223 |
| 224 // Now clear the selection. |
| 225 SelectedObjectMap::iterator old_objects_end = old_selected_objects.end(); |
| 226 for (SelectedObjectMap::iterator i = old_selected_objects.begin(); |
| 227 i != old_objects_end; ++i) |
| 228 i->key->SetSelectionStateIfNeeded(SelectionNone); |
| 229 |
| 230 // set selection start and end |
| 231 selection_start_ = start; |
| 232 selection_start_pos_ = start_pos; |
| 233 selection_end_ = end; |
| 234 selection_end_pos_ = end_pos; |
| 235 |
| 236 // Update the selection status of all objects between m_selectionStart and |
| 237 // m_selectionEnd |
| 238 if (start && start == end) { |
| 239 start->SetSelectionStateIfNeeded(SelectionBoth); |
| 240 } else { |
| 241 if (start) |
| 242 start->SetSelectionStateIfNeeded(SelectionStart); |
| 243 if (end) |
| 244 end->SetSelectionStateIfNeeded(SelectionEnd); |
| 245 } |
| 246 |
| 247 LayoutObject* o = start; |
| 248 stop = LayoutObjectAfterPosition(end, end_pos); |
| 249 |
| 250 while (o && o != stop) { |
| 251 if (o != start && o != end && o->CanBeSelectionLeaf()) |
| 252 o->SetSelectionStateIfNeeded(SelectionInside); |
| 253 o = o->NextInPreOrder(); |
| 254 } |
| 255 |
| 256 // Now that the selection state has been updated for the new objects, walk |
| 257 // them again and put them in the new objects list. |
| 258 o = start; |
| 259 exploring_backwards = false; |
| 260 continue_exploring = o && (o != stop); |
| 261 while (continue_exploring) { |
| 262 if ((o->CanBeSelectionLeaf() || o == start || o == end) && |
| 263 o->GetSelectionState() != SelectionNone) { |
| 264 new_selected_objects.Set(o, o->GetSelectionState()); |
| 265 LayoutBlock* cb = o->ContainingBlock(); |
| 266 while (cb && !cb->IsLayoutView()) { |
| 267 SelectedBlockMap::AddResult result = |
| 268 new_selected_blocks.insert(cb, cb->GetSelectionState()); |
| 269 if (!result.is_new_entry) |
| 270 break; |
| 271 cb = cb->ContainingBlock(); |
| 272 } |
| 273 } |
| 274 |
| 275 o = GetNextOrPrevLayoutObjectBasedOnDirection(o, stop, continue_exploring, |
| 276 exploring_backwards); |
| 277 } |
| 278 |
| 279 // TODO(yoichio): DCHECK(frame_selection_->,,,->GetFrameView()); |
| 280 if (!frame_selection_->GetDocument().GetLayoutView()->GetFrameView()) |
| 281 return; |
| 282 |
| 283 // Have any of the old selected objects changed compared to the new selection? |
| 284 for (SelectedObjectMap::iterator i = old_selected_objects.begin(); |
| 285 i != old_objects_end; ++i) { |
| 286 LayoutObject* obj = i->key; |
| 287 SelectionState new_selection_state = obj->GetSelectionState(); |
| 288 SelectionState old_selection_state = i->value; |
| 289 if (new_selection_state != old_selection_state || |
| 290 (selection_start_ == obj && old_start_pos != selection_start_pos_) || |
| 291 (selection_end_ == obj && old_end_pos != selection_end_pos_)) { |
| 292 obj->SetShouldInvalidateSelection(); |
| 293 new_selected_objects.erase(obj); |
| 294 } |
| 295 } |
| 296 |
| 297 // Any new objects that remain were not found in the old objects dict, and so |
| 298 // they need to be updated. |
| 299 SelectedObjectMap::iterator new_objects_end = new_selected_objects.end(); |
| 300 for (SelectedObjectMap::iterator i = new_selected_objects.begin(); |
| 301 i != new_objects_end; ++i) |
| 302 i->key->SetShouldInvalidateSelection(); |
| 303 |
| 304 // Have any of the old blocks changed? |
| 305 SelectedBlockMap::iterator old_blocks_end = old_selected_blocks.end(); |
| 306 for (SelectedBlockMap::iterator i = old_selected_blocks.begin(); |
| 307 i != old_blocks_end; ++i) { |
| 308 LayoutBlock* block = i->key; |
| 309 SelectionState new_selection_state = block->GetSelectionState(); |
| 310 SelectionState old_selection_state = i->value; |
| 311 if (new_selection_state != old_selection_state) { |
| 312 block->SetShouldInvalidateSelection(); |
| 313 new_selected_blocks.erase(block); |
| 314 } |
| 315 } |
| 316 |
| 317 // Any new blocks that remain were not found in the old blocks dict, and so |
| 318 // they need to be updated. |
| 319 SelectedBlockMap::iterator new_blocks_end = new_selected_blocks.end(); |
| 320 for (SelectedBlockMap::iterator i = new_selected_blocks.begin(); |
| 321 i != new_blocks_end; ++i) |
| 322 i->key->SetShouldInvalidateSelection(); |
| 323 } |
| 324 |
| 325 void LayoutSelection::SelectionStartEnd(int& start_pos, int& end_pos) { |
| 326 Commit(*frame_selection_->GetDocument().GetLayoutView()); |
| 327 start_pos = selection_start_pos_; |
| 328 end_pos = selection_end_pos_; |
| 329 } |
| 330 |
| 331 void LayoutSelection::ClearSelection() { |
| 332 // For querying Layer::compositingState() |
| 333 // This is correct, since destroying layout objects needs to cause eager paint |
| 334 // invalidations. |
| 335 DisableCompositingQueryAsserts disabler; |
| 336 |
| 337 SetSelection(0, -1, 0, -1, kPaintInvalidationNewMinusOld); |
| 338 } |
| 339 |
114 void LayoutSelection::Commit(LayoutView& layout_view) { | 340 void LayoutSelection::Commit(LayoutView& layout_view) { |
115 if (!HasPendingSelection()) | 341 if (!HasPendingSelection()) |
116 return; | 342 return; |
117 DCHECK(!layout_view.NeedsLayout()); | 343 DCHECK(!layout_view.NeedsLayout()); |
118 has_pending_selection_ = false; | 344 has_pending_selection_ = false; |
119 | 345 |
120 const VisibleSelectionInFlatTree& original_selection = | 346 const VisibleSelectionInFlatTree& original_selection = |
121 frame_selection_->ComputeVisibleSelectionInFlatTree(); | 347 frame_selection_->ComputeVisibleSelectionInFlatTree(); |
122 | 348 |
123 // Skip if pending VisibilePositions became invalid before we reach here. | 349 // Skip if pending VisibilePositions became invalid before we reach here. |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 } | 395 } |
170 | 396 |
171 void LayoutSelection::OnDocumentShutdown() { | 397 void LayoutSelection::OnDocumentShutdown() { |
172 has_pending_selection_ = false; | 398 has_pending_selection_ = false; |
173 selection_start_ = nullptr; | 399 selection_start_ = nullptr; |
174 selection_end_ = nullptr; | 400 selection_end_ = nullptr; |
175 selection_start_pos_ = -1; | 401 selection_start_pos_ = -1; |
176 selection_end_pos_ = -1; | 402 selection_end_pos_ = -1; |
177 } | 403 } |
178 | 404 |
| 405 static LayoutRect SelectionRectForLayoutObject(const LayoutObject* object) { |
| 406 if (!object->IsRooted()) |
| 407 return LayoutRect(); |
| 408 |
| 409 if (!object->CanUpdateSelectionOnRootLineBoxes()) |
| 410 return LayoutRect(); |
| 411 |
| 412 return object->SelectionRectInViewCoordinates(); |
| 413 } |
| 414 |
| 415 IntRect LayoutSelection::SelectionBounds() { |
| 416 // Now create a single bounding box rect that encloses the whole selection. |
| 417 LayoutRect sel_rect; |
| 418 |
| 419 typedef HashSet<const LayoutBlock*> VisitedContainingBlockSet; |
| 420 VisitedContainingBlockSet visited_containing_blocks; |
| 421 |
| 422 Commit(*frame_selection_->GetDocument().GetLayoutView()); |
| 423 LayoutObject* os = selection_start_; |
| 424 LayoutObject* stop = |
| 425 LayoutObjectAfterPosition(selection_end_, selection_end_pos_); |
| 426 while (os && os != stop) { |
| 427 if ((os->CanBeSelectionLeaf() || os == selection_start_ || |
| 428 os == selection_end_) && |
| 429 os->GetSelectionState() != SelectionNone) { |
| 430 // Blocks are responsible for painting line gaps and margin gaps. They |
| 431 // must be examined as well. |
| 432 sel_rect.Unite(SelectionRectForLayoutObject(os)); |
| 433 const LayoutBlock* cb = os->ContainingBlock(); |
| 434 while (cb && !cb->IsLayoutView()) { |
| 435 sel_rect.Unite(SelectionRectForLayoutObject(cb)); |
| 436 VisitedContainingBlockSet::AddResult add_result = |
| 437 visited_containing_blocks.insert(cb); |
| 438 if (!add_result.is_new_entry) |
| 439 break; |
| 440 cb = cb->ContainingBlock(); |
| 441 } |
| 442 } |
| 443 |
| 444 os = os->NextInPreOrder(); |
| 445 } |
| 446 |
| 447 return PixelSnappedIntRect(sel_rect); |
| 448 } |
| 449 |
| 450 void LayoutSelection::InvalidatePaintForSelection() { |
| 451 LayoutObject* end = |
| 452 LayoutObjectAfterPosition(selection_end_, selection_end_pos_); |
| 453 for (LayoutObject* o = selection_start_; o && o != end; |
| 454 o = o->NextInPreOrder()) { |
| 455 if (!o->CanBeSelectionLeaf() && o != selection_start_ && |
| 456 o != selection_end_) |
| 457 continue; |
| 458 if (o->GetSelectionState() == SelectionNone) |
| 459 continue; |
| 460 |
| 461 o->SetShouldInvalidateSelection(); |
| 462 } |
| 463 } |
| 464 |
179 DEFINE_TRACE(LayoutSelection) { | 465 DEFINE_TRACE(LayoutSelection) { |
180 visitor->Trace(frame_selection_); | 466 visitor->Trace(frame_selection_); |
181 } | 467 } |
182 | 468 |
183 } // namespace blink | 469 } // namespace blink |
OLD | NEW |