pepper_flash_clipboard_message_filter.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 base::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    base::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 base::string16& format,
69                               const Pickle& pickle) {
70  PickleIterator iter(pickle);
71  return JumpToFormatInPickle(format, &iter);
72}
73
74std::string ReadDataFromPickle(const base::string16& format,
75                               const Pickle& pickle) {
76  std::string result;
77  PickleIterator iter(pickle);
78  if (!JumpToFormatInPickle(format, &iter) || !iter.ReadString(&result))
79    return std::string();
80  return result;
81}
82
83bool WriteDataToPickle(const std::map<base::string16, std::string>& data,
84                       Pickle* pickle) {
85  pickle->WriteUInt64(data.size());
86  for (std::map<base::string16, std::string>::const_iterator it = data.begin();
87       it != data.end(); ++it) {
88    if (!pickle->WriteString16(it->first))
89      return false;
90    if (!pickle->WriteString(it->second))
91      return false;
92  }
93  return true;
94}
95
96}  // namespace
97
98PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() {
99}
100
101PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() {
102}
103
104scoped_refptr<base::TaskRunner>
105PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage(
106    const IPC::Message& msg) {
107  // Clipboard writes should always occur on the UI thread due to the
108  // restrictions of various platform APIs. In general, the clipboard is not
109  // thread-safe, so all clipboard calls should be serviced from the UI thread.
110  if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID)
111    return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
112
113  // Windows needs clipboard reads to be serviced from the IO thread because
114  // these are sync IPCs which can result in deadlocks with plugins if serviced
115  // from the UI thread. Note that Windows clipboard calls ARE thread-safe so it
116  // is ok for reads and writes to be serviced from different threads.
117#if !defined(OS_WIN)
118  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
119#else
120  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
121#endif
122}
123
124int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived(
125    const IPC::Message& msg,
126    ppapi::host::HostMessageContext* context) {
127  IPC_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg)
128    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
129        PpapiHostMsg_FlashClipboard_RegisterCustomFormat,
130        OnMsgRegisterCustomFormat);
131    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
132        PpapiHostMsg_FlashClipboard_IsFormatAvailable,
133        OnMsgIsFormatAvailable);
134    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
135        PpapiHostMsg_FlashClipboard_ReadData,
136        OnMsgReadData);
137    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
138        PpapiHostMsg_FlashClipboard_WriteData,
139        OnMsgWriteData);
140    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
141        PpapiHostMsg_FlashClipboard_GetSequenceNumber,
142        OnMsgGetSequenceNumber);
143  IPC_END_MESSAGE_MAP()
144  return PP_ERROR_FAILED;
145}
146
147int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat(
148    ppapi::host::HostMessageContext* host_context,
149    const std::string& format_name) {
150  uint32_t format = custom_formats_.RegisterFormat(format_name);
151  if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID)
152    return PP_ERROR_FAILED;
153  host_context->reply_msg =
154      PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format);
155  return PP_OK;
156}
157
158int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable(
159    ppapi::host::HostMessageContext* host_context,
160    uint32_t clipboard_type,
161    uint32_t format) {
162  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
163    NOTIMPLEMENTED();
164    return PP_ERROR_FAILED;
165  }
166
167  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
168  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
169  bool available = false;
170  switch (format) {
171    case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
172      bool plain = clipboard->IsFormatAvailable(
173          ui::Clipboard::GetPlainTextFormatType(), type);
174      bool plainw = clipboard->IsFormatAvailable(
175          ui::Clipboard::GetPlainTextWFormatType(), type);
176      available = plain || plainw;
177      break;
178    }
179    case PP_FLASH_CLIPBOARD_FORMAT_HTML:
180      available = clipboard->IsFormatAvailable(
181          ui::Clipboard::GetHtmlFormatType(), type);
182      break;
183    case PP_FLASH_CLIPBOARD_FORMAT_RTF:
184      available =
185          clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type);
186      break;
187    case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
188      break;
189    default:
190      if (custom_formats_.IsFormatRegistered(format)) {
191        std::string format_name = custom_formats_.GetFormatName(format);
192        std::string clipboard_data;
193        clipboard->ReadData(
194            ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data);
195        Pickle pickle(clipboard_data.data(), clipboard_data.size());
196        available =
197            IsFormatAvailableInPickle(base::UTF8ToUTF16(format_name), pickle);
198      }
199      break;
200  }
201
202  return available ? PP_OK : PP_ERROR_FAILED;
203}
204
205int32_t PepperFlashClipboardMessageFilter::OnMsgReadData(
206    ppapi::host::HostMessageContext* host_context,
207    uint32_t clipboard_type,
208    uint32_t format) {
209  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
210    NOTIMPLEMENTED();
211    return PP_ERROR_FAILED;
212  }
213
214  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
215  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
216  std::string clipboard_string;
217  int32_t result = PP_ERROR_FAILED;
218  switch (format) {
219    case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
220      if (clipboard->IsFormatAvailable(
221          ui::Clipboard::GetPlainTextWFormatType(), type)) {
222        base::string16 text;
223        clipboard->ReadText(type, &text);
224        if (!text.empty()) {
225          result = PP_OK;
226          clipboard_string = base::UTF16ToUTF8(text);
227          break;
228        }
229      }
230      // If the PlainTextW format isn't available or is empty, take the
231      // ASCII text format.
232      if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
233                                       type)) {
234        result = PP_OK;
235        clipboard->ReadAsciiText(type, &clipboard_string);
236      }
237      break;
238    }
239    case PP_FLASH_CLIPBOARD_FORMAT_HTML: {
240      if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(),
241                                        type)) {
242        break;
243      }
244
245      base::string16 html;
246      std::string url;
247      uint32 fragment_start;
248      uint32 fragment_end;
249      clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end);
250      result = PP_OK;
251      clipboard_string = base::UTF16ToUTF8(
252          html.substr(fragment_start, fragment_end - fragment_start));
253      break;
254    }
255    case PP_FLASH_CLIPBOARD_FORMAT_RTF: {
256      if (!clipboard->IsFormatAvailable(
257          ui::Clipboard::GetRtfFormatType(), type)) {
258        break;
259      }
260      result = PP_OK;
261      clipboard->ReadRTF(type, &clipboard_string);
262      break;
263    }
264    case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
265      break;
266    default: {
267      if (custom_formats_.IsFormatRegistered(format)) {
268        base::string16 format_name =
269            base::UTF8ToUTF16(custom_formats_.GetFormatName(format));
270        std::string clipboard_data;
271        clipboard->ReadData(
272            ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data);
273        Pickle pickle(clipboard_data.data(), clipboard_data.size());
274        if (IsFormatAvailableInPickle(format_name, pickle)) {
275          result = PP_OK;
276          clipboard_string = ReadDataFromPickle(format_name, pickle);
277        }
278      }
279      break;
280    }
281  }
282
283  if (result == PP_OK) {
284    host_context->reply_msg =
285        PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string);
286  }
287  return result;
288}
289
290int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData(
291    ppapi::host::HostMessageContext* host_context,
292    uint32_t clipboard_type,
293    const std::vector<uint32_t>& formats,
294    const std::vector<std::string>& data) {
295  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
296    NOTIMPLEMENTED();
297    return PP_ERROR_FAILED;
298  }
299  if (formats.size() != data.size())
300    return PP_ERROR_FAILED;
301
302  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
303  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
304  // If no formats are passed in clear the clipboard.
305  if (formats.size() == 0) {
306    clipboard->Clear(type);
307    return PP_OK;
308  }
309
310  ui::ScopedClipboardWriter scw(clipboard, type);
311  std::map<base::string16, std::string> custom_data_map;
312  int32_t res = PP_OK;
313  for (uint32_t i = 0; i < formats.size(); ++i) {
314    if (data[i].length() > kMaxClipboardWriteSize) {
315      res = PP_ERROR_NOSPACE;
316      break;
317    }
318
319    switch (formats[i]) {
320      case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT:
321        scw.WriteText(base::UTF8ToUTF16(data[i]));
322        break;
323      case PP_FLASH_CLIPBOARD_FORMAT_HTML:
324        scw.WriteHTML(base::UTF8ToUTF16(data[i]), std::string());
325        break;
326      case PP_FLASH_CLIPBOARD_FORMAT_RTF:
327        scw.WriteRTF(data[i]);
328        break;
329      case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
330        res = PP_ERROR_BADARGUMENT;
331        break;
332      default:
333        if (custom_formats_.IsFormatRegistered(formats[i])) {
334          std::string format_name = custom_formats_.GetFormatName(formats[i]);
335          custom_data_map[base::UTF8ToUTF16(format_name)] = data[i];
336        } else {
337          // Invalid format.
338          res = PP_ERROR_BADARGUMENT;
339          break;
340        }
341    }
342
343    if (res != PP_OK)
344      break;
345  }
346
347  if (custom_data_map.size() > 0) {
348    Pickle pickle;
349    if (WriteDataToPickle(custom_data_map, &pickle)) {
350      scw.WritePickledData(pickle,
351                           ui::Clipboard::GetPepperCustomDataFormatType());
352    } else {
353      res = PP_ERROR_BADARGUMENT;
354    }
355  }
356
357  if (res != PP_OK) {
358    // Need to clear the objects so nothing is written.
359    scw.Reset();
360  }
361
362  return res;
363}
364
365int32_t PepperFlashClipboardMessageFilter::OnMsgGetSequenceNumber(
366    ppapi::host::HostMessageContext* host_context,
367    uint32_t clipboard_type) {
368  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
369    NOTIMPLEMENTED();
370    return PP_ERROR_FAILED;
371  }
372
373  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
374  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
375  int64_t sequence_number = clipboard->GetSequenceNumber(type);
376  host_context->reply_msg =
377      PpapiPluginMsg_FlashClipboard_GetSequenceNumberReply(sequence_number);
378  return PP_OK;
379}
380
381}  // namespace chrome
382