Home | History | Annotate | Download | only in dragdrop
      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 "base/memory/ref_counted.h"
      6 #include "base/memory/scoped_ptr.h"
      7 #include "base/strings/utf_string_conversions.h"
      8 #include "base/win/scoped_hglobal.h"
      9 #include "testing/gtest/include/gtest/gtest.h"
     10 #include "ui/base/clipboard/clipboard.h"
     11 #include "ui/base/dragdrop/os_exchange_data.h"
     12 #include "ui/base/dragdrop/os_exchange_data_provider_win.h"
     13 #include "url/gurl.h"
     14 
     15 namespace ui {
     16 
     17 // Test getting using the IDataObject COM API
     18 TEST(OSExchangeDataWinTest, StringDataAccessViaCOM) {
     19   OSExchangeData data;
     20   std::wstring input = L"O hai googlz.";
     21   data.SetString(input);
     22   base::win::ScopedComPtr<IDataObject> com_data(
     23       OSExchangeDataProviderWin::GetIDataObject(data));
     24 
     25   FORMATETC format_etc =
     26       { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
     27   EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc));
     28 
     29   STGMEDIUM medium;
     30   EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium));
     31   std::wstring output =
     32       base::win::ScopedHGlobal<wchar_t*>(medium.hGlobal).get();
     33   EXPECT_EQ(input, output);
     34   ReleaseStgMedium(&medium);
     35 }
     36 
     37 // Test setting using the IDataObject COM API
     38 TEST(OSExchangeDataWinTest, StringDataWritingViaCOM) {
     39   OSExchangeData data;
     40   std::wstring input = L"http://www.google.com/";
     41 
     42   base::win::ScopedComPtr<IDataObject> com_data(
     43       OSExchangeDataProviderWin::GetIDataObject(data));
     44 
     45   // Store data in the object using the COM SetData API.
     46   CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL);
     47   FORMATETC format_etc =
     48       { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
     49   STGMEDIUM medium;
     50   medium.tymed = TYMED_HGLOBAL;
     51   HGLOBAL glob = GlobalAlloc(GPTR, sizeof(wchar_t) * (input.size() + 1));
     52   size_t stringsz = input.size();
     53   SIZE_T sz = GlobalSize(glob);
     54   base::win::ScopedHGlobal<wchar_t*> global_lock(glob);
     55   wchar_t* buffer_handle = global_lock.get();
     56   wcscpy_s(buffer_handle, input.size() + 1, input.c_str());
     57   medium.hGlobal = glob;
     58   medium.pUnkForRelease = NULL;
     59   EXPECT_EQ(S_OK, com_data->SetData(&format_etc, &medium, TRUE));
     60 
     61   // Construct a new object with the old object so that we can use our access
     62   // APIs.
     63   OSExchangeData data2(data.provider().Clone());
     64   EXPECT_TRUE(data2.HasURL(OSExchangeData::CONVERT_FILENAMES));
     65   GURL url_from_data;
     66   std::wstring title;
     67   EXPECT_TRUE(data2.GetURLAndTitle(
     68       OSExchangeData::CONVERT_FILENAMES, &url_from_data, &title));
     69   GURL reference_url(input);
     70   EXPECT_EQ(reference_url.spec(), url_from_data.spec());
     71 }
     72 
     73 // Verifies SetData invoked twice with the same data clobbers existing data.
     74 TEST(OSExchangeDataWinTest, RemoveData) {
     75   OSExchangeData data;
     76   std::wstring input = L"http://www.google.com/";
     77   std::wstring input2 = L"http://www.google2.com/";
     78 
     79   base::win::ScopedComPtr<IDataObject> com_data(
     80       OSExchangeDataProviderWin::GetIDataObject(data));
     81 
     82   // Store data in the object using the COM SetData API.
     83   CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL);
     84   FORMATETC format_etc =
     85       { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
     86   STGMEDIUM medium;
     87   medium.tymed = TYMED_HGLOBAL;
     88   {
     89     HGLOBAL glob = GlobalAlloc(GPTR, sizeof(wchar_t) * (input.size() + 1));
     90     size_t stringsz = input.size();
     91     SIZE_T sz = GlobalSize(glob);
     92     base::win::ScopedHGlobal<wchar_t*> global_lock(glob);
     93     wchar_t* buffer_handle = global_lock.get();
     94     wcscpy_s(buffer_handle, input.size() + 1, input.c_str());
     95     medium.hGlobal = glob;
     96     medium.pUnkForRelease = NULL;
     97     EXPECT_EQ(S_OK, com_data->SetData(&format_etc, &medium, TRUE));
     98   }
     99   // This should clobber the existing data.
    100   {
    101     HGLOBAL glob = GlobalAlloc(GPTR, sizeof(wchar_t) * (input2.size() + 1));
    102     size_t stringsz = input2.size();
    103     SIZE_T sz = GlobalSize(glob);
    104     base::win::ScopedHGlobal<wchar_t*> global_lock(glob);
    105     wchar_t* buffer_handle = global_lock.get();
    106     wcscpy_s(buffer_handle, input2.size() + 1, input2.c_str());
    107     medium.hGlobal = glob;
    108     medium.pUnkForRelease = NULL;
    109     EXPECT_EQ(S_OK, com_data->SetData(&format_etc, &medium, TRUE));
    110   }
    111   EXPECT_EQ(1u, static_cast<DataObjectImpl*>(com_data.get())->size());
    112 
    113   // Construct a new object with the old object so that we can use our access
    114   // APIs.
    115   OSExchangeData data2(data.provider().Clone());
    116   EXPECT_TRUE(data2.HasURL(OSExchangeData::CONVERT_FILENAMES));
    117   GURL url_from_data;
    118   std::wstring title;
    119   EXPECT_TRUE(data2.GetURLAndTitle(
    120       OSExchangeData::CONVERT_FILENAMES, &url_from_data, &title));
    121   EXPECT_EQ(GURL(input2).spec(), url_from_data.spec());
    122 }
    123 
    124 TEST(OSExchangeDataWinTest, URLDataAccessViaCOM) {
    125   OSExchangeData data;
    126   GURL url("http://www.google.com/");
    127   data.SetURL(url, L"");
    128   base::win::ScopedComPtr<IDataObject> com_data(
    129       OSExchangeDataProviderWin::GetIDataObject(data));
    130 
    131   CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL);
    132   FORMATETC format_etc =
    133       { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    134   EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc));
    135 
    136   STGMEDIUM medium;
    137   EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium));
    138   std::wstring output =
    139       base::win::ScopedHGlobal<wchar_t*>(medium.hGlobal).get();
    140   EXPECT_EQ(url.spec(), base::WideToUTF8(output));
    141   ReleaseStgMedium(&medium);
    142 }
    143 
    144 TEST(OSExchangeDataWinTest, MultipleFormatsViaCOM) {
    145   OSExchangeData data;
    146   std::string url_spec = "http://www.google.com/";
    147   GURL url(url_spec);
    148   std::wstring text = L"O hai googlz.";
    149   data.SetURL(url, L"Google");
    150   data.SetString(text);
    151 
    152   base::win::ScopedComPtr<IDataObject> com_data(
    153       OSExchangeDataProviderWin::GetIDataObject(data));
    154 
    155   CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL);
    156   FORMATETC url_format_etc =
    157       { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    158   EXPECT_EQ(S_OK, com_data->QueryGetData(&url_format_etc));
    159   FORMATETC text_format_etc =
    160       { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    161   EXPECT_EQ(S_OK, com_data->QueryGetData(&text_format_etc));
    162 
    163   STGMEDIUM medium;
    164   EXPECT_EQ(S_OK, com_data->GetData(&url_format_etc, &medium));
    165   std::wstring output_url =
    166       base::win::ScopedHGlobal<wchar_t*>(medium.hGlobal).get();
    167   EXPECT_EQ(url.spec(), base::WideToUTF8(output_url));
    168   ReleaseStgMedium(&medium);
    169 
    170   // The text is supposed to be the raw text of the URL, _NOT_ the value of
    171   // |text|! This is because the URL is added first and thus takes precedence!
    172   EXPECT_EQ(S_OK, com_data->GetData(&text_format_etc, &medium));
    173   std::wstring output_text =
    174       base::win::ScopedHGlobal<wchar_t*>(medium.hGlobal).get();
    175   EXPECT_EQ(url_spec, base::WideToUTF8(output_text));
    176   ReleaseStgMedium(&medium);
    177 }
    178 
    179 TEST(OSExchangeDataWinTest, EnumerationViaCOM) {
    180   OSExchangeData data;
    181   data.SetURL(GURL("http://www.google.com/"), L"");
    182   data.SetString(L"O hai googlz.");
    183 
    184   CLIPFORMAT cfstr_file_group_descriptor =
    185       RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
    186   CLIPFORMAT text_x_moz_url = RegisterClipboardFormat(L"text/x-moz-url");
    187 
    188   base::win::ScopedComPtr<IDataObject> com_data(
    189       OSExchangeDataProviderWin::GetIDataObject(data));
    190   base::win::ScopedComPtr<IEnumFORMATETC> enumerator;
    191   EXPECT_EQ(S_OK, com_data.get()->EnumFormatEtc(DATADIR_GET,
    192                                                 enumerator.Receive()));
    193 
    194   // Test that we can get one item.
    195   {
    196     // Explictly don't reset the first time, to verify the creation state is
    197     // OK.
    198     ULONG retrieved = 0;
    199     FORMATETC elements_array[1];
    200     EXPECT_EQ(S_OK, enumerator->Next(1,
    201         reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
    202     EXPECT_EQ(1, retrieved);
    203     EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat);
    204   }
    205 
    206   // Test that we can get one item with a NULL retrieved value.
    207   {
    208     EXPECT_EQ(S_OK, enumerator->Reset());
    209     FORMATETC elements_array[1];
    210     EXPECT_EQ(S_OK, enumerator->Next(1,
    211         reinterpret_cast<FORMATETC*>(&elements_array), NULL));
    212     EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat);
    213   }
    214 
    215   // Test that we can get two items.
    216   {
    217     EXPECT_EQ(S_OK, enumerator->Reset());
    218     ULONG retrieved = 0;
    219     FORMATETC elements_array[2];
    220     EXPECT_EQ(S_OK, enumerator->Next(2,
    221         reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
    222     EXPECT_EQ(2, retrieved);
    223     EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat);
    224     EXPECT_EQ(cfstr_file_group_descriptor, elements_array[1].cfFormat);
    225   }
    226 
    227   // Test that we can skip the first item.
    228   {
    229     EXPECT_EQ(S_OK, enumerator->Reset());
    230     EXPECT_EQ(S_OK, enumerator->Skip(1));
    231     ULONG retrieved = 0;
    232     FORMATETC elements_array[1];
    233     EXPECT_EQ(S_OK, enumerator->Next(1,
    234         reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
    235     EXPECT_EQ(1, retrieved);
    236     EXPECT_EQ(cfstr_file_group_descriptor, elements_array[0].cfFormat);
    237   }
    238 
    239   // Test that we can skip the first item, and create a clone that matches in
    240   // this state, and modify the original without affecting the clone.
    241   {
    242     EXPECT_EQ(S_OK, enumerator->Reset());
    243     EXPECT_EQ(S_OK, enumerator->Skip(1));
    244     base::win::ScopedComPtr<IEnumFORMATETC> cloned_enumerator;
    245     EXPECT_EQ(S_OK, enumerator.get()->Clone(cloned_enumerator.Receive()));
    246     EXPECT_EQ(S_OK, enumerator.get()->Reset());
    247 
    248     {
    249       ULONG retrieved = 0;
    250       FORMATETC elements_array[1];
    251       EXPECT_EQ(S_OK, cloned_enumerator->Next(1,
    252           reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
    253       EXPECT_EQ(1, retrieved);
    254       EXPECT_EQ(cfstr_file_group_descriptor, elements_array[0].cfFormat);
    255     }
    256 
    257     {
    258       ULONG retrieved = 0;
    259       FORMATETC elements_array[1];
    260       EXPECT_EQ(S_OK, enumerator->Next(1,
    261           reinterpret_cast<FORMATETC*>(&elements_array), &retrieved));
    262       EXPECT_EQ(1, retrieved);
    263       EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat);
    264     }
    265   }
    266 }
    267 
    268 TEST(OSExchangeDataWinTest, TestURLExchangeFormatsViaCOM) {
    269   OSExchangeData data;
    270   std::string url_spec = "http://www.google.com/";
    271   GURL url(url_spec);
    272   std::wstring url_title = L"www.google.com";
    273   data.SetURL(url, url_title);
    274 
    275   // File contents access via COM
    276   base::win::ScopedComPtr<IDataObject> com_data(
    277       OSExchangeDataProviderWin::GetIDataObject(data));
    278   {
    279     CLIPFORMAT cfstr_file_contents =
    280         RegisterClipboardFormat(CFSTR_FILECONTENTS);
    281     FORMATETC format_etc =
    282         { cfstr_file_contents, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
    283     EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc));
    284 
    285     STGMEDIUM medium;
    286     EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium));
    287     base::win::ScopedHGlobal<char*> glob(medium.hGlobal);
    288     std::string output(glob.get(), glob.Size());
    289     std::string file_contents = "[InternetShortcut]\r\nURL=";
    290     file_contents += url_spec;
    291     file_contents += "\r\n";
    292     EXPECT_EQ(file_contents, output);
    293     ReleaseStgMedium(&medium);
    294   }
    295 }
    296 
    297 TEST(OSExchangeDataWinTest, FileContents) {
    298   OSExchangeData data;
    299   std::string file_contents("data\0with\0nulls", 15);
    300   data.SetFileContents(base::FilePath(L"filename.txt"), file_contents);
    301 
    302   OSExchangeData copy(data.provider().Clone());
    303   base::FilePath filename;
    304   std::string read_contents;
    305   EXPECT_TRUE(copy.GetFileContents(&filename, &read_contents));
    306   EXPECT_EQ(L"filename.txt", filename.value());
    307   EXPECT_EQ(file_contents, read_contents);
    308 }
    309 
    310 TEST(OSExchangeDataWinTest, CFHtml) {
    311   OSExchangeData data;
    312   GURL url("http://www.google.com/");
    313   std::wstring html(
    314       L"<HTML>\n<BODY>\n"
    315       L"<b>bold.</b> <i><b>This is bold italic.</b></i>\n"
    316       L"</BODY>\n</HTML>");
    317   data.SetHtml(html, url);
    318 
    319   // Check the CF_HTML too.
    320   std::string expected_cf_html(
    321       "Version:0.9\r\nStartHTML:0000000139\r\nEndHTML:0000000288\r\n"
    322       "StartFragment:0000000175\r\nEndFragment:0000000252\r\n"
    323       "SourceURL:http://www.google.com/\r\n<html>\r\n<body>\r\n"
    324       "<!--StartFragment-->");
    325   expected_cf_html += base::WideToUTF8(html);
    326   expected_cf_html.append("<!--EndFragment-->\r\n</body>\r\n</html>");
    327 
    328   FORMATETC format = Clipboard::GetHtmlFormatType().ToFormatEtc();
    329   STGMEDIUM medium;
    330   IDataObject* data_object = OSExchangeDataProviderWin::GetIDataObject(data);
    331   EXPECT_EQ(S_OK, data_object->GetData(&format, &medium));
    332   base::win::ScopedHGlobal<char*> glob(medium.hGlobal);
    333   std::string output(glob.get(), glob.Size());
    334   EXPECT_EQ(expected_cf_html, output);
    335   ReleaseStgMedium(&medium);
    336 }
    337 
    338 TEST(OSExchangeDataWinTest, SetURLWithMaxPath) {
    339   OSExchangeData data;
    340   std::wstring long_title(L'a', MAX_PATH + 1);
    341   data.SetURL(GURL("http://google.com"), long_title);
    342 }
    343 
    344 TEST(OSExchangeDataWinTest, ProvideURLForPlainTextURL) {
    345   OSExchangeData data;
    346   data.SetString(L"http://google.com");
    347 
    348   OSExchangeData data2(data.provider().Clone());
    349   ASSERT_TRUE(data2.HasURL(OSExchangeData::CONVERT_FILENAMES));
    350   GURL read_url;
    351   std::wstring title;
    352   EXPECT_TRUE(data2.GetURLAndTitle(
    353       OSExchangeData::CONVERT_FILENAMES, &read_url, &title));
    354   EXPECT_EQ(GURL("http://google.com"), read_url);
    355 }
    356 
    357 }  // namespace ui
    358