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 "ppapi/tests/test_flash_clipboard.h"
6
7#include <algorithm>
8#include <vector>
9
10#include "ppapi/cpp/instance.h"
11#include "ppapi/cpp/module.h"
12#include "ppapi/cpp/point.h"
13#include "ppapi/cpp/private/flash_clipboard.h"
14#include "ppapi/cpp/var.h"
15#include "ppapi/cpp/var_array_buffer.h"
16#include "ppapi/tests/testing_instance.h"
17
18// http://crbug.com/176822
19#if !defined(OS_WIN)
20REGISTER_TEST_CASE(FlashClipboard);
21#endif
22
23// WriteData() sends an async request to the browser process. As a result, the
24// string written may not be reflected by IsFormatAvailable() or ReadPlainText()
25// immediately. We need to wait and retry.
26const int kIntervalMs = 250;
27const int kMaxIntervals = kActionTimeoutMs / kIntervalMs;
28
29TestFlashClipboard::TestFlashClipboard(TestingInstance* instance)
30    : TestCase(instance) {
31}
32
33void TestFlashClipboard::RunTests(const std::string& filter) {
34  RUN_TEST(ReadWritePlainText, filter);
35  RUN_TEST(ReadWriteHTML, filter);
36  RUN_TEST(ReadWriteRTF, filter);
37  RUN_TEST(ReadWriteCustomData, filter);
38  RUN_TEST(ReadWriteMultipleFormats, filter);
39  RUN_TEST(Clear, filter);
40  RUN_TEST(InvalidFormat, filter);
41  RUN_TEST(RegisterCustomFormat, filter);
42  RUN_TEST(GetSequenceNumber, filter);
43}
44
45bool TestFlashClipboard::ReadStringVar(uint32_t format, std::string* result) {
46  pp::Var text;
47  bool success = pp::flash::Clipboard::ReadData(
48      instance_,
49      PP_FLASH_CLIPBOARD_TYPE_STANDARD,
50      format,
51      &text);
52  if (success && text.is_string()) {
53    *result = text.AsString();
54    return true;
55  }
56  return false;
57}
58
59bool TestFlashClipboard::WriteStringVar(uint32_t format,
60                                        const std::string& text) {
61  std::vector<uint32_t> formats_vector(1, format);
62  std::vector<pp::Var> data_vector(1, pp::Var(text));
63  bool success = pp::flash::Clipboard::WriteData(
64      instance_,
65      PP_FLASH_CLIPBOARD_TYPE_STANDARD,
66      formats_vector,
67      data_vector);
68  return success;
69}
70
71bool TestFlashClipboard::IsFormatAvailableMatches(uint32_t format,
72                                                  bool expected) {
73  for (int i = 0; i < kMaxIntervals; ++i) {
74    bool is_available = pp::flash::Clipboard::IsFormatAvailable(
75        instance_,
76        PP_FLASH_CLIPBOARD_TYPE_STANDARD,
77        format);
78    if (is_available == expected)
79      return true;
80
81    PlatformSleep(kIntervalMs);
82  }
83  return false;
84}
85
86bool TestFlashClipboard::ReadPlainTextMatches(const std::string& expected) {
87  for (int i = 0; i < kMaxIntervals; ++i) {
88    std::string result;
89    bool success = ReadStringVar(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT, &result);
90    if (success && result == expected)
91      return true;
92
93    PlatformSleep(kIntervalMs);
94  }
95  return false;
96}
97
98bool TestFlashClipboard::ReadHTMLMatches(const std::string& expected) {
99  for (int i = 0; i < kMaxIntervals; ++i) {
100    std::string result;
101    bool success = ReadStringVar(PP_FLASH_CLIPBOARD_FORMAT_HTML, &result);
102    // Harmless markup may be inserted around the copied html on some
103    // platforms, so just check that the pasted string contains the
104    // copied string. Also check that we only paste the copied fragment, see
105    // http://code.google.com/p/chromium/issues/detail?id=130827.
106    if (success && result.find(expected) != std::string::npos &&
107        result.find("<!--StartFragment-->") == std::string::npos &&
108        result.find("<!--EndFragment-->") == std::string::npos) {
109      return true;
110    }
111
112    PlatformSleep(kIntervalMs);
113  }
114  return false;
115}
116
117uint64_t TestFlashClipboard::GetSequenceNumber(uint64_t last_sequence_number) {
118  uint64_t next_sequence_number = last_sequence_number;
119  for (int i = 0; i < kMaxIntervals; ++i) {
120    pp::flash::Clipboard::GetSequenceNumber(
121        instance_, PP_FLASH_CLIPBOARD_TYPE_STANDARD, &next_sequence_number);
122    if (next_sequence_number != last_sequence_number)
123      return next_sequence_number;
124
125    PlatformSleep(kIntervalMs);
126  }
127  return next_sequence_number;
128}
129
130std::string TestFlashClipboard::TestReadWritePlainText() {
131  std::string input = "Hello world plain text!";
132  ASSERT_TRUE(WriteStringVar(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT, input));
133  ASSERT_TRUE(IsFormatAvailableMatches(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT,
134                                       true));
135  ASSERT_TRUE(ReadPlainTextMatches(input));
136
137  PASS();
138}
139
140std::string TestFlashClipboard::TestReadWriteHTML() {
141  std::string input = "Hello world html!";
142  ASSERT_TRUE(WriteStringVar(PP_FLASH_CLIPBOARD_FORMAT_HTML, input));
143  ASSERT_TRUE(IsFormatAvailableMatches(PP_FLASH_CLIPBOARD_FORMAT_HTML, true));
144  ASSERT_TRUE(ReadHTMLMatches(input));
145
146  PASS();
147}
148
149std::string TestFlashClipboard::TestReadWriteRTF() {
150  std::string rtf_string =
151        "{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\n"
152        "This is some {\\b bold} text.\\par\n"
153        "}";
154  pp::VarArrayBuffer array_buffer(rtf_string.size());
155  char* bytes = static_cast<char*>(array_buffer.Map());
156  std::copy(rtf_string.data(), rtf_string.data() + rtf_string.size(), bytes);
157  std::vector<uint32_t> formats_vector(1, PP_FLASH_CLIPBOARD_FORMAT_RTF);
158  std::vector<pp::Var> data_vector(1, array_buffer);
159  ASSERT_TRUE(pp::flash::Clipboard::WriteData(
160      instance_,
161      PP_FLASH_CLIPBOARD_TYPE_STANDARD,
162      formats_vector,
163      data_vector));
164
165  ASSERT_TRUE(IsFormatAvailableMatches(PP_FLASH_CLIPBOARD_FORMAT_RTF, true));
166
167  pp::Var rtf_result;
168  ASSERT_TRUE(pp::flash::Clipboard::ReadData(
169        instance_,
170        PP_FLASH_CLIPBOARD_TYPE_STANDARD,
171        PP_FLASH_CLIPBOARD_FORMAT_RTF,
172        &rtf_result));
173  ASSERT_TRUE(rtf_result.is_array_buffer());
174  pp::VarArrayBuffer array_buffer_result(rtf_result);
175  ASSERT_TRUE(array_buffer_result.ByteLength() == array_buffer.ByteLength());
176  char* bytes_result = static_cast<char*>(array_buffer_result.Map());
177  ASSERT_TRUE(std::equal(bytes, bytes + array_buffer.ByteLength(),
178      bytes_result));
179
180  PASS();
181}
182
183std::string TestFlashClipboard::TestReadWriteCustomData() {
184  std::string custom_data = "custom_data";
185  pp::VarArrayBuffer array_buffer(custom_data.size());
186  char* bytes = static_cast<char*>(array_buffer.Map());
187  std::copy(custom_data.begin(), custom_data.end(), bytes);
188  uint32_t format_id =
189      pp::flash::Clipboard::RegisterCustomFormat(instance_, "my-format");
190  ASSERT_NE(static_cast<uint32_t>(PP_FLASH_CLIPBOARD_FORMAT_INVALID),
191            format_id);
192
193  std::vector<uint32_t> formats_vector(1, format_id);
194  std::vector<pp::Var> data_vector(1, array_buffer);
195  ASSERT_TRUE(pp::flash::Clipboard::WriteData(
196      instance_,
197      PP_FLASH_CLIPBOARD_TYPE_STANDARD,
198      formats_vector,
199      data_vector));
200
201  ASSERT_TRUE(IsFormatAvailableMatches(format_id, true));
202
203  pp::Var custom_data_result;
204  ASSERT_TRUE(pp::flash::Clipboard::ReadData(
205      instance_,
206      PP_FLASH_CLIPBOARD_TYPE_STANDARD,
207      format_id,
208      &custom_data_result));
209  ASSERT_TRUE(custom_data_result.is_array_buffer());
210  pp::VarArrayBuffer array_buffer_result(custom_data_result);
211  ASSERT_EQ(array_buffer_result.ByteLength(), array_buffer.ByteLength());
212  char* bytes_result = static_cast<char*>(array_buffer_result.Map());
213  ASSERT_TRUE(std::equal(bytes, bytes + array_buffer.ByteLength(),
214      bytes_result));
215
216  PASS();
217}
218
219std::string TestFlashClipboard::TestReadWriteMultipleFormats() {
220  std::vector<uint32_t> formats;
221  std::vector<pp::Var> data;
222  formats.push_back(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT);
223  data.push_back(pp::Var("plain text"));
224  formats.push_back(PP_FLASH_CLIPBOARD_FORMAT_HTML);
225  data.push_back(pp::Var("html"));
226  bool success = pp::flash::Clipboard::WriteData(
227      instance_,
228      PP_FLASH_CLIPBOARD_TYPE_STANDARD,
229      formats,
230      data);
231  ASSERT_TRUE(success);
232  ASSERT_TRUE(IsFormatAvailableMatches(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT,
233                                       true));
234  ASSERT_TRUE(IsFormatAvailableMatches(PP_FLASH_CLIPBOARD_FORMAT_HTML, true));
235  ASSERT_TRUE(ReadPlainTextMatches(data[0].AsString()));
236  ASSERT_TRUE(ReadHTMLMatches(data[1].AsString()));
237
238  PASS();
239}
240
241std::string TestFlashClipboard::TestClear() {
242  std::string input = "Hello world plain text!";
243  ASSERT_TRUE(WriteStringVar(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT, input));
244  ASSERT_TRUE(IsFormatAvailableMatches(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT,
245                                       true));
246  bool success = pp::flash::Clipboard::WriteData(
247      instance_,
248      PP_FLASH_CLIPBOARD_TYPE_STANDARD,
249      std::vector<uint32_t>(),
250      std::vector<pp::Var>());
251  ASSERT_TRUE(success);
252  ASSERT_TRUE(IsFormatAvailableMatches(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT,
253                                       false));
254
255  PASS();
256}
257
258std::string TestFlashClipboard::TestInvalidFormat() {
259  uint32_t invalid_format = 999;
260  ASSERT_FALSE(WriteStringVar(invalid_format, "text"));
261  ASSERT_TRUE(IsFormatAvailableMatches(invalid_format, false));
262  std::string unused;
263  ASSERT_FALSE(ReadStringVar(invalid_format, &unused));
264
265  PASS();
266}
267
268std::string TestFlashClipboard::TestRegisterCustomFormat() {
269  // Test an empty name is rejected.
270  uint32_t format_id =
271      pp::flash::Clipboard::RegisterCustomFormat(instance_, std::string());
272  ASSERT_EQ(static_cast<uint32_t>(PP_FLASH_CLIPBOARD_FORMAT_INVALID),
273            format_id);
274
275  // Test a valid format name.
276  format_id = pp::flash::Clipboard::RegisterCustomFormat(instance_, "a-b");
277  ASSERT_NE(static_cast<uint32_t>(PP_FLASH_CLIPBOARD_FORMAT_INVALID),
278            format_id);
279  // Make sure the format doesn't collide with predefined formats.
280  ASSERT_NE(static_cast<uint32_t>(PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT),
281            format_id);
282  ASSERT_NE(static_cast<uint32_t>(PP_FLASH_CLIPBOARD_FORMAT_HTML),
283            format_id);
284  ASSERT_NE(static_cast<uint32_t>(PP_FLASH_CLIPBOARD_FORMAT_RTF),
285            format_id);
286
287  // Check that if the same name is registered, the same id comes out.
288  uint32_t format_id2 =
289      pp::flash::Clipboard::RegisterCustomFormat(instance_, "a-b");
290  ASSERT_EQ(format_id, format_id2);
291
292  // Check that the second format registered has a different id.
293  uint32_t format_id3 =
294      pp::flash::Clipboard::RegisterCustomFormat(instance_, "a-b-c");
295  ASSERT_NE(format_id, format_id3);
296
297  PASS();
298}
299
300std::string TestFlashClipboard::TestGetSequenceNumber() {
301  uint64_t sequence_number_before = 0;
302  uint64_t sequence_number_after = 0;
303  ASSERT_TRUE(pp::flash::Clipboard::GetSequenceNumber(
304      instance_, PP_FLASH_CLIPBOARD_TYPE_STANDARD, &sequence_number_before));
305
306  // Test the sequence number changes after writing html.
307  ASSERT_TRUE(WriteStringVar(PP_FLASH_CLIPBOARD_FORMAT_HTML, "<html>"));
308  sequence_number_after = GetSequenceNumber(sequence_number_before);
309  ASSERT_NE(sequence_number_before, sequence_number_after);
310  sequence_number_before = sequence_number_after;
311
312  // Test the sequence number changes after writing some custom data.
313  std::string custom_data = "custom_data";
314  pp::VarArrayBuffer array_buffer(custom_data.size());
315  char* bytes = static_cast<char*>(array_buffer.Map());
316  std::copy(custom_data.begin(), custom_data.end(), bytes);
317  uint32_t format_id =
318      pp::flash::Clipboard::RegisterCustomFormat(instance_, "my-format");
319  std::vector<uint32_t> formats_vector(1, format_id);
320  std::vector<pp::Var> data_vector(1, array_buffer);
321  ASSERT_TRUE(pp::flash::Clipboard::WriteData(instance_,
322                                              PP_FLASH_CLIPBOARD_TYPE_STANDARD,
323                                              formats_vector,
324                                              data_vector));
325  sequence_number_after = GetSequenceNumber(sequence_number_before);
326  ASSERT_NE(sequence_number_before, sequence_number_after);
327  sequence_number_before = sequence_number_after;
328
329  // Read the data and make sure the sequence number doesn't change.
330  pp::Var custom_data_result;
331  ASSERT_TRUE(pp::flash::Clipboard::ReadData(
332      instance_,
333      PP_FLASH_CLIPBOARD_TYPE_STANDARD,
334      format_id,
335      &custom_data_result));
336  ASSERT_TRUE(pp::flash::Clipboard::GetSequenceNumber(
337      instance_, PP_FLASH_CLIPBOARD_TYPE_STANDARD, &sequence_number_after));
338  ASSERT_EQ(sequence_number_before, sequence_number_after);
339  sequence_number_before = sequence_number_after;
340
341  // Clear the clipboard and check the sequence number changes.
342  pp::flash::Clipboard::WriteData(instance_,
343                                  PP_FLASH_CLIPBOARD_TYPE_STANDARD,
344                                  std::vector<uint32_t>(),
345                                  std::vector<pp::Var>());
346  sequence_number_after = GetSequenceNumber(sequence_number_before);
347  ASSERT_NE(sequence_number_before, sequence_number_after);
348
349  PASS();
350}
351