pepper_flash_clipboard_message_filter.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu// Use of this source code is governed by a BSD-style license that can be
3dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu// found in the LICENSE file.
4dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
5dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h"
6dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
7dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "base/pickle.h"
8dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "base/strings/utf_string_conversions.h"
9dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "content/public/browser/browser_thread.h"
10dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ipc/ipc_message.h"
11dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ipc/ipc_message_macros.h"
12dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ppapi/c/pp_errors.h"
13dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ppapi/c/private/ppb_flash_clipboard.h"
14dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ppapi/host/dispatch_host_message.h"
15dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ppapi/host/host_message_context.h"
16dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ppapi/host/ppapi_host.h"
17dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ppapi/proxy/ppapi_messages.h"
18dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ppapi/proxy/resource_message_params.h"
19dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu#include "ui/base/clipboard/scoped_clipboard_writer.h"
20dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
21dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuusing content::BrowserThread;
22dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
23dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhunamespace chrome {
24dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
25dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhunamespace {
26dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
27dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuconst size_t kMaxClipboardWriteSize = 1000000;
28dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
29dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhuui::ClipboardType ConvertClipboardType(uint32_t type) {
30dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu  switch (type) {
31dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    case PP_FLASH_CLIPBOARD_TYPE_STANDARD:
32dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      return ui::CLIPBOARD_TYPE_COPY_PASTE;
33dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu    case PP_FLASH_CLIPBOARD_TYPE_SELECTION:
34dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu      return ui::CLIPBOARD_TYPE_SELECTION;
35dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu  }
36dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu  NOTREACHED();
37dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu  return ui::CLIPBOARD_TYPE_COPY_PASTE;
38dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu}
39dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu
40dd9eb897ee7c7b507cbdcf80263bb4b5de6966bfTeng-Hui Zhu// 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();
88       ++it) {
89    if (!pickle->WriteString16(it->first))
90      return false;
91    if (!pickle->WriteString(it->second))
92      return false;
93  }
94  return true;
95}
96
97}  // namespace
98
99PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() {}
100
101PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() {}
102
103scoped_refptr<base::TaskRunner>
104PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage(
105    const IPC::Message& msg) {
106  // Clipboard writes should always occur on the UI thread due to the
107  // restrictions of various platform APIs. In general, the clipboard is not
108  // thread-safe, so all clipboard calls should be serviced from the UI thread.
109  if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID)
110    return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
111
112// Windows needs clipboard reads to be serviced from the IO thread because
113// these are sync IPCs which can result in deadlocks with plugins if serviced
114// from the UI thread. Note that Windows clipboard calls ARE thread-safe so it
115// is ok for reads and writes to be serviced from different threads.
116#if !defined(OS_WIN)
117  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
118#else
119  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
120#endif
121}
122
123int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived(
124    const IPC::Message& msg,
125    ppapi::host::HostMessageContext* context) {
126  PPAPI_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg)
127    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
128        PpapiHostMsg_FlashClipboard_RegisterCustomFormat,
129        OnMsgRegisterCustomFormat)
130    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
131        PpapiHostMsg_FlashClipboard_IsFormatAvailable, OnMsgIsFormatAvailable)
132    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashClipboard_ReadData,
133                                      OnMsgReadData)
134    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashClipboard_WriteData,
135                                      OnMsgWriteData)
136    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
137        PpapiHostMsg_FlashClipboard_GetSequenceNumber, OnMsgGetSequenceNumber)
138  PPAPI_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(ui::Clipboard::GetPepperCustomDataFormatType(),
189                            &clipboard_data);
190        Pickle pickle(clipboard_data.data(), clipboard_data.size());
191        available =
192            IsFormatAvailableInPickle(base::UTF8ToUTF16(format_name), pickle);
193      }
194      break;
195  }
196
197  return available ? PP_OK : PP_ERROR_FAILED;
198}
199
200int32_t PepperFlashClipboardMessageFilter::OnMsgReadData(
201    ppapi::host::HostMessageContext* host_context,
202    uint32_t clipboard_type,
203    uint32_t format) {
204  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
205    NOTIMPLEMENTED();
206    return PP_ERROR_FAILED;
207  }
208
209  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
210  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
211  std::string clipboard_string;
212  int32_t result = PP_ERROR_FAILED;
213  switch (format) {
214    case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
215      if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(),
216                                       type)) {
217        base::string16 text;
218        clipboard->ReadText(type, &text);
219        if (!text.empty()) {
220          result = PP_OK;
221          clipboard_string = base::UTF16ToUTF8(text);
222          break;
223        }
224      }
225      // If the PlainTextW format isn't available or is empty, take the
226      // ASCII text format.
227      if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
228                                       type)) {
229        result = PP_OK;
230        clipboard->ReadAsciiText(type, &clipboard_string);
231      }
232      break;
233    }
234    case PP_FLASH_CLIPBOARD_FORMAT_HTML: {
235      if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(),
236                                        type)) {
237        break;
238      }
239
240      base::string16 html;
241      std::string url;
242      uint32 fragment_start;
243      uint32 fragment_end;
244      clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end);
245      result = PP_OK;
246      clipboard_string = base::UTF16ToUTF8(
247          html.substr(fragment_start, fragment_end - fragment_start));
248      break;
249    }
250    case PP_FLASH_CLIPBOARD_FORMAT_RTF: {
251      if (!clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(),
252                                        type)) {
253        break;
254      }
255      result = PP_OK;
256      clipboard->ReadRTF(type, &clipboard_string);
257      break;
258    }
259    case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
260      break;
261    default: {
262      if (custom_formats_.IsFormatRegistered(format)) {
263        base::string16 format_name =
264            base::UTF8ToUTF16(custom_formats_.GetFormatName(format));
265        std::string clipboard_data;
266        clipboard->ReadData(ui::Clipboard::GetPepperCustomDataFormatType(),
267                            &clipboard_data);
268        Pickle pickle(clipboard_data.data(), clipboard_data.size());
269        if (IsFormatAvailableInPickle(format_name, pickle)) {
270          result = PP_OK;
271          clipboard_string = ReadDataFromPickle(format_name, pickle);
272        }
273      }
274      break;
275    }
276  }
277
278  if (result == PP_OK) {
279    host_context->reply_msg =
280        PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string);
281  }
282  return result;
283}
284
285int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData(
286    ppapi::host::HostMessageContext* host_context,
287    uint32_t clipboard_type,
288    const std::vector<uint32_t>& formats,
289    const std::vector<std::string>& data) {
290  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
291    NOTIMPLEMENTED();
292    return PP_ERROR_FAILED;
293  }
294  if (formats.size() != data.size())
295    return PP_ERROR_FAILED;
296
297  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
298  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
299  // If no formats are passed in clear the clipboard.
300  if (formats.size() == 0) {
301    clipboard->Clear(type);
302    return PP_OK;
303  }
304
305  ui::ScopedClipboardWriter scw(clipboard, type);
306  std::map<base::string16, std::string> custom_data_map;
307  int32_t res = PP_OK;
308  for (uint32_t i = 0; i < formats.size(); ++i) {
309    if (data[i].length() > kMaxClipboardWriteSize) {
310      res = PP_ERROR_NOSPACE;
311      break;
312    }
313
314    switch (formats[i]) {
315      case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT:
316        scw.WriteText(base::UTF8ToUTF16(data[i]));
317        break;
318      case PP_FLASH_CLIPBOARD_FORMAT_HTML:
319        scw.WriteHTML(base::UTF8ToUTF16(data[i]), std::string());
320        break;
321      case PP_FLASH_CLIPBOARD_FORMAT_RTF:
322        scw.WriteRTF(data[i]);
323        break;
324      case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
325        res = PP_ERROR_BADARGUMENT;
326        break;
327      default:
328        if (custom_formats_.IsFormatRegistered(formats[i])) {
329          std::string format_name = custom_formats_.GetFormatName(formats[i]);
330          custom_data_map[base::UTF8ToUTF16(format_name)] = data[i];
331        } else {
332          // Invalid format.
333          res = PP_ERROR_BADARGUMENT;
334          break;
335        }
336    }
337
338    if (res != PP_OK)
339      break;
340  }
341
342  if (custom_data_map.size() > 0) {
343    Pickle pickle;
344    if (WriteDataToPickle(custom_data_map, &pickle)) {
345      scw.WritePickledData(pickle,
346                           ui::Clipboard::GetPepperCustomDataFormatType());
347    } else {
348      res = PP_ERROR_BADARGUMENT;
349    }
350  }
351
352  if (res != PP_OK) {
353    // Need to clear the objects so nothing is written.
354    scw.Reset();
355  }
356
357  return res;
358}
359
360int32_t PepperFlashClipboardMessageFilter::OnMsgGetSequenceNumber(
361    ppapi::host::HostMessageContext* host_context,
362    uint32_t clipboard_type) {
363  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
364    NOTIMPLEMENTED();
365    return PP_ERROR_FAILED;
366  }
367
368  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
369  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
370  int64_t sequence_number = clipboard->GetSequenceNumber(type);
371  host_context->reply_msg =
372      PpapiPluginMsg_FlashClipboard_GetSequenceNumberReply(sequence_number);
373  return PP_OK;
374}
375
376}  // namespace chrome
377