root/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ConvertClipboardType
  2. JumpToFormatInPickle
  3. IsFormatAvailableInPickle
  4. ReadDataFromPickle
  5. WriteDataToPickle
  6. OverrideTaskRunnerForMessage
  7. OnResourceMessageReceived
  8. OnMsgRegisterCustomFormat
  9. OnMsgIsFormatAvailable
  10. OnMsgReadData
  11. OnMsgWriteData
  12. OnMsgGetSequenceNumber

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h"

#include "base/pickle.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/private/ppb_flash_clipboard.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/resource_message_params.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"

using content::BrowserThread;

namespace chrome {

namespace {

const size_t kMaxClipboardWriteSize = 1000000;

ui::ClipboardType ConvertClipboardType(uint32_t type) {
  switch (type) {
    case PP_FLASH_CLIPBOARD_TYPE_STANDARD:
      return ui::CLIPBOARD_TYPE_COPY_PASTE;
    case PP_FLASH_CLIPBOARD_TYPE_SELECTION:
      return ui::CLIPBOARD_TYPE_SELECTION;
  }
  NOTREACHED();
  return ui::CLIPBOARD_TYPE_COPY_PASTE;
}

// Functions to pack/unpack custom data from a pickle. See the header file for
// more detail on custom formats in Pepper.
// TODO(raymes): Currently pepper custom formats are stored in their own
// native format type. However we should be able to store them in the same way
// as "Web Custom" formats are. This would allow clipboard data to be shared
// between pepper applications and web applications. However currently web apps
// assume all data that is placed on the clipboard is UTF16 and pepper allows
// arbitrary data so this change would require some reworking of the chrome
// clipboard interface for custom data.
bool JumpToFormatInPickle(const base::string16& format, PickleIterator* iter) {
  uint64 size = 0;
  if (!iter->ReadUInt64(&size))
    return false;
  for (uint64 i = 0; i < size; ++i) {
    base::string16 stored_format;
    if (!iter->ReadString16(&stored_format))
      return false;
    if (stored_format == format)
      return true;
    int skip_length;
    if (!iter->ReadLength(&skip_length))
      return false;
    if (!iter->SkipBytes(skip_length))
      return false;
  }
  return false;
}

bool IsFormatAvailableInPickle(const base::string16& format,
                               const Pickle& pickle) {
  PickleIterator iter(pickle);
  return JumpToFormatInPickle(format, &iter);
}

std::string ReadDataFromPickle(const base::string16& format,
                               const Pickle& pickle) {
  std::string result;
  PickleIterator iter(pickle);
  if (!JumpToFormatInPickle(format, &iter) || !iter.ReadString(&result))
    return std::string();
  return result;
}

bool WriteDataToPickle(const std::map<base::string16, std::string>& data,
                       Pickle* pickle) {
  pickle->WriteUInt64(data.size());
  for (std::map<base::string16, std::string>::const_iterator it = data.begin();
       it != data.end(); ++it) {
    if (!pickle->WriteString16(it->first))
      return false;
    if (!pickle->WriteString(it->second))
      return false;
  }
  return true;
}

}  // namespace

PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() {
}

PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() {
}

scoped_refptr<base::TaskRunner>
PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage(
    const IPC::Message& msg) {
  // Clipboard writes should always occur on the UI thread due to the
  // restrictions of various platform APIs. In general, the clipboard is not
  // thread-safe, so all clipboard calls should be serviced from the UI thread.
  if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID)
    return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);

  // Windows needs clipboard reads to be serviced from the IO thread because
  // these are sync IPCs which can result in deadlocks with plugins if serviced
  // from the UI thread. Note that Windows clipboard calls ARE thread-safe so it
  // is ok for reads and writes to be serviced from different threads.
#if !defined(OS_WIN)
  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
#else
  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
#endif
}

int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived(
    const IPC::Message& msg,
    ppapi::host::HostMessageContext* context) {
  IPC_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
        PpapiHostMsg_FlashClipboard_RegisterCustomFormat,
        OnMsgRegisterCustomFormat);
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
        PpapiHostMsg_FlashClipboard_IsFormatAvailable,
        OnMsgIsFormatAvailable);
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
        PpapiHostMsg_FlashClipboard_ReadData,
        OnMsgReadData);
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
        PpapiHostMsg_FlashClipboard_WriteData,
        OnMsgWriteData);
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
        PpapiHostMsg_FlashClipboard_GetSequenceNumber,
        OnMsgGetSequenceNumber);
  IPC_END_MESSAGE_MAP()
  return PP_ERROR_FAILED;
}

int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat(
    ppapi::host::HostMessageContext* host_context,
    const std::string& format_name) {
  uint32_t format = custom_formats_.RegisterFormat(format_name);
  if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID)
    return PP_ERROR_FAILED;
  host_context->reply_msg =
      PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format);
  return PP_OK;
}

int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable(
    ppapi::host::HostMessageContext* host_context,
    uint32_t clipboard_type,
    uint32_t format) {
  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
    NOTIMPLEMENTED();
    return PP_ERROR_FAILED;
  }

  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
  bool available = false;
  switch (format) {
    case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
      bool plain = clipboard->IsFormatAvailable(
          ui::Clipboard::GetPlainTextFormatType(), type);
      bool plainw = clipboard->IsFormatAvailable(
          ui::Clipboard::GetPlainTextWFormatType(), type);
      available = plain || plainw;
      break;
    }
    case PP_FLASH_CLIPBOARD_FORMAT_HTML:
      available = clipboard->IsFormatAvailable(
          ui::Clipboard::GetHtmlFormatType(), type);
      break;
    case PP_FLASH_CLIPBOARD_FORMAT_RTF:
      available =
          clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type);
      break;
    case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
      break;
    default:
      if (custom_formats_.IsFormatRegistered(format)) {
        std::string format_name = custom_formats_.GetFormatName(format);
        std::string clipboard_data;
        clipboard->ReadData(
            ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data);
        Pickle pickle(clipboard_data.data(), clipboard_data.size());
        available =
            IsFormatAvailableInPickle(base::UTF8ToUTF16(format_name), pickle);
      }
      break;
  }

  return available ? PP_OK : PP_ERROR_FAILED;
}

int32_t PepperFlashClipboardMessageFilter::OnMsgReadData(
    ppapi::host::HostMessageContext* host_context,
    uint32_t clipboard_type,
    uint32_t format) {
  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
    NOTIMPLEMENTED();
    return PP_ERROR_FAILED;
  }

  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
  std::string clipboard_string;
  int32_t result = PP_ERROR_FAILED;
  switch (format) {
    case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
      if (clipboard->IsFormatAvailable(
          ui::Clipboard::GetPlainTextWFormatType(), type)) {
        base::string16 text;
        clipboard->ReadText(type, &text);
        if (!text.empty()) {
          result = PP_OK;
          clipboard_string = base::UTF16ToUTF8(text);
          break;
        }
      }
      // If the PlainTextW format isn't available or is empty, take the
      // ASCII text format.
      if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
                                       type)) {
        result = PP_OK;
        clipboard->ReadAsciiText(type, &clipboard_string);
      }
      break;
    }
    case PP_FLASH_CLIPBOARD_FORMAT_HTML: {
      if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(),
                                        type)) {
        break;
      }

      base::string16 html;
      std::string url;
      uint32 fragment_start;
      uint32 fragment_end;
      clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end);
      result = PP_OK;
      clipboard_string = base::UTF16ToUTF8(
          html.substr(fragment_start, fragment_end - fragment_start));
      break;
    }
    case PP_FLASH_CLIPBOARD_FORMAT_RTF: {
      if (!clipboard->IsFormatAvailable(
          ui::Clipboard::GetRtfFormatType(), type)) {
        break;
      }
      result = PP_OK;
      clipboard->ReadRTF(type, &clipboard_string);
      break;
    }
    case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
      break;
    default: {
      if (custom_formats_.IsFormatRegistered(format)) {
        base::string16 format_name =
            base::UTF8ToUTF16(custom_formats_.GetFormatName(format));
        std::string clipboard_data;
        clipboard->ReadData(
            ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data);
        Pickle pickle(clipboard_data.data(), clipboard_data.size());
        if (IsFormatAvailableInPickle(format_name, pickle)) {
          result = PP_OK;
          clipboard_string = ReadDataFromPickle(format_name, pickle);
        }
      }
      break;
    }
  }

  if (result == PP_OK) {
    host_context->reply_msg =
        PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string);
  }
  return result;
}

int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData(
    ppapi::host::HostMessageContext* host_context,
    uint32_t clipboard_type,
    const std::vector<uint32_t>& formats,
    const std::vector<std::string>& data) {
  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
    NOTIMPLEMENTED();
    return PP_ERROR_FAILED;
  }
  if (formats.size() != data.size())
    return PP_ERROR_FAILED;

  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
  // If no formats are passed in clear the clipboard.
  if (formats.size() == 0) {
    clipboard->Clear(type);
    return PP_OK;
  }

  ui::ScopedClipboardWriter scw(clipboard, type);
  std::map<base::string16, std::string> custom_data_map;
  int32_t res = PP_OK;
  for (uint32_t i = 0; i < formats.size(); ++i) {
    if (data[i].length() > kMaxClipboardWriteSize) {
      res = PP_ERROR_NOSPACE;
      break;
    }

    switch (formats[i]) {
      case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT:
        scw.WriteText(base::UTF8ToUTF16(data[i]));
        break;
      case PP_FLASH_CLIPBOARD_FORMAT_HTML:
        scw.WriteHTML(base::UTF8ToUTF16(data[i]), std::string());
        break;
      case PP_FLASH_CLIPBOARD_FORMAT_RTF:
        scw.WriteRTF(data[i]);
        break;
      case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
        res = PP_ERROR_BADARGUMENT;
        break;
      default:
        if (custom_formats_.IsFormatRegistered(formats[i])) {
          std::string format_name = custom_formats_.GetFormatName(formats[i]);
          custom_data_map[base::UTF8ToUTF16(format_name)] = data[i];
        } else {
          // Invalid format.
          res = PP_ERROR_BADARGUMENT;
          break;
        }
    }

    if (res != PP_OK)
      break;
  }

  if (custom_data_map.size() > 0) {
    Pickle pickle;
    if (WriteDataToPickle(custom_data_map, &pickle)) {
      scw.WritePickledData(pickle,
                           ui::Clipboard::GetPepperCustomDataFormatType());
    } else {
      res = PP_ERROR_BADARGUMENT;
    }
  }

  if (res != PP_OK) {
    // Need to clear the objects so nothing is written.
    scw.Reset();
  }

  return res;
}

int32_t PepperFlashClipboardMessageFilter::OnMsgGetSequenceNumber(
    ppapi::host::HostMessageContext* host_context,
    uint32_t clipboard_type) {
  if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
    NOTIMPLEMENTED();
    return PP_ERROR_FAILED;
  }

  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
  ui::ClipboardType type = ConvertClipboardType(clipboard_type);
  int64_t sequence_number = clipboard->GetSequenceNumber(type);
  host_context->reply_msg =
      PpapiPluginMsg_FlashClipboard_GetSequenceNumberReply(sequence_number);
  return PP_OK;
}

}  // namespace chrome

/* [<][>][^][v][top][bottom][index][help] */