1/*
2 * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "core/platform/Pasteboard.h"
33
34#include "HTMLNames.h"
35#include "SVGNames.h"
36#include "XLinkNames.h"
37#include "core/dom/Document.h"
38#include "core/dom/DocumentFragment.h"
39#include "core/dom/Element.h"
40#include "core/dom/Range.h"
41#include "core/editing/markup.h"
42#include "core/html/parser/HTMLParserIdioms.h"
43#include "core/loader/cache/ImageResource.h"
44#include "core/page/Frame.h"
45#include "core/platform/chromium/ClipboardChromium.h"
46#include "core/platform/chromium/ClipboardUtilitiesChromium.h"
47#include "core/platform/graphics/Image.h"
48#include "core/platform/graphics/skia/NativeImageSkia.h"
49#include "core/rendering/RenderImage.h"
50#include "public/platform/Platform.h"
51#include "public/platform/WebClipboard.h"
52#include "public/platform/WebDragData.h"
53#include "weborigin/KURL.h"
54
55namespace WebCore {
56
57Pasteboard* Pasteboard::generalPasteboard()
58{
59    static Pasteboard* pasteboard = new Pasteboard;
60    return pasteboard;
61}
62
63Pasteboard::Pasteboard()
64    : m_selectionMode(false)
65{
66}
67
68void Pasteboard::clear()
69{
70    // The ScopedClipboardWriter class takes care of clearing the clipboard's
71    // previous contents.
72}
73
74bool Pasteboard::isSelectionMode() const
75{
76    return m_selectionMode;
77}
78
79void Pasteboard::setSelectionMode(bool selectionMode)
80{
81    m_selectionMode = selectionMode;
82}
83
84void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame, ShouldSerializeSelectedTextForClipboard shouldSerializeSelectedTextForClipboard)
85{
86    String html = createMarkup(selectedRange, 0, AnnotateForInterchange, false, ResolveNonLocalURLs);
87    KURL url = selectedRange->startContainer()->document()->url();
88    String plainText = shouldSerializeSelectedTextForClipboard == IncludeImageAltTextForClipboard ? frame->selectedTextForClipboard() : frame->selectedText();
89#if OS(WINDOWS)
90    replaceNewlinesWithWindowsStyleNewlines(plainText);
91#endif
92    replaceNBSPWithSpace(plainText);
93
94    WebKit::Platform::current()->clipboard()->writeHTML(html, url, plainText, canSmartCopyOrDelete);
95}
96
97void Pasteboard::writePlainText(const String& text, SmartReplaceOption)
98{
99    // FIXME: add support for smart replace
100#if OS(WINDOWS)
101    String plainText(text);
102    replaceNewlinesWithWindowsStyleNewlines(plainText);
103    WebKit::Platform::current()->clipboard()->writePlainText(plainText);
104#else
105    WebKit::Platform::current()->clipboard()->writePlainText(text);
106#endif
107}
108
109void Pasteboard::writeURL(const KURL& url, const String& titleStr, Frame* frame)
110{
111    ASSERT(!url.isEmpty());
112
113    String title(titleStr);
114    if (title.isEmpty()) {
115        title = url.lastPathComponent();
116        if (title.isEmpty())
117            title = url.host();
118    }
119
120    WebKit::Platform::current()->clipboard()->writeURL(url, title);
121}
122
123void Pasteboard::writeImage(Node* node, const KURL&, const String& title)
124{
125    ASSERT(node);
126
127    if (!(node->renderer() && node->renderer()->isImage()))
128        return;
129
130    RenderImage* renderer = toRenderImage(node->renderer());
131    ImageResource* cachedImage = renderer->cachedImage();
132    if (!cachedImage || cachedImage->errorOccurred())
133        return;
134    Image* image = cachedImage->imageForRenderer(renderer);
135    ASSERT(image);
136
137    RefPtr<NativeImageSkia> bitmap = image->nativeImageForCurrentFrame();
138    if (!bitmap)
139        return;
140
141    // If the image is wrapped in a link, |url| points to the target of the
142    // link.  This isn't useful to us, so get the actual image URL.
143    AtomicString urlString;
144    if (node->hasTagName(HTMLNames::imgTag) || node->hasTagName(HTMLNames::inputTag))
145        urlString = toElement(node)->getAttribute(HTMLNames::srcAttr);
146    else if (node->hasTagName(SVGNames::imageTag))
147        urlString = toElement(node)->getAttribute(XLinkNames::hrefAttr);
148    else if (node->hasTagName(HTMLNames::embedTag) || node->hasTagName(HTMLNames::objectTag)) {
149        Element* element = toElement(node);
150        urlString = element->imageSourceURL();
151    }
152    KURL url = urlString.isEmpty() ? KURL() : node->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
153    WebKit::WebImage webImage = bitmap->bitmap();
154    WebKit::Platform::current()->clipboard()->writeImage(webImage, WebKit::WebURL(url), WebKit::WebString(title));
155}
156
157void Pasteboard::writeClipboard(Clipboard* clipboard)
158{
159    WebKit::WebDragData dragData = static_cast<ClipboardChromium*>(clipboard)->dataObject();
160    WebKit::Platform::current()->clipboard()->writeDataObject(dragData);
161}
162
163bool Pasteboard::canSmartReplace()
164{
165    return WebKit::Platform::current()->clipboard()->isFormatAvailable(WebKit::WebClipboard::FormatSmartPaste, m_selectionMode ? WebKit::WebClipboard::BufferSelection : WebKit::WebClipboard::BufferStandard);
166}
167
168String Pasteboard::plainText(Frame* frame)
169{
170    return WebKit::Platform::current()->clipboard()->readPlainText(m_selectionMode ? WebKit::WebClipboard::BufferSelection : WebKit::WebClipboard::BufferStandard);
171}
172
173PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefPtr<Range> context, bool allowPlainText, bool& chosePlainText)
174{
175    chosePlainText = false;
176    WebKit::WebClipboard::Buffer buffer = m_selectionMode ? WebKit::WebClipboard::BufferSelection : WebKit::WebClipboard::BufferStandard;
177
178    if (WebKit::Platform::current()->clipboard()->isFormatAvailable(WebKit::WebClipboard::FormatHTML, buffer)) {
179        unsigned fragmentStart = 0;
180        unsigned fragmentEnd = 0;
181        WebKit::WebURL url;
182        WebKit::WebString markup = WebKit::Platform::current()->clipboard()->readHTML(buffer, &url, &fragmentStart, &fragmentEnd);
183        if (!markup.isEmpty()) {
184            if (RefPtr<DocumentFragment> fragment = createFragmentFromMarkupWithContext(frame->document(), markup, fragmentStart, fragmentEnd, KURL(url), DisallowScriptingAndPluginContent))
185                return fragment.release();
186        }
187    }
188
189    if (allowPlainText) {
190        String markup = WebKit::Platform::current()->clipboard()->readPlainText(buffer);
191        if (!markup.isEmpty()) {
192            chosePlainText = true;
193            if (RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), markup))
194                return fragment.release();
195        }
196    }
197
198    return 0;
199}
200
201} // namespace WebCore
202