Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(830)

Unified Diff: third_party/WebKit/Source/core/editing/LayoutSelection.cpp

Issue 2813403002: Move LayouetSelection function definition from layout/LayoutView.cpp to editing/LayoutSelection.cpp (Closed)
Patch Set: update Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | third_party/WebKit/Source/core/layout/LayoutView.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/core/editing/LayoutSelection.cpp
diff --git a/third_party/WebKit/Source/core/editing/LayoutSelection.cpp b/third_party/WebKit/Source/core/editing/LayoutSelection.cpp
index 540146afea59317b2f41c95f2de8b3f74415c3f7..1dccdebda0a3ee1b88a8e1343b95427310c1fd8c 100644
--- a/third_party/WebKit/Source/core/editing/LayoutSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/LayoutSelection.cpp
@@ -28,6 +28,7 @@
#include "core/editing/VisibleUnits.h"
#include "core/html/TextControlElement.h"
#include "core/layout/LayoutView.h"
+#include "core/paint/PaintLayer.h"
namespace blink {
@@ -111,6 +112,231 @@ SelectionInFlatTree LayoutSelection::CalcVisibleSelection(
return builder.Build();
}
+static LayoutObject* LayoutObjectAfterPosition(LayoutObject* object,
+ unsigned offset) {
+ if (!object)
+ return nullptr;
+
+ LayoutObject* child = object->ChildAt(offset);
+ return child ? child : object->NextInPreOrderAfterChildren();
+}
+
+// When exploring the LayoutTree looking for the nodes involved in the
+// Selection, sometimes it's required to change the traversing direction because
+// the "start" position is below the "end" one.
+static inline LayoutObject* GetNextOrPrevLayoutObjectBasedOnDirection(
+ const LayoutObject* o,
+ const LayoutObject* stop,
+ bool& continue_exploring,
+ bool& exploring_backwards) {
+ LayoutObject* next;
+ if (exploring_backwards) {
+ next = o->PreviousInPreOrder();
+ continue_exploring = next && !(next)->IsLayoutView();
+ } else {
+ next = o->NextInPreOrder();
+ continue_exploring = next && next != stop;
+ exploring_backwards = !next && (next != stop);
+ if (exploring_backwards) {
+ next = stop->PreviousInPreOrder();
+ continue_exploring = next && !next->IsLayoutView();
+ }
+ }
+
+ return next;
+}
+
+void LayoutSelection::SetSelection(
+ LayoutObject* start,
+ int start_pos,
+ LayoutObject* end,
+ int end_pos,
+ SelectionPaintInvalidationMode block_paint_invalidation_mode) {
+ // This code makes no assumptions as to if the layout tree is up to date or
+ // not and will not try to update it. Currently clearSelection calls this
+ // (intentionally) without updating the layout tree as it doesn't care.
+ // Other callers may want to force recalc style before calling this.
+
+ // Make sure both our start and end objects are defined.
+ // Check www.msnbc.com and try clicking around to find the case where this
+ // happened.
+ if ((start && !end) || (end && !start))
+ return;
+
+ // Just return if the selection hasn't changed.
+ if (selection_start_ == start && selection_start_pos_ == start_pos &&
+ selection_end_ == end && selection_end_pos_ == end_pos)
+ return;
+
+ // Record the old selected objects. These will be used later when we compare
+ // against the new selected objects.
+ int old_start_pos = selection_start_pos_;
+ int old_end_pos = selection_end_pos_;
+
+ // Objects each have a single selection rect to examine.
+ typedef HashMap<LayoutObject*, SelectionState> SelectedObjectMap;
+ SelectedObjectMap old_selected_objects;
+ // FIXME: |newSelectedObjects| doesn't really need to store the
+ // SelectionState, it's just more convenient to have it use the same data
+ // structure as |oldSelectedObjects|.
+ SelectedObjectMap new_selected_objects;
+
+ // Blocks contain selected objects and fill gaps between them, either on the
+ // left, right, or in between lines and blocks.
+ // In order to get the visual rect right, we have to examine left, middle, and
+ // right rects individually, since otherwise the union of those rects might
+ // remain the same even when changes have occurred.
+ typedef HashMap<LayoutBlock*, SelectionState> SelectedBlockMap;
+ SelectedBlockMap old_selected_blocks;
+ // FIXME: |newSelectedBlocks| doesn't really need to store the SelectionState,
+ // it's just more convenient to have it use the same data structure as
+ // |oldSelectedBlocks|.
+ SelectedBlockMap new_selected_blocks;
+
+ LayoutObject* os = selection_start_;
+ LayoutObject* stop =
+ LayoutObjectAfterPosition(selection_end_, selection_end_pos_);
+ bool exploring_backwards = false;
+ bool continue_exploring = os && (os != stop);
+ while (continue_exploring) {
+ if ((os->CanBeSelectionLeaf() || os == selection_start_ ||
+ os == selection_end_) &&
+ os->GetSelectionState() != SelectionNone) {
+ // Blocks are responsible for painting line gaps and margin gaps. They
+ // must be examined as well.
+ old_selected_objects.Set(os, os->GetSelectionState());
+ if (block_paint_invalidation_mode == kPaintInvalidationNewXOROld) {
+ LayoutBlock* cb = os->ContainingBlock();
+ while (cb && !cb->IsLayoutView()) {
+ SelectedBlockMap::AddResult result =
+ old_selected_blocks.insert(cb, cb->GetSelectionState());
+ if (!result.is_new_entry)
+ break;
+ cb = cb->ContainingBlock();
+ }
+ }
+ }
+
+ os = GetNextOrPrevLayoutObjectBasedOnDirection(os, stop, continue_exploring,
+ exploring_backwards);
+ }
+
+ // Now clear the selection.
+ SelectedObjectMap::iterator old_objects_end = old_selected_objects.end();
+ for (SelectedObjectMap::iterator i = old_selected_objects.begin();
+ i != old_objects_end; ++i)
+ i->key->SetSelectionStateIfNeeded(SelectionNone);
+
+ // set selection start and end
+ selection_start_ = start;
+ selection_start_pos_ = start_pos;
+ selection_end_ = end;
+ selection_end_pos_ = end_pos;
+
+ // Update the selection status of all objects between m_selectionStart and
+ // m_selectionEnd
+ if (start && start == end) {
+ start->SetSelectionStateIfNeeded(SelectionBoth);
+ } else {
+ if (start)
+ start->SetSelectionStateIfNeeded(SelectionStart);
+ if (end)
+ end->SetSelectionStateIfNeeded(SelectionEnd);
+ }
+
+ LayoutObject* o = start;
+ stop = LayoutObjectAfterPosition(end, end_pos);
+
+ while (o && o != stop) {
+ if (o != start && o != end && o->CanBeSelectionLeaf())
+ o->SetSelectionStateIfNeeded(SelectionInside);
+ o = o->NextInPreOrder();
+ }
+
+ // Now that the selection state has been updated for the new objects, walk
+ // them again and put them in the new objects list.
+ o = start;
+ exploring_backwards = false;
+ continue_exploring = o && (o != stop);
+ while (continue_exploring) {
+ if ((o->CanBeSelectionLeaf() || o == start || o == end) &&
+ o->GetSelectionState() != SelectionNone) {
+ new_selected_objects.Set(o, o->GetSelectionState());
+ LayoutBlock* cb = o->ContainingBlock();
+ while (cb && !cb->IsLayoutView()) {
+ SelectedBlockMap::AddResult result =
+ new_selected_blocks.insert(cb, cb->GetSelectionState());
+ if (!result.is_new_entry)
+ break;
+ cb = cb->ContainingBlock();
+ }
+ }
+
+ o = GetNextOrPrevLayoutObjectBasedOnDirection(o, stop, continue_exploring,
+ exploring_backwards);
+ }
+
+ // TODO(yoichio): DCHECK(frame_selection_->,,,->GetFrameView());
+ if (!frame_selection_->GetDocument().GetLayoutView()->GetFrameView())
+ return;
+
+ // Have any of the old selected objects changed compared to the new selection?
+ for (SelectedObjectMap::iterator i = old_selected_objects.begin();
+ i != old_objects_end; ++i) {
+ LayoutObject* obj = i->key;
+ SelectionState new_selection_state = obj->GetSelectionState();
+ SelectionState old_selection_state = i->value;
+ if (new_selection_state != old_selection_state ||
+ (selection_start_ == obj && old_start_pos != selection_start_pos_) ||
+ (selection_end_ == obj && old_end_pos != selection_end_pos_)) {
+ obj->SetShouldInvalidateSelection();
+ new_selected_objects.erase(obj);
+ }
+ }
+
+ // Any new objects that remain were not found in the old objects dict, and so
+ // they need to be updated.
+ SelectedObjectMap::iterator new_objects_end = new_selected_objects.end();
+ for (SelectedObjectMap::iterator i = new_selected_objects.begin();
+ i != new_objects_end; ++i)
+ i->key->SetShouldInvalidateSelection();
+
+ // Have any of the old blocks changed?
+ SelectedBlockMap::iterator old_blocks_end = old_selected_blocks.end();
+ for (SelectedBlockMap::iterator i = old_selected_blocks.begin();
+ i != old_blocks_end; ++i) {
+ LayoutBlock* block = i->key;
+ SelectionState new_selection_state = block->GetSelectionState();
+ SelectionState old_selection_state = i->value;
+ if (new_selection_state != old_selection_state) {
+ block->SetShouldInvalidateSelection();
+ new_selected_blocks.erase(block);
+ }
+ }
+
+ // Any new blocks that remain were not found in the old blocks dict, and so
+ // they need to be updated.
+ SelectedBlockMap::iterator new_blocks_end = new_selected_blocks.end();
+ for (SelectedBlockMap::iterator i = new_selected_blocks.begin();
+ i != new_blocks_end; ++i)
+ i->key->SetShouldInvalidateSelection();
+}
+
+void LayoutSelection::SelectionStartEnd(int& start_pos, int& end_pos) {
+ Commit(*frame_selection_->GetDocument().GetLayoutView());
+ start_pos = selection_start_pos_;
+ end_pos = selection_end_pos_;
+}
+
+void LayoutSelection::ClearSelection() {
+ // For querying Layer::compositingState()
+ // This is correct, since destroying layout objects needs to cause eager paint
+ // invalidations.
+ DisableCompositingQueryAsserts disabler;
+
+ SetSelection(0, -1, 0, -1, kPaintInvalidationNewMinusOld);
+}
+
void LayoutSelection::Commit(LayoutView& layout_view) {
if (!HasPendingSelection())
return;
@@ -176,6 +402,66 @@ void LayoutSelection::OnDocumentShutdown() {
selection_end_pos_ = -1;
}
+static LayoutRect SelectionRectForLayoutObject(const LayoutObject* object) {
+ if (!object->IsRooted())
+ return LayoutRect();
+
+ if (!object->CanUpdateSelectionOnRootLineBoxes())
+ return LayoutRect();
+
+ return object->SelectionRectInViewCoordinates();
+}
+
+IntRect LayoutSelection::SelectionBounds() {
+ // Now create a single bounding box rect that encloses the whole selection.
+ LayoutRect sel_rect;
+
+ typedef HashSet<const LayoutBlock*> VisitedContainingBlockSet;
+ VisitedContainingBlockSet visited_containing_blocks;
+
+ Commit(*frame_selection_->GetDocument().GetLayoutView());
+ LayoutObject* os = selection_start_;
+ LayoutObject* stop =
+ LayoutObjectAfterPosition(selection_end_, selection_end_pos_);
+ while (os && os != stop) {
+ if ((os->CanBeSelectionLeaf() || os == selection_start_ ||
+ os == selection_end_) &&
+ os->GetSelectionState() != SelectionNone) {
+ // Blocks are responsible for painting line gaps and margin gaps. They
+ // must be examined as well.
+ sel_rect.Unite(SelectionRectForLayoutObject(os));
+ const LayoutBlock* cb = os->ContainingBlock();
+ while (cb && !cb->IsLayoutView()) {
+ sel_rect.Unite(SelectionRectForLayoutObject(cb));
+ VisitedContainingBlockSet::AddResult add_result =
+ visited_containing_blocks.insert(cb);
+ if (!add_result.is_new_entry)
+ break;
+ cb = cb->ContainingBlock();
+ }
+ }
+
+ os = os->NextInPreOrder();
+ }
+
+ return PixelSnappedIntRect(sel_rect);
+}
+
+void LayoutSelection::InvalidatePaintForSelection() {
+ LayoutObject* end =
+ LayoutObjectAfterPosition(selection_end_, selection_end_pos_);
+ for (LayoutObject* o = selection_start_; o && o != end;
+ o = o->NextInPreOrder()) {
+ if (!o->CanBeSelectionLeaf() && o != selection_start_ &&
+ o != selection_end_)
+ continue;
+ if (o->GetSelectionState() == SelectionNone)
+ continue;
+
+ o->SetShouldInvalidateSelection();
+ }
+}
+
DEFINE_TRACE(LayoutSelection) {
visitor->Trace(frame_selection_);
}
« no previous file with comments | « no previous file | third_party/WebKit/Source/core/layout/LayoutView.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698