Import Cobalt 23.master.0.308929
diff --git a/cobalt/base/c_val.cc b/cobalt/base/c_val.cc
index c1f7684..c020671 100644
--- a/cobalt/base/c_val.cc
+++ b/cobalt/base/c_val.cc
@@ -56,7 +56,8 @@
// CVals cannot share name. If this assert is triggered, we are trying to
// register more than one CVal with the same name, which this system is
// not designed to handle.
- DCHECK(registered_vars_->find(cval->GetName()) == registered_vars_->end());
+ DCHECK(registered_vars_->find(cval->GetName()) == registered_vars_->end())
+ << " CVal " << cval->GetName() << " Already registered.";
(*registered_vars_)[cval->GetName()] = cval;
*value_lock = value_lock_refptr_;
diff --git a/cobalt/bindings/v8c/idl_compiler_v8c.py b/cobalt/bindings/v8c/idl_compiler_v8c.py
index 4b0c8bc..09ca3f4 100644
--- a/cobalt/bindings/v8c/idl_compiler_v8c.py
+++ b/cobalt/bindings/v8c/idl_compiler_v8c.py
@@ -17,21 +17,29 @@
CodeGenerator class.
"""
+import logging
import sys
-import _env # pylint: disable=unused-import
+import _env #pylint: disable=import-error,unused-import
from cobalt.bindings.idl_compiler_cobalt import generate_bindings
from cobalt.bindings.v8c.code_generator_v8c import CodeGeneratorV8c
-if __name__ == '__main__':
- # TODO(b/225964218): The flakiness here should be resolved and retries
- # removed.
- # Retry up to 5 times as this can be flaky
+
+def main():
+ # TODO(b/225964218, b/229148609): The flakiness here should be resolved and
+ # retries removed. Retry up to 5 times as this can be flaky.
latest_error = None
for i in range(5):
try:
+ if i > 0:
+ logging.warning('Failed IDL v8 compilation. Retry Attempt #%d', i)
sys.exit(generate_bindings(CodeGeneratorV8c))
- except EOFError as e:
+ except Exception as e: #pylint: disable=broad-except
+ logging.warning('Failed IDL v8 compilation. Cause: %s', str(e))
latest_error = e
if latest_error:
raise latest_error
+
+
+if __name__ == '__main__':
+ main()
diff --git a/cobalt/browser/browser_module.cc b/cobalt/browser/browser_module.cc
index 79dd071..d95d150 100644
--- a/cobalt/browser/browser_module.cc
+++ b/cobalt/browser/browser_module.cc
@@ -684,6 +684,7 @@
web_module_loaded_.Signal();
options_.persistent_settings->ValidatePersistentSettings();
+ ValidateCacheBackendSettings();
}
bool BrowserModule::WaitForLoad(const base::TimeDelta& timeout) {
@@ -2087,5 +2088,17 @@
web_module_->SetDeepLinkTimestamp(timestamp);
}
+void BrowserModule::ValidateCacheBackendSettings() {
+ DCHECK(network_module_);
+ auto url_request_context = network_module_->url_request_context();
+ auto http_cache = url_request_context->http_transaction_factory()->GetCache();
+ if (!http_cache) return;
+ auto cache_backend = static_cast<disk_cache::CobaltBackendImpl*>(
+ http_cache->GetCurrentBackend());
+ if (cache_backend) {
+ cache_backend->ValidatePersistentSettings();
+ }
+}
+
} // namespace browser
} // namespace cobalt
diff --git a/cobalt/browser/browser_module.h b/cobalt/browser/browser_module.h
index a12551e..b618caa 100644
--- a/cobalt/browser/browser_module.h
+++ b/cobalt/browser/browser_module.h
@@ -473,6 +473,9 @@
scoped_refptr<script::Wrappable> CreateH5vcc(
script::EnvironmentSettings* settings);
+ // Validates the PersistentSettings for cache backend, if in use.
+ void ValidateCacheBackendSettings();
+
// TODO:
// WeakPtr usage here can be avoided if BrowserModule has a thread to
// own where it can ensure that its tasks are all resolved when it is
diff --git a/cobalt/browser/web_module.cc b/cobalt/browser/web_module.cc
index 330de1e..8311b31 100644
--- a/cobalt/browser/web_module.cc
+++ b/cobalt/browser/web_module.cc
@@ -583,7 +583,10 @@
data.can_play_type_handler, memory_info, &mutation_observer_task_manager_,
data.options.dom_settings_options));
DCHECK(web_context_->environment_settings());
- web_context_->environment_settings()->set_base_url(data.initial_url);
+ // From algorithm to setup up a window environment settings object:
+ // https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-window-environment-settings-object
+ // 6. Set settings object's creation URL to creationURL.
+ web_context_->environment_settings()->set_creation_url(data.initial_url);
system_caption_settings_ = new cobalt::dom::captions::SystemCaptionSettings(
web_context_->environment_settings());
@@ -658,11 +661,6 @@
window_weak_ = base::AsWeakPtr(window_.get());
DCHECK(window_weak_);
- dom::DOMSettings* dom_settings =
- base::polymorphic_downcast<dom::DOMSettings*>(
- web_context_->environment_settings());
- dom_settings->set_window(window_);
-
web_context_->global_environment()->CreateGlobalObject(
window_, web_context_->environment_settings());
DCHECK(web_context_->GetWindowOrWorkerGlobalScope()->IsWindow());
diff --git a/cobalt/cache/cache.cc b/cobalt/cache/cache.cc
index e2c568d..867db60 100644
--- a/cobalt/cache/cache.cc
+++ b/cobalt/cache/cache.cc
@@ -22,6 +22,7 @@
#include "base/files/file_util.h"
#include "base/memory/singleton.h"
#include "base/optional.h"
+#include "base/values.h"
#include "cobalt/configuration/configuration.h"
#include "cobalt/extension/javascript_cache.h"
#include "cobalt/persistent_storage/persistent_settings.h"
@@ -31,16 +32,6 @@
namespace {
-base::Optional<uint32_t> GetMaxCacheStorageInBytes(
- disk_cache::ResourceType resource_type) {
- switch (resource_type) {
- case disk_cache::ResourceType::kCompiledScript:
- return 5u << 20; // 5MiB
- default:
- return base::nullopt;
- }
-}
-
base::Optional<uint32_t> GetMinSizeToCacheInBytes(
disk_cache::ResourceType resource_type) {
switch (resource_type) {
@@ -105,6 +96,14 @@
}
}
+void Cache::DeleteAll() {
+ auto* memory_capped_directory =
+ GetMemoryCappedDirectory(disk_cache::ResourceType::kCompiledScript);
+ if (memory_capped_directory) {
+ memory_capped_directory->DeleteAll();
+ }
+}
+
std::unique_ptr<std::vector<uint8_t>> Cache::Retrieve(
disk_cache::ResourceType resource_type, uint32_t key,
std::function<std::unique_ptr<std::vector<uint8_t>>()> generate) {
@@ -173,6 +172,16 @@
return it->second.get();
}
+ // Read in size from persistent storage.
+ auto metadata = disk_cache::kTypeMetadata[resource_type];
+ if (persistent_settings_) {
+ uint32_t bucket_size = static_cast<uint32_t>(
+ persistent_settings_->GetPersistentSettingAsDouble(
+ metadata.directory, metadata.max_size_bytes));
+ disk_cache::kTypeMetadata[resource_type] = {metadata.directory,
+ bucket_size};
+ }
+
auto cache_directory = GetCacheDirectory(resource_type);
auto max_size = GetMaxCacheStorageInBytes(resource_type);
if (!cache_directory || !max_size) {
@@ -186,6 +195,32 @@
return memory_capped_directories_[resource_type].get();
}
+void Cache::Resize(disk_cache::ResourceType resource_type, uint32_t bytes) {
+ if (resource_type != disk_cache::ResourceType::kCompiledScript) return;
+ if (bytes == disk_cache::kTypeMetadata[resource_type].max_size_bytes) return;
+
+ if (persistent_settings_) {
+ persistent_settings_->SetPersistentSetting(
+ disk_cache::kTypeMetadata[resource_type].directory,
+ std::make_unique<base::Value>(static_cast<double>(bytes)));
+ }
+ disk_cache::kTypeMetadata[resource_type].max_size_bytes = bytes;
+ auto* memory_capped_directory = GetMemoryCappedDirectory(resource_type);
+ if (memory_capped_directory) {
+ memory_capped_directory->Resize(bytes);
+ }
+}
+
+base::Optional<uint32_t> Cache::GetMaxCacheStorageInBytes(
+ disk_cache::ResourceType resource_type) {
+ switch (resource_type) {
+ case disk_cache::ResourceType::kCompiledScript:
+ return disk_cache::kTypeMetadata[resource_type].max_size_bytes;
+ default:
+ return base::nullopt;
+ }
+}
+
base::WaitableEvent* Cache::GetWaitableEvent(
disk_cache::ResourceType resource_type, uint32_t key) {
base::AutoLock auto_lock(lock_);
diff --git a/cobalt/cache/cache.h b/cobalt/cache/cache.h
index 4f9e4fe..fd106f1 100644
--- a/cobalt/cache/cache.h
+++ b/cobalt/cache/cache.h
@@ -42,9 +42,13 @@
public:
static Cache* GetInstance();
void Delete(disk_cache::ResourceType resource_type, uint32_t key);
+ void DeleteAll();
std::unique_ptr<std::vector<uint8_t>> Retrieve(
disk_cache::ResourceType resource_type, uint32_t key,
std::function<std::unique_ptr<std::vector<uint8_t>>()> generate);
+ void Resize(disk_cache::ResourceType resource_type, uint32_t bytes);
+ base::Optional<uint32_t> GetMaxCacheStorageInBytes(
+ disk_cache::ResourceType resource_type);
void set_enabled(bool enabled);
@@ -74,7 +78,7 @@
pending_;
bool enabled_;
- persistent_storage::PersistentSettings* persistent_settings_;
+ persistent_storage::PersistentSettings* persistent_settings_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(Cache);
}; // class Cache
diff --git a/cobalt/cache/memory_capped_directory.cc b/cobalt/cache/memory_capped_directory.cc
index 2ac9cb1..3dc064c 100644
--- a/cobalt/cache/memory_capped_directory.cc
+++ b/cobalt/cache/memory_capped_directory.cc
@@ -91,6 +91,17 @@
}
}
+void MemoryCappedDirectory::DeleteAll() {
+ base::AutoLock auto_lock(lock_);
+ // Recursively delete the contents of the directory_path_.
+ base::DeleteFile(directory_path_, true);
+ // Re-create the directory_path_ which will now be empty.
+ SbDirectoryCreate(directory_path_.value().c_str());
+ file_info_heap_.clear();
+ file_sizes_.clear();
+ size_ = 0;
+}
+
std::unique_ptr<std::vector<uint8_t>> MemoryCappedDirectory::Retrieve(
uint32_t key) {
auto file_path = GetFilePath(key);
@@ -131,6 +142,14 @@
file_sizes_[file_path] = new_entry_size;
}
+void MemoryCappedDirectory::Resize(uint32_t size) {
+ if (max_size_ > size) {
+ uint32_t space_to_be_freed = max_size_ - size;
+ EnsureEnoughSpace(space_to_be_freed);
+ }
+ max_size_ = size;
+}
+
MemoryCappedDirectory::MemoryCappedDirectory(
const base::FilePath& directory_path, uint32_t max_size)
: directory_path_(directory_path), max_size_(max_size), size_(0u) {}
diff --git a/cobalt/cache/memory_capped_directory.h b/cobalt/cache/memory_capped_directory.h
index d44681e..941ab19 100644
--- a/cobalt/cache/memory_capped_directory.h
+++ b/cobalt/cache/memory_capped_directory.h
@@ -51,8 +51,10 @@
static std::unique_ptr<MemoryCappedDirectory> Create(
const base::FilePath& directory_path, uint32_t max_size);
void Delete(uint32_t key);
+ void DeleteAll();
std::unique_ptr<std::vector<uint8_t>> Retrieve(uint32_t key);
void Store(uint32_t key, const std::vector<uint8_t>& data);
+ void Resize(uint32_t size);
private:
MemoryCappedDirectory(const base::FilePath& directory_path,
diff --git a/cobalt/dom/dom_settings.cc b/cobalt/dom/dom_settings.cc
index 60a123c..6c9e66d 100644
--- a/cobalt/dom/dom_settings.cc
+++ b/cobalt/dom/dom_settings.cc
@@ -14,9 +14,13 @@
#include "cobalt/dom/dom_settings.h"
+#include "base/logging.h"
#include "cobalt/dom/document.h"
+#include "cobalt/dom/location.h"
#include "cobalt/dom/window.h"
+#include "cobalt/web/context.h"
#include "cobalt/web/url_utils.h"
+#include "cobalt/web/window_or_worker_global_scope.h"
namespace cobalt {
namespace dom {
@@ -38,11 +42,20 @@
DOMSettings::~DOMSettings() {}
-void DOMSettings::set_window(const scoped_refptr<Window>& window) {
- window_ = window;
- set_base_url(window->document()->url_as_gurl());
+const GURL& DOMSettings::base_url() const {
+ // From algorithm for to setup up a window environment settings object:
+ // https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-window-environment-settings-object
+ // 3. Let settings object be a new environment settings object whose
+ // algorithms are defined as follows:
+ // The API base URL
+ // Return the current base URL of window's associated Document.
+ return window()->document()->url_as_gurl();
}
-scoped_refptr<Window> DOMSettings::window() const { return window_; }
+
+scoped_refptr<Window> DOMSettings::window() const {
+ DCHECK(context()->GetWindowOrWorkerGlobalScope()->IsWindow());
+ return context()->GetWindowOrWorkerGlobalScope()->AsWindow();
+}
loader::Origin DOMSettings::document_origin() const {
return window()->document()->location()->GetOriginAsObject();
diff --git a/cobalt/dom/dom_settings.h b/cobalt/dom/dom_settings.h
index 0c81c39..8eb437a 100644
--- a/cobalt/dom/dom_settings.h
+++ b/cobalt/dom/dom_settings.h
@@ -66,7 +66,6 @@
return microphone_options_;
}
- void set_window(const scoped_refptr<Window>& window);
scoped_refptr<Window> window() const;
MediaSourceRegistry* media_source_registry() const {
@@ -89,10 +88,13 @@
// Return's document's origin.
loader::Origin document_origin() const;
+ // From: script::EnvironmentSettings
+ //
+ const GURL& base_url() const override;
+
private:
const int max_dom_element_depth_;
const speech::Microphone::Options microphone_options_;
- scoped_refptr<Window> window_;
MediaSourceRegistry* media_source_registry_;
media::CanPlayTypeHandler* can_play_type_handler_;
const media::DecoderBufferMemoryInfo* decoder_buffer_memory_info_;
diff --git a/cobalt/dom/global_stats.cc b/cobalt/dom/global_stats.cc
index eccae4c..66acba6 100644
--- a/cobalt/dom/global_stats.cc
+++ b/cobalt/dom/global_stats.cc
@@ -47,6 +47,13 @@
GlobalStats::~GlobalStats() {}
bool GlobalStats::CheckNoLeaks() {
+ DCHECK(num_attrs_ == 0);
+ DCHECK(num_dom_string_maps_ == 0);
+ DCHECK(num_dom_token_lists_ == 0);
+ DCHECK(num_html_collections_ == 0);
+ DCHECK(num_named_node_maps_ == 0);
+ DCHECK(num_nodes_ == 0);
+ DCHECK(num_node_lists_ == 0);
return web::GlobalStats::GetInstance()->CheckNoLeaks() &&
xhr::GlobalStats::GetInstance()->CheckNoLeaks() && num_attrs_ == 0 &&
num_dom_string_maps_ == 0 && num_dom_token_lists_ == 0 &&
diff --git a/cobalt/dom/html_media_element.cc b/cobalt/dom/html_media_element.cc
index 91bb221..099559b 100644
--- a/cobalt/dom/html_media_element.cc
+++ b/cobalt/dom/html_media_element.cc
@@ -632,7 +632,8 @@
}
void HTMLMediaElement::ScheduleEvent(const scoped_refptr<web::Event>& event) {
- TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleEvent()");
+ TRACE_EVENT1("cobalt::dom", "HTMLMediaElement::ScheduleEvent()", "event",
+ TRACE_STR_COPY(event->type().c_str()));
MLOG() << "Schedule event " << event->type() << ".";
event_queue_.Enqueue(event);
}
diff --git a/cobalt/dom/local_storage_database.h b/cobalt/dom/local_storage_database.h
index 4541001..ea8dfd3 100644
--- a/cobalt/dom/local_storage_database.h
+++ b/cobalt/dom/local_storage_database.h
@@ -35,7 +35,7 @@
typedef base::Callback<void(std::unique_ptr<StorageArea::StorageMap>)>
ReadCompletionCallback;
- explicit LocalStorageDatabase(storage::StorageManager* storage);
+ explicit LocalStorageDatabase(storage::StorageManager* storage = nullptr);
// Load the LocalStorage from the Storage Manager, and extract
// all key/values for the given origin. Calls callback and transfers ownership
diff --git a/cobalt/dom/on_screen_keyboard_test.cc b/cobalt/dom/on_screen_keyboard_test.cc
index 8a7131f..4ab1e6f 100644
--- a/cobalt/dom/on_screen_keyboard_test.cc
+++ b/cobalt/dom/on_screen_keyboard_test.cc
@@ -172,8 +172,8 @@
public:
OnScreenKeyboardTest()
: on_screen_keyboard_bridge_(new OnScreenKeyboardMockBridge()) {
- set_stub_window(new testing::StubWindow(DOMSettings::Options(),
- on_screen_keyboard_bridge_.get()));
+ stub_window()->set_on_screen_keyboard_bridge(
+ on_screen_keyboard_bridge_.get());
on_screen_keyboard_bridge_->window_ = window();
}
diff --git a/cobalt/dom/testing/BUILD.gn b/cobalt/dom/testing/BUILD.gn
index 01add77..49453ae 100644
--- a/cobalt/dom/testing/BUILD.gn
+++ b/cobalt/dom/testing/BUILD.gn
@@ -37,6 +37,7 @@
"//cobalt/dom",
"//cobalt/dom_parser",
"//cobalt/loader",
+ "//cobalt/network",
"//cobalt/script",
"//cobalt/web",
"//cobalt/web:dom_exception",
diff --git a/cobalt/dom/testing/stub_window.h b/cobalt/dom/testing/stub_window.h
index fa17f5c..6651633 100644
--- a/cobalt/dom/testing/stub_window.h
+++ b/cobalt/dom/testing/stub_window.h
@@ -31,8 +31,8 @@
#include "cobalt/dom/testing/stub_environment_settings.h"
#include "cobalt/dom/window.h"
#include "cobalt/dom_parser/parser.h"
-#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/loader/loader_factory.h"
+#include "cobalt/network/network_module.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
#include "cobalt/web/context.h"
@@ -49,42 +49,85 @@
// stubbed out.
class StubWindow {
public:
- explicit StubWindow(
- const DOMSettings::Options& options = DOMSettings::Options(),
- OnScreenKeyboardBridge* on_screen_keyboard_bridge = nullptr,
- web::testing::StubWebContext* stub_web_context =
- new web::testing::StubWebContext())
- : css_parser_(css_parser::Parser::Create()),
- dom_parser_(
- new dom_parser::Parser(base::Bind(&StubLoadCompleteCallback))),
- local_storage_database_(NULL),
- dom_stat_tracker_(new dom::DomStatTracker("StubWindow")) {
- web_context_.reset(stub_web_context);
- web_context_->setup_environment_settings(
- new dom::testing::StubEnvironmentSettings(options));
- web_context_->environment_settings()->set_base_url(GURL("about:blank"));
- web_context_->set_fetcher_factory(new loader::FetcherFactory(NULL));
+ StubWindow() {}
+ virtual ~StubWindow() {
+ if (window_) {
+ global_environment()->SetReportEvalCallback(base::Closure());
+ global_environment()->SetReportErrorCallback(
+ script::GlobalEnvironment::ReportErrorCallback());
+ window_->DispatchEvent(new web::Event(base::Tokens::unload()));
+ window_->DestroyTimers();
+ window_.reset();
+ }
+ }
+
+ void set_options(const DOMSettings::Options& options) { options_ = options; }
+
+ web::testing::StubWebContext* web_context() {
+ if (!web_context_) InitializeWebContext();
+ return web_context_.get();
+ }
+
+ scoped_refptr<dom::Window> window() {
+ if (!window_) InitializeWindow();
+ return window_;
+ }
+ scoped_refptr<script::GlobalEnvironment> global_environment() {
+ // The Window has to be set as the global object for the global environment
+ // to be valid.
+ if (!window_) InitializeWindow();
+ return web_context()->global_environment();
+ }
+ css_parser::Parser* css_parser() { return css_parser_.get(); }
+ web::EnvironmentSettings* environment_settings() {
+ // The Window has to be set as the global object for the environment
+ // settings object to be valid.
+ if (!window_) InitializeWindow();
+ return web_context()->environment_settings();
+ }
+
+ void set_on_screen_keyboard_bridge(
+ OnScreenKeyboardBridge* on_screen_keyboard_bridge) {
+ on_screen_keyboard_bridge_ = on_screen_keyboard_bridge;
+ }
+
+ private:
+ static void StubLoadCompleteCallback(
+ const base::Optional<std::string>& error) {}
+
+ void InitializeWebContext() {
+ web_context_.reset(new web::testing::StubWebContext());
+ web_context()->setup_environment_settings(
+ new dom::testing::StubEnvironmentSettings(options_));
+ web_context()->environment_settings()->set_creation_url(
+ GURL("about:blank"));
+ }
+
+ void InitializeWindow() {
loader_factory_.reset(new loader::LoaderFactory(
- "Test", web_context_->fetcher_factory(), NULL,
- web_context_->environment_settings()->debugger_hooks(), 0,
+ "Test", web_context()->fetcher_factory(), NULL,
+ web_context()->environment_settings()->debugger_hooks(), 0,
base::ThreadPriority::DEFAULT));
system_caption_settings_ = new cobalt::dom::captions::SystemCaptionSettings(
- web_context_->environment_settings());
-
+ web_context()->environment_settings());
+ css_parser_.reset(css_parser::Parser::Create().release());
+ dom_parser_.reset(
+ new dom_parser::Parser(base::Bind(&StubLoadCompleteCallback)));
+ dom_stat_tracker_.reset(new dom::DomStatTracker("StubWindow"));
window_ = new dom::Window(
- web_context_->environment_settings(), cssom::ViewportSize(1920, 1080),
+ web_context()->environment_settings(), cssom::ViewportSize(1920, 1080),
base::kApplicationStateStarted, css_parser_.get(), dom_parser_.get(),
- web_context_->fetcher_factory(), loader_factory_.get(), nullptr,
+ web_context()->fetcher_factory(), loader_factory_.get(), nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, &local_storage_database_,
nullptr, nullptr, nullptr, nullptr,
- global_environment()->script_value_factory(), nullptr,
+ web_context()->global_environment()->script_value_factory(), nullptr,
dom_stat_tracker_.get(), "en", base::Callback<void(const GURL&)>(),
base::Bind(&StubLoadCompleteCallback), nullptr,
- network_bridge::PostSender(), csp::kCSPRequired,
+ network_bridge::PostSender(), csp::kCSPOptional,
web::kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
base::Closure() /* ran_animation_frame_callbacks */,
dom::Window::CloseCallback() /* window_close */,
- base::Closure() /* window_minimize */, on_screen_keyboard_bridge,
+ base::Closure() /* window_minimize */, on_screen_keyboard_bridge_,
nullptr /* camera_3d */, dom::Window::OnStartDispatchEventCallback(),
dom::Window::OnStopDispatchEventCallback(),
dom::ScreenshotManager::ProvideScreenshotFunctionCallback(),
@@ -96,28 +139,12 @@
dom::Window::CacheCallback() /* splash_screen_cache_callback */,
system_caption_settings_ /* captions */
);
- base::polymorphic_downcast<dom::DOMSettings*>(
- web_context_->environment_settings())
- ->set_window(window_);
global_environment()->CreateGlobalObject(
- window_, web_context_->environment_settings());
- }
- virtual ~StubWindow() { window_->DestroyTimers(); }
-
- web::testing::StubWebContext* web_context() { return web_context_.get(); }
- scoped_refptr<dom::Window> window() { return window_; }
- scoped_refptr<script::GlobalEnvironment> global_environment() {
- return web_context_->global_environment();
- }
- css_parser::Parser* css_parser() { return css_parser_.get(); }
- web::EnvironmentSettings* environment_settings() {
- return web_context_->environment_settings();
+ window_, web_context()->environment_settings());
}
- private:
- static void StubLoadCompleteCallback(
- const base::Optional<std::string>& error) {}
-
+ OnScreenKeyboardBridge* on_screen_keyboard_bridge_ = nullptr;
+ DOMSettings::Options options_;
std::unique_ptr<css_parser::Parser> css_parser_;
std::unique_ptr<dom_parser::Parser> dom_parser_;
std::unique_ptr<loader::LoaderFactory> loader_factory_;
diff --git a/cobalt/dom/testing/test_with_javascript.h b/cobalt/dom/testing/test_with_javascript.h
index f7a281d..58df232 100644
--- a/cobalt/dom/testing/test_with_javascript.h
+++ b/cobalt/dom/testing/test_with_javascript.h
@@ -36,40 +36,28 @@
// Helper class for running tests in a Window JavaScript context.
class TestWithJavaScript : public ::testing::Test {
public:
- TestWithJavaScript() {}
+ TestWithJavaScript() { stub_window_.reset(new StubWindow()); }
~TestWithJavaScript() {
- if (stub_window_) {
- stub_window_->global_environment()->SetReportEvalCallback(
- base::Closure());
- stub_window_->global_environment()->SetReportErrorCallback(
- script::GlobalEnvironment::ReportErrorCallback());
- DCHECK(window());
- window()->DispatchEvent(new web::Event(base::Tokens::unload()));
- stub_window_.reset();
- }
+ stub_window_.reset();
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
}
- void set_stub_window(StubWindow* stub_window) {
- stub_window_.reset(stub_window);
+ StubWindow* stub_window() { return stub_window_.get(); }
+ web::testing::StubWebContext* stub_web_context() {
+ return stub_window_->web_context();
}
- Window* window() {
- if (!stub_window_) stub_window_.reset(new StubWindow());
- return stub_window_->window().get();
- }
+ Window* window() { return stub_window_->window().get(); }
bool EvaluateScript(const std::string& js_code, std::string* result) {
- if (!stub_window_) stub_window_.reset(new StubWindow());
- DCHECK(stub_window_->global_environment());
+ DCHECK(global_environment());
scoped_refptr<script::SourceCode> source_code =
script::SourceCode::CreateSourceCode(
js_code, base::SourceLocation(__FILE__, __LINE__, 1));
- stub_window_->global_environment()->EnableEval();
- stub_window_->global_environment()->SetReportEvalCallback(base::Closure());
- bool succeeded =
- stub_window_->global_environment()->EvaluateScript(source_code, result);
+ global_environment()->EnableEval();
+ global_environment()->SetReportEvalCallback(base::Closure());
+ bool succeeded = global_environment()->EvaluateScript(source_code, result);
return succeeded;
}
diff --git a/cobalt/dom/user_agent_data_test.cc b/cobalt/dom/user_agent_data_test.cc
index 2fd56e4..dc3c350 100644
--- a/cobalt/dom/user_agent_data_test.cc
+++ b/cobalt/dom/user_agent_data_test.cc
@@ -36,13 +36,7 @@
UserAgentDataTest() {
InitializeEmptyPlatformInfo();
- web::testing::StubWebContext* stub_web_context =
- new web::testing::StubWebContext();
- stub_web_context->set_platform_info(platform_info_.get());
- testing::StubWindow* stub_window = new testing::StubWindow(
- DOMSettings::Options(), nullptr /* on_screen_keyboard_bridge */,
- stub_web_context);
- set_stub_window(stub_window);
+ stub_web_context()->set_platform_info(platform_info_.get());
// Inject H5vcc interface to make it also accessible via Window
h5vcc::H5vcc::Settings h5vcc_settings;
diff --git a/cobalt/dom_parser/html_decoder_test.cc b/cobalt/dom_parser/html_decoder_test.cc
index 78dd0da..e92f571 100644
--- a/cobalt/dom_parser/html_decoder_test.cc
+++ b/cobalt/dom_parser/html_decoder_test.cc
@@ -71,8 +71,7 @@
};
HTMLDecoderTest::HTMLDecoderTest()
- : fetcher_factory_(NULL /* network_module */),
- loader_factory_("Test" /* name */, &fetcher_factory_,
+ : loader_factory_("Test" /* name */, &fetcher_factory_,
NULL /* ResourceProvider */, null_debugger_hooks_,
0 /* encoded_image_cache_capacity */,
base::ThreadPriority::DEFAULT),
diff --git a/cobalt/h5vcc/h5vcc_crash_log.idl b/cobalt/h5vcc/h5vcc_crash_log.idl
index f551cd7..45f6df3 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.idl
+++ b/cobalt/h5vcc/h5vcc_crash_log.idl
@@ -32,15 +32,15 @@
// Returns true if Watchdog client was registered.
// name, Watchdog client to register.
// description, information on the Watchdog client.
- // monitor_state, application state to continue monitoring client up to.
+ // watchdog_state, application state to continue monitoring client up to.
// Inclusive.
// time_interval, maximum number of microseconds allowed between pings
- // before triggering a Watchdog violation.
+ // before triggering a Watchdog violation. Min value of 1000000.
// time_wait, number of microseconds to initially wait before Watchdog
// violations can be triggered. Reapplies after client resumes from idle
// state due to application state changes.
- // replace, behavior with previously registered Watchdog clients of the
- // same name.
+ // watchdog_replace, behavior with previously registered Watchdog clients
+ // of the same name.
boolean register(DOMString name, DOMString description,
WatchdogState watchdog_state, long long time_interval,
long long time_wait, WatchdogReplace watchdog_replace);
@@ -51,7 +51,7 @@
// Returns true if Watchdog client was pinged. Name determines the Watchdog
// client to ping with ping_info which can either be empty or contain relevant
- // metadata.
+ // metadata. ping_info has a max length of 1024.
boolean ping(DOMString name, DOMString ping_info);
// Returns a json string containing the Watchdog violations since the last
diff --git a/cobalt/h5vcc/h5vcc_storage.cc b/cobalt/h5vcc/h5vcc_storage.cc
index dbb6b3b..b5c3af0 100644
--- a/cobalt/h5vcc/h5vcc_storage.cc
+++ b/cobalt/h5vcc/h5vcc_storage.cc
@@ -15,6 +15,7 @@
#include <algorithm>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include "base/files/file_util.h"
@@ -23,6 +24,7 @@
#include "cobalt/h5vcc/h5vcc_storage.h"
#include "cobalt/persistent_storage/persistent_settings.h"
#include "cobalt/storage/storage_manager.h"
+#include "net/base/completion_once_callback.h"
#include "net/disk_cache/cobalt/cobalt_backend_impl.h"
#include "net/disk_cache/cobalt/resource_type.h"
#include "net/http/http_cache.h"
@@ -68,6 +70,10 @@
return response;
}
+void ClearCacheHelper(disk_cache::Backend* backend) {
+ backend->DoomAllEntries(base::DoNothing());
+}
+
} // namespace
H5vccStorage::H5vccStorage(
@@ -76,6 +82,7 @@
: network_module_(network_module),
persistent_settings_(persistent_settings) {
http_cache_ = nullptr;
+ cache_backend_ = nullptr;
if (network_module == nullptr) {
return;
}
@@ -254,87 +261,55 @@
quota_total, max_quota_size));
}
- // Write to persistent storage with the new quota values.
- // Static cast value to double since base::Value cannot be a long.
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[disk_cache::kOther].directory,
- std::make_unique<base::Value>(static_cast<double>(quota.other())));
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[disk_cache::kHTML].directory,
- std::make_unique<base::Value>(static_cast<double>(quota.html())));
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[disk_cache::kCSS].directory,
- std::make_unique<base::Value>(static_cast<double>(quota.css())));
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[disk_cache::kImage].directory,
- std::make_unique<base::Value>(static_cast<double>(quota.image())));
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[disk_cache::kFont].directory,
- std::make_unique<base::Value>(static_cast<double>(quota.font())));
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[disk_cache::kSplashScreen].directory,
- std::make_unique<base::Value>(static_cast<double>(quota.splash())));
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[disk_cache::kUncompiledScript].directory,
- std::make_unique<base::Value>(
- static_cast<double>(quota.uncompiled_js())));
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[disk_cache::kCompiledScript].directory,
- std::make_unique<base::Value>(static_cast<double>(quota.compiled_js())));
+ ValidatedCacheBackend();
+ // Write to persistent storage with the new quota values.
+ SetAndSaveQuotaForBackend(disk_cache::kOther,
+ static_cast<uint32_t>(quota.other()));
+ SetAndSaveQuotaForBackend(disk_cache::kHTML,
+ static_cast<uint32_t>(quota.html()));
+ SetAndSaveQuotaForBackend(disk_cache::kCSS,
+ static_cast<uint32_t>(quota.css()));
+ SetAndSaveQuotaForBackend(disk_cache::kImage,
+ static_cast<uint32_t>(quota.image()));
+ SetAndSaveQuotaForBackend(disk_cache::kFont,
+ static_cast<uint32_t>(quota.font()));
+ SetAndSaveQuotaForBackend(disk_cache::kSplashScreen,
+ static_cast<uint32_t>(quota.splash()));
+ SetAndSaveQuotaForBackend(disk_cache::kUncompiledScript,
+ static_cast<uint32_t>(quota.uncompiled_js()));
+ SetAndSaveQuotaForBackend(disk_cache::kCompiledScript,
+ static_cast<uint32_t>(quota.compiled_js()));
return SetQuotaResponse("", true);
}
+void H5vccStorage::SetAndSaveQuotaForBackend(disk_cache::ResourceType type,
+ uint32_t bytes) {
+ if (cache_backend_) {
+ cache_backend_->UpdateSizes(type, bytes);
+ }
+ cobalt::cache::Cache::GetInstance()->Resize(type, bytes);
+}
+
H5vccStorageResourceTypeQuotaBytesDictionary H5vccStorage::GetQuota() {
// Return persistent storage quota values.
H5vccStorageResourceTypeQuotaBytesDictionary quota;
+ if (!ValidatedCacheBackend()) {
+ return quota;
+ }
- auto other_meta_data = disk_cache::kTypeMetadata[disk_cache::kOther];
- quota.set_other(
- static_cast<uint32>(persistent_settings_->GetPersistentSettingAsDouble(
- other_meta_data.directory,
- other_meta_data.max_size_mb * 1024 * 1024)));
-
- auto html_meta_data = disk_cache::kTypeMetadata[disk_cache::kHTML];
- quota.set_html(
- static_cast<uint32>(persistent_settings_->GetPersistentSettingAsDouble(
- html_meta_data.directory, html_meta_data.max_size_mb * 1024 * 1024)));
-
- auto css_meta_data = disk_cache::kTypeMetadata[disk_cache::kCSS];
- quota.set_css(
- static_cast<uint32>(persistent_settings_->GetPersistentSettingAsDouble(
- css_meta_data.directory, css_meta_data.max_size_mb * 1024 * 1024)));
-
- auto image_meta_data = disk_cache::kTypeMetadata[disk_cache::kImage];
- quota.set_image(
- static_cast<uint32>(persistent_settings_->GetPersistentSettingAsDouble(
- image_meta_data.directory,
- image_meta_data.max_size_mb * 1024 * 1024)));
-
- auto font_meta_data = disk_cache::kTypeMetadata[disk_cache::kFont];
- quota.set_font(
- static_cast<uint32>(persistent_settings_->GetPersistentSettingAsDouble(
- font_meta_data.directory, font_meta_data.max_size_mb * 1024 * 1024)));
-
- auto splash_meta_data = disk_cache::kTypeMetadata[disk_cache::kSplashScreen];
- quota.set_splash(
- static_cast<uint32>(persistent_settings_->GetPersistentSettingAsDouble(
- splash_meta_data.directory,
- splash_meta_data.max_size_mb * 1024 * 1024)));
-
- auto uncompiled_meta_data =
- disk_cache::kTypeMetadata[disk_cache::kUncompiledScript];
+ quota.set_other(cache_backend_->GetQuota(disk_cache::kOther));
+ quota.set_html(cache_backend_->GetQuota(disk_cache::kHTML));
+ quota.set_css(cache_backend_->GetQuota(disk_cache::kCSS));
+ quota.set_image(cache_backend_->GetQuota(disk_cache::kImage));
+ quota.set_font(cache_backend_->GetQuota(disk_cache::kFont));
+ quota.set_splash(cache_backend_->GetQuota(disk_cache::kSplashScreen));
quota.set_uncompiled_js(
- static_cast<uint32>(persistent_settings_->GetPersistentSettingAsDouble(
- uncompiled_meta_data.directory,
- uncompiled_meta_data.max_size_mb * 1024 * 1024)));
-
- auto compiled_meta_data =
- disk_cache::kTypeMetadata[disk_cache::kCompiledScript];
+ cache_backend_->GetQuota(disk_cache::kUncompiledScript));
quota.set_compiled_js(
- static_cast<uint32>(persistent_settings_->GetPersistentSettingAsDouble(
- compiled_meta_data.directory,
- compiled_meta_data.max_size_mb * 1024 * 1024)));
+ cobalt::cache::Cache::GetInstance()
+ ->GetMaxCacheStorageInBytes(disk_cache::kCompiledScript)
+ .value());
// TODO(b/235529738): Calculate correct max_quota_size that subtracts
// non-cache memory used in the kSbSystemPathCacheDirectory.
@@ -372,5 +347,26 @@
}
}
+void H5vccStorage::ClearCache() {
+ if (ValidatedCacheBackend()) {
+ network_module_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&ClearCacheHelper, base::Unretained(cache_backend_)));
+ }
+ cobalt::cache::Cache::GetInstance()->DeleteAll();
+}
+
+bool H5vccStorage::ValidatedCacheBackend() {
+ if (!http_cache_) {
+ return false;
+ }
+ if (cache_backend_) {
+ return true;
+ }
+ cache_backend_ = static_cast<disk_cache::CobaltBackendImpl*>(
+ http_cache_->GetCurrentBackend());
+ return cache_backend_ != nullptr;
+}
+
} // namespace h5vcc
} // namespace cobalt
diff --git a/cobalt/h5vcc/h5vcc_storage.h b/cobalt/h5vcc/h5vcc_storage.h
index ff96351..54b06c8 100644
--- a/cobalt/h5vcc/h5vcc_storage.h
+++ b/cobalt/h5vcc/h5vcc_storage.h
@@ -26,6 +26,8 @@
#include "cobalt/network/network_module.h"
#include "cobalt/persistent_storage/persistent_settings.h"
#include "cobalt/script/wrappable.h"
+#include "net/disk_cache/cobalt/cobalt_backend_impl.h"
+#include "net/disk_cache/cobalt/resource_type.h"
#include "net/http/http_cache.h"
namespace cobalt {
@@ -56,11 +58,14 @@
// Set Quota bytes per disk_cache::ResourceType.
H5vccStorageSetQuotaResponse SetQuota(
H5vccStorageResourceTypeQuotaBytesDictionary quota);
+ void SetAndSaveQuotaForBackend(disk_cache::ResourceType type, uint32_t bytes);
void EnableCache();
void DisableCache();
+ void ClearCache();
+
DEFINE_WRAPPABLE_TYPE(H5vccStorage);
private:
@@ -69,6 +74,9 @@
persistent_storage::PersistentSettings* persistent_settings_;
net::HttpCache* http_cache_;
+ disk_cache::CobaltBackendImpl* cache_backend_;
+
+ bool ValidatedCacheBackend();
DISALLOW_COPY_AND_ASSIGN(H5vccStorage);
};
diff --git a/cobalt/h5vcc/h5vcc_storage.idl b/cobalt/h5vcc/h5vcc_storage.idl
index 93a6d37..f1a15d2 100644
--- a/cobalt/h5vcc/h5vcc_storage.idl
+++ b/cobalt/h5vcc/h5vcc_storage.idl
@@ -27,4 +27,6 @@
void enableCache();
void disableCache();
+
+ void clearCache();
};
diff --git a/cobalt/loader/fetcher_factory.cc b/cobalt/loader/fetcher_factory.cc
index bded32c..ce60177 100644
--- a/cobalt/loader/fetcher_factory.cc
+++ b/cobalt/loader/fetcher_factory.cc
@@ -64,20 +64,12 @@
} // namespace
FetcherFactory::FetcherFactory(network::NetworkModule* network_module)
- : file_thread_("File"),
- network_module_(network_module),
- read_cache_callback_() {
- file_thread_.Start();
-}
+ : network_module_(network_module) {}
-FetcherFactory::FetcherFactory(network::NetworkModule* network_module,
- const base::FilePath& extra_search_dir)
- : file_thread_("File"),
- network_module_(network_module),
- extra_search_dir_(extra_search_dir),
- read_cache_callback_() {
- file_thread_.Start();
-}
+FetcherFactory::FetcherFactory(
+ network::NetworkModule* network_module,
+ const BlobFetcher::ResolverCallback& blob_resolver)
+ : network_module_(network_module), blob_resolver_(blob_resolver) {}
FetcherFactory::FetcherFactory(
network::NetworkModule* network_module,
@@ -85,13 +77,10 @@
const BlobFetcher::ResolverCallback& blob_resolver,
const base::Callback<int(const std::string&, std::unique_ptr<char[]>*)>&
read_cache_callback)
- : file_thread_("File"),
- network_module_(network_module),
+ : network_module_(network_module),
extra_search_dir_(extra_search_dir),
blob_resolver_(blob_resolver),
- read_cache_callback_(read_cache_callback) {
- file_thread_.Start();
-}
+ read_cache_callback_(read_cache_callback) {}
std::unique_ptr<Fetcher> FetcherFactory::CreateFetcher(
const GURL& url, const disk_cache::ResourceType type,
@@ -150,6 +139,9 @@
}
FileFetcher::Options options;
+ if (!file_thread_.IsRunning()) {
+ file_thread_.Start();
+ }
options.message_loop_proxy = file_thread_.task_runner();
options.extra_search_dir = extra_search_dir_;
return std::unique_ptr<Fetcher>(
diff --git a/cobalt/loader/fetcher_factory.h b/cobalt/loader/fetcher_factory.h
index 2630031..4638e10 100644
--- a/cobalt/loader/fetcher_factory.h
+++ b/cobalt/loader/fetcher_factory.h
@@ -36,9 +36,10 @@
class FetcherFactory {
public:
+ FetcherFactory() {}
explicit FetcherFactory(network::NetworkModule* network_module);
FetcherFactory(network::NetworkModule* network_module,
- const base::FilePath& extra_search_dir);
+ const BlobFetcher::ResolverCallback& blob_resolver);
FetcherFactory(
network::NetworkModule* network_module,
const base::FilePath& extra_search_dir,
@@ -60,8 +61,8 @@
network::NetworkModule* network_module() const { return network_module_; }
private:
- base::Thread file_thread_;
- network::NetworkModule* network_module_;
+ base::Thread file_thread_{"File"};
+ network::NetworkModule* network_module_ = nullptr;
base::FilePath extra_search_dir_;
BlobFetcher::ResolverCallback blob_resolver_;
base::Callback<int(const std::string&, std::unique_ptr<char[]>*)>
diff --git a/cobalt/loader/fetcher_factory_test.cc b/cobalt/loader/fetcher_factory_test.cc
index ac16d44..9767e69 100644
--- a/cobalt/loader/fetcher_factory_test.cc
+++ b/cobalt/loader/fetcher_factory_test.cc
@@ -27,8 +27,7 @@
class StubFetcherHandler : public Fetcher::Handler {
public:
- explicit StubFetcherHandler(base::RunLoop* run_loop)
- : run_loop_(run_loop), fetcher_(NULL) {}
+ explicit StubFetcherHandler(base::RunLoop* run_loop) : run_loop_(run_loop) {}
// From Fetcher::Handler.
void OnReceived(Fetcher* fetcher, const char* data, size_t size) override {
@@ -55,7 +54,7 @@
private:
void CheckSameFetcher(Fetcher* fetcher) {
EXPECT_TRUE(fetcher);
- if (fetcher_ == NULL) {
+ if (fetcher_ == nullptr) {
fetcher_ = fetcher;
return;
}
@@ -63,7 +62,7 @@
}
base::RunLoop* run_loop_;
- Fetcher* fetcher_;
+ Fetcher* fetcher_ = nullptr;
base::Optional<std::string> error_message_;
};
@@ -71,9 +70,7 @@
class FetcherFactoryTest : public ::testing::Test {
protected:
- FetcherFactoryTest()
- : message_loop_(base::MessageLoop::TYPE_DEFAULT),
- fetcher_factory_(NULL) {}
+ FetcherFactoryTest() : message_loop_(base::MessageLoop::TYPE_DEFAULT) {}
~FetcherFactoryTest() override {}
base::MessageLoop message_loop_;
diff --git a/cobalt/media/base/sbplayer_pipeline.cc b/cobalt/media/base/sbplayer_pipeline.cc
index b9c8f65..d64ce70 100644
--- a/cobalt/media/base/sbplayer_pipeline.cc
+++ b/cobalt/media/base/sbplayer_pipeline.cc
@@ -1484,12 +1484,10 @@
LOG(INFO) << "SbPlayerPipeline::ResumeTask failed to create a valid "
"StarboardPlayer - "
<< time_information << " \'" << error_message << "\'";
- // TODO: Determine if CallSeekCB() may be used here, as |seek_cb_| may be
- // available if the app is suspended before a seek is completed.
- CallSeekCB(::media::DECODER_ERROR_NOT_SUPPORTED,
- "SbPlayerPipeline::ResumeTask failed to create a valid "
- "StarboardPlayer - " +
- time_information + " \'" + error_message + "\'");
+ CallErrorCB(::media::DECODER_ERROR_NOT_SUPPORTED,
+ "SbPlayerPipeline::ResumeTask failed to create a valid "
+ "StarboardPlayer - " +
+ time_information + " \'" + error_message + "\'");
done_event->Signal();
return;
}
diff --git a/cobalt/media_capture/get_user_media_test.cc b/cobalt/media_capture/get_user_media_test.cc
index fee9ad3..768b26a 100644
--- a/cobalt/media_capture/get_user_media_test.cc
+++ b/cobalt/media_capture/get_user_media_test.cc
@@ -29,12 +29,12 @@
class GetUserMediaTest : public ::testing::Test {
protected:
GetUserMediaTest() {
- cobalt::dom::DOMSettings::Options options;
+ window_.reset(new dom::testing::StubWindow());
#if defined(ENABLE_FAKE_MICROPHONE)
+ cobalt::dom::DOMSettings::Options options;
options.microphone_options.enable_fake_microphone = true;
+ window_->set_options();
#endif // defined(ENABLE_FAKE_MICROPHONE)
-
- window_.reset(new dom::testing::StubWindow(options));
media_devices_ =
new MediaDevices(window_->environment_settings(),
window_->global_environment()->script_value_factory());
diff --git a/cobalt/renderer/pipeline.cc b/cobalt/renderer/pipeline.cc
index 6198549..98a2eab 100644
--- a/cobalt/renderer/pipeline.cc
+++ b/cobalt/renderer/pipeline.cc
@@ -337,9 +337,9 @@
// Registers Pipeline as a watchdog client.
watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
if (watchdog)
- watchdog->Register(kWatchdogName, base::kApplicationStateStarted,
- kWatchdogTimeInterval, kWatchdogTimeWait,
- watchdog::PING);
+ watchdog->Register(kWatchdogName, kWatchdogName,
+ base::kApplicationStateStarted, kWatchdogTimeInterval,
+ kWatchdogTimeWait, watchdog::PING);
// If a time fence is active, save the submission to be queued only after
// we pass the time fence. Overwrite any existing waiting submission in this
@@ -405,7 +405,7 @@
watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
if (watchdog) {
#if defined(_DEBUG)
- // Injects delay based off of environment variables for watchdog debugging.
+ // Injects delay for watchdog debugging.
watchdog->MaybeInjectDebugDelay(kWatchdogName);
#endif // defined(_DEBUG)
watchdog->Ping(kWatchdogName);
diff --git a/cobalt/script/environment_settings.h b/cobalt/script/environment_settings.h
index dd615ed..d1761da 100644
--- a/cobalt/script/environment_settings.h
+++ b/cobalt/script/environment_settings.h
@@ -39,12 +39,12 @@
// The API base URL.
// https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#api-base-url
- void set_base_url(const GURL& url) { base_url_ = url; }
- const GURL& base_url() const { return base_url_; }
+ virtual const GURL& base_url() const { return creation_url(); }
// The API creation URL
// https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#concept-environment-creation-url
- const GURL& creation_url() const { return base_url(); }
+ void set_creation_url(const GURL& url) { creation_url_ = url; }
+ const GURL& creation_url() const { return creation_url_; }
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-origin
const GURL GetOrigin() const { return creation_url().GetOrigin(); }
@@ -60,7 +60,7 @@
std::string uuid_;
static const base::NullDebuggerHooks null_debugger_hooks_;
const base::DebuggerHooks& debugger_hooks_;
- GURL base_url_;
+ GURL creation_url_;
};
} // namespace script
diff --git a/cobalt/site/docs/reference/starboard/gn-configuration.md b/cobalt/site/docs/reference/starboard/gn-configuration.md
index cd877fa..4c72557 100644
--- a/cobalt/site/docs/reference/starboard/gn-configuration.md
+++ b/cobalt/site/docs/reference/starboard/gn-configuration.md
@@ -6,22 +6,22 @@
| Variables |
| :--- |
| **`abort_on_allocation_failure`**<br><br> Halt execution on failure to allocate memory.<br><br>The default value is `true`. |
-| **`asan_symbolizer_path`**<br><br> A symbolizer path for ASAN can be added to allow translation of callstacks.<br><br>The default value is `"$clang_base_path/bin/llvm-symbolizer"`. |
+| **`asan_symbolizer_path`**<br><br> A symbolizer path for ASAN can be added to allow translation of callstacks.<br><br>The default value is `"<clang_base_path>/bin/llvm-symbolizer"`. |
| **`cobalt_licenses_platform`**<br><br> Sub-directory to copy license file to.<br><br>The default value is `"default"`. |
-| **`cobalt_platform_dependencies`**<br><br> TODO(b/173248397): Migrate to CobaltExtensions or PlatformServices. List of platform-specific targets that get compiled into cobalt.<br><br>The default value is `[]`. |
+| **`cobalt_platform_dependencies`**<br><br> List of platform-specific targets that get compiled into cobalt.<br><br>The default value is `[]`. |
| **`cobalt_v8_emit_builtins_as_inline_asm`**<br><br> Some compiler can not compile with raw assembly(.S files) and v8 converts asm to inline assembly for these platforms.<br><br>The default value is `false`. |
| **`default_renderer_options_dependency`**<br><br> Override this value to adjust the default rasterizer setting for your platform.<br><br>The default value is `"//cobalt/renderer:default_options"`. |
| **`enable_account_manager`**<br><br> Set to true to enable H5vccAccountManager.<br><br>The default value is `false`. |
| **`enable_in_app_dial`**<br><br> Enables or disables the DIAL server that runs inside Cobalt. Note: Only enable if there's no system-wide DIAL support.<br><br>The default value is `false`. |
| **`enable_sso`**<br><br> Set to true to enable H5vccSSO (Single Sign On).<br><br>The default value is `false`. |
| **`enable_xhr_header_filtering`**<br><br> Set to true to enable filtering of HTTP headers before sending.<br><br>The default value is `false`. |
-| **`executable_configs`**<br><br> Target-specific configurations for each platform.<br><br>The default value is `[]`. |
-| **`final_executable_type`**<br><br> The variables allow changing the target type on platforms where the native code may require an additional packaging step (ex. Android).<br><br>The default value is `"executable"`. |
+| **`executable_configs`**<br><br> Target-specific configurations for executable targets.<br><br>The default value is `[]`. |
+| **`final_executable_type`**<br><br> The target type for executable targets. Allows changing the target type on platforms where the native code may require an additional packaging step (ex. Android).<br><br>The default value is `"executable"`. |
| **`gl_type`**<br><br> The source of EGL and GLES headers and libraries. Valid values (case and everything sensitive!):<ul><li><code>none</code> - No EGL + GLES implementation is available on this platform.<li><code>system_gles2</code> - Use the system implementation of EGL + GLES2. The headers and libraries must be on the system include and link paths.<li><code>glimp</code> - Cobalt's own EGL + GLES2 implementation. This requires a valid Glimp implementation for the platform.<li><code>angle</code> - A DirectX-to-OpenGL adaptation layer. This requires a valid ANGLE implementation for the platform.<br><br>The default value is `"system_gles2"`. |
-| **`gtest_target_type`**<br><br> The variables allow changing the target type on platforms where the native code may require an additional packaging step (ex. Android).<br><br>The default value is `"executable"`. |
+| **`gtest_target_type`**<br><br> The target type for test targets. Allows changing the target type on platforms where the native code may require an additional packaging step (ex. Android).<br><br>The default value is `"executable"`. |
| **`has_platform_targets`**<br><br> Whether the platform has platform-specific targets to depend on.<br><br>The default value is `false`. |
| **`install_target_path`**<br><br> The path to the gni file containing the install_target template which defines how the build should produce the install/ directory.<br><br>The default value is `"//starboard/build/install/no_install.gni"`. |
-| **`loadable_module_configs`**<br><br> Target-specific configurations for each platform.<br><br>The default value is `[]`. |
+| **`loadable_module_configs`**<br><br> Target-specific configurations for loadable_module targets.<br><br>The default value is `[]`. |
| **`path_to_yasm`**<br><br> Where yasm can be found on the host device.<br><br>The default value is `"yasm"`. |
| **`platform_tests_path`**<br><br> Set to the starboard_platform_tests target if the platform implements them.<br><br>The default value is `""`. |
| **`sabi_path`**<br><br> Where the Starboard ABI file for this platform can be found.<br><br>The default value is `"starboard/sabi/default/sabi.json"`. |
@@ -36,13 +36,13 @@
| **`sb_is_evergreen`**<br><br> Whether this is an Evergreen build.<br><br>The default value is `false`. |
| **`sb_is_evergreen_compatible`**<br><br> Whether this is an Evergreen compatible platform. A compatible platform can run the elf_loader and launch the Evergreen build.<br><br>The default value is `false`. |
| **`sb_libevent_method`**<br><br> The event polling mechanism available on this platform to support libevent. Platforms may redefine to 'poll' if necessary. Other mechanisms, e.g. devpoll, kqueue, select, are not yet supported.<br><br>The default value is `"epoll"`. |
-| **`sb_static_contents_output_data_dir`**<br><br> Directory path to static contents' data.<br><br>The default value is `"$root_out_dir/content/data"`. |
-| **`sb_use_no_rtti`**<br><br>The default value is `false`. |
+| **`sb_static_contents_output_data_dir`**<br><br> Directory path to static contents' data.<br><br>The default value is `"/project_out_dir/content/data"`. |
+| **`sb_use_no_rtti`**<br><br> Whether or not to disable run-time type information (adding no_rtti flag).<br><br>The default value is `false`. |
| **`separate_install_targets_for_bundling`**<br><br> Set to true to separate install target directories.<br><br>The default value is `false`. |
-| **`shared_library_configs`**<br><br> Target-specific configurations for each platform.<br><br>The default value is `[]`. |
-| **`source_set_configs`**<br><br> Target-specific configurations for each platform.<br><br>The default value is `[]`. |
-| **`static_library_configs`**<br><br> Target-specific configurations for each platform.<br><br>The default value is `[]`. |
-| **`use_skia_next`**<br><br> Flag to use a future version of Skia, currently not available<br><br>The default value is `false`. |
+| **`shared_library_configs`**<br><br> Target-specific configurations for shared_library targets.<br><br>The default value is `[]`. |
+| **`source_set_configs`**<br><br> Target-specific configurations for source_set targets.<br><br>The default value is `[]`. |
+| **`static_library_configs`**<br><br> Target-specific configurations for static_library targets.<br><br>The default value is `[]`. |
+| **`use_skia_next`**<br><br> Flag to use a future version of Skia, currently not available.<br><br>The default value is `false`. |
| **`use_thin_archive`**<br><br> Whether or not to link with thin archives.<br><br>The default value is `true`. |
| **`v8_enable_pointer_compression_override`**<br><br> Set to true to enable pointer compression for v8.<br><br>The default value is `true`. |
| **`yasm_exists`**<br><br> Enables the yasm compiler to be used to compile .asm files.<br><br>The default value is `false`. |
diff --git a/cobalt/updater/BUILD.gn b/cobalt/updater/BUILD.gn
index 4026027..da5cd31 100644
--- a/cobalt/updater/BUILD.gn
+++ b/cobalt/updater/BUILD.gn
@@ -110,6 +110,7 @@
":updater",
"//base",
"//components/update_client",
+ "//testing/gmock",
"//testing/gtest",
]
}
diff --git a/cobalt/updater/utils.cc b/cobalt/updater/utils.cc
index d1678de..5be22e3 100644
--- a/cobalt/updater/utils.cc
+++ b/cobalt/updater/utils.cc
@@ -127,13 +127,14 @@
.DirName();
}
-const base::FilePath FindInstallationPath() {
+const base::FilePath FindInstallationPath(
+ std::function<const void*(const char*)> get_extension_fn) {
// TODO(b/233914266): consider using base::NoDestructor to give the
// installation path static duration once found.
auto installation_manager =
static_cast<const CobaltExtensionInstallationManagerApi*>(
- SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
+ get_extension_fn(kCobaltExtensionInstallationManagerName));
if (!installation_manager) {
LOG(ERROR) << "Failed to get installation manager extension, getting the "
"installation path of the loaded library.";
@@ -171,14 +172,16 @@
return version.GetString();
}
-const std::string GetCurrentEvergreenVersion() {
- base::FilePath installation_path = FindInstallationPath();
+const std::string GetCurrentEvergreenVersion(
+ std::function<const void*(const char*)> get_extension_fn) {
+ base::FilePath installation_path = FindInstallationPath(get_extension_fn);
return GetValidOrDefaultEvergreenVersion(installation_path);
}
-EvergreenLibraryMetadata GetCurrentEvergreenLibraryMetadata() {
+EvergreenLibraryMetadata GetCurrentEvergreenLibraryMetadata(
+ std::function<const void*(const char*)> get_extension_fn) {
EvergreenLibraryMetadata evergreen_library_metadata;
- base::FilePath installation_path = FindInstallationPath();
+ base::FilePath installation_path = FindInstallationPath(get_extension_fn);
evergreen_library_metadata.version =
GetValidOrDefaultEvergreenVersion(installation_path);
diff --git a/cobalt/updater/utils.h b/cobalt/updater/utils.h
index 2b7b1ba..abcabfc 100644
--- a/cobalt/updater/utils.h
+++ b/cobalt/updater/utils.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/version.h"
+#include "starboard/system.h"
namespace base {
class FilePath;
@@ -34,10 +35,14 @@
bool GetProductDirectoryPath(base::FilePath* path);
// Returns the Evergreen library metadata of the current installation.
-EvergreenLibraryMetadata GetCurrentEvergreenLibraryMetadata();
+EvergreenLibraryMetadata GetCurrentEvergreenLibraryMetadata(
+ std::function<const void*(const char*)> get_extension_fn =
+ &SbSystemGetExtension);
// Returns the Evergreen version of the current installation.
-const std::string GetCurrentEvergreenVersion();
+const std::string GetCurrentEvergreenVersion(
+ std::function<const void*(const char*)> get_extension_fn =
+ &SbSystemGetExtension);
// Reads the Evergreen version of the installation dir.
base::Version ReadEvergreenVersion(base::FilePath installation_dir);
diff --git a/cobalt/updater/utils_test.cc b/cobalt/updater/utils_test.cc
index 91fc329..4a43cde 100644
--- a/cobalt/updater/utils_test.cc
+++ b/cobalt/updater/utils_test.cc
@@ -19,6 +19,8 @@
#include "base/files/file_path.h"
#include "base/strings/strcat.h"
#include "base/values.h"
+#include "cobalt/extension/installation_manager.h"
+#include "gmock/gmock.h"
#include "starboard/common/file.h"
#include "starboard/directory.h"
#include "starboard/file.h"
@@ -31,6 +33,71 @@
const char kEvergreenManifestFilename[] = "manifest.json";
const char kEvergreenLibDirname[] = "lib";
+// Based on the CobaltExtensionInstallationManagerApi struct typedef.
+class MockInstallationManagerApi {
+ public:
+ MOCK_METHOD0(GetCurrentInstallationIndex, int());
+ MOCK_METHOD3(GetInstallationPath,
+ int(int installation_index, char* path, int path_length));
+};
+
+MockInstallationManagerApi* GetMockInstallationManagerApi() {
+ static auto* const installation_manager_api = []() {
+ auto* inner_installation_manager_api = new MockInstallationManagerApi;
+ // Because the mocked methods are non-state-changing, this mock is really
+ // just used as a stub. It's therefore ok for this mock object to be leaked
+ // and not verified.
+ testing::Mock::AllowLeak(inner_installation_manager_api);
+ return inner_installation_manager_api;
+ }();
+
+ return installation_manager_api;
+}
+
+// Stub definitions that delegate to the mock installation manager API.
+int StubGetCurrentInstallationIndex() {
+ return GetMockInstallationManagerApi()->GetCurrentInstallationIndex();
+}
+
+int StubGetInstallationPath(int installation_index, char* path,
+ int path_length) {
+ return GetMockInstallationManagerApi()->GetInstallationPath(
+ installation_index, path, path_length);
+}
+
+// No-op stub definitions for functions that aren't exercised.
+int StubMarkInstallationSuccessful(int installation_index) {
+ return IM_EXT_SUCCESS;
+}
+
+int StubRequestRollForwardToInstallation(int installation_index) {
+ return IM_EXT_SUCCESS;
+}
+
+int StubSelectNewInstallationIndex() { return IM_EXT_SUCCESS; }
+
+int StubGetAppKey(char* app_key, int app_key_length) { return IM_EXT_SUCCESS; }
+
+int StubGetMaxNumberInstallations() { return IM_EXT_SUCCESS; }
+
+int StubResetInstallation(int installation_index) { return IM_EXT_SUCCESS; }
+
+int StubReset() { return IM_EXT_SUCCESS; }
+
+const CobaltExtensionInstallationManagerApi kStubInstallationManagerApi = {
+ kCobaltExtensionInstallationManagerName,
+ 1,
+ &StubGetCurrentInstallationIndex,
+ &StubMarkInstallationSuccessful,
+ &StubRequestRollForwardToInstallation,
+ &StubGetInstallationPath,
+ &StubSelectNewInstallationIndex,
+ &StubGetAppKey,
+ &StubGetMaxNumberInstallations,
+ &StubResetInstallation,
+ &StubReset,
+};
+
class UtilsTest : public testing::Test {
protected:
void SetUp() override {
@@ -134,8 +201,90 @@
ASSERT_FALSE(version.IsValid());
}
+TEST_F(UtilsTest, ReturnsEvergreenVersionFromCurrentManagedInstallation) {
+ std::string installation_path = base::StrCat(
+ {temp_dir_path_.data(), kSbFileSepString, "some_installation_path"});
+ int installation_index = 1;
+
+ MockInstallationManagerApi* mock_installation_manager_api =
+ GetMockInstallationManagerApi();
+ EXPECT_CALL(*mock_installation_manager_api, GetCurrentInstallationIndex())
+ .Times(1)
+ .WillOnce(testing::Return(installation_index));
+ EXPECT_CALL(
+ *mock_installation_manager_api,
+ GetInstallationPath(installation_index, testing::_, kSbFileMaxPath))
+ .Times(1)
+ .WillOnce(
+ testing::DoAll(testing::SetArrayArgument<1>(installation_path.begin(),
+ installation_path.end()),
+ testing::Return(IM_EXT_SUCCESS)));
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return &kStubInstallationManagerApi; };
+
+ ASSERT_TRUE(SbDirectoryCreate(installation_path.c_str()));
+ char manifest_content[] = R"json(
+ {
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ "version": "1.2.0"
+ })json";
+ CreateManifest(manifest_content, installation_path);
+
+ std::string version = GetCurrentEvergreenVersion(stub_get_extension_fn);
+
+ ASSERT_EQ(version, "1.2.0");
+
+ DeleteManifest(installation_path);
+}
+
TEST_F(UtilsTest,
- ReturnsValidCurrentEvergreenVersionForManifestInLoadedInstallation) {
+ ReturnsDefaultVersionWhenManifestMissingFromCurrentManagedInstallation) {
+ std::string installation_path = base::StrCat(
+ {temp_dir_path_.data(), kSbFileSepString, "some_installation_path"});
+ int installation_index = 1;
+
+ MockInstallationManagerApi* mock_installation_manager_api =
+ GetMockInstallationManagerApi();
+ EXPECT_CALL(*mock_installation_manager_api, GetCurrentInstallationIndex())
+ .Times(1)
+ .WillOnce(testing::Return(installation_index));
+ EXPECT_CALL(
+ *mock_installation_manager_api,
+ GetInstallationPath(installation_index, testing::_, kSbFileMaxPath))
+ .Times(1)
+ .WillOnce(
+ testing::DoAll(testing::SetArrayArgument<1>(installation_path.begin(),
+ installation_path.end()),
+ testing::Return(IM_EXT_SUCCESS)));
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return &kStubInstallationManagerApi; };
+
+ ASSERT_TRUE(SbDirectoryCreate(installation_path.c_str()));
+ // No manifest is created in the installation directory.
+
+ std::string version = GetCurrentEvergreenVersion(stub_get_extension_fn);
+
+ ASSERT_EQ(version, kDefaultManifestVersion);
+}
+
+TEST_F(UtilsTest,
+ ReturnsVersionFromLoadedInstallationWhenErrorGettingInstallationPath) {
+ int installation_index = 1;
+ MockInstallationManagerApi* mock_installation_manager_api =
+ GetMockInstallationManagerApi();
+ EXPECT_CALL(*mock_installation_manager_api, GetCurrentInstallationIndex())
+ .Times(1)
+ .WillOnce(testing::Return(installation_index));
+ EXPECT_CALL(
+ *mock_installation_manager_api,
+ GetInstallationPath(installation_index, testing::_, kSbFileMaxPath))
+ .Times(1)
+ .WillOnce(testing::Return(IM_EXT_ERROR));
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return &kStubInstallationManagerApi; };
+
std::vector<char> system_path_content_dir(kSbFileMaxPath);
SbSystemGetPath(kSbSystemPathContentDirectory, system_path_content_dir.data(),
kSbFileMaxPath);
@@ -156,7 +305,7 @@
})json";
CreateManifest(manifest_content, installation_path);
- std::string version = GetCurrentEvergreenVersion();
+ std::string version = GetCurrentEvergreenVersion(stub_get_extension_fn);
ASSERT_EQ(version, "1.2.0");
@@ -164,14 +313,15 @@
}
TEST_F(UtilsTest,
- ReturnsDefaultEvergreenVersionForManifestMissingFromLoadedInstallation) {
- std::string version = GetCurrentEvergreenVersion();
+ ReturnsVersionFromLoadedInstallationWhenErrorGettingInstallationIndex) {
+ MockInstallationManagerApi* mock_installation_manager_api =
+ GetMockInstallationManagerApi();
+ EXPECT_CALL(*mock_installation_manager_api, GetCurrentInstallationIndex())
+ .Times(1)
+ .WillOnce(testing::Return(IM_EXT_ERROR));
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return &kStubInstallationManagerApi; };
- ASSERT_EQ(version, kDefaultManifestVersion);
-}
-
-TEST_F(UtilsTest,
- ReturnsValidCurrentMetadataForValidFilesInLoadedInstallation) {
std::vector<char> system_path_content_dir(kSbFileMaxPath);
SbSystemGetPath(kSbSystemPathContentDirectory, system_path_content_dir.data(),
kSbFileMaxPath);
@@ -191,9 +341,99 @@
"version": "1.2.0"
})json";
CreateManifest(manifest_content, installation_path);
+
+ std::string version = GetCurrentEvergreenVersion(stub_get_extension_fn);
+
+ ASSERT_EQ(version, "1.2.0");
+
+ DeleteManifest(installation_path);
+}
+
+TEST_F(UtilsTest,
+ ReturnsVersionFromLoadedInstallationWhenErrorGettingIMExtension) {
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return nullptr; };
+
+ std::vector<char> system_path_content_dir(kSbFileMaxPath);
+ SbSystemGetPath(kSbSystemPathContentDirectory, system_path_content_dir.data(),
+ kSbFileMaxPath);
+ // Since the libupdater_test.so library has already been loaded,
+ // kSbSystemPathContentDirectory points to the content dir of the running
+ // library and the installation dir is therefore its parent.
+ std::string installation_path =
+ base::FilePath(std::string(system_path_content_dir.begin(),
+ system_path_content_dir.end()))
+ .DirName()
+ .value();
+ char manifest_content[] = R"json(
+ {
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ "version": "1.2.0"
+ })json";
+ CreateManifest(manifest_content, installation_path);
+
+ std::string version = GetCurrentEvergreenVersion(stub_get_extension_fn);
+
+ ASSERT_EQ(version, "1.2.0");
+
+ DeleteManifest(installation_path);
+}
+
+TEST_F(UtilsTest,
+ ReturnsDefaultVersionWhenManifestMissingFromLoadedInstallation) {
+ // Two levels of resilience are actually tested...
+
+ // First, the loaded installation is used because an error is encountered
+ // while getting the Installation Manager extension. This is similar to
+ // previous test cases.
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return nullptr; };
+
+ // And second, kDefaultManifestVersion should be used because no manifest is
+ // found in the loaded installation.
+
+ std::string version = GetCurrentEvergreenVersion(stub_get_extension_fn);
+
+ ASSERT_EQ(version, kDefaultManifestVersion);
+}
+
+TEST_F(UtilsTest,
+ ReturnsEvergreenLibraryMetadataFromCurrentManagedInstallation) {
+ std::string installation_path = base::StrCat(
+ {temp_dir_path_.data(), kSbFileSepString, "some_installation_path"});
+ int installation_index = 1;
+
+ MockInstallationManagerApi* mock_installation_manager_api =
+ GetMockInstallationManagerApi();
+ EXPECT_CALL(*mock_installation_manager_api, GetCurrentInstallationIndex())
+ .Times(1)
+ .WillOnce(testing::Return(installation_index));
+ EXPECT_CALL(
+ *mock_installation_manager_api,
+ GetInstallationPath(installation_index, testing::_, kSbFileMaxPath))
+ .Times(1)
+ .WillOnce(
+ testing::DoAll(testing::SetArrayArgument<1>(installation_path.begin(),
+ installation_path.end()),
+ testing::Return(IM_EXT_SUCCESS)));
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return &kStubInstallationManagerApi; };
+
+ ASSERT_TRUE(SbDirectoryCreate(installation_path.c_str()));
+ char manifest_content[] = R"json(
+ {
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ "version": "1.2.0"
+ })json";
+ CreateManifest(manifest_content, installation_path);
CreateEmptyLibrary("libcobalt.so", installation_path);
- EvergreenLibraryMetadata metadata = GetCurrentEvergreenLibraryMetadata();
+ EvergreenLibraryMetadata metadata =
+ GetCurrentEvergreenLibraryMetadata(stub_get_extension_fn);
ASSERT_EQ(metadata.version, "1.2.0");
ASSERT_EQ(metadata.file_type, "Uncompressed");
@@ -203,7 +443,43 @@
}
TEST_F(UtilsTest,
- ReturnsUnknownFileTypeInMetadataForUnexpectedLibInLoadedInstallation) {
+ ReturnsFileTypeUnknownInMetadataForUnexpectedLibInManagedInstallation) {
+ std::string installation_path = base::StrCat(
+ {temp_dir_path_.data(), kSbFileSepString, "some_installation_path"});
+ int installation_index = 1;
+
+ MockInstallationManagerApi* mock_installation_manager_api =
+ GetMockInstallationManagerApi();
+ EXPECT_CALL(*mock_installation_manager_api, GetCurrentInstallationIndex())
+ .Times(1)
+ .WillOnce(testing::Return(installation_index));
+ EXPECT_CALL(
+ *mock_installation_manager_api,
+ GetInstallationPath(installation_index, testing::_, kSbFileMaxPath))
+ .Times(1)
+ .WillOnce(
+ testing::DoAll(testing::SetArrayArgument<1>(installation_path.begin(),
+ installation_path.end()),
+ testing::Return(IM_EXT_SUCCESS)));
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return &kStubInstallationManagerApi; };
+
+ ASSERT_TRUE(SbDirectoryCreate(installation_path.c_str()));
+ CreateEmptyLibrary("libcobalt.unexpected", installation_path);
+
+ EvergreenLibraryMetadata metadata =
+ GetCurrentEvergreenLibraryMetadata(stub_get_extension_fn);
+
+ ASSERT_EQ(metadata.file_type, "FileTypeUnknown");
+
+ DeleteLibraryDirRecursively(installation_path);
+}
+
+TEST_F(UtilsTest,
+ ReturnsEvergreenLibMetadataFromLoadedInstallationWhenErrorGettingIM) {
+ std::function<const void*(const char*)> stub_get_extension_fn =
+ [](const char* name) { return nullptr; };
+
std::vector<char> system_path_content_dir(kSbFileMaxPath);
SbSystemGetPath(kSbSystemPathContentDirectory, system_path_content_dir.data(),
kSbFileMaxPath);
@@ -215,12 +491,23 @@
system_path_content_dir.end()))
.DirName()
.value();
- CreateEmptyLibrary("libcobalt.unexpected", installation_path);
+ char manifest_content[] = R"json(
+ {
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ "version": "1.2.0"
+ })json";
+ CreateManifest(manifest_content, installation_path);
+ CreateEmptyLibrary("libcobalt.lz4", installation_path);
- EvergreenLibraryMetadata metadata = GetCurrentEvergreenLibraryMetadata();
+ EvergreenLibraryMetadata metadata =
+ GetCurrentEvergreenLibraryMetadata(stub_get_extension_fn);
- ASSERT_EQ(metadata.file_type, "FileTypeUnknown");
+ ASSERT_EQ(metadata.version, "1.2.0");
+ ASSERT_EQ(metadata.file_type, "Compressed");
+ DeleteManifest(installation_path);
DeleteLibraryDirRecursively(installation_path);
}
diff --git a/cobalt/watchdog/watchdog.cc b/cobalt/watchdog/watchdog.cc
index 544a47d..a8dd794 100644
--- a/cobalt/watchdog/watchdog.cc
+++ b/cobalt/watchdog/watchdog.cc
@@ -36,12 +36,16 @@
// The Watchdog violations json filename.
const char kWatchdogViolationsJson[] = "watchdog.json";
-// The default number of microseconds between each monitor loop.
-const int64_t kWatchdogSmallestTimeInterval = 1000000;
+// The frequency in microseconds of monitor loops.
+const int64_t kWatchdogMonitorFrequency = 1000000;
// The maximum number of most recent repeated Watchdog violations.
const int64_t kWatchdogMaxViolations = 100;
+// The minimum number of microseconds between writes.
+const int64_t kWatchdogWriteWaitTime = 300000000;
// The maximum number of most recent ping infos.
-const int64_t kWatchdogMaxPingInfos = 100;
+const int64_t kWatchdogMaxPingInfos = 20;
+// The maximum length of each ping info.
+const int64_t kWatchdogMaxPingInfoLength = 1024;
// Persistent setting name and default setting for the boolean that controls
// whether or not Watchdog is enabled. When disabled, Watchdog behaves like a
@@ -66,7 +70,8 @@
if (is_disabled_) return true;
SB_CHECK(SbMutexCreate(&mutex_));
- smallest_time_interval_ = kWatchdogSmallestTimeInterval;
+ pending_write_ = false;
+ write_wait_time_microseconds_ = kWatchdogWriteWaitTime;
#if defined(_DEBUG)
// Sets Watchdog delay settings from command line switch.
@@ -111,6 +116,7 @@
if (is_disabled_) return;
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
+ if (pending_write_) WriteWatchdogViolations();
is_monitoring_ = false;
SB_CHECK(SbMutexRelease(&mutex_));
SbThreadJoin(watchdog_thread_, nullptr);
@@ -130,6 +136,19 @@
return watchdog_file_;
}
+void Watchdog::WriteWatchdogViolations() {
+ // Writes Watchdog violations to persistent storage as a json file.
+ std::string watchdog_json;
+ base::JSONWriter::Write(*violations_map_, &watchdog_json);
+ SB_LOG(INFO) << "[Watchdog] Writing violations to JSON:\n" << watchdog_json;
+ starboard::ScopedFile watchdog_file(GetWatchdogFilePath().c_str(),
+ kSbFileCreateAlways | kSbFileWrite);
+ watchdog_file.WriteAll(watchdog_json.c_str(),
+ static_cast<int>(watchdog_json.size()));
+ pending_write_ = false;
+ time_last_written_microseconds_ = SbTimeGetMonotonicNow();
+}
+
void Watchdog::UpdateState(base::ApplicationState state) {
if (is_disabled_) return;
@@ -264,19 +283,19 @@
"violationDurationMicroseconds",
base::Value(std::to_string(violation_duration + time_delta)));
}
+ static_cast<Watchdog*>(context)->pending_write_ = true;
// Resets time last updated.
client->time_last_updated_monotonic_microseconds =
current_monotonic_time;
}
}
- if (watchdog_violation) {
- WriteWatchdogViolations(context);
- MaybeTriggerCrash(context);
- }
+ if (static_cast<Watchdog*>(context)->pending_write_)
+ MaybeWriteWatchdogViolations(context);
+ if (watchdog_violation) MaybeTriggerCrash(context);
SB_CHECK(SbMutexRelease(&(static_cast<Watchdog*>(context))->mutex_));
- SbThreadSleep(static_cast<Watchdog*>(context)->smallest_time_interval_);
+ SbThreadSleep(kWatchdogMonitorFrequency);
}
return nullptr;
}
@@ -302,40 +321,41 @@
}
}
-void Watchdog::WriteWatchdogViolations(void* context) {
- // Writes Watchdog violations to persistent storage as a json file.
- std::string watchdog_json;
- base::JSONWriter::Write(*(static_cast<Watchdog*>(context)->violations_map_),
- &watchdog_json);
-
- SB_LOG(INFO) << "[Watchdog] Writing violations to JSON:\n" << watchdog_json;
-
- starboard::ScopedFile watchdog_file(
- (static_cast<Watchdog*>(context)->GetWatchdogFilePath()).c_str(),
- kSbFileCreateAlways | kSbFileWrite);
- watchdog_file.WriteAll(watchdog_json.c_str(),
- static_cast<int>(watchdog_json.size()));
+void Watchdog::MaybeWriteWatchdogViolations(void* context) {
+ if (SbTimeGetMonotonicNow() >
+ static_cast<Watchdog*>(context)->time_last_written_microseconds_ +
+ static_cast<Watchdog*>(context)->write_wait_time_microseconds_) {
+ static_cast<Watchdog*>(context)->WriteWatchdogViolations();
+ }
}
void Watchdog::MaybeTriggerCrash(void* context) {
if (static_cast<Watchdog*>(context)->GetPersistentSettingWatchdogCrash()) {
+ if (static_cast<Watchdog*>(context)->pending_write_)
+ static_cast<Watchdog*>(context)->WriteWatchdogViolations();
SB_LOG(ERROR) << "[Watchdog] Triggering violation Crash!";
CHECK(false);
}
}
-bool Watchdog::Register(std::string name, base::ApplicationState monitor_state,
- int64_t time_interval, int64_t time_wait,
- Replace replace) {
- return Register(name, name, monitor_state, time_interval, time_wait, replace);
-}
-
bool Watchdog::Register(std::string name, std::string description,
base::ApplicationState monitor_state,
int64_t time_interval, int64_t time_wait,
Replace replace) {
if (is_disabled_) return true;
+ // Validates parameters.
+ if (time_interval < 1000000 || time_wait < 0) {
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Register: " << name;
+ if (time_interval < 1000000) {
+ SB_DLOG(ERROR) << "[Watchdog] Time interval less than min: "
+ << kWatchdogMonitorFrequency;
+ } else {
+ SB_DLOG(ERROR) << "[Watchdog] Time wait is negative.";
+ }
+ return false;
+ }
+
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
int64_t current_time = SbTimeToPosix(SbTimeGetNow());
@@ -373,8 +393,6 @@
// Registers.
auto result = client_map_.emplace(name, std::move(client));
- // Sets new smallest_time_interval_.
- smallest_time_interval_ = std::min(smallest_time_interval_, time_interval);
SB_CHECK(SbMutexRelease(&mutex_));
@@ -389,17 +407,9 @@
bool Watchdog::Unregister(const std::string& name, bool lock) {
if (is_disabled_) return true;
- if (lock) SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
// Unregisters.
+ if (lock) SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
auto result = client_map_.erase(name);
- // Sets new smallest_time_interval_.
- if (result) {
- smallest_time_interval_ = kWatchdogSmallestTimeInterval;
- for (auto& it : client_map_) {
- smallest_time_interval_ = std::min(smallest_time_interval_,
- it.second->time_interval_microseconds);
- }
- }
if (lock) SB_CHECK(SbMutexRelease(&mutex_));
if (result) {
@@ -415,7 +425,16 @@
bool Watchdog::Ping(const std::string& name, const std::string& info) {
if (is_disabled_) return true;
+ // Validates parameters.
+ if (info.length() > kWatchdogMaxPingInfoLength) {
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Ping: " << name;
+ SB_DLOG(ERROR) << "[Watchdog] Ping info length exceeds max: "
+ << kWatchdogMaxPingInfoLength;
+ return false;
+ }
+
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
+
auto it = client_map_.find(name);
bool client_exists = it != client_map_.end();
@@ -454,7 +473,11 @@
if (is_disabled_) return "";
std::string watchdog_json = "";
+
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
+
+ if (pending_write_) WriteWatchdogViolations();
+
starboard::ScopedFile read_file(GetWatchdogFilePath().c_str(),
kSbFileOpenOnly | kSbFileRead);
if (read_file.IsValid()) {
@@ -517,7 +540,7 @@
}
#if defined(_DEBUG)
-// Sleeps threads based off of environment variables for Watchdog debugging.
+// Sleeps threads for Watchdog debugging.
void Watchdog::MaybeInjectDebugDelay(const std::string& name) {
if (is_disabled_) return;
@@ -525,12 +548,7 @@
if (name != delay_name_) return;
- SbTimeMonotonic current_time = SbTimeGetMonotonicNow();
-
- if (time_last_delayed_microseconds_ == 0)
- time_last_delayed_microseconds_ = current_time;
-
- if (current_time >
+ if (SbTimeGetMonotonicNow() >
time_last_delayed_microseconds_ + delay_wait_time_microseconds_) {
SbThreadSleep(delay_sleep_time_microseconds_);
time_last_delayed_microseconds_ = SbTimeGetMonotonicNow();
diff --git a/cobalt/watchdog/watchdog.h b/cobalt/watchdog/watchdog.h
index b4535f1..7ab5d96 100644
--- a/cobalt/watchdog/watchdog.h
+++ b/cobalt/watchdog/watchdog.h
@@ -58,8 +58,8 @@
// Ping() and Register() when in PING replace mode or set initially by
// Register(). Also reset by Monitor() when in idle states or when a
// violation occurs. Prevents excessive violations as they must occur
- // time_interval_microseconds apart rather than smallest_time_interval_
- // apart. Used as the start value for time interval calculations.
+ // time_interval_microseconds apart rather than on each monitor loop. Used as
+ // the start value for time interval calculations.
SbTimeMonotonic time_last_updated_monotonic_microseconds;
} Client;
@@ -76,9 +76,6 @@
bool InitializeStub();
void Uninitialize();
void UpdateState(base::ApplicationState state);
- bool Register(std::string name, base::ApplicationState monitor_state,
- int64_t time_interval, int64_t time_wait = 0,
- Replace replace = NONE);
bool Register(std::string name, std::string description,
base::ApplicationState monitor_state, int64_t time_interval,
int64_t time_wait = 0, Replace replace = NONE);
@@ -98,9 +95,10 @@
private:
std::string GetWatchdogFilePath();
+ void WriteWatchdogViolations();
static void* Monitor(void* context);
static void InitializeViolationsMap(void* context);
- static void WriteWatchdogViolations(void* context);
+ static void MaybeWriteWatchdogViolations(void* context);
static void MaybeTriggerCrash(void* context);
// Watchdog violations file path.
@@ -111,16 +109,21 @@
// except that persistent settings can still be get/set.
bool is_disabled_;
// Creates a lock which ensures that each loop of monitor is atomic in that
- // modifications to is_monitoring_, state_, smallest_time_interval_, and most
- // importantly to the dictionaries containing Watchdog clients, client_map_
- // and violations_map_, only occur in between loops of monitor. API functions
- // like Register(), Unregister(), Ping(), and GetWatchdogViolations() will be
- // called by various threads and interact with these class variables.
+ // modifications to is_monitoring_, state_, and most importantly to the
+ // dictionaries containing Watchdog clients, client_map_ and violations_map_,
+ // only occur in between loops of monitor. API functions like Register(),
+ // Unregister(), Ping(), and GetWatchdogViolations() will be called by
+ // various threads and interact with these class variables.
SbMutex mutex_;
// Tracks application state.
base::ApplicationState state_ = base::kApplicationStateStarted;
- // Time interval between monitor loops.
- int64_t smallest_time_interval_;
+ // Flag to trigger Watchdog violations writes to persistent storage.
+ bool pending_write_;
+ // Monotonically increasing timestamp when Watchdog violations were last
+ // written to persistent storage. 0 indicates that it has never been written.
+ SbTimeMonotonic time_last_written_microseconds_ = 0;
+ // Number of microseconds between writes.
+ int64_t write_wait_time_microseconds_;
// Dictionary of registered Watchdog clients.
std::unordered_map<std::string, std::unique_ptr<Client>> client_map_;
// Dictionary of lists of Watchdog violations represented as dictionaries.
@@ -132,13 +135,14 @@
#if defined(_DEBUG)
starboard::Mutex delay_lock_;
- // name of the client to inject delay
+ // Name of the client to inject a delay for.
std::string delay_name_ = "";
- // since (relative)
+ // Monotonically increasing timestamp when a delay was last injected. 0
+ // indicates that it has never been injected.
SbTimeMonotonic time_last_delayed_microseconds_ = 0;
- // time in between delays (periodic)
+ // Number of microseconds between delays.
int64_t delay_wait_time_microseconds_ = 0;
- // delay duration
+ // Number of microseconds to delay.
int64_t delay_sleep_time_microseconds_ = 0;
#endif
};
diff --git a/cobalt/web/agent.cc b/cobalt/web/agent.cc
index e936b37..d017e6d 100644
--- a/cobalt/web/agent.cc
+++ b/cobalt/web/agent.cc
@@ -58,9 +58,6 @@
}
base::MessageLoop* message_loop() const final { return message_loop_; }
void ShutDownJavaScriptEngine() final;
- void set_fetcher_factory(loader::FetcherFactory* factory) final {
- fetcher_factory_.reset(factory);
- }
loader::FetcherFactory* fetcher_factory() const final {
return fetcher_factory_.get();
}
@@ -116,7 +113,9 @@
WindowOrWorkerGlobalScope* GetWindowOrWorkerGlobalScope() final;
- UserAgentPlatformInfo* platform_info() const final { return platform_info_; }
+ const UserAgentPlatformInfo* platform_info() const final {
+ return platform_info_;
+ }
std::string GetUserAgent() const final {
return network_module()->GetUserAgent();
}
@@ -201,7 +200,7 @@
service_worker_object_map_;
worker::ServiceWorkerJobs* service_worker_jobs_;
- web::UserAgentPlatformInfo* platform_info_;
+ const web::UserAgentPlatformInfo* platform_info_;
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-active-service-worker
// Note: When a service worker is unregistered from the last client, this will
diff --git a/cobalt/web/agent.h b/cobalt/web/agent.h
index c6cb7e9..956a6f6 100644
--- a/cobalt/web/agent.h
+++ b/cobalt/web/agent.h
@@ -76,7 +76,7 @@
worker::ServiceWorkerJobs* service_worker_jobs = nullptr;
- web::UserAgentPlatformInfo* platform_info = nullptr;
+ const UserAgentPlatformInfo* platform_info = nullptr;
};
typedef base::Callback<void(const script::HeapStatistics&)>
diff --git a/cobalt/web/blob.idl b/cobalt/web/blob.idl
index 4f6c9a8..8c3f3e7 100644
--- a/cobalt/web/blob.idl
+++ b/cobalt/web/blob.idl
@@ -17,7 +17,8 @@
[
Constructor,
Constructor(sequence<(ArrayBuffer or ArrayBufferView or DataView or Blob or USVString)> blobParts, optional BlobPropertyBag options),
- ConstructorCallWith=EnvironmentSettings
+ ConstructorCallWith=EnvironmentSettings,
+ Exposed=(Window,Worker)
]
interface Blob {
readonly attribute unsigned long long size;
diff --git a/cobalt/web/blob_test.cc b/cobalt/web/blob_test.cc
index 32ac89e..f3cb1b3 100644
--- a/cobalt/web/blob_test.cc
+++ b/cobalt/web/blob_test.cc
@@ -20,16 +20,20 @@
#include "cobalt/script/data_view.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/testing/mock_exception_state.h"
+#include "cobalt/web/testing/test_with_javascript.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using cobalt::script::testing::MockExceptionState;
+using ::testing::_;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+
namespace cobalt {
namespace web {
namespace {
-
-using script::testing::MockExceptionState;
-using ::testing::_;
-using ::testing::SaveArg;
-using ::testing::StrictMock;
+class BlobTestWithJavaScript : public testing::TestWebWithJavaScript {};
+} // namespace
TEST(BlobTest, Constructors) {
// Initialize JavaScriptEngine and its environment.
@@ -118,6 +122,93 @@
EXPECT_EQ(0x7, blob_with_buffer->data()[1]);
}
-} // namespace
+TEST_P(BlobTestWithJavaScript, ConstructorWithArray) {
+ std::string result;
+ bool success = EvaluateScript(
+ "try {"
+ " var blob = new Blob([\" TEST \"]);"
+ "} catch (e) {"
+ " result = e.name;"
+ "}"
+ "if (!blob) result; else blob;",
+ &result);
+ EXPECT_TRUE(success);
+ EXPECT_EQ("[object Blob]", result);
+
+ if (!success) {
+ DLOG(ERROR) << "Failed to evaluate test: "
+ << "\"" << result << "\"";
+ }
+}
+
+
+TEST_P(BlobTestWithJavaScript, ConstructorWithArrayType) {
+ std::string result;
+ bool success = EvaluateScript(
+ "let result = 'unknown';"
+ "try {"
+ " var blob = new Blob([\" TEST \"]);"
+ " result = blob.type;"
+ "} catch (e) {"
+ " result = e.name;"
+ "}"
+ "result;",
+ &result);
+ EXPECT_TRUE(success);
+ EXPECT_EQ("", result);
+
+ if (!success) {
+ DLOG(ERROR) << "Failed to evaluate test: "
+ << "\"" << result << "\"";
+ }
+}
+
+TEST_P(BlobTestWithJavaScript, ConstructorWithBlobPropertyBag) {
+ std::string result;
+ bool success = EvaluateScript(
+ "let result = 'unknown';"
+ "try {"
+ " var blob = new Blob([\" TEST \"], {type : 'test/foo'});"
+ " result = blob.type;"
+ "} catch (e) {"
+ " result = e.name;"
+ "}"
+ "result;",
+ &result);
+ EXPECT_TRUE(success);
+ EXPECT_EQ("test/foo", result);
+
+ if (!success) {
+ DLOG(ERROR) << "Failed to evaluate test: "
+ << "\"" << result << "\"";
+ }
+}
+
+TEST_P(BlobTestWithJavaScript, ConstructorWithArraySize) {
+ std::string result;
+ bool success = EvaluateScript(
+ "try {"
+ " var blob = new Blob([\" TEST \"]);"
+ "} catch (e) {"
+ " result = e.name;"
+ "}"
+ "if (!blob) result; else blob.size;",
+ &result);
+ EXPECT_TRUE(success);
+ EXPECT_EQ("6", result);
+
+ if (!success) {
+ DLOG(ERROR) << "Failed to evaluate test: "
+ << "\"" << result << "\"";
+ }
+}
+
+
+INSTANTIATE_TEST_CASE_P(
+ BlobTestsWithJavaScript, BlobTestWithJavaScript,
+ ::testing::ValuesIn(testing::TestWebWithJavaScript::GetWorkerTypes()),
+ testing::TestWebWithJavaScript::GetTypeName);
+
+
} // namespace web
} // namespace cobalt
diff --git a/cobalt/web/context.h b/cobalt/web/context.h
index e3f9b85..f48182f 100644
--- a/cobalt/web/context.h
+++ b/cobalt/web/context.h
@@ -46,7 +46,6 @@
virtual ~Context() {}
virtual base::MessageLoop* message_loop() const = 0;
virtual void ShutDownJavaScriptEngine() = 0;
- virtual void set_fetcher_factory(loader::FetcherFactory* factory) = 0;
virtual loader::FetcherFactory* fetcher_factory() const = 0;
virtual loader::ScriptLoaderFactory* script_loader_factory() const = 0;
virtual script::JavaScriptEngine* javascript_engine() const = 0;
@@ -78,7 +77,7 @@
virtual WindowOrWorkerGlobalScope* GetWindowOrWorkerGlobalScope() = 0;
- virtual UserAgentPlatformInfo* platform_info() const = 0;
+ virtual const UserAgentPlatformInfo* platform_info() const = 0;
virtual std::string GetUserAgent() const = 0;
virtual std::string GetPreferredLanguage() const = 0;
diff --git a/cobalt/web/csp_delegate_test.cc b/cobalt/web/csp_delegate_test.cc
index 837d504..b10dbaf 100644
--- a/cobalt/web/csp_delegate_test.cc
+++ b/cobalt/web/csp_delegate_test.cc
@@ -53,6 +53,13 @@
{CspDelegate::kWebSocket, "connect-src"},
};
+std::string ResourcePairName(::testing::TestParamInfo<ResourcePair> info) {
+ std::string name(info.param.directive);
+ std::replace(name.begin(), name.end(), '-', '_');
+ base::StringAppendF(&name, "_type_%d", info.param.type);
+ return name;
+}
+
class MockViolationReporter : public CspViolationReporter {
public:
MockViolationReporter()
@@ -132,7 +139,8 @@
EXPECT_EQ(effective_directive, info.effective_directive);
}
-INSTANTIATE_TEST_CASE_P(CanLoad, CspDelegateTest, ValuesIn(s_params));
+INSTANTIATE_TEST_CASE_P(CanLoad, CspDelegateTest, ValuesIn(s_params),
+ ResourcePairName);
TEST(CspDelegateFactoryTest, Secure) {
std::unique_ptr<CspDelegate> delegate =
diff --git a/cobalt/web/custom_event.idl b/cobalt/web/custom_event.idl
index babd097..f327201 100644
--- a/cobalt/web/custom_event.idl
+++ b/cobalt/web/custom_event.idl
@@ -13,8 +13,10 @@
// limitations under the License.
// https://www.w3.org/TR/2015/REC-dom-20151119/#customevent
-[Constructor(DOMString type, optional CustomEventInit eventInitDict)]
-interface CustomEvent : Event {
+[
+ Constructor(DOMString type, optional CustomEventInit eventInitDict),
+ Exposed = (Window,Worker)
+] interface CustomEvent : Event {
readonly attribute any detail;
void initCustomEvent(DOMString type,
diff --git a/cobalt/web/custom_event_test.cc b/cobalt/web/custom_event_test.cc
index 2b989e6..9bf87ae 100644
--- a/cobalt/web/custom_event_test.cc
+++ b/cobalt/web/custom_event_test.cc
@@ -19,18 +19,19 @@
#include "base/bind.h"
#include "base/logging.h"
-#include "cobalt/dom/testing/test_with_javascript.h"
#include "cobalt/web/custom_event_init.h"
#include "cobalt/web/testing/gtest_workarounds.h"
+#include "cobalt/web/testing/test_with_javascript.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using ::testing::_;
+
namespace cobalt {
namespace web {
namespace {
-class CustomEventTestWithJavaScript : public dom::testing::TestWithJavaScript {
-};
+class CustomEventTestWithJavaScript : public testing::TestWebWithJavaScript {};
} // namespace
TEST(CustomEventTest, ConstructorWithEventTypeString) {
@@ -66,7 +67,7 @@
EXPECT_EQ(NULL, event->detail());
}
-TEST_F(CustomEventTestWithJavaScript,
+TEST_P(CustomEventTestWithJavaScript,
ConstructorWithEventTypeAndCustomInitDict) {
std::string result;
bool success = EvaluateScript(
@@ -84,13 +85,10 @@
if (!success) {
DLOG(ERROR) << "Failed to evaluate test: "
<< "\"" << result << "\"";
- } else {
- LOG(INFO) << "Test result : "
- << "\"" << result << "\"";
}
}
-TEST_F(CustomEventTestWithJavaScript, InitCustomEvent) {
+TEST_P(CustomEventTestWithJavaScript, InitCustomEvent) {
std::string result;
bool success = EvaluateScript(
"var event = new CustomEvent('cat');\n"
@@ -106,11 +104,13 @@
if (!success) {
DLOG(ERROR) << "Failed to evaluate test: "
<< "\"" << result << "\"";
- } else {
- LOG(INFO) << "Test result : "
- << "\"" << result << "\"";
}
}
+INSTANTIATE_TEST_CASE_P(
+ CustomEventTestsWithJavaScript, CustomEventTestWithJavaScript,
+ ::testing::ValuesIn(testing::TestWebWithJavaScript::GetWorkerTypes()),
+ testing::TestWebWithJavaScript::GetTypeName);
+
} // namespace web
} // namespace cobalt
diff --git a/cobalt/web/event_target.cc b/cobalt/web/event_target.cc
index 5d20131..eaea80d 100644
--- a/cobalt/web/event_target.cc
+++ b/cobalt/web/event_target.cc
@@ -105,8 +105,8 @@
DCHECK(event);
DCHECK(!event->IsBeingDispatched());
DCHECK(event->initialized_flag());
- TRACE_EVENT1("cobalt::dom", "EventTarget::DispatchEvent", "event",
- TRACE_STR_COPY(event->type().c_str()));
+ TRACE_EVENT2("cobalt::dom", "EventTarget::DispatchEvent", "name",
+ GetDebugName(), "event", TRACE_STR_COPY(event->type().c_str()));
if (!event || event->IsBeingDispatched() || !event->initialized_flag()) {
return false;
}
diff --git a/cobalt/web/global_stats.cc b/cobalt/web/global_stats.cc
index 97980c4..e4fd2a6 100644
--- a/cobalt/web/global_stats.cc
+++ b/cobalt/web/global_stats.cc
@@ -35,6 +35,8 @@
GlobalStats::~GlobalStats() {}
bool GlobalStats::CheckNoLeaks() {
+ DCHECK(num_event_listeners_ == 0);
+ DCHECK(num_active_java_script_events_ == 0);
return num_event_listeners_ == 0 && num_active_java_script_events_ == 0;
}
diff --git a/cobalt/web/navigator_ua_data.cc b/cobalt/web/navigator_ua_data.cc
index b2bae8b..52ac278 100644
--- a/cobalt/web/navigator_ua_data.cc
+++ b/cobalt/web/navigator_ua_data.cc
@@ -20,7 +20,7 @@
namespace web {
NavigatorUAData::NavigatorUAData(
- UserAgentPlatformInfo* platform_info,
+ const UserAgentPlatformInfo* platform_info,
script::ScriptValueFactory* script_value_factory)
: script_value_factory_(script_value_factory) {
if (platform_info == nullptr) {
diff --git a/cobalt/web/navigator_ua_data.h b/cobalt/web/navigator_ua_data.h
index 1dfd873..1284bb2 100644
--- a/cobalt/web/navigator_ua_data.h
+++ b/cobalt/web/navigator_ua_data.h
@@ -36,7 +36,7 @@
public:
using InterfacePromise = script::Promise<scoped_refptr<script::Wrappable>>;
- NavigatorUAData(UserAgentPlatformInfo* platform_info,
+ NavigatorUAData(const UserAgentPlatformInfo* platform_info,
script::ScriptValueFactory* script_value_factory);
script::Sequence<NavigatorUABrandVersion> brands() const { return brands_; }
diff --git a/cobalt/web/testing/BUILD.gn b/cobalt/web/testing/BUILD.gn
index 3b785ca..17f0326 100644
--- a/cobalt/web/testing/BUILD.gn
+++ b/cobalt/web/testing/BUILD.gn
@@ -19,6 +19,7 @@
sources = [
"gtest_workarounds.h",
"mock_event_listener.h",
+ "mock_user_agent_platform_info.h",
"stub_environment_settings.h",
"stub_web_context.h",
]
@@ -27,6 +28,7 @@
"//base",
"//base/test:test_support",
"//cobalt/base",
+ "//cobalt/loader",
"//cobalt/network",
"//cobalt/script",
"//cobalt/script/testing:script_testing",
diff --git a/cobalt/web/testing/mock_user_agent_platform_info.h b/cobalt/web/testing/mock_user_agent_platform_info.h
new file mode 100644
index 0000000..4f87cfd
--- /dev/null
+++ b/cobalt/web/testing/mock_user_agent_platform_info.h
@@ -0,0 +1,93 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_WEB_TESTING_MOCK_USER_AGENT_PLATFORM_INFO_H_
+#define COBALT_WEB_TESTING_MOCK_USER_AGENT_PLATFORM_INFO_H_
+
+#include <string>
+
+#include "cobalt/web/user_agent_platform_info.h"
+
+namespace cobalt {
+namespace web {
+namespace testing {
+
+// Mock class implementing UserAgentPlatformInfo returning empty strings.
+class MockUserAgentPlatformInfo : public web::UserAgentPlatformInfo {
+ public:
+ MockUserAgentPlatformInfo() {}
+ ~MockUserAgentPlatformInfo() override{};
+
+ // From: dom:UserAgentPlatformInfo
+ //
+ const std::string& starboard_version() const override {
+ return empty_string_;
+ }
+ const std::string& os_name_and_version() const override {
+ return empty_string_;
+ }
+ base::Optional<std::string> original_design_manufacturer() const override {
+ return optional_empty_string_;
+ }
+ SbSystemDeviceType device_type() const override {
+ return kSbSystemDeviceTypeUnknown;
+ }
+ const std::string& device_type_string() const override {
+ return empty_string_;
+ }
+ base::Optional<std::string> chipset_model_number() const override {
+ return optional_empty_string_;
+ }
+ base::Optional<std::string> model_year() const override {
+ return optional_empty_string_;
+ }
+ base::Optional<std::string> firmware_version() const override {
+ return optional_empty_string_;
+ }
+ base::Optional<std::string> brand() const override {
+ return optional_empty_string_;
+ }
+ base::Optional<std::string> model() const override {
+ return optional_empty_string_;
+ }
+ const std::string& aux_field() const override { return empty_string_; }
+ const std::string& javascript_engine_version() const override {
+ return empty_string_;
+ }
+ const std::string& rasterizer_type() const override { return empty_string_; }
+ const std::string& evergreen_type() const override { return empty_string_; }
+ const std::string& evergreen_file_type() const override {
+ return empty_string_;
+ }
+ const std::string& evergreen_version() const override {
+ return empty_string_;
+ }
+ const std::string& cobalt_version() const override { return empty_string_; }
+ const std::string& cobalt_build_version_number() const override {
+ return empty_string_;
+ }
+ const std::string& build_configuration() const override {
+ return empty_string_;
+ }
+
+ private:
+ const std::string empty_string_;
+ base::Optional<std::string> optional_empty_string_;
+}; // namespace cobalt
+
+} // namespace testing
+} // namespace web
+} // namespace cobalt
+
+#endif // COBALT_WEB_TESTING_MOCK_USER_AGENT_PLATFORM_INFO_H_
diff --git a/cobalt/web/testing/stub_web_context.h b/cobalt/web/testing/stub_web_context.h
index 9442674..f709d0e 100644
--- a/cobalt/web/testing/stub_web_context.h
+++ b/cobalt/web/testing/stub_web_context.h
@@ -20,13 +20,18 @@
#include "base/message_loop/message_loop.h"
#include "base/test/scoped_task_environment.h"
+#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/network/network_module.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
#include "cobalt/script/wrappable.h"
+#include "cobalt/web/blob.h"
#include "cobalt/web/context.h"
#include "cobalt/web/environment_settings.h"
+#include "cobalt/web/testing/mock_user_agent_platform_info.h"
#include "cobalt/web/testing/stub_environment_settings.h"
+#include "cobalt/web/url.h"
+#include "cobalt/web/user_agent_platform_info.h"
#include "cobalt/worker/service_worker.h"
#include "cobalt/worker/service_worker_object.h"
#include "cobalt/worker/service_worker_registration.h"
@@ -40,20 +45,18 @@
namespace web {
namespace testing {
-class StubSettings : public EnvironmentSettings {
- public:
- explicit StubSettings(const GURL& base) { set_base_url(base); }
-
- private:
-};
-
class StubWebContext final : public Context {
public:
StubWebContext() : Context(), name_("StubWebInstance") {
javascript_engine_ = script::JavaScriptEngine::CreateEngine();
global_environment_ = javascript_engine_->CreateGlobalEnvironment();
+ blob_registry_.reset(new Blob::Registry);
+ network_module_.reset(new network::NetworkModule());
+ fetcher_factory_.reset(new loader::FetcherFactory(
+ network_module_.get(),
+ URL::MakeBlobResolverCallback(blob_registry_.get())));
}
- ~StubWebContext() final {}
+ ~StubWebContext() final { blob_registry_.reset(); }
// WebInstance
//
@@ -62,9 +65,6 @@
return nullptr;
}
void ShutDownJavaScriptEngine() final { NOTREACHED(); }
- void set_fetcher_factory(loader::FetcherFactory* factory) final {
- fetcher_factory_.reset(factory);
- }
loader::FetcherFactory* fetcher_factory() const final {
DCHECK(fetcher_factory_);
return fetcher_factory_.get();
@@ -92,11 +92,8 @@
return nullptr;
}
Blob::Registry* blob_registry() const final {
- NOTREACHED();
- return nullptr;
- }
- void set_network_module(network::NetworkModule* module) {
- network_module_.reset(module);
+ DCHECK(blob_registry_);
+ return blob_registry_.get();
}
network::NetworkModule* network_module() const final {
DCHECK(network_module_);
@@ -149,14 +146,25 @@
}
WindowOrWorkerGlobalScope* GetWindowOrWorkerGlobalScope() final {
- NOTIMPLEMENTED();
- return nullptr;
+ script::Wrappable* global_wrappable =
+ global_environment()->global_wrappable();
+ if (!global_wrappable) {
+ return nullptr;
+ }
+ DCHECK(global_wrappable->IsWrappable());
+ DCHECK_EQ(script::Wrappable::JSObjectType::kObject,
+ global_wrappable->GetJSObjectType());
+
+ return base::polymorphic_downcast<WindowOrWorkerGlobalScope*>(
+ global_wrappable);
}
void set_platform_info(UserAgentPlatformInfo* platform_info) {
platform_info_ = platform_info;
}
- UserAgentPlatformInfo* platform_info() const final { return platform_info_; }
+ const UserAgentPlatformInfo* platform_info() const final {
+ return (platform_info_ ? platform_info_ : &mock_platform_info_);
+ }
std::string GetUserAgent() const final {
return std::string("StubUserAgentString");
@@ -190,6 +198,7 @@
base::test::ScopedTaskEnvironment env_;
std::unique_ptr<loader::FetcherFactory> fetcher_factory_;
+ std::unique_ptr<Blob::Registry> blob_registry_;
std::unique_ptr<loader::ScriptLoaderFactory> script_loader_factory_;
std::unique_ptr<script::JavaScriptEngine> javascript_engine_;
scoped_refptr<script::GlobalEnvironment> global_environment_;
@@ -199,6 +208,7 @@
std::unique_ptr<EnvironmentSettings> environment_settings_;
UserAgentPlatformInfo* platform_info_ = nullptr;
scoped_refptr<worker::ServiceWorkerObject> service_worker_object_;
+ MockUserAgentPlatformInfo mock_platform_info_;
};
} // namespace testing
diff --git a/cobalt/web/testing/test_with_javascript.h b/cobalt/web/testing/test_with_javascript.h
new file mode 100644
index 0000000..f37276b
--- /dev/null
+++ b/cobalt/web/testing/test_with_javascript.h
@@ -0,0 +1,87 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_WEB_TESTING_TEST_WITH_JAVASCRIPT_H_
+#define COBALT_WEB_TESTING_TEST_WITH_JAVASCRIPT_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "cobalt/dom/dom_settings.h"
+#include "cobalt/dom/testing/stub_window.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/script/wrappable.h"
+#include "cobalt/web/window_or_worker_global_scope.h"
+#include "cobalt/worker/testing/test_with_javascript.h"
+
+namespace cobalt {
+namespace web {
+namespace testing {
+
+// Helper class for extending the worker tests with JavaScript to include
+// testing in Window global scope.
+template <class TypeIdProvider>
+class TestWithJavaScriptBase
+ : public worker::testing::TestWithJavaScriptBase<TypeIdProvider> {
+ public:
+ TestWithJavaScriptBase() {
+ if (TypeIdProvider::GetGlobalScopeTypeId() ==
+ base::GetTypeId<dom::Window>()) {
+ DCHECK(!this->worker_global_scope());
+ this->ClearWebContext();
+ window_.reset(new dom::testing::StubWindow());
+ }
+ }
+
+ WindowOrWorkerGlobalScope* window_or_worker_global_scope() {
+ return window_ ? window_->window().get() : this->worker_global_scope();
+ }
+
+ scoped_refptr<script::GlobalEnvironment> global_environment() override {
+ if (window_) return window_->global_environment();
+ return worker::testing::TestWithJavaScriptBase<
+ TypeIdProvider>::global_environment();
+ }
+
+ private:
+ std::unique_ptr<dom::testing::StubWindow> window_;
+};
+
+// Helper class for running TEST_P() tests on all known worker types.
+class TestWebWithJavaScript
+ : public TestWithJavaScriptBase<
+ worker::testing::GetGlobalScopeTypeIdWithParam> {
+ public:
+ // Return a vector of values for all known worker types, to be used in the
+ // INSTANTIATE_TEST_CASE_P() declaration.
+ static std::vector<base::TypeId> GetWorkerTypes() {
+ std::vector<base::TypeId> worker_types =
+ worker::testing::TestWorkersWithJavaScript::GetWorkerTypes();
+ worker_types.push_back(base::GetTypeId<dom::Window>());
+ return worker_types;
+ }
+ static std::string GetTypeName(::testing::TestParamInfo<base::TypeId> info) {
+ if (info.param == base::GetTypeId<dom::Window>()) {
+ return "Window";
+ }
+ return worker::testing::TestWorkersWithJavaScript::GetTypeName(info);
+ }
+};
+
+} // namespace testing
+} // namespace web
+} // namespace cobalt
+
+#endif // COBALT_WEB_TESTING_TEST_WITH_JAVASCRIPT_H_
diff --git a/cobalt/web/url.idl b/cobalt/web/url.idl
index eed2f23..fe75fd3 100644
--- a/cobalt/web/url.idl
+++ b/cobalt/web/url.idl
@@ -14,7 +14,8 @@
// https://www.w3.org/TR/2014/WD-url-1-20141209/#dom-url
[RaisesException=Constructor, Constructor(
- USVString url, optional USVString base = "about:blank")] interface URL {
+ USVString url, optional USVString base = "about:blank"),
+ Exposed=(Window,Worker)] interface URL {
[CallWith=EnvironmentSettings] static DOMString
createObjectURL(Blob blob);
[CallWith=EnvironmentSettings] static void revokeObjectURL(DOMString url);
diff --git a/cobalt/web/url_test.cc b/cobalt/web/url_test.cc
index 3521dc5..ee6f405 100644
--- a/cobalt/web/url_test.cc
+++ b/cobalt/web/url_test.cc
@@ -18,8 +18,8 @@
#include <string>
#include "base/logging.h"
-#include "cobalt/dom/testing/test_with_javascript.h"
#include "cobalt/web/testing/gtest_workarounds.h"
+#include "cobalt/web/testing/test_with_javascript.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -30,7 +30,6 @@
namespace cobalt {
namespace web {
-
namespace {
class URLTest : public ::testing::Test {
public:
@@ -44,7 +43,7 @@
StrictMock<MockExceptionState> exception_state_;
};
-class URLTestWithJavaScript : public dom::testing::TestWithJavaScript {};
+class URLTestWithJavaScript : public testing::TestWebWithJavaScript {};
} // namespace
TEST_F(URLTest, ConstructorWithValidURL) {
@@ -99,7 +98,7 @@
EXPECT_EQ("", url->search());
}
-TEST_F(URLTestWithJavaScript, ConstructorWithValidURL) {
+TEST_P(URLTestWithJavaScript, ConstructorWithValidURL) {
std::string result;
bool success = EvaluateScript(
"var url = new "
@@ -119,16 +118,13 @@
EXPECT_EQ("https://user:password@www.example.com:1234/foo/bar?baz#qux",
result);
- if (success) {
- LOG(INFO) << "Test result : "
- << "\"" << result << "\"";
- } else {
+ if (!success) {
DLOG(ERROR) << "Failed to evaluate test: "
<< "\"" << result << "\"";
}
}
-TEST_F(URLTestWithJavaScript, ConstructorWithInvalidBase) {
+TEST_P(URLTestWithJavaScript, ConstructorWithInvalidBase) {
std::string result;
bool success = EvaluateScript(
"let result = 'unknown';"
@@ -142,16 +138,13 @@
EXPECT_TRUE(success);
EXPECT_EQ("TypeError", result);
- if (success) {
- LOG(INFO) << "Test result : "
- << "\"" << result << "\"";
- } else {
+ if (!success) {
DLOG(ERROR) << "Failed to evaluate test: "
<< "\"" << result << "\"";
}
}
-TEST_F(URLTestWithJavaScript, ConstructorWithInvalidURL) {
+TEST_P(URLTestWithJavaScript, ConstructorWithInvalidURL) {
std::string result;
bool success = EvaluateScript(
"let result = 'unknown';"
@@ -165,14 +158,46 @@
EXPECT_TRUE(success);
EXPECT_EQ("TypeError", result);
- if (success) {
- LOG(INFO) << "Test result : "
- << "\"" << result << "\"";
- } else {
+ if (!success) {
DLOG(ERROR) << "Failed to evaluate test: "
<< "\"" << result << "\"";
}
}
+TEST_P(URLTestWithJavaScript, CreateObjectURL) {
+ std::string result;
+ bool success = EvaluateScript(
+ "let result = 'unknown';"
+ "function assert(condition, message) {"
+ " if (!condition) {"
+ " throw new Error(message || \"Assertion failed\");"
+ " }"
+ "}"
+ "try {"
+ " var blob = new Blob([\" TEST \"]);"
+ " assert(blob.size == 6, \'Blob has wrong size\');"
+ " var url = URL.createObjectURL(blob);"
+ " result = url;"
+ "} catch (e) {"
+ " result = e;"
+ "}"
+ "result;",
+ &result);
+ EXPECT_TRUE(success);
+ EXPECT_GT(result.length(), 5U);
+ EXPECT_TRUE(Value(result, ::testing::StartsWith("blob:")));
+
+ if (!success) {
+ DLOG(ERROR) << "Failed to evaluate test: "
+ << "\"" << result << "\"";
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ URLTestsWithJavaScript, URLTestWithJavaScript,
+ ::testing::ValuesIn(testing::TestWebWithJavaScript::GetWorkerTypes()),
+ testing::TestWebWithJavaScript::GetTypeName);
+
+
} // namespace web
} // namespace cobalt
diff --git a/cobalt/web/window_or_worker_global_scope.cc b/cobalt/web/window_or_worker_global_scope.cc
index baa40ac..26cfeff 100644
--- a/cobalt/web/window_or_worker_global_scope.cc
+++ b/cobalt/web/window_or_worker_global_scope.cc
@@ -39,6 +39,10 @@
return GetWrappableType() == base::GetTypeId<dom::Window>();
}
+bool WindowOrWorkerGlobalScope::IsWorker() {
+ return IsDedicatedWorker() || IsServiceWorker();
+}
+
bool WindowOrWorkerGlobalScope::IsDedicatedWorker() {
return GetWrappableType() ==
base::GetTypeId<worker::DedicatedWorkerGlobalScope>();
diff --git a/cobalt/web/window_or_worker_global_scope.h b/cobalt/web/window_or_worker_global_scope.h
index 2fd0294..6f079ca 100644
--- a/cobalt/web/window_or_worker_global_scope.h
+++ b/cobalt/web/window_or_worker_global_scope.h
@@ -32,6 +32,7 @@
class Window;
} // namespace dom
namespace worker {
+class WorkerGlobalScope;
class DedicatedWorkerGlobalScope;
class ServiceWorkerGlobalScope;
} // namespace worker
@@ -54,10 +55,12 @@
NavigatorBase* navigator_base() { return navigator_base_; }
bool IsWindow();
+ bool IsWorker();
bool IsDedicatedWorker();
bool IsServiceWorker();
virtual dom::Window* AsWindow() { return nullptr; }
+ virtual worker::WorkerGlobalScope* AsWorker() { return nullptr; }
virtual worker::DedicatedWorkerGlobalScope* AsDedicatedWorker() {
return nullptr;
}
diff --git a/cobalt/web/window_timers_test.cc b/cobalt/web/window_timers_test.cc
index d4a4ee7..f389997 100644
--- a/cobalt/web/window_timers_test.cc
+++ b/cobalt/web/window_timers_test.cc
@@ -43,9 +43,13 @@
public:
MOCK_CONST_METHOD0(Run, script::CallbackResult<void>());
void ExpectRunCall(int times) {
- EXPECT_CALL(*this, Run())
- .Times(times)
- .WillRepeatedly(Return(script::CallbackResult<void>()));
+ if (!times) {
+ EXPECT_CALL(*this, Run()).Times(times);
+ } else {
+ EXPECT_CALL(*this, Run())
+ .Times(times)
+ .WillRepeatedly(Return(script::CallbackResult<void>()));
+ }
}
};
diff --git a/cobalt/websocket/web_socket_impl_test.cc b/cobalt/websocket/web_socket_impl_test.cc
index ebbe3fc..66da04c 100644
--- a/cobalt/websocket/web_socket_impl_test.cc
+++ b/cobalt/websocket/web_socket_impl_test.cc
@@ -63,10 +63,9 @@
protected:
WebSocketImplTest() : web_context_(new web::testing::StubWebContext()) {
- web_context_->set_network_module(new network::NetworkModule());
web_context_->setup_environment_settings(
new dom::testing::StubEnvironmentSettings());
- web_context_->environment_settings()->set_base_url(
+ web_context_->environment_settings()->set_creation_url(
GURL("https://127.0.0.1:1234"));
std::vector<std::string> sub_protocols;
sub_protocols.push_back("chat");
diff --git a/cobalt/websocket/web_socket_test.cc b/cobalt/websocket/web_socket_test.cc
index 18c47b3..0547e88 100644
--- a/cobalt/websocket/web_socket_test.cc
+++ b/cobalt/websocket/web_socket_test.cc
@@ -19,13 +19,12 @@
#include "base/memory/ref_counted.h"
#include "cobalt/base/polymorphic_downcast.h"
-#include "cobalt/dom/testing/stub_environment_settings.h"
+#include "cobalt/dom/testing/test_with_javascript.h"
#include "cobalt/dom/window.h"
#include "cobalt/script/script_exception.h"
#include "cobalt/script/testing/mock_exception_state.h"
#include "cobalt/web/context.h"
#include "cobalt/web/dom_exception.h"
-#include "cobalt/web/testing/stub_web_context.h"
#include "testing/gtest/include/gtest/gtest.h"
using cobalt::script::testing::MockExceptionState;
@@ -36,35 +35,29 @@
namespace cobalt {
namespace websocket {
-class WebSocketTest : public ::testing::Test {
+class WebSocketTest : public dom::testing::TestWithJavaScript {
public:
- web::EnvironmentSettings* settings() const {
- return web_context_->environment_settings();
+ web::EnvironmentSettings* settings() {
+ return window()->environment_settings();
}
protected:
- WebSocketTest() : web_context_(new web::testing::StubWebContext()) {
- web_context_->set_network_module(new network::NetworkModule());
- web_context_->setup_environment_settings(
- new dom::testing::StubEnvironmentSettings());
- web_context_->environment_settings()->set_base_url(
- GURL("https://example.com"));
+ WebSocketTest() {
+ settings()->set_creation_url(GURL("https://example.com"));
+ window()->location()->set_url(settings()->creation_url());
}
-
- std::unique_ptr<web::testing::StubWebContext> web_context_;
- StrictMock<MockExceptionState> exception_state_;
};
TEST_F(WebSocketTest, BadOrigin) {
scoped_refptr<script::ScriptException> exception;
- settings()->set_base_url(GURL());
+ window()->location()->set_url(GURL());
- EXPECT_CALL(exception_state_, SetException(_))
+ EXPECT_CALL(*exception_state(), SetException(_))
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_, false));
+ new WebSocket(settings(), "ws://example.com", exception_state(), false));
ASSERT_TRUE(exception.get());
web::DOMException& dom_exception(
@@ -83,35 +76,35 @@
{"https://example.com/", "https://example.com/"},
{"https://exAMPle.com/", "https://example.com/"},
};
- scoped_refptr<script::ScriptException> exception;
+
+ EXPECT_CALL(*exception_state(), SetException(_)).Times(0);
for (std::size_t i(0); i != ARRAYSIZE_UNSAFE(origin_test_cases); ++i) {
const OriginTestCase& test_case(origin_test_cases[i]);
std::string new_base = test_case.input_base_url;
- settings()->set_base_url(GURL(new_base));
+ window()->location()->set_url(GURL(new_base));
scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
- &exception_state_, false));
+ exception_state(), false));
- ASSERT_FALSE(exception.get());
EXPECT_EQ(ws->entry_script_origin_, test_case.expected_output);
}
}
TEST_F(WebSocketTest, TestInitialReadyState) {
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_, false));
+ new WebSocket(settings(), "ws://example.com", exception_state(), false));
EXPECT_EQ(ws->ready_state(), WebSocket::kConnecting);
}
TEST_F(WebSocketTest, SyntaxErrorWhenBadScheme) {
scoped_refptr<script::ScriptException> exception;
- EXPECT_CALL(exception_state_, SetException(_))
+ EXPECT_CALL(*exception_state(), SetException(_))
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<WebSocket> ws(new WebSocket(
- settings(), "badscheme://example.com", &exception_state_, false));
+ settings(), "badscheme://example.com", exception_state(), false));
web::DOMException& dom_exception(
*base::polymorphic_downcast<web::DOMException*>(exception.get()));
@@ -124,24 +117,24 @@
}
TEST_F(WebSocketTest, ParseWsAndWssCorrectly) {
- EXPECT_CALL(exception_state_, SetException(_)).Times(0);
+ EXPECT_CALL(*exception_state(), SetException(_)).Times(0);
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com/", &exception_state_, false));
+ new WebSocket(settings(), "ws://example.com/", exception_state(), false));
- EXPECT_CALL(exception_state_, SetException(_)).Times(0);
+ EXPECT_CALL(*exception_state(), SetException(_)).Times(0);
scoped_refptr<WebSocket> wss(
- new WebSocket(settings(), "wss://example.com", &exception_state_, false));
+ new WebSocket(settings(), "wss://example.com", exception_state(), false));
}
TEST_F(WebSocketTest, CheckSecure) {
- EXPECT_CALL(exception_state_, SetException(_)).Times(0);
+ EXPECT_CALL(*exception_state(), SetException(_)).Times(0);
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com/", &exception_state_, false));
+ new WebSocket(settings(), "ws://example.com/", exception_state(), false));
EXPECT_FALSE(ws->IsSecure());
- EXPECT_CALL(exception_state_, SetException(_)).Times(0);
+ EXPECT_CALL(*exception_state(), SetException(_)).Times(0);
scoped_refptr<WebSocket> wss(
- new WebSocket(settings(), "wss://example.com", &exception_state_, false));
+ new WebSocket(settings(), "wss://example.com", exception_state(), false));
EXPECT_TRUE(wss->IsSecure());
}
@@ -150,11 +143,11 @@
TEST_F(WebSocketTest, SyntaxErrorWhenRelativeUrl) {
scoped_refptr<script::ScriptException> exception;
- EXPECT_CALL(exception_state_, SetException(_))
+ EXPECT_CALL(*exception_state(), SetException(_))
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "relative_url", &exception_state_, false));
+ new WebSocket(settings(), "relative_url", exception_state(), false));
web::DOMException& dom_exception(
*base::polymorphic_downcast<web::DOMException*>(exception.get()));
@@ -168,11 +161,11 @@
TEST_F(WebSocketTest, URLHasFragments) {
scoped_refptr<script::ScriptException> exception;
- EXPECT_CALL(exception_state_, SetException(_))
+ EXPECT_CALL(*exception_state(), SetException(_))
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<WebSocket> ws(new WebSocket(
- settings(), "wss://example.com/#fragment", &exception_state_, false));
+ settings(), "wss://example.com/#fragment", exception_state(), false));
web::DOMException& dom_exception(
*base::polymorphic_downcast<web::DOMException*>(exception.get()));
@@ -185,7 +178,7 @@
TEST_F(WebSocketTest, URLHasPort) {
scoped_refptr<WebSocket> ws(new WebSocket(settings(), "wss://example.com:789",
- &exception_state_, false));
+ exception_state(), false));
EXPECT_EQ(ws->GetPort(), 789);
EXPECT_EQ(ws->GetPortAsString(), "789");
@@ -197,13 +190,13 @@
// otherwise let port be 443.
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_, false));
+ new WebSocket(settings(), "ws://example.com", exception_state(), false));
EXPECT_EQ(ws->GetPort(), 80);
EXPECT_EQ(ws->GetPortAsString(), "80");
scoped_refptr<WebSocket> wss(
- new WebSocket(settings(), "wss://example.com", &exception_state_, false));
+ new WebSocket(settings(), "wss://example.com", exception_state(), false));
EXPECT_EQ(wss->GetPort(), 443);
EXPECT_EQ(wss->GetPortAsString(), "443");
@@ -214,7 +207,7 @@
// Let host be the value of the <host> component of url, converted to ASCII
// lowercase.
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "wss://eXAmpLe.com", &exception_state_, false));
+ new WebSocket(settings(), "wss://eXAmpLe.com", exception_state(), false));
std::string host(ws->GetHost());
EXPECT_EQ(host, "example.com");
@@ -226,7 +219,7 @@
// empty) of
// url.
scoped_refptr<WebSocket> ws(new WebSocket(
- settings(), "ws://eXAmpLe.com/resource_name", &exception_state_, false));
+ settings(), "ws://eXAmpLe.com/resource_name", exception_state(), false));
std::string resource_name(ws->GetResourceName());
EXPECT_EQ(resource_name, "/resource_name");
@@ -237,7 +230,7 @@
// If resource name is the empty string, set it to a single character U+002F
// SOLIDUS (/).
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_, false));
+ new WebSocket(settings(), "ws://example.com", exception_state(), false));
std::string resource_name(ws->GetResourceName());
EXPECT_EQ(resource_name, "/");
@@ -250,7 +243,7 @@
// component.
scoped_refptr<WebSocket> ws(
new WebSocket(settings(), "ws://example.com/resource_name?abc=xyz&j=3",
- &exception_state_, false));
+ exception_state(), false));
std::string resource_name(ws->GetResourceName());
EXPECT_EQ(resource_name, "/resource_name?abc=xyz&j=3");
@@ -259,11 +252,11 @@
TEST_F(WebSocketTest, FailInsecurePort) {
scoped_refptr<script::ScriptException> exception;
- EXPECT_CALL(exception_state_, SetException(_))
+ EXPECT_CALL(*exception_state(), SetException(_))
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com:22",
- &exception_state_, false));
+ exception_state(), false));
web::DOMException& dom_exception(
*base::polymorphic_downcast<web::DOMException*>(exception.get()));
@@ -300,13 +293,13 @@
scoped_refptr<script::ScriptException> exception;
if (test_case.exception_thrown) {
- EXPECT_CALL(exception_state_, SetException(_))
+ EXPECT_CALL(*exception_state(), SetException(_))
.WillOnce(SaveArg<0>(&exception));
}
scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
test_case.sub_protocol,
- &exception_state_, false));
+ exception_state(), false));
if (test_case.exception_thrown) {
web::DOMException& dom_exception(
@@ -325,7 +318,7 @@
sub_protocols.push_back("chat.example.com");
sub_protocols.push_back("bicker.example.com");
scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
- sub_protocols, &exception_state_,
+ sub_protocols, exception_state(),
false));
ASSERT_FALSE(exception.get());
@@ -334,7 +327,7 @@
scoped_refptr<script::ScriptException> exception;
std::string sub_protocol("chat");
scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
- sub_protocol, &exception_state_,
+ sub_protocol, exception_state(),
false));
ASSERT_FALSE(exception.get());
@@ -344,13 +337,13 @@
TEST_F(WebSocketTest, DuplicatedSubProtocols) {
scoped_refptr<script::ScriptException> exception;
- EXPECT_CALL(exception_state_, SetException(_))
+ EXPECT_CALL(*exception_state(), SetException(_))
.WillOnce(SaveArg<0>(&exception));
std::vector<std::string> sub_protocols;
sub_protocols.push_back("chat");
sub_protocols.push_back("chat");
scoped_refptr<WebSocket> ws(new WebSocket(
- settings(), "ws://example.com", sub_protocols, &exception_state_, false));
+ settings(), "ws://example.com", sub_protocols, exception_state(), false));
ASSERT_TRUE(exception.get());
diff --git a/cobalt/worker/service_worker_object.cc b/cobalt/worker/service_worker_object.cc
index d442ff1..193638b 100644
--- a/cobalt/worker/service_worker_object.cc
+++ b/cobalt/worker/service_worker_object.cc
@@ -163,7 +163,7 @@
// origin to an implementation-defined value, target browsing context to
// null, and active service worker to null.
web_context_->setup_environment_settings(new WorkerSettings());
- web_context_->environment_settings()->set_base_url(script_url_);
+ web_context_->environment_settings()->set_creation_url(script_url_);
scoped_refptr<ServiceWorkerGlobalScope> service_worker_global_scope =
new ServiceWorkerGlobalScope(web_context_->environment_settings(), this);
worker_global_scope_ = service_worker_global_scope;
@@ -186,8 +186,7 @@
#endif // ENABLE_DEBUGGER
// 8.5. Set workerGlobalScope’s url to serviceWorker’s script url.
- worker_global_scope_->set_url(
- web_context_->environment_settings()->base_url());
+ worker_global_scope_->set_url(script_url_);
// 8.6. Set workerGlobalScope’s policy container to serviceWorker’s script
// resource’s policy container.
// 8.7. Set workerGlobalScope’s type to serviceWorker’s type.
diff --git a/cobalt/worker/testing/BUILD.gn b/cobalt/worker/testing/BUILD.gn
index fbce0cf..e91994b 100644
--- a/cobalt/worker/testing/BUILD.gn
+++ b/cobalt/worker/testing/BUILD.gn
@@ -24,6 +24,7 @@
"//cobalt/base",
"//cobalt/browser:bindings",
"//cobalt/loader",
+ "//cobalt/network",
"//cobalt/script",
"//cobalt/web",
"//cobalt/web:dom_exception",
diff --git a/cobalt/worker/testing/test_with_javascript.h b/cobalt/worker/testing/test_with_javascript.h
index be37e56..f6499d5 100644
--- a/cobalt/worker/testing/test_with_javascript.h
+++ b/cobalt/worker/testing/test_with_javascript.h
@@ -21,6 +21,7 @@
#include "base/logging.h"
#include "cobalt/base/type_id.h"
+#include "cobalt/network/network_module.h"
#include "cobalt/script/exception_message.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/global_environment.h"
@@ -43,10 +44,8 @@
public:
TestWithJavaScriptBase() {
web_context_.reset(new web::testing::StubWebContext());
- web_context_->set_network_module(new network::NetworkModule());
web_context_->setup_environment_settings(new WorkerSettings());
- web_context_->environment_settings()->set_base_url(GURL("about:blank"));
- web_context_->set_fetcher_factory(new loader::FetcherFactory(NULL));
+ web_context_->environment_settings()->set_creation_url(GURL("about:blank"));
if (TypeIdProvider::GetGlobalScopeTypeId() ==
base::GetTypeId<DedicatedWorkerGlobalScope>()) {
@@ -73,18 +72,33 @@
}
}
+ ~TestWithJavaScriptBase() { ClearWebContext(); }
+
+ void ClearWebContext() {
+ dedicated_worker_global_scope_ = nullptr;
+ service_worker_object_ = nullptr;
+ containing_service_worker_registration_ = nullptr;
+ service_worker_global_scope_ = nullptr;
+ worker_global_scope_ = nullptr;
+ web_context_.reset();
+ }
+
WorkerGlobalScope* worker_global_scope() { return worker_global_scope_; }
+ virtual scoped_refptr<script::GlobalEnvironment> global_environment() {
+ return web_context_->global_environment();
+ }
+
bool EvaluateScript(const std::string& js_code, std::string* result) {
- DCHECK(web_context_->global_environment());
+ DCHECK(this->global_environment());
scoped_refptr<script::SourceCode> source_code =
script::SourceCode::CreateSourceCode(
js_code, base::SourceLocation(__FILE__, __LINE__, 1));
- web_context_->global_environment()->EnableEval();
- web_context_->global_environment()->SetReportEvalCallback(base::Closure());
+ this->global_environment()->EnableEval();
+ this->global_environment()->SetReportEvalCallback(base::Closure());
bool succeeded =
- web_context_->global_environment()->EvaluateScript(source_code, result);
+ this->global_environment()->EvaluateScript(source_code, result);
return succeeded;
}
@@ -132,6 +146,15 @@
base::GetTypeId<ServiceWorkerGlobalScope>()};
return worker_types;
}
+ static std::string GetTypeName(::testing::TestParamInfo<base::TypeId> info) {
+ if (info.param == base::GetTypeId<DedicatedWorkerGlobalScope>()) {
+ return "DedicatedWorkerGlobalScope";
+ }
+ if (info.param == base::GetTypeId<ServiceWorkerGlobalScope>()) {
+ return "ServiceWorkerGlobalScope";
+ }
+ return "Unknown";
+ }
};
// Helper classes for running TEST_F() tests on only one type of worker.
diff --git a/cobalt/worker/worker.cc b/cobalt/worker/worker.cc
index 3414b2e..495a95d 100644
--- a/cobalt/worker/worker.cc
+++ b/cobalt/worker/worker.cc
@@ -72,7 +72,7 @@
Worker::~Worker() { Abort(); }
void Worker::Initialize(web::Context* context) {
- // Algorithm for 'run a worker'
+ // Algorithm for 'run a worker':
// https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#run-a-worker
// 7. Let realm execution context be the result of creating a new
// JavaScript realm given agent and the following customizations:
@@ -80,11 +80,13 @@
// . For the global object, if is shared is true, create a new
// SharedWorkerGlobalScope object. Otherwise, create a new
// DedicatedWorkerGlobalScope object.
- // TODO: Actual type here should depend on derived class (e.g. dedicated,
- // shared, service)
web_context_->setup_environment_settings(
new WorkerSettings(options_.outside_port));
- web_context_->environment_settings()->set_base_url(options_.url);
+ // From algorithm for to setup up a worker environment settings object:
+ // https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-worker-environment-settings-object
+ // 5. Set settings object's creation URL to worker global scope's url.
+ web_context_->environment_settings()->set_creation_url(options_.url);
+ // Continue algorithm for 'run a worker'.
// 8. Let worker global scope be the global object of realm execution
// context's Realm component.
scoped_refptr<DedicatedWorkerGlobalScope> dedicated_worker_global_scope =
@@ -148,7 +150,7 @@
// 1. Set request's reserved client to inside settings.
// 2. Fetch request, and asynchronously wait to run the remaining steps as
// part of fetch's process response for the response response.
- const GURL& url = web_context_->environment_settings()->base_url();
+ const GURL& url = web_context_->environment_settings()->creation_url();
loader::Origin origin = loader::Origin(url.GetOrigin());
// Todo: implement csp check (b/225037465)
@@ -167,7 +169,7 @@
DCHECK(content);
// 14.3. "Set worker global scope's url to response's url."
worker_global_scope_->set_url(
- web_context_->environment_settings()->base_url());
+ web_context_->environment_settings()->creation_url());
// 14.4 - 14.10 initialize worker global scope
worker_global_scope_->Initialize();
// 14.11. Asynchronously complete the perform the fetch steps with response.
diff --git a/cobalt/worker/worker_global_scope.h b/cobalt/worker/worker_global_scope.h
index 72e1bab..f4e2291 100644
--- a/cobalt/worker/worker_global_scope.h
+++ b/cobalt/worker/worker_global_scope.h
@@ -74,7 +74,7 @@
void set_url(const GURL& url) { url_ = url; }
- const GURL Url() const { return url_; }
+ const GURL& Url() const { return url_; }
const web::EventTargetListenerInfo::EventListenerScriptValue*
onlanguagechange() {
@@ -106,6 +106,10 @@
event_listener);
}
+ // From web::WindowOrWorkerGlobalScope
+ //
+ WorkerGlobalScope* AsWorker() override { return this; }
+
// Custom, not in any spec.
//
bool LoadImportsAndReturnIfUpdated(
diff --git a/cobalt/worker/worker_global_scope_test.cc b/cobalt/worker/worker_global_scope_test.cc
index efa6207..67ee3ed 100644
--- a/cobalt/worker/worker_global_scope_test.cc
+++ b/cobalt/worker/worker_global_scope_test.cc
@@ -312,7 +312,8 @@
INSTANTIATE_TEST_CASE_P(
WorkerGlobalScopeTests, WorkerGlobalScopeTest,
- ::testing::ValuesIn(testing::TestWorkersWithJavaScript::GetWorkerTypes()));
+ ::testing::ValuesIn(testing::TestWorkersWithJavaScript::GetWorkerTypes()),
+ testing::TestWorkersWithJavaScript::GetTypeName);
} // namespace worker
} // namespace cobalt
diff --git a/cobalt/worker/worker_navigator_test.cc b/cobalt/worker/worker_navigator_test.cc
index 49e4b47..31b6340 100644
--- a/cobalt/worker/worker_navigator_test.cc
+++ b/cobalt/worker/worker_navigator_test.cc
@@ -93,7 +93,8 @@
INSTANTIATE_TEST_CASE_P(
WorkerNavigatorTests, WorkerNavigatorTest,
- ::testing::ValuesIn(testing::TestWorkersWithJavaScript::GetWorkerTypes()));
+ ::testing::ValuesIn(testing::TestWorkersWithJavaScript::GetWorkerTypes()),
+ testing::TestWorkersWithJavaScript::GetTypeName);
} // namespace worker
} // namespace cobalt
diff --git a/cobalt/worker/worker_settings.cc b/cobalt/worker/worker_settings.cc
index 3c730e3..8e53e10 100644
--- a/cobalt/worker/worker_settings.cc
+++ b/cobalt/worker/worker_settings.cc
@@ -14,10 +14,15 @@
#include "cobalt/worker/worker_settings.h"
+#include "base/logging.h"
#include "cobalt/base/debugger_hooks.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
+#include "cobalt/web/context.h"
+#include "cobalt/web/environment_settings.h"
+#include "cobalt/web/window_or_worker_global_scope.h"
+#include "cobalt/worker/worker_global_scope.h"
#include "url/gurl.h"
namespace cobalt {
@@ -26,5 +31,18 @@
WorkerSettings::WorkerSettings(web::MessagePort* message_port)
: web::EnvironmentSettings(), message_port_(message_port) {}
+
+const GURL& WorkerSettings::base_url() const {
+ // From algorithm for to setup up a worker environment settings object:
+ // https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-worker-environment-settings-object
+ // 3. Let settings object be a new environment settings object whose
+ // algorithms are defined as follows:
+ // The API base URL
+ // Return worker global scope's url.
+ DCHECK(context()->GetWindowOrWorkerGlobalScope()->IsWorker());
+ return context()->GetWindowOrWorkerGlobalScope()->AsWorker()->Url();
+}
+
+
} // namespace worker
} // namespace cobalt
diff --git a/cobalt/worker/worker_settings.h b/cobalt/worker/worker_settings.h
index 99fae7f..b2d7097 100644
--- a/cobalt/worker/worker_settings.h
+++ b/cobalt/worker/worker_settings.h
@@ -33,6 +33,10 @@
web::MessagePort* message_port() const { return message_port_; }
+ // From: script::EnvironmentSettings
+ //
+ const GURL& base_url() const override;
+
private:
// Outer message port.
web::MessagePort* message_port_ = nullptr;
diff --git a/cobalt/xhr/global_stats.cc b/cobalt/xhr/global_stats.cc
index d6dd0f7..fbbb9b6 100644
--- a/cobalt/xhr/global_stats.cc
+++ b/cobalt/xhr/global_stats.cc
@@ -31,7 +31,11 @@
GlobalStats::~GlobalStats() {}
-bool GlobalStats::CheckNoLeaks() { return num_xhrs_ == 0 && xhr_memory_ == 0; }
+bool GlobalStats::CheckNoLeaks() {
+ DCHECK(num_xhrs_ == 0);
+ DCHECK(xhr_memory_ == 0);
+ return num_xhrs_ == 0 && xhr_memory_ == 0;
+}
void GlobalStats::Add(xhr::XMLHttpRequest* object) { ++num_xhrs_; }
diff --git a/cobalt/xhr/xml_http_request.cc b/cobalt/xhr/xml_http_request.cc
index 831b5cb..6f16335 100644
--- a/cobalt/xhr/xml_http_request.cc
+++ b/cobalt/xhr/xml_http_request.cc
@@ -161,73 +161,236 @@
new dom::ProgressEvent(event_name, loaded, total, length_computable));
}
+#if !defined(COBALT_BUILD_TYPE_GOLD)
int s_xhr_sequence_num_ = 0;
+#endif // !defined(COBALT_BUILD_TYPE_GOLD)
// https://fetch.spec.whatwg.org/#concept-http-redirect-fetch
// 5. If request's redirect count is twenty, return a network error.
const int kRedirectLimit = 20;
} // namespace
-bool XMLHttpRequest::verbose_ = false;
+bool XMLHttpRequestImpl::verbose_ = false;
XMLHttpRequest::XMLHttpRequest(script::EnvironmentSettings* settings)
- : XMLHttpRequestEventTarget(settings),
- response_body_(new URLFetcherResponseWriter::Buffer(
- URLFetcherResponseWriter::Buffer::kString)),
- settings_(base::polymorphic_downcast<dom::DOMSettings*>(settings)),
- state_(kUnsent),
- response_type_(kDefault),
- timeout_ms_(0),
- method_(net::URLFetcher::GET),
- http_status_(0),
- with_credentials_(false),
- error_(false),
- sent_(false),
- stop_timeout_(false),
- upload_complete_(false),
- active_requests_count_(0),
- upload_listener_(false),
- is_cross_origin_(false),
- is_redirect_(false),
- redirect_times_(0),
- is_data_url_(false) {
- DCHECK(settings_);
+ : XMLHttpRequestEventTarget(settings) {
+ // Determine which implementation of XHR to use based on being in a window or
+ // worker.
+ if (environment_settings()
+ ->context()
+ ->GetWindowOrWorkerGlobalScope()
+ ->IsWindow()) {
+ xhr_impl_ = std::make_unique<DOMXMLHttpRequestImpl>(this);
+ } else if (environment_settings()
+ ->context()
+ ->GetWindowOrWorkerGlobalScope()
+ ->IsDedicatedWorker() ||
+ environment_settings()
+ ->context()
+ ->GetWindowOrWorkerGlobalScope()
+ ->IsServiceWorker()) {
+ xhr_impl_ = std::make_unique<XMLHttpRequestImpl>(this);
+ }
xhr::GlobalStats::GetInstance()->Add(this);
+#if !defined(COBALT_BUILD_TYPE_GOLD)
xhr_id_ = ++s_xhr_sequence_num_;
+#endif // !defined(COBALT_BUILD_TYPE_GOLD)
}
-void XMLHttpRequest::Abort() {
+XMLHttpRequest::~XMLHttpRequest() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ xhr::GlobalStats::GetInstance()->Remove(this);
+}
+
+void XMLHttpRequest::Abort() { xhr_impl_->Abort(); }
+void XMLHttpRequest::Open(const std::string& method, const std::string& url,
+ bool async,
+ const base::Optional<std::string>& username,
+ const base::Optional<std::string>& password,
+ script::ExceptionState* exception_state) {
+ xhr_impl_->Open(method, url, async, username, password, exception_state);
+}
+
+// Must be called after open(), but before send().
+void XMLHttpRequest::SetRequestHeader(const std::string& header,
+ const std::string& value,
+ script::ExceptionState* exception_state) {
+ xhr_impl_->SetRequestHeader(header, value, exception_state);
+}
+
+// Override the MIME type returned by the server.
+// Call before Send(), otherwise throws InvalidStateError.
+void XMLHttpRequest::OverrideMimeType(const std::string& mime_type,
+ script::ExceptionState* exception_state) {
+ xhr_impl_->OverrideMimeType(mime_type, exception_state);
+}
+
+void XMLHttpRequest::Send(const base::Optional<RequestBodyType>& request_body,
+ script::ExceptionState* exception_state) {
+ xhr_impl_->Send(request_body, exception_state);
+}
+
+void XMLHttpRequest::Fetch(const FetchUpdateCallbackArg& fetch_callback,
+ const FetchModeCallbackArg& fetch_mode_callback,
+ const base::Optional<RequestBodyType>& request_body,
+ script::ExceptionState* exception_state) {
+ xhr_impl_->Fetch(fetch_callback, fetch_mode_callback, request_body,
+ exception_state);
+}
+
+base::Optional<std::string> XMLHttpRequest::GetResponseHeader(
+ const std::string& header) {
+ return xhr_impl_->GetResponseHeader(header);
+}
+std::string XMLHttpRequest::GetAllResponseHeaders() {
+ return xhr_impl_->GetAllResponseHeaders();
+}
+
+const std::string& XMLHttpRequest::response_text(
+ script::ExceptionState* exception_state) {
+ return xhr_impl_->response_text(exception_state);
+}
+scoped_refptr<dom::Document> XMLHttpRequest::response_xml(
+ script::ExceptionState* exception_state) {
+ return xhr_impl_->response_xml(exception_state);
+}
+base::Optional<XMLHttpRequest::ResponseType> XMLHttpRequest::response(
+ script::ExceptionState* exception_state) {
+ return xhr_impl_->response(exception_state);
+}
+
+int XMLHttpRequest::ready_state() const { return xhr_impl_->ready_state(); }
+int XMLHttpRequest::status() const { return xhr_impl_->status(); }
+std::string XMLHttpRequest::status_text() { return xhr_impl_->status_text(); }
+void XMLHttpRequest::set_response_type(
+ const std::string& response_type, script::ExceptionState* exception_state) {
+ xhr_impl_->set_response_type(response_type, exception_state);
+}
+std::string XMLHttpRequest::response_type(
+ script::ExceptionState* exception_state) const {
+ return xhr_impl_->response_type(exception_state);
+}
+
+uint32 XMLHttpRequest::timeout() const { return xhr_impl_->timeout(); }
+void XMLHttpRequest::set_timeout(uint32 timeout) {
+ xhr_impl_->set_timeout(timeout);
+}
+bool XMLHttpRequest::with_credentials(
+ script::ExceptionState* exception_state) const {
+ return xhr_impl_->with_credentials(exception_state);
+}
+void XMLHttpRequest::set_with_credentials(
+ bool b, script::ExceptionState* exception_state) {
+ xhr_impl_->set_with_credentials(b, exception_state);
+}
+
+scoped_refptr<XMLHttpRequestUpload> XMLHttpRequest::upload() {
+ return xhr_impl_->upload();
+}
+
+void XMLHttpRequest::set_verbose(bool verbose) {
+ XMLHttpRequestImpl::set_verbose(verbose);
+}
+bool XMLHttpRequest::verbose() { return XMLHttpRequestImpl::verbose(); }
+
+// net::URLFetcherDelegate interface
+void XMLHttpRequest::OnURLFetchResponseStarted(const net::URLFetcher* source) {
+ xhr_impl_->OnURLFetchResponseStarted(source);
+}
+void XMLHttpRequest::OnURLFetchDownloadProgress(const net::URLFetcher* source,
+ int64_t current, int64_t total,
+ int64_t current_network_bytes) {
+ xhr_impl_->OnURLFetchDownloadProgress(source, current, total,
+ current_network_bytes);
+}
+void XMLHttpRequest::OnURLFetchComplete(const net::URLFetcher* source) {
+ xhr_impl_->OnURLFetchComplete(source);
+}
+
+void XMLHttpRequest::OnURLFetchUploadProgress(const net::URLFetcher* source,
+ int64 current, int64 total) {
+ xhr_impl_->OnURLFetchUploadProgress(source, current, total);
+}
+void XMLHttpRequest::OnRedirect(const net::HttpResponseHeaders& headers) {
+ xhr_impl_->OnRedirect(headers);
+}
+
+// Called from bindings layer to tie objects' lifetimes to this XHR instance.
+XMLHttpRequestUpload* XMLHttpRequest::upload_or_null() {
+ return xhr_impl_->upload_or_null();
+}
+
+void XMLHttpRequest::ReportLoadTimingInfo(
+ const net::LoadTimingInfo& timing_info) {
+ xhr_impl_->ReportLoadTimingInfo(timing_info);
+}
+// Create Performance Resource Timing entry for XMLHttpRequest.
+void XMLHttpRequest::GetLoadTimingInfoAndCreateResourceTiming() {
+ xhr_impl_->GetLoadTimingInfoAndCreateResourceTiming();
+}
+
+void XMLHttpRequest::TraceMembers(script::Tracer* tracer) {
+ XMLHttpRequestEventTarget::TraceMembers(tracer);
+ xhr_impl_->TraceMembers(tracer);
+}
+
+XMLHttpRequestImpl::XMLHttpRequestImpl(XMLHttpRequest* xhr)
+ : error_(false),
+ is_cross_origin_(false),
+ is_data_url_(false),
+ is_redirect_(false),
+ method_(net::URLFetcher::GET),
+ response_body_(new URLFetcherResponseWriter::Buffer(
+ URLFetcherResponseWriter::Buffer::kString)),
+ response_type_(XMLHttpRequest::kDefault),
+ state_(XMLHttpRequest::kUnsent),
+ upload_listener_(false),
+ with_credentials_(false),
+ xhr_(xhr),
+ active_requests_count_(0),
+ http_status_(0),
+ redirect_times_(0),
+ sent_(false),
+ settings_(xhr->environment_settings()),
+ stop_timeout_(false),
+ timeout_ms_(0),
+ upload_complete_(false) {
+ DCHECK(settings_);
+}
+
+void XMLHttpRequestImpl::Abort() {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-abort()-method
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Cancel any in-flight request and set error flag.
TerminateRequest();
- bool abort_is_no_op =
- state_ == kUnsent || state_ == kDone || (state_ == kOpened && !sent_);
+ bool abort_is_no_op = state_ == XMLHttpRequest::kUnsent ||
+ state_ == XMLHttpRequest::kDone ||
+ (state_ == XMLHttpRequest::kOpened && !sent_);
if (!abort_is_no_op) {
sent_ = false;
- HandleRequestError(kAbortError);
+ HandleRequestError(XMLHttpRequest::kAbortError);
}
- ChangeState(kUnsent);
+ ChangeState(XMLHttpRequest::kUnsent);
response_body_->Clear();
response_array_buffer_reference_.reset();
}
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-open()-method
-void XMLHttpRequest::Open(const std::string& method, const std::string& url,
- bool async,
- const base::Optional<std::string>& username,
- const base::Optional<std::string>& password,
- script::ExceptionState* exception_state) {
+void XMLHttpRequestImpl::Open(const std::string& method, const std::string& url,
+ bool async,
+ const base::Optional<std::string>& username,
+ const base::Optional<std::string>& password,
+ script::ExceptionState* exception_state) {
TRACK_MEMORY_SCOPE("XHR");
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- State previous_state = state_;
+ XMLHttpRequest::State previous_state = state_;
// Cancel any outstanding request.
TerminateRequest();
- state_ = kUnsent;
+ state_ = XMLHttpRequest::kUnsent;
if (!async) {
DLOG(ERROR) << "synchronous XHR is not supported";
@@ -254,7 +417,7 @@
return;
}
- web::CspDelegate* csp = csp_delegate();
+ web::CspDelegate* csp = this->csp_delegate();
if (csp && !csp->CanLoad(web::CspDelegate::kXhr, request_url_, false)) {
web::DOMException::Raise(web::DOMException::kSecurityErr, exception_state);
return;
@@ -267,19 +430,19 @@
// Check previous state to avoid dispatching readyState event when calling
// open several times in a row.
- if (previous_state != kOpened) {
- ChangeState(kOpened);
+ if (previous_state != XMLHttpRequest::kOpened) {
+ ChangeState(XMLHttpRequest::kOpened);
} else {
- state_ = kOpened;
+ state_ = XMLHttpRequest::kOpened;
}
}
-void XMLHttpRequest::SetRequestHeader(const std::string& header,
- const std::string& value,
- script::ExceptionState* exception_state) {
+void XMLHttpRequestImpl::SetRequestHeader(
+ const std::string& header, const std::string& value,
+ script::ExceptionState* exception_state) {
TRACK_MEMORY_SCOPE("XHR");
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#dom-xmlhttprequest-setrequestheader
- if (state_ != kOpened || sent_) {
+ if (state_ != XMLHttpRequest::kOpened || sent_) {
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
exception_state);
return;
@@ -307,11 +470,11 @@
}
}
-void XMLHttpRequest::OverrideMimeType(const std::string& override_mime,
- script::ExceptionState* exception_state) {
+void XMLHttpRequestImpl::OverrideMimeType(
+ const std::string& override_mime, script::ExceptionState* exception_state) {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#dom-xmlhttprequest-overridemimetype
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (state_ == kLoading || state_ == kDone) {
+ if (state_ == XMLHttpRequest::kLoading || state_ == XMLHttpRequest::kDone) {
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
exception_state);
return;
@@ -332,17 +495,14 @@
mime_type_override_ = mime_type;
}
-void XMLHttpRequest::Send(script::ExceptionState* exception_state) {
- Send(base::nullopt, exception_state);
-}
-
-void XMLHttpRequest::Send(const base::Optional<RequestBodyType>& request_body,
- script::ExceptionState* exception_state) {
+void XMLHttpRequestImpl::Send(
+ const base::Optional<XMLHttpRequest::RequestBodyType>& request_body,
+ script::ExceptionState* exception_state) {
TRACK_MEMORY_SCOPE("XHR");
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-send()-method
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Step 1
- if (state_ != kOpened) {
+ if (state_ != XMLHttpRequest::kOpened) {
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
exception_state);
return;
@@ -398,22 +558,22 @@
if (upload_) {
upload_listener_ = upload_->HasOneOrMoreAttributeEventListener();
}
- origin_ = settings_->document_origin();
+ origin_ = loader::Origin(settings_->GetOrigin());
// Step 9
sent_ = true;
// Now that a send is happening, prevent this object
// from being collected until it's complete or aborted
// if no currently active request has called it before.
IncrementActiveRequests();
- FireProgressEvent(this, base::Tokens::loadstart());
+ FireProgressEvent(xhr_, base::Tokens::loadstart());
if (!upload_complete_) {
FireProgressEvent(upload_, base::Tokens::loadstart());
}
// The loadstart callback may abort or modify the XHR request in some way.
// 11.3. If state is not opened or the send() flag is unset, then return.
- if (state_ == kOpened && sent_) {
- StartRequest(request_body_text_);
+ if (state_ == XMLHttpRequest::kOpened && sent_) {
+ this->StartRequest(request_body_text_);
// Start the timeout timer running, if applicable.
send_start_time_ = base::TimeTicks::Now();
@@ -426,23 +586,25 @@
}
}
-void XMLHttpRequest::Fetch(const FetchUpdateCallbackArg& fetch_callback,
- const FetchModeCallbackArg& fetch_mode_callback,
- const base::Optional<RequestBodyType>& request_body,
- script::ExceptionState* exception_state) {
+void XMLHttpRequestImpl::Fetch(
+ const FetchUpdateCallbackArg& fetch_callback,
+ const FetchModeCallbackArg& fetch_mode_callback,
+ const base::Optional<XMLHttpRequest::RequestBodyType>& request_body,
+ script::ExceptionState* exception_state) {
fetch_callback_.reset(
- new FetchUpdateCallbackArg::Reference(this, fetch_callback));
+ new FetchUpdateCallbackArg::Reference(xhr_, fetch_callback));
fetch_mode_callback_.reset(
- new FetchModeCallbackArg::Reference(this, fetch_mode_callback));
+ new FetchModeCallbackArg::Reference(xhr_, fetch_mode_callback));
Send(request_body, exception_state);
}
-base::Optional<std::string> XMLHttpRequest::GetResponseHeader(
+base::Optional<std::string> XMLHttpRequestImpl::GetResponseHeader(
const std::string& header) {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-getresponseheader()-method
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (state_ == kUnsent || state_ == kOpened || error_) {
+ if (state_ == XMLHttpRequest::kUnsent || state_ == XMLHttpRequest::kOpened ||
+ error_) {
return base::nullopt;
}
@@ -463,12 +625,13 @@
return found ? base::make_optional(value) : base::nullopt;
}
-std::string XMLHttpRequest::GetAllResponseHeaders() {
+std::string XMLHttpRequestImpl::GetAllResponseHeaders() {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-getallresponseheaders()-method
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::string output;
- if (state_ == kUnsent || state_ == kOpened || error_) {
+ if (state_ == XMLHttpRequest::kUnsent || state_ == XMLHttpRequest::kOpened ||
+ error_) {
return output;
}
@@ -485,21 +648,23 @@
return output;
}
-const std::string& XMLHttpRequest::response_text(
+const std::string& XMLHttpRequestImpl::response_text(
script::ExceptionState* exception_state) {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-responsetext-attribute
- if (response_type_ != kDefault && response_type_ != kText) {
+ if (response_type_ != XMLHttpRequest::kDefault &&
+ response_type_ != XMLHttpRequest::kText) {
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
exception_state);
}
- if (error_ || (state_ != kLoading && state_ != kDone)) {
+ if (error_ ||
+ (state_ != XMLHttpRequest::kLoading && state_ != XMLHttpRequest::kDone)) {
return base::EmptyString();
}
// Note that the conversion from |response_body_| to std::string when |state_|
// isn't kDone isn't efficient for large responses. Fortunately this feature
// is rarely used.
- if (state_ == kLoading) {
+ if (state_ == XMLHttpRequest::kLoading) {
LOG_ONCE(WARNING)
<< "Retrieving responseText while loading can be inefficient.";
return response_body_->GetTemporaryReferenceOfString();
@@ -508,84 +673,70 @@
}
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-responsexml-attribute
-scoped_refptr<dom::Document> XMLHttpRequest::response_xml(
+scoped_refptr<dom::Document> XMLHttpRequestImpl::response_xml(
script::ExceptionState* exception_state) {
- // 1. If responseType is not the empty string or "document", throw an
- // "InvalidStateError" exception.
- if (response_type_ != kDefault && response_type_ != kDocument) {
- web::DOMException::Raise(web::DOMException::kInvalidStateErr,
- exception_state);
- return NULL;
- }
-
- // 2. If the state is not DONE, return null.
- if (state_ != kDone) {
- return NULL;
- }
-
- // 3. If the error flag is set, return null.
- if (error_) {
- return NULL;
- }
-
- // 4. Return the document response entity body.
- return GetDocumentResponseEntityBody();
+ // Workers don't have access to DOM APIs, including Document objects. Nothing
+ // to do.
+ // https://www.w3.org/TR/2012/CR-workers-20120501/#apis-available-to-workers
+ return NULL;
}
-base::Optional<XMLHttpRequest::ResponseType> XMLHttpRequest::response(
+base::Optional<XMLHttpRequest::ResponseType> XMLHttpRequestImpl::response(
script::ExceptionState* exception_state) {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#response
switch (response_type_) {
- case kDefault:
- case kText:
- return ResponseType(response_text(exception_state));
- case kArrayBuffer: {
+ case XMLHttpRequest::kDefault:
+ case XMLHttpRequest::kText:
+ return XMLHttpRequest::ResponseType(response_text(exception_state));
+ case XMLHttpRequest::kArrayBuffer: {
script::Handle<script::ArrayBuffer> maybe_array_buffer_response =
response_array_buffer();
if (maybe_array_buffer_response.IsEmpty()) {
return base::nullopt;
}
- return ResponseType(maybe_array_buffer_response);
+ return XMLHttpRequest::ResponseType(maybe_array_buffer_response);
}
- case kJson:
- case kDocument:
- case kBlob:
- case kResponseTypeCodeMax:
+ case XMLHttpRequest::kJson:
+ case XMLHttpRequest::kDocument:
+ case XMLHttpRequest::kBlob:
+ case XMLHttpRequest::kResponseTypeCodeMax:
NOTIMPLEMENTED() << "Unsupported response_type_ "
<< response_type(exception_state);
}
return base::nullopt;
}
-int XMLHttpRequest::status() const {
+int XMLHttpRequestImpl::status() const {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-status-attribute
- if (state_ == kUnsent || state_ == kOpened || error_) {
+ if (state_ == XMLHttpRequest::kUnsent || state_ == XMLHttpRequest::kOpened ||
+ error_) {
return 0;
} else {
return http_status_;
}
}
-std::string XMLHttpRequest::status_text() {
+std::string XMLHttpRequestImpl::status_text() {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-statustext-attribute
- if (state_ == kUnsent || state_ == kOpened || error_) {
+ if (state_ == XMLHttpRequest::kUnsent || state_ == XMLHttpRequest::kOpened ||
+ error_) {
return std::string();
}
return http_response_headers_->GetStatusText();
}
-void XMLHttpRequest::set_response_type(
+void XMLHttpRequestImpl::set_response_type(
const std::string& response_type, script::ExceptionState* exception_state) {
- if (state_ == kLoading || state_ == kDone) {
+ if (state_ == XMLHttpRequest::kLoading || state_ == XMLHttpRequest::kDone) {
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
exception_state);
return;
}
for (size_t i = 0; i < arraysize(kResponseTypes); ++i) {
if (response_type == kResponseTypes[i]) {
- DCHECK_LT(i, kResponseTypeCodeMax);
- response_type_ = static_cast<ResponseTypeCode>(i);
+ DCHECK_LT(i, XMLHttpRequest::kResponseTypeCodeMax);
+ response_type_ = static_cast<XMLHttpRequest::ResponseTypeCode>(i);
return;
}
}
@@ -593,14 +744,14 @@
DLOG(WARNING) << "Unexpected response type " << response_type;
}
-std::string XMLHttpRequest::response_type(
+std::string XMLHttpRequestImpl::response_type(
script::ExceptionState* unused) const {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-responsetype-attribute
DCHECK_LT(response_type_, arraysize(kResponseTypes));
return kResponseTypes[response_type_];
}
-void XMLHttpRequest::set_timeout(uint32 timeout) {
+void XMLHttpRequestImpl::set_timeout(uint32 timeout) {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-timeout-attribute
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -615,14 +766,17 @@
}
}
-bool XMLHttpRequest::with_credentials(script::ExceptionState* unused) const {
+bool XMLHttpRequestImpl::with_credentials(
+ script::ExceptionState* unused) const {
return with_credentials_;
}
-void XMLHttpRequest::set_with_credentials(
+void XMLHttpRequestImpl::set_with_credentials(
bool with_credentials, script::ExceptionState* exception_state) {
// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-withcredentials-attribute
- if ((state_ != kUnsent && state_ != kOpened) || sent_) {
+ if ((state_ != XMLHttpRequest::kUnsent &&
+ state_ != XMLHttpRequest::kOpened) ||
+ sent_) {
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
exception_state);
return;
@@ -630,20 +784,21 @@
with_credentials_ = with_credentials;
}
-scoped_refptr<XMLHttpRequestUpload> XMLHttpRequest::upload() {
+scoped_refptr<XMLHttpRequestUpload> XMLHttpRequestImpl::upload() {
if (!upload_) {
upload_ = new XMLHttpRequestUpload(settings_);
}
return upload_;
}
-void XMLHttpRequest::OnURLFetchResponseStarted(const net::URLFetcher* source) {
+void XMLHttpRequestImpl::OnURLFetchResponseStarted(
+ const net::URLFetcher* source) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
http_status_ = source->GetResponseCode();
// Don't handle a response without headers.
if (!source->GetResponseHeaders()) {
- HandleRequestError(kNetworkError);
+ HandleRequestError(XMLHttpRequest::kNetworkError);
return;
}
// Copy the response headers from the fetcher. It's not safe for us to
@@ -657,7 +812,7 @@
if (!loader::CORSPreflight::CORSCheck(*http_response_headers_,
origin_.SerializedOrigin(),
with_credentials_)) {
- HandleRequestError(kNetworkError);
+ HandleRequestError(XMLHttpRequest::kNetworkError);
return;
}
}
@@ -711,24 +866,24 @@
}
}
- ChangeState(kHeadersReceived);
+ ChangeState(XMLHttpRequest::kHeadersReceived);
UpdateProgress(0);
}
-void XMLHttpRequest::OnURLFetchDownloadProgress(const net::URLFetcher* source,
- int64_t current, int64_t total,
- int64_t current_network_bytes) {
+void XMLHttpRequestImpl::OnURLFetchDownloadProgress(
+ const net::URLFetcher* source, int64_t current, int64_t total,
+ int64_t current_network_bytes) {
TRACK_MEMORY_SCOPE("XHR");
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK_NE(state_, kDone);
+ DCHECK_NE(state_, XMLHttpRequest::kDone);
if (response_body_->HasProgressSinceLastGetAndReset() == 0) {
return;
}
// Signal to JavaScript that new data is now available.
- ChangeState(kLoading);
+ ChangeState(XMLHttpRequest::kLoading);
if (fetch_callback_) {
std::string downloaded_data;
@@ -754,7 +909,8 @@
}
}
-void XMLHttpRequest::OnURLFetchComplete(const net::URLFetcher* source) {
+
+void XMLHttpRequestImpl::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (source->GetResponseHeaders()) {
if (source->GetResponseHeaders()->IsRedirect(NULL)) {
@@ -766,7 +922,7 @@
return;
}
// Create Performance Resource Timing entry after fetch complete.
- GetLoadTimingInfoAndCreateResourceTiming();
+ this->GetLoadTimingInfoAndCreateResourceTiming();
}
const net::URLRequestStatus& status = source->GetStatus();
@@ -792,33 +948,21 @@
FireProgressEvent(upload_, base::Tokens::load());
FireProgressEvent(upload_, base::Tokens::loadend());
}
- ChangeState(kDone);
+ ChangeState(XMLHttpRequest::kDone);
UpdateProgress(response_body_->GetAndResetDownloadProgress());
// Undo the ref we added in Send()
DecrementActiveRequests();
} else {
- HandleRequestError(kNetworkError);
+ HandleRequestError(XMLHttpRequest::kNetworkError);
}
fetch_callback_.reset();
fetch_mode_callback_.reset();
}
-// Reset some variables in case the XHR object is reused.
-void XMLHttpRequest::PrepareForNewRequest() {
- request_headers_.Clear();
- // Below are variables used for CORS.
- request_body_text_.clear();
- is_cross_origin_ = false;
- redirect_times_ = 0;
- is_data_url_ = false;
- upload_listener_ = false;
- is_redirect_ = false;
-}
-
-void XMLHttpRequest::OnURLFetchUploadProgress(const net::URLFetcher* source,
- int64 current_val,
- int64 total_val) {
+void XMLHttpRequestImpl::OnURLFetchUploadProgress(const net::URLFetcher* source,
+ int64 current_val,
+ int64 total_val) {
TRACK_MEMORY_SCOPE("XHR");
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (upload_complete_) {
@@ -863,14 +1007,14 @@
}
}
-void XMLHttpRequest::OnRedirect(const net::HttpResponseHeaders& headers) {
+void XMLHttpRequestImpl::OnRedirect(const net::HttpResponseHeaders& headers) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
GURL new_url = url_fetcher_->GetURL();
// Since we moved redirect from url_request to here, we also need to
// handle redirecting too many times.
if (redirect_times_ >= kRedirectLimit) {
DLOG(INFO) << "XHR's redirect times hit limit, aborting request.";
- HandleRequestError(kNetworkError);
+ HandleRequestError(XMLHttpRequest::kNetworkError);
return;
}
@@ -886,30 +1030,31 @@
if (loader::Origin(new_url) != loader::Origin(request_url_)) {
DLOG(INFO) << "XHR is redirected to cross-origin url with credentials, "
"aborting request for security reasons.";
- HandleRequestError(kNetworkError);
+ HandleRequestError(XMLHttpRequest::kNetworkError);
return;
} else if (is_cross_origin_) {
DLOG(INFO) << "XHR is redirected with credentials and cors_flag set, "
"aborting request for security reasons.";
- HandleRequestError(kNetworkError);
+ HandleRequestError(XMLHttpRequest::kNetworkError);
return;
}
}
if (!new_url.is_valid()) {
- HandleRequestError(kNetworkError);
+ HandleRequestError(XMLHttpRequest::kNetworkError);
return;
}
// This is a redirect. Re-check the CSP.
- if (!csp_delegate()->CanLoad(web::CspDelegate::kXhr, new_url,
- true /* is_redirect */)) {
- HandleRequestError(kNetworkError);
+ web::CspDelegate* csp = this->csp_delegate();
+ if (csp &&
+ !csp->CanLoad(web::CspDelegate::kXhr, new_url, true /* is_redirect */)) {
+ HandleRequestError(XMLHttpRequest::kNetworkError);
return;
}
// CORS check for the received response
if (is_cross_origin_) {
if (!loader::CORSPreflight::CORSCheck(headers, origin_.SerializedOrigin(),
with_credentials_)) {
- HandleRequestError(kNetworkError);
+ HandleRequestError(XMLHttpRequest::kNetworkError);
return;
}
}
@@ -935,42 +1080,45 @@
}
request_url_ = new_url;
redirect_times_++;
- StartRequest(request_body_text_);
+ this->StartRequest(request_body_text_);
}
-void XMLHttpRequest::TraceMembers(script::Tracer* tracer) {
- XMLHttpRequestEventTarget::TraceMembers(tracer);
+void XMLHttpRequestImpl::ReportLoadTimingInfo(
+ const net::LoadTimingInfo& timing_info) {
+ load_timing_info_ = timing_info;
+}
+void XMLHttpRequestImpl::GetLoadTimingInfoAndCreateResourceTiming() {
+ // Performance info is only available through window currently. Not available
+ // in workers.
+ return;
+}
+
+void XMLHttpRequestImpl::TraceMembers(script::Tracer* tracer) {
tracer->Trace(upload_);
}
-XMLHttpRequest::~XMLHttpRequest() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- xhr::GlobalStats::GetInstance()->Remove(this);
+web::CspDelegate* XMLHttpRequestImpl::csp_delegate() const {
+ // TODO (b/239733363): csp_delegate is currently available through window.
+ // Refactor to make it available outside of window then implement this. At
+ // that point, there should be no more need to override this function in
+ // DOMXMLHttpRequestImpl.
+ return NULL;
}
-web::CspDelegate* XMLHttpRequest::csp_delegate() const {
- DCHECK(settings_);
- if (settings_->window() && settings_->window()->document()) {
- return settings_->window()->document()->csp_delegate();
- } else {
- return NULL;
- }
-}
-
-void XMLHttpRequest::TerminateRequest() {
+void XMLHttpRequestImpl::TerminateRequest() {
error_ = true;
corspreflight_.reset(NULL);
url_fetcher_.reset(NULL);
}
-void XMLHttpRequest::HandleRequestError(
+void XMLHttpRequestImpl::HandleRequestError(
XMLHttpRequest::RequestErrorType request_error_type) {
// https://www.w3.org/TR/XMLHttpRequest/#timeout-error
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DLOG_IF(INFO, verbose())
<< __FUNCTION__ << " (" << RequestErrorTypeName(request_error_type)
- << ") " << *this << std::endl
+ << ") " << *xhr_ << std::endl
<< script::StackTraceToString(
settings_->context()->global_environment()->GetStackTrace(
0 /*max_frames*/));
@@ -979,7 +1127,7 @@
TerminateRequest();
// Steps 2-4
// Change state and fire readystatechange event.
- ChangeState(kDone);
+ ChangeState(XMLHttpRequest::kDone);
base::Token error_name = RequestErrorTypeName(request_error_type);
// Step 5
@@ -991,23 +1139,23 @@
}
// Steps 6-8
- FireProgressEvent(this, base::Tokens::progress());
- FireProgressEvent(this, error_name);
- FireProgressEvent(this, base::Tokens::loadend());
+ FireProgressEvent(xhr_, base::Tokens::progress());
+ FireProgressEvent(xhr_, error_name);
+ FireProgressEvent(xhr_, base::Tokens::loadend());
fetch_callback_.reset();
fetch_mode_callback_.reset();
DecrementActiveRequests();
}
-void XMLHttpRequest::OnTimeout() {
+void XMLHttpRequestImpl::OnTimeout() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!stop_timeout_) {
- HandleRequestError(kTimeoutError);
+ HandleRequestError(XMLHttpRequest::kTimeoutError);
}
}
-void XMLHttpRequest::StartTimer(base::TimeDelta time_since_send) {
+void XMLHttpRequestImpl::StartTimer(base::TimeDelta time_since_send) {
// Subtract any time that has already elapsed from the timeout.
// This is in case the user has set a timeout after send() was already in
// flight.
@@ -1017,27 +1165,28 @@
// Queue the callback even if delay ends up being zero, to preserve the
// previous semantics.
- timer_.Start(FROM_HERE, delay, this, &XMLHttpRequest::OnTimeout);
+ timer_.Start(FROM_HERE, delay, this, &XMLHttpRequestImpl::OnTimeout);
}
-void XMLHttpRequest::ChangeState(XMLHttpRequest::State new_state) {
+void XMLHttpRequestImpl::ChangeState(XMLHttpRequest::State new_state) {
// Always dispatch state change events for LOADING, also known as
// INTERACTIVE, so that clients can get partial data (XHR streaming).
// This is to match the behavior of Chrome (which took it from Firefox).
- if (state_ == new_state && new_state != kLoading) {
+ if (state_ == new_state && new_state != XMLHttpRequest::kLoading) {
return;
}
state_ = new_state;
- if (state_ != kUnsent) {
- DispatchEvent(new web::Event(base::Tokens::readystatechange()));
+ if (state_ != XMLHttpRequest::kUnsent) {
+ xhr_->DispatchEvent(new web::Event(base::Tokens::readystatechange()));
}
}
-script::Handle<script::ArrayBuffer> XMLHttpRequest::response_array_buffer() {
+script::Handle<script::ArrayBuffer>
+XMLHttpRequestImpl::response_array_buffer() {
TRACK_MEMORY_SCOPE("XHR");
// https://www.w3.org/TR/XMLHttpRequest/#response-entity-body
- if (error_ || state_ != kDone) {
+ if (error_ || state_ != XMLHttpRequest::kDone) {
// Return a handle holding a nullptr.
return script::Handle<script::ArrayBuffer>();
}
@@ -1051,7 +1200,7 @@
auto array_buffer = script::ArrayBuffer::New(
settings_->context()->global_environment(), std::move(downloaded_data));
response_array_buffer_reference_.reset(
- new script::ScriptValue<script::ArrayBuffer>::Reference(this,
+ new script::ScriptValue<script::ArrayBuffer>::Reference(xhr_,
array_buffer));
return array_buffer;
} else {
@@ -1060,7 +1209,7 @@
}
}
-void XMLHttpRequest::UpdateProgress(int64_t received_length) {
+void XMLHttpRequestImpl::UpdateProgress(int64_t received_length) {
DCHECK(http_response_headers_);
const int64 content_length = http_response_headers_->GetContentLength();
const bool length_computable =
@@ -1069,62 +1218,30 @@
length_computable ? static_cast<uint64>(content_length) : 0;
DLOG_IF(INFO, verbose()) << __FUNCTION__ << " (" << received_length << " / "
- << total << ") " << *this;
+ << total << ") " << *xhr_;
- if (state_ == kDone) {
- FireProgressEvent(this, base::Tokens::load(),
+ if (state_ == XMLHttpRequest::kDone) {
+ FireProgressEvent(xhr_, base::Tokens::load(),
static_cast<uint64>(received_length), total,
length_computable);
- FireProgressEvent(this, base::Tokens::loadend(),
+ FireProgressEvent(xhr_, base::Tokens::loadend(),
static_cast<uint64>(received_length), total,
length_computable);
} else {
- FireProgressEvent(this, base::Tokens::progress(),
+ FireProgressEvent(xhr_, base::Tokens::progress(),
static_cast<uint64>(received_length), total,
length_computable);
}
}
-void XMLHttpRequest::IncrementActiveRequests() {
- if (active_requests_count_ == 0) {
- DCHECK(settings_);
- DCHECK(settings_->context());
- prevent_gc_until_send_complete_.reset(
- new script::GlobalEnvironment::ScopedPreventGarbageCollection(
- settings_->context()->global_environment(), this));
- }
- active_requests_count_++;
-}
-
-void XMLHttpRequest::DecrementActiveRequests() {
- DCHECK_GT(active_requests_count_, 0);
- active_requests_count_--;
- if (active_requests_count_ == 0) {
- bool is_active = (state_ == kOpened && sent_) ||
- state_ == kHeadersReceived || state_ == kLoading;
- bool has_event_listeners =
- GetAttributeEventListener(base::Tokens::readystatechange()) ||
- GetAttributeEventListener(base::Tokens::progress()) ||
- GetAttributeEventListener(base::Tokens::abort()) ||
- GetAttributeEventListener(base::Tokens::error()) ||
- GetAttributeEventListener(base::Tokens::load()) ||
- GetAttributeEventListener(base::Tokens::timeout()) ||
- GetAttributeEventListener(base::Tokens::loadend());
-
- DCHECK_EQ((is_active && has_event_listeners), false);
-
- prevent_gc_until_send_complete_.reset();
- }
-}
-
-void XMLHttpRequest::StartRequest(const std::string& request_body) {
+void XMLHttpRequestImpl::StartRequest(const std::string& request_body) {
TRACK_MEMORY_SCOPE("XHR");
response_array_buffer_reference_.reset();
network::NetworkModule* network_module =
settings_->context()->fetcher_factory()->network_module();
- url_fetcher_ = net::URLFetcher::Create(request_url_, method_, this);
+ url_fetcher_ = net::URLFetcher::Create(request_url_, method_, xhr_);
++url_fetcher_generation_;
url_fetcher_->SetRequestContext(network_module->url_request_context_getter());
if (fetch_callback_) {
@@ -1133,7 +1250,195 @@
response_body_->DisablePreallocate();
} else {
response_body_ = new URLFetcherResponseWriter::Buffer(
- response_type_ == kArrayBuffer
+ response_type_ == XMLHttpRequest::kArrayBuffer
+ ? URLFetcherResponseWriter::Buffer::kArrayBuffer
+ : URLFetcherResponseWriter::Buffer::kString);
+ }
+ std::unique_ptr<net::URLFetcherResponseWriter> download_data_writer(
+ new URLFetcherResponseWriter(response_body_));
+ url_fetcher_->SaveResponseWithWriter(std::move(download_data_writer));
+ // Don't retry, let the caller deal with it.
+ url_fetcher_->SetAutomaticallyRetryOn5xx(false);
+ url_fetcher_->SetExtraRequestHeaders(request_headers_.ToString());
+
+ // We want to do cors check and preflight during redirects
+ url_fetcher_->SetStopOnRedirect(true);
+
+ if (request_body.size()) {
+ // If applicable, the request body Content-Type is already set in
+ // request_headers.
+ url_fetcher_->SetUploadData("", request_body);
+ }
+
+ // We let data url fetch resources freely but with no response headers.
+ is_data_url_ = is_data_url_ || request_url_.SchemeIs("data");
+ is_cross_origin_ = (is_redirect_ && is_cross_origin_) ||
+ (origin_ != loader::Origin(request_url_) && !is_data_url_);
+ is_redirect_ = false;
+ // If the CORS flag is set, httpRequest’s method is neither `GET` nor `HEAD`
+ // or httpRequest’s mode is "websocket", then append `Origin`/httpRequest’s
+ // origin, serialized and UTF-8 encoded, to httpRequest’s header list.
+ if (is_cross_origin_ ||
+ (method_ != net::URLFetcher::GET && method_ != net::URLFetcher::HEAD)) {
+ url_fetcher_->AddExtraRequestHeader("Origin:" + origin_.SerializedOrigin());
+ }
+ bool dopreflight = false;
+ // TODO (b/239733363): Include CORS functionality once preflight cache is
+ // accessible outside of window. At that point, there should be no more need
+ // to override this function in DOMXMLHttpRequestImpl.
+ DLOG_IF(INFO, verbose()) << __FUNCTION__ << *xhr_;
+ if (!dopreflight) {
+ DCHECK(settings_->context()->network_module());
+ StartURLFetcher(settings_->context()->network_module()->max_network_delay(),
+ url_fetcher_generation_);
+ }
+}
+
+void XMLHttpRequestImpl::IncrementActiveRequests() {
+ if (active_requests_count_ == 0) {
+ DCHECK(settings_);
+ DCHECK(settings_->context());
+ prevent_gc_until_send_complete_.reset(
+ new script::GlobalEnvironment::ScopedPreventGarbageCollection(
+ settings_->context()->global_environment(), xhr_));
+ }
+ active_requests_count_++;
+}
+
+void XMLHttpRequestImpl::DecrementActiveRequests() {
+ DCHECK_GT(active_requests_count_, 0);
+ active_requests_count_--;
+ if (active_requests_count_ == 0) {
+ bool is_active = (state_ == XMLHttpRequest::kOpened && sent_) ||
+ state_ == XMLHttpRequest::kHeadersReceived ||
+ state_ == XMLHttpRequest::kLoading;
+ bool has_event_listeners =
+ xhr_->GetAttributeEventListener(base::Tokens::readystatechange()) ||
+ xhr_->GetAttributeEventListener(base::Tokens::progress()) ||
+ xhr_->GetAttributeEventListener(base::Tokens::abort()) ||
+ xhr_->GetAttributeEventListener(base::Tokens::error()) ||
+ xhr_->GetAttributeEventListener(base::Tokens::load()) ||
+ xhr_->GetAttributeEventListener(base::Tokens::timeout()) ||
+ xhr_->GetAttributeEventListener(base::Tokens::loadend());
+
+ DCHECK_EQ((is_active && has_event_listeners), false);
+
+ prevent_gc_until_send_complete_.reset();
+ }
+}
+
+// Reset some variables in case the XHR object is reused.
+void XMLHttpRequestImpl::PrepareForNewRequest() {
+ request_headers_.Clear();
+ // Below are variables used for CORS.
+ request_body_text_.clear();
+ is_cross_origin_ = false;
+ redirect_times_ = 0;
+ is_data_url_ = false;
+ upload_listener_ = false;
+ is_redirect_ = false;
+}
+
+void XMLHttpRequestImpl::StartURLFetcher(const SbTime max_artificial_delay,
+ const int url_fetcher_generation) {
+ if (max_artificial_delay > 0) {
+ base::MessageLoop::current()->task_runner()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&XMLHttpRequestImpl::StartURLFetcher, base::Unretained(this),
+ 0, url_fetcher_generation_),
+ base::TimeDelta::FromMicroseconds(base::RandUint64() %
+ max_artificial_delay));
+ return;
+ }
+
+ // Note: Checking that "url_fetcher_generation_" != "url_fetcher_generation"
+ // is to verify the "url_fetcher_" is currently the same one that was present
+ // upon a delayed url fetch. This works because the incoming parameter
+ // "url_fetcher_generation" will hold the value at the time of the initial
+ // call, and if a delayed binding has waited while a new "url_fetcher_" has
+ // changed state, "url_fetcher_generation_" will have incremented.
+ if (nullptr != url_fetcher_ &&
+ url_fetcher_generation == url_fetcher_generation_) {
+ url_fetcher_->Start();
+ }
+}
+
+void XMLHttpRequestImpl::CORSPreflightErrorCallback() {
+ HandleRequestError(XMLHttpRequest::kNetworkError);
+}
+
+void XMLHttpRequestImpl::CORSPreflightSuccessCallback() {
+ DCHECK(settings_->context()->network_module());
+ StartURLFetcher(settings_->context()->network_module()->max_network_delay(),
+ url_fetcher_generation_);
+}
+
+DOMXMLHttpRequestImpl::DOMXMLHttpRequestImpl(XMLHttpRequest* xhr)
+ : XMLHttpRequestImpl(xhr),
+ settings_(base::polymorphic_downcast<dom::DOMSettings*>(
+ xhr->environment_settings())) {
+ DCHECK(settings_);
+}
+
+// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#the-responsexml-attribute
+scoped_refptr<dom::Document> DOMXMLHttpRequestImpl::response_xml(
+ script::ExceptionState* exception_state) {
+ // 1. If responseType is not the empty string or "document", throw an
+ // "InvalidStateError" exception.
+ if (response_type_ != XMLHttpRequest::kDefault &&
+ response_type_ != XMLHttpRequest::kDocument) {
+ web::DOMException::Raise(web::DOMException::kInvalidStateErr,
+ exception_state);
+ return NULL;
+ }
+
+ // 2. If the state is not DONE, return null.
+ if (state_ != XMLHttpRequest::kDone) {
+ return NULL;
+ }
+
+ // 3. If the error flag is set, return null.
+ if (error_) {
+ return NULL;
+ }
+
+ // 4. Return the document response entity body.
+ return GetDocumentResponseEntityBody();
+}
+
+void DOMXMLHttpRequestImpl::GetLoadTimingInfoAndCreateResourceTiming() {
+ if (settings_->window()->performance() == nullptr) return;
+ settings_->window()->performance()->CreatePerformanceResourceTiming(
+ load_timing_info_, kPerformanceResourceTimingInitiatorType,
+ request_url_.spec());
+}
+
+web::CspDelegate* DOMXMLHttpRequestImpl::csp_delegate() const {
+ DCHECK(settings_);
+ if (settings_->window() && settings_->window()->document()) {
+ return settings_->window()->document()->csp_delegate();
+ } else {
+ return NULL;
+ }
+}
+
+void DOMXMLHttpRequestImpl::StartRequest(const std::string& request_body) {
+ TRACK_MEMORY_SCOPE("XHR");
+
+ response_array_buffer_reference_.reset();
+
+ network::NetworkModule* network_module =
+ settings_->context()->fetcher_factory()->network_module();
+ url_fetcher_ = net::URLFetcher::Create(request_url_, method_, xhr_);
+ ++url_fetcher_generation_;
+ url_fetcher_->SetRequestContext(network_module->url_request_context_getter());
+ if (fetch_callback_) {
+ response_body_ = new URLFetcherResponseWriter::Buffer(
+ URLFetcherResponseWriter::Buffer::kString);
+ response_body_->DisablePreallocate();
+ } else {
+ response_body_ = new URLFetcherResponseWriter::Buffer(
+ response_type_ == XMLHttpRequest::kArrayBuffer
? URLFetcherResponseWriter::Buffer::kArrayBuffer
: URLFetcherResponseWriter::Buffer::kString);
}
@@ -1169,10 +1474,10 @@
if (is_cross_origin_) {
corspreflight_.reset(new cobalt::loader::CORSPreflight(
request_url_, method_, network_module,
- base::Bind(&XMLHttpRequest::CORSPreflightSuccessCallback,
+ base::Bind(&DOMXMLHttpRequestImpl::CORSPreflightSuccessCallback,
base::Unretained(this)),
origin_.SerializedOrigin(),
- base::Bind(&XMLHttpRequest::CORSPreflightErrorCallback,
+ base::Bind(&DOMXMLHttpRequestImpl::CORSPreflightErrorCallback,
base::Unretained(this)),
settings_->window()->get_preflight_cache()));
corspreflight_->set_headers(request_headers_);
@@ -1193,7 +1498,7 @@
corspreflight_->set_force_preflight(upload_listener_);
dopreflight = corspreflight_->Send();
}
- DLOG_IF(INFO, verbose()) << __FUNCTION__ << *this;
+ DLOG_IF(INFO, verbose()) << __FUNCTION__ << *xhr_;
if (!dopreflight) {
DCHECK(settings_->context()->network_module());
StartURLFetcher(settings_->context()->network_module()->max_network_delay(),
@@ -1201,37 +1506,57 @@
}
}
-void XMLHttpRequest::CORSPreflightErrorCallback() {
- HandleRequestError(XMLHttpRequest::kNetworkError);
+// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#document-response-entity-body
+scoped_refptr<dom::Document>
+DOMXMLHttpRequestImpl::GetDocumentResponseEntityBody() {
+ DCHECK_EQ(state_, XMLHttpRequest::kDone);
+
+ // Step 1..5
+ const std::string final_mime_type =
+ mime_type_override_.empty() ? response_mime_type_ : mime_type_override_;
+ if (final_mime_type != "text/xml" && final_mime_type != "application/xml") {
+ return NULL;
+ }
+
+ // 6. Otherwise, let document be a document that represents the result of
+ // parsing the response entity body following the rules set forth in the XML
+ // specifications. If that fails (unsupported character encoding, namespace
+ // well-formedness error, etc.), return null.
+ scoped_refptr<dom::XMLDocument> xml_document =
+ new dom::XMLDocument(settings_->window()->html_element_context());
+ dom_parser::XMLDecoder xml_decoder(
+ xml_document, xml_document, NULL, settings_->max_dom_element_depth(),
+ base::SourceLocation("[object XMLHttpRequest]", 1, 1),
+ base::Bind(&DOMXMLHttpRequestImpl::XMLDecoderLoadCompleteCallback,
+ base::Unretained(this)));
+ has_xml_decoder_error_ = false;
+ xml_decoder.DecodeChunk(response_body_->GetReferenceOfStringAndSeal().c_str(),
+ response_body_->GetReferenceOfStringAndSeal().size());
+ xml_decoder.Finish();
+ if (has_xml_decoder_error_) {
+ return NULL;
+ }
+
+ // Step 7..11 Not needed by Cobalt.
+
+ // 12. Return document.
+ return xml_document;
}
-void XMLHttpRequest::CORSPreflightSuccessCallback() {
- DCHECK(settings_->context()->network_module());
- StartURLFetcher(settings_->context()->network_module()->max_network_delay(),
- url_fetcher_generation_);
-}
-
-void XMLHttpRequest::ReportLoadTimingInfo(
- const net::LoadTimingInfo& timing_info) {
- load_timing_info_ = timing_info;
-}
-
-void XMLHttpRequest::GetLoadTimingInfoAndCreateResourceTiming() {
- if (settings_->window()->performance() == nullptr) return;
- settings_->window()->performance()->CreatePerformanceResourceTiming(
- load_timing_info_, kPerformanceResourceTimingInitiatorType,
- request_url_.spec());
+void DOMXMLHttpRequestImpl::XMLDecoderLoadCompleteCallback(
+ const base::Optional<std::string>& error) {
+ if (error) has_xml_decoder_error_ = true;
}
std::ostream& operator<<(std::ostream& out, const XMLHttpRequest& xhr) {
#if !defined(COBALT_BUILD_TYPE_GOLD)
base::StringPiece response_text("");
- if ((xhr.state_ == XMLHttpRequest::kDone) &&
- (xhr.response_type_ == XMLHttpRequest::kDefault ||
- xhr.response_type_ == XMLHttpRequest::kText)) {
+ if ((xhr.xhr_impl_->state_ == XMLHttpRequest::kDone) &&
+ (xhr.xhr_impl_->response_type_ == XMLHttpRequest::kDefault ||
+ xhr.xhr_impl_->response_type_ == XMLHttpRequest::kText)) {
size_t kMaxSize = 4096;
const auto& response_body =
- xhr.response_body_->GetTemporaryReferenceOfString();
+ xhr.xhr_impl_->response_body_->GetTemporaryReferenceOfString();
response_text =
base::StringPiece(reinterpret_cast<const char*>(response_body.data()),
std::min(kMaxSize, response_body.size()));
@@ -1251,11 +1576,15 @@
"\tsent: %s\n"
"\tstop_timeout: %s\n"
"\tresponse_body: %s\n",
- xhr.xhr_id_, xhr.request_url_.spec().c_str(), StateName(xhr.state_),
- xhr.response_type(NULL).c_str(), xhr.timeout_ms_,
- RequestTypeToMethodName(xhr.method_), xhr.http_status_,
- xhr.with_credentials_ ? "true" : "false", xhr.error_ ? "true" : "false",
- xhr.sent_ ? "true" : "false", xhr.stop_timeout_ ? "true" : "false",
+ xhr.xhr_id_, xhr.xhr_impl_->request_url_.spec().c_str(),
+ StateName(xhr.xhr_impl_->state_), xhr.response_type(NULL).c_str(),
+ xhr.xhr_impl_->timeout_ms_,
+ RequestTypeToMethodName(xhr.xhr_impl_->method_),
+ xhr.xhr_impl_->http_status_,
+ xhr.xhr_impl_->with_credentials_ ? "true" : "false",
+ xhr.xhr_impl_->error_ ? "true" : "false",
+ xhr.xhr_impl_->sent_ ? "true" : "false",
+ xhr.xhr_impl_->stop_timeout_ ? "true" : "false",
response_text.as_string().c_str());
out << xhr_out;
#else
@@ -1263,70 +1592,5 @@
return out;
}
-// https://www.w3.org/TR/2014/WD-XMLHttpRequest-20140130/#document-response-entity-body
-scoped_refptr<dom::Document> XMLHttpRequest::GetDocumentResponseEntityBody() {
- DCHECK_EQ(state_, kDone);
-
- // Step 1..5
- const std::string final_mime_type =
- mime_type_override_.empty() ? response_mime_type_ : mime_type_override_;
- if (final_mime_type != "text/xml" && final_mime_type != "application/xml") {
- return NULL;
- }
-
- // 6. Otherwise, let document be a document that represents the result of
- // parsing the response entity body following the rules set forth in the XML
- // specifications. If that fails (unsupported character encoding, namespace
- // well-formedness error, etc.), return null.
- scoped_refptr<dom::XMLDocument> xml_document =
- new dom::XMLDocument(settings_->window()->html_element_context());
- dom_parser::XMLDecoder xml_decoder(
- xml_document, xml_document, NULL, settings_->max_dom_element_depth(),
- base::SourceLocation("[object XMLHttpRequest]", 1, 1),
- base::Bind(&XMLHttpRequest::XMLDecoderLoadCompleteCallback,
- base::Unretained(this)));
- has_xml_decoder_error_ = false;
- xml_decoder.DecodeChunk(response_body_->GetReferenceOfStringAndSeal().c_str(),
- response_body_->GetReferenceOfStringAndSeal().size());
- xml_decoder.Finish();
- if (has_xml_decoder_error_) {
- return NULL;
- }
-
- // Step 7..11 Not needed by Cobalt.
-
- // 12. Return document.
- return xml_document;
-}
-
-void XMLHttpRequest::XMLDecoderLoadCompleteCallback(
- const base::Optional<std::string>& error) {
- if (error) has_xml_decoder_error_ = true;
-}
-
-void XMLHttpRequest::StartURLFetcher(const SbTime max_artificial_delay,
- const int url_fetcher_generation) {
- if (max_artificial_delay > 0) {
- base::MessageLoop::current()->task_runner()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&XMLHttpRequest::StartURLFetcher, this, 0,
- url_fetcher_generation_),
- base::TimeDelta::FromMicroseconds(base::RandUint64() %
- max_artificial_delay));
- return;
- }
-
- // Note: Checking that "url_fetcher_generation_" != "url_fetcher_generation"
- // is to verify the "url_fetcher_" is currently the same one that was present
- // upon a delayed url fetch. This works because the incoming parameter
- // "url_fetcher_generation" will hold the value at the time of the initial
- // call, and if a delayed binding has waited while a new "url_fetcher_" has
- // changed state, "url_fetcher_generation_" will have incremented.
- if (nullptr != url_fetcher_ &&
- url_fetcher_generation == url_fetcher_generation_) {
- url_fetcher_->Start();
- }
-}
-
} // namespace xhr
} // namespace cobalt
diff --git a/cobalt/xhr/xml_http_request.h b/cobalt/xhr/xml_http_request.h
index 2c26720..e3510f0 100644
--- a/cobalt/xhr/xml_http_request.h
+++ b/cobalt/xhr/xml_http_request.h
@@ -34,6 +34,7 @@
#include "cobalt/script/union_type.h"
#include "cobalt/web/csp_delegate.h"
#include "cobalt/web/dom_exception.h"
+#include "cobalt/web/environment_settings.h"
#include "cobalt/xhr/url_fetcher_buffer_writer.h"
#include "cobalt/xhr/xml_http_request_event_target.h"
#include "cobalt/xhr/xml_http_request_upload.h"
@@ -45,9 +46,6 @@
#include "url/gurl.h"
namespace cobalt {
-namespace dom {
-class DOMSettings;
-}
namespace script {
class EnvironmentSettings;
@@ -55,12 +53,14 @@
namespace xhr {
+class XMLHttpRequestImpl;
+
class XMLHttpRequest : public XMLHttpRequestEventTarget,
- net::URLFetcherDelegate {
+ public net::URLFetcherDelegate {
public:
- // Note: This is expected to be a DOMSettings object, but we declare it as
- // EnvironmentSettings so that JSC doesn't need to know about dom.
explicit XMLHttpRequest(script::EnvironmentSettings* settings);
+ XMLHttpRequest(const XMLHttpRequest&) = delete;
+ XMLHttpRequest& operator=(const XMLHttpRequest&) = delete;
typedef script::UnionType2<std::string, script::Handle<script::ArrayBuffer> >
ResponseType;
@@ -131,7 +131,9 @@
void OverrideMimeType(const std::string& mime_type,
script::ExceptionState* exception_state);
- void Send(script::ExceptionState* exception_state);
+ void Send(script::ExceptionState* exception_state) {
+ Send(base::nullopt, exception_state);
+ }
void Send(const base::Optional<RequestBodyType>& request_body,
script::ExceptionState* exception_state);
@@ -153,12 +155,120 @@
const std::string& response_text(script::ExceptionState* exception_state);
scoped_refptr<dom::Document> response_xml(
script::ExceptionState* exception_state);
- std::string status_text();
base::Optional<ResponseType> response(
script::ExceptionState* exception_state);
+ int ready_state() const;
+ int status() const;
+ std::string status_text();
+ std::string status_text() const;
+ void set_response_type(const std::string& response_type,
+ script::ExceptionState* exception_state);
+ std::string response_type(script::ExceptionState* exception_state) const;
+
+ uint32 timeout() const;
+ void set_timeout(uint32 timeout);
+ bool with_credentials(script::ExceptionState* exception_state) const;
+ void set_with_credentials(bool b, script::ExceptionState* exception_state);
+
+ scoped_refptr<XMLHttpRequestUpload> upload();
+
+ static void set_verbose(bool verbose);
+ static bool verbose();
+
+ // net::URLFetcherDelegate interface
+ void OnURLFetchResponseStarted(const net::URLFetcher* source) override;
+ void OnURLFetchDownloadProgress(const net::URLFetcher* source,
+ int64_t current, int64_t total,
+ int64_t current_network_bytes) override;
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ void OnURLFetchUploadProgress(const net::URLFetcher* source, int64 current,
+ int64 total) override;
+ void OnRedirect(const net::HttpResponseHeaders& headers);
+
+ // Called from bindings layer to tie objects' lifetimes to this XHR instance.
+ XMLHttpRequestUpload* upload_or_null();
+
+ void ReportLoadTimingInfo(const net::LoadTimingInfo& timing_info) override;
+ // Create Performance Resource Timing entry for XMLHttpRequest.
+ void GetLoadTimingInfoAndCreateResourceTiming();
+
+ friend std::ostream& operator<<(std::ostream& os, const XMLHttpRequest& xhr);
+ DEFINE_WRAPPABLE_TYPE(XMLHttpRequest);
+ void TraceMembers(script::Tracer* tracer) override;
+
+ protected:
+ ~XMLHttpRequest() override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(XhrTest, GetResponseHeader);
+ FRIEND_TEST_ALL_PREFIXES(XhrTest, InvalidMethod);
+ FRIEND_TEST_ALL_PREFIXES(XhrTest, Open);
+ FRIEND_TEST_ALL_PREFIXES(XhrTest, OpenFailConnectSrc);
+ FRIEND_TEST_ALL_PREFIXES(XhrTest, OverrideMimeType);
+ FRIEND_TEST_ALL_PREFIXES(XhrTest, SetRequestHeader);
+
+ std::unique_ptr<XMLHttpRequestImpl> xhr_impl_;
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+ // Unique ID for debugging.
+ int xhr_id_;
+#endif // !defined(COBALT_BUILD_TYPE_GOLD)
+
+ THREAD_CHECKER(thread_checker_);
+};
+
+
+class XMLHttpRequestImpl {
+ public:
+ explicit XMLHttpRequestImpl(XMLHttpRequest* xhr);
+ XMLHttpRequestImpl(const XMLHttpRequestImpl&) = delete;
+ XMLHttpRequestImpl& operator=(const XMLHttpRequestImpl&) = delete;
+ virtual ~XMLHttpRequestImpl() {}
+
+ void Abort();
+ void Open(const std::string& method, const std::string& url, bool async,
+ const base::Optional<std::string>& username,
+ const base::Optional<std::string>& password,
+ script::ExceptionState* exception_state);
+
+ // Must be called after open(), but before send().
+ void SetRequestHeader(const std::string& header, const std::string& value,
+ script::ExceptionState* exception_state);
+
+ // Override the MIME type returned by the server.
+ // Call before Send(), otherwise throws InvalidStateError.
+ void OverrideMimeType(const std::string& mime_type,
+ script::ExceptionState* exception_state);
+
+ void Send(const base::Optional<XMLHttpRequest::RequestBodyType>& request_body,
+ script::ExceptionState* exception_state);
+
+ // FetchAPI: replacement for Send() when fetch functionality is required.
+ typedef script::CallbackFunction<void(
+ const script::Handle<script::Uint8Array>& data)>
+ FetchUpdateCallback;
+ typedef script::CallbackFunction<void(bool)> FetchModeCallback;
+ typedef script::ScriptValue<FetchUpdateCallback> FetchUpdateCallbackArg;
+ typedef script::ScriptValue<FetchModeCallback> FetchModeCallbackArg;
+ void Fetch(
+ const FetchUpdateCallbackArg& fetch_callback,
+ const FetchModeCallbackArg& fetch_mode_callback,
+ const base::Optional<XMLHttpRequest::RequestBodyType>& request_body,
+ script::ExceptionState* exception_state);
+
+ base::Optional<std::string> GetResponseHeader(const std::string& header);
+ std::string GetAllResponseHeaders();
+
+ const std::string& response_text(script::ExceptionState* exception_state);
+ virtual scoped_refptr<dom::Document> response_xml(
+ script::ExceptionState* exception_state);
+ base::Optional<XMLHttpRequest::ResponseType> response(
+ script::ExceptionState* exception_state);
+
int ready_state() const { return static_cast<int>(state_); }
int status() const;
+ std::string status_text();
std::string status_text() const;
void set_response_type(const std::string& reponse_type,
script::ExceptionState* exception_state);
@@ -173,57 +283,97 @@
static void set_verbose(bool verbose) { verbose_ = verbose; }
static bool verbose() { return verbose_; }
+
+
// net::URLFetcherDelegate interface
- void OnURLFetchResponseStarted(const net::URLFetcher* source) override;
+ void OnURLFetchResponseStarted(const net::URLFetcher* source);
void OnURLFetchDownloadProgress(const net::URLFetcher* source,
int64_t current, int64_t total,
- int64_t current_network_bytes) override;
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ int64_t current_network_bytes);
+ void OnURLFetchComplete(const net::URLFetcher* source);
void OnURLFetchUploadProgress(const net::URLFetcher* source, int64 current,
- int64 total) override;
+ int64 total);
void OnRedirect(const net::HttpResponseHeaders& headers);
// Called from bindings layer to tie objects' lifetimes to this XHR instance.
XMLHttpRequestUpload* upload_or_null() { return upload_.get(); }
- void ReportLoadTimingInfo(const net::LoadTimingInfo& timing_info) override;
+ void ReportLoadTimingInfo(const net::LoadTimingInfo& timing_info);
// Create Performance Resource Timing entry for XMLHttpRequest.
- void GetLoadTimingInfoAndCreateResourceTiming();
+ virtual void GetLoadTimingInfoAndCreateResourceTiming();
friend std::ostream& operator<<(std::ostream& os, const XMLHttpRequest& xhr);
- DEFINE_WRAPPABLE_TYPE(XMLHttpRequest);
- void TraceMembers(script::Tracer* tracer) override;
+ void TraceMembers(script::Tracer* tracer);
protected:
- ~XMLHttpRequest() override;
+ void CORSPreflightErrorCallback();
+ void CORSPreflightSuccessCallback();
// Return the CSP delegate from the Settings object.
// virtual for use by tests.
virtual web::CspDelegate* csp_delegate() const;
+ // The following method starts "url_fetcher_" with a possible pre-delay.
+ void StartURLFetcher(const SbTime max_artificial_delay,
+ const int url_fetcher_generation);
+
+ // Protected members for visibility in derived class.
+ // All members requiring initialization are grouped below.
+ bool error_;
+ bool is_cross_origin_;
+ bool is_data_url_;
+ bool is_redirect_;
+ net::URLFetcher::RequestType method_;
+ scoped_refptr<URLFetcherResponseWriter::Buffer> response_body_;
+ XMLHttpRequest::ResponseTypeCode response_type_;
+ XMLHttpRequest::State state_;
+ // https://xhr.spec.whatwg.org/#upload-listener-flag
+ bool upload_listener_;
+ bool with_credentials_;
+ XMLHttpRequest* xhr_;
+
+ // A corspreflight instance for potentially sending preflight
+ // request and performing cors check for all cross origin requests.
+ std::unique_ptr<cobalt::loader::CORSPreflight> corspreflight_;
+ // FetchAPI: transfer progress callback.
+ std::unique_ptr<FetchUpdateCallbackArg::Reference> fetch_callback_;
+ net::LoadTimingInfo load_timing_info_;
+ std::string mime_type_override_;
+ // net::URLRequest does not have origin variable so we can only store it here.
+ // https://fetch.spec.whatwg.org/#concept-request-origin
+ loader::Origin origin_;
+ net::HttpRequestHeaders request_headers_;
+ GURL request_url_;
+ std::unique_ptr<script::ScriptValue<script::ArrayBuffer>::Reference>
+ response_array_buffer_reference_;
+ std::string response_mime_type_;
+ std::unique_ptr<net::URLFetcher> url_fetcher_;
+ int url_fetcher_generation_ = -1;
+
private:
FRIEND_TEST_ALL_PREFIXES(XhrTest, GetResponseHeader);
FRIEND_TEST_ALL_PREFIXES(XhrTest, InvalidMethod);
FRIEND_TEST_ALL_PREFIXES(XhrTest, Open);
FRIEND_TEST_ALL_PREFIXES(XhrTest, OverrideMimeType);
FRIEND_TEST_ALL_PREFIXES(XhrTest, SetRequestHeader);
+
// Cancel any inflight request and set error flag.
void TerminateRequest();
// Dispatch events based on the type of error.
- void HandleRequestError(RequestErrorType request_error);
+ void HandleRequestError(XMLHttpRequest::RequestErrorType request_error);
// Callback when timeout fires.
void OnTimeout();
// Starts the timeout timer running.
void StartTimer(base::TimeDelta time_since_send);
// Update the internal ready state and fire events.
- void ChangeState(State new_state);
+ void ChangeState(XMLHttpRequest::State new_state);
// Return array buffer response body as an ArrayBuffer.
script::Handle<script::ArrayBuffer> response_array_buffer();
void UpdateProgress(int64_t received_length);
- void StartRequest(const std::string& request_body);
+ virtual void StartRequest(const std::string& request_body);
// The following two methods are used to determine if garbage collection is
// needed. It is legal to reuse XHR and send a new request in last request's
@@ -241,37 +391,19 @@
const net::HttpRequestHeaders& request_headers() const {
return request_headers_;
}
- void set_state(State state) { state_ = state; }
+ void set_state(XMLHttpRequest::State state) { state_ = state; }
void set_http_response_headers(
const scoped_refptr<net::HttpResponseHeaders>& response_headers) {
http_response_headers_ = response_headers;
}
void PrepareForNewRequest();
- scoped_refptr<dom::Document> GetDocumentResponseEntityBody();
- void XMLDecoderLoadCompleteCallback(
- const base::Optional<std::string>& status);
-
- // The following method starts "url_fetcher_" with a possible pre-delay.
- void StartURLFetcher(const SbTime max_artificial_delay,
- const int url_fetcher_generation);
-
- void CORSPreflightErrorCallback();
- void CORSPreflightSuccessCallback();
-
THREAD_CHECKER(thread_checker_);
- std::unique_ptr<net::URLFetcher> url_fetcher_;
scoped_refptr<net::HttpResponseHeaders> http_response_headers_;
- scoped_refptr<URLFetcherResponseWriter::Buffer> response_body_;
- std::unique_ptr<script::ScriptValue<script::ArrayBuffer>::Reference>
- response_array_buffer_reference_;
scoped_refptr<XMLHttpRequestUpload> upload_;
- std::string mime_type_override_;
GURL base_url_;
- GURL request_url_;
- net::HttpRequestHeaders request_headers_;
// For handling send() timeout.
base::OneShotTimer timer_;
@@ -281,53 +413,52 @@
base::TimeTicks last_progress_time_;
base::TimeTicks upload_last_progress_time_;
- // FetchAPI: transfer progress callback.
- std::unique_ptr<FetchUpdateCallbackArg::Reference> fetch_callback_;
// FetchAPI: tell fetch polyfill if the response mode is cors.
std::unique_ptr<FetchModeCallbackArg::Reference> fetch_mode_callback_;
// All members requiring initialization are grouped below.
- dom::DOMSettings* const settings_;
- State state_;
- ResponseTypeCode response_type_;
- uint32 timeout_ms_;
- net::URLFetcher::RequestType method_;
- int http_status_;
- bool with_credentials_;
- bool error_;
- bool sent_;
- bool stop_timeout_;
- bool upload_complete_;
int active_requests_count_;
- // https://xhr.spec.whatwg.org/#upload-listener-flag
- bool upload_listener_;
+ int http_status_;
+ int redirect_times_;
+ bool sent_;
+ web::EnvironmentSettings* const settings_;
+ bool stop_timeout_;
+ uint32 timeout_ms_;
+ bool upload_complete_;
static bool verbose_;
- // Unique ID for debugging.
- int xhr_id_;
-
- bool has_xml_decoder_error_;
std::unique_ptr<script::GlobalEnvironment::ScopedPreventGarbageCollection>
prevent_gc_until_send_complete_;
- // A corspreflight instance for potentially sending preflight
- // request and performing cors check for all cross origin requests.
- std::unique_ptr<cobalt::loader::CORSPreflight> corspreflight_;
- bool is_cross_origin_;
- // net::URLRequest does not have origin variable so we can only store it here.
- // https://fetch.spec.whatwg.org/#concept-request-origin
- loader::Origin origin_;
- bool is_redirect_;
- std::string response_mime_type_;
std::string request_body_text_;
- int redirect_times_;
- bool is_data_url_;
- int url_fetcher_generation_ = -1;
+};
- net::LoadTimingInfo load_timing_info_;
+class DOMXMLHttpRequestImpl : public XMLHttpRequestImpl {
+ public:
+ explicit DOMXMLHttpRequestImpl(XMLHttpRequest* xhr);
+ DOMXMLHttpRequestImpl(const DOMXMLHttpRequestImpl&) = delete;
+ DOMXMLHttpRequestImpl& operator=(const DOMXMLHttpRequestImpl&) = delete;
+ ~DOMXMLHttpRequestImpl() override {}
- DISALLOW_COPY_AND_ASSIGN(XMLHttpRequest);
+ scoped_refptr<dom::Document> response_xml(
+ script::ExceptionState* exception_state) override;
+
+ void GetLoadTimingInfoAndCreateResourceTiming() override;
+
+ protected:
+ web::CspDelegate* csp_delegate() const override;
+
+ private:
+ void StartRequest(const std::string& request_body) override;
+
+ scoped_refptr<dom::Document> GetDocumentResponseEntityBody();
+
+ void XMLDecoderLoadCompleteCallback(
+ const base::Optional<std::string>& status);
+
+ dom::DOMSettings* const settings_;
+ bool has_xml_decoder_error_;
};
std::ostream& operator<<(std::ostream& out, const XMLHttpRequest& xhr);
diff --git a/cobalt/xhr/xml_http_request.idl b/cobalt/xhr/xml_http_request.idl
index 8c72a22..e6fde17 100644
--- a/cobalt/xhr/xml_http_request.idl
+++ b/cobalt/xhr/xml_http_request.idl
@@ -17,6 +17,7 @@
[
Constructor,
ConstructorCallWith=EnvironmentSettings,
+ Exposed=(Window,Worker),
] interface XMLHttpRequest : XMLHttpRequestEventTarget {
// event handler
attribute EventHandler onreadystatechange;
@@ -60,6 +61,7 @@
// TODO: Use a union type for all the possible response types.
[RaisesException] readonly attribute (DOMString or ArrayBuffer) response;
[RaisesException] readonly attribute DOMString responseText;
+ // TODO: responseXML should only be exposed to Window, not Worker
[RaisesException] readonly attribute Document? responseXML;
// Not part of the spec. Enable verbose XHR logging.
diff --git a/cobalt/xhr/xml_http_request_test.cc b/cobalt/xhr/xml_http_request_test.cc
index e83e21a..290566a 100644
--- a/cobalt/xhr/xml_http_request_test.cc
+++ b/cobalt/xhr/xml_http_request_test.cc
@@ -17,7 +17,9 @@
#include <memory>
#include "base/logging.h"
+#include "cobalt/dom/dom_settings.h"
#include "cobalt/dom/testing/stub_environment_settings.h"
+#include "cobalt/dom/testing/stub_window.h"
#include "cobalt/dom/window.h"
#include "cobalt/script/testing/fake_script_value.h"
#include "cobalt/script/testing/mock_exception_state.h"
@@ -90,11 +92,6 @@
ScopedLogInterceptor* ScopedLogInterceptor::log_interceptor_;
-class FakeSettings : public dom::testing::StubEnvironmentSettings {
- public:
- FakeSettings() { set_base_url(GURL("http://example.com")); }
-};
-
class MockCspDelegate : public web::CspDelegateInsecure {
public:
MockCspDelegate() {}
@@ -102,13 +99,14 @@
bool(web::CspDelegate::ResourceType, const GURL&, bool));
};
-// Derive from XMLHttpRequest in order to override its csp_delegate.
+// Derive from XMLHttpRequestImpl in order to override its csp_delegate.
// Normally this would come from the Document via DOMSettings.
-class FakeXmlHttpRequest : public XMLHttpRequest {
+class FakeXmlHttpRequestImpl : public DOMXMLHttpRequestImpl {
public:
- FakeXmlHttpRequest(script::EnvironmentSettings* settings,
- web::CspDelegate* csp_delegate)
- : XMLHttpRequest(settings), csp_delegate_(csp_delegate) {}
+ FakeXmlHttpRequestImpl(xhr::XMLHttpRequest* xhr,
+ web::EnvironmentSettings* settings,
+ web::CspDelegate* csp_delegate)
+ : DOMXMLHttpRequestImpl(xhr), csp_delegate_(csp_delegate) {}
web::CspDelegate* csp_delegate() const override { return csp_delegate_; }
private:
@@ -119,19 +117,23 @@
class XhrTest : public ::testing::Test {
public:
- dom::DOMSettings* settings() const { return settings_.get(); }
+ web::EnvironmentSettings* settings() const {
+ return stub_window_->environment_settings();
+ }
protected:
XhrTest();
~XhrTest() override;
- std::unique_ptr<FakeSettings> settings_;
+ std::unique_ptr<dom::testing::StubWindow> stub_window_;
scoped_refptr<XMLHttpRequest> xhr_;
StrictMock<MockExceptionState> exception_state_;
};
-XhrTest::XhrTest()
- : settings_(new FakeSettings()), xhr_(new XMLHttpRequest(settings())) {}
+XhrTest::XhrTest() : stub_window_(new dom::testing::StubWindow()) {
+ settings()->set_creation_url(GURL("http://example.com"));
+ xhr_ = scoped_refptr<XMLHttpRequest>(new XMLHttpRequest(settings()));
+}
XhrTest::~XhrTest() {}
@@ -147,6 +149,8 @@
TEST_F(XhrTest, Open) {
std::unique_ptr<MockEventListener> listener = MockEventListener::Create();
FakeScriptValue<web::EventListener> script_object(listener.get());
+ xhr_->xhr_impl_ = std::unique_ptr<FakeXmlHttpRequestImpl>(
+ new FakeXmlHttpRequestImpl(xhr_, settings(), NULL));
xhr_->set_onreadystatechange(script_object);
EXPECT_CALL(*listener,
HandleEvent(Eq(xhr_),
@@ -157,7 +161,7 @@
xhr_->Open("GET", "https://www.google.com", &exception_state_);
EXPECT_EQ(XMLHttpRequest::kOpened, xhr_->ready_state());
- EXPECT_EQ(GURL("https://www.google.com"), xhr_->request_url());
+ EXPECT_EQ(GURL("https://www.google.com"), xhr_->xhr_impl_->request_url());
}
TEST_F(XhrTest, OpenFailConnectSrc) {
@@ -167,8 +171,8 @@
StrictMock<MockCspDelegate> csp_delegate;
EXPECT_CALL(exception_state_, SetException(_))
.WillOnce(SaveArg<0>(&exception));
-
- xhr_ = new FakeXmlHttpRequest(settings(), &csp_delegate);
+ xhr_->xhr_impl_ = std::unique_ptr<FakeXmlHttpRequestImpl>(
+ new FakeXmlHttpRequestImpl(xhr_, settings(), &csp_delegate));
EXPECT_CALL(csp_delegate, CanLoad(_, _, _)).WillOnce(Return(false));
xhr_->Open("GET", "https://www.google.com", &exception_state_);
@@ -178,18 +182,18 @@
}
TEST_F(XhrTest, OverrideMimeType) {
- EXPECT_EQ("", xhr_->mime_type_override());
+ EXPECT_EQ("", xhr_->xhr_impl_->mime_type_override());
scoped_refptr<script::ScriptException> exception;
EXPECT_CALL(exception_state_, SetException(_))
.WillOnce(SaveArg<0>(&exception));
xhr_->OverrideMimeType("invalidmimetype", &exception_state_);
- EXPECT_EQ("", xhr_->mime_type_override());
+ EXPECT_EQ("", xhr_->xhr_impl_->mime_type_override());
EXPECT_EQ(web::DOMException::kSyntaxErr,
dynamic_cast<web::DOMException*>(exception.get())->code());
xhr_->OverrideMimeType("text/xml", &exception_state_);
- EXPECT_EQ("text/xml", xhr_->mime_type_override());
+ EXPECT_EQ("text/xml", xhr_->xhr_impl_->mime_type_override());
}
TEST_F(XhrTest, SetResponseType) {
@@ -214,26 +218,29 @@
}
TEST_F(XhrTest, SetRequestHeader) {
+ xhr_->xhr_impl_ = std::unique_ptr<FakeXmlHttpRequestImpl>(
+ new FakeXmlHttpRequestImpl(xhr_, settings(), NULL));
xhr_->Open("GET", "https://www.google.com", &exception_state_);
- EXPECT_EQ("\r\n", xhr_->request_headers().ToString());
+ EXPECT_EQ("\r\n", xhr_->xhr_impl_->request_headers().ToString());
xhr_->SetRequestHeader("Foo", "bar", &exception_state_);
- EXPECT_EQ("Foo: bar\r\n\r\n", xhr_->request_headers().ToString());
+ EXPECT_EQ("Foo: bar\r\n\r\n", xhr_->xhr_impl_->request_headers().ToString());
xhr_->SetRequestHeader("Foo", "baz", &exception_state_);
- EXPECT_EQ("Foo: bar, baz\r\n\r\n", xhr_->request_headers().ToString());
+ EXPECT_EQ("Foo: bar, baz\r\n\r\n",
+ xhr_->xhr_impl_->request_headers().ToString());
}
TEST_F(XhrTest, GetResponseHeader) {
- xhr_->set_state(XMLHttpRequest::kUnsent);
+ xhr_->xhr_impl_->set_state(XMLHttpRequest::kUnsent);
EXPECT_EQ(base::nullopt, xhr_->GetResponseHeader("Content-Type"));
- xhr_->set_state(XMLHttpRequest::kOpened);
+ xhr_->xhr_impl_->set_state(XMLHttpRequest::kOpened);
EXPECT_EQ(base::nullopt, xhr_->GetResponseHeader("Content-Type"));
- xhr_->set_state(XMLHttpRequest::kHeadersReceived);
+ xhr_->xhr_impl_->set_state(XMLHttpRequest::kHeadersReceived);
scoped_refptr<net::HttpResponseHeaders> fake_headers(
new net::HttpResponseHeaders(
std::string(kFakeHeaders, kFakeHeaders + sizeof(kFakeHeaders))));
- xhr_->set_http_response_headers(fake_headers);
+ xhr_->xhr_impl_->set_http_response_headers(fake_headers);
EXPECT_EQ(base::nullopt, xhr_->GetResponseHeader("Unknown"));
EXPECT_EQ("text/plain", xhr_->GetResponseHeader("Content-Type").value_or(""));
EXPECT_EQ("text/plain", xhr_->GetResponseHeader("CONTENT-TYPE").value_or(""));
diff --git a/docker/docsite/Dockerfile b/docker/docsite/Dockerfile
index 3ed5f9a..fc278cc 100644
--- a/docker/docsite/Dockerfile
+++ b/docker/docsite/Dockerfile
@@ -19,7 +19,15 @@
ARG GID
RUN apt update -qqy \
- && apt install -qqy --no-install-recommends bundler doxygen git nodejs python \
+ && apt install -qqy --no-install-recommends \
+ bundler \
+ doxygen \
+ git \
+ nodejs \
+ python \
+ # Required for GN build.
+ python3 \
+ python3-requests \
&& apt-get clean autoclean \
&& apt-get autoremove -y --purge \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
@@ -29,11 +37,26 @@
RUN bundle install --gemfile=/app/Gemfile
+# === Get GN via CIPD
+ARG GN_SHA256SUM="af7b2dcb3905bca56655e12131b365f1cba8e159db80d2022330c4f522fab2ef /tmp/gn.zip"
+ARG GN_HASH=r3styzkFvKVmVeEhMbNl8cuo4VnbgNICIzDE9SL6su8C
+
+RUN curl --location --silent --output /tmp/gn.zip \
+ "https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/${GN_HASH}" \
+ && echo ${GN_SHA256SUM} | sha256sum --check \
+ && unzip /tmp/gn.zip -d /usr/local/bin \
+ && rm /tmp/gn.zip
+
# We create and use a non-root user explicitly so that the generated and
# modified files maintain the same permissions as the user that launched the
# Docker container.
RUN addgroup --group --gid "${GID}" defaultgroup \
- && adduser --disabled-password --gecos '' --uid "${UID}" --gid "${GID}" defaultuser
+ && adduser --disabled-password --gecos '' --uid "${UID}" --gid "${GID}" ${USER:-defaultuser}
+
+# Allow the new uses to run gn and create a descriptive out directory.
+RUN chmod a+x /usr/local/bin/gn
+RUN mkdir /project_out_dir \
+ && chown ${USER:-defaultuser}:defaultgroup /project_out_dir
USER ${USER:-defaultuser}
diff --git a/docker/docsite/docker-compose.yml b/docker/docsite/docker-compose.yml
index bc0b6e0..02e0aa5 100644
--- a/docker/docsite/docker-compose.yml
+++ b/docker/docsite/docker-compose.yml
@@ -20,12 +20,16 @@
context: .
dockerfile: Dockerfile
container_name: docsite
+ environment:
+ - IS_DOCKER=1
+ - PYTHONPATH=/cobalt
ports:
- "4000:4000"
volumes:
# We use ../../ when the environment variable COBALT_SRC is not set since
# this file is located two directories under the root of the repository.
- ${COBALT_SRC:-../../}:/cobalt/
+
docsite-build-only:
container_name: docsite-build-only
environment:
diff --git a/docker/linux/base/build/Dockerfile b/docker/linux/base/build/Dockerfile
index 27050e3..a2d683e 100644
--- a/docker/linux/base/build/Dockerfile
+++ b/docker/linux/base/build/Dockerfile
@@ -15,8 +15,6 @@
ARG FROM_IMAGE
FROM ${FROM_IMAGE:-cobalt-base}
-ARG gn_hash=vC0rxqiqGTD3ls9KJHrgJoWP2OBiPk_QEO_xbDItKYoC
-
ARG HOME=/root
# === Install common build tools, required by all platforms
@@ -50,8 +48,12 @@
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
# === Get GN via CIPD
+ARG GN_SHA256SUM="af7b2dcb3905bca56655e12131b365f1cba8e159db80d2022330c4f522fab2ef /tmp/gn.zip"
+ARG GN_HASH=r3styzkFvKVmVeEhMbNl8cuo4VnbgNICIzDE9SL6su8C
+
RUN curl --location --silent --output /tmp/gn.zip \
- "https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/${gn_hash}" \
+ "https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/${GN_HASH}" \
+ && echo ${GN_SHA256SUM} | sha256sum --check \
&& unzip /tmp/gn.zip -d /usr/local/bin \
&& rm /tmp/gn.zip
diff --git a/docker/precommit_hooks/Dockerfile b/docker/precommit_hooks/Dockerfile
index 19630fb..956c86c 100644
--- a/docker/precommit_hooks/Dockerfile
+++ b/docker/precommit_hooks/Dockerfile
@@ -27,14 +27,14 @@
RUN pip3 install "pre-commit<3" "cpplint<2" "yapf<1" "pylint<3"
# === Get GN via CIPD
-ARG GN_SHA256SUM="1291d4cf9729b6615c621139be4e9c8bb49b5cc80330e7a9e3e83c583d683f71 /usr/local/bin/gn"
-ARG GN_HASH=vC0rxqiqGTD3ls9KJHrgJoWP2OBiPk_QEO_xbDItKYoC
+ARG GN_SHA256SUM="af7b2dcb3905bca56655e12131b365f1cba8e159db80d2022330c4f522fab2ef /tmp/gn.zip"
+ARG GN_HASH=r3styzkFvKVmVeEhMbNl8cuo4VnbgNICIzDE9SL6su8C
RUN curl --location --silent --output /tmp/gn.zip \
"https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/${GN_HASH}" \
+ && echo ${GN_SHA256SUM} | sha256sum --check \
&& unzip /tmp/gn.zip -d /usr/local/bin \
- && rm /tmp/gn.zip \
- && echo ${GN_SHA256SUM} | sha256sum --check
+ && rm /tmp/gn.zip
WORKDIR /code
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 8c7de67..1044e1d 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -4218,6 +4218,7 @@
"//base:i18n",
"//base/test:test_support",
"//base/third_party/dynamic_annotations",
+ "//cobalt/persistent_storage:persistent_settings",
"//crypto",
"//testing/gmock",
"//testing/gtest",
diff --git a/net/dial/dial_http_server.cc b/net/dial/dial_http_server.cc
index dc59290..6a0dc72 100644
--- a/net/dial/dial_http_server.cc
+++ b/net/dial/dial_http_server.cc
@@ -4,6 +4,7 @@
#include "net/dial/dial_http_server.h"
+#include <memory>
#include <vector>
#include "base/bind.h"
@@ -20,11 +21,6 @@
#include "net/socket/stream_socket.h"
#include "net/socket/tcp_server_socket.h"
-#if defined(__LB_SHELL__)
-#include "lb_network_helpers.h"
-#include "starboard/string.h"
-#endif
-
namespace net {
namespace {
@@ -59,7 +55,7 @@
memset(&local_ip, 0, sizeof(local_ip));
bool result = false;
- // Dial Server only supports Ipv4 now.
+ // DIAL Server only supports Ipv4 now.
SbSocketAddressType address_types = {kSbSocketAddressTypeIpv4};
SbSocketAddress destination;
memset(&(destination.address), 0, sizeof(destination.address));
@@ -86,7 +82,7 @@
new net::TCPServerSocket(NULL /*net_log*/, net::NetLogSource());
base::Optional<net::IPEndPoint> ip_addr = GetLocalIpAddress();
if (!ip_addr) {
- DLOG(ERROR) << "Can not get a local address for Dial HTTP Server";
+ LOG(ERROR) << "Can not get a local address for DIAL HTTP Server";
} else {
server_socket->ListenWithAddressAndPort(
ip_addr.value().address().ToString(), ip_addr.value().port(),
@@ -124,12 +120,14 @@
SbSocketAddress destination = {0};
SbSocketAddress netmask = {0};
- // Dial only works with IPv4.
+ // DIAL only works with IPv4.
destination.type = kSbSocketAddressTypeIpv4;
if (!SbSocketGetInterfaceAddress(&destination, &local_ip, NULL)) {
return ERR_FAILED;
}
local_ip.port = addr->port();
+ LOG_ONCE(INFO) << "In-App DIAL Address http://" << addr->address().ToString()
+ << ":" << addr->port();
if (addr->FromSbSocketAddress(&local_ip)) {
return OK;
@@ -148,8 +146,7 @@
SendDeviceDescriptionManifest(conn_id);
} else if (strstr(info.path.c_str(), kAppsPrefix)) {
- if (info.method == "GET" &&
- info.path.length() == strlen(kAppsPrefix)) {
+ if (info.method == "GET" && info.path.length() == strlen(kAppsPrefix)) {
// If /apps/ request, send 302 to current application.
http_server_->SendRaw(
conn_id,
diff --git a/net/dial/dial_udp_server.cc b/net/dial/dial_udp_server.cc
index 2555e03..3ad43c0 100644
--- a/net/dial/dial_udp_server.cc
+++ b/net/dial/dial_udp_server.cc
@@ -9,6 +9,7 @@
#else
#include <arpa/inet.h>
#endif
+#include <memory>
#include <utility>
#include <vector>
@@ -77,7 +78,7 @@
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
socket_ = factory_->CreateAndBind(GetAddressForAllInterfaces(1900));
if (!socket_) {
- DLOG(WARNING) << "Failed to bind socket for Dial UDP Server";
+ LOG(WARNING) << "Failed to bind socket for DIAL UDP Server";
return;
}
@@ -103,8 +104,7 @@
void DialUdpServer::Stop() {
DCHECK(is_running_);
thread_.message_loop()->task_runner()->PostBlockingTask(
- FROM_HERE, base::Bind(&DialUdpServer::Shutdown,
- base::Unretained(this)));
+ FROM_HERE, base::Bind(&DialUdpServer::Shutdown, base::Unretained(this)));
thread_.Stop();
}
@@ -251,6 +251,7 @@
DialSystemConfig::GetInstance()->model_uuid(),
kDialStRequest));
ret.append("\r\n");
+ LOG_ONCE(INFO) << "In-App DIAL Discovery response : " << ret;
return std::move(ret);
}
diff --git a/net/disk_cache/cobalt/cobalt_backend_impl.cc b/net/disk_cache/cobalt/cobalt_backend_impl.cc
index ec25ce8..76c845a 100644
--- a/net/disk_cache/cobalt/cobalt_backend_impl.cc
+++ b/net/disk_cache/cobalt/cobalt_backend_impl.cc
@@ -19,7 +19,9 @@
#include <utility>
#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/values.h"
#include "net/disk_cache/backend_cleanup_tracker.h"
using base::Time;
@@ -28,6 +30,8 @@
namespace {
+const char kPersistentSettingsJson[] = "cache_settings.json";
+
void CompletionOnceCallbackHandler(
scoped_refptr<CobaltBackendImpl::RefCountedRunner> runner,
int result) {
@@ -49,6 +53,17 @@
return kOther;
}
+void ReadDiskCacheSize(
+ cobalt::persistent_storage::PersistentSettings* settings) {
+ for (int i = 0; i < disk_cache::kTypeCount; i++) {
+ auto metadata = disk_cache::kTypeMetadata[i];
+ uint32_t bucket_size =
+ static_cast<uint32_t>(settings->GetPersistentSettingAsDouble(
+ metadata.directory, metadata.max_size_bytes));
+ disk_cache::kTypeMetadata[i] = {metadata.directory, bucket_size};
+ }
+}
+
} // namespace
CobaltBackendImpl::CobaltBackendImpl(
@@ -58,12 +73,17 @@
net::CacheType cache_type,
net::NetLog* net_log)
: weak_factory_(this) {
+ persistent_settings_ =
+ std::make_unique<cobalt::persistent_storage::PersistentSettings>(
+ kPersistentSettingsJson, base::MessageLoop::current()->task_runner());
+ ReadDiskCacheSize(persistent_settings_.get());
+
// Initialize disk backend for each resource type.
int64_t total_size = 0;
for (int i = 0; i < kTypeCount; i++) {
- base::FilePath dir =
- path.Append(FILE_PATH_LITERAL(kTypeMetadata[i].directory));
- int64_t bucket_size = kTypeMetadata[i].max_size_mb * 1024 * 1024;
+ auto metadata = kTypeMetadata[i];
+ base::FilePath dir = path.Append(FILE_PATH_LITERAL(metadata.directory));
+ int64_t bucket_size = metadata.max_size_bytes;
total_size += bucket_size;
SimpleBackendImpl* simple_backend = new SimpleBackendImpl(
dir, cleanup_tracker, /* file_tracker = */ nullptr, bucket_size,
@@ -82,6 +102,28 @@
simple_backend_map_.clear();
}
+void CobaltBackendImpl::UpdateSizes(ResourceType type, uint32_t bytes) {
+ if (bytes == disk_cache::kTypeMetadata[type].max_size_bytes)
+ return;
+
+ // Static cast value to double since base::Value cannot be a long.
+ persistent_settings_->SetPersistentSetting(
+ disk_cache::kTypeMetadata[type].directory,
+ std::make_unique<base::Value>(static_cast<double>(bytes)));
+
+ disk_cache::kTypeMetadata[type].max_size_bytes = bytes;
+ SimpleBackendImpl* simple_backend = simple_backend_map_[type];
+ simple_backend->SetMaxSize(bytes);
+}
+
+uint32_t CobaltBackendImpl::GetQuota(ResourceType type) {
+ return disk_cache::kTypeMetadata[type].max_size_bytes;
+}
+
+void CobaltBackendImpl::ValidatePersistentSettings() {
+ persistent_settings_->ValidatePersistentSettings();
+}
+
net::Error CobaltBackendImpl::Init(CompletionOnceCallback completion_callback) {
auto closure_runner =
base::MakeRefCounted<RefCountedRunner>(std::move(completion_callback));
diff --git a/net/disk_cache/cobalt/cobalt_backend_impl.h b/net/disk_cache/cobalt/cobalt_backend_impl.h
index 5a4443b..f76d0e9 100644
--- a/net/disk_cache/cobalt/cobalt_backend_impl.h
+++ b/net/disk_cache/cobalt/cobalt_backend_impl.h
@@ -23,6 +23,7 @@
#include <utility>
#include "base/callback_helpers.h"
+#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/base/completion_once_callback.h"
#include "net/disk_cache/cobalt/resource_type.h"
#include "net/disk_cache/disk_cache.h"
@@ -48,6 +49,9 @@
~CobaltBackendImpl() override;
net::Error Init(CompletionOnceCallback completion_callback);
+ void UpdateSizes(ResourceType type, uint32_t bytes);
+ uint32_t GetQuota(ResourceType type);
+ void ValidatePersistentSettings();
// Backend interface.
net::CacheType GetCacheType() const override;
@@ -111,6 +115,10 @@
base::WeakPtrFactory<CobaltBackendImpl> weak_factory_;
std::map<ResourceType, SimpleBackendImpl*> simple_backend_map_;
+
+ // Json PrefStore used for persistent settings.
+ std::unique_ptr<cobalt::persistent_storage::PersistentSettings>
+ persistent_settings_;
};
} // namespace disk_cache
diff --git a/net/disk_cache/cobalt/resource_type.h b/net/disk_cache/cobalt/resource_type.h
index 72af7d7..b6b5cc7 100644
--- a/net/disk_cache/cobalt/resource_type.h
+++ b/net/disk_cache/cobalt/resource_type.h
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef RESOURCE_TYPE_H_
-#define RESOURCE_TYPE_H_
+#ifndef NET_DISK_CACHE_COBALT_RESOURCE_TYPE_H_
+#define NET_DISK_CACHE_COBALT_RESOURCE_TYPE_H_
+
+#include <string>
namespace disk_cache {
@@ -32,15 +34,19 @@
struct ResourceTypeMetadata {
std::string directory;
- int64_t max_size_mb;
+ uint32_t max_size_bytes;
};
-// TODO: Store sizes on disk.
+static uint32_t kInitialBytes = 3 * 1024 * 1024;
+// These values are updated on start up in application.cc, using the
+// persisted values saved in settings.json.
static ResourceTypeMetadata kTypeMetadata[] = {
- {"other", 3}, {"html", 3}, {"css", 3}, {"image", 3},
- {"font", 3}, {"splash", 3}, {"uncompiled_js", 3}, {"compiled_js", 3},
+ {"other", kInitialBytes}, {"html", kInitialBytes},
+ {"css", kInitialBytes}, {"image", kInitialBytes},
+ {"font", kInitialBytes}, {"splash", kInitialBytes},
+ {"uncompiled_js", kInitialBytes}, {"compiled_js", kInitialBytes},
};
} // namespace disk_cache
-#endif // RESOURCE_TYPE_H_
+#endif // NET_DISK_CACHE_COBALT_RESOURCE_TYPE_H_
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index f9ceeee..f956eff 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -42,7 +42,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import dev.cobalt.account.UserAuthorizer;
-import dev.cobalt.libraries.services.clientloginfo.ClientLogInfo;
import dev.cobalt.media.AudioOutputManager;
import dev.cobalt.media.CaptionSettings;
import dev.cobalt.media.CobaltMediaSession;
@@ -146,12 +145,6 @@
activityHolder.set(activity);
this.keyboardEditor = keyboardEditor;
sysConfigChangeReceiver.setForeground(true);
-
- // TODO: v0_1231sd2 is the default value used for testing,
- // delete it once we verify it can be queried in QOE system.
- if (!isReleaseBuild()) {
- ClientLogInfo.setClientInfo("v0_1231sd2");
- }
}
protected void onActivityStop(Activity activity) {
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
index e2e9b43..c0cee61 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
@@ -365,6 +365,7 @@
private MediaCodecUtil() {}
/** A wrapper class of codec capability infos. */
+ @UsedByNative
public static class CodecCapabilityInfo {
CodecCapabilityInfo(MediaCodecInfo codecInfo, String mimeType) {
this.codecInfo = codecInfo;
@@ -375,13 +376,14 @@
this.videoCapabilities = this.codecCapabilities.getVideoCapabilities();
}
- public MediaCodecInfo codecInfo;
- public String mimeType;
- public String decoderName;
- public CodecCapabilities codecCapabilities;
- public AudioCapabilities audioCapabilities;
- public VideoCapabilities videoCapabilities;
+ @UsedByNative public MediaCodecInfo codecInfo;
+ @UsedByNative public String mimeType;
+ @UsedByNative public String decoderName;
+ @UsedByNative public CodecCapabilities codecCapabilities;
+ @UsedByNative public AudioCapabilities audioCapabilities;
+ @UsedByNative public VideoCapabilities videoCapabilities;
+ @UsedByNative
public boolean isSecureRequired() {
// MediaCodecList is supposed to feed us names of decoders that do NOT end in ".secure". We
// are then supposed to check if FEATURE_SecurePlayback is supported, and if it is and we
@@ -397,25 +399,30 @@
MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
}
+ @UsedByNative
public boolean isSecureSupported() {
return this.codecCapabilities.isFeatureSupported(
MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
}
+ @UsedByNative
public boolean isTunnelModeRequired() {
return this.codecCapabilities.isFeatureRequired(
MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
}
+ @UsedByNative
public boolean isTunnelModeSupported() {
return this.codecCapabilities.isFeatureSupported(
MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
}
+ @UsedByNative
public boolean isSoftware() {
return isSoftwareDecoder(this.codecInfo);
}
+ @UsedByNative
public boolean isHdrCapable() {
return isHdrCapableVideoDecoder(this.mimeType, this.codecCapabilities);
}
@@ -523,6 +530,7 @@
* <p>NOTE: This code path is called repeatedly by the player to determine the decoding
* capabilities of the device. To ensure speedy playback the code below should be kept performant.
*/
+ @UsedByNative
public static String findVideoDecoder(
String mimeType,
boolean mustSupportSecure,
@@ -743,6 +751,7 @@
* The same as hasAudioDecoderFor, only return the name of the audio decoder if it is found, and
* "" otherwise.
*/
+ @UsedByNative
public static String findAudioDecoder(
String mimeType, int bitrate, boolean mustSupportTunnelMode) {
// Note: MediaCodecList is sorted by the framework such that the best decoders come first.
diff --git a/starboard/android/shared/test_filters.py b/starboard/android/shared/test_filters.py
index a57ab71..8d5d0c0 100644
--- a/starboard/android/shared/test_filters.py
+++ b/starboard/android/shared/test_filters.py
@@ -16,6 +16,7 @@
from starboard.tools.testing import test_filter
# A map of failing or crashing tests per target.
+# pylint: disable=line-too-long
_FILTERED_TESTS = {
'player_filter_tests': [
# GetMaxNumberOfCachedFrames() on Android is device dependent,
@@ -43,7 +44,7 @@
'nplb': [
# This test is failing because localhost is not defined for IPv6 in
# /etc/hosts.
- 'SbSocketAddressTypes/SbSocketResolveTest.Localhost/1',
+ 'SbSocketAddressTypes/SbSocketResolveTest.Localhost/filter_ipv6_type_ipv6',
# SbDirectory has problems with empty Asset dirs.
'SbDirectoryCanOpenTest.SunnyDayStaticContent',
@@ -67,6 +68,7 @@
'SbDrmSessionTest.InvalidSessionUpdateRequestParams',
],
}
+# pylint: enable=line-too-long
class TestFilters(object):
diff --git a/starboard/build/config/base_configuration.gni b/starboard/build/config/base_configuration.gni
index 23b45ac..25d3ba3 100644
--- a/starboard/build/config/base_configuration.gni
+++ b/starboard/build/config/base_configuration.gni
@@ -16,6 +16,10 @@
import("//cobalt/content/fonts/font_configuration.gni")
import("//starboard/build/config/enable_vr.gni")
+# NOTE:
+# All build arguments in this file must have documentation.
+# Please follow the formatting in this file when adding new ones.
+
declare_args() {
# Enables the yasm compiler to be used to compile .asm files.
yasm_exists = false
@@ -47,9 +51,14 @@
# Whether to adopt Evergreen Lite on the Evergreen compatible platform.
sb_evergreen_compatible_enable_lite = false
- # The variables allow changing the target type on platforms where the
- # native code may require an additional packaging step (ex. Android).
+ # The target type for test targets. Allows changing the target type
+ # on platforms where the native code may require an additional packaging step
+ # (ex. Android).
gtest_target_type = "executable"
+
+ # The target type for executable targets. Allows changing the target type
+ # on platforms where the native code may require an additional packaging step
+ # (ex. Android).
final_executable_type = "executable"
# Halt execution on failure to allocate memory.
@@ -57,13 +66,13 @@
# The source of EGL and GLES headers and libraries.
# Valid values (case and everything sensitive!):
- # 'none' - No EGL + GLES implementation is available on this platform.
- # 'system_gles2' - Use the system implementation of EGL + GLES2. The
+ # "none" - No EGL + GLES implementation is available on this platform.
+ # "system_gles2" - Use the system implementation of EGL + GLES2. The
# headers and libraries must be on the system include and
# link paths.
- # 'glimp' - Cobalt's own EGL + GLES2 implementation. This requires a
+ # "glimp" - Cobalt's own EGL + GLES2 implementation. This requires a
# valid Glimp implementation for the platform.
- # 'angle' - A DirectX-to-OpenGL adaptation layer. This requires a valid
+ # "angle" - A DirectX-to-OpenGL adaptation layer. This requires a valid
# ANGLE implementation for the platform.
gl_type = "system_gles2"
@@ -91,11 +100,19 @@
# defines how the build should produce the install/ directory.
install_target_path = "//starboard/build/install/no_install.gni"
- # Target-specific configurations for each platform.
+ # Target-specific configurations for executable targets.
executable_configs = []
+
+ # Target-specific configurations for shared_library targets.
shared_library_configs = []
+
+ # Target-specific configurations for static_library targets.
static_library_configs = []
+
+ # Target-specific configurations for source_set targets.
source_set_configs = []
+
+ # Target-specific configurations for loadable_module targets.
loadable_module_configs = []
# Enables optimizations on SSE compatible platforms.
@@ -138,6 +155,7 @@
# Whether or not to link with thin archives.
use_thin_archive = true
+ # Whether or not to disable run-time type information (adding no_rtti flag).
sb_use_no_rtti = false
# Set to true to separate install target directories.
@@ -146,6 +164,6 @@
# Enables an NPLB audit of C++17 support.
sb_enable_cpp17_audit = true
- # Flag to use a future version of Skia, currently not available
+ # Flag to use a future version of Skia, currently not available.
use_skia_next = false
}
diff --git a/starboard/build/copy_install_content.py b/starboard/build/copy_install_content.py
index 1e29cbf..00fa015 100644
--- a/starboard/build/copy_install_content.py
+++ b/starboard/build/copy_install_content.py
@@ -40,13 +40,14 @@
# if not os.path.isfile(path):
# raise InvalidArgumentException(path + ' is not a file.')
- # Get the path of the file relative to the source base_dir.
- rel_path = os.path.relpath(path, base_dir)
-
# In certain cases, files would fail to open on windows if relative paths
# were provided. Using absolute paths fixes this.
- filename = os.path.abspath(os.path.join(base_dir, rel_path))
+ filename = os.path.abspath(path)
+
+ # Get the path of the file relative to the source base_dir.
+ rel_path = os.path.relpath(path, base_dir)
output_dir = os.path.abspath(output_dir)
+ # Use rel_path to preserve the input folder structure in the output.
output_filename = os.path.abspath(os.path.join(output_dir, rel_path))
# In cases where a directory has turned into a file or vice versa, delete it
diff --git a/starboard/doc/evergreen/evergreen_binary_compression.md b/starboard/doc/evergreen/evergreen_binary_compression.md
new file mode 100644
index 0000000..fd8100d
--- /dev/null
+++ b/starboard/doc/evergreen/evergreen_binary_compression.md
@@ -0,0 +1,110 @@
+# Evergreen Binary Compression
+
+## What is Evergreen Binary Compression?
+
+Evergreen Binary Compression is a feature that reduces the amount of space used
+on-device to store Cobalt Core binaries. The binaries are stored compressed,
+using the
+[LZ4 Frame Format](https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md),
+and decompressed when they are loaded and run. This optional feature is off by
+default but partners can enable it by following the instructions below.
+
+## Storage Savings
+
+Across all reference devices tested, including Raspberry Pi 2, we have seen
+compression ratios just above 2.0. We therefore expect that partners can halve
+the space used for Cobalt binary storage by enabling the feature for all
+installation slots: the system image slot and the writable slots.
+
+## Caveats
+
+### Performance Costs
+
+Because the Cobalt Core shared library must be decompressed before it's loaded,
+there is necessarily some effect on startup latency and CPU memory usage. Based
+on our analysis across a range of devices these effects are relatively small.
+
+For startup latency, we measured an increase in `libcobalt` load time in the
+hundreds of milliseconds for the low-powered Raspberry Pi 2 and in the tens of
+milliseconds for more representative, higher-powered devices. This increase is
+small in the context of overall app load time.
+
+For CPU memory usage, `libcobalt.lz4` is decompressed to an in-memory ELF file
+before the program is loaded. We therefore expect CPU memory usage to
+temporarily increase by roughly the size of the uncompressed `libcobalt` and
+then to return to a normal level once the library is loaded. And this is exactly
+what we've seen in our analysis.
+
+### Incompatibilities
+
+This feature is incompatible with the Memory Mapped file feature that is
+controlled by the `--loader_use_mmap_file` switch. With compression, we lose the
+required 1:1 mapping between the file and virtual memory. So,
+`--loader_use_mmap_file` should not be set if compression is enabled for any
+installation slots (next section).
+
+## Enabling Binary Compression
+
+Separate steps are required in order for a partner to enable compression for a)
+the system image slot and b) (Evergreen-Full only) the 2+ writable slots on a
+device.
+
+Compression for the system image slot is enabled by installing `libcobalt.lz4`
+instead of `libcobalt.so` in the read-only system image slot (i.e., SLOT_0).
+Starting with a to be determined Cobalt 23 release, the open-source releases on
+[GitHub](https://github.com/youtube/cobalt/releases) include separate CRX
+packages, denoted by a "compressed" suffix, that contain the pre-built and
+LZ4-compressed Cobalt Core binary.
+
+Compression for the writable slots is enabled by configuring Cobalt to launch
+with the `--use_compressed_updates` flag:
+
+```
+$ ./loader_app --use_compressed_updates
+```
+
+This flag instructs the Cobalt Updater to request, download, and install
+packages containing compressed Cobalt Core binaries from Google Update and
+Google Downloads. As a result, the device ends up with `libcobalt.lz4` instead
+of `libcobalt.so` in the relevant slot after its next update.
+
+Note that the system image slot is independent from the writable slots with
+respect to the compression feature: the feature can be enabled for the system
+image slot but disabled for the writable slots, or vice versa. However, we
+expect that partners will typically enable the feature for all slots in order to
+take full advantage of the storage reduction.
+
+## Disabling Binary Compression
+
+The compression feature is turned off by default. Once enabled, though, it can
+be disabled by undoing the steps required for enablement.
+
+Compression for the system image slot is disabled by installing the uncompressed
+`libcobalt.so` instead of `libcobalt.lz4` in the read-only system image slot.
+
+Compression for the writable slots is disabled by configuring Cobalt to launch
+**without** the `--use_compressed_updates` flag. The Cobalt Updater will then be
+instructed to request, download, and install packages containing the
+uncompressed Cobalt Core binaries.
+
+Note that disabling compression for the writable slots is eventual in the sense
+that the compressed Cobalt Core binaries in these slots will remain and continue
+to be used until newer versions of Cobalt Core are available on Google Update
+and Google Downloads.
+
+## FAQ
+
+### Should multiple apps that share binaries also share compression settings?
+
+The [Cobalt Evergreen Overview doc](cobalt_evergreen_overview.md) describes
+multi-app support, where multiple Cobalt-based applications share Cobalt Core
+binaries (i.e., they share the same installation slot(s)). When multi-app
+support is used then, yes, the apps should also share compression settings.
+
+For the system image slot, this means that either `libcobalt.lz4` or
+`libcobalt.so` is installed in the one, shared slot.
+
+For the writable slots, this means that the loader app is either launched with
+`--use_compressed_updates` set for all apps or launched without it for all apps.
+Otherwise, the different apps would compete with respect to whether the writable
+slots are upgraded to compressed or uncompressed binaries.
diff --git a/starboard/evergreen/arm/shared/install_target.gni b/starboard/evergreen/arm/shared/install_target.gni
index 83e7915..eaa6f16 100644
--- a/starboard/evergreen/arm/shared/install_target.gni
+++ b/starboard/evergreen/arm/shared/install_target.gni
@@ -23,7 +23,8 @@
}
if (invoker.type == "shared_library" &&
- invoker.installable_target_name == "cobalt") {
+ filter_include([ invoker.installable_target_name ],
+ lz4_install_targets) != []) {
lz4_compress_install_target(target_name) {
forward_variables_from(invoker, "*")
}
@@ -33,7 +34,8 @@
forward_variables_from(invoker, [ "testonly" ])
deps = [ ":${target_name}_run_strip" ]
if (invoker.type == "shared_library" &&
- invoker.installable_target_name == "cobalt") {
+ filter_include([ invoker.installable_target_name ],
+ lz4_install_targets) != []) {
deps += [ ":${target_name}_run_lz4_compress" ]
}
}
diff --git a/starboard/evergreen/arm64/install_target.gni b/starboard/evergreen/arm64/install_target.gni
index bf0f54a..55d3b07 100644
--- a/starboard/evergreen/arm64/install_target.gni
+++ b/starboard/evergreen/arm64/install_target.gni
@@ -23,7 +23,8 @@
}
if (invoker.type == "shared_library" &&
- invoker.installable_target_name == "cobalt") {
+ filter_include([ invoker.installable_target_name ],
+ lz4_install_targets) != []) {
lz4_compress_install_target(target_name) {
forward_variables_from(invoker, "*")
}
@@ -33,7 +34,8 @@
forward_variables_from(invoker, [ "testonly" ])
deps = [ ":${target_name}_run_strip" ]
if (invoker.type == "shared_library" &&
- invoker.installable_target_name == "cobalt") {
+ filter_include([ invoker.installable_target_name ],
+ lz4_install_targets) != []) {
deps += [ ":${target_name}_run_lz4_compress" ]
}
}
diff --git a/starboard/evergreen/shared/lz4_compress_install_target.gni b/starboard/evergreen/shared/lz4_compress_install_target.gni
index 69c7603..b9c0b27 100644
--- a/starboard/evergreen/shared/lz4_compress_install_target.gni
+++ b/starboard/evergreen/shared/lz4_compress_install_target.gni
@@ -14,6 +14,13 @@
import("//build/compiled_action.gni")
+lz4_install_targets = [
+ "cobalt",
+ "crash_sandbox",
+ "noop_sandbox",
+ "one_app_only_sandbox",
+]
+
template("lz4_compress_install_target") {
install_target_name = target_name
installable_target_name = invoker.installable_target_name
diff --git a/starboard/evergreen/x64/install_target.gni b/starboard/evergreen/x64/install_target.gni
index 389def0..3b5fd48 100644
--- a/starboard/evergreen/x64/install_target.gni
+++ b/starboard/evergreen/x64/install_target.gni
@@ -23,7 +23,8 @@
}
if (invoker.type == "shared_library" &&
- invoker.installable_target_name == "cobalt") {
+ filter_include([ invoker.installable_target_name ],
+ lz4_install_targets) != []) {
lz4_compress_install_target(target_name) {
forward_variables_from(invoker, "*")
}
@@ -33,7 +34,8 @@
forward_variables_from(invoker, [ "testonly" ])
deps = [ ":${target_name}_run_strip" ]
if (invoker.type == "shared_library" &&
- invoker.installable_target_name == "cobalt") {
+ filter_include([ invoker.installable_target_name ],
+ lz4_install_targets) != []) {
deps += [ ":${target_name}_run_lz4_compress" ]
}
}
diff --git a/starboard/evergreen/x86/install_target.gni b/starboard/evergreen/x86/install_target.gni
index d0ab661..90a1b77 100644
--- a/starboard/evergreen/x86/install_target.gni
+++ b/starboard/evergreen/x86/install_target.gni
@@ -23,7 +23,8 @@
}
if (invoker.type == "shared_library" &&
- invoker.installable_target_name == "cobalt") {
+ filter_include([ invoker.installable_target_name ],
+ lz4_install_targets) != []) {
lz4_compress_install_target(target_name) {
forward_variables_from(invoker, "*")
}
@@ -33,7 +34,8 @@
forward_variables_from(invoker, [ "testonly" ])
deps = [ ":${target_name}_run_strip" ]
if (invoker.type == "shared_library" &&
- invoker.installable_target_name == "cobalt") {
+ filter_include([ invoker.installable_target_name ],
+ lz4_install_targets) != []) {
deps += [ ":${target_name}_run_lz4_compress" ]
}
}
diff --git a/starboard/linux/shared/test_filters.py b/starboard/linux/shared/test_filters.py
index 9592f73..b92b3a9 100644
--- a/starboard/linux/shared/test_filters.py
+++ b/starboard/linux/shared/test_filters.py
@@ -30,27 +30,24 @@
# Conditionally disables tests that require ipv6
if os.getenv('IPV6_AVAILABLE', '1') == '0':
_FILTERED_TESTS['nplb'].extend([
- 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDayDestination/1',
- 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceForDestination/1',
- 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceNotLoopback/1',
+ 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDayDestination/type_ipv6',
+ 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceForDestination/type_ipv6',
+ 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceNotLoopback/type_ipv6',
+ 'SbSocketAddressTypes/SbSocketBindTest.RainyDayBadInterface/type_ipv6_filter_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketGetLocalAddressTest.SunnyDayConnected/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketIsConnectedAndIdleTest.SunnyDay/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketIsConnectedTest.SunnyDay/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketReceiveFromTest.SunnyDay/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/SbSocketResolveTest.Localhost/filter_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/SbSocketResolveTest.SunnyDayFiltered/filter_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToClosedSocket/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToSocketUntilBlocking/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToSocketConnectionReset/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketWaiterWaitTest.SunnyDay/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketWaiterWaitTest.SunnyDayAlreadyReady/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketWaiterWaitTimedTest.SunnyDay/type_ipv6_type_ipv6',
+ 'SbSocketAddressTypes/PairSbSocketWrapperTest.SunnyDay/type_ipv6_type_ipv6',
])
-# TODO: Re-enable once tests or infra fixed.
-_FILTERED_TESTS['nplb'].extend([
- 'SbSocketAddressTypes/SbSocketBindTest.RainyDayBadInterface/1',
- 'SbSocketAddressTypes/PairSbSocketGetLocalAddressTest.SunnyDayConnected/1',
- 'SbSocketAddressTypes/PairSbSocketIsConnectedAndIdleTest.SunnyDay/1',
- 'SbSocketAddressTypes/PairSbSocketIsConnectedTest.SunnyDay/1',
- 'SbSocketAddressTypes/PairSbSocketReceiveFromTest.SunnyDay/1',
- 'SbSocketAddressTypes/SbSocketResolveTest.Localhost/1',
- 'SbSocketAddressTypes/SbSocketResolveTest.SunnyDayFiltered/1',
- 'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToClosedSocket/1',
- 'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToSocketUntilBlocking/1',
- 'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToSocketConnectionReset/1',
- 'SbSocketAddressTypes/PairSbSocketWaiterWaitTest.SunnyDay/1',
- 'SbSocketAddressTypes/PairSbSocketWaiterWaitTest.SunnyDayAlreadyReady/1',
- 'SbSocketAddressTypes/PairSbSocketWaiterWaitTimedTest.SunnyDay/1',
- 'SbSocketAddressTypes/PairSbSocketWrapperTest.SunnyDay/1',
-])
# pylint: enable=line-too-long
diff --git a/starboard/loader_app/loader_app.cc b/starboard/loader_app/loader_app.cc
index 929c875..8bae605 100644
--- a/starboard/loader_app/loader_app.cc
+++ b/starboard/loader_app/loader_app.cc
@@ -249,7 +249,7 @@
std::string url =
command_line.GetSwitchValue(starboard::loader_app::kURL);
if (url.empty()) {
- url = "https://www.youtube.com/tv";
+ url = kCobaltDefaultUrl;
}
std::string app_key = starboard::loader_app::GetAppKey(url);
SB_CHECK(!app_key.empty());
diff --git a/starboard/nplb/file_read_write_all_test.cc b/starboard/nplb/file_read_write_all_test.cc
index 9782fde..4871cea 100644
--- a/starboard/nplb/file_read_write_all_test.cc
+++ b/starboard/nplb/file_read_write_all_test.cc
@@ -20,8 +20,7 @@
namespace nplb {
namespace {
-class SbReadWriteAllTestWithBuffer
- : public ::testing::TestWithParam<int> {
+class SbReadWriteAllTestWithBuffer : public ::testing::TestWithParam<int> {
public:
int GetBufferSize() { return GetParam(); }
};
@@ -30,8 +29,8 @@
ScopedRandomFile random_file(0, ScopedRandomFile::kDontCreate);
const std::string& filename = random_file.filename();
- SbFile file = SbFileOpen(
- filename.c_str(), kSbFileCreateAlways | kSbFileWrite, NULL, NULL);
+ SbFile file = SbFileOpen(filename.c_str(), kSbFileCreateAlways | kSbFileWrite,
+ NULL, NULL);
std::vector<char> file_contents;
file_contents.reserve(GetBufferSize());
@@ -44,8 +43,8 @@
SbFileClose(file);
- file = SbFileOpen(
- filename.c_str(), kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+ file =
+ SbFileOpen(filename.c_str(), kSbFileOpenOnly | kSbFileRead, NULL, NULL);
std::vector<char> read_contents(GetBufferSize());
int bytes_read =
SbFileReadAll(file, read_contents.data(), read_contents.size());
@@ -58,7 +57,8 @@
INSTANTIATE_TEST_CASE_P(
SbReadAllTestSbReadWriteAllTest,
SbReadWriteAllTestWithBuffer,
- ::testing::Values(0, 1, 1024, 16 * 1024, 128 * 1024, 1024 * 1024));
+ ::testing::Values(0, 1, 1024, 16 * 1024, 128 * 1024, 1024 * 1024),
+ ::testing::PrintToStringParamName());
} // namespace
} // namespace nplb
diff --git a/starboard/nplb/player_write_sample_test.cc b/starboard/nplb/player_write_sample_test.cc
index f80bb0e..1eca87f 100644
--- a/starboard/nplb/player_write_sample_test.cc
+++ b/starboard/nplb/player_write_sample_test.cc
@@ -192,6 +192,21 @@
: "Punchout");
}
+std::string GetSbPlayerTestConfigName(
+ ::testing::TestParamInfo<SbPlayerTestConfig> info) {
+ const char* audio_filename = std::get<0>(info.param);
+ const char* video_filename = std::get<1>(info.param);
+ SbPlayerOutputMode output_mode = std::get<2>(info.param);
+ std::string name(FormatString(
+ "audio_%s_video_%s_output_%s", audio_filename, video_filename,
+ output_mode == kSbPlayerOutputModeDecodeToTexture ? "DecodeToTexture"
+ : "Punchout"));
+ std::replace(name.begin(), name.end(), '.', '_');
+ std::replace(name.begin(), name.end(), '(', '_');
+ std::replace(name.begin(), name.end(), ')', '_');
+ return name;
+}
+
void SbPlayerWriteSampleTest::SetUp() {
SbMediaVideoCodec video_codec = dmp_reader_->video_codec();
SbMediaAudioCodec audio_codec = dmp_reader_->audio_codec();
@@ -506,7 +521,8 @@
INSTANTIATE_TEST_CASE_P(SbPlayerWriteSampleTests,
SbPlayerWriteSampleTest,
- ValuesIn(GetSupportedSbPlayerTestConfigs()));
+ ValuesIn(GetSupportedSbPlayerTestConfigs()),
+ GetSbPlayerTestConfigName);
} // namespace
} // namespace nplb
diff --git a/starboard/nplb/socket_accept_test.cc b/starboard/nplb/socket_accept_test.cc
index b3c38a4..35195bf 100644
--- a/starboard/nplb/socket_accept_test.cc
+++ b/starboard/nplb/socket_accept_test.cc
@@ -76,11 +76,13 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketAcceptTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketAcceptTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_bind_test.cc b/starboard/nplb/socket_bind_test.cc
index cf1ca79..f5b3a1a 100644
--- a/starboard/nplb/socket_bind_test.cc
+++ b/starboard/nplb/socket_bind_test.cc
@@ -129,19 +129,22 @@
SbSocketBindTest,
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketResolveFilterIpv4),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketResolveFilterIpv6)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketResolveFilterIpv6)),
+ GetSbSocketAddressTypeFilterPairName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketBindTest,
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
SbSocketBindTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketResolveFilterIpv4)));
+ kSbSocketResolveFilterIpv4)),
+ GetSbSocketAddressTypeFilterPairName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_connect_test.cc b/starboard/nplb/socket_connect_test.cc
index 91063ed..7c545a0 100644
--- a/starboard/nplb/socket_connect_test.cc
+++ b/starboard/nplb/socket_connect_test.cc
@@ -49,11 +49,13 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketConnectTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketConnectTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_create_test.cc b/starboard/nplb/socket_create_test.cc
index b34bdb0..301b12e 100644
--- a/starboard/nplb/socket_create_test.cc
+++ b/starboard/nplb/socket_create_test.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include "starboard/common/socket.h"
+#include "starboard/nplb/socket_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace starboard {
@@ -83,11 +84,13 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketCreateTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketCreateTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
#endif
INSTANTIATE_TEST_CASE_P(
@@ -97,7 +100,8 @@
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp),
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketProtocolTcp),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketProtocolUdp)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketProtocolUdp)),
+ GetSbSocketAddressTypeProtocolPairName);
} // namespace
} // namespace nplb
diff --git a/starboard/nplb/socket_get_interface_address_test.cc b/starboard/nplb/socket_get_interface_address_test.cc
index 9a01488..2bfc328 100644
--- a/starboard/nplb/socket_get_interface_address_test.cc
+++ b/starboard/nplb/socket_get_interface_address_test.cc
@@ -177,11 +177,13 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketGetInterfaceAddressTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketGetInterfaceAddressTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
#endif // SB_HAS(IPV6)
} // namespace
diff --git a/starboard/nplb/socket_get_local_address_test.cc b/starboard/nplb/socket_get_local_address_test.cc
index 6fd2b72..29d9a6d 100644
--- a/starboard/nplb/socket_get_local_address_test.cc
+++ b/starboard/nplb/socket_get_local_address_test.cc
@@ -125,23 +125,27 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketGetLocalAddressTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketGetLocalAddressTest,
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketGetLocalAddressTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketGetLocalAddressTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_helpers.cc b/starboard/nplb/socket_helpers.cc
index b9b33fc..6efecd2 100644
--- a/starboard/nplb/socket_helpers.cc
+++ b/starboard/nplb/socket_helpers.cc
@@ -14,8 +14,11 @@
#include "starboard/nplb/socket_helpers.h"
+#include <utility>
+
#include "starboard/common/scoped_ptr.h"
#include "starboard/common/socket.h"
+#include "starboard/common/string.h"
#include "starboard/once.h"
#include "starboard/socket_waiter.h"
#include "starboard/thread.h"
@@ -550,5 +553,87 @@
return SbTimeGetMonotonicNow() - start;
}
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+namespace {
+const char* SbSocketAddressTypeName(SbSocketAddressType type) {
+ const char* name = "unknown";
+ switch (type) {
+ case kSbSocketAddressTypeIpv4:
+ name = "ipv4";
+ break;
+ case kSbSocketAddressTypeIpv6:
+ name = "ipv6";
+ break;
+ }
+ return name;
+}
+
+const char* SbSocketAddressFilterName(SbSocketResolveFilter filter) {
+ const char* name = "unknown";
+ switch (filter) {
+ case kSbSocketResolveFilterNone:
+ name = "none";
+ break;
+ case kSbSocketResolveFilterIpv4:
+ name = "ipv4";
+ break;
+ case kSbSocketResolveFilterIpv6:
+ name = "ipv6";
+ break;
+ }
+ return name;
+}
+
+const char* SbSocketProtocolName(SbSocketProtocol protocol) {
+ const char* name = "unknown";
+ switch (protocol) {
+ case kSbSocketProtocolTcp:
+ name = "tcp";
+ break;
+ case kSbSocketProtocolUdp:
+ name = "udp";
+ break;
+ }
+ return name;
+}
+} // namespace
+
+std::string GetSbSocketAddressTypeName(
+ ::testing::TestParamInfo<SbSocketAddressType> info) {
+ return FormatString("type_%s", SbSocketAddressTypeName(info.param));
+}
+std::string GetSbSocketAddressTypePairName(
+ ::testing::TestParamInfo<
+ std::pair<SbSocketAddressType, SbSocketAddressType>> info) {
+ return FormatString("type_%s_type_%s",
+ SbSocketAddressTypeName(info.param.first),
+ SbSocketAddressTypeName(info.param.second));
+}
+
+std::string GetSbSocketAddressTypeFilterPairName(
+ ::testing::TestParamInfo<
+ std::pair<SbSocketAddressType, SbSocketResolveFilter>> info) {
+ return FormatString("type_%s_filter_%s",
+ SbSocketAddressTypeName(info.param.first),
+ SbSocketAddressFilterName(info.param.second));
+}
+
+std::string GetSbSocketFilterAddressTypePairName(
+ ::testing::TestParamInfo<
+ std::pair<SbSocketResolveFilter, SbSocketAddressType>> info) {
+ return FormatString("filter_%s_type_%s",
+ SbSocketAddressFilterName(info.param.first),
+ SbSocketAddressTypeName(info.param.second));
+}
+
+std::string GetSbSocketAddressTypeProtocolPairName(
+ ::testing::TestParamInfo<std::pair<SbSocketAddressType, SbSocketProtocol>>
+ info) {
+ return FormatString("type_%s_%s", SbSocketAddressTypeName(info.param.first),
+ SbSocketProtocolName(info.param.second));
+}
+
+#endif // #if !defined(COBALT_BUILD_TYPE_GOLD)
+
} // namespace nplb
} // namespace starboard
diff --git a/starboard/nplb/socket_helpers.h b/starboard/nplb/socket_helpers.h
index b9905bb..4d28134 100644
--- a/starboard/nplb/socket_helpers.h
+++ b/starboard/nplb/socket_helpers.h
@@ -15,6 +15,8 @@
#ifndef STARBOARD_NPLB_SOCKET_HELPERS_H_
#define STARBOARD_NPLB_SOCKET_HELPERS_H_
+#include <string>
+#include <utility>
#include <vector>
#include "starboard/common/scoped_ptr.h"
@@ -198,6 +200,23 @@
<< "With " #error " = " << error; \
} while (false)
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+std::string GetSbSocketAddressTypeName(
+ ::testing::TestParamInfo<SbSocketAddressType> info);
+std::string GetSbSocketAddressTypePairName(
+ ::testing::TestParamInfo<
+ std::pair<SbSocketAddressType, SbSocketAddressType>> info);
+std::string GetSbSocketAddressTypeFilterPairName(
+ ::testing::TestParamInfo<
+ std::pair<SbSocketAddressType, SbSocketResolveFilter>> info);
+std::string GetSbSocketFilterAddressTypePairName(
+ ::testing::TestParamInfo<
+ std::pair<SbSocketResolveFilter, SbSocketAddressType>> info);
+std::string GetSbSocketAddressTypeProtocolPairName(
+ ::testing::TestParamInfo<std::pair<SbSocketAddressType, SbSocketProtocol>>
+ info);
+#endif // #if !defined(COBALT_BUILD_TYPE_GOLD)
+
} // namespace nplb
} // namespace starboard
diff --git a/starboard/nplb/socket_is_connected_and_idle_test.cc b/starboard/nplb/socket_is_connected_and_idle_test.cc
index 56ded31..3a16795 100644
--- a/starboard/nplb/socket_is_connected_and_idle_test.cc
+++ b/starboard/nplb/socket_is_connected_and_idle_test.cc
@@ -97,23 +97,27 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketIsConnectedAndIdleTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketIsConnectedAndIdleTest,
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketIsConnectedAndIdleTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketIsConnectedAndIdleTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_is_connected_test.cc b/starboard/nplb/socket_is_connected_test.cc
index f847917..3c2bfdd 100644
--- a/starboard/nplb/socket_is_connected_test.cc
+++ b/starboard/nplb/socket_is_connected_test.cc
@@ -81,23 +81,27 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketIsConnectedTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketIsConnectedTest,
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketIsConnectedTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketIsConnectedTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_listen_test.cc b/starboard/nplb/socket_listen_test.cc
index 13366e5..9dff540 100644
--- a/starboard/nplb/socket_listen_test.cc
+++ b/starboard/nplb/socket_listen_test.cc
@@ -54,11 +54,13 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketListenTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketListenTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_receive_from_test.cc b/starboard/nplb/socket_receive_from_test.cc
index 2c05228..b9deb06 100644
--- a/starboard/nplb/socket_receive_from_test.cc
+++ b/starboard/nplb/socket_receive_from_test.cc
@@ -132,13 +132,15 @@
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketReceiveFromTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_resolve_test.cc b/starboard/nplb/socket_resolve_test.cc
index f50f855..ad923c0 100644
--- a/starboard/nplb/socket_resolve_test.cc
+++ b/starboard/nplb/socket_resolve_test.cc
@@ -114,13 +114,15 @@
SbSocketResolveTest,
::testing::Values(
std::make_pair(kSbSocketResolveFilterIpv4, kSbSocketAddressTypeIpv4),
- std::make_pair(kSbSocketResolveFilterIpv6, kSbSocketAddressTypeIpv6)));
+ std::make_pair(kSbSocketResolveFilterIpv6, kSbSocketAddressTypeIpv6)),
+ GetSbSocketFilterAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
SbSocketResolveTest,
::testing::Values(std::make_pair(kSbSocketResolveFilterIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketFilterAddressTypePairName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_send_to_test.cc b/starboard/nplb/socket_send_to_test.cc
index b20f816..f0be4d5 100644
--- a/starboard/nplb/socket_send_to_test.cc
+++ b/starboard/nplb/socket_send_to_test.cc
@@ -195,13 +195,15 @@
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketSendToTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#endif
} // namespace
} // namespace nplb
diff --git a/starboard/nplb/socket_set_options_test.cc b/starboard/nplb/socket_set_options_test.cc
index 6a11414..60e0c17 100644
--- a/starboard/nplb/socket_set_options_test.cc
+++ b/starboard/nplb/socket_set_options_test.cc
@@ -60,11 +60,13 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketSetOptionsTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketSetOptionsTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_waiter_add_test.cc b/starboard/nplb/socket_waiter_add_test.cc
index 1b29ae7..f1d60cc 100644
--- a/starboard/nplb/socket_waiter_add_test.cc
+++ b/starboard/nplb/socket_waiter_add_test.cc
@@ -210,11 +210,13 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketWaiterAddTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketWaiterAddTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_waiter_remove_test.cc b/starboard/nplb/socket_waiter_remove_test.cc
index 7ef883b..12c3627 100644
--- a/starboard/nplb/socket_waiter_remove_test.cc
+++ b/starboard/nplb/socket_waiter_remove_test.cc
@@ -82,11 +82,13 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketWaiterRemoveTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketWaiterRemoveTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_waiter_wait_test.cc b/starboard/nplb/socket_waiter_wait_test.cc
index f49b8ad..53743e2 100644
--- a/starboard/nplb/socket_waiter_wait_test.cc
+++ b/starboard/nplb/socket_waiter_wait_test.cc
@@ -259,23 +259,27 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketWaiterWaitTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketWaiterWaitTest,
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketWaiterWaitTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketWaiterWaitTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_waiter_wait_timed_test.cc b/starboard/nplb/socket_waiter_wait_timed_test.cc
index a35a17b..7541bef 100644
--- a/starboard/nplb/socket_waiter_wait_timed_test.cc
+++ b/starboard/nplb/socket_waiter_wait_timed_test.cc
@@ -129,23 +129,27 @@
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketWaiterWaitTimedTest,
::testing::Values(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv6));
+ kSbSocketAddressTypeIpv6),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketWaiterWaitTimedTest,
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
SbSocketWaiterWaitTimedTest,
- ::testing::Values(kSbSocketAddressTypeIpv4));
+ ::testing::Values(kSbSocketAddressTypeIpv4),
+ GetSbSocketAddressTypeName);
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketWaiterWaitTimedTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#endif
} // namespace
diff --git a/starboard/nplb/socket_wrapper_test.cc b/starboard/nplb/socket_wrapper_test.cc
index bff5189..9d2c442 100644
--- a/starboard/nplb/socket_wrapper_test.cc
+++ b/starboard/nplb/socket_wrapper_test.cc
@@ -89,13 +89,15 @@
::testing::Values(
std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
- std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#else
INSTANTIATE_TEST_CASE_P(
SbSocketAddressTypes,
PairSbSocketWrapperTest,
::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
- kSbSocketAddressTypeIpv4)));
+ kSbSocketAddressTypeIpv4)),
+ GetSbSocketAddressTypePairName);
#endif
} // namespace
diff --git a/starboard/shared/ffmpeg/BUILD.gn b/starboard/shared/ffmpeg/BUILD.gn
index 21316bb..2482113 100644
--- a/starboard/shared/ffmpeg/BUILD.gn
+++ b/starboard/shared/ffmpeg/BUILD.gn
@@ -12,15 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-declare_args() {
- # Whether or not to enable the ffmpeg_demuxer_test target. The target will
- # only work if ffmpeg 58.35.100 is installed on the build machine.
- #
- # TODO(b/239961799): Rework the test to run regardless of the installed
- # ffmpeg version.
- enable_ffmpeg_demuxer_test = false
-}
-
ffmpeg_specialization_sources = [
"ffmpeg_audio_decoder_impl.cc",
"ffmpeg_audio_decoder_impl.h",
@@ -106,29 +97,17 @@
public_configs = [ "//starboard/build/config:starboard_implementation" ]
}
-if (enable_ffmpeg_demuxer_test) {
- target(gtest_target_type, "ffmpeg_demuxer_test") {
- testonly = true
- configs += [ "//starboard/build/config:starboard_implementation" ]
- sources = ffmpeg_specialization_sources + [
- "ffmpeg_demuxer.h",
- "ffmpeg_demuxer.cc",
- "ffmpeg_demuxer_test.cc",
- ]
-
- # Build only against one specified version of the ffmpeg includes. That means
- # that this binary will only work well when run on a machine with the given
- # version of ffmpeg installed. This test binary actually should have
- # specializations for all supported ffmpeg versions, or it should only test
- # the behavior of the abstraction layer without testing implementation
- # details.
- include_dirs = [ "//third_party/ffmpeg_includes/ffmpeg.58.35.100" ]
- deps = [
- "//cobalt/test:run_all_unittests",
- "//starboard",
- "//starboard/common",
- "//testing/gmock",
- "//testing/gtest",
- ]
- }
+target(gtest_target_type, "ffmpeg_demuxer_test") {
+ testonly = true
+ sources = [ "ffmpeg_demuxer_test.cc" ]
+ configs += [ "//starboard/build/config:starboard_implementation" ]
+ deps = [
+ ":ffmpeg_dispatch_sources",
+ "//cobalt/test:run_all_unittests",
+ "//starboard",
+ "//starboard/common",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+ data_deps = [ "//third_party/chromium/media/test/data:media_testdata" ]
}
diff --git a/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.cc b/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.cc
index b95d777..0e6cf9a 100644
--- a/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.cc
@@ -400,10 +400,17 @@
}
#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
-void FFmpegDemuxerImpl<FFMPEG>::ScopedPtrAVFree::operator()(void* ptr) const {
+void FFmpegDemuxerImpl<FFMPEG>::ScopedPtrAVFreeAVIOContext::operator()(
+ void* ptr) const {
if (!ptr) {
return;
}
+ // From the documentation of avio_alloc_context, AVIOContext's buffer must be
+ // freed separately from the AVIOContext.
+ unsigned char* buffer = static_cast<AVIOContext*>(ptr)->buffer;
+ if (buffer) {
+ GetDispatch()->av_free(buffer);
+ }
GetDispatch()->av_free(ptr);
}
diff --git a/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.h b/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.h
index cc609c5..cbe4b09 100644
--- a/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.h
+++ b/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.h
@@ -69,7 +69,7 @@
};
#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
- struct ScopedPtrAVFree {
+ struct ScopedPtrAVFreeAVIOContext {
void operator()(void* ptr) const;
};
@@ -125,7 +125,7 @@
int64_t timeline_offset_us_ = 0L;
// FFmpeg-related structs.
- std::unique_ptr<AVIOContext, ScopedPtrAVFree> avio_context_;
+ std::unique_ptr<AVIOContext, ScopedPtrAVFreeAVIOContext> avio_context_;
AVFormatContext* format_context_ = nullptr;
};
diff --git a/starboard/shared/ffmpeg/ffmpeg_demuxer_test.cc b/starboard/shared/ffmpeg/ffmpeg_demuxer_test.cc
index 927adcb..49b7f5d 100644
--- a/starboard/shared/ffmpeg/ffmpeg_demuxer_test.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_demuxer_test.cc
@@ -16,12 +16,14 @@
#include <cstdint>
#include <cstdlib>
+#include <fstream>
#include <memory>
-#include <tuple>
+#include <string>
#include <vector>
-#include "starboard/shared/ffmpeg/ffmpeg_common.h"
-#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+#include "starboard/configuration_constants.h"
+#include "starboard/log.h"
+#include "starboard/system.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,70 +33,32 @@
namespace {
using ::testing::_;
-using ::testing::AllArgs;
+using ::testing::AtLeast;
using ::testing::ElementsAreArray;
-using ::testing::Eq;
using ::testing::ExplainMatchResult;
+using ::testing::Ge;
using ::testing::Invoke;
using ::testing::IsNull;
using ::testing::MockFunction;
using ::testing::NotNull;
-using ::testing::Pointee;
-using ::testing::Pointwise;
-using ::testing::Return;
-using ::testing::SaveArg;
-using ::testing::WithArg;
-
-// Compares two CobaltExtensionDemuxerSideData structs. Deep equality is
-// checked; in other words, the actual side data is inspected (not just the ptr
-// addresses).
-MATCHER_P(SideDataEq, expected, "") {
- return arg.type == expected.type && arg.data_size == expected.data_size &&
- ExplainMatchResult(
- ElementsAreArray(expected.data,
- static_cast<size_t>(expected.data_size)),
- std::tuple<uint8_t*, size_t>{arg.data, arg.data_size},
- result_listener);
-}
-
-// Compares two CobaltExtensionDemuxerBuffers. Deep equality is checked; the
-// side data and data are checked element-by-element, rather than simply
-// checking ptr addresses.
-MATCHER_P(BufferMatches, expected_buffer, "") {
- if (expected_buffer.end_of_stream) {
- // For EoS buffers, we don't care about the other values.
- return arg.end_of_stream;
- }
-
- if (arg.data_size != expected_buffer.data_size) {
- return false;
- }
- if (!ExplainMatchResult(
- ElementsAreArray(expected_buffer.data,
- static_cast<size_t>(expected_buffer.data_size)),
- std::tuple<uint8_t*, size_t>{arg.data, arg.data_size},
- result_listener)) {
- return false;
- }
- if (arg.side_data_elements != expected_buffer.side_data_elements) {
- return false;
- }
- // Note: ::testing::Pointwise doesn't support pointer/count as a
- // representation of an array, so we manually check each side data element.
- for (int i = 0; i < expected_buffer.side_data_elements; ++i) {
- if (!ExplainMatchResult(SideDataEq(expected_buffer.side_data[i]),
- arg.side_data[i], result_listener)) {
- return false;
- }
- }
- return (arg.pts == expected_buffer.pts &&
- arg.duration == expected_buffer.duration &&
- arg.is_keyframe == expected_buffer.is_keyframe &&
- arg.end_of_stream == expected_buffer.end_of_stream);
-}
+using ::testing::SizeIs;
// Streaming is not supported.
constexpr bool kIsStreaming = false;
+constexpr char kInputWebm[] = "bear-320x240.webm";
+
+std::string ResolveTestFilename(const char* filename) {
+ std::vector<char> content_path(kSbFileMaxPath);
+ SB_CHECK(SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
+ kSbFileMaxPath));
+ const std::string directory_path =
+ std::string(content_path.data()) + kSbFileSepChar + "third_party" +
+ kSbFileSepChar + "chromium" + kSbFileSepChar + "media" + kSbFileSepChar +
+ "test" + kSbFileSepChar + "data";
+ SB_CHECK(SbDirectoryCanOpen(directory_path.c_str()))
+ << "Cannot open directory " << directory_path;
+ return directory_path + kSbFileSepChar + filename;
+}
// Used to convert a MockFn to a pure C function.
template <typename T, typename U>
@@ -102,179 +66,6 @@
static_cast<T*>(user_data)->AsStdFunction()(u);
}
-// A mock class for receiving FFmpeg calls. The API mimics the relevant parts of
-// the real FFmpeg API.
-class MockFFmpegImpl {
- public:
- MOCK_METHOD1(AVCodecFreeContext, void(AVCodecContext** avctx));
- MOCK_METHOD1(AVFree, void(void* ptr));
- MOCK_METHOD4(AVRescaleRnd, int64_t(int64_t a, int64_t b, int64_t c, int rnd));
- MOCK_METHOD4(AVDictGet,
- AVDictionaryEntry*(const AVDictionary* m,
- const char* key,
- const AVDictionaryEntry* prev,
- int flags));
- MOCK_METHOD4(AVFormatOpenInput,
- int(AVFormatContext** ps,
- const char* filename,
- AVInputFormat* fmt,
- AVDictionary** options));
- MOCK_METHOD1(AVFormatCloseInput, void(AVFormatContext** s));
- MOCK_METHOD7(
- AVIOAllocContext,
- AVIOContext*(
- unsigned char* buffer,
- int buffer_size,
- int write_flag,
- void* opaque,
- int (*read_packet)(void* opaque, uint8_t* buf, int buf_size),
- int (*write_packet)(void* opaque, uint8_t* buf, int buf_size),
- int64_t (*seek)(void* opaque, int64_t offset, int whence)));
- MOCK_METHOD1(AVMalloc, void*(size_t size));
- MOCK_METHOD0(AVFormatAllocContext, AVFormatContext*());
- MOCK_METHOD2(AVFormatFindStreamInfo,
- int(AVFormatContext* ic, AVDictionary** options));
- MOCK_METHOD4(
- AVSeekFrame,
- int(AVFormatContext* s, int stream_index, int64_t timestamp, int flags));
- MOCK_METHOD0(AVPacketAlloc, AVPacket*());
- MOCK_METHOD1(AVPacketFree, void(AVPacket** pkt));
- MOCK_METHOD1(AVPacketUnref, void(AVPacket* pkt));
- MOCK_METHOD2(AVReadFrame, int(AVFormatContext* s, AVPacket* pkt));
- MOCK_METHOD1(AVCodecAllocContext3, AVCodecContext*(const AVCodec* codec));
- MOCK_METHOD2(AVCodecParametersToContext,
- int(AVCodecContext* codec, const AVCodecParameters* par));
-};
-
-// Returns a MockFFmpegImpl instance. It should not be deleted by the caller.
-MockFFmpegImpl* GetMockFFmpegImpl() {
- static auto* const ffmpeg_wrapper = []() {
- auto* wrapper = new MockFFmpegImpl;
- // This mock won't be destructed.
- testing::Mock::AllowLeak(wrapper);
- return wrapper;
- }();
- return ffmpeg_wrapper;
-}
-
-// Pure C functions that call the static mock.
-void mock_avcodec_free_context(AVCodecContext** avctx) {
- GetMockFFmpegImpl()->AVCodecFreeContext(avctx);
-}
-
-void mock_av_free(void* ptr) {
- GetMockFFmpegImpl()->AVFree(ptr);
-}
-
-int64_t mock_av_rescale_rnd(int64_t a, int64_t b, int64_t c, int rnd) {
- return GetMockFFmpegImpl()->AVRescaleRnd(a, b, c, rnd);
-}
-
-AVDictionaryEntry* mock_av_dict_get(const AVDictionary* m,
- const char* key,
- const AVDictionaryEntry* prev,
- int flags) {
- return GetMockFFmpegImpl()->AVDictGet(m, key, prev, flags);
-}
-
-int mock_avformat_open_input(AVFormatContext** ps,
- const char* filename,
- AVInputFormat* fmt,
- AVDictionary** options) {
- return GetMockFFmpegImpl()->AVFormatOpenInput(ps, filename, fmt, options);
-}
-
-void mock_avformat_close_input(AVFormatContext** s) {
- GetMockFFmpegImpl()->AVFormatCloseInput(s);
-}
-
-AVIOContext* mock_avio_alloc_context(
- unsigned char* buffer,
- int buffer_size,
- int write_flag,
- void* opaque,
- int (*read_packet)(void* opaque, uint8_t* buf, int buf_size),
- int (*write_packet)(void* opaque, uint8_t* buf, int buf_size),
- int64_t (*seek)(void* opaque, int64_t offset, int whence)) {
- return GetMockFFmpegImpl()->AVIOAllocContext(
- buffer, buffer_size, write_flag, opaque, read_packet, write_packet, seek);
-}
-
-void* mock_av_malloc(size_t size) {
- return GetMockFFmpegImpl()->AVMalloc(size);
-}
-
-AVFormatContext* mock_avformat_alloc_context() {
- return GetMockFFmpegImpl()->AVFormatAllocContext();
-}
-
-int mock_avformat_find_stream_info(AVFormatContext* ic,
- AVDictionary** options) {
- return GetMockFFmpegImpl()->AVFormatFindStreamInfo(ic, options);
-}
-
-int mock_av_seek_frame(AVFormatContext* s,
- int stream_index,
- int64_t timestamp,
- int flags) {
- return GetMockFFmpegImpl()->AVSeekFrame(s, stream_index, timestamp, flags);
-}
-
-AVPacket* mock_av_packet_alloc() {
- return GetMockFFmpegImpl()->AVPacketAlloc();
-}
-
-void mock_av_packet_free(AVPacket** pkt) {
- GetMockFFmpegImpl()->AVPacketFree(pkt);
-}
-
-void mock_av_packet_unref(AVPacket* pkt) {
- GetMockFFmpegImpl()->AVPacketUnref(pkt);
-}
-
-int mock_av_read_frame(AVFormatContext* s, AVPacket* pkt) {
- return GetMockFFmpegImpl()->AVReadFrame(s, pkt);
-}
-
-AVCodecContext* mock_avcodec_alloc_context3(const AVCodec* codec) {
- return GetMockFFmpegImpl()->AVCodecAllocContext3(codec);
-}
-
-int mock_avcodec_parameters_to_context(AVCodecContext* codec,
- const AVCodecParameters* par) {
- return GetMockFFmpegImpl()->AVCodecParametersToContext(codec, par);
-}
-
-// Returns an FFMPEGDispatch instance that forwards calls to the mock stored in
-// GetMockFFmpegImpl() above. The returned FFMPEGDispatch should not be
-// deleted; it has static storage duration.
-FFMPEGDispatch* GetFFMPEGDispatch() {
- static auto* const ffmpeg_dispatch = []() -> FFMPEGDispatch* {
- auto* dispatch = new FFMPEGDispatch;
- dispatch->avcodec_free_context = &mock_avcodec_free_context;
- dispatch->av_free = &mock_av_free;
- dispatch->av_rescale_rnd = &mock_av_rescale_rnd;
- dispatch->av_dict_get = &mock_av_dict_get;
- dispatch->avformat_open_input = &mock_avformat_open_input;
- dispatch->avformat_close_input = &mock_avformat_close_input;
- dispatch->avio_alloc_context = &mock_avio_alloc_context;
- dispatch->av_malloc = &mock_av_malloc;
- dispatch->avformat_alloc_context = &mock_avformat_alloc_context;
- dispatch->avformat_find_stream_info = &mock_avformat_find_stream_info;
- dispatch->av_seek_frame = &mock_av_seek_frame;
- dispatch->av_packet_alloc = &mock_av_packet_alloc;
- dispatch->av_packet_free = &mock_av_packet_free;
- dispatch->av_packet_unref = &mock_av_packet_unref;
- dispatch->av_read_frame = &mock_av_read_frame;
- dispatch->avcodec_alloc_context3 = &mock_avcodec_alloc_context3;
- dispatch->avcodec_parameters_to_context =
- &mock_avcodec_parameters_to_context;
- return dispatch;
- }();
-
- return ffmpeg_dispatch;
-}
-
// A mock class representing a data source passed to the cobalt extension
// demuxer.
class MockDataSource {
@@ -285,104 +76,44 @@
MOCK_METHOD0(GetSize, int64_t());
};
-// These functions forward calls to a MockDataSource.
-int MockBlockingRead(uint8_t* data, int bytes_requested, void* user_data) {
- return static_cast<MockDataSource*>(user_data)->BlockingRead(data,
- bytes_requested);
+// These functions forward calls to a FstreamDataSource.
+int FstreamBlockingRead(uint8_t* data, int bytes_requested, void* user_data) {
+ auto* input = static_cast<std::ifstream*>(user_data);
+ input->read(reinterpret_cast<char*>(data), bytes_requested);
+ return input->gcount();
}
-void MockSeekTo(int position, void* user_data) {
- static_cast<MockDataSource*>(user_data)->SeekTo(position);
+void FstreamSeekTo(int position, void* user_data) {
+ auto* input = static_cast<std::ifstream*>(user_data);
+ input->seekg(position, input->beg);
}
-int64_t MockGetPosition(void* user_data) {
- return static_cast<MockDataSource*>(user_data)->GetPosition();
+int64_t FstreamGetPosition(void* user_data) {
+ return static_cast<std::ifstream*>(user_data)->tellg();
}
-int64_t MockGetSize(void* user_data) {
- return static_cast<MockDataSource*>(user_data)->GetSize();
+int64_t FstreamGetSize(void* user_data) {
+ // Seek to the end of the file to get the size, then seek back to the current
+ // position.
+ auto* input = static_cast<std::ifstream*>(user_data);
+ const auto current_pos = input->tellg();
+ input->seekg(0, input->end);
+ const int64_t filesize = input->tellg();
+ input->seekg(current_pos);
+ return filesize;
}
-// A test fixture is used to ensure that the (static) mock is checked and reset
-// between tests.
-class FFmpegDemuxerTest : public ::testing::Test {
- public:
- FFmpegDemuxerTest() {
- FFmpegDemuxer::TestOnlySetFFmpegDispatch(GetFFMPEGDispatch());
- }
-
- ~FFmpegDemuxerTest() override {
- testing::Mock::VerifyAndClearExpectations(GetMockFFmpegImpl());
- }
-};
-
-TEST_F(FFmpegDemuxerTest, InitializeAllocatesContextAndOpensInput) {
- auto* const mock_ffmpeg_wrapper = GetMockFFmpegImpl();
-
- AVFormatContext format_context = {};
- AVInputFormat iformat = {};
- iformat.name = "mp4";
- format_context.iformat = &iformat;
-
- std::vector<AVStream> streams = {AVStream{}};
- std::vector<AVStream*> stream_ptrs = {&streams[0]};
- std::vector<AVCodecParameters> stream_params = {AVCodecParameters{}};
- stream_params[0].codec_type = AVMEDIA_TYPE_AUDIO;
- stream_params[0].codec_id = AV_CODEC_ID_AAC;
-
- AVIOContext avio_context = {};
- AVCodecContext codec_context = {};
-
- // Sanity checks; if any of these fail, the test has a bug.
- SB_CHECK(streams.size() == stream_ptrs.size());
- SB_CHECK(streams.size() == stream_params.size());
-
- for (int i = 0; i < streams.size(); ++i) {
- streams[i].codecpar = &stream_params[i];
- streams[i].time_base.num = 1;
- streams[i].time_base.den = 1000000;
- streams[i].start_time = 0;
- }
-
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVFormatAllocContext())
- .WillOnce(Return(&format_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVFormatCloseInput(Pointee(Eq(&format_context))))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVMalloc(_)).Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVIOAllocContext(_, _, _, _, _, _, _))
- .WillOnce(Return(&avio_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVFormatOpenInput(Pointee(Eq(&format_context)), _, _, _))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVFormatFindStreamInfo(&format_context, _))
- .WillOnce(WithArg<0>(Invoke([&stream_ptrs](AVFormatContext* context) {
- context->nb_streams = stream_ptrs.size();
- context->streams = stream_ptrs.data();
- context->duration = 120 * AV_TIME_BASE;
- return 0;
- })));
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVCodecAllocContext3(_))
- .WillOnce(Return(&codec_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecFreeContext(Pointee(Eq(&codec_context))))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecParametersToContext(&codec_context, streams[0].codecpar))
- .WillOnce(WithArg<0>(Invoke([](AVCodecContext* context) {
- context->codec_id = AV_CODEC_ID_AAC;
- context->sample_fmt = AV_SAMPLE_FMT_FLT;
- context->channel_layout = AV_CH_LAYOUT_STEREO;
- context->channels = 2;
- context->sample_rate = 44100;
- return 0;
- })));
+TEST(FFmpegDemuxerTest, InitializeSucceeds) {
+ const std::string filename = ResolveTestFilename(kInputWebm);
+ std::ifstream input(filename, std::ios_base::binary);
+ SB_CHECK(input.good()) << "Unable to load file " << filename;
const CobaltExtensionDemuxerApi* api = GetFFmpegDemuxerApi();
+ ASSERT_THAT(api, NotNull());
MockDataSource data_source;
CobaltExtensionDemuxerDataSource c_data_source{
- &MockBlockingRead, &MockSeekTo, &MockGetPosition,
- &MockGetSize, kIsStreaming, &data_source};
+ &FstreamBlockingRead, &FstreamSeekTo, &FstreamGetPosition,
+ &FstreamGetSize, kIsStreaming, &input};
std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs;
std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs;
@@ -391,114 +122,23 @@
supported_audio_codecs.size(), supported_video_codecs.data(),
supported_video_codecs.size());
- ASSERT_THAT(api, NotNull());
+ ASSERT_THAT(demuxer, NotNull());
EXPECT_EQ(demuxer->Initialize(demuxer->user_data), kCobaltExtensionDemuxerOk);
api->DestroyDemuxer(demuxer);
}
-TEST_F(FFmpegDemuxerTest, ReadsDataFromDataSource) {
- auto* const mock_ffmpeg_wrapper = GetMockFFmpegImpl();
- constexpr size_t kReadSize = 5;
-
- AVFormatContext format_context = {};
- AVInputFormat iformat = {};
- iformat.name = "mp4";
- format_context.iformat = &iformat;
-
- std::vector<AVStream> streams = {AVStream{}};
- std::vector<AVStream*> stream_ptrs = {&streams[0]};
- std::vector<AVCodecParameters> stream_params = {AVCodecParameters{}};
- stream_params[0].codec_type = AVMEDIA_TYPE_AUDIO;
- stream_params[0].codec_id = AV_CODEC_ID_AAC;
-
- AVIOContext avio_context = {};
- AVCodecContext codec_context = {};
-
- // Sanity checks; if any of these fail, the test has a bug.
- SB_CHECK(streams.size() == stream_ptrs.size());
- SB_CHECK(streams.size() == stream_params.size());
-
- for (int i = 0; i < streams.size(); ++i) {
- streams[i].codecpar = &stream_params[i];
- streams[i].time_base.num = 1;
- streams[i].time_base.den = 1000000;
- streams[i].start_time = 0;
- streams[i].index = i;
- }
-
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVFormatAllocContext())
- .WillOnce(Return(&format_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVFormatCloseInput(Pointee(Eq(&format_context))))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVMalloc(_)).Times(1);
-
- // We will capture the AVIO read operation passed to FFmpeg, so that we can
- // simulate FFmpeg reading data from the data source.
- int (*read_packet)(void*, uint8_t*, int) = nullptr;
- // Data blob that will be passed to read_packet.
- void* opaque_read_packet = nullptr;
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVIOAllocContext(_, _, _, _, _, _, _))
- .WillOnce(DoAll(SaveArg<3>(&opaque_read_packet), SaveArg<4>(&read_packet),
- Return(&avio_context)));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVFormatOpenInput(Pointee(Eq(&format_context)), _, _, _))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVFormatFindStreamInfo(&format_context, _))
- .WillOnce(WithArg<0>(Invoke([&stream_ptrs](AVFormatContext* context) {
- context->nb_streams = stream_ptrs.size();
- context->streams = stream_ptrs.data();
- context->duration = 120 * AV_TIME_BASE;
- return 0;
- })));
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVCodecAllocContext3(_))
- .WillOnce(Return(&codec_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecFreeContext(Pointee(Eq(&codec_context))))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecParametersToContext(&codec_context, streams[0].codecpar))
- .WillOnce(WithArg<0>(Invoke([](AVCodecContext* context) {
- context->codec_id = AV_CODEC_ID_AAC;
- context->sample_fmt = AV_SAMPLE_FMT_FLT;
- context->channel_layout = AV_CH_LAYOUT_STEREO;
- context->channels = 2;
- context->sample_rate = 44100;
- return 0;
- })));
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVReadFrame(&format_context, _))
- .WillOnce(WithArg<1>(Invoke([&opaque_read_packet, &read_packet,
- kReadSize](AVPacket* packet) {
- SB_CHECK(read_packet != nullptr)
- << "FFmpeg's read operation should be set via avio_alloc_context "
- "before av_read_frame is called.";
- // This will be freed when av_packet_free is called (which eventually
- // calls AVPacketFree).
- packet->data =
- static_cast<uint8_t*>(malloc(kReadSize * sizeof(uint8_t)));
- packet->size = kReadSize;
- read_packet(opaque_read_packet, packet->data, kReadSize);
- return 0;
- })));
+TEST(FFmpegDemuxerTest, ReadsAudioData) {
+ const std::string filename = ResolveTestFilename(kInputWebm);
+ std::ifstream input(filename, std::ios_base::binary);
+ SB_CHECK(input.good()) << "Unable to load file " << filename;
const CobaltExtensionDemuxerApi* api = GetFFmpegDemuxerApi();
ASSERT_THAT(api, NotNull());
-
- std::vector<uint8_t> expected_data = {0, 1, 2, 3, 4};
- SB_CHECK(expected_data.size() == kReadSize);
-
MockDataSource data_source;
- EXPECT_CALL(data_source, BlockingRead(_, kReadSize))
- .WillOnce(WithArg<0>(Invoke([expected_data](uint8_t* buffer) {
- for (int i = 0; i < expected_data.size(); ++i) {
- buffer[i] = expected_data[i];
- }
- return kReadSize;
- })));
CobaltExtensionDemuxerDataSource c_data_source{
- &MockBlockingRead, &MockSeekTo, &MockGetPosition,
- &MockGetSize, kIsStreaming, &data_source};
+ &FstreamBlockingRead, &FstreamSeekTo, &FstreamGetPosition,
+ &FstreamGetSize, kIsStreaming, &input};
std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs;
std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs;
@@ -507,103 +147,56 @@
supported_audio_codecs.size(), supported_video_codecs.data(),
supported_video_codecs.size());
+ ASSERT_THAT(demuxer, NotNull());
EXPECT_EQ(demuxer->Initialize(demuxer->user_data), kCobaltExtensionDemuxerOk);
- const CobaltExtensionDemuxerBuffer expected_buffer = {
- expected_data.data(),
- static_cast<int64_t>(expected_data.size()),
- nullptr,
- 0,
- 0,
- 0,
- false,
- false};
+ const std::vector<uint8_t> expected_audio = {
+ 0xbc, 0xd6, 0xf2, 0xda, 0x7b, 0xad, 0xe5, 0xb5,
+ 0x87, 0x29, 0x05, 0x50, 0x6b, 0xfc, 0x6f, 0xd6,
+ };
MockFunction<void(CobaltExtensionDemuxerBuffer*)> read_cb;
- AVPacket av_packet = {};
+ std::vector<uint8_t> actual_audio;
+ bool read_eos = false;
- // This is the main check: we ensure that the expected buffer is passed to us
- // via the read callback.
- EXPECT_CALL(read_cb, Call(Pointee(BufferMatches(expected_buffer)))).Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVPacketAlloc())
- .WillOnce(Return(&av_packet));
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVPacketFree(Pointee(Eq(&av_packet))))
- .WillOnce(Invoke([](AVPacket** av_packet) { free((*av_packet)->data); }));
+ EXPECT_CALL(read_cb, Call(_))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Invoke([&](CobaltExtensionDemuxerBuffer* buffer) {
+ SB_CHECK(buffer);
+ if (buffer->end_of_stream || buffer->data_size <= 0) {
+ read_eos = buffer->end_of_stream;
+ return;
+ }
+ actual_audio.insert(actual_audio.end(), buffer->data,
+ buffer->data + buffer->data_size);
+ }));
- demuxer->Read(kCobaltExtensionDemuxerStreamTypeAudio,
- &CallMockCB<decltype(read_cb), CobaltExtensionDemuxerBuffer>,
- &read_cb, demuxer->user_data);
+ // Calling Read may return less -- or more -- data than we need. Keep reading
+ // until we have at least as much data as the amount we're checking.
+ while (!read_eos && actual_audio.size() < expected_audio.size()) {
+ demuxer->Read(kCobaltExtensionDemuxerStreamTypeAudio,
+ &CallMockCB<decltype(read_cb), CobaltExtensionDemuxerBuffer>,
+ &read_cb, demuxer->user_data);
+ }
+
+ ASSERT_THAT(actual_audio, SizeIs(Ge(expected_audio.size())));
+ actual_audio.resize(expected_audio.size());
+ EXPECT_THAT(actual_audio, ElementsAreArray(expected_audio));
api->DestroyDemuxer(demuxer);
}
-TEST_F(FFmpegDemuxerTest, ReturnsAudioConfig) {
- auto* const mock_ffmpeg_wrapper = GetMockFFmpegImpl();
-
- AVFormatContext format_context = {};
- AVInputFormat iformat = {};
- iformat.name = "mp4";
- format_context.iformat = &iformat;
-
- std::vector<AVStream> streams = {AVStream{}};
- std::vector<AVStream*> stream_ptrs = {&streams[0]};
- std::vector<AVCodecParameters> stream_params = {AVCodecParameters{}};
- stream_params[0].codec_type = AVMEDIA_TYPE_AUDIO;
- stream_params[0].codec_id = AV_CODEC_ID_AAC;
-
- AVIOContext avio_context = {};
- AVCodecContext codec_context = {};
-
- // Sanity checks; if any of these fail, the test has a bug.
- SB_CHECK(streams.size() == stream_ptrs.size());
- SB_CHECK(streams.size() == stream_params.size());
-
- for (int i = 0; i < streams.size(); ++i) {
- streams[i].codecpar = &stream_params[i];
- streams[i].time_base.num = 1;
- streams[i].time_base.den = 1000000;
- streams[i].start_time = 0;
- }
-
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVFormatAllocContext())
- .WillOnce(Return(&format_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVFormatCloseInput(Pointee(Eq(&format_context))))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVMalloc(_)).Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVIOAllocContext(_, _, _, _, _, _, _))
- .WillOnce(Return(&avio_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVFormatOpenInput(Pointee(Eq(&format_context)), _, _, _))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVFormatFindStreamInfo(&format_context, _))
- .WillOnce(WithArg<0>(Invoke([&stream_ptrs](AVFormatContext* context) {
- context->nb_streams = stream_ptrs.size();
- context->streams = stream_ptrs.data();
- context->duration = 120 * AV_TIME_BASE;
- return 0;
- })));
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVCodecAllocContext3(_))
- .WillOnce(Return(&codec_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecFreeContext(Pointee(Eq(&codec_context))))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecParametersToContext(&codec_context, streams[0].codecpar))
- .WillOnce(WithArg<0>(Invoke([](AVCodecContext* context) {
- context->codec_id = AV_CODEC_ID_AAC;
- context->sample_fmt = AV_SAMPLE_FMT_FLT;
- context->channel_layout = AV_CH_LAYOUT_STEREO;
- context->channels = 2;
- context->sample_rate = 44100;
- return 0;
- })));
+TEST(FFmpegDemuxerTest, ReadsVideoData) {
+ const std::string filename = ResolveTestFilename(kInputWebm);
+ std::ifstream input(filename, std::ios_base::binary);
+ SB_CHECK(input.good()) << "Unable to load file " << filename;
const CobaltExtensionDemuxerApi* api = GetFFmpegDemuxerApi();
+ ASSERT_THAT(api, NotNull());
MockDataSource data_source;
CobaltExtensionDemuxerDataSource c_data_source{
- &MockBlockingRead, &MockSeekTo, &MockGetPosition,
- &MockGetSize, kIsStreaming, &data_source};
+ &FstreamBlockingRead, &FstreamSeekTo, &FstreamGetPosition,
+ &FstreamGetSize, kIsStreaming, &input};
std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs;
std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs;
@@ -612,119 +205,93 @@
supported_audio_codecs.size(), supported_video_codecs.data(),
supported_video_codecs.size());
- ASSERT_THAT(api, NotNull());
+ ASSERT_THAT(demuxer, NotNull());
EXPECT_EQ(demuxer->Initialize(demuxer->user_data), kCobaltExtensionDemuxerOk);
- CobaltExtensionDemuxerAudioDecoderConfig actual_audio_config = {};
- demuxer->GetAudioConfig(&actual_audio_config, demuxer->user_data);
+ const std::vector<uint8_t> expected_video = {
+ 0x50, 0x04, 0x01, 0x9d, 0x01, 0x2a, 0x40, 0x01,
+ 0xf0, 0x00, 0x00, 0x47, 0x08, 0x85, 0x85, 0x88,
+ };
- // These values are derived from those set via AVCodecParametersToContext.
- EXPECT_EQ(actual_audio_config.codec, kCobaltExtensionDemuxerCodecAAC);
- EXPECT_EQ(actual_audio_config.sample_format,
- kCobaltExtensionDemuxerSampleFormatF32);
- EXPECT_EQ(actual_audio_config.channel_layout,
+ MockFunction<void(CobaltExtensionDemuxerBuffer*)> read_cb;
+ std::vector<uint8_t> actual_video;
+ bool read_eos = false;
+
+ EXPECT_CALL(read_cb, Call(_))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Invoke([&](CobaltExtensionDemuxerBuffer* buffer) {
+ SB_CHECK(buffer);
+ if (buffer->end_of_stream || buffer->data_size <= 0) {
+ read_eos = buffer->end_of_stream;
+ return;
+ }
+ actual_video.insert(actual_video.end(), buffer->data,
+ buffer->data + buffer->data_size);
+ }));
+
+ // Calling Read may return less -- or more -- data than we need. Keep reading
+ // until we have at least as much data as the amount we're checking.
+ while (!read_eos && actual_video.size() < expected_video.size()) {
+ demuxer->Read(kCobaltExtensionDemuxerStreamTypeVideo,
+ &CallMockCB<decltype(read_cb), CobaltExtensionDemuxerBuffer>,
+ &read_cb, demuxer->user_data);
+ }
+
+ ASSERT_THAT(actual_video, SizeIs(Ge(expected_video.size())));
+ actual_video.resize(expected_video.size());
+ EXPECT_THAT(actual_video, ElementsAreArray(expected_video));
+
+ api->DestroyDemuxer(demuxer);
+}
+
+TEST(FFmpegDemuxerTest, PopulatesAudioConfig) {
+ const std::string filename = ResolveTestFilename(kInputWebm);
+ std::ifstream input(filename, std::ios_base::binary);
+ SB_CHECK(input.good()) << "Unable to load file " << filename;
+
+ const CobaltExtensionDemuxerApi* api = GetFFmpegDemuxerApi();
+ ASSERT_THAT(api, NotNull());
+ MockDataSource data_source;
+ CobaltExtensionDemuxerDataSource c_data_source{
+ &FstreamBlockingRead, &FstreamSeekTo, &FstreamGetPosition,
+ &FstreamGetSize, kIsStreaming, &input};
+ std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs;
+ std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs;
+
+ CobaltExtensionDemuxer* demuxer = api->CreateDemuxer(
+ &c_data_source, supported_audio_codecs.data(),
+ supported_audio_codecs.size(), supported_video_codecs.data(),
+ supported_video_codecs.size());
+
+ ASSERT_THAT(demuxer, NotNull());
+ EXPECT_EQ(demuxer->Initialize(demuxer->user_data), kCobaltExtensionDemuxerOk);
+
+ CobaltExtensionDemuxerAudioDecoderConfig actual_config = {};
+ demuxer->GetAudioConfig(&actual_config, demuxer->user_data);
+
+ EXPECT_EQ(actual_config.codec, kCobaltExtensionDemuxerCodecVorbis);
+ EXPECT_EQ(actual_config.sample_format,
+ kCobaltExtensionDemuxerSampleFormatPlanarF32);
+ EXPECT_EQ(actual_config.channel_layout,
kCobaltExtensionDemuxerChannelLayoutStereo);
- EXPECT_EQ(actual_audio_config.encryption_scheme,
+ EXPECT_EQ(actual_config.encryption_scheme,
kCobaltExtensionDemuxerEncryptionSchemeUnencrypted);
- EXPECT_EQ(actual_audio_config.samples_per_second, 44100);
- EXPECT_THAT(actual_audio_config.extra_data, IsNull());
- EXPECT_EQ(actual_audio_config.extra_data_size, 0);
+ EXPECT_EQ(actual_config.samples_per_second, 44100);
api->DestroyDemuxer(demuxer);
}
-TEST_F(FFmpegDemuxerTest, ReturnsVideoConfig) {
- auto* const mock_ffmpeg_wrapper = GetMockFFmpegImpl();
-
- AVFormatContext format_context = {};
- AVInputFormat iformat = {};
- iformat.name = "mp4";
- format_context.iformat = &iformat;
-
- // In this test we simulate both an audio stream and a video stream being
- // present.
- std::vector<AVStream> streams = {AVStream{}, AVStream{}};
- std::vector<AVStream*> stream_ptrs = {&streams[0], &streams[1]};
- std::vector<AVCodecParameters> stream_params = {AVCodecParameters{},
- AVCodecParameters{}};
- stream_params[0].codec_type = AVMEDIA_TYPE_AUDIO;
- stream_params[0].codec_id = AV_CODEC_ID_AAC;
- stream_params[1].codec_type = AVMEDIA_TYPE_VIDEO;
- stream_params[1].codec_id = AV_CODEC_ID_H264;
-
- AVIOContext avio_context = {};
- AVCodecContext codec_context_1 = {};
- AVCodecContext codec_context_2 = {};
-
- // Sanity checks; if any of these fail, the test has a bug.
- SB_CHECK(streams.size() == stream_ptrs.size());
- SB_CHECK(streams.size() == stream_params.size());
-
- for (int i = 0; i < streams.size(); ++i) {
- streams[i].codecpar = &stream_params[i];
- streams[i].time_base.num = 1;
- streams[i].time_base.den = 1000000;
- streams[i].start_time = 0;
- }
-
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVFormatAllocContext())
- .WillOnce(Return(&format_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVFormatCloseInput(Pointee(Eq(&format_context))))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVMalloc(_)).Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVIOAllocContext(_, _, _, _, _, _, _))
- .WillOnce(Return(&avio_context));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVFormatOpenInput(Pointee(Eq(&format_context)), _, _, _))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVFormatFindStreamInfo(&format_context, _))
- .WillOnce(WithArg<0>(Invoke([&stream_ptrs](AVFormatContext* context) {
- context->nb_streams = stream_ptrs.size();
- context->streams = stream_ptrs.data();
- context->duration = 120 * AV_TIME_BASE;
- return 0;
- })));
- EXPECT_CALL(*mock_ffmpeg_wrapper, AVCodecAllocContext3(_))
- .WillOnce(Return(&codec_context_1))
- .WillOnce(Return(&codec_context_2));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecFreeContext(Pointee(Eq(&codec_context_1))))
- .Times(1);
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecFreeContext(Pointee(Eq(&codec_context_2))))
- .Times(1);
-
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecParametersToContext(_, streams[0].codecpar))
- .WillOnce(WithArg<0>(Invoke([](AVCodecContext* context) {
- context->codec_id = AV_CODEC_ID_AAC;
- context->sample_fmt = AV_SAMPLE_FMT_FLT;
- context->channel_layout = AV_CH_LAYOUT_STEREO;
- context->channels = 2;
- context->sample_rate = 44100;
- return 0;
- })));
- EXPECT_CALL(*mock_ffmpeg_wrapper,
- AVCodecParametersToContext(_, streams[1].codecpar))
- .WillOnce(WithArg<0>(Invoke([](AVCodecContext* context) {
- context->codec_id = AV_CODEC_ID_H264;
- context->width = 1920;
- context->height = 1080;
- context->sample_aspect_ratio.num = 1;
- context->sample_aspect_ratio.den = 1;
- context->profile = FF_PROFILE_H264_BASELINE;
- context->pix_fmt = AV_PIX_FMT_YUVJ420P;
- context->colorspace = AVCOL_SPC_BT709;
- context->color_range = AVCOL_RANGE_MPEG;
- return 0;
- })));
+TEST(FFmpegDemuxerTest, PopulatesVideoConfig) {
+ const std::string filename = ResolveTestFilename(kInputWebm);
+ std::ifstream input(filename, std::ios_base::binary);
+ SB_CHECK(input.good()) << "Unable to load file " << filename;
const CobaltExtensionDemuxerApi* api = GetFFmpegDemuxerApi();
+ ASSERT_THAT(api, NotNull());
MockDataSource data_source;
CobaltExtensionDemuxerDataSource c_data_source{
- &MockBlockingRead, &MockSeekTo, &MockGetPosition,
- &MockGetSize, kIsStreaming, &data_source};
+ &FstreamBlockingRead, &FstreamSeekTo, &FstreamGetPosition,
+ &FstreamGetSize, kIsStreaming, &input};
std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs;
std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs;
@@ -733,26 +300,110 @@
supported_audio_codecs.size(), supported_video_codecs.data(),
supported_video_codecs.size());
- ASSERT_THAT(api, NotNull());
+ ASSERT_THAT(demuxer, NotNull());
EXPECT_EQ(demuxer->Initialize(demuxer->user_data), kCobaltExtensionDemuxerOk);
- CobaltExtensionDemuxerVideoDecoderConfig actual_video_config = {};
- demuxer->GetVideoConfig(&actual_video_config, demuxer->user_data);
+ CobaltExtensionDemuxerVideoDecoderConfig actual_config = {};
+ demuxer->GetVideoConfig(&actual_config, demuxer->user_data);
- // These values are derived from those set via AVCodecParametersToContext.
- EXPECT_EQ(actual_video_config.codec, kCobaltExtensionDemuxerCodecH264);
- EXPECT_EQ(actual_video_config.profile,
- kCobaltExtensionDemuxerH264ProfileBaseline);
- EXPECT_EQ(actual_video_config.coded_width, 1920);
- EXPECT_EQ(actual_video_config.coded_height, 1080);
- EXPECT_EQ(actual_video_config.visible_rect_x, 0);
- EXPECT_EQ(actual_video_config.visible_rect_y, 0);
- EXPECT_EQ(actual_video_config.visible_rect_width, 1920);
- EXPECT_EQ(actual_video_config.visible_rect_height, 1080);
- EXPECT_EQ(actual_video_config.natural_width, 1920);
- EXPECT_EQ(actual_video_config.natural_height, 1080);
- EXPECT_THAT(actual_video_config.extra_data, IsNull());
- EXPECT_EQ(actual_video_config.extra_data_size, 0);
+ EXPECT_EQ(actual_config.codec, kCobaltExtensionDemuxerCodecVP8);
+ EXPECT_EQ(actual_config.profile, kCobaltExtensionDemuxerVp8ProfileMin);
+ EXPECT_EQ(actual_config.color_space_primaries, 2);
+ EXPECT_EQ(actual_config.color_space_transfer, 2);
+ EXPECT_EQ(actual_config.color_space_matrix, 2);
+ EXPECT_EQ(actual_config.color_space_range_id,
+ kCobaltExtensionDemuxerColorSpaceRangeIdLimited);
+ EXPECT_EQ(actual_config.alpha_mode, kCobaltExtensionDemuxerHasAlpha);
+ EXPECT_EQ(actual_config.coded_width, 320);
+ EXPECT_EQ(actual_config.coded_height, 240);
+ EXPECT_EQ(actual_config.visible_rect_x, 0);
+ EXPECT_EQ(actual_config.visible_rect_y, 0);
+ EXPECT_EQ(actual_config.visible_rect_width, 320);
+ EXPECT_EQ(actual_config.visible_rect_height, 240);
+ EXPECT_EQ(actual_config.natural_width, 320);
+ EXPECT_EQ(actual_config.natural_height, 240);
+ EXPECT_EQ(actual_config.encryption_scheme,
+ kCobaltExtensionDemuxerEncryptionSchemeUnencrypted);
+ EXPECT_THAT(actual_config.extra_data, IsNull());
+ EXPECT_EQ(actual_config.extra_data_size, 0);
+
+ api->DestroyDemuxer(demuxer);
+}
+
+TEST(FFmpegDemuxerTest, ReturnsVideoDuration) {
+ const std::string filename = ResolveTestFilename(kInputWebm);
+ std::ifstream input(filename, std::ios_base::binary);
+ SB_CHECK(input.good()) << "Unable to load file " << filename;
+
+ const CobaltExtensionDemuxerApi* api = GetFFmpegDemuxerApi();
+ ASSERT_THAT(api, NotNull());
+ MockDataSource data_source;
+ CobaltExtensionDemuxerDataSource c_data_source{
+ &FstreamBlockingRead, &FstreamSeekTo, &FstreamGetPosition,
+ &FstreamGetSize, kIsStreaming, &input};
+ std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs;
+ std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs;
+
+ CobaltExtensionDemuxer* demuxer = api->CreateDemuxer(
+ &c_data_source, supported_audio_codecs.data(),
+ supported_audio_codecs.size(), supported_video_codecs.data(),
+ supported_video_codecs.size());
+
+ ASSERT_THAT(demuxer, NotNull());
+ EXPECT_EQ(demuxer->Initialize(demuxer->user_data), kCobaltExtensionDemuxerOk);
+ EXPECT_EQ(demuxer->GetDuration(demuxer->user_data), 2744000);
+
+ api->DestroyDemuxer(demuxer);
+}
+
+TEST(FFmpegDemuxerTest, TimelineOffsetReturnsZero) {
+ const std::string filename = ResolveTestFilename(kInputWebm);
+ std::ifstream input(filename, std::ios_base::binary);
+ SB_CHECK(input.good()) << "Unable to load file " << filename;
+
+ const CobaltExtensionDemuxerApi* api = GetFFmpegDemuxerApi();
+ ASSERT_THAT(api, NotNull());
+ MockDataSource data_source;
+ CobaltExtensionDemuxerDataSource c_data_source{
+ &FstreamBlockingRead, &FstreamSeekTo, &FstreamGetPosition,
+ &FstreamGetSize, kIsStreaming, &input};
+ std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs;
+ std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs;
+
+ CobaltExtensionDemuxer* demuxer = api->CreateDemuxer(
+ &c_data_source, supported_audio_codecs.data(),
+ supported_audio_codecs.size(), supported_video_codecs.data(),
+ supported_video_codecs.size());
+
+ ASSERT_THAT(demuxer, NotNull());
+ EXPECT_EQ(demuxer->Initialize(demuxer->user_data), kCobaltExtensionDemuxerOk);
+ EXPECT_EQ(demuxer->GetTimelineOffset(demuxer->user_data), 0);
+
+ api->DestroyDemuxer(demuxer);
+}
+
+TEST(FFmpegDemuxerTest, StartTimeReturnsZero) {
+ const std::string filename = ResolveTestFilename(kInputWebm);
+ std::ifstream input(filename, std::ios_base::binary);
+ SB_CHECK(input.good()) << "Unable to load file " << filename;
+
+ const CobaltExtensionDemuxerApi* api = GetFFmpegDemuxerApi();
+ ASSERT_THAT(api, NotNull());
+ MockDataSource data_source;
+ CobaltExtensionDemuxerDataSource c_data_source{
+ &FstreamBlockingRead, &FstreamSeekTo, &FstreamGetPosition,
+ &FstreamGetSize, kIsStreaming, &input};
+ std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs;
+ std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs;
+
+ CobaltExtensionDemuxer* demuxer = api->CreateDemuxer(
+ &c_data_source, supported_audio_codecs.data(),
+ supported_audio_codecs.size(), supported_video_codecs.data(),
+ supported_video_codecs.size());
+
+ ASSERT_THAT(demuxer, NotNull());
+ EXPECT_EQ(demuxer->Initialize(demuxer->user_data), kCobaltExtensionDemuxerOk);
+ EXPECT_EQ(demuxer->GetStartTime(demuxer->user_data), 0);
api->DestroyDemuxer(demuxer);
}
diff --git a/third_party/chromium/media/test/data/BUILD.gn b/third_party/chromium/media/test/data/BUILD.gn
new file mode 100644
index 0000000..582f118
--- /dev/null
+++ b/third_party/chromium/media/test/data/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A target containing test data to be used by Cobalt. Other files can be added
+# as needed.
+copy("media_testdata") {
+ testonly = true
+ install_content = true
+ sources = [ "bear-320x240.webm" ]
+ subdir = "third_party/chromium/media/test/data"
+ outputs = [
+ "$sb_static_contents_output_data_dir/$subdir/{{source_target_relative}}",
+ ]
+}