Assertion in LegacyWebArchive::create() in editing tests
[WebKit-https.git] / Source / WebCore / platform / mac / PasteboardMac.mm
index dce160d..c20b7bb 100644 (file)
@@ -34,6 +34,7 @@
 #import "DocumentLoader.h"
 #import "Editor.h"
 #import "EditorClient.h"
+#import "ExceptionCodePlaceholder.h"
 #import "Frame.h"
 #import "FrameView.h"
 #import "FrameLoaderClient.h"
@@ -49,6 +50,7 @@
 #import "MIMETypeRegistry.h"
 #import "Page.h"
 #import "RenderImage.h"
+#import "ResourceBuffer.h"
 #import "Text.h"
 #import "WebCoreNSStringExtras.h"
 #import "WebNSAttributedStringExtras.h"
@@ -56,6 +58,7 @@
 #import <wtf/StdLibExtras.h>
 #import <wtf/RetainPtr.h>
 #import <wtf/UnusedParam.h>
+#import <wtf/text/StringBuilder.h>
 #import <wtf/unicode/CharacterNames.h>
 
 #if USE(PLATFORM_STRATEGIES)
@@ -132,79 +135,101 @@ void Pasteboard::clear()
     platformStrategies()->pasteboardStrategy()->setTypes(Vector<String>(), m_pasteboardName);
 }
 
-void Pasteboard::writeSelectionForTypes(const Vector<String>& pasteboardTypes, Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame)
+String Pasteboard::getStringSelection(Frame* frame)
 {
-    if (!WebArchivePboardType)
-        Pasteboard::generalPasteboard(); // Initializes pasteboard types.
-    ASSERT(selectedRange);
-    
-    // If the selection is at the beginning of content inside an anchor tag
-    // we move the selection start to include the anchor.
-    // This way the attributed string will contain the url attribute as well.
-    // See <rdar://problem/9084267>.
-    ExceptionCode ec;
-    Node* commonAncestor = selectedRange->commonAncestorContainer(ec);
-    ASSERT(commonAncestor);
-    Node* enclosingAnchor = enclosingNodeWithTag(firstPositionInNode(commonAncestor), HTMLNames::aTag);
-    if (enclosingAnchor && comparePositions(firstPositionInOrBeforeNode(selectedRange->startPosition().anchorNode()), selectedRange->startPosition()) >= 0)
-        selectedRange->setStart(enclosingAnchor, 0, ec);
-
-    NSAttributedString *attributedString = nil;
-    RetainPtr<WebHTMLConverter> converter(AdoptNS, [[WebHTMLConverter alloc] initWithDOMRange:kit(selectedRange)]);
-    if (converter)
-        attributedString = [converter.get() attributedString];
+    String text = frame->editor()->selectedText();
+    text.replace(noBreakSpace, ' ');
+    return text;
+}
 
-    const Vector<String> types = !pasteboardTypes.isEmpty() ? pasteboardTypes : selectionPasteboardTypes(canSmartCopyOrDelete, [attributedString containsAttachments]);
-    platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
-    frame->editor()->client()->didSetSelectionTypesForPasteboard();
-    
-    // Put HTML on the pasteboard.
-    if (types.contains(WebArchivePboardType)) {
+PassRefPtr<SharedBuffer> Pasteboard::getDataSelection(Frame* frame, const String& pasteboardType)
+{
+    if (pasteboardType == WebArchivePboardType) {
         RefPtr<LegacyWebArchive> archive = LegacyWebArchive::createFromSelection(frame);
         RetainPtr<CFDataRef> data = archive ? archive->rawDataRepresentation() : 0;
-        platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData((NSData *)data.get()), WebArchivePboardType, m_pasteboardName);
+        return SharedBuffer::wrapNSData((NSData *)data.get());
     }
+
+    RefPtr<Range> range = frame->editor()->selectedRange();
+    Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION);
+    ASSERT(commonAncestor);
+    Node* enclosingAnchor = enclosingNodeWithTag(firstPositionInNode(commonAncestor), HTMLNames::aTag);
+    if (enclosingAnchor && comparePositions(firstPositionInOrBeforeNode(range->startPosition().anchorNode()), range->startPosition()) >= 0)
+        range->setStart(enclosingAnchor, 0, IGNORE_EXCEPTION);
     
-    // Put the attributed string on the pasteboard (RTF/RTFD format).
-    if (types.contains(String(NSRTFDPboardType))) {
+    NSAttributedString* attributedString = nil;
+    RetainPtr<WebHTMLConverter> converter(AdoptNS, [[WebHTMLConverter alloc] initWithDOMRange:kit(range.get())]);
+    if (converter)
+        attributedString = [converter.get() attributedString];
+    
+    if (pasteboardType == String(NSRTFDPboardType)) {
         NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
-        platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData((NSData *)RTFDData).get(), NSRTFDPboardType, m_pasteboardName);
+        return SharedBuffer::wrapNSData((NSData *)RTFDData);
     }
-    if (types.contains(String(NSRTFPboardType))) {
+    if (pasteboardType == String(NSRTFPboardType)) {
         if ([attributedString containsAttachments])
             attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
         NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
-        platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData((NSData *)RTFData).get(), NSRTFPboardType, m_pasteboardName);
+        return SharedBuffer::wrapNSData((NSData *)RTFData);
     }
+    return 0;
+}
+
+void Pasteboard::writeSelectionForTypes(const Vector<String>& pasteboardTypes, bool canSmartCopyOrDelete, Frame* frame)
+{
+    NSAttributedString* attributedString = nil;
+    RetainPtr<WebHTMLConverter> converter(AdoptNS, [[WebHTMLConverter alloc] initWithDOMRange:kit(frame->editor()->selectedRange().get())]);
+    if (converter)
+        attributedString = [converter.get() attributedString];
+    
+    Vector<String> types = !pasteboardTypes.isEmpty() ? pasteboardTypes : selectionPasteboardTypes(canSmartCopyOrDelete, [attributedString containsAttachments]);
+
+    Vector<String> clientTypes;
+    Vector<RefPtr<SharedBuffer> > clientData;
+    frame->editor()->client()->getClientPasteboardDataForRange(frame->editor()->selectedRange().get(), clientTypes, clientData);
+    types.append(clientTypes);
+
+    platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
+    frame->editor()->client()->didSetSelectionTypesForPasteboard();
+
+    for (size_t i = 0; i < clientTypes.size(); ++i)
+        platformStrategies()->pasteboardStrategy()->setBufferForType(clientData[i], clientTypes[i], m_pasteboardName);
+
+    // Put HTML on the pasteboard.
+    if (types.contains(WebArchivePboardType))
+        platformStrategies()->pasteboardStrategy()->setBufferForType(getDataSelection(frame, WebArchivePboardType), WebArchivePboardType, m_pasteboardName);
+    
+    // Put the attributed string on the pasteboard (RTF/RTFD format).
+    if (types.contains(String(NSRTFDPboardType)))
+        platformStrategies()->pasteboardStrategy()->setBufferForType(getDataSelection(frame, NSRTFDPboardType), NSRTFDPboardType, m_pasteboardName);
+
+    if (types.contains(String(NSRTFPboardType)))
+        platformStrategies()->pasteboardStrategy()->setBufferForType(getDataSelection(frame, NSRTFPboardType), NSRTFPboardType, m_pasteboardName);
     
     // Put plain string on the pasteboard.
-    if (types.contains(String(NSStringPboardType))) {
-        // Map &nbsp; to a plain old space because this is better for source code, other browsers do it,
-        // and because HTML forces you to do this any time you want two spaces in a row.
-        String text = frame->editor()->selectedText();
-        NSMutableString *s = [[[(NSString*)text copy] autorelease] mutableCopy];
-        
-        NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&noBreakSpace length:1];
-        [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
-        platformStrategies()->pasteboardStrategy()->setStringForType(s, NSStringPboardType, m_pasteboardName);
-        [s release];
-    }
+    if (types.contains(String(NSStringPboardType)))
+        platformStrategies()->pasteboardStrategy()->setStringForType(getStringSelection(frame), NSStringPboardType, m_pasteboardName);
     
     if (types.contains(WebSmartPastePboardType))
         platformStrategies()->pasteboardStrategy()->setBufferForType(0, WebSmartPastePboardType, m_pasteboardName);
 }
 
-void Pasteboard::writePlainText(const String& text)
+void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption)
 {
     Vector<String> types;
     types.append(NSStringPboardType);
+    if (smartReplaceOption == CanSmartReplace)
+        types.append(WebSmartPastePboardType);
+
     platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
     platformStrategies()->pasteboardStrategy()->setStringForType(text, NSStringPboardType, m_pasteboardName);
+    if (smartReplaceOption == CanSmartReplace)
+        platformStrategies()->pasteboardStrategy()->setBufferForType(0, WebSmartPastePboardType, m_pasteboardName);
 }
     
-void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame)
+void Pasteboard::writeSelection(Range*, bool canSmartCopyOrDelete, Frame* frame)
 {
-    writeSelectionForTypes(Vector<String>(), selectedRange, canSmartCopyOrDelete, frame);
+    writeSelectionForTypes(Vector<String>(), canSmartCopyOrDelete, frame);
 }
 
 static void writeURLForTypes(const Vector<String>& types, const String& pasteboardName, const KURL& url, const String& titleStr, Frame* frame)
@@ -224,7 +249,7 @@ static void writeURLForTypes(const Vector<String>& types, const String& pasteboa
     }
     if (types.contains(WebURLsWithTitlesPboardType)) {
         Vector<String> paths;
-        paths.append(userVisibleString);
+        paths.append([cocoaURL absoluteString]);
         paths.append(titleStr.stripWhiteSpace());
         platformStrategies()->pasteboardStrategy()->setPathnamesForType(paths, WebURLsWithTitlesPboardType, pasteboardName);
     }
@@ -245,7 +270,7 @@ void Pasteboard::writeURL(const KURL& url, const String& titleStr, Frame* frame)
 
 static NSFileWrapper* fileWrapperForImage(CachedResource* resource, NSURL *url)
 {
-    SharedBuffer* coreData = resource->data();
+    ResourceBuffer* coreData = resource->resourceBuffer();
     NSData *data = [[[NSData alloc] initWithBytes:coreData->data() length:coreData->size()] autorelease];
     NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease];
     String coreMIMEType = resource->response().mimeType();
@@ -264,7 +289,8 @@ static void writeFileWrapperAsRTFDAttachment(NSFileWrapper* wrapper, const Strin
     [attachment release];
     
     NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
-    platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData((NSData *)RTFDData).get(), NSRTFDPboardType, pasteboardName);
+    if (RTFDData)
+        platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData(RTFDData).get(), NSRTFDPboardType, pasteboardName);
 }
 
 void Pasteboard::writeImage(Node* node, const KURL& url, const String& title)
@@ -285,9 +311,12 @@ void Pasteboard::writeImage(Node* node, const KURL& url, const String& title)
     writeURLForTypes(writableTypesForImage(), m_pasteboardName, cocoaURL, nsStringNilIfEmpty(title), node->document()->frame());
     
     Image* image = cachedImage->imageForRenderer(renderer);
-    ASSERT(image);
-    
-    platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData((NSData *)[image->getNSImage() TIFFRepresentation]), NSTIFFPboardType, m_pasteboardName);
+    if (!image)
+        return;
+    NSData *imageData = (NSData *)[image->getNSImage() TIFFRepresentation];
+    if (!imageData)
+        return;
+    platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData(imageData), NSTIFFPboardType, m_pasteboardName);
 
     String MIMEType = cachedImage->response().mimeType();
     ASSERT(MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType));
@@ -316,15 +345,17 @@ String Pasteboard::plainText(Frame* frame)
         return [(NSString *)platformStrategies()->pasteboardStrategy()->stringForType(NSStringPboardType, m_pasteboardName) precomposedStringWithCanonicalMapping];
     
     NSAttributedString *attributedString = nil;
-    NSString *string = nil;
+    NSString *string;
 
     if (types.contains(String(NSRTFDPboardType))) {
         RefPtr<SharedBuffer> data = platformStrategies()->pasteboardStrategy()->bufferForType(NSRTFDPboardType, m_pasteboardName);
-        attributedString = [[NSAttributedString alloc] initWithRTFD:[data->createNSData() autorelease] documentAttributes:NULL];
+        if (data)
+            attributedString = [[NSAttributedString alloc] initWithRTFD:[data->createNSData() autorelease] documentAttributes:NULL];
     }
     if (attributedString == nil && types.contains(String(NSRTFPboardType))) {
         RefPtr<SharedBuffer> data = platformStrategies()->pasteboardStrategy()->bufferForType(NSRTFPboardType, m_pasteboardName);
-        attributedString = [[NSAttributedString alloc] initWithRTF:[data->createNSData() autorelease] documentAttributes:NULL];
+        if (data)
+            attributedString = [[NSAttributedString alloc] initWithRTF:[data->createNSData() autorelease] documentAttributes:NULL];
     }
     if (attributedString != nil) {
         string = [[attributedString string] precomposedStringWithCanonicalMapping];
@@ -335,11 +366,11 @@ String Pasteboard::plainText(Frame* frame)
     if (types.contains(String(NSFilenamesPboardType))) {
         Vector<String> pathnames;
         platformStrategies()->pasteboardStrategy()->getPathnamesForType(pathnames, NSFilenamesPboardType, m_pasteboardName);
+        StringBuilder builder;
         for (size_t i = 0; i < pathnames.size(); i++)
-            string = [string length] ? @"\n" + pathnames[i] : pathnames[i];
-        string = [string precomposedStringWithCanonicalMapping];
-        if (string != nil)
-            return string;
+            builder.append(i ? "\n" + pathnames[i] : pathnames[i]);
+        string = builder.toString();
+        return [string precomposedStringWithCanonicalMapping];
     }
     
     string = platformStrategies()->pasteboardStrategy()->stringForType(NSURLPboardType, m_pasteboardName);
@@ -358,6 +389,9 @@ String Pasteboard::plainText(Frame* frame)
     
 static PassRefPtr<DocumentFragment> documentFragmentWithImageResource(Frame* frame, PassRefPtr<ArchiveResource> resource)
 {
+    if (!resource)
+        return 0;
+    
     if (DocumentLoader* loader = frame->loader()->documentLoader())
         loader->addArchiveResource(resource.get());
 
@@ -369,9 +403,8 @@ static PassRefPtr<DocumentFragment> documentFragmentWithImageResource(Frame* fra
     imageElement->setAttribute(HTMLNames::srcAttr, [URL isFileURL] ? [URL absoluteString] : resource->url());
     RefPtr<DocumentFragment> fragment = frame->document()->createDocumentFragment();
     if (fragment) {
-        ExceptionCode ec;
-        fragment->appendChild(imageElement, ec);
-        return fragment.release();       
+        fragment->appendChild(imageElement, IGNORE_EXCEPTION);
+        return fragment.release();
     }
     return 0;
 }
@@ -384,11 +417,13 @@ static PassRefPtr<DocumentFragment> documentFragmentWithRTF(Frame* frame, NSStri
     NSAttributedString *string = nil;
     if (pasteboardType == NSRTFDPboardType) {
         RefPtr<SharedBuffer> data = platformStrategies()->pasteboardStrategy()->bufferForType(NSRTFDPboardType, pastebordName);
-        string = [[NSAttributedString alloc] initWithRTFD:[data->createNSData() autorelease] documentAttributes:NULL];
+        if (data)
+            string = [[NSAttributedString alloc] initWithRTFD:[data->createNSData() autorelease] documentAttributes:NULL];
     }
     if (string == nil) {
         RefPtr<SharedBuffer> data = platformStrategies()->pasteboardStrategy()->bufferForType(NSRTFPboardType, pastebordName);
-        string = [[NSAttributedString alloc] initWithRTF:[data->createNSData() autorelease] documentAttributes:NULL];
+        if (data)
+            string = [[NSAttributedString alloc] initWithRTF:[data->createNSData() autorelease] documentAttributes:NULL];
     }
     if (string == nil)
         return nil;
@@ -427,6 +462,30 @@ static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
     return URL;
 }
 
+static PassRefPtr<DocumentFragment> fragmentFromWebArchive(Frame* frame, PassRefPtr<LegacyWebArchive> coreArchive)
+{
+    RefPtr<ArchiveResource> mainResource = coreArchive->mainResource();
+    if (!mainResource)
+        return 0;
+
+    const String& MIMEType = mainResource->mimeType();
+    if (!frame || !frame->document())
+        return 0;
+
+    if (frame->loader()->client()->canShowMIMETypeAsHTML(MIMEType)) {
+        RetainPtr<NSString> markupString(AdoptNS, [[NSString alloc] initWithData:[mainResource->data()->createNSData() autorelease] encoding:NSUTF8StringEncoding]);
+        // FIXME: seems poor form to do this as a side effect of getting a document fragment
+        if (DocumentLoader* loader = frame->loader()->documentLoader())
+            loader->addAllArchiveResources(coreArchive.get());
+        return createFragmentFromMarkup(frame->document(), markupString.get(), mainResource->url(), DisallowScriptingAndPluginContent);
+    }
+
+    if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType))
+        return documentFragmentWithImageResource(frame, mainResource);
+
+    return 0;
+}
+
 PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefPtr<Range> context, bool allowPlainText, bool& chosePlainText)
 {
     Vector<String> types;
@@ -435,28 +494,13 @@ PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefP
     chosePlainText = false;
 
     if (types.contains(WebArchivePboardType)) {
-        RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::create(KURL(), platformStrategies()->pasteboardStrategy()->bufferForType(WebArchivePboardType, m_pasteboardName).get());
-        if (coreArchive) {
-            RefPtr<ArchiveResource> mainResource = coreArchive->mainResource();
-            if (mainResource) {
-                NSString *MIMEType = mainResource->mimeType();
-                if (!frame || !frame->document())
-                    return 0;
-                if (frame->loader()->client()->canShowMIMETypeAsHTML(MIMEType)) {
-                    NSString *markupString = [[NSString alloc] initWithData:[mainResource->data()->createNSData() autorelease] encoding:NSUTF8StringEncoding];
-                    // FIXME: seems poor form to do this as a side effect of getting a document fragment
-                    if (DocumentLoader* loader = frame->loader()->documentLoader())
-                        loader->addAllArchiveResources(coreArchive.get());
-                    
-                    fragment = createFragmentFromMarkup(frame->document(), markupString, mainResource->url(), FragmentScriptingNotAllowed);
-                    [markupString release];
-                } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType))
-                   fragment = documentFragmentWithImageResource(frame, mainResource);                    
+        if (RefPtr<SharedBuffer> webArchiveBuffer = platformStrategies()->pasteboardStrategy()->bufferForType(WebArchivePboardType, m_pasteboardName)) {
+            if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::create(KURL(), webArchiveBuffer.get())) {
+                if ((fragment = fragmentFromWebArchive(frame, coreArchive)))
+                    return fragment.release();
             }
         }
-        if (fragment)
-            return fragment.release();
-    } 
+    }
 
     if (types.contains(String(NSFilenamesPboardType))) {
         Vector<String> paths;
@@ -487,7 +531,7 @@ PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefP
             }
         }
         if ([HTMLString length] != 0 &&
-            (fragment = createFragmentFromMarkup(frame->document(), HTMLString, "", FragmentScriptingNotAllowed)))
+            (fragment = createFragmentFromMarkup(frame->document(), HTMLString, "", DisallowScriptingAndPluginContent)))
             return fragment.release();
     }
 
@@ -512,7 +556,7 @@ PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefP
         return fragment.release();
 
     if (types.contains(String(NSURLPboardType))) {
-        NSURL *URL = [NSURL URLWithString:platformStrategies()->pasteboardStrategy()->stringForType(NSURLPboardType, m_pasteboardName)];
+        NSURL *URL = platformStrategies()->pasteboardStrategy()->url(m_pasteboardName);
         Document* document = frame->document();
         ASSERT(document);
         if (!document)
@@ -522,12 +566,11 @@ PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefP
         if ([URLString length] == 0)
             return nil;
         NSString *URLTitleString = [platformStrategies()->pasteboardStrategy()->stringForType(WebURLNamePboardType, m_pasteboardName) precomposedStringWithCanonicalMapping];
-        ExceptionCode ec;
         anchor->setAttribute(HTMLNames::hrefAttr, URLString);
-        anchor->appendChild(document->createTextNode(URLTitleString), ec);
+        anchor->appendChild(document->createTextNode(URLTitleString), IGNORE_EXCEPTION);
         fragment = document->createDocumentFragment();
         if (fragment) {
-            fragment->appendChild(anchor, ec);
+            fragment->appendChild(anchor, IGNORE_EXCEPTION);
             return fragment.release();
         }
     }