Diff
Modified: trunk/Source/WebCore/ChangeLog (97496 => 97497)
--- trunk/Source/WebCore/ChangeLog 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebCore/ChangeLog 2011-10-14 19:55:59 UTC (rev 97497)
@@ -1,3 +1,35 @@
+2011-10-14 Daniel Cheng <dch...@chromium.org>
+
+ Context-aware HTML paste for Chromium
+ https://bugs.webkit.org/show_bug.cgi?id=62112
+
+ Reviewed by Ryosuke Niwa.
+
+ Add createFragmentFromMarkupWithContext which understands enough about DOM structure to
+ retain necessary elements to preserve structure and appearance when extracting a subset of
+ a DOM tree.
+
+ Covered by existing layout tests.
+
+ * editing/MarkupAccumulator.h:
+ * editing/markup.cpp:
+ (WebCore::isNonTableCellHTMLBlockElement):
+ (WebCore::isHTMLBlockElement):
+ (WebCore::ancestorToRetainStructureAndAppearanceForBlock):
+ (WebCore::ancestorToRetainStructureAndAppearance):
+ (WebCore::ancestorToRetainStructureAndAppearanceWithNoRenderer):
+ (WebCore::findNodesSurroundingContext):
+ (WebCore::trimFragment):
+ (WebCore::createFragmentFromMarkupWithContext):
+ * editing/markup.h:
+ * platform/chromium/ChromiumDataObject.cpp:
+ (WebCore::ChromiumDataObject::getData):
+ * platform/chromium/DataTransferItemChromium.cpp:
+ (WebCore::DataTransferItemChromium::getAsString):
+ * platform/chromium/PasteboardChromium.cpp:
+ (WebCore::Pasteboard::documentFragment):
+ * platform/chromium/PlatformSupport.h:
+
2011-10-14 Peter Beverloo <pe...@chromium.org>
[Chromium] Inherit settings from Chromium's envsetup.sh, address a NDK todo
Modified: trunk/Source/WebCore/editing/MarkupAccumulator.h (97496 => 97497)
--- trunk/Source/WebCore/editing/MarkupAccumulator.h 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebCore/editing/MarkupAccumulator.h 2011-10-14 19:55:59 UTC (rev 97497)
@@ -71,6 +71,8 @@
String serializeNodes(Node* node, Node* nodeToSkip, EChildrenOnly childrenOnly);
+ static void appendComment(StringBuilder& out, const String& comment);
+
protected:
virtual void appendString(const String&);
void appendStartTag(Node*, Namespaces* = 0);
@@ -86,7 +88,6 @@
void appendNamespace(StringBuilder& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces&);
EntityMask entityMaskForText(Text*) const;
virtual void appendText(StringBuilder& out, Text*);
- void appendComment(StringBuilder& out, const String& comment);
void appendDocumentType(StringBuilder& result, const DocumentType*);
void appendProcessingInstruction(StringBuilder& out, const String& target, const String& data);
virtual void appendElement(StringBuilder& out, Element*, Namespaces*);
Modified: trunk/Source/WebCore/editing/markup.cpp (97496 => 97497)
--- trunk/Source/WebCore/editing/markup.cpp 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebCore/editing/markup.cpp 2011-10-14 19:55:59 UTC (rev 97497)
@@ -428,10 +428,33 @@
return lastClosed;
}
-static Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor)
+bool isNonTableCellHTMLBlockElement(const Node* node)
{
- Node* commonAncestorBlock = enclosingBlock(commonAncestor);
+ return node->hasTagName(listingTag)
+ || node->hasTagName(olTag)
+ || node->hasTagName(preTag)
+ || node->hasTagName(tableTag)
+ || node->hasTagName(ulTag)
+ || node->hasTagName(xmpTag)
+ || node->hasTagName(h1Tag)
+ || node->hasTagName(h2Tag)
+ || node->hasTagName(h3Tag)
+ || node->hasTagName(h4Tag)
+ || node->hasTagName(h5Tag);
+}
+static bool isHTMLBlockElement(const Node* node)
+{
+ return node->hasTagName(tdTag)
+ || node->hasTagName(thTag)
+ || isNonTableCellHTMLBlockElement(node);
+}
+
+// FIXME: Do we want to handle mail quotes here instead?
+// This is currently handled in highestAncestorToWrapMarkup but it might make more
+// sense to move that into here.
+static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestorBlock)
+{
if (!commonAncestorBlock)
return 0;
@@ -443,22 +466,23 @@
return table;
}
- if (commonAncestorBlock->hasTagName(listingTag)
- || commonAncestorBlock->hasTagName(olTag)
- || commonAncestorBlock->hasTagName(preTag)
- || commonAncestorBlock->hasTagName(tableTag)
- || commonAncestorBlock->hasTagName(ulTag)
- || commonAncestorBlock->hasTagName(xmpTag)
- || commonAncestorBlock->hasTagName(h1Tag)
- || commonAncestorBlock->hasTagName(h2Tag)
- || commonAncestorBlock->hasTagName(h3Tag)
- || commonAncestorBlock->hasTagName(h4Tag)
- || commonAncestorBlock->hasTagName(h5Tag))
+ if (isNonTableCellHTMLBlockElement(commonAncestorBlock))
return commonAncestorBlock;
return 0;
}
+static inline Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor)
+{
+ return ancestorToRetainStructureAndAppearanceForBlock(enclosingBlock(commonAncestor));
+}
+
+static inline Node* ancestorToRetainStructureAndAppearanceWithNoRenderer(Node* commonAncestor)
+{
+ Node* commonAncestorBlock = enclosingNodeOfType(firstPositionInOrBeforeNode(commonAncestor), isHTMLBlockElement);
+ return ancestorToRetainStructureAndAppearanceForBlock(commonAncestorBlock);
+}
+
static bool propertyMissingOrEqualToNone(CSSStyleDeclaration* style, int propertyID)
{
if (!style)
@@ -684,6 +708,92 @@
return fragment.release();
}
+static const char fragmentMarkerTag[] = "webkit-fragment-marker";
+
+static bool findNodesSurroundingContext(Document* document, RefPtr<Node>& nodeBeforeContext, RefPtr<Node>& nodeAfterContext)
+{
+ for (Node* node = document->firstChild(); node; node = node->traverseNextNode()) {
+ if (node->nodeType() == Node::COMMENT_NODE && static_cast<CharacterData*>(node)->data() == fragmentMarkerTag) {
+ if (!nodeBeforeContext)
+ nodeBeforeContext = node;
+ else {
+ nodeAfterContext = node;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void trimFragment(DocumentFragment* fragment, Node* nodeBeforeContext, Node* nodeAfterContext)
+{
+ ExceptionCode ec = 0;
+ Node* next;
+ for (RefPtr<Node> node = fragment->firstChild(); node; node = next) {
+ if (nodeBeforeContext->isDescendantOf(node.get())) {
+ next = node->traverseNextNode();
+ continue;
+ }
+ next = node->traverseNextSibling();
+ ASSERT(!node->contains(nodeAfterContext));
+ node->parentNode()->removeChild(node.get(), ec);
+ if (nodeBeforeContext == node)
+ break;
+ }
+
+ ASSERT(nodeAfterContext->parentNode());
+ for (Node* node = nodeAfterContext; node; node = next) {
+ next = node->traverseNextSibling();
+ node->parentNode()->removeChild(node, ec);
+ ASSERT(!ec);
+ }
+}
+
+PassRefPtr<DocumentFragment> createFragmentFromMarkupWithContext(Document* document, const String& markup, unsigned fragmentStart, unsigned fragmentEnd,
+ const String& baseURL, FragmentScriptingPermission scriptingPermission)
+{
+ // FIXME: Need to handle the case where the markup already contains these markers.
+
+ StringBuilder taggedMarkup;
+ taggedMarkup.append(markup.left(fragmentStart));
+ MarkupAccumulator::appendComment(taggedMarkup, fragmentMarkerTag);
+ taggedMarkup.append(markup.substring(fragmentStart, fragmentEnd - fragmentStart));
+ MarkupAccumulator::appendComment(taggedMarkup, fragmentMarkerTag);
+ taggedMarkup.append(markup.substring(fragmentEnd));
+
+ RefPtr<DocumentFragment> taggedFragment = createFragmentFromMarkup(document, taggedMarkup.toString(), baseURL, scriptingPermission);
+ RefPtr<Document> taggedDocument = Document::create(0, KURL());
+ taggedDocument->takeAllChildrenFrom(taggedFragment.get());
+
+ RefPtr<Node> nodeBeforeContext;
+ RefPtr<Node> nodeAfterContext;
+ if (!findNodesSurroundingContext(taggedDocument.get(), nodeBeforeContext, nodeAfterContext))
+ return 0;
+
+ RefPtr<Range> range = Range::create(taggedDocument.get(),
+ positionAfterNode(nodeBeforeContext.get()).parentAnchoredEquivalent(),
+ positionBeforeNode(nodeAfterContext.get()).parentAnchoredEquivalent());
+
+ ExceptionCode ec = 0;
+ Node* commonAncestor = range->commonAncestorContainer(ec);
+ ASSERT(!ec);
+ Node* specialCommonAncestor = ancestorToRetainStructureAndAppearanceWithNoRenderer(commonAncestor);
+
+ // When there's a special common ancestor outside of the fragment, we must include it as well to
+ // preserve the structure and appearance of the fragment. For example, if the fragment contains
+ // TD, we need to include the enclosing TABLE tag as well.
+ RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
+ if (specialCommonAncestor) {
+ fragment->appendChild(specialCommonAncestor, ec);
+ ASSERT(!ec);
+ } else
+ fragment->takeAllChildrenFrom(static_cast<ContainerNode*>(commonAncestor));
+
+ trimFragment(fragment.get(), nodeBeforeContext.get(), nodeAfterContext.get());
+
+ return fragment;
+}
+
String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs)
{
if (!node)
Modified: trunk/Source/WebCore/editing/markup.h (97496 => 97497)
--- trunk/Source/WebCore/editing/markup.h 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebCore/editing/markup.h 2011-10-14 19:55:59 UTC (rev 97497)
@@ -45,6 +45,7 @@
PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text);
PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document*, const String& markup, const String& baseURL, FragmentScriptingPermission = FragmentScriptingAllowed);
+ PassRefPtr<DocumentFragment> createFragmentFromMarkupWithContext(Document*, const String& markup, unsigned fragmentStart, unsigned fragmentEnd, const String& baseURL, FragmentScriptingPermission);
PassRefPtr<DocumentFragment> createFragmentFromNodes(Document*, const Vector<Node*>&);
bool isPlainTextMarkup(Node *node);
Modified: trunk/Source/WebCore/platform/chromium/ChromiumDataObject.cpp (97496 => 97497)
--- trunk/Source/WebCore/platform/chromium/ChromiumDataObject.cpp 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebCore/platform/chromium/ChromiumDataObject.cpp 2011-10-14 19:55:59 UTC (rev 97497)
@@ -160,7 +160,8 @@
PasteboardPrivate::StandardBuffer;
String htmlText;
KURL sourceURL;
- PlatformSupport::clipboardReadHTML(buffer, &htmlText, &sourceURL);
+ unsigned ignored;
+ PlatformSupport::clipboardReadHTML(buffer, &htmlText, &sourceURL, &ignored, &ignored);
success = !htmlText.isEmpty();
return htmlText;
}
Modified: trunk/Source/WebCore/platform/chromium/DataTransferItemChromium.cpp (97496 => 97497)
--- trunk/Source/WebCore/platform/chromium/DataTransferItemChromium.cpp 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebCore/platform/chromium/DataTransferItemChromium.cpp 2011-10-14 19:55:59 UTC (rev 97497)
@@ -97,7 +97,8 @@
if (type() == mimeTypeTextHTML) {
String html;
KURL ignoredSourceURL;
- PlatformSupport::clipboardReadHTML(PasteboardPrivate::StandardBuffer, &html, &ignoredSourceURL);
+ unsigned ignored;
+ PlatformSupport::clipboardReadHTML(PasteboardPrivate::StandardBuffer, &html, &ignoredSourceURL, &ignored, &ignored);
callback->scheduleCallback(m_context, html);
return;
}
Modified: trunk/Source/WebCore/platform/chromium/PasteboardChromium.cpp (97496 => 97497)
--- trunk/Source/WebCore/platform/chromium/PasteboardChromium.cpp 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebCore/platform/chromium/PasteboardChromium.cpp 2011-10-14 19:55:59 UTC (rev 97497)
@@ -173,10 +173,12 @@
if (PlatformSupport::clipboardIsFormatAvailable(PasteboardPrivate::HTMLFormat, buffer)) {
String markup;
KURL srcURL;
- PlatformSupport::clipboardReadHTML(buffer, &markup, &srcURL);
+ unsigned fragmentStart = 0;
+ unsigned fragmentEnd = 0;
+ PlatformSupport::clipboardReadHTML(buffer, &markup, &srcURL, &fragmentStart, &fragmentEnd);
RefPtr<DocumentFragment> fragment =
- createFragmentFromMarkup(frame->document(), markup, srcURL, FragmentScriptingNotAllowed);
+ createFragmentFromMarkupWithContext(frame->document(), markup, fragmentStart, fragmentEnd, srcURL, FragmentScriptingNotAllowed);
if (fragment)
return fragment.release();
}
Modified: trunk/Source/WebCore/platform/chromium/PlatformSupport.h (97496 => 97497)
--- trunk/Source/WebCore/platform/chromium/PlatformSupport.h 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebCore/platform/chromium/PlatformSupport.h 2011-10-14 19:55:59 UTC (rev 97497)
@@ -98,7 +98,7 @@
static bool clipboardIsFormatAvailable(PasteboardPrivate::ClipboardFormat, PasteboardPrivate::ClipboardBuffer);
static String clipboardReadPlainText(PasteboardPrivate::ClipboardBuffer);
- static void clipboardReadHTML(PasteboardPrivate::ClipboardBuffer, String*, KURL*);
+ static void clipboardReadHTML(PasteboardPrivate::ClipboardBuffer, String*, KURL*, unsigned* fragmentStart, unsigned* fragmentEnd);
static PassRefPtr<SharedBuffer> clipboardReadImage(PasteboardPrivate::ClipboardBuffer);
static uint64_t clipboardGetSequenceNumber();
Modified: trunk/Source/WebKit/chromium/ChangeLog (97496 => 97497)
--- trunk/Source/WebKit/chromium/ChangeLog 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebKit/chromium/ChangeLog 2011-10-14 19:55:59 UTC (rev 97497)
@@ -1,3 +1,17 @@
+2011-10-14 Daniel Cheng <dch...@chromium.org>
+
+ Context-aware HTML paste for Chromium
+ https://bugs.webkit.org/show_bug.cgi?id=62112
+
+ Reviewed by Ryosuke Niwa.
+
+ Add WebKit side for plumbing to receive context for HTML paste.
+
+ * public/WebClipboard.h:
+ (WebKit::WebClipboard::readHTML):
+ * src/PlatformSupport.cpp:
+ (WebCore::PlatformSupport::clipboardReadHTML):
+
2011-10-14 Peter Beverloo <pe...@chromium.org>
[Chromium] Inherit settings from Chromium's envsetup.sh, address a NDK todo
Modified: trunk/Source/WebKit/chromium/public/WebClipboard.h (97496 => 97497)
--- trunk/Source/WebKit/chromium/public/WebClipboard.h 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebKit/chromium/public/WebClipboard.h 2011-10-14 19:55:59 UTC (rev 97497)
@@ -63,7 +63,14 @@
virtual bool isFormatAvailable(Format, Buffer) { return false; }
virtual WebString readPlainText(Buffer) { return WebString(); }
- virtual WebString readHTML(Buffer, WebURL*) { return WebString(); }
+ // fragmentStart and fragmentEnd are indexes into the returned markup that
+ // indicate the start and end of the fragment if the returned markup
+ // contains additional context. If there is no additional context,
+ // fragmentStart will be zero and fragmentEnd will be the same as the length
+ // of the returned markup.
+ virtual WebString readHTML(
+ Buffer buffer, WebURL* pageURL, unsigned* fragmentStart,
+ unsigned* fragmentEnd) { return WebString(); }
virtual WebData readImage(Buffer) { return WebData(); }
// Returns an identifier which can be used to determine whether the data
@@ -87,7 +94,7 @@
// paste, drag and drop, and selection copy (on X).
virtual WebVector<WebString> readAvailableTypes(
Buffer, bool* containsFilenames) { return WebVector<WebString>(); }
- // Returns true if the requested type was successfully read from the buffer.
+ // Returns true if the requested type was successfully read from the buffer.
virtual bool readData(
Buffer, const WebString& type, WebString* data,
WebString* metadata) { return false; }
Modified: trunk/Source/WebKit/chromium/src/PlatformSupport.cpp (97496 => 97497)
--- trunk/Source/WebKit/chromium/src/PlatformSupport.cpp 2011-10-14 19:52:53 UTC (rev 97496)
+++ trunk/Source/WebKit/chromium/src/PlatformSupport.cpp 2011-10-14 19:55:59 UTC (rev 97497)
@@ -168,11 +168,11 @@
void PlatformSupport::clipboardReadHTML(
PasteboardPrivate::ClipboardBuffer buffer,
- String* htmlText, KURL* sourceURL)
+ String* htmlText, KURL* sourceURL, unsigned* fragmentStart, unsigned* fragmentEnd)
{
WebURL url;
*htmlText = webKitPlatformSupport()->clipboard()->readHTML(
- static_cast<WebClipboard::Buffer>(buffer), &url);
+ static_cast<WebClipboard::Buffer>(buffer), &url, fragmentStart, fragmentEnd);
*sourceURL = url;
}