📄 replaceselectioncommand.cpp
字号:
/* * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "config.h"#include "ReplaceSelectionCommand.h"#include "ApplyStyleCommand.h"#include "BeforeTextInsertedEvent.h"#include "BreakBlockquoteCommand.h" #include "CSSComputedStyleDeclaration.h"#include "CSSProperty.h"#include "CSSPropertyNames.h"#include "CSSValueKeywords.h"#include "Document.h"#include "DocumentFragment.h"#include "EditingText.h"#include "EventNames.h"#include "Element.h"#include "Frame.h"#include "HTMLElement.h"#include "HTMLInterchange.h"#include "HTMLInputElement.h"#include "HTMLNames.h"#include "SelectionController.h"#include "SmartReplace.h"#include "TextIterator.h"#include "htmlediting.h"#include "markup.h"#include "visible_units.h"#include <wtf/StdLibExtras.h>namespace WebCore {using namespace HTMLNames;enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment };// --- ReplacementFragment helper classclass ReplacementFragment : Noncopyable {public: ReplacementFragment(Document*, DocumentFragment*, bool matchStyle, const VisibleSelection&); Node* firstChild() const; Node* lastChild() const; bool isEmpty() const; bool hasInterchangeNewlineAtStart() const { return m_hasInterchangeNewlineAtStart; } bool hasInterchangeNewlineAtEnd() const { return m_hasInterchangeNewlineAtEnd; } void removeNode(PassRefPtr<Node>); void removeNodePreservingChildren(Node*);private: PassRefPtr<Node> insertFragmentForTestRendering(Node* context); void removeUnrenderedNodes(Node*); void restoreTestRenderingNodesToFragment(Node*); void removeInterchangeNodes(Node*); void insertNodeBefore(PassRefPtr<Node> node, Node* refNode); RefPtr<Document> m_document; RefPtr<DocumentFragment> m_fragment; bool m_matchStyle; bool m_hasInterchangeNewlineAtStart; bool m_hasInterchangeNewlineAtEnd;};static bool isInterchangeNewlineNode(const Node *node){ DEFINE_STATIC_LOCAL(String, interchangeNewlineClassString, (AppleInterchangeNewline)); return node && node->hasTagName(brTag) && static_cast<const Element *>(node)->getAttribute(classAttr) == interchangeNewlineClassString;}static bool isInterchangeConvertedSpaceSpan(const Node *node){ DEFINE_STATIC_LOCAL(String, convertedSpaceSpanClassString, (AppleConvertedSpace)); return node->isHTMLElement() && static_cast<const HTMLElement *>(node)->getAttribute(classAttr) == convertedSpaceSpanClassString;}ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* fragment, bool matchStyle, const VisibleSelection& selection) : m_document(document), m_fragment(fragment), m_matchStyle(matchStyle), m_hasInterchangeNewlineAtStart(false), m_hasInterchangeNewlineAtEnd(false){ if (!m_document) return; if (!m_fragment) return; if (!m_fragment->firstChild()) return; Element* editableRoot = selection.rootEditableElement(); ASSERT(editableRoot); if (!editableRoot) return; Node* shadowAncestorNode = editableRoot->shadowAncestorNode(); if (!editableRoot->inlineEventListenerForType(eventNames().webkitBeforeTextInsertedEvent) && // FIXME: Remove these checks once textareas and textfields actually register an event handler. !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextControl()) && editableRoot->isContentRichlyEditable()) { removeInterchangeNodes(m_fragment.get()); return; } Node* styleNode = selection.base().node(); RefPtr<Node> holder = insertFragmentForTestRendering(styleNode); RefPtr<Range> range = VisibleSelection::selectionFromContentsOfNode(holder.get()).toNormalizedRange(); String text = plainText(range.get()); // Give the root a chance to change the text. RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text); ExceptionCode ec = 0; editableRoot->dispatchEvent(evt, ec); ASSERT(ec == 0); if (text != evt->text() || !editableRoot->isContentRichlyEditable()) { restoreTestRenderingNodesToFragment(holder.get()); removeNode(holder); m_fragment = createFragmentFromText(selection.toNormalizedRange().get(), evt->text()); if (!m_fragment->firstChild()) return; holder = insertFragmentForTestRendering(styleNode); } removeInterchangeNodes(holder.get()); removeUnrenderedNodes(holder.get()); restoreTestRenderingNodesToFragment(holder.get()); removeNode(holder);}bool ReplacementFragment::isEmpty() const{ return (!m_fragment || !m_fragment->firstChild()) && !m_hasInterchangeNewlineAtStart && !m_hasInterchangeNewlineAtEnd;}Node *ReplacementFragment::firstChild() const { return m_fragment ? m_fragment->firstChild() : 0; }Node *ReplacementFragment::lastChild() const { return m_fragment ? m_fragment->lastChild() : 0; }void ReplacementFragment::removeNodePreservingChildren(Node *node){ if (!node) return; while (RefPtr<Node> n = node->firstChild()) { removeNode(n); insertNodeBefore(n.release(), node); } removeNode(node);}void ReplacementFragment::removeNode(PassRefPtr<Node> node){ if (!node) return; Node *parent = node->parentNode(); if (!parent) return; ExceptionCode ec = 0; parent->removeChild(node.get(), ec); ASSERT(ec == 0);}void ReplacementFragment::insertNodeBefore(PassRefPtr<Node> node, Node* refNode){ if (!node || !refNode) return; Node* parent = refNode->parentNode(); if (!parent) return; ExceptionCode ec = 0; parent->insertBefore(node, refNode, ec); ASSERT(ec == 0);}PassRefPtr<Node> ReplacementFragment::insertFragmentForTestRendering(Node* context){ Node* body = m_document->body(); if (!body) return 0; RefPtr<StyledElement> holder = createDefaultParagraphElement(m_document.get()); ExceptionCode ec = 0; // Copy the whitespace and user-select style from the context onto this element. // FIXME: We should examine other style properties to see if they would be appropriate to consider during the test rendering. Node* n = context; while (n && !n->isElementNode()) n = n->parentNode(); if (n) { RefPtr<CSSComputedStyleDeclaration> conFontStyle = computedStyle(n); CSSStyleDeclaration* style = holder->style(); style->setProperty(CSSPropertyWhiteSpace, conFontStyle->getPropertyValue(CSSPropertyWhiteSpace), false, ec); ASSERT(ec == 0); style->setProperty(CSSPropertyWebkitUserSelect, conFontStyle->getPropertyValue(CSSPropertyWebkitUserSelect), false, ec); ASSERT(ec == 0); } holder->appendChild(m_fragment, ec); ASSERT(ec == 0); body->appendChild(holder.get(), ec); ASSERT(ec == 0); m_document->updateLayoutIgnorePendingStylesheets(); return holder.release();}void ReplacementFragment::restoreTestRenderingNodesToFragment(Node *holder){ if (!holder) return; ExceptionCode ec = 0; while (RefPtr<Node> node = holder->firstChild()) { holder->removeChild(node.get(), ec); ASSERT(ec == 0); m_fragment->appendChild(node.get(), ec); ASSERT(ec == 0); }}void ReplacementFragment::removeUnrenderedNodes(Node* holder){ Vector<Node*> unrendered; for (Node* node = holder->firstChild(); node; node = node->traverseNextNode(holder))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -