diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/third_party/blink/renderer/core/fileapi | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) | |
download | qtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/fileapi')
33 files changed, 3950 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/core/fileapi/BUILD.gn b/chromium/third_party/blink/renderer/core/fileapi/BUILD.gn new file mode 100644 index 00000000000..d6b3ff11937 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/BUILD.gn @@ -0,0 +1,51 @@ +# Copyright 2016 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. + +import("//third_party/blink/renderer/core/core.gni") + +blink_core_sources("fileapi") { + sources = [ + "blob.cc", + "blob.h", + "file.cc", + "file.h", + "file_error.cc", + "file_error.h", + "file_list.cc", + "file_list.h", + "file_reader.cc", + "file_reader.h", + "file_reader_loader.cc", + "file_reader_loader.h", + "file_reader_loader_client.h", + "file_reader_sync.cc", + "file_reader_sync.h", + "public_url_manager.cc", + "public_url_manager.h", + "url_file_api.cc", + "url_file_api.h", + "url_registry.h", + ] +} + +jumbo_source_set("unit_tests") { + testonly = true + sources = [ + "file_list_test.cc", + "file_test.cc", + "public_url_manager_test.cc", + ] + + configs += [ + "//third_party/blink/renderer/core:blink_core_pch", + "//third_party/blink/renderer:config", + "//third_party/blink/renderer:inside_blink", + ] + + deps = [ + "//testing/gmock", + "//testing/gtest", + "//third_party/blink/renderer/core", + ] +} diff --git a/chromium/third_party/blink/renderer/core/fileapi/OWNERS b/chromium/third_party/blink/renderer/core/fileapi/OWNERS new file mode 100644 index 00000000000..75048f422da --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/OWNERS @@ -0,0 +1,6 @@ +jsbell@chromium.org +mek@chromium.org +pwnall@chromium.org + +# TEAM: storage-dev@chromium.org +# COMPONENT: Blink>FileAPI diff --git a/chromium/third_party/blink/renderer/core/fileapi/blob.cc b/chromium/third_party/blink/renderer/core/fileapi/blob.cc new file mode 100644 index 00000000000..5839473349f --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/blob.cc @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2010 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/fileapi/blob.h" + +#include <memory> +#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" +#include "third_party/blink/renderer/core/dom/exception_code.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/fileapi/blob_property_bag.h" +#include "third_party/blink/renderer/core/frame/use_counter.h" +#include "third_party/blink/renderer/core/url/dom_url.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/blob/blob_registry.h" +#include "third_party/blink/renderer/platform/blob/blob_url.h" + +namespace blink { + +class BlobURLRegistry final : public URLRegistry { + public: + // SecurityOrigin is passed together with KURL so that the registry can + // save it for entries from whose KURL the origin is not recoverable by + // using BlobURL::getOrigin(). + void RegisterURL(SecurityOrigin*, const KURL&, URLRegistrable*) override; + void UnregisterURL(const KURL&) override; + + static URLRegistry& Registry(); +}; + +void BlobURLRegistry::RegisterURL(SecurityOrigin* origin, + const KURL& public_url, + URLRegistrable* registrable_object) { + DCHECK_EQ(®istrable_object->Registry(), this); + Blob* blob = static_cast<Blob*>(registrable_object); + BlobRegistry::RegisterPublicBlobURL(origin, public_url, + blob->GetBlobDataHandle()); +} + +void BlobURLRegistry::UnregisterURL(const KURL& public_url) { + BlobRegistry::RevokePublicBlobURL(public_url); +} + +URLRegistry& BlobURLRegistry::Registry() { + // This is called on multiple threads. + // (This code assumes it is safe to register or unregister URLs on + // BlobURLRegistry (that is implemented by the embedder) on + // multiple threads.) + DEFINE_THREAD_SAFE_STATIC_LOCAL(BlobURLRegistry, instance, ()); + return instance; +} + +Blob::Blob(scoped_refptr<BlobDataHandle> data_handle) + : blob_data_handle_(std::move(data_handle)) {} + +Blob::~Blob() = default; + +// static +Blob* Blob::Create( + ExecutionContext* context, + const HeapVector<ArrayBufferOrArrayBufferViewOrBlobOrUSVString>& blob_parts, + const BlobPropertyBag& options, + ExceptionState& exception_state) { + DCHECK(options.hasType()); + + DCHECK(options.hasEndings()); + bool normalize_line_endings_to_native = options.endings() == "native"; + if (normalize_line_endings_to_native) + UseCounter::Count(context, WebFeature::kFileAPINativeLineEndings); + + std::unique_ptr<BlobData> blob_data = BlobData::Create(); + blob_data->SetContentType(NormalizeType(options.type())); + + PopulateBlobData(blob_data.get(), blob_parts, + normalize_line_endings_to_native); + + long long blob_size = blob_data->length(); + return new Blob(BlobDataHandle::Create(std::move(blob_data), blob_size)); +} + +Blob* Blob::Create(const unsigned char* data, + size_t bytes, + const String& content_type) { + DCHECK(data); + + std::unique_ptr<BlobData> blob_data = BlobData::Create(); + blob_data->SetContentType(content_type); + blob_data->AppendBytes(data, bytes); + long long blob_size = blob_data->length(); + + return new Blob(BlobDataHandle::Create(std::move(blob_data), blob_size)); +} + +// static +void Blob::PopulateBlobData( + BlobData* blob_data, + const HeapVector<ArrayBufferOrArrayBufferViewOrBlobOrUSVString>& parts, + bool normalize_line_endings_to_native) { + for (const auto& item : parts) { + if (item.IsArrayBuffer()) { + DOMArrayBuffer* array_buffer = item.GetAsArrayBuffer(); + blob_data->AppendBytes(array_buffer->Data(), array_buffer->ByteLength()); + } else if (item.IsArrayBufferView()) { + DOMArrayBufferView* array_buffer_view = + item.GetAsArrayBufferView().View(); + blob_data->AppendBytes(array_buffer_view->BaseAddress(), + array_buffer_view->byteLength()); + } else if (item.IsBlob()) { + item.GetAsBlob()->AppendTo(*blob_data); + } else if (item.IsUSVString()) { + blob_data->AppendText(item.GetAsUSVString(), + normalize_line_endings_to_native); + } else { + NOTREACHED(); + } + } +} + +// static +void Blob::ClampSliceOffsets(long long size, long long& start, long long& end) { + DCHECK_NE(size, -1); + + // Convert the negative value that is used to select from the end. + if (start < 0) + start = start + size; + if (end < 0) + end = end + size; + + // Clamp the range if it exceeds the size limit. + if (start < 0) + start = 0; + if (end < 0) + end = 0; + if (start >= size) { + start = 0; + end = 0; + } else if (end < start) { + end = start; + } else if (end > size) { + end = size; + } +} + +Blob* Blob::slice(long long start, + long long end, + const String& content_type, + ExceptionState& exception_state) const { + long long size = this->size(); + ClampSliceOffsets(size, start, end); + + long long length = end - start; + std::unique_ptr<BlobData> blob_data = BlobData::Create(); + blob_data->SetContentType(NormalizeType(content_type)); + blob_data->AppendBlob(blob_data_handle_, start, length); + return Blob::Create(BlobDataHandle::Create(std::move(blob_data), length)); +} + +void Blob::AppendTo(BlobData& blob_data) const { + blob_data.AppendBlob(blob_data_handle_, 0, blob_data_handle_->size()); +} + +URLRegistry& Blob::Registry() const { + return BlobURLRegistry::Registry(); +} + +mojom::blink::BlobPtr Blob::AsMojoBlob() { + return blob_data_handle_->CloneBlobPtr(); +} + +// static +String Blob::NormalizeType(const String& type) { + if (type.IsNull()) + return g_empty_string; + const size_t length = type.length(); + if (type.Is8Bit()) { + const LChar* chars = type.Characters8(); + for (size_t i = 0; i < length; ++i) { + if (chars[i] < 0x20 || chars[i] > 0x7e) + return g_empty_string; + } + } else { + const UChar* chars = type.Characters16(); + for (size_t i = 0; i < length; ++i) { + if (chars[i] < 0x0020 || chars[i] > 0x007e) + return g_empty_string; + } + } + return type.DeprecatedLower(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/blob.h b/chromium/third_party/blink/renderer/core/fileapi/blob.h new file mode 100644 index 00000000000..2173f64c1f5 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/blob.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_BLOB_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_BLOB_H_ + +#include "base/memory/scoped_refptr.h" +#include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/fileapi/url_registry.h" +#include "third_party/blink/renderer/core/imagebitmap/image_bitmap_source.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/blob/blob_data.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +class BlobPropertyBag; +class ExceptionState; +class ExecutionContext; + +class CORE_EXPORT Blob : public ScriptWrappable, + public URLRegistrable, + public ImageBitmapSource { + DEFINE_WRAPPERTYPEINFO(); + + public: + static Blob* Create(ExecutionContext*, ExceptionState&) { + return new Blob(BlobDataHandle::Create()); + } + + static Blob* Create( + ExecutionContext*, + const HeapVector<ArrayBufferOrArrayBufferViewOrBlobOrUSVString>&, + const BlobPropertyBag&, + ExceptionState&); + + static Blob* Create(scoped_refptr<BlobDataHandle> blob_data_handle) { + return new Blob(std::move(blob_data_handle)); + } + + static Blob* Create(const unsigned char* data, + size_t bytes, + const String& content_type); + + ~Blob() override; + + virtual unsigned long long size() const { return blob_data_handle_->size(); } + virtual Blob* slice(long long start, + long long end, + const String& content_type, + ExceptionState&) const; + + // To allow ExceptionState to be passed in last, manually enumerate the + // optional argument overloads. + Blob* slice(ExceptionState& exception_state) const { + return slice(0, std::numeric_limits<long long>::max(), String(), + exception_state); + } + Blob* slice(long long start, ExceptionState& exception_state) const { + return slice(start, std::numeric_limits<long long>::max(), String(), + exception_state); + } + Blob* slice(long long start, + long long end, + ExceptionState& exception_state) const { + return slice(start, end, String(), exception_state); + } + + String type() const { return blob_data_handle_->GetType(); } + String Uuid() const { return blob_data_handle_->Uuid(); } + scoped_refptr<BlobDataHandle> GetBlobDataHandle() const { + return blob_data_handle_; + } + // True for all File instances, including the user-built ones. + virtual bool IsFile() const { return false; } + // Only true for File instances that are backed by platform files. + virtual bool HasBackingFile() const { return false; } + + // Used by the JavaScript Blob and File constructors. + virtual void AppendTo(BlobData&) const; + + // URLRegistrable to support PublicURLs. + URLRegistry& Registry() const final; + mojom::blink::BlobPtr AsMojoBlob() final; + + // ImageBitmapSource implementation + bool IsBlob() const override { return true; } + + protected: + explicit Blob(scoped_refptr<BlobDataHandle>); + + static void PopulateBlobData( + BlobData*, + const HeapVector<ArrayBufferOrArrayBufferViewOrBlobOrUSVString>& parts, + bool normalize_line_endings_to_native); + static void ClampSliceOffsets(long long size, + long long& start, + long long& end); + + // Called by the Blob and File constructors when processing the 'type' + // option per the FileAPI standard. Returns "" if |type| contains any + // character outside U+0020...U+007E, or |type| ASCII-lowercased otherwise. + static String NormalizeType(const String& type); + + private: + Blob() = delete; + + scoped_refptr<BlobDataHandle> blob_data_handle_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_BLOB_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/blob.idl b/chromium/third_party/blink/renderer/core/fileapi/blob.idl new file mode 100644 index 00000000000..fa0d2500180 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/blob.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 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. + */ + +// https://w3c.github.io/FileAPI/#blob + +typedef (ArrayBuffer or ArrayBufferView or Blob or USVString) BlobPart; +[ + Constructor(optional sequence<BlobPart> blobParts, optional BlobPropertyBag options), + ConstructorCallWith=ExecutionContext, + RaisesException=Constructor, + Exposed=(Window,Worker) +] interface Blob { + readonly attribute unsigned long long size; + readonly attribute DOMString type; + + // TODO(jsbell): start and end arguments should be [Clamp] + [RaisesException] Blob slice(optional long long start, optional long long end, optional DOMString contentType); +}; diff --git a/chromium/third_party/blink/renderer/core/fileapi/blob_property_bag.idl b/chromium/third_party/blink/renderer/core/fileapi/blob_property_bag.idl new file mode 100644 index 00000000000..009bbd1e83c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/blob_property_bag.idl @@ -0,0 +1,12 @@ +// Copyright 2015 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. + +// https://w3c.github.io/FileAPI/#dfn-BlobPropertyBag + +enum EndingType { "transparent", "native" }; + +dictionary BlobPropertyBag { + DOMString type = ""; + EndingType endings = "transparent"; +}; diff --git a/chromium/third_party/blink/renderer/core/fileapi/file.cc b/chromium/third_party/blink/renderer/core/fileapi/file.cc new file mode 100644 index 00000000000..d028cd0c66c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file.cc @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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/fileapi/file.h" + +#include <memory> +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/platform/web_file_utilities.h" +#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" +#include "third_party/blink/renderer/core/dom/exception_code.h" +#include "third_party/blink/renderer/core/fileapi/file_property_bag.h" +#include "third_party/blink/renderer/core/frame/use_counter.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/blob/blob_data.h" +#include "third_party/blink/renderer/platform/file_metadata.h" +#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" +#include "third_party/blink/renderer/platform/wtf/date_math.h" +#include "third_party/blink/renderer/platform/wtf/time.h" + +namespace blink { + +static String GetContentTypeFromFileName(const String& name, + File::ContentTypeLookupPolicy policy) { + String type; + int index = name.ReverseFind('.'); + if (index != -1) { + if (policy == File::kWellKnownContentTypes) { + type = MIMETypeRegistry::GetWellKnownMIMETypeForExtension( + name.Substring(index + 1)); + } else { + DCHECK_EQ(policy, File::kAllContentTypes); + type = + MIMETypeRegistry::GetMIMETypeForExtension(name.Substring(index + 1)); + } + } + return type; +} + +static std::unique_ptr<BlobData> CreateBlobDataForFileWithType( + const String& path, + const String& content_type) { + std::unique_ptr<BlobData> blob_data = + BlobData::CreateForFileWithUnknownSize(path); + blob_data->SetContentType(content_type); + return blob_data; +} + +static std::unique_ptr<BlobData> CreateBlobDataForFile( + const String& path, + File::ContentTypeLookupPolicy policy) { + return CreateBlobDataForFileWithType( + path, GetContentTypeFromFileName(path, policy)); +} + +static std::unique_ptr<BlobData> CreateBlobDataForFileWithName( + const String& path, + const String& file_system_name, + File::ContentTypeLookupPolicy policy) { + return CreateBlobDataForFileWithType( + path, GetContentTypeFromFileName(file_system_name, policy)); +} + +static std::unique_ptr<BlobData> CreateBlobDataForFileWithMetadata( + const String& file_system_name, + const FileMetadata& metadata) { + std::unique_ptr<BlobData> blob_data; + if (metadata.length == BlobData::kToEndOfFile) { + blob_data = BlobData::CreateForFileWithUnknownSize( + metadata.platform_path, metadata.modification_time / kMsPerSecond); + } else { + blob_data = BlobData::Create(); + blob_data->AppendFile(metadata.platform_path, 0, metadata.length, + metadata.modification_time / kMsPerSecond); + } + blob_data->SetContentType(GetContentTypeFromFileName( + file_system_name, File::kWellKnownContentTypes)); + return blob_data; +} + +static std::unique_ptr<BlobData> CreateBlobDataForFileSystemURL( + const KURL& file_system_url, + const FileMetadata& metadata) { + std::unique_ptr<BlobData> blob_data; + if (metadata.length == BlobData::kToEndOfFile) { + blob_data = BlobData::CreateForFileSystemURLWithUnknownSize( + file_system_url, metadata.modification_time / kMsPerSecond); + } else { + blob_data = BlobData::Create(); + blob_data->AppendFileSystemURL(file_system_url, 0, metadata.length, + metadata.modification_time / kMsPerSecond); + } + blob_data->SetContentType(GetContentTypeFromFileName( + file_system_url.GetPath(), File::kWellKnownContentTypes)); + return blob_data; +} + +// static +File* File::Create( + ExecutionContext* context, + const HeapVector<ArrayBufferOrArrayBufferViewOrBlobOrUSVString>& file_bits, + const String& file_name, + const FilePropertyBag& options, + ExceptionState& exception_state) { + DCHECK(options.hasType()); + + double last_modified; + if (options.hasLastModified()) + last_modified = static_cast<double>(options.lastModified()); + else + last_modified = CurrentTimeMS(); + DCHECK(options.hasEndings()); + bool normalize_line_endings_to_native = options.endings() == "native"; + if (normalize_line_endings_to_native) + UseCounter::Count(context, WebFeature::kFileAPINativeLineEndings); + + std::unique_ptr<BlobData> blob_data = BlobData::Create(); + blob_data->SetContentType(NormalizeType(options.type())); + PopulateBlobData(blob_data.get(), file_bits, + normalize_line_endings_to_native); + + long long file_size = blob_data->length(); + return File::Create(file_name, last_modified, + BlobDataHandle::Create(std::move(blob_data), file_size)); +} + +File* File::CreateWithRelativePath(const String& path, + const String& relative_path) { + File* file = new File(path, File::kAllContentTypes, File::kIsUserVisible); + file->relative_path_ = relative_path; + return file; +} + +File::File(const String& path, + ContentTypeLookupPolicy policy, + UserVisibility user_visibility) + : Blob(BlobDataHandle::Create(CreateBlobDataForFile(path, policy), -1)), + has_backing_file_(true), + user_visibility_(user_visibility), + path_(path), + name_(Platform::Current()->GetFileUtilities()->BaseName(path)), + snapshot_size_(-1), + snapshot_modification_time_ms_(InvalidFileTime()) {} + +File::File(const String& path, + const String& name, + ContentTypeLookupPolicy policy, + UserVisibility user_visibility) + : Blob(BlobDataHandle::Create( + CreateBlobDataForFileWithName(path, name, policy), + -1)), + has_backing_file_(true), + user_visibility_(user_visibility), + path_(path), + name_(name), + snapshot_size_(-1), + snapshot_modification_time_ms_(InvalidFileTime()) {} + +File::File(const String& path, + const String& name, + const String& relative_path, + UserVisibility user_visibility, + bool has_snapshot_data, + uint64_t size, + double last_modified, + scoped_refptr<BlobDataHandle> blob_data_handle) + : Blob(std::move(blob_data_handle)), + has_backing_file_(!path.IsEmpty() || !relative_path.IsEmpty()), + user_visibility_(user_visibility), + path_(path), + name_(name), + snapshot_size_(has_snapshot_data ? static_cast<long long>(size) : -1), + snapshot_modification_time_ms_(has_snapshot_data ? last_modified + : InvalidFileTime()), + relative_path_(relative_path) {} + +File::File(const String& name, + double modification_time_ms, + scoped_refptr<BlobDataHandle> blob_data_handle) + : Blob(std::move(blob_data_handle)), + has_backing_file_(false), + user_visibility_(File::kIsNotUserVisible), + name_(name), + snapshot_size_(Blob::size()), + snapshot_modification_time_ms_(modification_time_ms) {} + +File::File(const String& name, + const FileMetadata& metadata, + UserVisibility user_visibility) + : Blob(BlobDataHandle::Create( + CreateBlobDataForFileWithMetadata(name, metadata), + metadata.length)), + has_backing_file_(true), + user_visibility_(user_visibility), + path_(metadata.platform_path), + name_(name), + snapshot_size_(metadata.length), + snapshot_modification_time_ms_(metadata.modification_time) {} + +File::File(const KURL& file_system_url, + const FileMetadata& metadata, + UserVisibility user_visibility) + : Blob(BlobDataHandle::Create( + CreateBlobDataForFileSystemURL(file_system_url, metadata), + metadata.length)), + has_backing_file_(false), + user_visibility_(user_visibility), + name_(DecodeURLEscapeSequences(file_system_url.LastPathComponent())), + file_system_url_(file_system_url), + snapshot_size_(metadata.length), + snapshot_modification_time_ms_(metadata.modification_time) {} + +File::File(const File& other) + : Blob(other.GetBlobDataHandle()), + has_backing_file_(other.has_backing_file_), + user_visibility_(other.user_visibility_), + path_(other.path_), + name_(other.name_), + file_system_url_(other.file_system_url_), + snapshot_size_(other.snapshot_size_), + snapshot_modification_time_ms_(other.snapshot_modification_time_ms_), + relative_path_(other.relative_path_) {} + +File* File::Clone(const String& name) const { + File* file = new File(*this); + if (!name.IsNull()) + file->name_ = name; + return file; +} + +double File::LastModifiedMS() const { + if (HasValidSnapshotMetadata() && + IsValidFileTime(snapshot_modification_time_ms_)) + return snapshot_modification_time_ms_; + + double modification_time_ms; + if (HasBackingFile() && + GetFileModificationTime(path_, modification_time_ms) && + IsValidFileTime(modification_time_ms)) + return modification_time_ms; + + return CurrentTimeMS(); +} + +long long File::lastModified() const { + double modified_date = LastModifiedMS(); + + // The getter should return the current time when the last modification time + // isn't known. + if (!IsValidFileTime(modified_date)) + modified_date = CurrentTimeMS(); + + // lastModified returns a number, not a Date instance, + // http://dev.w3.org/2006/webapi/FileAPI/#file-attrs + return floor(modified_date); +} + +double File::lastModifiedDate() const { + double modified_date = LastModifiedMS(); + + // The getter should return the current time when the last modification time + // isn't known. + if (!IsValidFileTime(modified_date)) + modified_date = CurrentTimeMS(); + + // lastModifiedDate returns a Date instance, + // http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate + return modified_date; +} + +unsigned long long File::size() const { + if (HasValidSnapshotMetadata()) + return snapshot_size_; + + // FIXME: JavaScript cannot represent sizes as large as unsigned long long, we + // need to come up with an exception to throw if file size is not + // representable. + long long size; + if (!HasBackingFile() || !GetFileSize(path_, size)) + return 0; + return static_cast<unsigned long long>(size); +} + +Blob* File::slice(long long start, + long long end, + const String& content_type, + ExceptionState& exception_state) const { + if (!has_backing_file_) + return Blob::slice(start, end, content_type, exception_state); + + // FIXME: This involves synchronous file operation. We need to figure out how + // to make it asynchronous. + long long size; + double modification_time_ms; + CaptureSnapshot(size, modification_time_ms); + ClampSliceOffsets(size, start, end); + + long long length = end - start; + std::unique_ptr<BlobData> blob_data = BlobData::Create(); + blob_data->SetContentType(NormalizeType(content_type)); + DCHECK(!path_.IsEmpty()); + blob_data->AppendFile(path_, start, length, + modification_time_ms / kMsPerSecond); + return Blob::Create(BlobDataHandle::Create(std::move(blob_data), length)); +} + +void File::CaptureSnapshot(long long& snapshot_size, + double& snapshot_modification_time_ms) const { + if (HasValidSnapshotMetadata()) { + snapshot_size = snapshot_size_; + snapshot_modification_time_ms = snapshot_modification_time_ms_; + return; + } + + // Obtains a snapshot of the file by capturing its current size and + // modification time. This is used when we slice a file for the first time. + // If we fail to retrieve the size or modification time, probably due to that + // the file has been deleted, 0 size is returned. + FileMetadata metadata; + if (!HasBackingFile() || !GetFileMetadata(path_, metadata)) { + snapshot_size = 0; + snapshot_modification_time_ms = InvalidFileTime(); + return; + } + + snapshot_size = metadata.length; + snapshot_modification_time_ms = metadata.modification_time; +} + +void File::AppendTo(BlobData& blob_data) const { + if (!has_backing_file_) { + Blob::AppendTo(blob_data); + return; + } + + // FIXME: This involves synchronous file operation. We need to figure out how + // to make it asynchronous. + long long size; + double modification_time_ms; + CaptureSnapshot(size, modification_time_ms); + DCHECK(!path_.IsEmpty()); + blob_data.AppendFile(path_, 0, size, modification_time_ms / kMsPerSecond); +} + +bool File::HasSameSource(const File& other) const { + if (has_backing_file_ != other.has_backing_file_) + return false; + + if (has_backing_file_) + return path_ == other.path_; + + if (file_system_url_.IsEmpty() != other.file_system_url_.IsEmpty()) + return false; + + if (!file_system_url_.IsEmpty()) + return file_system_url_ == other.file_system_url_; + + return Uuid() == other.Uuid(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/file.h b/chromium/third_party/blink/renderer/core/fileapi/file.h new file mode 100644 index 00000000000..39831f1902c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_H_ + +#include "base/memory/scoped_refptr.h" +#include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/fileapi/blob.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +class ExceptionState; +class ExecutionContext; +class FilePropertyBag; +class FileMetadata; +class KURL; + +class CORE_EXPORT File final : public Blob { + DEFINE_WRAPPERTYPEINFO(); + + public: + // AllContentTypes should only be used when the full path/name are trusted; + // otherwise, it could allow arbitrary pages to determine what applications an + // user has installed. + enum ContentTypeLookupPolicy { + kWellKnownContentTypes, + kAllContentTypes, + }; + + // The user should not be able to browse to some files, such as the ones + // generated by the Filesystem API. + enum UserVisibility { kIsUserVisible, kIsNotUserVisible }; + + // Constructor in File.idl + static File* Create( + ExecutionContext*, + const HeapVector<ArrayBufferOrArrayBufferViewOrBlobOrUSVString>&, + const String& file_name, + const FilePropertyBag&, + ExceptionState&); + + static File* Create(const String& path, + ContentTypeLookupPolicy policy = kWellKnownContentTypes) { + return new File(path, policy, File::kIsUserVisible); + } + + static File* Create(const String& name, + double modification_time, + scoped_refptr<BlobDataHandle> blob_data_handle) { + return new File(name, modification_time, std::move(blob_data_handle)); + } + + // For deserialization. + static File* CreateFromSerialization( + const String& path, + const String& name, + const String& relative_path, + UserVisibility user_visibility, + bool has_snapshot_data, + uint64_t size, + double last_modified, + scoped_refptr<BlobDataHandle> blob_data_handle) { + return new File(path, name, relative_path, user_visibility, + has_snapshot_data, size, last_modified, + std::move(blob_data_handle)); + } + static File* CreateFromIndexedSerialization( + const String& path, + const String& name, + uint64_t size, + double last_modified, + scoped_refptr<BlobDataHandle> blob_data_handle) { + return new File(path, name, String(), kIsNotUserVisible, true, size, + last_modified, std::move(blob_data_handle)); + } + + static File* CreateWithRelativePath(const String& path, + const String& relative_path); + + // If filesystem files live in the remote filesystem, the port might pass the + // valid metadata (whose length field is non-negative) and cache in the File + // object. + // + // Otherwise calling size(), lastModifiedTime() and slice() will synchronously + // query the file metadata. + static File* CreateForFileSystemFile(const String& name, + const FileMetadata& metadata, + UserVisibility user_visibility) { + return new File(name, metadata, user_visibility); + } + + static File* CreateForFileSystemFile(const KURL& url, + const FileMetadata& metadata, + UserVisibility user_visibility) { + return new File(url, metadata, user_visibility); + } + + KURL FileSystemURL() const { +#if DCHECK_IS_ON() + DCHECK(HasValidFileSystemURL()); +#endif + return file_system_url_; + } + + // Create a file with a name exposed to the author (via File.name and + // associated DOM properties) that differs from the one provided in the path. + static File* CreateForUserProvidedFile(const String& path, + const String& display_name) { + if (display_name.IsEmpty()) + return new File(path, File::kAllContentTypes, File::kIsUserVisible); + return new File(path, display_name, File::kAllContentTypes, + File::kIsUserVisible); + } + + static File* CreateForFileSystemFile( + const String& path, + const String& name, + ContentTypeLookupPolicy policy = kWellKnownContentTypes) { + if (name.IsEmpty()) + return new File(path, policy, File::kIsNotUserVisible); + return new File(path, name, policy, File::kIsNotUserVisible); + } + + File* Clone(const String& name = String()) const; + + unsigned long long size() const override; + Blob* slice(long long start, + long long end, + const String& content_type, + ExceptionState&) const override; + + bool IsFile() const override { return true; } + bool HasBackingFile() const override { return has_backing_file_; } + + void AppendTo(BlobData&) const override; + + const String& GetPath() const { +#if DCHECK_IS_ON() + DCHECK(HasValidFilePath()); +#endif + return path_; + } + const String& name() const { return name_; } + + // Getter for the lastModified IDL attribute, + // http://dev.w3.org/2006/webapi/FileAPI/#file-attrs + long long lastModified() const; + + // Getter for the lastModifiedDate IDL attribute, + // http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate + double lastModifiedDate() const; + + UserVisibility GetUserVisibility() const { return user_visibility_; } + + // Returns the relative path of this file in the context of a directory + // selection. + const String& webkitRelativePath() const { return relative_path_; } + + // Note that this involves synchronous file operation. Think twice before + // calling this function. + void CaptureSnapshot(long long& snapshot_size, + double& snapshot_modification_time_ms) const; + + // Returns true if this has a valid snapshot metadata + // (i.e. m_snapshotSize >= 0). + bool HasValidSnapshotMetadata() const { return snapshot_size_ >= 0; } + + // Returns true if the sources (file path, file system URL, or blob handler) + // of the file objects are same or not. + bool HasSameSource(const File& other) const; + + private: + File(const String& path, ContentTypeLookupPolicy, UserVisibility); + File(const String& path, + const String& name, + ContentTypeLookupPolicy, + UserVisibility); + File(const String& path, + const String& name, + const String& relative_path, + UserVisibility, + bool has_snapshot_data, + uint64_t size, + double last_modified, + scoped_refptr<BlobDataHandle>); + File(const String& name, + double modification_time, + scoped_refptr<BlobDataHandle>); + File(const String& name, const FileMetadata&, UserVisibility); + File(const KURL& file_system_url, const FileMetadata&, UserVisibility); + File(const File&); + + void InvalidateSnapshotMetadata() { snapshot_size_ = -1; } + + // Returns File's last modified time (in MS since Epoch.) + // If the modification time isn't known, the current time is returned. + double LastModifiedMS() const; + +#if DCHECK_IS_ON() + // Instances backed by a file must have an empty file system URL. + bool HasValidFileSystemURL() const { + return !HasBackingFile() || file_system_url_.IsEmpty(); + } + // Instances not backed by a file must have an empty path set. + bool HasValidFilePath() const { return HasBackingFile() || path_.IsEmpty(); } +#endif + + bool has_backing_file_; + UserVisibility user_visibility_; + String path_; + String name_; + + KURL file_system_url_; + + // If m_snapshotSize is negative (initialized to -1 by default), the snapshot + // metadata is invalid and we retrieve the latest metadata synchronously in + // size(), lastModifiedTime() and slice(). + // Otherwise, the snapshot metadata are used directly in those methods. + long long snapshot_size_; + const double snapshot_modification_time_ms_; + + String relative_path_; +}; + +DEFINE_TYPE_CASTS(File, Blob, blob, blob->IsFile(), blob.IsFile()); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/file.idl b/chromium/third_party/blink/renderer/core/fileapi/file.idl new file mode 100644 index 00000000000..a74beceda3e --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// https://w3c.github.io/FileAPI/#file + +[ + Constructor(sequence<BlobPart> fileBits, USVString fileName, optional FilePropertyBag options), + ConstructorCallWith=ExecutionContext, + RaisesException=Constructor, + Exposed=(Window,Worker) +] interface File : Blob { + readonly attribute DOMString name; + readonly attribute long long lastModified; + + // Non-standard APIs + [MeasureAs=FileGetLastModifiedDate] readonly attribute Date lastModifiedDate; + [MeasureAs=PrefixedFileRelativePath] readonly attribute DOMString webkitRelativePath; +}; diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_error.cc b/chromium/third_party/blink/renderer/core/fileapi/file_error.cc new file mode 100644 index 00000000000..1dd5f239998 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_error.cc @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2013 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/fileapi/file_error.h" + +#include "third_party/blink/public/platform/web_file_error.h" +#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" +#include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/dom/exception_code.h" +#include "third_party/blink/renderer/platform/wtf/assertions.h" + +namespace blink { + +namespace FileError { + +const char kAbortErrorMessage[] = + "An ongoing operation was aborted, typically with a call to abort()."; +const char kEncodingErrorMessage[] = + "A URI supplied to the API was malformed, or the resulting Data URL has " + "exceeded the URL length limitations for Data URLs."; +const char kInvalidStateErrorMessage[] = + "An operation that depends on state cached in an interface object was made " + "but the state had changed since it was read from disk."; +const char kNoModificationAllowedErrorMessage[] = + "An attempt was made to write to a file or directory which could not be " + "modified due to the state of the underlying filesystem."; +const char kNotFoundErrorMessage[] = + "A requested file or directory could not be found at the time an operation " + "was processed."; +const char kNotReadableErrorMessage[] = + "The requested file could not be read, typically due to permission " + "problems that have occurred after a reference to a file was acquired."; +const char kPathExistsErrorMessage[] = + "An attempt was made to create a file or directory where an element " + "already exists."; +const char kQuotaExceededErrorMessage[] = + "The operation failed because it would cause the application to exceed its " + "storage quota."; +const char kSecurityErrorMessage[] = + "It was determined that certain files are unsafe for access within a Web " + "application, or that too many calls are being made on file resources."; +const char kSyntaxErrorMessage[] = + "An invalid or unsupported argument was given, like an invalid line ending " + "specifier."; +const char kTypeMismatchErrorMessage[] = + "The path supplied exists, but was not an entry of requested type."; + +namespace { + +ExceptionCode ErrorCodeToExceptionCode(ErrorCode code) { + switch (code) { + case kOK: + return 0; + case kNotFoundErr: + return kNotFoundError; + case kSecurityErr: + return kSecurityError; + case kAbortErr: + return kAbortError; + case kNotReadableErr: + return kNotReadableError; + case kEncodingErr: + return kEncodingError; + case kNoModificationAllowedErr: + return kNoModificationAllowedError; + case kInvalidStateErr: + return kInvalidStateError; + case kSyntaxErr: + return kSyntaxError; + case kInvalidModificationErr: + return kInvalidModificationError; + case kQuotaExceededErr: + return kQuotaExceededError; + case kTypeMismatchErr: + return kTypeMismatchError; + case kPathExistsErr: + return kPathExistsError; + default: + NOTREACHED(); + return code; + } +} + +const char* ErrorCodeToMessage(ErrorCode code) { + // Note that some of these do not set message. If message is 0 then the + // default message is used. + switch (code) { + case kOK: + return nullptr; + case kSecurityErr: + return kSecurityErrorMessage; + case kNotFoundErr: + return kNotFoundErrorMessage; + case kAbortErr: + return kAbortErrorMessage; + case kNotReadableErr: + return kNotReadableErrorMessage; + case kEncodingErr: + return kEncodingErrorMessage; + case kNoModificationAllowedErr: + return kNoModificationAllowedErrorMessage; + case kInvalidStateErr: + return kInvalidStateErrorMessage; + case kSyntaxErr: + return kSyntaxErrorMessage; + case kInvalidModificationErr: + return nullptr; + case kQuotaExceededErr: + return kQuotaExceededErrorMessage; + case kTypeMismatchErr: + return nullptr; + case kPathExistsErr: + return kPathExistsErrorMessage; + default: + NOTREACHED(); + return nullptr; + } +} + +} // namespace + +void ThrowDOMException(ExceptionState& exception_state, ErrorCode code) { + if (code == kOK) + return; + + // SecurityError is special-cased, as we want to route those exceptions + // through ExceptionState::throwSecurityError. + if (code == kSecurityErr) { + exception_state.ThrowSecurityError(kSecurityErrorMessage); + return; + } + + exception_state.ThrowDOMException(ErrorCodeToExceptionCode(code), + ErrorCodeToMessage(code)); +} + +DOMException* CreateDOMException(ErrorCode code) { + DCHECK_NE(code, kOK); + return DOMException::Create(ErrorCodeToExceptionCode(code), + ErrorCodeToMessage(code)); +} + +STATIC_ASSERT_ENUM(kWebFileErrorNotFound, kNotFoundErr); +STATIC_ASSERT_ENUM(kWebFileErrorSecurity, kSecurityErr); +STATIC_ASSERT_ENUM(kWebFileErrorAbort, kAbortErr); +STATIC_ASSERT_ENUM(kWebFileErrorNotReadable, kNotReadableErr); +STATIC_ASSERT_ENUM(kWebFileErrorEncoding, kEncodingErr); +STATIC_ASSERT_ENUM(kWebFileErrorNoModificationAllowed, + kNoModificationAllowedErr); +STATIC_ASSERT_ENUM(kWebFileErrorInvalidState, kInvalidStateErr); +STATIC_ASSERT_ENUM(kWebFileErrorSyntax, kSyntaxErr); +STATIC_ASSERT_ENUM(kWebFileErrorInvalidModification, kInvalidModificationErr); +STATIC_ASSERT_ENUM(kWebFileErrorQuotaExceeded, kQuotaExceededErr); +STATIC_ASSERT_ENUM(kWebFileErrorTypeMismatch, kTypeMismatchErr); +STATIC_ASSERT_ENUM(kWebFileErrorPathExists, kPathExistsErr); + +} // namespace FileError + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_error.h b/chromium/third_party/blink/renderer/core/fileapi/file_error.h new file mode 100644 index 00000000000..55f11174804 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_error.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_ERROR_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_ERROR_H_ + +#include "third_party/blink/renderer/core/core_export.h" + +namespace blink { + +class DOMException; +class ExceptionState; + +namespace FileError { + +enum ErrorCode { + kOK = 0, + kNotFoundErr = 1, + kSecurityErr = 2, + kAbortErr = 3, + kNotReadableErr = 4, + kEncodingErr = 5, + kNoModificationAllowedErr = 6, + kInvalidStateErr = 7, + kSyntaxErr = 8, + kInvalidModificationErr = 9, + kQuotaExceededErr = 10, + kTypeMismatchErr = 11, + kPathExistsErr = 12, +}; + +CORE_EXPORT extern const char kAbortErrorMessage[]; +CORE_EXPORT extern const char kEncodingErrorMessage[]; +CORE_EXPORT extern const char kInvalidStateErrorMessage[]; +CORE_EXPORT extern const char kNoModificationAllowedErrorMessage[]; +CORE_EXPORT extern const char kNotFoundErrorMessage[]; +CORE_EXPORT extern const char kNotReadableErrorMessage[]; +CORE_EXPORT extern const char kPathExistsErrorMessage[]; +CORE_EXPORT extern const char kQuotaExceededErrorMessage[]; +CORE_EXPORT extern const char kSecurityErrorMessage[]; +CORE_EXPORT extern const char kSyntaxErrorMessage[]; +CORE_EXPORT extern const char kTypeMismatchErrorMessage[]; + +CORE_EXPORT void ThrowDOMException(ExceptionState&, ErrorCode); +CORE_EXPORT DOMException* CreateDOMException(ErrorCode); + +} // namespace FileError + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_ERROR_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_list.cc b/chromium/third_party/blink/renderer/core/fileapi/file_list.cc new file mode 100644 index 00000000000..720371ecba0 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_list.cc @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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/fileapi/file_list.h" + +namespace blink { + +FileList::FileList() = default; + +File* FileList::item(unsigned index) const { + if (index >= files_.size()) + return nullptr; + return files_[index].Get(); +} + +Vector<String> FileList::PathsForUserVisibleFiles() const { + Vector<String> paths; + for (unsigned i = 0; i < files_.size(); ++i) { + if (files_[i]->GetUserVisibility() == File::kIsUserVisible) { + if (files_[i]->HasBackingFile()) + paths.push_back(files_[i]->GetPath()); + else + paths.push_back(files_[i]->name()); + } + } + + return paths; +} + +void FileList::Trace(blink::Visitor* visitor) { + visitor->Trace(files_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_list.h b/chromium/third_party/blink/renderer/core/fileapi/file_list.h new file mode 100644 index 00000000000..550a03406c3 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_list.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_LIST_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_LIST_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/fileapi/file.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +class CORE_EXPORT FileList final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + static FileList* Create() { return new FileList; } + + unsigned length() const { return files_.size(); } + File* item(unsigned index) const; + + bool IsEmpty() const { return files_.IsEmpty(); } + void clear() { files_.clear(); } + void Append(File* file) { files_.push_back(file); } + Vector<String> PathsForUserVisibleFiles() const; + + void Trace(blink::Visitor*); + + private: + FileList(); + + HeapVector<Member<File>> files_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_LIST_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_list.idl b/chromium/third_party/blink/renderer/core/fileapi/file_list.idl new file mode 100644 index 00000000000..25db9a0ba23 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_list.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// https://w3c.github.io/FileAPI/#filelist-section + +[ + Exposed=(Window,Worker) +] interface FileList { + getter File? item(unsigned long index); + readonly attribute unsigned long length; +}; diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_list_test.cc b/chromium/third_party/blink/renderer/core/fileapi/file_list_test.cc new file mode 100644 index 00000000000..ef6ffcadab4 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_list_test.cc @@ -0,0 +1,64 @@ +// Copyright 2014 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 "third_party/blink/renderer/core/fileapi/file_list.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { + +TEST(FileListTest, pathsForUserVisibleFiles) { + FileList* const file_list = FileList::Create(); + + // Native file. + file_list->Append(File::Create("/native/path")); + + // Blob file. + const scoped_refptr<BlobDataHandle> blob_data_handle = + BlobDataHandle::Create(); + file_list->Append(File::Create("name", 0.0, blob_data_handle)); + + // User visible snapshot file. + { + FileMetadata metadata; + metadata.platform_path = "/native/visible/snapshot"; + file_list->Append( + File::CreateForFileSystemFile("name", metadata, File::kIsUserVisible)); + } + + // Not user visible snapshot file. + { + FileMetadata metadata; + metadata.platform_path = "/native/not-visible/snapshot"; + file_list->Append(File::CreateForFileSystemFile("name", metadata, + File::kIsNotUserVisible)); + } + + // User visible file system URL file. + { + KURL url( + "filesystem:http://example.com/isolated/hash/visible-non-native-file"); + file_list->Append(File::CreateForFileSystemFile(url, FileMetadata(), + File::kIsUserVisible)); + } + + // Not user visible file system URL file. + { + KURL url( + "filesystem:http://example.com/isolated/hash/" + "not-visible-non-native-file"); + file_list->Append(File::CreateForFileSystemFile(url, FileMetadata(), + File::kIsNotUserVisible)); + } + + Vector<String> paths = file_list->PathsForUserVisibleFiles(); + + ASSERT_EQ(3u, paths.size()); + EXPECT_EQ("/native/path", paths[0]); + EXPECT_EQ("/native/visible/snapshot", paths[1]); + EXPECT_EQ("visible-non-native-file", paths[2]) + << "Files not backed by a native file should return name."; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_property_bag.idl b/chromium/third_party/blink/renderer/core/fileapi/file_property_bag.idl new file mode 100644 index 00000000000..692b13853ee --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_property_bag.idl @@ -0,0 +1,9 @@ +// Copyright 2015 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. + +// https://w3c.github.io/FileAPI/#file + +dictionary FilePropertyBag : BlobPropertyBag { + long long lastModified; +}; diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader.cc b/chromium/third_party/blink/renderer/core/fileapi/file_reader.cc new file mode 100644 index 00000000000..71d893dd055 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader.cc @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2010 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/fileapi/file_reader.h" + +#include "third_party/blink/public/platform/task_type.h" +#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" +#include "third_party/blink/renderer/bindings/core/v8/string_or_array_buffer.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/exception_code.h" +#include "third_party/blink/renderer/core/events/progress_event.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/fileapi/file.h" +#include "third_party/blink/renderer/core/frame/use_counter.h" +#include "third_party/blink/renderer/core/probe/core_probes.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" +#include "third_party/blink/renderer/platform/supplementable.h" +#include "third_party/blink/renderer/platform/wtf/auto_reset.h" +#include "third_party/blink/renderer/platform/wtf/deque.h" +#include "third_party/blink/renderer/platform/wtf/hash_set.h" +#include "third_party/blink/renderer/platform/wtf/text/cstring.h" +#include "third_party/blink/renderer/platform/wtf/time.h" + +namespace blink { + +namespace { + +const CString Utf8BlobUUID(Blob* blob) { + return blob->Uuid().Utf8(); +} + +const CString Utf8FilePath(Blob* blob) { + return blob->HasBackingFile() ? ToFile(blob)->GetPath().Utf8() : ""; +} + +} // namespace + +// Embedders like chromium limit the number of simultaneous requests to avoid +// excessive IPC congestion. We limit this to 100 per thread to throttle the +// requests (the value is arbitrarily chosen). +static const size_t kMaxOutstandingRequestsPerThread = 100; +static const double kProgressNotificationIntervalMS = 50; + +class FileReader::ThrottlingController final + : public GarbageCollected<FileReader::ThrottlingController>, + public Supplement<ExecutionContext> { + USING_GARBAGE_COLLECTED_MIXIN(FileReader::ThrottlingController); + + public: + static const char kSupplementName[]; + + static ThrottlingController* From(ExecutionContext* context) { + if (!context) + return nullptr; + + ThrottlingController* controller = + Supplement<ExecutionContext>::From<ThrottlingController>(*context); + if (!controller) { + controller = new ThrottlingController(*context); + ProvideTo(*context, controller); + } + return controller; + } + + enum FinishReaderType { kDoNotRunPendingReaders, kRunPendingReaders }; + + static void PushReader(ExecutionContext* context, FileReader* reader) { + ThrottlingController* controller = From(context); + if (!controller) + return; + + probe::AsyncTaskScheduled(context, "FileReader", reader); + controller->PushReader(reader); + } + + static FinishReaderType RemoveReader(ExecutionContext* context, + FileReader* reader) { + ThrottlingController* controller = From(context); + if (!controller) + return kDoNotRunPendingReaders; + + return controller->RemoveReader(reader); + } + + static void FinishReader(ExecutionContext* context, + FileReader* reader, + FinishReaderType next_step) { + ThrottlingController* controller = From(context); + if (!controller) + return; + + controller->FinishReader(reader, next_step); + probe::AsyncTaskCanceled(context, reader); + } + + void Trace(blink::Visitor* visitor) { + visitor->Trace(pending_readers_); + visitor->Trace(running_readers_); + Supplement<ExecutionContext>::Trace(visitor); + } + + private: + explicit ThrottlingController(ExecutionContext& context) + : Supplement<ExecutionContext>(context), + max_running_readers_(kMaxOutstandingRequestsPerThread) {} + + void PushReader(FileReader* reader) { + if (pending_readers_.IsEmpty() && + running_readers_.size() < max_running_readers_) { + reader->ExecutePendingRead(); + DCHECK(!running_readers_.Contains(reader)); + running_readers_.insert(reader); + return; + } + pending_readers_.push_back(reader); + ExecuteReaders(); + } + + FinishReaderType RemoveReader(FileReader* reader) { + FileReaderHashSet::const_iterator hash_iter = running_readers_.find(reader); + if (hash_iter != running_readers_.end()) { + running_readers_.erase(hash_iter); + return kRunPendingReaders; + } + FileReaderDeque::const_iterator deque_end = pending_readers_.end(); + for (FileReaderDeque::const_iterator it = pending_readers_.begin(); + it != deque_end; ++it) { + if (*it == reader) { + pending_readers_.erase(it); + break; + } + } + return kDoNotRunPendingReaders; + } + + void FinishReader(FileReader* reader, FinishReaderType next_step) { + if (next_step == kRunPendingReaders) + ExecuteReaders(); + } + + void ExecuteReaders() { + while (running_readers_.size() < max_running_readers_) { + if (pending_readers_.IsEmpty()) + return; + FileReader* reader = pending_readers_.TakeFirst(); + reader->ExecutePendingRead(); + running_readers_.insert(reader); + } + } + + const size_t max_running_readers_; + + using FileReaderDeque = HeapDeque<Member<FileReader>>; + using FileReaderHashSet = HeapHashSet<Member<FileReader>>; + + FileReaderDeque pending_readers_; + FileReaderHashSet running_readers_; +}; + +// static +const char FileReader::ThrottlingController::kSupplementName[] = + "FileReaderThrottlingController"; + +FileReader* FileReader::Create(ExecutionContext* context) { + return new FileReader(context); +} + +FileReader::FileReader(ExecutionContext* context) + : ContextLifecycleObserver(context), + state_(kEmpty), + loading_state_(kLoadingStateNone), + still_firing_events_(false), + read_type_(FileReaderLoader::kReadAsBinaryString), + last_progress_notification_time_ms_(0) {} + +FileReader::~FileReader() { + Terminate(); +} + +const AtomicString& FileReader::InterfaceName() const { + return EventTargetNames::FileReader; +} + +void FileReader::ContextDestroyed(ExecutionContext* destroyed_context) { + // The delayed abort task tidies up and advances to the DONE state. + if (loading_state_ == kLoadingStateAborted) + return; + + if (HasPendingActivity()) { + ThrottlingController::FinishReader( + destroyed_context, this, + ThrottlingController::RemoveReader(destroyed_context, this)); + } + Terminate(); +} + +bool FileReader::HasPendingActivity() const { + return state_ == kLoading || still_firing_events_; +} + +void FileReader::readAsArrayBuffer(Blob* blob, + ExceptionState& exception_state) { + DCHECK(blob); + DVLOG(1) << "reading as array buffer: " << Utf8BlobUUID(blob).data() << " " + << Utf8FilePath(blob).data(); + + ReadInternal(blob, FileReaderLoader::kReadAsArrayBuffer, exception_state); +} + +void FileReader::readAsBinaryString(Blob* blob, + ExceptionState& exception_state) { + DCHECK(blob); + DVLOG(1) << "reading as binary: " << Utf8BlobUUID(blob).data() << " " + << Utf8FilePath(blob).data(); + + ReadInternal(blob, FileReaderLoader::kReadAsBinaryString, exception_state); +} + +void FileReader::readAsText(Blob* blob, + const String& encoding, + ExceptionState& exception_state) { + DCHECK(blob); + DVLOG(1) << "reading as text: " << Utf8BlobUUID(blob).data() << " " + << Utf8FilePath(blob).data(); + + encoding_ = encoding; + ReadInternal(blob, FileReaderLoader::kReadAsText, exception_state); +} + +void FileReader::readAsText(Blob* blob, ExceptionState& exception_state) { + readAsText(blob, String(), exception_state); +} + +void FileReader::readAsDataURL(Blob* blob, ExceptionState& exception_state) { + DCHECK(blob); + DVLOG(1) << "reading as data URL: " << Utf8BlobUUID(blob).data() << " " + << Utf8FilePath(blob).data(); + + ReadInternal(blob, FileReaderLoader::kReadAsDataURL, exception_state); +} + +void FileReader::ReadInternal(Blob* blob, + FileReaderLoader::ReadType type, + ExceptionState& exception_state) { + // If multiple concurrent read methods are called on the same FileReader, + // InvalidStateError should be thrown when the state is kLoading. + if (state_ == kLoading) { + exception_state.ThrowDOMException( + kInvalidStateError, "The object is already busy reading Blobs."); + return; + } + + ExecutionContext* context = GetExecutionContext(); + if (!context) { + exception_state.ThrowDOMException( + kAbortError, "Reading from a detached FileReader is not supported."); + return; + } + + // A document loader will not load new resources once the Document has + // detached from its frame. + if (context->IsDocument() && !ToDocument(context)->GetFrame()) { + exception_state.ThrowDOMException( + kAbortError, + "Reading from a Document-detached FileReader is not supported."); + return; + } + + // "Snapshot" the Blob data rather than the Blob itself as ongoing + // read operations should not be affected if close() is called on + // the Blob being read. + blob_data_handle_ = blob->GetBlobDataHandle(); + blob_type_ = blob->type(); + read_type_ = type; + state_ = kLoading; + loading_state_ = kLoadingStatePending; + error_ = nullptr; + DCHECK(ThrottlingController::From(context)); + ThrottlingController::PushReader(context, this); +} + +void FileReader::ExecutePendingRead() { + DCHECK_EQ(loading_state_, kLoadingStatePending); + loading_state_ = kLoadingStateLoading; + + loader_ = FileReaderLoader::Create(read_type_, this); + loader_->SetEncoding(encoding_); + loader_->SetDataType(blob_type_); + loader_->Start(blob_data_handle_); + blob_data_handle_ = nullptr; +} + +void FileReader::abort() { + DVLOG(1) << "aborting"; + + if (loading_state_ != kLoadingStateLoading && + loading_state_ != kLoadingStatePending) { + return; + } + loading_state_ = kLoadingStateAborted; + + DCHECK_NE(kDone, state_); + state_ = kDone; + + AutoReset<bool> firing_events(&still_firing_events_, true); + + // Setting error implicitly makes |result| return null. + error_ = FileError::CreateDOMException(FileError::kAbortErr); + + // Unregister the reader. + ThrottlingController::FinishReaderType final_step = + ThrottlingController::RemoveReader(GetExecutionContext(), this); + + FireEvent(EventTypeNames::abort); + FireEvent(EventTypeNames::loadend); + + // All possible events have fired and we're done, no more pending activity. + ThrottlingController::FinishReader(GetExecutionContext(), this, final_step); + + // ..but perform the loader cancellation asynchronously as abort() could be + // called from the event handler and we do not want the resource loading code + // to be on the stack when doing so. The persistent reference keeps the + // reader alive until the task has completed. + GetExecutionContext() + ->GetTaskRunner(TaskType::kFileReading) + ->PostTask(FROM_HERE, + WTF::Bind(&FileReader::Terminate, WrapPersistent(this))); +} + +void FileReader::result(ScriptState* state, + StringOrArrayBuffer& result_attribute) const { + if (error_ || !loader_) + return; + + if (!loader_->HasFinishedLoading()) { + UseCounter::Count(ExecutionContext::From(state), + WebFeature::kFileReaderResultBeforeCompletion); + } + + if (read_type_ == FileReaderLoader::kReadAsArrayBuffer) + result_attribute.SetArrayBuffer(loader_->ArrayBufferResult()); + else + result_attribute.SetString(loader_->StringResult()); +} + +void FileReader::Terminate() { + if (loader_) { + loader_->Cancel(); + loader_ = nullptr; + } + state_ = kDone; + loading_state_ = kLoadingStateNone; +} + +void FileReader::DidStartLoading() { + AutoReset<bool> firing_events(&still_firing_events_, true); + FireEvent(EventTypeNames::loadstart); +} + +void FileReader::DidReceiveData() { + // Fire the progress event at least every 50ms. + double now = CurrentTimeMS(); + if (!last_progress_notification_time_ms_) { + last_progress_notification_time_ms_ = now; + } else if (now - last_progress_notification_time_ms_ > + kProgressNotificationIntervalMS) { + AutoReset<bool> firing_events(&still_firing_events_, true); + FireEvent(EventTypeNames::progress); + last_progress_notification_time_ms_ = now; + } +} + +void FileReader::DidFinishLoading() { + if (loading_state_ == kLoadingStateAborted) + return; + DCHECK_EQ(loading_state_, kLoadingStateLoading); + + // TODO(jochen): When we set m_state to DONE below, we still need to fire + // the load and loadend events. To avoid GC to collect this FileReader, we + // use this separate variable to keep the wrapper of this FileReader alive. + // An alternative would be to keep any ActiveScriptWrappables alive that is on + // the stack. + AutoReset<bool> firing_events(&still_firing_events_, true); + + // It's important that we change m_loadingState before firing any events + // since any of the events could call abort(), which internally checks + // if we're still loading (therefore we need abort process) or not. + loading_state_ = kLoadingStateNone; + + FireEvent(EventTypeNames::progress); + + DCHECK_NE(kDone, state_); + state_ = kDone; + + // Unregister the reader. + ThrottlingController::FinishReaderType final_step = + ThrottlingController::RemoveReader(GetExecutionContext(), this); + + FireEvent(EventTypeNames::load); + FireEvent(EventTypeNames::loadend); + + // All possible events have fired and we're done, no more pending activity. + ThrottlingController::FinishReader(GetExecutionContext(), this, final_step); +} + +void FileReader::DidFail(FileError::ErrorCode error_code) { + if (loading_state_ == kLoadingStateAborted) + return; + + AutoReset<bool> firing_events(&still_firing_events_, true); + + DCHECK_EQ(kLoadingStateLoading, loading_state_); + loading_state_ = kLoadingStateNone; + + DCHECK_NE(kDone, state_); + state_ = kDone; + + error_ = FileError::CreateDOMException(error_code); + + // Unregister the reader. + ThrottlingController::FinishReaderType final_step = + ThrottlingController::RemoveReader(GetExecutionContext(), this); + + FireEvent(EventTypeNames::error); + FireEvent(EventTypeNames::loadend); + + // All possible events have fired and we're done, no more pending activity. + ThrottlingController::FinishReader(GetExecutionContext(), this, final_step); +} + +void FileReader::FireEvent(const AtomicString& type) { + probe::AsyncTask async_task(GetExecutionContext(), this, "event"); + if (!loader_) { + DispatchEvent(ProgressEvent::Create(type, false, 0, 0)); + return; + } + + if (loader_->TotalBytes()) { + DispatchEvent(ProgressEvent::Create(type, true, loader_->BytesLoaded(), + *loader_->TotalBytes())); + } else { + DispatchEvent( + ProgressEvent::Create(type, false, loader_->BytesLoaded(), 0)); + } +} + +void FileReader::Trace(blink::Visitor* visitor) { + visitor->Trace(error_); + EventTargetWithInlineData::Trace(visitor); + ContextLifecycleObserver::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader.h b/chromium/third_party/blink/renderer/core/fileapi/file_reader.h new file mode 100644 index 00000000000..c891c4f514a --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_H_ + +#include <memory> +#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h" +#include "third_party/blink/renderer/core/dom/events/event_target.h" +#include "third_party/blink/renderer/core/fileapi/file_error.h" +#include "third_party/blink/renderer/core/fileapi/file_reader_loader.h" +#include "third_party/blink/renderer/core/fileapi/file_reader_loader_client.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class Blob; +class ExceptionState; +class ExecutionContext; +class StringOrArrayBuffer; + +class CORE_EXPORT FileReader final : public EventTargetWithInlineData, + public ActiveScriptWrappable<FileReader>, + public ContextLifecycleObserver, + public FileReaderLoaderClient { + DEFINE_WRAPPERTYPEINFO(); + USING_GARBAGE_COLLECTED_MIXIN(FileReader); + + public: + static FileReader* Create(ExecutionContext*); + + ~FileReader() override; + + enum ReadyState { kEmpty = 0, kLoading = 1, kDone = 2 }; + + void readAsArrayBuffer(Blob*, ExceptionState&); + void readAsBinaryString(Blob*, ExceptionState&); + void readAsText(Blob*, const String& encoding, ExceptionState&); + void readAsText(Blob*, ExceptionState&); + void readAsDataURL(Blob*, ExceptionState&); + void abort(); + + ReadyState getReadyState() const { return state_; } + DOMException* error() { return error_; } + void result(ScriptState*, StringOrArrayBuffer& result_attribute) const; + + // ContextLifecycleObserver + void ContextDestroyed(ExecutionContext*) override; + + // ScriptWrappable + bool HasPendingActivity() const final; + + // EventTarget + const AtomicString& InterfaceName() const override; + ExecutionContext* GetExecutionContext() const override { + return ContextLifecycleObserver::GetExecutionContext(); + } + + // FileReaderLoaderClient + void DidStartLoading() override; + void DidReceiveData() override; + void DidFinishLoading() override; + void DidFail(FileError::ErrorCode) override; + + DEFINE_ATTRIBUTE_EVENT_LISTENER(loadstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(progress); + DEFINE_ATTRIBUTE_EVENT_LISTENER(load); + DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); + DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + DEFINE_ATTRIBUTE_EVENT_LISTENER(loadend); + + virtual void Trace(blink::Visitor*); + + private: + class ThrottlingController; + + explicit FileReader(ExecutionContext*); + + void Terminate(); + void ReadInternal(Blob*, FileReaderLoader::ReadType, ExceptionState&); + void FireEvent(const AtomicString& type); + + void ExecutePendingRead(); + + ReadyState state_; + + // Internal loading state, which could differ from ReadyState as it's + // for script-visible state while this one's for internal state. + enum LoadingState { + kLoadingStateNone, + kLoadingStatePending, + kLoadingStateLoading, + kLoadingStateAborted + }; + LoadingState loading_state_; + bool still_firing_events_; + + String blob_type_; + scoped_refptr<BlobDataHandle> blob_data_handle_; + FileReaderLoader::ReadType read_type_; + String encoding_; + + std::unique_ptr<FileReaderLoader> loader_; + Member<DOMException> error_; + double last_progress_notification_time_ms_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader.idl b/chromium/third_party/blink/renderer/core/fileapi/file_reader.idl new file mode 100644 index 00000000000..ab2c692417e --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader.idl @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple 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. + */ + +// https://w3c.github.io/FileAPI/#APIASynch + +[ + ActiveScriptWrappable, + Constructor, + ConstructorCallWith=ExecutionContext, + Exposed=(Window,Worker) +] interface FileReader : EventTarget { + // async read methods + [RaisesException] void readAsArrayBuffer(Blob blob); + [RaisesException] void readAsBinaryString(Blob blob); + [RaisesException] void readAsText(Blob blob, optional DOMString label); + [RaisesException] void readAsDataURL(Blob blob); + + void abort(); + + // states + const unsigned short EMPTY = 0; + const unsigned short LOADING = 1; + const unsigned short DONE = 2; + + [ImplementedAs=getReadyState] readonly attribute unsigned short readyState; + + // File or Blob data + [CallWith=ScriptState] + readonly attribute (DOMString or ArrayBuffer)? result; + + readonly attribute DOMException? error; + + // event handler attributes + attribute EventHandler onloadstart; + attribute EventHandler onprogress; + attribute EventHandler onload; + attribute EventHandler onabort; + attribute EventHandler onerror; + attribute EventHandler onloadend; +}; diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.cc b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.cc new file mode 100644 index 00000000000..5543f63bc48 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.cc @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2010 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/fileapi/file_reader_loader.h" + +#include <limits> +#include <memory> +#include <utility> + +#include "base/memory/scoped_refptr.h" +#include "mojo/public/cpp/system/wait.h" +#include "third_party/blink/public/platform/web_url_request.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/fileapi/blob.h" +#include "third_party/blink/renderer/core/fileapi/file_reader_loader_client.h" +#include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h" +#include "third_party/blink/renderer/core/loader/threadable_loader.h" +#include "third_party/blink/renderer/core/loader/threadable_loader_client.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" +#include "third_party/blink/renderer/platform/blob/blob_registry.h" +#include "third_party/blink/renderer/platform/blob/blob_url.h" +#include "third_party/blink/renderer/platform/histogram.h" +#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h" +#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h" +#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h" +#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" +#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" +#include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" +#include "third_party/blink/renderer/platform/wtf/text/base64.h" +#include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" +#include "v8/include/v8.h" + +namespace blink { + +// static +std::unique_ptr<FileReaderLoader> FileReaderLoader::Create( + ReadType read_type, + FileReaderLoaderClient* client) { + return std::make_unique<FileReaderLoader>(read_type, client); +} + +FileReaderLoader::FileReaderLoader(ReadType read_type, + FileReaderLoaderClient* client) + : read_type_(read_type), + client_(client), + // TODO(hajimehoshi): Pass an appropriate task runner to SimpleWatcher + // constructor. + handle_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC), + binding_(this), + weak_factory_(this) {} + +FileReaderLoader::~FileReaderLoader() { + Cleanup(); + UnadjustReportedMemoryUsageToV8(); +} + +void FileReaderLoader::Start(scoped_refptr<BlobDataHandle> blob_data) { +#if DCHECK_IS_ON() + DCHECK(!started_loading_) << "FileReaderLoader can only be used once"; + started_loading_ = true; +#endif // DCHECK_IS_ON() + + mojo::ScopedDataPipeProducerHandle producer_handle; + MojoResult result = + CreateDataPipe(nullptr, &producer_handle, &consumer_handle_); + if (result != MOJO_RESULT_OK) { + Failed(FileError::kNotReadableErr); + return; + } + + mojom::blink::BlobReaderClientPtr client_ptr; + binding_.Bind(MakeRequest(&client_ptr)); + blob_data->ReadAll(std::move(producer_handle), std::move(client_ptr)); + + if (IsSyncLoad()) { + // Wait for OnCalculatedSize, which will also synchronously drain the data + // pipe. + binding_.WaitForIncomingMethodCall(); + if (received_on_complete_) + return; + if (!received_all_data_) { + Failed(FileError::kNotReadableErr); + return; + } + + // Wait for OnComplete + binding_.WaitForIncomingMethodCall(); + if (!received_on_complete_) + Failed(FileError::kNotReadableErr); + } +} + +void FileReaderLoader::Cancel() { + error_code_ = FileError::kAbortErr; + Cleanup(); +} + +DOMArrayBuffer* FileReaderLoader::ArrayBufferResult() { + DCHECK_EQ(read_type_, kReadAsArrayBuffer); + if (array_buffer_result_) + return array_buffer_result_; + + // If the loading is not started or an error occurs, return an empty result. + if (!raw_data_ || error_code_) + return nullptr; + + DOMArrayBuffer* result = DOMArrayBuffer::Create(raw_data_->ToArrayBuffer()); + if (finished_loading_) { + array_buffer_result_ = result; + AdjustReportedMemoryUsageToV8( + -1 * static_cast<int64_t>(raw_data_->ByteLength())); + raw_data_.reset(); + } + return result; +} + +String FileReaderLoader::StringResult() { + DCHECK_NE(read_type_, kReadAsArrayBuffer); + DCHECK_NE(read_type_, kReadByClient); + + if (!raw_data_ || error_code_ || is_raw_data_converted_) + return string_result_; + + switch (read_type_) { + case kReadAsArrayBuffer: + // No conversion is needed. + return string_result_; + case kReadAsBinaryString: + SetStringResult(raw_data_->ToString()); + break; + case kReadAsText: + SetStringResult(ConvertToText()); + break; + case kReadAsDataURL: + // Partial data is not supported when reading as data URL. + if (finished_loading_) + SetStringResult(ConvertToDataURL()); + break; + default: + NOTREACHED(); + } + + if (finished_loading_) { + DCHECK(is_raw_data_converted_); + AdjustReportedMemoryUsageToV8( + -1 * static_cast<int64_t>(raw_data_->ByteLength())); + raw_data_.reset(); + } + return string_result_; +} + +void FileReaderLoader::SetEncoding(const String& encoding) { + if (!encoding.IsEmpty()) + encoding_ = WTF::TextEncoding(encoding); +} + +void FileReaderLoader::Cleanup() { + handle_watcher_.Cancel(); + consumer_handle_.reset(); + + // If we get any error, we do not need to keep a buffer around. + if (error_code_) { + raw_data_.reset(); + string_result_ = ""; + is_raw_data_converted_ = true; + decoder_.reset(); + array_buffer_result_ = nullptr; + UnadjustReportedMemoryUsageToV8(); + } +} + +void FileReaderLoader::Failed(FileError::ErrorCode error_code) { + // If an error was already reported, don't report this error again. + if (error_code_ != FileError::kOK) + return; + error_code_ = error_code; + Cleanup(); + if (client_) + client_->DidFail(error_code_); +} + +void FileReaderLoader::OnStartLoading(uint64_t total_bytes) { + total_bytes_ = total_bytes; + + DCHECK(!raw_data_); + + if (read_type_ != kReadByClient) { + // Check that we can cast to unsigned since we have to do + // so to call ArrayBuffer's create function. + // FIXME: Support reading more than the current size limit of ArrayBuffer. + if (total_bytes > std::numeric_limits<unsigned>::max()) { + Failed(FileError::kNotReadableErr); + return; + } + + raw_data_ = std::make_unique<ArrayBufferBuilder>(total_bytes); + if (!raw_data_->IsValid()) { + Failed(FileError::kNotReadableErr); + return; + } + raw_data_->SetVariableCapacity(false); + } + + if (client_) + client_->DidStartLoading(); +} + +void FileReaderLoader::OnReceivedData(const char* data, unsigned data_length) { + DCHECK(data); + + // Bail out if we already encountered an error. + if (error_code_) + return; + + if (read_type_ == kReadByClient) { + bytes_loaded_ += data_length; + + if (client_) + client_->DidReceiveDataForClient(data, data_length); + return; + } + + unsigned bytes_appended = raw_data_->Append(data, data_length); + if (!bytes_appended) { + raw_data_.reset(); + bytes_loaded_ = 0; + Failed(FileError::kNotReadableErr); + return; + } + bytes_loaded_ += bytes_appended; + is_raw_data_converted_ = false; + AdjustReportedMemoryUsageToV8(bytes_appended); + + if (client_) + client_->DidReceiveData(); +} + +void FileReaderLoader::OnFinishLoading() { + if (read_type_ != kReadByClient && raw_data_) { + raw_data_->ShrinkToFit(); + is_raw_data_converted_ = false; + } + + finished_loading_ = true; + + Cleanup(); + if (client_) + client_->DidFinishLoading(); +} + +void FileReaderLoader::OnCalculatedSize(uint64_t total_size, + uint64_t expected_content_size) { + auto weak_this = weak_factory_.GetWeakPtr(); + OnStartLoading(expected_content_size); + // OnStartLoading calls out to our client, which could delete |this|, so bail + // out if that happened. + if (!weak_this) + return; + + if (expected_content_size == 0) { + received_all_data_ = true; + return; + } + + if (IsSyncLoad()) { + OnDataPipeReadable(MOJO_RESULT_OK); + } else { + handle_watcher_.Watch( + consumer_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE, + WTF::BindRepeating(&FileReaderLoader::OnDataPipeReadable, + WTF::Unretained(this))); + } +} + +void FileReaderLoader::OnComplete(int32_t status, uint64_t data_length) { + DEFINE_THREAD_SAFE_STATIC_LOCAL(SparseHistogram, + file_reader_loader_read_errors_histogram, + ("Storage.Blob.FileReaderLoader.ReadError")); + if (status != net::OK || data_length != total_bytes_) { + net_error_ = status; + if (net_error_ != net::OK) + file_reader_loader_read_errors_histogram.Sample(std::max(0, -net_error_)); + Failed(status == net::ERR_FILE_NOT_FOUND ? FileError::kNotFoundErr + : FileError::kNotReadableErr); + return; + } + + received_on_complete_ = true; + if (received_all_data_) + OnFinishLoading(); +} + +void FileReaderLoader::OnDataPipeReadable(MojoResult result) { + if (result != MOJO_RESULT_OK) { + if (!received_all_data_) + Failed(FileError::kNotReadableErr); + return; + } + + while (true) { + uint32_t num_bytes; + const void* buffer; + MojoResult result = consumer_handle_->BeginReadData( + &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE); + if (result == MOJO_RESULT_SHOULD_WAIT) { + if (!IsSyncLoad()) + return; + + result = mojo::Wait(consumer_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE); + if (result == MOJO_RESULT_OK) + continue; + } + if (result == MOJO_RESULT_FAILED_PRECONDITION) { + // Pipe closed. + if (!received_all_data_) + Failed(FileError::kNotReadableErr); + return; + } + if (result != MOJO_RESULT_OK) { + Failed(FileError::kNotReadableErr); + return; + } + OnReceivedData(static_cast<const char*>(buffer), num_bytes); + consumer_handle_->EndReadData(num_bytes); + if (BytesLoaded() >= total_bytes_) { + received_all_data_ = true; + if (received_on_complete_) + OnFinishLoading(); + return; + } + } +} + +void FileReaderLoader::AdjustReportedMemoryUsageToV8(int64_t usage) { + if (!usage) + return; + memory_usage_reported_to_v8_ += usage; + v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(usage); + DCHECK_GE(memory_usage_reported_to_v8_, 0); +} + +void FileReaderLoader::UnadjustReportedMemoryUsageToV8() { + if (!memory_usage_reported_to_v8_) + return; + v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory( + -memory_usage_reported_to_v8_); + memory_usage_reported_to_v8_ = 0; +} + +String FileReaderLoader::ConvertToText() { + if (!bytes_loaded_) + return ""; + + // Decode the data. + // The File API spec says that we should use the supplied encoding if it is + // valid. However, we choose to ignore this requirement in order to be + // consistent with how WebKit decodes the web content: always has the BOM + // override the provided encoding. + // FIXME: consider supporting incremental decoding to improve the perf. + StringBuilder builder; + if (!decoder_) { + decoder_ = TextResourceDecoder::Create(TextResourceDecoderOptions( + TextResourceDecoderOptions::kPlainTextContent, + encoding_.IsValid() ? encoding_ : UTF8Encoding())); + } + builder.Append(decoder_->Decode(static_cast<const char*>(raw_data_->Data()), + raw_data_->ByteLength())); + + if (finished_loading_) + builder.Append(decoder_->Flush()); + + return builder.ToString(); +} + +String FileReaderLoader::ConvertToDataURL() { + StringBuilder builder; + builder.Append("data:"); + + if (!bytes_loaded_) + return builder.ToString(); + + builder.Append(data_type_); + builder.Append(";base64,"); + + Vector<char> out; + Base64Encode(static_cast<const char*>(raw_data_->Data()), + raw_data_->ByteLength(), out); + out.push_back('\0'); + builder.Append(out.data()); + + return builder.ToString(); +} + +void FileReaderLoader::SetStringResult(const String& result) { + AdjustReportedMemoryUsageToV8( + -1 * static_cast<int64_t>(string_result_.CharactersSizeInBytes())); + is_raw_data_converted_ = true; + string_result_ = result; + AdjustReportedMemoryUsageToV8(string_result_.CharactersSizeInBytes()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.h b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.h new file mode 100644 index 00000000000..d8fdaa8b743 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_LOADER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_LOADER_H_ + +#include <memory> + +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "third_party/blink/public/mojom/blob/blob.mojom-blink.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/fileapi/file_error.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" +#include "third_party/blink/renderer/platform/wtf/optional.h" +#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.h" + +namespace blink { + +class BlobDataHandle; +class DOMArrayBuffer; +class FileReaderLoaderClient; +class TextResourceDecoder; + +// Reads a Blob's content into memory. +// +// Blobs are typically stored on disk, and should be read asynchronously +// whenever possible. Synchronous loading is implemented to support Web Platform +// features that we cannot (yet) remove, such as FileReaderSync and synchronous +// XMLHttpRequest. +// +// Each FileReaderLoader instance is only good for reading one Blob, and will +// leak resources if used multiple times. +class CORE_EXPORT FileReaderLoader : public mojom::blink::BlobReaderClient { + USING_FAST_MALLOC(FileReaderLoader); + + public: + enum ReadType { + kReadAsArrayBuffer, + kReadAsBinaryString, + kReadAsText, + kReadAsDataURL, + kReadByClient + }; + + // If client is given, do the loading asynchronously. Otherwise, load + // synchronously. + static std::unique_ptr<FileReaderLoader> Create(ReadType, + FileReaderLoaderClient*); + FileReaderLoader(ReadType, FileReaderLoaderClient*); + ~FileReaderLoader() override; + + void Start(scoped_refptr<BlobDataHandle>); + void Cancel(); + + DOMArrayBuffer* ArrayBufferResult(); + String StringResult(); + + // Returns the total bytes received. Bytes ignored by m_rawData won't be + // counted. + // + // This value doesn't grow more than numeric_limits<unsigned> when + // m_readType is not set to ReadByClient. + uint64_t BytesLoaded() const { return bytes_loaded_; } + + // Before OnCalculatedSize() is called: Returns nullopt. + // After OnCalculatedSize() is called: Returns the size of the resource. + Optional<uint64_t> TotalBytes() const { return total_bytes_; } + + FileError::ErrorCode GetErrorCode() const { return error_code_; } + + int32_t GetNetError() const { return net_error_; } + + void SetEncoding(const String&); + void SetDataType(const String& data_type) { data_type_ = data_type; } + + bool HasFinishedLoading() const { return finished_loading_; } + + private: + void Cleanup(); + void Failed(FileError::ErrorCode); + + void OnStartLoading(uint64_t total_bytes); + void OnReceivedData(const char* data, unsigned data_length); + void OnFinishLoading(); + + bool IsSyncLoad() const { return !client_; } + + // BlobReaderClient: + void OnCalculatedSize(uint64_t total_size, + uint64_t expected_content_size) override; + void OnComplete(int32_t status, uint64_t data_length) override; + void OnDataPipeReadable(MojoResult); + + void AdjustReportedMemoryUsageToV8(int64_t usage); + void UnadjustReportedMemoryUsageToV8(); + + String ConvertToText(); + String ConvertToDataURL(); + void SetStringResult(const String&); + + ReadType read_type_; + FileReaderLoaderClient* client_; + WTF::TextEncoding encoding_; + String data_type_; + + std::unique_ptr<ArrayBufferBuilder> raw_data_; + bool is_raw_data_converted_ = false; + + Persistent<DOMArrayBuffer> array_buffer_result_; + String string_result_; + + // The decoder used to decode the text data. + std::unique_ptr<TextResourceDecoder> decoder_; + + bool finished_loading_ = false; + uint64_t bytes_loaded_ = 0; + // total_bytes_ is set to the total size of the blob being loaded as soon as + // it is known, and the buffer for receiving data of total_bytes_ is + // allocated and never grow even when extra data is appended. + Optional<uint64_t> total_bytes_; + int64_t memory_usage_reported_to_v8_ = 0; + + int32_t net_error_ = 0; // net::OK + FileError::ErrorCode error_code_ = FileError::kOK; + + mojo::ScopedDataPipeConsumerHandle consumer_handle_; + mojo::SimpleWatcher handle_watcher_; + mojo::Binding<mojom::blink::BlobReaderClient> binding_; + bool received_all_data_ = false; + bool received_on_complete_ = false; +#if DCHECK_IS_ON() + bool started_loading_ = false; +#endif // DCHECK_IS_ON() + + base::WeakPtrFactory<FileReaderLoader> weak_factory_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_LOADER_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader_client.h b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader_client.h new file mode 100644 index 00000000000..d52508fbbd3 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader_loader_client.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_LOADER_CLIENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_LOADER_CLIENT_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/fileapi/file_error.h" +#include "third_party/blink/renderer/platform/wtf/assertions.h" + +namespace blink { + +class CORE_EXPORT FileReaderLoaderClient { + public: + virtual ~FileReaderLoaderClient() = default; + + virtual void DidStartLoading() = 0; + // Clients must implement this method if they are using any ReadType except + // ReadByClient. + virtual void DidReceiveData() { NOTREACHED(); } + // Clients must implement this method if they are using the ReadByClient + // ReadType. + virtual void DidReceiveDataForClient(const char* data, unsigned data_length) { + NOTREACHED(); + } + virtual void DidFinishLoading() = 0; + virtual void DidFail(FileError::ErrorCode) = 0; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_LOADER_CLIENT_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.cc b/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.cc new file mode 100644 index 00000000000..94d3943339b --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.cc @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010 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/fileapi/file_reader_sync.h" + +#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/fileapi/blob.h" +#include "third_party/blink/renderer/core/fileapi/file_error.h" +#include "third_party/blink/renderer/core/fileapi/file_reader_loader.h" +#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/histogram.h" + +namespace blink { + +namespace { +// These values are written to logs. New enum values can be added, but existing +// enums must never be renumbered or deleted and reused. +enum class WorkerType { + OTHER = 0, + DEDICATED_WORKER = 1, + SHARED_WORKER = 2, + SERVICE_WORKER = 3, + MAX +}; +} // namespace + +FileReaderSync::FileReaderSync(ExecutionContext* context) { + WorkerType type = WorkerType::OTHER; + if (context->IsDedicatedWorkerGlobalScope()) + type = WorkerType::DEDICATED_WORKER; + else if (context->IsSharedWorkerGlobalScope()) + type = WorkerType::SHARED_WORKER; + else if (context->IsServiceWorkerGlobalScope()) + type = WorkerType::SERVICE_WORKER; + DEFINE_THREAD_SAFE_STATIC_LOCAL( + EnumerationHistogram, worker_type_histogram, + ("FileReaderSync.WorkerType", static_cast<int>(WorkerType::MAX))); + worker_type_histogram.Count(static_cast<int>(type)); +} + +DOMArrayBuffer* FileReaderSync::readAsArrayBuffer( + Blob* blob, + ExceptionState& exception_state) { + DCHECK(blob); + + std::unique_ptr<FileReaderLoader> loader = + FileReaderLoader::Create(FileReaderLoader::kReadAsArrayBuffer, nullptr); + StartLoading(*loader, *blob, exception_state); + + return loader->ArrayBufferResult(); +} + +String FileReaderSync::readAsBinaryString(Blob* blob, + ExceptionState& exception_state) { + DCHECK(blob); + + std::unique_ptr<FileReaderLoader> loader = + FileReaderLoader::Create(FileReaderLoader::kReadAsBinaryString, nullptr); + StartLoading(*loader, *blob, exception_state); + return loader->StringResult(); +} + +String FileReaderSync::readAsText(Blob* blob, + const String& encoding, + ExceptionState& exception_state) { + DCHECK(blob); + + std::unique_ptr<FileReaderLoader> loader = + FileReaderLoader::Create(FileReaderLoader::kReadAsText, nullptr); + loader->SetEncoding(encoding); + StartLoading(*loader, *blob, exception_state); + return loader->StringResult(); +} + +String FileReaderSync::readAsDataURL(Blob* blob, + ExceptionState& exception_state) { + DCHECK(blob); + + std::unique_ptr<FileReaderLoader> loader = + FileReaderLoader::Create(FileReaderLoader::kReadAsDataURL, nullptr); + loader->SetDataType(blob->type()); + StartLoading(*loader, *blob, exception_state); + return loader->StringResult(); +} + +void FileReaderSync::StartLoading(FileReaderLoader& loader, + const Blob& blob, + ExceptionState& exception_state) { + loader.Start(blob.GetBlobDataHandle()); + if (loader.GetErrorCode()) + FileError::ThrowDOMException(exception_state, loader.GetErrorCode()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.h b/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.h new file mode 100644 index 00000000000..d5db3fca225 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_SYNC_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_SYNC_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +class Blob; +class DOMArrayBuffer; +class ExceptionState; +class ExecutionContext; +class FileReaderLoader; + +class FileReaderSync final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + static FileReaderSync* Create(ExecutionContext* context) { + return new FileReaderSync(context); + } + + DOMArrayBuffer* readAsArrayBuffer(Blob*, ExceptionState&); + String readAsBinaryString(Blob*, ExceptionState&); + String readAsText(Blob* blob, ExceptionState& ec) { + return readAsText(blob, "", ec); + } + String readAsText(Blob*, const String& encoding, ExceptionState&); + String readAsDataURL(Blob*, ExceptionState&); + + private: + explicit FileReaderSync(ExecutionContext*); + + void StartLoading(FileReaderLoader&, const Blob&, ExceptionState&); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_FILE_READER_SYNC_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.idl b/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.idl new file mode 100644 index 00000000000..2f8ef263c0c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_reader_sync.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 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. + */ + +// https://w3c.github.io/FileAPI/#FileReaderSync + +[ + Exposed=(DedicatedWorker,SharedWorker), + Constructor, + ConstructorCallWith=ExecutionContext, + Measure +] interface FileReaderSync { + [RaisesException] ArrayBuffer readAsArrayBuffer(Blob blob); + [RaisesException] DOMString readAsBinaryString(Blob blob); + [RaisesException] DOMString readAsText(Blob blob, optional DOMString label); + [RaisesException] DOMString readAsDataURL(Blob blob); +}; diff --git a/chromium/third_party/blink/renderer/core/fileapi/file_test.cc b/chromium/third_party/blink/renderer/core/fileapi/file_test.cc new file mode 100644 index 00000000000..d09de6af79b --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/file_test.cc @@ -0,0 +1,97 @@ +// Copyright 2014 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 "third_party/blink/renderer/core/fileapi/file.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/file_metadata.h" + +namespace blink { + +TEST(FileTest, nativeFile) { + File* const file = File::Create("/native/path"); + EXPECT_TRUE(file->HasBackingFile()); + EXPECT_EQ("/native/path", file->GetPath()); + EXPECT_TRUE(file->FileSystemURL().IsEmpty()); +} + +TEST(FileTest, blobBackingFile) { + const scoped_refptr<BlobDataHandle> blob_data_handle = + BlobDataHandle::Create(); + File* const file = File::Create("name", 0.0, blob_data_handle); + EXPECT_FALSE(file->HasBackingFile()); + EXPECT_TRUE(file->GetPath().IsEmpty()); + EXPECT_TRUE(file->FileSystemURL().IsEmpty()); +} + +TEST(FileTest, fileSystemFileWithNativeSnapshot) { + FileMetadata metadata; + metadata.platform_path = "/native/snapshot"; + File* const file = + File::CreateForFileSystemFile("name", metadata, File::kIsUserVisible); + EXPECT_TRUE(file->HasBackingFile()); + EXPECT_EQ("/native/snapshot", file->GetPath()); + EXPECT_TRUE(file->FileSystemURL().IsEmpty()); +} + +TEST(FileTest, fileSystemFileWithNativeSnapshotAndSize) { + FileMetadata metadata; + metadata.length = 1024ll; + metadata.platform_path = "/native/snapshot"; + File* const file = + File::CreateForFileSystemFile("name", metadata, File::kIsUserVisible); + EXPECT_TRUE(file->HasBackingFile()); + EXPECT_EQ("/native/snapshot", file->GetPath()); + EXPECT_TRUE(file->FileSystemURL().IsEmpty()); +} + +TEST(FileTest, fileSystemFileWithoutNativeSnapshot) { + KURL url("filesystem:http://example.com/isolated/hash/non-native-file"); + FileMetadata metadata; + File* const file = + File::CreateForFileSystemFile(url, metadata, File::kIsUserVisible); + EXPECT_FALSE(file->HasBackingFile()); + EXPECT_TRUE(file->GetPath().IsEmpty()); + EXPECT_EQ(url, file->FileSystemURL()); +} + +TEST(FileTest, hsaSameSource) { + File* const native_file_a1 = File::Create("/native/pathA"); + File* const native_file_a2 = File::Create("/native/pathA"); + File* const native_file_b = File::Create("/native/pathB"); + + const scoped_refptr<BlobDataHandle> blob_data_a = BlobDataHandle::Create(); + const scoped_refptr<BlobDataHandle> blob_data_b = BlobDataHandle::Create(); + File* const blob_file_a1 = File::Create("name", 0.0, blob_data_a); + File* const blob_file_a2 = File::Create("name", 0.0, blob_data_a); + File* const blob_file_b = File::Create("name", 0.0, blob_data_b); + + KURL url_a("filesystem:http://example.com/isolated/hash/non-native-file-A"); + KURL url_b("filesystem:http://example.com/isolated/hash/non-native-file-B"); + FileMetadata metadata; + File* const file_system_file_a1 = + File::CreateForFileSystemFile(url_a, metadata, File::kIsUserVisible); + File* const file_system_file_a2 = + File::CreateForFileSystemFile(url_a, metadata, File::kIsUserVisible); + File* const file_system_file_b = + File::CreateForFileSystemFile(url_b, metadata, File::kIsUserVisible); + + EXPECT_FALSE(native_file_a1->HasSameSource(*blob_file_a1)); + EXPECT_FALSE(blob_file_a1->HasSameSource(*file_system_file_a1)); + EXPECT_FALSE(file_system_file_a1->HasSameSource(*native_file_a1)); + + EXPECT_TRUE(native_file_a1->HasSameSource(*native_file_a1)); + EXPECT_TRUE(native_file_a1->HasSameSource(*native_file_a2)); + EXPECT_FALSE(native_file_a1->HasSameSource(*native_file_b)); + + EXPECT_TRUE(blob_file_a1->HasSameSource(*blob_file_a1)); + EXPECT_TRUE(blob_file_a1->HasSameSource(*blob_file_a2)); + EXPECT_FALSE(blob_file_a1->HasSameSource(*blob_file_b)); + + EXPECT_TRUE(file_system_file_a1->HasSameSource(*file_system_file_a1)); + EXPECT_TRUE(file_system_file_a1->HasSameSource(*file_system_file_a2)); + EXPECT_FALSE(file_system_file_a1->HasSameSource(*file_system_file_b)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/public_url_manager.cc b/chromium/third_party/blink/renderer/core/fileapi/public_url_manager.cc new file mode 100644 index 00000000000..19faf5d2f96 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/public_url_manager.cc @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2012 Motorola Mobility Inc. + * Copyright (C) 2013 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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/fileapi/public_url_manager.h" + +#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" +#include "third_party/blink/renderer/core/fileapi/url_registry.h" +#include "third_party/blink/renderer/platform/blob/blob_data.h" +#include "third_party/blink/renderer/platform/blob/blob_url.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "third_party/blink/renderer/platform/weborigin/url_security_origin_map.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" +#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "third_party/blink/renderer/platform/wtf/thread_specific.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +namespace { + +// When a blob URL is created in a unique origin the origin is serialized into +// the URL as "null". Since that makes it impossible to parse the origin back +// out and compare it against a context's origin (to check if a context is +// allowed to dereference the URL) we store a map of blob URL to SecurityOrigin +// instance for blob URLs with unique origins. + +class BlobOriginMap : public URLSecurityOriginMap { + public: + BlobOriginMap(); + SecurityOrigin* GetOrigin(const KURL&) override; +}; + +typedef HashMap<String, scoped_refptr<SecurityOrigin>> BlobURLOriginMap; +static ThreadSpecific<BlobURLOriginMap>& OriginMap() { + // We want to create the BlobOriginMap exactly once because it is shared by + // all the threads. + DEFINE_THREAD_SAFE_STATIC_LOCAL(BlobOriginMap, cache, ()); + (void)cache; // BlobOriginMap's constructor does the interesting work. + + DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<BlobURLOriginMap>, map, ()); + return map; +} + +static void SaveToOriginMap(SecurityOrigin* origin, const KURL& url) { + // If the blob URL contains null origin, as in the context with unique + // security origin or file URL, save the mapping between url and origin so + // that the origin can be retrieved when doing security origin check. + // + // See the definition of the origin of a Blob URL in the File API spec. + DCHECK(!url.HasFragmentIdentifier()); + if (origin && BlobURL::GetOrigin(url) == "null") + OriginMap()->insert(url.GetString(), origin); +} + +static void RemoveFromOriginMap(const KURL& url) { + if (BlobURL::GetOrigin(url) == "null") + OriginMap()->erase(url.GetString()); +} + +BlobOriginMap::BlobOriginMap() { + SecurityOrigin::SetMap(this); +} + +SecurityOrigin* BlobOriginMap::GetOrigin(const KURL& url) { + if (url.ProtocolIs("blob")) { + KURL url_without_fragment = url; + url_without_fragment.RemoveFragmentIdentifier(); + return OriginMap()->at(url_without_fragment.GetString()); + } + return nullptr; +} + +} // namespace + +PublicURLManager* PublicURLManager::Create(ExecutionContext* context) { + return new PublicURLManager(context); +} + +PublicURLManager::PublicURLManager(ExecutionContext* context) + : ContextLifecycleObserver(context), is_stopped_(false) {} + +String PublicURLManager::RegisterURL(URLRegistrable* registrable) { + if (is_stopped_) + return String(); + + SecurityOrigin* origin = GetExecutionContext()->GetMutableSecurityOrigin(); + const KURL& url = BlobURL::CreatePublicURL(origin); + DCHECK(!url.IsEmpty()); + const String& url_string = url.GetString(); + + mojom::blink::BlobPtr blob; + if (RuntimeEnabledFeatures::MojoBlobURLsEnabled()) + blob = registrable->AsMojoBlob(); + if (blob) { + if (!url_store_) { + BlobDataHandle::GetBlobRegistry()->URLStoreForOrigin( + origin, MakeRequest(&url_store_)); + } + url_store_->Register(std::move(blob), url); + mojo_urls_.insert(url_string); + } else { + URLRegistry* registry = ®istrable->Registry(); + registry->RegisterURL(origin, url, registrable); + url_to_registry_.insert(url_string, registry); + } + SaveToOriginMap(origin, url); + + return url_string; +} + +void PublicURLManager::Revoke(const KURL& url) { + if (is_stopped_) + return; + // Don't bother trying to revoke URLs that can't have been registered anyway. + if (!url.ProtocolIs("blob") || url.HasFragmentIdentifier()) + return; + // Don't support revoking cross-origin blob URLs. + if (!SecurityOrigin::Create(url)->IsSameSchemeHostPort( + GetExecutionContext()->GetSecurityOrigin())) + return; + + if (RuntimeEnabledFeatures::MojoBlobURLsEnabled()) { + if (!url_store_) { + BlobDataHandle::GetBlobRegistry()->URLStoreForOrigin( + GetExecutionContext()->GetSecurityOrigin(), MakeRequest(&url_store_)); + } + url_store_->Revoke(url); + mojo_urls_.erase(url.GetString()); + } + RemoveFromOriginMap(url); + auto it = url_to_registry_.find(url.GetString()); + if (it == url_to_registry_.end()) + return; + it->value->UnregisterURL(url); + url_to_registry_.erase(it); +} + +void PublicURLManager::Resolve( + const KURL& url, + network::mojom::blink::URLLoaderFactoryRequest factory_request) { + DCHECK(RuntimeEnabledFeatures::MojoBlobURLsEnabled()); + DCHECK(url.ProtocolIs("blob")); + if (!url_store_) { + BlobDataHandle::GetBlobRegistry()->URLStoreForOrigin( + GetExecutionContext()->GetSecurityOrigin(), MakeRequest(&url_store_)); + } + url_store_->ResolveAsURLLoaderFactory(url, std::move(factory_request)); +} + +void PublicURLManager::Resolve( + const KURL& url, + mojom::blink::BlobURLTokenRequest token_request) { + DCHECK(RuntimeEnabledFeatures::MojoBlobURLsEnabled()); + DCHECK(url.ProtocolIs("blob")); + if (!url_store_) { + BlobDataHandle::GetBlobRegistry()->URLStoreForOrigin( + GetExecutionContext()->GetSecurityOrigin(), MakeRequest(&url_store_)); + } + url_store_->ResolveForNavigation(url, std::move(token_request)); +} + +void PublicURLManager::ContextDestroyed(ExecutionContext*) { + if (is_stopped_) + return; + + is_stopped_ = true; + for (auto& url_registry : url_to_registry_) { + url_registry.value->UnregisterURL(KURL(url_registry.key)); + RemoveFromOriginMap(KURL(url_registry.key)); + } + for (const auto& url : mojo_urls_) + RemoveFromOriginMap(KURL(url)); + + url_to_registry_.clear(); + mojo_urls_.clear(); + + url_store_.reset(); +} + +void PublicURLManager::Trace(blink::Visitor* visitor) { + ContextLifecycleObserver::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/public_url_manager.h b/chromium/third_party/blink/renderer/core/fileapi/public_url_manager.h new file mode 100644 index 00000000000..9d5e9454f5c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/public_url_manager.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 Motorola Mobility Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_PUBLIC_URL_MANAGER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_PUBLIC_URL_MANAGER_H_ + +#include "services/network/public/mojom/url_loader_factory.mojom-blink.h" +#include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" +#include "third_party/blink/renderer/platform/wtf/hash_set.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +class KURL; +class ExecutionContext; +class URLRegistry; +class URLRegistrable; + +class CORE_EXPORT PublicURLManager final + : public GarbageCollectedFinalized<PublicURLManager>, + public ContextLifecycleObserver { + USING_GARBAGE_COLLECTED_MIXIN(PublicURLManager); + + public: + static PublicURLManager* Create(ExecutionContext*); + + // Generates a new Blob URL and registers the URLRegistrable to the + // corresponding URLRegistry with the Blob URL. Returns the serialization + // of the Blob URL. + String RegisterURL(URLRegistrable*); + // Revokes the given URL. + void Revoke(const KURL&); + // When mojo Blob URLs are enabled this resolves the provided URL to a + // factory capable of creating loaders for the specific URL. + void Resolve(const KURL&, network::mojom::blink::URLLoaderFactoryRequest); + // When mojo Blob URLs are enabled this resolves the provided URL to a mojom + // BlobURLToken. This token can be used by the browser process to securely + // lookup what blob a URL used to refer to, even after the URL is revoked. + // If the URL fails to resolve the request will simply be disconnected. + void Resolve(const KURL&, mojom::blink::BlobURLTokenRequest); + + // ContextLifecycleObserver interface. + void ContextDestroyed(ExecutionContext*) override; + + virtual void Trace(blink::Visitor*); + + void SetURLStoreForTesting( + mojom::blink::BlobURLStoreAssociatedPtr url_store) { + url_store_ = std::move(url_store); + } + + private: + explicit PublicURLManager(ExecutionContext*); + + typedef String URLString; + // Map from URLs to the URLRegistry they are registered with. + typedef HashMap<URLString, URLRegistry*> URLToRegistryMap; + URLToRegistryMap url_to_registry_; + HashSet<URLString> mojo_urls_; + + bool is_stopped_; + + mojom::blink::BlobURLStoreAssociatedPtr url_store_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_PUBLIC_URL_MANAGER_H_ diff --git a/chromium/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc b/chromium/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc new file mode 100644 index 00000000000..7e2a27d1284 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/public_url_manager_test.cc @@ -0,0 +1,174 @@ +// Copyright 2018 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 "third_party/blink/renderer/core/fileapi/public_url_manager.h" + +#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/fileapi/url_registry.h" +#include "third_party/blink/renderer/core/testing/null_execution_context.h" +#include "third_party/blink/renderer/platform/blob/testing/fake_blob.h" +#include "third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" + +namespace blink { +namespace { + +using mojom::blink::Blob; +using mojom::blink::BlobPtr; +using mojom::blink::BlobRequest; +using mojom::blink::BlobURLStore; +using mojom::blink::BlobURLStoreAssociatedPtr; + +class TestURLRegistrable : public URLRegistrable { + public: + TestURLRegistrable(URLRegistry* registry, BlobPtr blob = nullptr) + : registry_(registry), blob_(std::move(blob)) {} + + URLRegistry& Registry() const override { return *registry_; } + + BlobPtr AsMojoBlob() override { + if (!blob_) + return nullptr; + BlobPtr result; + blob_->Clone(MakeRequest(&result)); + return result; + } + + private: + URLRegistry* const registry_; + BlobPtr blob_; +}; + +class FakeURLRegistry : public URLRegistry { + public: + void RegisterURL(SecurityOrigin* origin, + const KURL& url, + URLRegistrable* registrable) override { + registrations.push_back(Registration{origin, url, registrable}); + } + void UnregisterURL(const KURL&) override {} + + struct Registration { + SecurityOrigin* origin; + KURL url; + URLRegistrable* registrable; + }; + Vector<Registration> registrations; +}; + +} // namespace + +class PublicURLManagerTest : public testing::Test { + public: + PublicURLManagerTest() : url_store_binding_(&url_store_) {} + + void SetUp() override { + execution_context_ = new NullExecutionContext; + // By default this creates a unique origin, which is exactly what this test + // wants. + execution_context_->SetUpSecurityContext(); + + BlobURLStoreAssociatedPtr url_store_ptr; + url_store_binding_.Bind( + MakeRequestAssociatedWithDedicatedPipe(&url_store_ptr)); + url_manager().SetURLStoreForTesting(std::move(url_store_ptr)); + } + + PublicURLManager& url_manager() { + return execution_context_->GetPublicURLManager(); + } + + BlobPtr CreateMojoBlob(const String& uuid) { + BlobPtr result; + mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid), + MakeRequest(&result)); + return result; + } + + protected: + ScopedMojoBlobURLsForTest mojo_blob_urls_ = true; + Persistent<NullExecutionContext> execution_context_; + + FakeBlobURLStore url_store_; + mojo::AssociatedBinding<BlobURLStore> url_store_binding_; +}; + +TEST_F(PublicURLManagerTest, RegisterNonMojoBlob) { + FakeURLRegistry registry; + TestURLRegistrable registrable(®istry); + String url = url_manager().RegisterURL(®istrable); + ASSERT_EQ(1u, registry.registrations.size()); + EXPECT_EQ(0u, url_store_.registrations.size()); + EXPECT_EQ(execution_context_->GetSecurityOrigin(), + registry.registrations[0].origin); + EXPECT_EQ(url, registry.registrations[0].url); + EXPECT_EQ(®istrable, registry.registrations[0].registrable); + + EXPECT_TRUE(SecurityOrigin::CreateFromString(url)->IsSameSchemeHostPort( + execution_context_->GetSecurityOrigin())); + EXPECT_EQ(execution_context_->GetSecurityOrigin(), + SecurityOrigin::CreateFromString(url)); + + url_manager().Revoke(KURL(url)); + EXPECT_FALSE(SecurityOrigin::CreateFromString(url)->IsSameSchemeHostPort( + execution_context_->GetSecurityOrigin())); + url_store_binding_.FlushForTesting(); + // Even though this was not a mojo blob, the PublicURLManager might not know + // that, so still expect a revocation on the mojo interface. + ASSERT_EQ(1u, url_store_.revocations.size()); + EXPECT_EQ(url, url_store_.revocations[0]); +} + +TEST_F(PublicURLManagerTest, RegisterMojoBlob) { + FakeURLRegistry registry; + TestURLRegistrable registrable(®istry, CreateMojoBlob("id")); + String url = url_manager().RegisterURL(®istrable); + + EXPECT_EQ(0u, registry.registrations.size()); + ASSERT_EQ(1u, url_store_.registrations.size()); + EXPECT_EQ(url, url_store_.registrations.begin()->key); + + EXPECT_TRUE(SecurityOrigin::CreateFromString(url)->IsSameSchemeHostPort( + execution_context_->GetSecurityOrigin())); + EXPECT_EQ(execution_context_->GetSecurityOrigin(), + SecurityOrigin::CreateFromString(url)); + + url_manager().Revoke(KURL(url)); + EXPECT_FALSE(SecurityOrigin::CreateFromString(url)->IsSameSchemeHostPort( + execution_context_->GetSecurityOrigin())); + url_store_binding_.FlushForTesting(); + ASSERT_EQ(1u, url_store_.revocations.size()); + EXPECT_EQ(url, url_store_.revocations[0]); +} + +TEST_F(PublicURLManagerTest, RevokeValidNonRegisteredURL) { + execution_context_->SetURL(KURL("http://example.com/foo/bar")); + execution_context_->SetUpSecurityContext(); + + KURL url = KURL("blob:http://example.com/id"); + url_manager().Revoke(url); + url_store_binding_.FlushForTesting(); + ASSERT_EQ(1u, url_store_.revocations.size()); + EXPECT_EQ(url, url_store_.revocations[0]); +} + +TEST_F(PublicURLManagerTest, RevokeInvalidURL) { + execution_context_->SetURL(KURL("http://example.com/foo/bar")); + execution_context_->SetUpSecurityContext(); + + KURL invalid_scheme_url = KURL("blb:http://example.com/id"); + KURL fragment_url = KURL("blob:http://example.com/id#fragment"); + KURL invalid_origin_url = KURL("blob:http://foobar.com/id"); + url_manager().Revoke(invalid_scheme_url); + url_manager().Revoke(fragment_url); + url_manager().Revoke(invalid_origin_url); + url_store_binding_.FlushForTesting(); + // Both should have been silently ignored. + EXPECT_TRUE(url_store_.revocations.IsEmpty()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/url_file_api.cc b/chromium/third_party/blink/renderer/core/fileapi/url_file_api.cc new file mode 100644 index 00000000000..dc29bc939df --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/url_file_api.cc @@ -0,0 +1,40 @@ +// Copyright 2016 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 "third_party/blink/renderer/core/fileapi/url_file_api.h" + +#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/fileapi/blob.h" +#include "third_party/blink/renderer/core/fileapi/public_url_manager.h" +#include "third_party/blink/renderer/core/frame/use_counter.h" +#include "third_party/blink/renderer/core/url/dom_url.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" + +namespace blink { + +// static +String URLFileAPI::createObjectURL(ScriptState* script_state, + Blob* blob, + ExceptionState& exception_state) { + DCHECK(blob); + ExecutionContext* execution_context = ExecutionContext::From(script_state); + DCHECK(execution_context); + + UseCounter::Count(execution_context, WebFeature::kCreateObjectURLBlob); + return DOMURL::CreatePublicURL(execution_context, blob); +} + +// static +void URLFileAPI::revokeObjectURL(ScriptState* script_state, + const String& url_string) { + ExecutionContext* execution_context = ExecutionContext::From(script_state); + DCHECK(execution_context); + + KURL url(NullURL(), url_string); + execution_context->RemoveURLFromMemoryCache(url); + execution_context->GetPublicURLManager().Revoke(url); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fileapi/url_file_api.h b/chromium/third_party/blink/renderer/core/fileapi/url_file_api.h new file mode 100644 index 00000000000..dcab4f27217 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/url_file_api.h @@ -0,0 +1,27 @@ +// Copyright 2016 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_URL_FILE_API_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_URL_FILE_API_H_ + +#include "third_party/blink/renderer/platform/wtf/allocator.h" +#include "third_party/blink/renderer/platform/wtf/forward.h" + +namespace blink { + +class ExceptionState; +class ScriptState; +class Blob; + +class URLFileAPI { + STATIC_ONLY(URLFileAPI); + + public: + static String createObjectURL(ScriptState*, Blob*, ExceptionState&); + static void revokeObjectURL(ScriptState*, const String&); +}; + +} // namespace blink + +#endif diff --git a/chromium/third_party/blink/renderer/core/fileapi/url_file_api.idl b/chromium/third_party/blink/renderer/core/fileapi/url_file_api.idl new file mode 100644 index 00000000000..da2b186b2bb --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/url_file_api.idl @@ -0,0 +1,11 @@ +// Copyright 2016 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. + +// https://w3c.github.io/FileAPI/#creating-revoking +[ + ImplementedAs=URLFileAPI +] partial interface URL { + [Exposed=(Window,DedicatedWorker,SharedWorker), CallWith=ScriptState, RaisesException] static DOMString createObjectURL(Blob blob); + [Exposed=(Window,DedicatedWorker,SharedWorker), CallWith=ScriptState] static void revokeObjectURL(DOMString url); +}; diff --git a/chromium/third_party/blink/renderer/core/fileapi/url_registry.h b/chromium/third_party/blink/renderer/core/fileapi/url_registry.h new file mode 100644 index 00000000000..e68d8ae63cd --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fileapi/url_registry.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_URL_REGISTRY_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_URL_REGISTRY_H_ + +#include "third_party/blink/public/mojom/blob/blob.mojom-blink.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +class KURL; +class SecurityOrigin; +class URLRegistry; + +class CORE_EXPORT URLRegistrable { + public: + virtual ~URLRegistrable() = default; + virtual URLRegistry& Registry() const = 0; + virtual mojom::blink::BlobPtr AsMojoBlob() { return nullptr; } +}; + +class CORE_EXPORT URLRegistry { + USING_FAST_MALLOC(URLRegistry); + + public: + virtual ~URLRegistry() = default; + virtual void RegisterURL(SecurityOrigin*, const KURL&, URLRegistrable*) = 0; + virtual void UnregisterURL(const KURL&) = 0; + + // These are optional APIs + virtual URLRegistrable* Lookup(const String&) { + NOTREACHED(); + return nullptr; + } + virtual bool Contains(const String&) { + NOTREACHED(); + return false; + } +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FILEAPI_URL_REGISTRY_H_ |