root/ui/base/clipboard/clipboard_unittest.cc

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

DEFINITIONS

This source file includes following definitions.
  1. WriteObjectsToClipboard
  2. clipboard
  3. WriteObjectsToClipboard
  4. MarkupMatches
  5. TEST_F
  6. TEST_F
  7. TEST_F
  8. TEST_F
  9. TEST_F
  10. TEST_F
  11. TEST_F
  12. TEST_F
  13. TEST_F
  14. TEST_F
  15. TEST_F
  16. TestBitmapWrite
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F
  23. TEST_F
  24. TEST_F
  25. TEST_F
  26. HtmlTestHelper
  27. TEST_F
  28. TEST_F
  29. TEST_F
  30. TEST_F

// 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 "build/build_config.h"

#include <string>

#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/pickle.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "third_party/skia/include/core/SkUnPreMultiply.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/gfx/size.h"

#if defined(OS_WIN)
#include "ui/base/clipboard/clipboard_util_win.h"
#endif

#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#endif

using base::ASCIIToUTF16;
using base::UTF8ToUTF16;
using base::UTF16ToUTF8;

namespace ui {

class ClipboardTest : public PlatformTest {
 public:
  static void WriteObjectsToClipboard(ui::Clipboard* clipboard,
                                      const Clipboard::ObjectMap& objects) {
    clipboard->WriteObjects(ui::CLIPBOARD_TYPE_COPY_PASTE, objects);
  }

 protected:
  Clipboard& clipboard() { return clipboard_; }

  void WriteObjectsToClipboard(const Clipboard::ObjectMap& objects) {
    WriteObjectsToClipboard(&clipboard(), objects);
  }

 private:
  base::MessageLoopForUI message_loop_;
  Clipboard clipboard_;
};

namespace {

bool MarkupMatches(const base::string16& expected_markup,
                   const base::string16& actual_markup) {
  return actual_markup.find(expected_markup) != base::string16::npos;
}

}  // namespace

TEST_F(ClipboardTest, ClearTest) {
  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteText(ASCIIToUTF16("clear me"));
  }

  clipboard().Clear(CLIPBOARD_TYPE_COPY_PASTE);

  EXPECT_FALSE(clipboard().IsFormatAvailable(
      Clipboard::GetPlainTextWFormatType(), CLIPBOARD_TYPE_COPY_PASTE));
  EXPECT_FALSE(clipboard().IsFormatAvailable(
      Clipboard::GetPlainTextFormatType(), CLIPBOARD_TYPE_COPY_PASTE));
}

TEST_F(ClipboardTest, TextTest) {
  base::string16 text(ASCIIToUTF16("This is a base::string16!#$")), text_result;
  std::string ascii_text;

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteText(text);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(
      Clipboard::GetPlainTextWFormatType(), CLIPBOARD_TYPE_COPY_PASTE));
  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetPlainTextFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  clipboard().ReadText(CLIPBOARD_TYPE_COPY_PASTE, &text_result);

  EXPECT_EQ(text, text_result);
  clipboard().ReadAsciiText(CLIPBOARD_TYPE_COPY_PASTE, &ascii_text);
  EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
}

TEST_F(ClipboardTest, HTMLTest) {
  base::string16 markup(ASCIIToUTF16("<string>Hi!</string>")), markup_result;
  base::string16 plain(ASCIIToUTF16("Hi!")), plain_result;
  std::string url("http://www.example.com/"), url_result;

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteText(plain);
    clipboard_writer.WriteHTML(markup, url);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetHtmlFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  uint32 ignored;
  clipboard().ReadHTML(CLIPBOARD_TYPE_COPY_PASTE, &markup_result, &url_result,
                     &ignored, &ignored);
  EXPECT_PRED2(MarkupMatches, markup, markup_result);
#if defined(OS_WIN)
  // TODO(playmobil): It's not clear that non windows clipboards need to support
  // this.
  EXPECT_EQ(url, url_result);
#endif  // defined(OS_WIN)
}

TEST_F(ClipboardTest, RTFTest) {
  std::string rtf =
      "{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\n"
      "This is some {\\b bold} text.\\par\n"
      "}";

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteRTF(rtf);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetRtfFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  std::string result;
  clipboard().ReadRTF(CLIPBOARD_TYPE_COPY_PASTE, &result);
  EXPECT_EQ(rtf, result);
}

#if defined(TOOLKIT_GTK)
TEST_F(ClipboardTest, MultipleBufferTest) {
  base::string16 text(ASCIIToUTF16("Standard")), text_result;
  base::string16 markup(ASCIIToUTF16("<string>Selection</string>"));
  std::string url("http://www.example.com/"), url_result;

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteText(text);
  }

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_SELECTION);
    clipboard_writer.WriteHTML(markup, url);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetPlainTextFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  EXPECT_FALSE(clipboard().IsFormatAvailable(
      Clipboard::GetPlainTextFormatType(),
      CLIPBOARD_TYPE_SELECTION));

  EXPECT_FALSE(clipboard().IsFormatAvailable(Clipboard::GetHtmlFormatType(),
                                             CLIPBOARD_TYPE_COPY_PASTE));
  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetHtmlFormatType(),
                                            CLIPBOARD_TYPE_SELECTION));

  clipboard().ReadText(CLIPBOARD_TYPE_COPY_PASTE, &text_result);
  EXPECT_EQ(text, text_result);

  uint32 ignored;
  base::string16 markup_result;
  clipboard().ReadHTML(CLIPBOARD_TYPE_SELECTION,
                       &markup_result,
                       &url_result,
                       &ignored,
                       &ignored);
  EXPECT_PRED2(MarkupMatches, markup, markup_result);
}
#endif

TEST_F(ClipboardTest, TrickyHTMLTest) {
  base::string16 markup(ASCIIToUTF16("<em>Bye!<!--EndFragment --></em>")),
      markup_result;
  std::string url, url_result;
  base::string16 plain(ASCIIToUTF16("Bye!")), plain_result;

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteText(plain);
    clipboard_writer.WriteHTML(markup, url);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetHtmlFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  uint32 ignored;
  clipboard().ReadHTML(CLIPBOARD_TYPE_COPY_PASTE, &markup_result, &url_result,
                       &ignored, &ignored);
  EXPECT_PRED2(MarkupMatches, markup, markup_result);
#if defined(OS_WIN)
  // TODO(playmobil): It's not clear that non windows clipboards need to support
  // this.
  EXPECT_EQ(url, url_result);
#endif  // defined(OS_WIN)
}

#if defined(OS_WIN)
TEST_F(ClipboardTest, UniodeHTMLTest) {
  base::string16 markup(UTF8ToUTF16("<div>A \xc3\xb8 \xe6\xb0\xb4</div>")),
      markup_result;
  std::string url, url_result;

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteHTML(markup, url);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetHtmlFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  uint32 fragment_start;
  uint32 fragment_end;
  clipboard().ReadHTML(CLIPBOARD_TYPE_COPY_PASTE, &markup_result, &url_result,
                       &fragment_start, &fragment_end);
  EXPECT_PRED2(MarkupMatches, markup, markup_result);
  EXPECT_EQ(url, url_result);
  // Make sure that fragment indices were adjusted when converting.
  EXPECT_EQ(36, fragment_start);
  EXPECT_EQ(52, fragment_end);
}
#endif  // defined(OS_WIN)

#if defined(TOOLKIT_GTK)
// Regression test for crbug.com/56298 (pasting empty HTML crashes Linux).
TEST_F(ClipboardTest, EmptyHTMLTest) {
  // ScopedClipboardWriter doesn't let us write empty data to the clipboard.
  clipboard().clipboard_data_ = new Clipboard::TargetMap();
  // The 1 is so the compiler doesn't warn about allocating an empty array.
  char* empty = new char[1];
  clipboard().InsertMapping("text/html", empty, 0U);
  clipboard().SetGtkClipboard(CLIPBOARD_TYPE_COPY_PASTE);

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetHtmlFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  base::string16 markup_result;
  std::string url_result;
  uint32 ignored;
  clipboard().ReadHTML(CLIPBOARD_TYPE_COPY_PASTE, &markup_result, &url_result,
                       &ignored, &ignored);
  EXPECT_PRED2(MarkupMatches, base::string16(), markup_result);
}
#endif

// TODO(estade): Port the following test (decide what target we use for urls)
#if !defined(OS_POSIX) || defined(OS_MACOSX)
TEST_F(ClipboardTest, BookmarkTest) {
  base::string16 title(ASCIIToUTF16("The Example Company")), title_result;
  std::string url("http://www.example.com/"), url_result;

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteBookmark(title, url);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetUrlWFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  clipboard().ReadBookmark(&title_result, &url_result);
  EXPECT_EQ(title, title_result);
  EXPECT_EQ(url, url_result);
}
#endif  // defined(OS_WIN)

TEST_F(ClipboardTest, MultiFormatTest) {
  base::string16 text(ASCIIToUTF16("Hi!")), text_result;
  base::string16 markup(ASCIIToUTF16("<strong>Hi!</string>")), markup_result;
  std::string url("http://www.example.com/"), url_result;
  std::string ascii_text;

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteHTML(markup, url);
    clipboard_writer.WriteText(text);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetHtmlFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  EXPECT_TRUE(clipboard().IsFormatAvailable(
      Clipboard::GetPlainTextWFormatType(), CLIPBOARD_TYPE_COPY_PASTE));
  EXPECT_TRUE(clipboard().IsFormatAvailable(
      Clipboard::GetPlainTextFormatType(), CLIPBOARD_TYPE_COPY_PASTE));
  uint32 ignored;
  clipboard().ReadHTML(CLIPBOARD_TYPE_COPY_PASTE, &markup_result, &url_result,
                       &ignored, &ignored);
  EXPECT_PRED2(MarkupMatches, markup, markup_result);
#if defined(OS_WIN)
  // TODO(playmobil): It's not clear that non windows clipboards need to support
  // this.
  EXPECT_EQ(url, url_result);
#endif  // defined(OS_WIN)
  clipboard().ReadText(CLIPBOARD_TYPE_COPY_PASTE, &text_result);
  EXPECT_EQ(text, text_result);
  clipboard().ReadAsciiText(CLIPBOARD_TYPE_COPY_PASTE, &ascii_text);
  EXPECT_EQ(UTF16ToUTF8(text), ascii_text);
}

TEST_F(ClipboardTest, URLTest) {
  base::string16 url(ASCIIToUTF16("http://www.google.com/"));

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteURL(url);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(
      Clipboard::GetPlainTextWFormatType(), CLIPBOARD_TYPE_COPY_PASTE));
  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetPlainTextFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  base::string16 text_result;
  clipboard().ReadText(CLIPBOARD_TYPE_COPY_PASTE, &text_result);

  EXPECT_EQ(text_result, url);

  std::string ascii_text;
  clipboard().ReadAsciiText(CLIPBOARD_TYPE_COPY_PASTE, &ascii_text);
  EXPECT_EQ(UTF16ToUTF8(url), ascii_text);

#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
  ascii_text.clear();
  clipboard().ReadAsciiText(CLIPBOARD_TYPE_SELECTION, &ascii_text);
  EXPECT_EQ(UTF16ToUTF8(url), ascii_text);
#endif
}

// TODO(dcheng): The tests for copying to the clipboard also test IPC
// interaction... consider moving them to a different layer so we can
// consolidate the validation logic.
// Note that |bitmap_data| is not premultiplied!
static void TestBitmapWrite(Clipboard* clipboard,
                            const uint32* bitmap_data,
                            size_t bitmap_data_size,
                            const gfx::Size& size) {
  // Create shared memory region.
  base::SharedMemory shared_buf;
  ASSERT_TRUE(shared_buf.CreateAndMapAnonymous(bitmap_data_size));
  memcpy(shared_buf.memory(), bitmap_data, bitmap_data_size);
  // CBF_SMBITMAP expects premultiplied bitmap data so do that now.
  uint32* pixel_buffer = static_cast<uint32*>(shared_buf.memory());
  for (int j = 0; j < size.height(); ++j) {
    for (int i = 0; i < size.width(); ++i) {
      uint32& pixel = pixel_buffer[i + j * size.width()];
      pixel = SkPreMultiplyColor(pixel);
    }
  }
  base::SharedMemoryHandle handle_to_share;
  base::ProcessHandle current_process = base::kNullProcessHandle;
#if defined(OS_WIN)
  current_process = GetCurrentProcess();
#endif
  shared_buf.ShareToProcess(current_process, &handle_to_share);
  ASSERT_TRUE(shared_buf.Unmap());

  // Setup data for clipboard().
  Clipboard::ObjectMapParam placeholder_param;
  Clipboard::ObjectMapParam size_param;
  const char* size_data = reinterpret_cast<const char*>(&size);
  for (size_t i = 0; i < sizeof(size); ++i)
    size_param.push_back(size_data[i]);

  Clipboard::ObjectMapParams params;
  params.push_back(placeholder_param);
  params.push_back(size_param);

  Clipboard::ObjectMap objects;
  objects[Clipboard::CBF_SMBITMAP] = params;
  ASSERT_TRUE(Clipboard::ReplaceSharedMemHandle(
      &objects, handle_to_share, current_process));

  ClipboardTest::WriteObjectsToClipboard(clipboard, objects);

  EXPECT_TRUE(clipboard->IsFormatAvailable(Clipboard::GetBitmapFormatType(),
                                           CLIPBOARD_TYPE_COPY_PASTE));
  const SkBitmap& image = clipboard->ReadImage(CLIPBOARD_TYPE_COPY_PASTE);
  EXPECT_EQ(size, gfx::Size(image.width(), image.height()));
  SkAutoLockPixels image_lock(image);
  for (int j = 0; j < image.height(); ++j) {
    const uint32* row_address = image.getAddr32(0, j);
    for (int i = 0; i < image.width(); ++i) {
      int offset = i + j * image.width();
      uint32 pixel = SkPreMultiplyColor(bitmap_data[offset]);
#if defined(TOOLKIT_GTK)
      // Non-Aura GTK doesn't support alpha transparency. Instead, the alpha
      // channel is always set to 0xFF - see http://crbug.com/154573.
      // However, since we premultiplied above, we must also premultiply here
      // before unpremultiplying and setting alpha to 0xFF; otherwise, the
      // results will not match GTK's.
      EXPECT_EQ(
          SkUnPreMultiply::PMColorToColor(pixel) | 0xFF000000, row_address[i])
          << "i = " << i << ", j = " << j;
#else
      EXPECT_EQ(pixel, row_address[i])
          << "i = " << i << ", j = " << j;
#endif  // defined(TOOLKIT_GTK)
    }
  }
}

TEST_F(ClipboardTest, SharedBitmapTest) {
  const uint32 fake_bitmap_1[] = {
    0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89,
    0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568,
    0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1,
  };
  {
    SCOPED_TRACE("first bitmap");
    TestBitmapWrite(
        &clipboard(), fake_bitmap_1, sizeof(fake_bitmap_1), gfx::Size(4, 3));
  }

  const uint32 fake_bitmap_2[] = {
    0x46155189, 0xF6A55C8D,
    0x79845674, 0xFA57BD89,
    0x78FD46AE, 0x87C64F5A,
    0x36EDC5AF, 0x4378F568,
    0x91E9F63A, 0xC31EA14F,
    0x69AB32DF, 0x643A3FD1,
    0xA6DF041D, 0x83046278,
  };
  {
    SCOPED_TRACE("second bitmap");
    TestBitmapWrite(
        &clipboard(), fake_bitmap_2, sizeof(fake_bitmap_2), gfx::Size(2, 7));
  }
}

namespace {
// A size class that just happens to have the same layout as gfx::Size!
struct UnsafeSize {
  int width;
  int height;
};
COMPILE_ASSERT(sizeof(UnsafeSize) == sizeof(gfx::Size),
               UnsafeSize_must_be_same_size_as_gfx_Size);
}  // namespace

TEST_F(ClipboardTest, SharedBitmapWithTwoNegativeSizes) {
  Clipboard::ObjectMapParam placeholder_param;
  void* crash_me = reinterpret_cast<void*>(57);
  placeholder_param.resize(sizeof(crash_me));
  memcpy(&placeholder_param.front(), &crash_me, sizeof(crash_me));

  Clipboard::ObjectMapParam size_param;
  UnsafeSize size = {-100, -100};
  size_param.resize(sizeof(size));
  memcpy(&size_param.front(), &size, sizeof(size));

  Clipboard::ObjectMapParams params;
  params.push_back(placeholder_param);
  params.push_back(size_param);

  Clipboard::ObjectMap objects;
  objects[Clipboard::CBF_SMBITMAP] = params;

  WriteObjectsToClipboard(objects);
  EXPECT_FALSE(clipboard().IsFormatAvailable(Clipboard::GetBitmapFormatType(),
                                             CLIPBOARD_TYPE_COPY_PASTE));
}

TEST_F(ClipboardTest, SharedBitmapWithOneNegativeSize) {
  Clipboard::ObjectMapParam placeholder_param;
  void* crash_me = reinterpret_cast<void*>(57);
  placeholder_param.resize(sizeof(crash_me));
  memcpy(&placeholder_param.front(), &crash_me, sizeof(crash_me));

  Clipboard::ObjectMapParam size_param;
  UnsafeSize size = {-100, 100};
  size_param.resize(sizeof(size));
  memcpy(&size_param.front(), &size, sizeof(size));

  Clipboard::ObjectMapParams params;
  params.push_back(placeholder_param);
  params.push_back(size_param);

  Clipboard::ObjectMap objects;
  objects[Clipboard::CBF_SMBITMAP] = params;

  WriteObjectsToClipboard(objects);
  EXPECT_FALSE(clipboard().IsFormatAvailable(Clipboard::GetBitmapFormatType(),
                                             CLIPBOARD_TYPE_COPY_PASTE));
}

TEST_F(ClipboardTest, BitmapWithSuperSize) {
  Clipboard::ObjectMapParam placeholder_param;
  void* crash_me = reinterpret_cast<void*>(57);
  placeholder_param.resize(sizeof(crash_me));
  memcpy(&placeholder_param.front(), &crash_me, sizeof(crash_me));

  Clipboard::ObjectMapParam size_param;
  // Width just big enough that bytes per row won't fit in a 32-bit
  // representation.
  gfx::Size size(0x20000000, 1);
  size_param.resize(sizeof(size));
  memcpy(&size_param.front(), &size, sizeof(size));

  Clipboard::ObjectMapParams params;
  params.push_back(placeholder_param);
  params.push_back(size_param);

  Clipboard::ObjectMap objects;
  objects[Clipboard::CBF_SMBITMAP] = params;

  WriteObjectsToClipboard(objects);
  EXPECT_FALSE(clipboard().IsFormatAvailable(Clipboard::GetBitmapFormatType(),
                                             CLIPBOARD_TYPE_COPY_PASTE));
}

TEST_F(ClipboardTest, BitmapWithSuperSize2) {
  Clipboard::ObjectMapParam placeholder_param;
  void* crash_me = reinterpret_cast<void*>(57);
  placeholder_param.resize(sizeof(crash_me));
  memcpy(&placeholder_param.front(), &crash_me, sizeof(crash_me));

  Clipboard::ObjectMapParam size_param;
  // Width and height large enough that SkBitmap::getSize() will be truncated.
  gfx::Size size(0x0fffffff, 0x0fffffff);
  size_param.resize(sizeof(size));
  memcpy(&size_param.front(), &size, sizeof(size));

  Clipboard::ObjectMapParams params;
  params.push_back(placeholder_param);
  params.push_back(size_param);

  Clipboard::ObjectMap objects;
  objects[Clipboard::CBF_SMBITMAP] = params;

  WriteObjectsToClipboard(objects);
  EXPECT_FALSE(clipboard().IsFormatAvailable(Clipboard::GetBitmapFormatType(),
                                             CLIPBOARD_TYPE_COPY_PASTE));
}

TEST_F(ClipboardTest, DataTest) {
  const ui::Clipboard::FormatType kFormat =
      ui::Clipboard::GetFormatType("chromium/x-test-format");
  std::string payload("test string");
  Pickle write_pickle;
  write_pickle.WriteString(payload);

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WritePickledData(write_pickle, kFormat);
  }

  ASSERT_TRUE(clipboard().IsFormatAvailable(
      kFormat, CLIPBOARD_TYPE_COPY_PASTE));
  std::string output;
  clipboard().ReadData(kFormat, &output);
  ASSERT_FALSE(output.empty());

  Pickle read_pickle(output.data(), output.size());
  PickleIterator iter(read_pickle);
  std::string unpickled_string;
  ASSERT_TRUE(read_pickle.ReadString(&iter, &unpickled_string));
  EXPECT_EQ(payload, unpickled_string);
}

TEST_F(ClipboardTest, MultipleDataTest) {
  const ui::Clipboard::FormatType kFormat1 =
      ui::Clipboard::GetFormatType("chromium/x-test-format1");
  std::string payload1("test string1");
  Pickle write_pickle1;
  write_pickle1.WriteString(payload1);

  const ui::Clipboard::FormatType kFormat2 =
      ui::Clipboard::GetFormatType("chromium/x-test-format2");
  std::string payload2("test string2");
  Pickle write_pickle2;
  write_pickle2.WriteString(payload2);

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WritePickledData(write_pickle1, kFormat1);
    // overwrite the previous pickle for fun
    clipboard_writer.WritePickledData(write_pickle2, kFormat2);
  }

  ASSERT_TRUE(clipboard().IsFormatAvailable(
      kFormat2, CLIPBOARD_TYPE_COPY_PASTE));

  // Check string 2.
  std::string output2;
  clipboard().ReadData(kFormat2, &output2);
  ASSERT_FALSE(output2.empty());

  Pickle read_pickle2(output2.data(), output2.size());
  PickleIterator iter2(read_pickle2);
  std::string unpickled_string2;
  ASSERT_TRUE(read_pickle2.ReadString(&iter2, &unpickled_string2));
  EXPECT_EQ(payload2, unpickled_string2);

  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WritePickledData(write_pickle2, kFormat2);
    // overwrite the previous pickle for fun
    clipboard_writer.WritePickledData(write_pickle1, kFormat1);
  }

  ASSERT_TRUE(clipboard().IsFormatAvailable(
      kFormat1, CLIPBOARD_TYPE_COPY_PASTE));

  // Check string 1.
  std::string output1;
  clipboard().ReadData(kFormat1, &output1);
  ASSERT_FALSE(output1.empty());

  Pickle read_pickle1(output1.data(), output1.size());
  PickleIterator iter1(read_pickle1);
  std::string unpickled_string1;
  ASSERT_TRUE(read_pickle1.ReadString(&iter1, &unpickled_string1));
  EXPECT_EQ(payload1, unpickled_string1);
}

#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
TEST_F(ClipboardTest, HyperlinkTest) {
  const std::string kTitle("The <Example> Company's \"home page\"");
  const std::string kUrl("http://www.example.com?x=3&lt=3#\"'<>");
  const std::string kExpectedHtml(
      "<a href=\"http://www.example.com?x=3&amp;lt=3#&quot;&#39;&lt;&gt;\">"
      "The &lt;Example&gt; Company&#39;s &quot;home page&quot;</a>");

  std::string url_result;
  base::string16 html_result;
  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteHyperlink(ASCIIToUTF16(kTitle), kUrl);
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(Clipboard::GetHtmlFormatType(),
                                            CLIPBOARD_TYPE_COPY_PASTE));
  uint32 ignored;
  clipboard().ReadHTML(CLIPBOARD_TYPE_COPY_PASTE, &html_result, &url_result,
                       &ignored, &ignored);
  EXPECT_PRED2(MarkupMatches, ASCIIToUTF16(kExpectedHtml), html_result);
}
#endif

#if defined(OS_WIN)  // Windows only tests.
TEST_F(ClipboardTest, WebSmartPasteTest) {
  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteWebSmartPaste();
  }

  EXPECT_TRUE(clipboard().IsFormatAvailable(
      Clipboard::GetWebKitSmartPasteFormatType(), CLIPBOARD_TYPE_COPY_PASTE));
}

void HtmlTestHelper(const std::string& cf_html,
                    const std::string& expected_html) {
  std::string html;
  ClipboardUtil::CFHtmlToHtml(cf_html, &html, NULL);
  EXPECT_EQ(html, expected_html);
}

TEST_F(ClipboardTest, HtmlTest) {
  // Test converting from CF_HTML format data with <!--StartFragment--> and
  // <!--EndFragment--> comments, like from MS Word.
  HtmlTestHelper("Version:1.0\r\n"
                 "StartHTML:0000000105\r\n"
                 "EndHTML:0000000199\r\n"
                 "StartFragment:0000000123\r\n"
                 "EndFragment:0000000161\r\n"
                 "\r\n"
                 "<html>\r\n"
                 "<body>\r\n"
                 "<!--StartFragment-->\r\n"
                 "\r\n"
                 "<p>Foo</p>\r\n"
                 "\r\n"
                 "<!--EndFragment-->\r\n"
                 "</body>\r\n"
                 "</html>\r\n\r\n",
                 "<p>Foo</p>");

  // Test converting from CF_HTML format data without <!--StartFragment--> and
  // <!--EndFragment--> comments, like from OpenOffice Writer.
  HtmlTestHelper("Version:1.0\r\n"
                 "StartHTML:0000000105\r\n"
                 "EndHTML:0000000151\r\n"
                 "StartFragment:0000000121\r\n"
                 "EndFragment:0000000131\r\n"
                 "<html>\r\n"
                 "<body>\r\n"
                 "<p>Foo</p>\r\n"
                 "</body>\r\n"
                 "</html>\r\n\r\n",
                 "<p>Foo</p>");
}
#endif  // defined(OS_WIN)

// Test writing all formats we have simultaneously.
TEST_F(ClipboardTest, WriteEverything) {
  {
    ScopedClipboardWriter writer(&clipboard(), CLIPBOARD_TYPE_COPY_PASTE);
    writer.WriteText(UTF8ToUTF16("foo"));
    writer.WriteURL(UTF8ToUTF16("foo"));
    writer.WriteHTML(UTF8ToUTF16("foo"), "bar");
    writer.WriteBookmark(UTF8ToUTF16("foo"), "bar");
    writer.WriteHyperlink(ASCIIToUTF16("foo"), "bar");
    writer.WriteWebSmartPaste();
    // Left out: WriteFile, WriteFiles, WriteBitmapFromPixels, WritePickledData.
  }

  // Passes if we don't crash.
}

// TODO(dcheng): Fix this test for Android. It's rather involved, since the
// clipboard change listener is posted to the Java message loop, and spinning
// that loop from C++ to trigger the callback in the test requires a non-trivial
// amount of additional work.
#if !defined(OS_ANDROID)
// Simple test that the sequence number appears to change when the clipboard is
// written to.
// TODO(dcheng): Add a version to test CLIPBOARD_TYPE_SELECTION.
TEST_F(ClipboardTest, GetSequenceNumber) {
  const uint64 first_sequence_number =
      clipboard().GetSequenceNumber(CLIPBOARD_TYPE_COPY_PASTE);

  {
    ScopedClipboardWriter writer(&clipboard(), CLIPBOARD_TYPE_COPY_PASTE);
    writer.WriteText(UTF8ToUTF16("World"));
  }

  // On some platforms, the sequence number is updated by a UI callback so pump
  // the message loop to make sure we get the notification.
  base::RunLoop().RunUntilIdle();

  const uint64 second_sequence_number =
      clipboard().GetSequenceNumber(CLIPBOARD_TYPE_COPY_PASTE);

  EXPECT_NE(first_sequence_number, second_sequence_number);
}
#endif

#if defined(OS_ANDROID)

// Test that if another application writes some text to the pasteboard the
// clipboard properly invalidates other types.
TEST_F(ClipboardTest, InternalClipboardInvalidation) {
  // Write a Webkit smart paste tag to our clipboard.
  {
    ScopedClipboardWriter clipboard_writer(&clipboard(),
                                           CLIPBOARD_TYPE_COPY_PASTE);
    clipboard_writer.WriteWebSmartPaste();
  }
  EXPECT_TRUE(clipboard().IsFormatAvailable(
      Clipboard::GetWebKitSmartPasteFormatType(), CLIPBOARD_TYPE_COPY_PASTE));

  //
  // Simulate that another application copied something in the Clipboard
  //
  std::string new_value("Some text copied by some other app");
  using base::android::ConvertUTF8ToJavaString;
  using base::android::MethodID;
  using base::android::ScopedJavaLocalRef;

  JNIEnv* env = base::android::AttachCurrentThread();
  ASSERT_TRUE(env);

  jobject context = base::android::GetApplicationContext();
  ASSERT_TRUE(context);

  ScopedJavaLocalRef<jclass> context_class =
      base::android::GetClass(env, "android/content/Context");

  jmethodID get_system_service = MethodID::Get<MethodID::TYPE_INSTANCE>(
      env, context_class.obj(), "getSystemService",
      "(Ljava/lang/String;)Ljava/lang/Object;");

  // Retrieve the system service.
  ScopedJavaLocalRef<jstring> service_name = ConvertUTF8ToJavaString(
      env, "clipboard");
  ScopedJavaLocalRef<jobject> clipboard_manager(
      env, env->CallObjectMethod(
        context, get_system_service, service_name.obj()));
  ASSERT_TRUE(clipboard_manager.obj() && !base::android::ClearException(env));

  ScopedJavaLocalRef<jclass> clipboard_class =
      base::android::GetClass(env, "android/text/ClipboardManager");
  jmethodID set_text = MethodID::Get<MethodID::TYPE_INSTANCE>(
      env, clipboard_class.obj(), "setText", "(Ljava/lang/CharSequence;)V");
  ScopedJavaLocalRef<jstring> new_value_string = ConvertUTF8ToJavaString(
      env, new_value.c_str());

  // Will need to call toString as CharSequence is not always a String.
  env->CallVoidMethod(clipboard_manager.obj(),
                      set_text,
                      new_value_string.obj());

  // The WebKit smart paste tag should now be gone.
  EXPECT_FALSE(clipboard().IsFormatAvailable(
      Clipboard::GetWebKitSmartPasteFormatType(), CLIPBOARD_TYPE_COPY_PASTE));

  // Make sure some text is available
  EXPECT_TRUE(clipboard().IsFormatAvailable(
      Clipboard::GetPlainTextWFormatType(), CLIPBOARD_TYPE_COPY_PASTE));

  // Make sure the text is what we inserted while simulating the other app
  std::string contents;
  clipboard().ReadAsciiText(CLIPBOARD_TYPE_COPY_PASTE, &contents);
  EXPECT_EQ(contents, new_value);
}
#endif
}  // namespace ui

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