pepper_flash_clipboard_message_filter.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h"
6
7#include "base/pickle.h"
8#include "base/strings/utf_string_conversions.h"
9#include "content/public/browser/browser_thread.h"
10#include "ipc/ipc_message.h"
11#include "ipc/ipc_message_macros.h"
12#include "ppapi/c/pp_errors.h"
13#include "ppapi/c/private/ppb_flash_clipboard.h"
14#include "ppapi/host/dispatch_host_message.h"
15#include "ppapi/host/host_message_context.h"
16#include "ppapi/host/ppapi_host.h"
17#include "ppapi/proxy/ppapi_messages.h"
18#include "ppapi/proxy/resource_message_params.h"
19#include "ui/base/clipboard/scoped_clipboard_writer.h"
20
21using content::BrowserThread;
22
23namespace chrome {
24
25namespace {
26
27const size_t kMaxClipboardWriteSize = 1000000;
28
29ui::ClipboardType ConvertClipboardType(uint32_t type) {
30  switch (type) {
31    case PP_FLASH_CLIPBOARD_TYPE_STANDARD:
32      return ui::CLIPBOARD_TYPE_COPY_PASTE;
33    case PP_FLASH_CLIPBOARD_TYPE_SELECTION:
34      return ui::CLIPBOARD_TYPE_SELECTION;
35  }
36  NOTREACHED();
37  return ui::CLIPBOARD_TYPE_COPY_PASTE;
38}
39
40// Functions to pack/unpack custom data from a pickle. See the header file for
41// more detail on custom formats in Pepper.
42// TODO(raymes): Currently pepper custom formats are stored in their own
43// native format type. However we should be able to store them in the same way
44// as "Web Custom" formats are. This would allow clipboard data to be shared
45// between pepper applications and web applications. However currently web apps
46// assume all data that is placed on the clipboard is UTF16 and pepper allows
47// arbitrary data so this change would require some reworking of the chrome
48// clipboard interface for custom data.
49bool JumpToFormatInPickle(const string16& format, PickleIterator* iter) {
50  uint64 size = 0;
51  if (!iter->ReadUInt64(&size))
52    return false;
53  for (uint64 i = 0; i < size; ++i) {
54    string16 stored_format;
55    if (!iter->ReadString16(&stored_format))
56      return false;
57    if (stored_format == format)
58      return true;
59    int skip_length;
60    if (!iter->ReadLength(&skip_length))
61      return false;
62    if (!iter->SkipBytes(skip_length))
63      return false;
64  }
65  return false;
66}
67
68bool IsFormatAvailableInPickle(const string16& format, const Pickle& pickle) {
69  PickleIterator iter(pickle);
70  return JumpToFormatInPickle(format, &iter);
71}
72
73std::string ReadDataFromPickle(const string16& format, const Pickle& pickle) {
74  std::string result;
75  PickleIterator iter(pickle);
76  if (!JumpToFormatInPickle(format, &iter) || !iter.ReadString(&result))
77    return std::string();
78  return result;
79}
80
81bool WriteDataToPickle(const std::map<string16, std::string>& data,
82                       Pickle* pickle) {
83  pickle->WriteUInt64(data.size());
84  for (std::map<string16, std::string>::const_iterator it = data.begin();
85       it != data.end(); ++it) {
86    if (!pickle->WriteString16(it->first))
87      return false;
88    if (!pickle->WriteString(it->second))
89      return false;
90  }
91  return true;
92}
93
94}  // namespace
95
96PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() {
97}
98
99PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() {
100}
101
102scoped_refptr<base::TaskRunner>
103PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage(
104    const IPC::Message& msg) {
105  // Clipboard writes should always occur on the UI thread due to the
106  // restrictions of various platform APIs. In general, the clipboard is not
107  // thread-safe, so all clipboard calls should be serviced from the UI thread.
108  if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID)
109    return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
110
111  // Windows needs clipboard reads to be serviced from the IO thread because
112  // these are sync IPCs which can result in deadlocks with plugins if serviced
113  // from the UI thread. Note that Windows clipboard calls ARE thread-safe so it
114  // is ok for reads and writes to be serviced from different threads.
115#if !defined(OS_WIN)
116  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
117#else
118  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
119#endif
120}
121
122int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived(
123    const IPC::Message& msg,
124    ppapi::host::HostMessageContext* context) {
125  IPC_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg)
126    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
127        PpapiHostMsg_FlashClipboard_RegisterCustomFormat,
128        OnMsgRegisterCustomFormat);
129    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
130        PpapiHostMsg_FlashClipboard_IsFormatAvailable,
131        OnMsgIsFormatAvailable);
132    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
133        PpapiHostMsg_FlashClipboard_ReadData,
134        OnMsgReadData);
135    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
136        PpapiHostMsg_FlashClipboard_WriteData,
137        OnMsgWriteData);
138  IPC_END_MESSAGE_MAP()
139  return PP_ERROR_FAILED;
140}
141
142int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat(
143    ppapi::host::HostMessageContext* host_context,
144    const std::string& format_name) {
145  uint32_t format = custom_formats_.RegisterFormat(format_name);
146  if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID)
147    return PP_ERROR_FAILED;
148  host_context->reply_msg =
149      PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format);
150  return PP_OK;
151}
152
153int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable(
154    ppapi::host::HostMessageContext* host_context,
155    uint32_t clipboard_type,
156    uint32_t format) {
157  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
158    NOTIMPLEMENTED();
159    return PP_ERROR_FAILED;
160  }
161
162  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
163  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
164  bool available = false;
165  switch (format) {
166    case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
167      bool plain = clipboard->IsFormatAvailable(
168          ui::Clipboard::GetPlainTextFormatType(), type);
169      bool plainw = clipboard->IsFormatAvailable(
170          ui::Clipboard::GetPlainTextWFormatType(), type);
171      available = plain || plainw;
172      break;
173    }
174    case PP_FLASH_CLIPBOARD_FORMAT_HTML:
175      available = clipboard->IsFormatAvailable(
176          ui::Clipboard::GetHtmlFormatType(), type);
177      break;
178    case PP_FLASH_CLIPBOARD_FORMAT_RTF:
179      available =
180          clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type);
181      break;
182    case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
183      break;
184    default:
185      if (custom_formats_.IsFormatRegistered(format)) {
186        std::string format_name = custom_formats_.GetFormatName(format);
187        std::string clipboard_data;
188        clipboard->ReadData(
189            ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data);
190        Pickle pickle(clipboard_data.data(), clipboard_data.size());
191        available = IsFormatAvailableInPickle(UTF8ToUTF16(format_name), pickle);
192      }
193      break;
194  }
195
196  return available ? PP_OK : PP_ERROR_FAILED;
197}
198
199int32_t PepperFlashClipboardMessageFilter::OnMsgReadData(
200    ppapi::host::HostMessageContext* host_context,
201    uint32_t clipboard_type,
202    uint32_t format) {
203  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
204    NOTIMPLEMENTED();
205    return PP_ERROR_FAILED;
206  }
207
208  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
209  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
210  std::string clipboard_string;
211  int32_t result = PP_ERROR_FAILED;
212  switch (format) {
213    case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
214      if (clipboard->IsFormatAvailable(
215          ui::Clipboard::GetPlainTextWFormatType(), type)) {
216        string16 text;
217        clipboard->ReadText(type, &text);
218        if (!text.empty()) {
219          result = PP_OK;
220          clipboard_string = UTF16ToUTF8(text);
221          break;
222        }
223      }
224      // If the PlainTextW format isn't available or is empty, take the
225      // ASCII text format.
226      if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
227                                       type)) {
228        result = PP_OK;
229        clipboard->ReadAsciiText(type, &clipboard_string);
230      }
231      break;
232    }
233    case PP_FLASH_CLIPBOARD_FORMAT_HTML: {
234      if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(),
235                                        type)) {
236        break;
237      }
238
239      string16 html;
240      std::string url;
241      uint32 fragment_start;
242      uint32 fragment_end;
243      clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end);
244      result = PP_OK;
245      clipboard_string = UTF16ToUTF8(
246          html.substr(fragment_start, fragment_end - fragment_start));
247      break;
248    }
249    case PP_FLASH_CLIPBOARD_FORMAT_RTF: {
250      if (!clipboard->IsFormatAvailable(
251          ui::Clipboard::GetRtfFormatType(), type)) {
252        break;
253      }
254      result = PP_OK;
255      clipboard->ReadRTF(type, &clipboard_string);
256      break;
257    }
258    case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
259      break;
260    default: {
261      if (custom_formats_.IsFormatRegistered(format)) {
262        string16 format_name =
263            UTF8ToUTF16(custom_formats_.GetFormatName(format));
264        std::string clipboard_data;
265        clipboard->ReadData(
266            ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data);
267        Pickle pickle(clipboard_data.data(), clipboard_data.size());
268        if (IsFormatAvailableInPickle(format_name, pickle)) {
269          result = PP_OK;
270          clipboard_string = ReadDataFromPickle(format_name, pickle);
271        }
272      }
273      break;
274    }
275  }
276
277  if (result == PP_OK) {
278    host_context->reply_msg =
279        PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string);
280  }
281  return result;
282}
283
284int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData(
285    ppapi::host::HostMessageContext* host_context,
286    uint32_t clipboard_type,
287    const std::vector<uint32_t>& formats,
288    const std::vector<std::string>& data) {
289  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
290    NOTIMPLEMENTED();
291    return PP_ERROR_FAILED;
292  }
293  if (formats.size() != data.size())
294    return PP_ERROR_FAILED;
295
296  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
297  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
298  // If no formats are passed in clear the clipboard.
299  if (formats.size() == 0) {
300    clipboard->Clear(type);
301    return PP_OK;
302  }
303
304  ui::ScopedClipboardWriter scw(clipboard, type);
305  std::map<string16, std::string> custom_data_map;
306  int32_t res = PP_OK;
307  for (uint32_t i = 0; i < formats.size(); ++i) {
308    if (data[i].length() > kMaxClipboardWriteSize) {
309      res = PP_ERROR_NOSPACE;
310      break;
311    }
312
313    switch (formats[i]) {
314      case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT:
315        scw.WriteText(UTF8ToUTF16(data[i]));
316        break;
317      case PP_FLASH_CLIPBOARD_FORMAT_HTML:
318        scw.WriteHTML(UTF8ToUTF16(data[i]), std::string());
319        break;
320      case PP_FLASH_CLIPBOARD_FORMAT_RTF:
321        scw.WriteRTF(data[i]);
322        break;
323      case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
324        res = PP_ERROR_BADARGUMENT;
325        break;
326      default:
327        if (custom_formats_.IsFormatRegistered(formats[i])) {
328          std::string format_name = custom_formats_.GetFormatName(formats[i]);
329          custom_data_map[UTF8ToUTF16(format_name)] = data[i];
330        } else {
331          // Invalid format.
332          res = PP_ERROR_BADARGUMENT;
333          break;
334        }
335    }
336
337    if (res != PP_OK)
338      break;
339  }
340
341  if (custom_data_map.size() > 0) {
342    Pickle pickle;
343    if (WriteDataToPickle(custom_data_map, &pickle)) {
344      scw.WritePickledData(pickle,
345                           ui::Clipboard::GetPepperCustomDataFormatType());
346    } else {
347      res = PP_ERROR_BADARGUMENT;
348    }
349  }
350
351  if (res != PP_OK) {
352    // Need to clear the objects so nothing is written.
353    scw.Reset();
354  }
355
356  return res;
357}
358
359}  // namespace chrome
360