1/*
2 * Copyright (C) 2006 Zack Rusin <zack@kde.org>
3 * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com>
4 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "Pasteboard.h"
30
31#include "DocumentFragment.h"
32#include "Editor.h"
33#include "Frame.h"
34#include "KURL.h"
35#include "NotImplemented.h"
36#include "TextResourceDecoder.h"
37#include "markup.h"
38#include <support/Locker.h>
39#include <Clipboard.h>
40#include <Message.h>
41#include <String.h>
42#include <wtf/text/CString.h>
43
44
45namespace WebCore {
46
47Pasteboard::Pasteboard()
48{
49}
50
51Pasteboard::~Pasteboard()
52{
53}
54
55Pasteboard* Pasteboard::generalPasteboard()
56{
57    static Pasteboard pasteboard;
58    return &pasteboard;
59}
60
61// BClipboard unfortunately does not derive from BLocker, so we cannot use BAutolock.
62class AutoClipboardLocker {
63public:
64    AutoClipboardLocker(BClipboard* clipboard)
65        : m_clipboard(clipboard)
66        , m_isLocked(clipboard && clipboard->Lock())
67    {
68    }
69
70    ~AutoClipboardLocker()
71    {
72        if (m_isLocked)
73            m_clipboard->Unlock();
74    }
75
76    bool isLocked() const
77    {
78        return m_isLocked;
79    }
80
81private:
82    BClipboard* m_clipboard;
83    bool m_isLocked;
84};
85
86void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame)
87{
88    AutoClipboardLocker locker(be_clipboard);
89    if (!locker.isLocked())
90        return;
91
92    be_clipboard->Clear();
93    BMessage* data = be_clipboard->Data();
94    if (!data)
95        return;
96
97    BString string(frame->selectedText());
98
99    // Replace unwanted representation of blank lines
100    const char* utf8BlankLine = "\302\240\n";
101    string.ReplaceAll(utf8BlankLine, "\n");
102
103    data->AddData("text/plain", B_MIME_TYPE, string.String(), string.Length());
104
105    BString markupString(createMarkup(selectedRange, 0, AnnotateForInterchange, false, AbsoluteURLs));
106    data->AddData("text/html", B_MIME_TYPE, markupString.String(), markupString.Length());
107
108    be_clipboard->Commit();
109}
110
111void Pasteboard::writePlainText(const String& text)
112{
113    AutoClipboardLocker locker(be_clipboard);
114    if (!locker.isLocked())
115        return;
116
117    be_clipboard->Clear();
118    BMessage* data = be_clipboard->Data();
119    if (!data)
120        return;
121
122    BString string(text);
123    data->AddData("text/plain", B_MIME_TYPE, string.String(), string.Length());
124    be_clipboard->Commit();
125}
126
127bool Pasteboard::canSmartReplace()
128{
129    notImplemented();
130    return false;
131}
132
133String Pasteboard::plainText(Frame* frame)
134{
135    AutoClipboardLocker locker(be_clipboard);
136    if (!locker.isLocked())
137        return String();
138
139    BMessage* data = be_clipboard->Data();
140    if (!data)
141        return String();
142
143    const char* buffer = 0;
144    ssize_t bufferLength;
145    BString string;
146    if (data->FindData("text/plain", B_MIME_TYPE, reinterpret_cast<const void**>(&buffer), &bufferLength) == B_OK)
147        string.Append(buffer, bufferLength);
148
149    return string;
150}
151
152PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame* frame, PassRefPtr<Range> context,
153                                                          bool allowPlainText, bool& chosePlainText)
154{
155    chosePlainText = false;
156
157    AutoClipboardLocker locker(be_clipboard);
158    if (!locker.isLocked())
159        return 0;
160
161    BMessage* data = be_clipboard->Data();
162    if (!data)
163        return 0;
164
165    const char* buffer = 0;
166    ssize_t bufferLength;
167    if (data->FindData("text/html", B_MIME_TYPE, reinterpret_cast<const void**>(&buffer), &bufferLength) == B_OK) {
168        RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/plain", "UTF-8", true);
169        String html = decoder->decode(buffer, bufferLength);
170        html += decoder->flush();
171
172        if (!html.isEmpty()) {
173            RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(frame->document(), html, "", FragmentScriptingNotAllowed);
174            if (fragment)
175                return fragment.release();
176        }
177    }
178
179    if (!allowPlainText)
180        return 0;
181
182    if (data->FindData("text/plain", B_MIME_TYPE, reinterpret_cast<const void**>(&buffer), &bufferLength) == B_OK) {
183        BString plainText(buffer, bufferLength);
184
185        chosePlainText = true;
186        RefPtr<DocumentFragment> fragment = createFragmentFromText(context.get(), plainText);
187        if (fragment)
188            return fragment.release();
189    }
190
191    return 0;
192}
193
194void Pasteboard::writeURL(const KURL& url, const String&, Frame*)
195{
196    AutoClipboardLocker locker(be_clipboard);
197    if (!locker.isLocked())
198        return;
199
200    be_clipboard->Clear();
201
202    BMessage* data = be_clipboard->Data();
203    if (!data)
204        return;
205
206    BString string(url.string());
207    data->AddData("text/plain", B_MIME_TYPE, string.String(), string.Length());
208    be_clipboard->Commit();
209}
210
211void Pasteboard::writeImage(Node*, const KURL&, const String&)
212{
213    notImplemented();
214}
215
216void Pasteboard::clear()
217{
218    AutoClipboardLocker locker(be_clipboard);
219    if (!locker.isLocked())
220        return;
221
222    be_clipboard->Clear();
223    be_clipboard->Commit();
224}
225
226} // namespace WebCore
227
228