root/ui/base/clipboard/clipboard.cc

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

DEFINITIONS

This source file includes following definitions.
  1. ValidateAndMapSharedBitmap
  2. SetAllowedThreads
  3. GetForCurrentThread
  4. DestroyClipboardForCurrentThread
  5. DispatchObject
  6. ReplaceSharedMemHandle

// 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 "ui/base/clipboard/clipboard.h"

#include <iterator>
#include <limits>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/size.h"

namespace ui {

namespace {

// Valides a shared bitmap on the clipboard.
// Returns true if the clipboard data makes sense and it's safe to access the
// bitmap.
bool ValidateAndMapSharedBitmap(size_t bitmap_bytes,
                                base::SharedMemory* bitmap_data) {
  using base::SharedMemory;

  if (!bitmap_data || !SharedMemory::IsHandleValid(bitmap_data->handle()))
    return false;

  if (!bitmap_data->Map(bitmap_bytes)) {
    PLOG(ERROR) << "Failed to map bitmap memory";
    return false;
  }
  return true;
}

// A list of allowed threads. By default, this is empty and no thread checking
// is done (in the unit test case), but a user (like content) can set which
// threads are allowed to call this method.
typedef std::vector<base::PlatformThreadId> AllowedThreadsVector;
static base::LazyInstance<AllowedThreadsVector> g_allowed_threads =
    LAZY_INSTANCE_INITIALIZER;

// Mapping from threads to clipboard objects.
typedef std::map<base::PlatformThreadId, Clipboard*> ClipboardMap;
static base::LazyInstance<ClipboardMap> g_clipboard_map =
    LAZY_INSTANCE_INITIALIZER;

// Mutex that controls access to |g_clipboard_map|.
static base::LazyInstance<base::Lock>::Leaky
    g_clipboard_map_lock = LAZY_INSTANCE_INITIALIZER;

}  // namespace

// static
void Clipboard::SetAllowedThreads(
    const std::vector<base::PlatformThreadId>& allowed_threads) {
  base::AutoLock lock(g_clipboard_map_lock.Get());

  g_allowed_threads.Get().clear();
  std::copy(allowed_threads.begin(), allowed_threads.end(),
            std::back_inserter(g_allowed_threads.Get()));
}

// static
Clipboard* Clipboard::GetForCurrentThread() {
  base::AutoLock lock(g_clipboard_map_lock.Get());

  base::PlatformThreadId id = base::PlatformThread::CurrentId();

  AllowedThreadsVector* allowed_threads = g_allowed_threads.Pointer();
  if (!allowed_threads->empty()) {
    bool found = false;
    for (AllowedThreadsVector::const_iterator it = allowed_threads->begin();
         it != allowed_threads->end(); ++it) {
      if (*it == id) {
        found = true;
        break;
      }
    }

    DCHECK(found);
  }

  ClipboardMap* clipboard_map = g_clipboard_map.Pointer();
  ClipboardMap::iterator it = clipboard_map->find(id);
  if (it != clipboard_map->end())
    return it->second;

  Clipboard* clipboard = new ui::Clipboard;
  clipboard_map->insert(std::make_pair(id, clipboard));
  return clipboard;
}

void Clipboard::DestroyClipboardForCurrentThread() {
  base::AutoLock lock(g_clipboard_map_lock.Get());

  ClipboardMap* clipboard_map = g_clipboard_map.Pointer();
  base::PlatformThreadId id = base::PlatformThread::CurrentId();
  ClipboardMap::iterator it = clipboard_map->find(id);
  if (it != clipboard_map->end()) {
    delete it->second;
    clipboard_map->erase(it);
  }
}

void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
  // All types apart from CBF_WEBKIT need at least 1 non-empty param.
  if (type != CBF_WEBKIT && (params.empty() || params[0].empty()))
    return;
  // Some other types need a non-empty 2nd param.
  if ((type == CBF_BOOKMARK || type == CBF_SMBITMAP || type == CBF_DATA) &&
      (params.size() != 2 || params[1].empty()))
    return;
  switch (type) {
    case CBF_TEXT:
      WriteText(&(params[0].front()), params[0].size());
      break;

    case CBF_HTML:
      if (params.size() == 2) {
        if (params[1].empty())
          return;
        WriteHTML(&(params[0].front()), params[0].size(),
                  &(params[1].front()), params[1].size());
      } else if (params.size() == 1) {
        WriteHTML(&(params[0].front()), params[0].size(), NULL, 0);
      }
      break;

    case CBF_RTF:
      WriteRTF(&(params[0].front()), params[0].size());
      break;

    case CBF_BOOKMARK:
      WriteBookmark(&(params[0].front()), params[0].size(),
                    &(params[1].front()), params[1].size());
      break;

    case CBF_WEBKIT:
      WriteWebSmartPaste();
      break;

    case CBF_SMBITMAP: {
      using base::SharedMemory;
      using base::SharedMemoryHandle;

      if (params[0].size() != sizeof(SharedMemory*) ||
          params[1].size() != sizeof(gfx::Size)) {
        return;
      }

      SkBitmap bitmap;
      const gfx::Size* unvalidated_size =
          reinterpret_cast<const gfx::Size*>(&params[1].front());
      // Let Skia do some sanity checking for us (no negative widths/heights, no
      // overflows while calculating bytes per row, etc).
      if (!bitmap.setConfig(SkBitmap::kARGB_8888_Config,
                            unvalidated_size->width(),
                            unvalidated_size->height())) {
        return;
      }
      // Make sure the size is representable as a signed 32-bit int, so
      // SkBitmap::getSize() won't be truncated.
      if (!sk_64_isS32(bitmap.computeSize64()))
        return;

      // It's OK to cast away constness here since we map the handle as
      // read-only.
      const char* raw_bitmap_data_const =
          reinterpret_cast<const char*>(&params[0].front());
      char* raw_bitmap_data = const_cast<char*>(raw_bitmap_data_const);
      scoped_ptr<SharedMemory> bitmap_data(
          *reinterpret_cast<SharedMemory**>(raw_bitmap_data));

      if (!ValidateAndMapSharedBitmap(bitmap.getSize(), bitmap_data.get()))
        return;
      bitmap.setPixels(bitmap_data->memory());

      WriteBitmap(bitmap);
      break;
    }

    case CBF_DATA:
      WriteData(
          FormatType::Deserialize(
              std::string(&(params[0].front()), params[0].size())),
          &(params[1].front()),
          params[1].size());
      break;

    default:
      NOTREACHED();
  }
}

// static
bool Clipboard::ReplaceSharedMemHandle(ObjectMap* objects,
                                       base::SharedMemoryHandle bitmap_handle,
                                       base::ProcessHandle process) {
  using base::SharedMemory;
  bool has_shared_bitmap = false;

  for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
       ++iter) {
    if (iter->first == CBF_SMBITMAP) {
      // The code currently only accepts sending a single bitmap over this way.
      // Fail if we ever encounter more than one shmem bitmap structure to fill.
      if (has_shared_bitmap)
        return false;

#if defined(OS_WIN)
      SharedMemory* bitmap = new SharedMemory(bitmap_handle, true, process);
#else
      SharedMemory* bitmap = new SharedMemory(bitmap_handle, true);
#endif

      // There must always be two parameters associated with each shmem bitmap.
      if (iter->second.size() != 2)
        return false;

      // We store the shared memory object pointer so it can be retrieved by the
      // UI thread (see DispatchObject()).
      iter->second[0].clear();
      for (size_t i = 0; i < sizeof(SharedMemory*); ++i)
        iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
      has_shared_bitmap = true;
    }
  }
  return true;
}

}  // namespace ui

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