IntRect RenderView::selectionBounds() const { typedef WillBeHeapHashMap<RawPtrWillBeMember<RenderObject>, OwnPtrWillBeMember<RenderSelectionInfo> > SelectionMap; SelectionMap selectedObjects; RenderObject* os = m_selectionStart; RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); while (os && os != stop) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. selectedObjects.set(os, adoptPtrWillBeNoop(new RenderSelectionInfo(os))); RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { OwnPtrWillBeMember<RenderSelectionInfo>& blockInfo = selectedObjects.add(cb, nullptr).storedValue->value; if (blockInfo) break; blockInfo = adoptPtrWillBeNoop(new RenderSelectionInfo(cb)); cb = cb->containingBlock(); } } os = os->nextInPreOrder(); } // Now create a single bounding box rect that encloses the whole selection. LayoutRect selRect; SelectionMap::iterator end = selectedObjects.end(); for (SelectionMap::iterator i = selectedObjects.begin(); i != end; ++i) selRect.unite(i->value->absoluteSelectionRect()); return pixelSnappedIntRect(selRect); }
LayoutUnit RenderSVGRoot::computeReplacedLogicalHeight() const { // When we're embedded through SVGImage (border-image/background-image/<html:img>/...) we're forced to resize to a specific size. if (!m_containerSize.isEmpty()) return m_containerSize.height(); if (style()->logicalHeight().isSpecified() || style()->logicalMaxHeight().isSpecified()) return RenderReplaced::computeReplacedLogicalHeight(); if (svgSVGElement().heightAttributeEstablishesViewport()) { Length height = svgSVGElement().intrinsicHeight(SVGSVGElement::IgnoreCSSProperties); if (height.isPercent()) { RenderBlock* cb = containingBlock(); ASSERT(cb); while (cb->isAnonymous() && !cb->isRenderView()) { cb = cb->containingBlock(); cb->addPercentHeightDescendant(const_cast<RenderSVGRoot&>(*this)); } } else RenderBlock::removePercentHeightDescendant(const_cast<RenderSVGRoot&>(*this)); return resolveLengthAttributeForSVG(height, style()->effectiveZoom(), containingBlock()->availableLogicalHeight(IncludeMarginBorderPadding), &view()); } // SVG embedded through object/embed/iframe. if (isEmbeddedThroughFrameContainingSVGDocument()) return frame().ownerRenderer()->availableLogicalHeight(IncludeMarginBorderPadding); // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG. return RenderReplaced::computeReplacedLogicalHeight(); }
void RenderView::invalidatePaintForSelection() const { HashSet<RenderBlock*> processedBlocks; // For querying RenderLayer::compositingState() // FIXME: this may be wrong. crbug.com/407416 DisableCompositingQueryAsserts disabler; RenderObject* end = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); for (RenderObject* o = m_selectionStart; o && o != end; o = o->nextInPreOrder()) { if (!o->canBeSelectionLeaf() && o != m_selectionStart && o != m_selectionEnd) continue; if (o->selectionState() == SelectionNone) continue; RenderSelectionInfo(o, true).invalidatePaint(); // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. for (RenderBlock* block = o->containingBlock(); block && !block->isRenderView(); block = block->containingBlock()) { if (!processedBlocks.add(block).isNewEntry) break; RenderSelectionInfo(block, true).invalidatePaint(); } } }
static inline RenderBlock* firstContainingBlockWithLogicalWidth(const RenderReplaced* replaced) { // We have to lookup the containing block, which has an explicit width, which must not be equal to our direct containing block. // If the embedded document appears _after_ we performed the initial layout, our intrinsic size is 300x150. If our containing // block doesn't provide an explicit width, it's set to the 300 default, coming from the initial layout run. RenderBlock* containingBlock = replaced->containingBlock(); if (!containingBlock) return 0; for (; !containingBlock->isRenderView() && !containingBlock->isBody(); containingBlock = containingBlock->containingBlock()) { if (containingBlock->style()->logicalWidth().isSpecified()) return containingBlock; } return 0; }
void RenderBoxModelObject::setSelectionState(SelectionState state) { if (state == SelectionInside && selectionState() != SelectionNone) return; if ((state == SelectionStart && selectionState() == SelectionEnd) || (state == SelectionEnd && selectionState() == SelectionStart)) RenderObject::setSelectionState(SelectionBoth); else RenderObject::setSelectionState(state); // FIXME: We should consider whether it is OK propagating to ancestor RenderInlines. // This is a workaround for http://webkit.org/b/32123 // The containing block can be null in case of an orphaned tree. RenderBlock* containingBlock = this->containingBlock(); if (containingBlock && !containingBlock->isRenderView()) containingBlock->setSelectionState(state); }
static inline bool hasAutoHeightOrContainingBlockWithAutoHeight(const RenderReplaced* replaced) { Length logicalHeightLength = replaced->style()->logicalHeight(); if (logicalHeightLength.isAuto()) return true; // For percentage heights: The percentage is calculated with respect to the height of the generated box's // containing block. If the height of the containing block is not specified explicitly (i.e., it depends // on content height), and this element is not absolutely positioned, the value computes to 'auto'. if (!logicalHeightLength.isPercent() || replaced->isPositioned() || replaced->document()->inQuirksMode()) return false; for (RenderBlock* cb = replaced->containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { if (cb->isTableCell() || (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto()))) return false; } return true; }
IntRect RenderView::selectionBounds(bool clipToVisibleContent) const { document()->updateStyleIfNeeded(); typedef HashMap<RenderObject*, RenderSelectionInfo*> SelectionMap; SelectionMap selectedObjects; RenderObject* os = m_selectionStart; RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); while (os && os != stop) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. selectedObjects.set(os, new RenderSelectionInfo(os, clipToVisibleContent)); RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { RenderSelectionInfo* blockInfo = selectedObjects.get(cb); if (blockInfo) break; selectedObjects.set(cb, new RenderSelectionInfo(cb, clipToVisibleContent)); cb = cb->containingBlock(); } } os = os->nextInPreOrder(); } // Now create a single bounding box rect that encloses the whole selection. IntRect selRect; SelectionMap::iterator end = selectedObjects.end(); for (SelectionMap::iterator i = selectedObjects.begin(); i != end; ++i) { RenderSelectionInfo* info = i->second; // RenderSelectionInfo::rect() is in the coordinates of the repaintContainer, so map to page coordinates. IntRect currRect = info->rect(); if (RenderBoxModelObject* repaintContainer = info->repaintContainer()) { FloatQuad absQuad = repaintContainer->localToAbsoluteQuad(FloatRect(currRect)); currRect = absQuad.enclosingBoundingBox(); } selRect.unite(currRect); delete info; } return selRect; }
void RenderView::invalidatePaintForSelection() const { HashSet<RenderBlock*> processedBlocks; RenderObject* end = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); for (RenderObject* o = m_selectionStart; o && o != end; o = o->nextInPreOrder()) { if (!o->canBeSelectionLeaf() && o != m_selectionStart && o != m_selectionEnd) continue; if (o->selectionState() == SelectionNone) continue; o->setShouldInvalidateSelection(); // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. for (RenderBlock* block = o->containingBlock(); block && !block->isRenderView(); block = block->containingBlock()) { if (!processedBlocks.add(block).isNewEntry) break; block->setShouldInvalidateSelection(); } } }
IntRect RenderView::selectionBounds(bool clipToVisibleContent) const { typedef HashMap<RawPtr<RenderObject>, OwnPtr<RenderSelectionInfo> > SelectionMap; SelectionMap selectedObjects; RenderObject* os = m_selectionStart; RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); while (os && os != stop) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. selectedObjects.set(os, adoptPtr(new RenderSelectionInfo(os, clipToVisibleContent))); RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { OwnPtr<RenderSelectionInfo>& blockInfo = selectedObjects.add(cb, nullptr).storedValue->value; if (blockInfo) break; blockInfo = adoptPtr(new RenderSelectionInfo(cb, clipToVisibleContent)); cb = cb->containingBlock(); } } os = os->nextInPreOrder(); } // Now create a single bounding box rect that encloses the whole selection. LayoutRect selRect; SelectionMap::iterator end = selectedObjects.end(); for (SelectionMap::iterator i = selectedObjects.begin(); i != end; ++i) { RenderSelectionInfo* info = i->value.get(); // RenderSelectionInfo::rect() is in the coordinates of the paintInvalidationContainer, so map to page coordinates. LayoutRect currRect = info->rect(); if (const RenderLayerModelObject* paintInvalidationContainer = info->paintInvalidationContainer()) { FloatQuad absQuad = paintInvalidationContainer->localToAbsoluteQuad(FloatRect(currRect)); currRect = absQuad.enclosingBoundingBox(); } selRect.unite(currRect); } return pixelSnappedIntRect(selRect); }
void RenderView::repaintSelection() const { document().updateStyleIfNeeded(); HashSet<RenderBlock*> processedBlocks; RenderObject* end = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); for (RenderObject* o = m_selectionStart; o && o != end; o = o->nextInPreOrder()) { if (!o->canBeSelectionLeaf() && o != m_selectionStart && o != m_selectionEnd) continue; if (o->selectionState() == SelectionNone) continue; RenderSelectionInfo(o, true).repaint(); // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. for (RenderBlock* block = o->containingBlock(); block && !block->isRenderView(); block = block->containingBlock()) { if (!processedBlocks.add(block).isNewEntry) break; RenderSelectionInfo(block, true).repaint(); } } }
bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() const { Length logicalHeightLength = style()->logicalHeight(); if (logicalHeightLength.isAuto()) return true; // For percentage heights: The percentage is calculated with respect to the height of the generated box's // containing block. If the height of the containing block is not specified explicitly (i.e., it depends // on content height), and this element is not absolutely positioned, the value computes to 'auto'. if (!logicalHeightLength.isPercent() || isOutOfFlowPositioned() || document().inQuirksMode()) return false; // Anonymous block boxes are ignored when resolving percentage values that would refer to it: // the closest non-anonymous ancestor box is used instead. RenderBlock* cb = containingBlock(); while (cb->isAnonymous()) cb = cb->containingBlock(); // Matching RenderBox::percentageLogicalHeightIsResolvableFromBlock() by // ignoring table cell's attribute value, where it says that table cells violate // what the CSS spec says to do with heights. Basically we // don't care if the cell specified a height or not. if (cb->isTableCell()) return false; // Match RenderBox::availableLogicalHeightUsing by special casing // the render view. The available height is taken from the frame. if (cb->isRenderView()) return false; if (cb->isOutOfFlowPositioned() && !cb->style()->logicalTop().isAuto() && !cb->style()->logicalBottom().isAuto()) return false; // If the height of the containing block computes to 'auto', then it hasn't been 'specified explicitly'. return cb->hasAutoHeightOrContainingBlockWithAutoHeight(); }
IntRect RenderView::selectionBounds(bool clipToVisibleContent) const { document()->updateRendering(); typedef HashMap<RenderObject*, SelectionInfo*> SelectionMap; SelectionMap selectedObjects; RenderObject* os = m_selectionStart; RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); while (os && os != stop) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. selectedObjects.set(os, new SelectionInfo(os, clipToVisibleContent)); RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { SelectionInfo* blockInfo = selectedObjects.get(cb); if (blockInfo) break; selectedObjects.set(cb, new SelectionInfo(cb, clipToVisibleContent)); cb = cb->containingBlock(); } } os = os->nextInPreOrder(); } // Now create a single bounding box rect that encloses the whole selection. IntRect selRect; SelectionMap::iterator end = selectedObjects.end(); for (SelectionMap::iterator i = selectedObjects.begin(); i != end; ++i) { SelectionInfo* info = i->second; selRect.unite(info->rect()); delete info; } return selRect; }
void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos, SelectionRepaintMode blockRepaintMode) { // 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 (m_selectionStart == start && m_selectionStartPos == startPos && m_selectionEnd == end && m_selectionEndPos == endPos) return; // Record the old selected objects. These will be used later // when we compare against the new selected objects. int oldStartPos = m_selectionStartPos; int oldEndPos = m_selectionEndPos; // Objects each have a single selection rect to examine. typedef HashMap<RenderObject*, OwnPtr<RenderSelectionInfo> > SelectedObjectMap; SelectedObjectMap oldSelectedObjects; SelectedObjectMap newSelectedObjects; // 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 repaint 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<RenderBlock*, OwnPtr<RenderBlockSelectionInfo> > SelectedBlockMap; SelectedBlockMap oldSelectedBlocks; SelectedBlockMap newSelectedBlocks; RenderObject* os = m_selectionStart; RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); while (os && os != stop) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. oldSelectedObjects.set(os, adoptPtr(new RenderSelectionInfo(os, true))); if (blockRepaintMode == RepaintNewXOROld) { RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { OwnPtr<RenderBlockSelectionInfo>& blockInfo = oldSelectedBlocks.add(cb, nullptr).iterator->second; if (blockInfo) break; blockInfo = adoptPtr(new RenderBlockSelectionInfo(cb)); cb = cb->containingBlock(); } } } os = os->nextInPreOrder(); } // Now clear the selection. SelectedObjectMap::iterator oldObjectsEnd = oldSelectedObjects.end(); for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) i->first->setSelectionStateIfNeeded(SelectionNone); // set selection start and end m_selectionStart = start; m_selectionStartPos = startPos; m_selectionEnd = end; m_selectionEndPos = endPos; // 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); } RenderObject* o = start; stop = rendererAfterPosition(end, endPos); while (o && o != stop) { if (o != start && o != end && o->canBeSelectionLeaf()) o->setSelectionStateIfNeeded(SelectionInside); o = o->nextInPreOrder(); } if (blockRepaintMode != RepaintNothing) m_layer->clearBlockSelectionGapsBounds(); // 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; while (o && o != stop) { if ((o->canBeSelectionLeaf() || o == start || o == end) && o->selectionState() != SelectionNone) { newSelectedObjects.set(o, adoptPtr(new RenderSelectionInfo(o, true))); RenderBlock* cb = o->containingBlock(); while (cb && !cb->isRenderView()) { OwnPtr<RenderBlockSelectionInfo>& blockInfo = newSelectedBlocks.add(cb, nullptr).iterator->second; if (blockInfo) break; blockInfo = adoptPtr(new RenderBlockSelectionInfo(cb)); cb = cb->containingBlock(); } } o = o->nextInPreOrder(); } if (!m_frameView || blockRepaintMode == RepaintNothing) return; m_frameView->beginDeferredRepaints(); // Have any of the old selected objects changed compared to the new selection? for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) { RenderObject* obj = i->first; RenderSelectionInfo* newInfo = newSelectedObjects.get(obj); RenderSelectionInfo* oldInfo = i->second.get(); if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state() || (m_selectionStart == obj && oldStartPos != m_selectionStartPos) || (m_selectionEnd == obj && oldEndPos != m_selectionEndPos)) { oldInfo->repaint(); if (newInfo) { newInfo->repaint(); newSelectedObjects.remove(obj); } } } // Any new objects that remain were not found in the old objects dict, and so they need to be updated. SelectedObjectMap::iterator newObjectsEnd = newSelectedObjects.end(); for (SelectedObjectMap::iterator i = newSelectedObjects.begin(); i != newObjectsEnd; ++i) i->second->repaint(); // Have any of the old blocks changed? SelectedBlockMap::iterator oldBlocksEnd = oldSelectedBlocks.end(); for (SelectedBlockMap::iterator i = oldSelectedBlocks.begin(); i != oldBlocksEnd; ++i) { RenderBlock* block = i->first; RenderBlockSelectionInfo* newInfo = newSelectedBlocks.get(block); RenderBlockSelectionInfo* oldInfo = i->second.get(); if (!newInfo || oldInfo->rects() != newInfo->rects() || oldInfo->state() != newInfo->state()) { oldInfo->repaint(); if (newInfo) { newInfo->repaint(); newSelectedBlocks.remove(block); } } } // Any new blocks that remain were not found in the old blocks dict, and so they need to be updated. SelectedBlockMap::iterator newBlocksEnd = newSelectedBlocks.end(); for (SelectedBlockMap::iterator i = newSelectedBlocks.begin(); i != newBlocksEnd; ++i) i->second->repaint(); m_frameView->endDeferredRepaints(); }
void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos, SelectionPaintInvalidationMode blockPaintInvalidationMode) { // This code makes no assumptions as to if the rendering tree is up to date or not // and will not try to update it. Currently clearSelection calls this // (intentionally) without updating the rendering 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 (m_selectionStart == start && m_selectionStartPos == startPos && m_selectionEnd == end && m_selectionEndPos == endPos) return; // Record the old selected objects. These will be used later // when we compare against the new selected objects. int oldStartPos = m_selectionStartPos; int oldEndPos = m_selectionEndPos; // Objects each have a single selection rect to examine. typedef WillBeHeapHashMap<RawPtrWillBeMember<RenderObject>, SelectionState > SelectedObjectMap; SelectedObjectMap oldSelectedObjects; // 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 newSelectedObjects; // 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 paint invalidation 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 WillBeHeapHashMap<RawPtrWillBeMember<RenderBlock>, SelectionState > SelectedBlockMap; SelectedBlockMap oldSelectedBlocks; // 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 newSelectedBlocks; RenderObject* os = m_selectionStart; RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); bool exploringBackwards = false; bool continueExploring = os && (os != stop); while (continueExploring) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. oldSelectedObjects.set(os, os->selectionState()); if (blockPaintInvalidationMode == PaintInvalidationNewXOROld) { RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { SelectedBlockMap::AddResult result = oldSelectedBlocks.add(cb, cb->selectionState()); if (!result.isNewEntry) break; cb = cb->containingBlock(); } } } os = getNextOrPrevRenderObjectBasedOnDirection(os, stop, continueExploring, exploringBackwards); } // Now clear the selection. SelectedObjectMap::iterator oldObjectsEnd = oldSelectedObjects.end(); for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) i->key->setSelectionStateIfNeeded(SelectionNone); // set selection start and end m_selectionStart = start; m_selectionStartPos = startPos; m_selectionEnd = end; m_selectionEndPos = endPos; // 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); } RenderObject* o = start; stop = rendererAfterPosition(end, endPos); while (o && o != stop) { if (o != start && o != end && o->canBeSelectionLeaf()) o->setSelectionStateIfNeeded(SelectionInside); o = o->nextInPreOrder(); } layer()->clearBlockSelectionGapsBounds(); // 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; exploringBackwards = false; continueExploring = o && (o != stop); while (continueExploring) { if ((o->canBeSelectionLeaf() || o == start || o == end) && o->selectionState() != SelectionNone) { newSelectedObjects.set(o, o->selectionState()); RenderBlock* cb = o->containingBlock(); while (cb && !cb->isRenderView()) { SelectedBlockMap::AddResult result = newSelectedBlocks.add(cb, cb->selectionState()); if (!result.isNewEntry) break; cb = cb->containingBlock(); } } o = getNextOrPrevRenderObjectBasedOnDirection(o, stop, continueExploring, exploringBackwards); } if (!m_frameView) return; // Have any of the old selected objects changed compared to the new selection? for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) { RenderObject* obj = i->key; SelectionState newSelectionState = obj->selectionState(); SelectionState oldSelectionState = i->value; if (newSelectionState != oldSelectionState || (m_selectionStart == obj && oldStartPos != m_selectionStartPos) || (m_selectionEnd == obj && oldEndPos != m_selectionEndPos)) { obj->setShouldInvalidateSelection(); newSelectedObjects.remove(obj); } } // Any new objects that remain were not found in the old objects dict, and so they need to be updated. SelectedObjectMap::iterator newObjectsEnd = newSelectedObjects.end(); for (SelectedObjectMap::iterator i = newSelectedObjects.begin(); i != newObjectsEnd; ++i) i->key->setShouldInvalidateSelection(); // Have any of the old blocks changed? SelectedBlockMap::iterator oldBlocksEnd = oldSelectedBlocks.end(); for (SelectedBlockMap::iterator i = oldSelectedBlocks.begin(); i != oldBlocksEnd; ++i) { RenderBlock* block = i->key; SelectionState newSelectionState = block->selectionState(); SelectionState oldSelectionState = i->value; if (newSelectionState != oldSelectionState) { block->setShouldInvalidateSelection(); newSelectedBlocks.remove(block); } } // Any new blocks that remain were not found in the old blocks dict, and so they need to be updated. SelectedBlockMap::iterator newBlocksEnd = newSelectedBlocks.end(); for (SelectedBlockMap::iterator i = newSelectedBlocks.begin(); i != newBlocksEnd; ++i) i->key->setShouldInvalidateSelection(); }
void RenderView::setSelection(RenderObject *s, int sp, RenderObject *e, int ep) { // 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 ((s && !e) || (e && !s)) return; // Just return if the selection hasn't changed. if (m_selectionStart == s && m_selectionStartPos == sp && m_selectionEnd == e && m_selectionEndPos == ep) return; // Record the old selected objects. These will be used later // when we compare against the new selected objects. int oldStartPos = m_selectionStartPos; int oldEndPos = m_selectionEndPos; // Objects each have a single selection rect to examine. typedef HashMap<RenderObject*, SelectionInfo*> SelectedObjectMap; SelectedObjectMap oldSelectedObjects; SelectedObjectMap newSelectedObjects; // 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 repaint 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<RenderBlock*, BlockSelectionInfo*> SelectedBlockMap; SelectedBlockMap oldSelectedBlocks; SelectedBlockMap newSelectedBlocks; RenderObject* os = m_selectionStart; RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); while (os && os != stop) { if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. oldSelectedObjects.set(os, new SelectionInfo(os)); RenderBlock* cb = os->containingBlock(); while (cb && !cb->isRenderView()) { BlockSelectionInfo* blockInfo = oldSelectedBlocks.get(cb); if (blockInfo) break; oldSelectedBlocks.set(cb, new BlockSelectionInfo(cb)); cb = cb->containingBlock(); } } os = os->nextInPreOrder(); } // Now clear the selection. SelectedObjectMap::iterator oldObjectsEnd = oldSelectedObjects.end(); for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) i->first->setSelectionState(SelectionNone); // set selection start and end m_selectionStart = s; m_selectionStartPos = sp; m_selectionEnd = e; m_selectionEndPos = ep; // Update the selection status of all objects between m_selectionStart and m_selectionEnd if (s && s == e) s->setSelectionState(SelectionBoth); else { if (s) s->setSelectionState(SelectionStart); if (e) e->setSelectionState(SelectionEnd); } RenderObject* o = s; stop = rendererAfterPosition(e, ep); while (o && o != stop) { if (o != s && o != e && o->canBeSelectionLeaf()) o->setSelectionState(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 = s; while (o && o != stop) { if ((o->canBeSelectionLeaf() || o == s || o == e) && o->selectionState() != SelectionNone) { newSelectedObjects.set(o, new SelectionInfo(o)); RenderBlock* cb = o->containingBlock(); while (cb && !cb->isRenderView()) { BlockSelectionInfo* blockInfo = newSelectedBlocks.get(cb); if (blockInfo) break; newSelectedBlocks.set(cb, new BlockSelectionInfo(cb)); cb = cb->containingBlock(); } } o = o->nextInPreOrder(); } if (!m_frameView) return; // Have any of the old selected objects changed compared to the new selection? for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) { RenderObject* obj = i->first; SelectionInfo* newInfo = newSelectedObjects.get(obj); SelectionInfo* oldInfo = i->second; if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state() || (m_selectionStart == obj && oldStartPos != m_selectionStartPos) || (m_selectionEnd == obj && oldEndPos != m_selectionEndPos)) { m_frameView->updateContents(oldInfo->rect()); if (newInfo) { m_frameView->updateContents(newInfo->rect()); newSelectedObjects.remove(obj); delete newInfo; } } delete oldInfo; } // Any new objects that remain were not found in the old objects dict, and so they need to be updated. SelectedObjectMap::iterator newObjectsEnd = newSelectedObjects.end(); for (SelectedObjectMap::iterator i = newSelectedObjects.begin(); i != newObjectsEnd; ++i) { SelectionInfo* newInfo = i->second; m_frameView->updateContents(newInfo->rect()); delete newInfo; } // Have any of the old blocks changed? SelectedBlockMap::iterator oldBlocksEnd = oldSelectedBlocks.end(); for (SelectedBlockMap::iterator i = oldSelectedBlocks.begin(); i != oldBlocksEnd; ++i) { RenderBlock* block = i->first; BlockSelectionInfo* newInfo = newSelectedBlocks.get(block); BlockSelectionInfo* oldInfo = i->second; if (!newInfo || oldInfo->rects() != newInfo->rects() || oldInfo->state() != newInfo->state()) { m_frameView->updateContents(oldInfo->rects()); if (newInfo) { m_frameView->updateContents(newInfo->rects()); newSelectedBlocks.remove(block); delete newInfo; } } delete oldInfo; } // Any new blocks that remain were not found in the old blocks dict, and so they need to be updated. SelectedBlockMap::iterator newBlocksEnd = newSelectedBlocks.end(); for (SelectedBlockMap::iterator i = newSelectedBlocks.begin(); i != newBlocksEnd; ++i) { BlockSelectionInfo* newInfo = i->second; m_frameView->updateContents(newInfo->rects()); delete newInfo; } }