blob: c285880a76f5ac58528b7ab4c3ca4ae1a30bcb3d [file] [log] [blame]
/*
* Copyright (c) 2008, 2009, 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/clipboard/data_object.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_drag_data.h"
#include "third_party/blink/renderer/core/clipboard/clipboard_mime_types.h"
#include "third_party/blink/renderer/core/clipboard/clipboard_utilities.h"
#include "third_party/blink/renderer/core/clipboard/dragged_isolated_file_system.h"
#include "third_party/blink/renderer/core/clipboard/paste_mode.h"
#include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
#include "third_party/blink/renderer/platform/file_metadata.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
namespace blink {
// static
DataObject* DataObject::CreateFromClipboard(SystemClipboard* system_clipboard,
PasteMode paste_mode) {
DataObject* data_object = Create();
#if DCHECK_IS_ON()
HashSet<String> types_seen;
#endif
uint64_t sequence_number = system_clipboard->SequenceNumber();
for (const String& type : system_clipboard->ReadAvailableTypes()) {
if (paste_mode == PasteMode::kPlainTextOnly && type != kMimeTypeTextPlain)
continue;
data_object->item_list_.push_back(DataObjectItem::CreateFromClipboard(
system_clipboard, type, sequence_number));
#if DCHECK_IS_ON()
DCHECK(types_seen.insert(type).is_new_entry);
#endif
}
return data_object;
}
// static
DataObject* DataObject::CreateFromString(const String& data) {
DataObject* data_object = Create();
data_object->Add(data, kMimeTypeTextPlain);
return data_object;
}
// static
DataObject* DataObject::Create() {
return MakeGarbageCollected<DataObject>();
}
DataObject::~DataObject() = default;
uint32_t DataObject::length() const {
return item_list_.size();
}
DataObjectItem* DataObject::Item(uint32_t index) {
if (index >= length())
return nullptr;
return item_list_[index];
}
void DataObject::DeleteItem(uint32_t index) {
if (index >= length())
return;
item_list_.EraseAt(index);
NotifyItemListChanged();
}
void DataObject::ClearAll() {
if (item_list_.IsEmpty())
return;
item_list_.clear();
NotifyItemListChanged();
}
DataObjectItem* DataObject::Add(const String& data, const String& type) {
DataObjectItem* item = DataObjectItem::CreateFromString(type, data);
if (!InternalAddStringItem(item))
return nullptr;
return item;
}
DataObjectItem* DataObject::Add(File* file) {
if (!file)
return nullptr;
DataObjectItem* item = DataObjectItem::CreateFromFile(file);
InternalAddFileItem(item);
return item;
}
DataObjectItem* DataObject::Add(File* file, const String& file_system_id) {
if (!file)
return nullptr;
DataObjectItem* item =
DataObjectItem::CreateFromFileWithFileSystemId(file, file_system_id);
InternalAddFileItem(item);
return item;
}
void DataObject::ClearData(const String& type) {
for (wtf_size_t i = 0; i < item_list_.size(); ++i) {
if (item_list_[i]->Kind() == DataObjectItem::kStringKind &&
item_list_[i]->GetType() == type) {
// Per the spec, type must be unique among all items of kind 'string'.
item_list_.EraseAt(i);
NotifyItemListChanged();
return;
}
}
}
Vector<String> DataObject::Types() const {
Vector<String> results;
#if DCHECK_IS_ON()
HashSet<String> types_seen;
#endif
bool contains_files = false;
for (const auto& item : item_list_) {
switch (item->Kind()) {
case DataObjectItem::kStringKind:
// Per the spec, type must be unique among all items of kind 'string'.
results.push_back(item->GetType());
#if DCHECK_IS_ON()
DCHECK(types_seen.insert(item->GetType()).is_new_entry);
#endif
break;
case DataObjectItem::kFileKind:
contains_files = true;
break;
}
}
if (contains_files) {
results.push_back(kMimeTypeFiles);
#if DCHECK_IS_ON()
DCHECK(types_seen.insert(kMimeTypeFiles).is_new_entry);
#endif
}
return results;
}
String DataObject::GetData(const String& type) const {
for (const auto& item : item_list_) {
if (item->Kind() == DataObjectItem::kStringKind && item->GetType() == type)
return item->GetAsString();
}
return String();
}
void DataObject::SetData(const String& type, const String& data) {
ClearData(type);
if (!Add(data, type))
NOTREACHED();
}
void DataObject::UrlAndTitle(String& url, String* title) const {
DataObjectItem* item = FindStringItem(kMimeTypeTextURIList);
if (!item)
return;
url = ConvertURIListToURL(item->GetAsString());
if (title)
*title = item->Title();
}
void DataObject::SetURLAndTitle(const String& url, const String& title) {
ClearData(kMimeTypeTextURIList);
InternalAddStringItem(DataObjectItem::CreateFromURL(url, title));
}
void DataObject::HtmlAndBaseURL(String& html, KURL& base_url) const {
DataObjectItem* item = FindStringItem(kMimeTypeTextHTML);
if (!item)
return;
html = item->GetAsString();
base_url = item->BaseURL();
}
void DataObject::SetHTMLAndBaseURL(const String& html, const KURL& base_url) {
ClearData(kMimeTypeTextHTML);
InternalAddStringItem(DataObjectItem::CreateFromHTML(html, base_url));
}
bool DataObject::ContainsFilenames() const {
for (const auto& item : item_list_) {
if (item->IsFilename())
return true;
}
return false;
}
Vector<String> DataObject::Filenames() const {
Vector<String> results;
for (const auto& item : item_list_) {
if (item->IsFilename())
results.push_back(item->GetAsFile()->GetPath());
}
return results;
}
void DataObject::AddFilename(const String& filename,
const String& display_name,
const String& file_system_id) {
InternalAddFileItem(DataObjectItem::CreateFromFileWithFileSystemId(
File::CreateForUserProvidedFile(filename, display_name), file_system_id));
}
void DataObject::AddSharedBuffer(scoped_refptr<SharedBuffer> buffer,
const KURL& source_url,
const String& filename_extension,
const AtomicString& content_disposition) {
InternalAddFileItem(DataObjectItem::CreateFromSharedBuffer(
std::move(buffer), source_url, filename_extension, content_disposition));
}
DataObject::DataObject() : modifiers_(0) {}
DataObjectItem* DataObject::FindStringItem(const String& type) const {
for (const auto& item : item_list_) {
if (item->Kind() == DataObjectItem::kStringKind && item->GetType() == type)
return item;
}
return nullptr;
}
bool DataObject::InternalAddStringItem(DataObjectItem* new_item) {
DCHECK_EQ(new_item->Kind(), DataObjectItem::kStringKind);
for (const auto& item : item_list_) {
if (item->Kind() == DataObjectItem::kStringKind &&
item->GetType() == new_item->GetType())
return false;
}
item_list_.push_back(new_item);
NotifyItemListChanged();
return true;
}
void DataObject::InternalAddFileItem(DataObjectItem* new_item) {
DCHECK_EQ(new_item->Kind(), DataObjectItem::kFileKind);
item_list_.push_back(new_item);
NotifyItemListChanged();
}
void DataObject::AddObserver(Observer* observer) {
DCHECK(!observers_.Contains(observer));
observers_.insert(observer);
}
void DataObject::NotifyItemListChanged() const {
for (const Member<Observer>& observer : observers_)
observer->OnItemListChanged();
}
void DataObject::Trace(Visitor* visitor) {
visitor->Trace(item_list_);
visitor->Trace(observers_);
Supplementable<DataObject>::Trace(visitor);
}
// static
DataObject* DataObject::Create(WebDragData data) {
DataObject* data_object = Create();
bool has_file_system = false;
for (const WebDragData::Item& item : data.Items()) {
switch (item.storage_type) {
case WebDragData::Item::kStorageTypeString:
if (String(item.string_type) == kMimeTypeTextURIList)
data_object->SetURLAndTitle(item.string_data, item.title);
else if (String(item.string_type) == kMimeTypeTextHTML)
data_object->SetHTMLAndBaseURL(item.string_data, item.base_url);
else
data_object->SetData(item.string_type, item.string_data);
break;
case WebDragData::Item::kStorageTypeFilename:
has_file_system = true;
data_object->AddFilename(item.filename_data, item.display_name_data,
data.FilesystemId());
break;
case WebDragData::Item::kStorageTypeBinaryData:
// This should never happen when dragging in.
break;
case WebDragData::Item::kStorageTypeFileSystemFile: {
// TODO(http://crbug.com/429077): The file system URL may refer a user
// visible file.
has_file_system = true;
FileMetadata file_metadata;
file_metadata.length = item.file_system_file_size;
data_object->Add(
File::CreateForFileSystemFile(item.file_system_url, file_metadata,
File::kIsNotUserVisible),
item.file_system_id);
} break;
}
}
data_object->SetFilesystemId(data.FilesystemId());
if (has_file_system)
DraggedIsolatedFileSystem::PrepareForDataObject(data_object);
return data_object;
}
WebDragData DataObject::ToWebDragData() {
WebDragData data;
data.Initialize();
data.SetModifierKeyState(modifiers_);
WebVector<WebDragData::Item> item_list(length());
for (wtf_size_t i = 0; i < length(); ++i) {
DataObjectItem* original_item = Item(i);
WebDragData::Item item;
if (original_item->Kind() == DataObjectItem::kStringKind) {
item.storage_type = WebDragData::Item::kStorageTypeString;
item.string_type = original_item->GetType();
item.string_data = original_item->GetAsString();
item.title = original_item->Title();
item.base_url = original_item->BaseURL();
} else if (original_item->Kind() == DataObjectItem::kFileKind) {
if (original_item->GetSharedBuffer()) {
item.storage_type = WebDragData::Item::kStorageTypeBinaryData;
item.binary_data = original_item->GetSharedBuffer();
item.binary_data_source_url = original_item->BaseURL();
item.binary_data_filename_extension =
original_item->FilenameExtension();
item.binary_data_content_disposition = original_item->Title();
} else if (original_item->IsFilename()) {
Blob* blob = original_item->GetAsFile();
if (auto* file = DynamicTo<File>(blob)) {
if (file->HasBackingFile()) {
item.storage_type = WebDragData::Item::kStorageTypeFilename;
item.filename_data = file->GetPath();
item.display_name_data = file->name();
} else if (!file->FileSystemURL().IsEmpty()) {
item.storage_type = WebDragData::Item::kStorageTypeFileSystemFile;
item.file_system_url = file->FileSystemURL();
item.file_system_file_size = file->size();
item.file_system_id = original_item->FileSystemId();
} else {
// TODO(http://crbug.com/394955): support dragging constructed Files
// across renderers.
item.storage_type = WebDragData::Item::kStorageTypeString;
item.string_type = "text/plain";
item.string_data = file->name();
}
} else {
NOTREACHED();
}
} else {
NOTREACHED();
}
} else {
NOTREACHED();
}
item_list[i] = item;
}
data.SwapItems(item_list);
return data;
}
} // namespace blink