📄 selectioncontroller.cpp
字号:
bool SelectionController::recomputeCaretRect(){ if (!m_frame) return false; FrameView* v = m_frame->document()->view(); if (!v) return false; if (!m_needsLayout) return false; IntRect oldRect = m_caretRect; m_needsLayout = true; IntRect newRect = localCaretRect(); if (oldRect == newRect && !m_absCaretBoundsDirty) return false; IntRect oldAbsRepaintRect = m_absCaretBounds; m_absCaretBounds = caretRepaintRect(); m_absCaretBoundsDirty = false; if (oldAbsRepaintRect == m_absCaretBounds) return false; if (RenderView* view = toRenderView(m_frame->document()->renderer())) { // FIXME: make caret repainting container-aware. view->repaintRectangleInViewAndCompositedLayers(oldAbsRepaintRect, false); view->repaintRectangleInViewAndCompositedLayers(m_absCaretBounds, false); } return true;}void SelectionController::invalidateCaretRect(){ if (!isCaret()) return; Document* d = m_sel.start().node()->document(); // recomputeCaretRect will always return false for the drag caret, // because its m_frame is always 0. bool caretRectChanged = recomputeCaretRect(); // EDIT FIXME: This is an unfortunate hack. // Basically, we can't trust this layout position since we // can't guarantee that the check to see if we are in unrendered // content will work at this point. We may have to wait for // a layout and re-render of the document to happen. So, resetting this // flag will cause another caret layout to happen the first time // that we try to paint the caret after this call. That one will work since // it happens after the document has accounted for any editing // changes which may have been done. // And, we need to leave this layout here so the caret moves right // away after clicking. m_needsLayout = true; if (!caretRectChanged) { if (RenderView* view = toRenderView(d->renderer())) view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(), false); }}void SelectionController::paintCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect){ if (! m_sel.isCaret()) return; if (m_needsLayout) layout(); IntRect drawingRect = localCaretRect(); drawingRect.move(tx, ty); IntRect caret = intersection(drawingRect, clipRect); if (!caret.isEmpty()) { Color caretColor = Color::black; Element* element = rootEditableElement(); if (element && element->renderer()) caretColor = element->renderer()->style()->color(); p->fillRect(caret, caretColor); }}void SelectionController::debugRenderer(RenderObject *r, bool selected) const{ if (r->node()->isElementNode()) { Element *element = static_cast<Element *>(r->node()); fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data()); } else if (r->isText()) { RenderText* textRenderer = toRenderText(r); if (textRenderer->textLength() == 0 || !textRenderer->firstTextBox()) { fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " "); return; } static const int max = 36; String text = textRenderer->text(); int textLength = text.length(); if (selected) { int offset = 0; if (r->node() == m_sel.start().node()) offset = m_sel.start().offset(); else if (r->node() == m_sel.end().node()) offset = m_sel.end().offset(); int pos; InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos); text = text.substring(box->start(), box->len()); String show; int mid = max / 2; int caret = 0; // text is shorter than max if (textLength < max) { show = text; caret = pos; } // too few characters to left else if (pos - mid < 0) { show = text.left(max - 3) + "..."; caret = pos; } // enough characters on each side else if (pos - mid >= 0 && pos + mid <= textLength) { show = "..." + text.substring(pos - mid + 3, max - 6) + "..."; caret = mid; } // too few characters on right else { show = "..." + text.right(max - 3); caret = pos - (textLength - show.length()); } show.replace('\n', ' '); show.replace('\r', ' '); fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos); fprintf(stderr, " "); for (int i = 0; i < caret; i++) fprintf(stderr, " "); fprintf(stderr, "^\n"); } else { if ((int)text.length() > max) text = text.left(max - 3) + "..."; else text = text.left(max); fprintf(stderr, " #text : \"%s\"\n", text.utf8().data()); } }}bool SelectionController::contains(const IntPoint& point){ Document* document = m_frame->document(); // Treat a collapsed selection like no selection. if (!isRange()) return false; if (!document->renderer()) return false; HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); HitTestResult result(point); document->renderView()->layer()->hitTest(request, result); Node* innerNode = result.innerNode(); if (!innerNode || !innerNode->renderer()) return false; VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint())); if (visiblePos.isNull()) return false; if (m_sel.visibleStart().isNull() || m_sel.visibleEnd().isNull()) return false; Position start(m_sel.visibleStart().deepEquivalent()); Position end(m_sel.visibleEnd().deepEquivalent()); Position p(visiblePos.deepEquivalent()); return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;}// Workaround for the fact that it's hard to delete a frame.// Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.// Can't do this implicitly as part of every setSelection call because in some contexts it might not be good// for the focus to move to another frame. So instead we call it from places where we are selecting with the// mouse or the keyboard after setting the selection.void SelectionController::selectFrameElementInParentIfFullySelected(){ // Find the parent frame; if there is none, then we have nothing to do. Frame* parent = m_frame->tree()->parent(); if (!parent) return; Page* page = m_frame->page(); if (!page) return; // Check if the selection contains the entire frame contents; if not, then there is nothing to do. if (!isRange()) return; if (!isStartOfDocument(selection().visibleStart())) return; if (!isEndOfDocument(selection().visibleEnd())) return; // Get to the <iframe> or <frame> (or even <object>) element in the parent frame. Document* doc = m_frame->document(); Element* ownerElement = doc->ownerElement(); if (!ownerElement) return; Node* ownerElementParent = ownerElement->parentNode(); if (!ownerElementParent) return; // This method's purpose is it to make it easier to select iframes (in order to delete them). Don't do anything if the iframe isn't deletable. if (!ownerElementParent->isContentEditable()) return; // Create compute positions before and after the element. unsigned ownerElementNodeIndex = ownerElement->nodeIndex(); VisiblePosition beforeOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex, SEL_DEFAULT_AFFINITY)); VisiblePosition afterOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex + 1, VP_UPSTREAM_IF_POSSIBLE)); // Focus on the parent frame, and then select from before this element to after. VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement); if (parent->shouldChangeSelection(newSelection)) { page->focusController()->setFocusedFrame(parent); parent->selection()->setSelection(newSelection); }}void SelectionController::selectAll(){ Document* document = m_frame->document(); if (document->focusedNode() && document->focusedNode()->canSelectAll()) { document->focusedNode()->selectAll(); return; } Node* root = 0; if (isContentEditable()) root = highestEditableRoot(m_sel.start()); else { root = shadowTreeRootNode(); if (!root) root = document->documentElement(); } if (!root) return; VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root)); if (m_frame->shouldChangeSelection(newSelection)) setSelection(newSelection); selectFrameElementInParentIfFullySelected(); m_frame->notifyRendererOfSelectionChange(true);}bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping){ if (!range) return false; ExceptionCode ec = 0; Node* startContainer = range->startContainer(ec); if (ec) return false; Node* endContainer = range->endContainer(ec); if (ec) return false; ASSERT(startContainer); ASSERT(endContainer); ASSERT(startContainer->document() == endContainer->document()); m_frame->document()->updateLayoutIgnorePendingStylesheets(); // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped, // they start at the beginning of the next line instead bool collapsed = range->collapsed(ec); if (ec) return false; int startOffset = range->startOffset(ec); if (ec) return false; int endOffset = range->endOffset(ec); if (ec) return false; // FIXME: Can we provide extentAffinity? VisiblePosition visibleStart(startContainer, startOffset, collapsed ? affinity : DOWNSTREAM); VisiblePosition visibleEnd(endContainer, endOffset, SEL_DEFAULT_AFFINITY); setSelection(VisibleSelection(visibleStart, visibleEnd), closeTyping); return true;}bool SelectionController::isInPasswordField() const{ Node* startNode = start().node(); if (!startNode) return false; startNode = startNode->shadowAncestorNode(); if (!startNode) return false; if (!startNode->hasTagName(inputTag)) return false; return static_cast<HTMLInputElement*>(startNode)->inputType() == HTMLInputElement::PASSWORD;}bool SelectionController::caretRendersInsideNode(Node* node) const{ if (!node) return false; return !isTableElement(node) && !editingIgnoresContent(node);}void SelectionController::focusedOrActiveStateChanged(){ bool activeAndFocused = isFocusedAndActive(); // Because RenderObject::selectionBackgroundColor() and // RenderObject::selectionForegroundColor() check if the frame is active, // we have to update places those colors were painted. if (RenderView* view = toRenderView(m_frame->document()->renderer())) view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(m_frame->selectionBounds())); // Caret appears in the active frame. if (activeAndFocused) m_frame->setSelectionFromNone(); m_frame->setCaretVisible(activeAndFocused); // Update for caps lock state m_frame->eventHandler()->capsLockStateMayHaveChanged(); // Because CSSStyleSelector::checkOneSelector() and // RenderTheme::isFocused() check if the frame is active, we have to // update style and theme state that depended on those. if (Node* node = m_frame->document()->focusedNode()) { node->setChanged(); if (RenderObject* renderer = node->renderer()) if (renderer && renderer->style()->hasAppearance()) theme()->stateChanged(renderer, FocusState); } // Secure keyboard entry is set by the active frame. if (m_frame->document()->useSecureKeyboardEntryWhenActive()) m_frame->setUseSecureKeyboardEntry(activeAndFocused);}void SelectionController::pageActivationChanged(){ focusedOrActiveStateChanged();}void SelectionController::setFocused(bool flag){ if (m_focused == flag) return; m_focused = flag; focusedOrActiveStateChanged(); m_frame->document()->dispatchWindowEvent(flag ? eventNames().focusEvent : eventNames().blurEvent, false, false);}bool SelectionController::isFocusedAndActive() const{ return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive();}#ifndef NDEBUGvoid SelectionController::formatForDebugger(char* buffer, unsigned length) const{ m_sel.formatForDebugger(buffer, length);}void SelectionController::showTreeForThis() const{ m_sel.showTreeForThis();}#endif}#ifndef NDEBUGvoid showTree(const WebCore::SelectionController& sel){ sel.showTreeForThis();}void showTree(const WebCore::SelectionController* sel){ if (sel) sel->showTreeForThis();}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -