diff --git a/chrome/browser/chrome_content_utility_manifest_overlay.json b/chrome/browser/chrome_content_utility_manifest_overlay.json index b6937f6..d53f298 100644 --- a/chrome/browser/chrome_content_utility_manifest_overlay.json +++ b/chrome/browser/chrome_content_utility_manifest_overlay.json
@@ -10,6 +10,7 @@ "chrome::mojom::SafeArchiveAnalyzer", "chrome::mojom::ShellHandler", "chrome::mojom::ZipFileCreator", + "extensions::mojom::ExtensionUnpacker", "extensions::mojom::MediaParser", "extensions::mojom::RemovableStorageWriter", "extensions::mojom::WiFiCredentialsGetter",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 13c322c4..1c6ccd6 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -314,6 +314,8 @@ "arc/process/arc_process.h", "arc/process/arc_process_service.cc", "arc/process/arc_process_service.h", + "arc/tracing/arc_tracing_bridge.cc", + "arc/tracing/arc_tracing_bridge.h", "arc/tts/arc_tts_service.cc", "arc/tts/arc_tts_service.h", "arc/video/gpu_arc_video_service_host.cc",
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc index 0365cbb..336048c 100644 --- a/chrome/browser/chromeos/arc/arc_service_launcher.cc +++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -27,6 +27,7 @@ #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h" #include "chrome/browser/chromeos/arc/print/arc_print_service.h" #include "chrome/browser/chromeos/arc/process/arc_process_service.h" +#include "chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h" #include "chrome/browser/chromeos/arc/tts/arc_tts_service.h" #include "chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.h" #include "chrome/browser/chromeos/arc/wallpaper/arc_wallpaper_service.h" @@ -135,6 +136,8 @@ arc_service_manager_->AddService( base::MakeUnique<ArcStorageManager>(arc_bridge_service)); arc_service_manager_->AddService( + base::MakeUnique<ArcTracingBridge>(arc_bridge_service)); + arc_service_manager_->AddService( base::MakeUnique<ArcTtsService>(arc_bridge_service)); arc_service_manager_->AddService( base::MakeUnique<ArcWallpaperService>(arc_bridge_service));
diff --git a/chrome/browser/chromeos/arc/tracing/DEPS b/chrome/browser/chromeos/arc/tracing/DEPS new file mode 100644 index 0000000..e94e96a --- /dev/null +++ b/chrome/browser/chromeos/arc/tracing/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+content/browser/tracing/arc_tracing_agent.h" +]
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc new file mode 100644 index 0000000..b45a4e1 --- /dev/null +++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
@@ -0,0 +1,106 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/arc/arc_bridge_service.h" +#include "components/arc/arc_service_manager.h" +#include "content/public/browser/browser_thread.h" + +namespace arc { + +namespace { + +// The prefix of the categories to be shown on the trace selection UI. +// The space at the end of the string is intentional as the separator between +// the prefix and the real categories. +constexpr char kCategoryPrefix[] = TRACE_DISABLED_BY_DEFAULT("android "); + +} // namespace + +struct ArcTracingBridge::Category { + // The name used by Android to trigger tracing. + std::string name; + // The full name shown in the tracing UI in chrome://tracing. + std::string full_name; +}; + +ArcTracingBridge::ArcTracingBridge(ArcBridgeService* bridge_service) + : ArcService(bridge_service), weak_ptr_factory_(this) { + arc_bridge_service()->tracing()->AddObserver(this); + content::ArcTracingAgent::GetInstance()->SetDelegate(this); +} + +ArcTracingBridge::~ArcTracingBridge() { + content::ArcTracingAgent::GetInstance()->SetDelegate(nullptr); + arc_bridge_service()->tracing()->RemoveObserver(this); +} + +void ArcTracingBridge::OnInstanceReady() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + mojom::TracingInstance* tracing_instance = ARC_GET_INSTANCE_FOR_METHOD( + arc_bridge_service()->tracing(), QueryAvailableCategories); + if (!tracing_instance) + return; + tracing_instance->QueryAvailableCategories(base::Bind( + &ArcTracingBridge::OnCategoriesReady, weak_ptr_factory_.GetWeakPtr())); +} + +void ArcTracingBridge::OnCategoriesReady( + const std::vector<std::string>& categories) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + // There is no API in TraceLog to remove a category from the UI. As an + // alternative, the old category that is no longer in |categories_| will be + // ignored when calling |StartTracing|. + categories_.clear(); + for (const auto& category : categories) { + categories_.push_back({category, kCategoryPrefix + category}); + // Show the category name in the selection UI. + base::trace_event::TraceLog::GetCategoryGroupEnabled( + categories_.back().full_name.c_str()); + } +} + +void ArcTracingBridge::StartTracing( + const base::trace_event::TraceConfig& trace_config, + const StartTracingCallback& callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + mojom::TracingInstance* tracing_instance = ARC_GET_INSTANCE_FOR_METHOD( + arc_bridge_service()->tracing(), StartTracing); + if (!tracing_instance) { + // Use PostTask as the convention of TracingAgent. The caller expects + // callback to be called after this function returns. + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, false)); + return; + } + + std::vector<std::string> selected_categories; + for (const auto& category : categories_) { + if (trace_config.IsCategoryGroupEnabled(category.full_name.c_str())) + selected_categories.push_back(category.name); + } + + tracing_instance->StartTracing(selected_categories, callback); +} + +void ArcTracingBridge::StopTracing(const StopTracingCallback& callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + mojom::TracingInstance* tracing_instance = + ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->tracing(), StopTracing); + if (!tracing_instance) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, false)); + return; + } + tracing_instance->StopTracing(callback); +} + +} // namespace arc
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h new file mode 100644 index 0000000..0fd911a1 --- /dev/null +++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h
@@ -0,0 +1,58 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_BRIDGE_H_ +#define CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_BRIDGE_H_ + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/trace_event/trace_event.h" +#include "components/arc/arc_service.h" +#include "components/arc/common/tracing.mojom.h" +#include "components/arc/instance_holder.h" +#include "content/browser/tracing/arc_tracing_agent.h" + +namespace arc { + +class ArcBridgeService; + +// This class provides the interface to trigger tracing in the container. +class ArcTracingBridge + : public ArcService, + public content::ArcTracingAgent::Delegate, + public InstanceHolder<mojom::TracingInstance>::Observer { + public: + explicit ArcTracingBridge(ArcBridgeService* bridge_service); + ~ArcTracingBridge() override; + + // InstanceHolder<mojom::TracingInstance>::Observer overrides: + void OnInstanceReady() override; + + // content::ArcTracingAgent::Delegate overrides: + void StartTracing(const base::trace_event::TraceConfig& trace_config, + const StartTracingCallback& callback) override; + void StopTracing(const StopTracingCallback& callback) override; + + private: + struct Category; + + // Callback for QueryAvailableCategories. + void OnCategoriesReady(const std::vector<std::string>& categories); + + // List of available categories. + std::vector<Category> categories_; + + // NOTE: Weak pointers must be invalidated before all other member variables + // so it must be the last member. + base::WeakPtrFactory<ArcTracingBridge> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ArcTracingBridge); +}; + +} // namespace arc + +#endif // CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_BRIDGE_H_
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index c07cb583..6ba3c6d 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -895,6 +895,7 @@ "//extensions:extensions_resources", "//extensions/browser", "//extensions/browser/api:api_registration", + "//extensions/common", "//extensions/common/api", "//extensions/features", "//extensions/strings",
diff --git a/chrome/browser/extensions/zipfile_installer.cc b/chrome/browser/extensions/zipfile_installer.cc index f0da06d..d838136 100644 --- a/chrome/browser/extensions/zipfile_installer.cc +++ b/chrome/browser/extensions/zipfile_installer.cc
@@ -6,128 +6,115 @@ #include "base/files/file_util.h" #include "base/path_service.h" -#include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/grit/generated_resources.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/utility_process_host.h" -#include "extensions/common/extension_utility_messages.h" +#include "extensions/common/extension_unpacker.mojom.h" #include "ui/base/l10n/l10n_util.h" -using content::BrowserThread; -using content::UtilityProcessHost; - namespace { const char kExtensionHandlerTempDirError[] = "Could not create temporary directory for zipped extension."; +const char kExtensionHandlerFileUnzipError[] = + "Could not unzip extension for install."; } // namespace namespace extensions { -ZipFileInstaller::ZipFileInstaller(ExtensionService* extension_service) - : be_noisy_on_failure_(true), - extension_service_weak_(extension_service->AsWeakPtr()) { -} - -void ZipFileInstaller::LoadFromZipFile(const base::FilePath& path) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - zip_path_ = path; - BrowserThread::PostTask(BrowserThread::FILE, - FROM_HERE, - base::Bind(&ZipFileInstaller::PrepareTempDir, this)); -} - -void ZipFileInstaller::PrepareTempDir() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - base::FilePath temp_dir; - PathService::Get(base::DIR_TEMP, &temp_dir); - base::FilePath new_temp_dir; - if (!base::CreateTemporaryDirInDir( - temp_dir, - zip_path_.RemoveExtension().BaseName().value() + - FILE_PATH_LITERAL("_"), - &new_temp_dir)) { - OnUnzipFailed(std::string(kExtensionHandlerTempDirError)); - return; - } - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&ZipFileInstaller::StartWorkOnIOThread, this, new_temp_dir)); -} - -ZipFileInstaller::~ZipFileInstaller() { -} - // static scoped_refptr<ZipFileInstaller> ZipFileInstaller::Create( - ExtensionService* extension_service) { - DCHECK(extension_service); - return scoped_refptr<ZipFileInstaller>( - new ZipFileInstaller(extension_service)); + ExtensionService* service) { + DCHECK(service); + return make_scoped_refptr(new ZipFileInstaller(service)); } -void ZipFileInstaller::StartWorkOnIOThread(const base::FilePath& temp_dir) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - UtilityProcessHost* host = - UtilityProcessHost::Create(this, - base::ThreadTaskRunnerHandle::Get().get()); - host->SetName(l10n_util::GetStringUTF16( - IDS_UTILITY_PROCESS_ZIP_FILE_INSTALLER_NAME)); - host->SetExposedDir(temp_dir); - host->Send(new ExtensionUtilityMsg_UnzipToDir(zip_path_, temp_dir)); +void ZipFileInstaller::LoadFromZipFile(const base::FilePath& zip_file) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK(!zip_file.empty()); + + zip_file_ = zip_file; + + content::BrowserThread::PostTask( + content::BrowserThread::FILE, FROM_HERE, + base::Bind(&ZipFileInstaller::PrepareUnzipDir, this, zip_file)); } -void ZipFileInstaller::ReportSuccessOnUIThread( - const base::FilePath& unzipped_path) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (extension_service_weak_.get()) - UnpackedInstaller::Create(extension_service_weak_.get()) - ->Load(unzipped_path); +ZipFileInstaller::ZipFileInstaller(ExtensionService* service) + : be_noisy_on_failure_(true), + extension_service_weak_(service->AsWeakPtr()) {} + +ZipFileInstaller::~ZipFileInstaller() = default; + +void ZipFileInstaller::PrepareUnzipDir(const base::FilePath& zip_file) { + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); + + base::FilePath dir_temp; + base::PathService::Get(base::DIR_TEMP, &dir_temp); + + base::FilePath::StringType dir_name = + zip_file.RemoveExtension().BaseName().value() + FILE_PATH_LITERAL("_"); + + base::FilePath unzip_dir; + if (!base::CreateTemporaryDirInDir(dir_temp, dir_name, &unzip_dir)) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&ZipFileInstaller::ReportFailure, this, + std::string(kExtensionHandlerTempDirError))); + return; + } + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&ZipFileInstaller::Unzip, this, unzip_dir)); } -void ZipFileInstaller::ReportErrorOnUIThread(const std::string& error) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (extension_service_weak_.get()) { +void ZipFileInstaller::Unzip(const base::FilePath& unzip_dir) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK(!utility_process_mojo_client_); + + utility_process_mojo_client_ = base::MakeUnique< + content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>( + l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_ZIP_FILE_INSTALLER_NAME)); + utility_process_mojo_client_->set_error_callback( + base::Bind(&ZipFileInstaller::UnzipDone, this, unzip_dir, false)); + + utility_process_mojo_client_->set_exposed_directory(unzip_dir); + + utility_process_mojo_client_->Start(); + + utility_process_mojo_client_->service()->Unzip( + zip_file_, unzip_dir, + base::Bind(&ZipFileInstaller::UnzipDone, this, unzip_dir)); +} + +void ZipFileInstaller::UnzipDone(const base::FilePath& unzip_dir, + bool success) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + utility_process_mojo_client_.reset(); + + if (!success) { + ReportFailure(kExtensionHandlerFileUnzipError); + return; + } + + if (extension_service_weak_) + UnpackedInstaller::Create(extension_service_weak_.get())->Load(unzip_dir); +} + +void ZipFileInstaller::ReportFailure(const std::string& error) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (extension_service_weak_) { ExtensionErrorReporter::GetInstance()->ReportLoadError( - zip_path_, - error, - extension_service_weak_->profile(), + zip_file_, error, extension_service_weak_->profile(), be_noisy_on_failure_); } } -void ZipFileInstaller::OnUnzipSucceeded(const base::FilePath& unzipped_path) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind( - &ZipFileInstaller::ReportSuccessOnUIThread, this, unzipped_path)); -} - -void ZipFileInstaller::OnUnzipFailed(const std::string& error) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&ZipFileInstaller::ReportErrorOnUIThread, this, error)); -} - -bool ZipFileInstaller::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(ZipFileInstaller, message) - IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Succeeded, - OnUnzipSucceeded) - IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Failed, OnUnzipFailed) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - } // namespace extensions
diff --git a/chrome/browser/extensions/zipfile_installer.h b/chrome/browser/extensions/zipfile_installer.h index 39de613..608885c 100644 --- a/chrome/browser/extensions/zipfile_installer.h +++ b/chrome/browser/extensions/zipfile_installer.h
@@ -8,50 +8,57 @@ #include <memory> #include <string> -#include "base/callback.h" #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "content/public/browser/utility_process_host_client.h" +#include "content/public/browser/utility_process_mojo_client.h" class ExtensionService; -namespace IPC { -class Message; -} - namespace extensions { -// ZipFileInstaller unzips an extension that is zipped up via a utility process. -// The contents are then loaded via UnpackedInstaller. -class ZipFileInstaller : public content::UtilityProcessHostClient { - public: - static scoped_refptr<ZipFileInstaller> Create( - ExtensionService* extension_service); +namespace mojom { +class ExtensionUnpacker; +} - void LoadFromZipFile(const base::FilePath& path); +// ZipFileInstaller unzips an extension in a utility process. On success, the +// extension content is loaded by an extensions::UnpackedInstaller. The class +// lives on the UI thread. +class ZipFileInstaller : public base::RefCountedThreadSafe<ZipFileInstaller> { + public: + static scoped_refptr<ZipFileInstaller> Create(ExtensionService* service); + + void LoadFromZipFile(const base::FilePath& zip_file); void set_be_noisy_on_failure(bool value) { be_noisy_on_failure_ = value; } - // UtilityProcessHostClient - bool OnMessageReceived(const IPC::Message& message) override; - private: - explicit ZipFileInstaller(ExtensionService* extension_service); - ~ZipFileInstaller() override; + friend class base::RefCountedThreadSafe<ZipFileInstaller>; - void PrepareTempDir(); - void StartWorkOnIOThread(const base::FilePath& temp_dir); - void ReportSuccessOnUIThread(const base::FilePath& unzipped_path); - void ReportErrorOnUIThread(const std::string& error); + explicit ZipFileInstaller(ExtensionService* service); + ~ZipFileInstaller(); - void OnUnzipSucceeded(const base::FilePath& unzipped_path); - void OnUnzipFailed(const std::string& error); + // Unzip an extension into |unzip_dir| and load it with an UnpackedInstaller. + void PrepareUnzipDir(const base::FilePath& zip_file); + void Unzip(const base::FilePath& unzip_dir); + void UnzipDone(const base::FilePath& unzip_dir, bool success); + // On failure, report the |error| reason. + void ReportFailure(const std::string& error); + + // Passed to the ExtensionErrorReporter when reporting errors. bool be_noisy_on_failure_; + + // Pointer to our controlling extension service. base::WeakPtr<ExtensionService> extension_service_weak_; - base::FilePath zip_path_; + + // File containing the extension to unzip. + base::FilePath zip_file_; + + // Utility process used to perform the unzip. + std::unique_ptr<content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>> + utility_process_mojo_client_; DISALLOW_COPY_AND_ASSIGN(ZipFileInstaller); };
diff --git a/chrome/browser/memory/tab_manager.cc b/chrome/browser/memory/tab_manager.cc index 74baafa..0cccbee9 100644 --- a/chrome/browser/memory/tab_manager.cc +++ b/chrome/browser/memory/tab_manager.cc
@@ -20,6 +20,7 @@ #include "base/metrics/histogram_macros.h" #include "base/observer_list.h" #include "base/process/process.h" +#include "base/rand_util.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -80,35 +81,17 @@ const int kRecentTabDiscardIntervalSeconds = 60; #endif -// If there has been no priority adjustment in this interval, assume the -// machine was suspended and correct the timing statistics. -const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; - -// A suspended renderer is suspended for this duration. -constexpr base::TimeDelta kDurationOfRendererSuspension = - base::TimeDelta::FromSeconds(1200); - -// A resumed renderer is resumed for this duration. -constexpr base::TimeDelta kDurationOfRendererResumption = - base::TimeDelta::FromSeconds(10); - // The time during which a tab is protected from discarding after it stops being // audible. const int kAudioProtectionTimeSeconds = 60; -int FindTabStripModelById(int64_t target_web_contents_id, - TabStripModel** model) { - DCHECK(model); - for (auto* browser : *BrowserList::GetInstance()) { - TabStripModel* local_model = browser->tab_strip_model(); - for (int idx = 0; idx < local_model->count(); idx++) { - WebContents* web_contents = local_model->GetWebContentsAt(idx); - int64_t web_contents_id = TabManager::IdFromWebContents(web_contents); - if (web_contents_id == target_web_contents_id) { - *model = local_model; - return idx; - } - } +int FindWebContentsById(const TabStripModel* model, + int64_t target_web_contents_id) { + for (int idx = 0; idx < model->count(); idx++) { + WebContents* web_contents = model->GetWebContentsAt(idx); + int64_t web_contents_id = TabManager::IdFromWebContents(web_contents); + if (web_contents_id == target_web_contents_id) + return idx; } return -1; @@ -139,7 +122,7 @@ //////////////////////////////////////////////////////////////////////////////// // TabManager -constexpr base::TimeDelta TabManager::kDefaultTimeToFirstPurge; +constexpr base::TimeDelta TabManager::kDefaultMinTimeToPurge; TabManager::TabManager() : discard_count_(0), @@ -227,13 +210,12 @@ // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7PPbk/edit?usp=sharing std::string purge_and_suspend_time = variations::GetVariationParamValue( "PurgeAndSuspend", "purge-and-suspend-time"); - unsigned int time_to_first_purge_sec = 0; + unsigned int min_time_to_purge_sec = 0; if (purge_and_suspend_time.empty() || - !base::StringToUint(purge_and_suspend_time, &time_to_first_purge_sec)) - time_to_first_suspension_ = kDefaultTimeToFirstPurge; + !base::StringToUint(purge_and_suspend_time, &min_time_to_purge_sec)) + min_time_to_purge_ = kDefaultMinTimeToPurge; else - time_to_first_suspension_ = - base::TimeDelta::FromSeconds(time_to_first_purge_sec); + min_time_to_purge_ = base::TimeDelta::FromSeconds(min_time_to_purge_sec); } void TabManager::Stop() { @@ -242,6 +224,36 @@ memory_pressure_listener_.reset(); } +int TabManager::FindTabStripModelById(int64_t target_web_contents_id, + TabStripModel** model) const { + DCHECK(model); + // TODO(tasak): Move this code to a TabStripModel enumeration delegate! + if (!test_tab_strip_models_.empty()) { + for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) { + TabStripModel* local_model = + const_cast<TabStripModel*>(test_tab_strip_models_[i].first); + int idx = FindWebContentsById(local_model, target_web_contents_id); + if (idx != -1) { + *model = local_model; + return idx; + } + } + + return -1; + } + + for (auto* browser : *BrowserList::GetInstance()) { + TabStripModel* local_model = browser->tab_strip_model(); + int idx = FindWebContentsById(local_model, target_web_contents_id); + if (idx != -1) { + *model = local_model; + return idx; + } + } + + return -1; +} + TabStatsList TabManager::GetTabStats() const { TabStatsList stats_list(GetUnsortedTabStats()); @@ -452,7 +464,7 @@ } bool TabManager::CanSuspendBackgroundedRenderer(int render_process_id) const { - // A renderer can be suspended if it's not playing media. + // A renderer can be purged if it's not playing media. auto tab_stats = GetUnsortedTabStats(); for (auto& tab : tab_stats) { if (tab.child_process_host_id != render_process_id) @@ -685,8 +697,7 @@ // This function is called when |update_timer_| fires. It will adjust the clock // if needed (if it detects that the machine was asleep) and will fire the stats // updating on ChromeOS via the delegate. This function also tries to purge -// cache memory and suspend tabs which becomes and keeps backgrounded for a -// while. +// cache memory. void TabManager::UpdateTimerCallback() { // If Chrome is shutting down, do not do anything. if (g_browser_process->IsShuttingDown()) @@ -695,17 +706,6 @@ if (BrowserList::GetInstance()->empty()) return; - // Check for a discontinuity in time caused by the machine being suspended. - if (!last_adjust_time_.is_null()) { - TimeDelta suspend_time = NowTicks() - last_adjust_time_; - if (suspend_time.InSeconds() > kSuspendThresholdSeconds) { - // System was probably suspended, move the event timers forward in time so - // when they get subtracted out later, "uptime" is being counted. - start_time_ += suspend_time; - if (!last_discard_time_.is_null()) - last_discard_time_ += suspend_time; - } - } last_adjust_time_ = NowTicks(); #if defined(OS_CHROMEOS) @@ -714,38 +714,26 @@ delegate_->AdjustOomPriorities(stats_list); #endif - PurgeAndSuspendBackgroundedTabs(); + PurgeBackgroundedTabsIfNeeded(); } -TabManager::PurgeAndSuspendState TabManager::GetNextPurgeAndSuspendState( - content::WebContents* content, - base::TimeTicks current_time, - const base::TimeDelta& time_to_first_suspension) const { - DCHECK(content); - PurgeAndSuspendState state = - GetWebContentsData(content)->GetPurgeAndSuspendState(); - - auto time_passed = current_time - - GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime(); - switch (state) { - case RUNNING: - if (time_passed > time_to_first_suspension) - return SUSPENDED; - break; - case RESUMED: - if (time_passed > kDurationOfRendererResumption) - return SUSPENDED; - break; - case SUSPENDED: - if (time_passed > kDurationOfRendererSuspension) - return RESUMED; - break; - } - return state; +base::TimeDelta TabManager::GetTimeToPurge( + base::TimeDelta min_time_to_purge) const { + return base::TimeDelta::FromSeconds( + base::RandInt(min_time_to_purge.InSeconds(), + min_time_to_purge.InSeconds() * kMinMaxTimeToPurgeRatio)); } -void TabManager::PurgeAndSuspendBackgroundedTabs() { - base::TimeTicks current_time = NowTicks(); +bool TabManager::ShouldPurgeNow(content::WebContents* content) const { + if (GetWebContentsData(content)->is_purged()) + return false; + + base::TimeDelta time_passed = + NowTicks() - GetWebContentsData(content)->LastInactiveTime(); + return time_passed > GetWebContentsData(content)->time_to_purge(); +} + +void TabManager::PurgeBackgroundedTabsIfNeeded() { auto tab_stats = GetUnsortedTabStats(); for (auto& tab : tab_stats) { if (!tab.render_process_host->IsProcessBackgrounded()) @@ -757,34 +745,16 @@ if (!content) continue; - PurgeAndSuspendState current_state = - GetWebContentsData(content)->GetPurgeAndSuspendState(); - // If the tab's purge-and-suspend state is not RUNNING, the tab should be - // backgrounded. Since tab.last_hidden is updated everytime the tab is - // hidden, we should see tab.last_hidden < last_modified_time. - DCHECK(current_state == RUNNING || - tab.last_hidden < - GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime()); - PurgeAndSuspendState next_state = GetNextPurgeAndSuspendState( - content, current_time, time_to_first_suspension_); - if (current_state == next_state) + bool purge_now = ShouldPurgeNow(content); + if (!purge_now) continue; - // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without - // timers for simplicity, so PurgeAndSuspend is called even after the - // renderer is purged and suspended once. This should be replaced with - // timers if we want necessary and sufficient signals. - GetWebContentsData(content)->SetPurgeAndSuspendState(next_state); - switch (next_state) { - case SUSPENDED: - tab.render_process_host->PurgeAndSuspend(); - break; - case RESUMED: - tab.render_process_host->Resume(); - break; - case RUNNING: - NOTREACHED(); - } + // Since |content|'s tab is kept inactive and background for more than + // time-to-purge time, its purged state changes: false => true. + GetWebContentsData(content)->set_is_purged(true); + // TODO(tasak): rename PurgeAndSuspend with a better name, e.g. + // RequestPurgeCache, because we don't suspend any renderers. + tab.render_process_host->PurgeAndSuspend(); } } @@ -824,6 +794,9 @@ GetWebContentsData(null_contents)->SetDiscardState(true); GetWebContentsData(null_contents)->IncrementDiscardCount(); + // Make the tab PURGED to avoid purging null_contents. + GetWebContentsData(null_contents)->set_is_purged(true); + // Discard the old tab's renderer. // TODO(jamescook): This breaks script connections with other tabs. // Find a different approach that doesn't do that, perhaps based on navigation @@ -885,11 +858,16 @@ int index, int reason) { GetWebContentsData(new_contents)->SetDiscardState(false); - GetWebContentsData(new_contents)->SetPurgeAndSuspendState(RUNNING); + // When ActiveTabChanged, |new_contents| purged state changes to be false. + GetWebContentsData(new_contents)->set_is_purged(false); // If |old_contents| is set, that tab has switched from being active to // inactive, so record the time of that transition. - if (old_contents) + if (old_contents) { GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks()); + // Re-setting time-to-purge every time a tab becomes inactive. + GetWebContentsData(old_contents) + ->set_time_to_purge(GetTimeToPurge(min_time_to_purge_)); + } } void TabManager::TabInsertedAt(TabStripModel* tab_strip_model, @@ -904,6 +882,9 @@ // A new background tab is similar to having a tab switch from being active to // inactive. GetWebContentsData(contents)->SetLastInactiveTime(NowTicks()); + // Re-setting time-to-purge every time a tab becomes inactive. + GetWebContentsData(contents)->set_time_to_purge( + GetTimeToPurge(min_time_to_purge_)); } bool TabManager::IsMediaTab(WebContents* contents) const {
diff --git a/chrome/browser/memory/tab_manager.h b/chrome/browser/memory/tab_manager.h index c384b7e..ecc23053 100644 --- a/chrome/browser/memory/tab_manager.h +++ b/chrome/browser/memory/tab_manager.h
@@ -147,7 +147,9 @@ // Sets/clears the auto-discardable state of the tab. void SetTabAutoDiscardableState(content::WebContents* contents, bool state); - // Returns true when a given renderer can suspend when it is backgrounded. + // Returns true when a given renderer can be purged if the specified + // renderer is eligible for purging. + // TODO(tasak): rename this to CanPurgeBackgroundedRenderer. bool CanSuspendBackgroundedRenderer(int render_process_id) const; // Returns true if |first| is considered less desirable to be killed than @@ -159,9 +161,10 @@ static int64_t IdFromWebContents(content::WebContents* web_contents); private: - FRIEND_TEST_ALL_PREFIXES(TabManagerTest, - ActivateTabResetPurgeAndSuspendState); - FRIEND_TEST_ALL_PREFIXES(TabManagerTest, NextPurgeAndSuspendState); + FRIEND_TEST_ALL_PREFIXES(TabManagerTest, PurgeBackgroundRenderer); + FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ActivateTabResetPurgeState); + FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ShouldPurgeAtDefaultTime); + FRIEND_TEST_ALL_PREFIXES(TabManagerTest, DefaultTimeToPurgeInCorrectRange); FRIEND_TEST_ALL_PREFIXES(TabManagerTest, AutoDiscardable); FRIEND_TEST_ALL_PREFIXES(TabManagerTest, CanOnlyDiscardOnce); FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ChildProcessNotifications); @@ -176,18 +179,26 @@ FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectVideoTabs); FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ReloadDiscardedTabContextMenu); FRIEND_TEST_ALL_PREFIXES(TabManagerTest, TabManagerBasics); - FRIEND_TEST_ALL_PREFIXES(TabManagerWebContentsDataTest, PurgeAndSuspendState); // The time of the first purging after a renderer is backgrounded. // The initial value was chosen because most of users activate backgrounded // tabs within 30 minutes. (c.f. Tabs.StateTransfer.Time_Inactive_Active) - static constexpr base::TimeDelta kDefaultTimeToFirstPurge = + static constexpr base::TimeDelta kDefaultMinTimeToPurge = base::TimeDelta::FromMinutes(30); + // The min/max time to purge ratio. The max time to purge is set to be + // min time to purge times this value. + const int kMinMaxTimeToPurgeRatio = 2; + // This is needed so WebContentsData can call OnDiscardedStateChange, and - // can use PurgeAndSuspendState. + // can use PurgeState. friend class WebContentsData; + // Finds TabStripModel which has a WebContents whose id is the given + // web_contents_id, and returns the WebContents index and the TabStripModel. + int FindTabStripModelById(int64_t target_web_contents_id, + TabStripModel** model) const; + // Called by WebContentsData whenever the discard state of a WebContents // changes, so that observers can be informed. void OnDiscardedStateChange(content::WebContents* contents, @@ -258,38 +269,27 @@ // that need to be run periodically (see comment in implementation). void UpdateTimerCallback(); - // Initially PurgeAndSuspendState is RUNNING. - // RUNNING => SUSPENDED - // - A tab has been backgrounded for more than purge-and-suspend-time - // seconds. - // SUSPENDED => RESUMED - // - A suspended tab is still suspended (i.e. last active time < last - // purge-and-suspend modified time), and - // - kMaxTimeRendererAllowedToBeSuspendedBeforeResume time passes since - // since the tab was suspended. - // RESUMED => SUSPENDED - // - A resumed tab is still backgrounded (i.e. last active time < last - // purge-and-suspend modified time), and - // - kSuspendedRendererLengthOfResumption time passes since the tab was - // resumed. - // SUSPENDED, RESUMED, RUNNING => RUNNING - // - When ActiveTabChaged, the newly activated tab's state will be RUNNING. - enum PurgeAndSuspendState { - RUNNING, - RESUMED, - SUSPENDED, - }; // Returns WebContents whose contents id matches the given tab_contents_id. content::WebContents* GetWebContentsById(int64_t tab_contents_id) const; - // Returns the next state of the purge and suspend. - PurgeAndSuspendState GetNextPurgeAndSuspendState( - content::WebContents* content, - base::TimeTicks current_time, - const base::TimeDelta& time_to_first_suspension) const; + // Returns a random time-to-purge whose min value is min_time_to_purge and max + // value is min_time_to_purge * kMinMaxTimeToPurgeRatio. + base::TimeDelta GetTimeToPurge(base::TimeDelta min_time_to_purge) const; - // Purges and suspends renderers in backgrounded tabs. - void PurgeAndSuspendBackgroundedTabs(); + // Returns true if the tab specified by |content| is now eligible to have + // its memory purged. + bool ShouldPurgeNow(content::WebContents* content) const; + + // Purges renderers in backgrounded tabs if the following conditions are + // satisfied: + // - the renderers are not purged yet, + // - the renderers are not playing media, + // (CanPurgeBackgroundedRenderer returns true) + // - the renderers are left inactive and background for time-to-purge. + // If renderers are purged, their internal states become 'purged'. + // The state is reset to be 'not purged' only when they are activated + // (=ActiveTabChanged is invoked). + void PurgeBackgroundedTabsIfNeeded(); // Does the actual discard by destroying the WebContents in |model| at |index| // and replacing it by an empty one. Returns the new WebContents or NULL if @@ -372,8 +372,8 @@ // backgrounded. base::TimeDelta minimum_protection_time_; - // A backgrounded renderer will be suspended when this time passes. - base::TimeDelta time_to_first_suspension_; + // A backgrounded renderer will be purged when this time passes. + base::TimeDelta min_time_to_purge_; #if defined(OS_CHROMEOS) std::unique_ptr<TabManagerDelegate> delegate_;
diff --git a/chrome/browser/memory/tab_manager_browsertest.cc b/chrome/browser/memory/tab_manager_browsertest.cc index 6f62268..a5bad6b 100644 --- a/chrome/browser/memory/tab_manager_browsertest.cc +++ b/chrome/browser/memory/tab_manager_browsertest.cc
@@ -481,6 +481,107 @@ EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0))); } +IN_PROC_BROWSER_TEST_F(TabManagerTest, PurgeBackgroundRenderer) { + TabManager* tab_manager = g_browser_process->GetTabManager(); + + base::SimpleTestTickClock test_clock_; + tab_manager->set_test_tick_clock(&test_clock_); + + // Get three tabs open. + content::WindowedNotificationObserver load1( + content::NOTIFICATION_NAV_ENTRY_COMMITTED, + content::NotificationService::AllSources()); + OpenURLParams open1(GURL(chrome::kChromeUIAboutURL), content::Referrer(), + WindowOpenDisposition::CURRENT_TAB, + ui::PAGE_TRANSITION_TYPED, false); + browser()->OpenURL(open1); + load1.Wait(); + + content::WindowedNotificationObserver load2( + content::NOTIFICATION_NAV_ENTRY_COMMITTED, + content::NotificationService::AllSources()); + OpenURLParams open2(GURL(chrome::kChromeUICreditsURL), content::Referrer(), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui::PAGE_TRANSITION_TYPED, false); + browser()->OpenURL(open2); + load2.Wait(); + + content::WindowedNotificationObserver load3( + content::NOTIFICATION_NAV_ENTRY_COMMITTED, + content::NotificationService::AllSources()); + OpenURLParams open3(GURL(chrome::kChromeUITermsURL), content::Referrer(), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui::PAGE_TRANSITION_TYPED, false); + browser()->OpenURL(open3); + load3.Wait(); + + auto* tsm = browser()->tab_strip_model(); + TabManager::WebContentsData* tab1_contents_data = + tab_manager->GetWebContentsData(tsm->GetWebContentsAt(0)); + TabManager::WebContentsData* tab2_contents_data = + tab_manager->GetWebContentsData(tsm->GetWebContentsAt(1)); + TabManager::WebContentsData* tab3_contents_data = + tab_manager->GetWebContentsData(tsm->GetWebContentsAt(2)); + + // The time-to-purge initialized at ActiveTabChanged should be in the + // right default range. + EXPECT_GE(tab1_contents_data->time_to_purge(), + base::TimeDelta::FromMinutes(30)); + EXPECT_LT(tab1_contents_data->time_to_purge(), + base::TimeDelta::FromMinutes(60)); + EXPECT_GE(tab2_contents_data->time_to_purge(), + base::TimeDelta::FromMinutes(30)); + EXPECT_LT(tab2_contents_data->time_to_purge(), + base::TimeDelta::FromMinutes(60)); + EXPECT_GE(tab3_contents_data->time_to_purge(), + base::TimeDelta::FromMinutes(30)); + EXPECT_LT(tab3_contents_data->time_to_purge(), + base::TimeDelta::FromMinutes(60)); + + // To make it easy to test, configure time-to-purge here. + base::TimeDelta time_to_purge1 = base::TimeDelta::FromMinutes(30); + base::TimeDelta time_to_purge2 = base::TimeDelta::FromMinutes(40); + tab1_contents_data->set_time_to_purge(time_to_purge1); + tab2_contents_data->set_time_to_purge(time_to_purge2); + tab3_contents_data->set_time_to_purge(time_to_purge1); + + // No tabs are not purged yet. + ASSERT_FALSE(tab1_contents_data->is_purged()); + ASSERT_FALSE(tab2_contents_data->is_purged()); + ASSERT_FALSE(tab3_contents_data->is_purged()); + + // Advance the clock for time_to_purge1. + test_clock_.Advance(time_to_purge1); + tab_manager->PurgeBackgroundedTabsIfNeeded(); + + ASSERT_FALSE(tab1_contents_data->is_purged()); + ASSERT_FALSE(tab2_contents_data->is_purged()); + ASSERT_FALSE(tab3_contents_data->is_purged()); + + // Advance the clock for 1 minutes. + test_clock_.Advance(base::TimeDelta::FromMinutes(1)); + tab_manager->PurgeBackgroundedTabsIfNeeded(); + + // Since tab1 is kept inactive and background for more than + // time_to_purge1, tab1 should be purged. + ASSERT_TRUE(tab1_contents_data->is_purged()); + ASSERT_FALSE(tab2_contents_data->is_purged()); + ASSERT_FALSE(tab3_contents_data->is_purged()); + + // Advance the clock. + test_clock_.Advance(time_to_purge2 - time_to_purge1); + tab_manager->PurgeBackgroundedTabsIfNeeded(); + + // Since tab2 is kept inactive and background for more than + // time_to_purge2, tab1 should be purged. + // Since tab3 is active, tab3 should not be purged. + ASSERT_TRUE(tab1_contents_data->is_purged()); + ASSERT_TRUE(tab2_contents_data->is_purged()); + ASSERT_FALSE(tab3_contents_data->is_purged()); + + tsm->CloseAllTabs(); +} + } // namespace memory #endif // OS_WIN || OS_MAXOSX || OS_LINUX
diff --git a/chrome/browser/memory/tab_manager_unittest.cc b/chrome/browser/memory/tab_manager_unittest.cc index c20c394..5fd7b77 100644 --- a/chrome/browser/memory/tab_manager_unittest.cc +++ b/chrome/browser/memory/tab_manager_unittest.cc
@@ -27,7 +27,9 @@ #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_profile.h" #include "components/variations/variations_associated_data.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" +#include "content/public/test/mock_render_process_host.h" #include "content/public/test/web_contents_tester.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -672,7 +674,15 @@ ASSERT_TRUE(tabstrip.empty()); } -TEST_F(TabManagerTest, NextPurgeAndSuspendState) { +TEST_F(TabManagerTest, DefaultTimeToPurgeInCorrectRange) { + TabManager tab_manager; + base::TimeDelta time_to_purge = + tab_manager.GetTimeToPurge(TabManager::kDefaultMinTimeToPurge); + EXPECT_GE(time_to_purge, base::TimeDelta::FromMinutes(30)); + EXPECT_LT(time_to_purge, base::TimeDelta::FromMinutes(60)); +} + +TEST_F(TabManagerTest, ShouldPurgeAtDefaultTime) { TabManager tab_manager; TabStripDummyDelegate delegate; TabStripModel tabstrip(&delegate, profile()); @@ -681,102 +691,67 @@ WebContents* test_contents = CreateWebContents(); tabstrip.AppendWebContents(test_contents, false); - // Use default time-to-first-purge defined in TabManager. - base::TimeDelta threshold = TabManager::kDefaultTimeToFirstPurge; base::SimpleTestTickClock test_clock; + tab_manager.set_test_tick_clock(&test_clock); + tab_manager.GetWebContentsData(test_contents)->set_is_purged(false); tab_manager.GetWebContentsData(test_contents) - ->SetPurgeAndSuspendState(TabManager::RUNNING); + ->SetLastInactiveTime(test_clock.NowTicks()); tab_manager.GetWebContentsData(test_contents) - ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks()); + ->set_time_to_purge(base::TimeDelta::FromMinutes(1)); - // Wait 30 minutes and verify that the tab is still RUNNING. - test_clock.Advance(base::TimeDelta::FromMinutes(30)); - EXPECT_EQ(TabManager::RUNNING, - tab_manager.GetNextPurgeAndSuspendState( - test_contents, test_clock.NowTicks(), threshold)); + // Wait 1 minute and verify that the tab is still not to be purged. + test_clock.Advance(base::TimeDelta::FromMinutes(1)); + EXPECT_FALSE(tab_manager.ShouldPurgeNow(test_contents)); - // Wait another second and verify that it is now SUSPENDED. + // Wait another 1 second and verify that it should be purged now . test_clock.Advance(base::TimeDelta::FromSeconds(1)); - EXPECT_EQ(TabManager::SUSPENDED, - tab_manager.GetNextPurgeAndSuspendState( - test_contents, test_clock.NowTicks(), threshold)); + EXPECT_TRUE(tab_manager.ShouldPurgeNow(test_contents)); + tab_manager.GetWebContentsData(test_contents)->set_is_purged(true); tab_manager.GetWebContentsData(test_contents) - ->SetPurgeAndSuspendState(TabManager::SUSPENDED); - tab_manager.GetWebContentsData(test_contents) - ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks()); + ->SetLastInactiveTime(test_clock.NowTicks()); - test_clock.Advance(base::TimeDelta::FromSeconds(1200)); - EXPECT_EQ(TabManager::SUSPENDED, - tab_manager.GetNextPurgeAndSuspendState( - test_contents, test_clock.NowTicks(), threshold)); - - test_clock.Advance(base::TimeDelta::FromSeconds(1)); - EXPECT_EQ(TabManager::RESUMED, - tab_manager.GetNextPurgeAndSuspendState( - test_contents, test_clock.NowTicks(), threshold)); - - tab_manager.GetWebContentsData(test_contents) - ->SetPurgeAndSuspendState(TabManager::RESUMED); - tab_manager.GetWebContentsData(test_contents) - ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks()); - - test_clock.Advance(base::TimeDelta::FromSeconds(10)); - EXPECT_EQ(TabManager::RESUMED, - tab_manager.GetNextPurgeAndSuspendState( - test_contents, test_clock.NowTicks(), threshold)); - - test_clock.Advance(base::TimeDelta::FromSeconds(1)); - EXPECT_EQ(TabManager::SUSPENDED, - tab_manager.GetNextPurgeAndSuspendState( - test_contents, test_clock.NowTicks(), threshold)); - - // Clean up the tabstrip. - tabstrip.CloseAllTabs(); - EXPECT_TRUE(tabstrip.empty()); + // Wait 1 day and verify that the tab is still be purged. + test_clock.Advance(base::TimeDelta::FromHours(24)); + EXPECT_FALSE(tab_manager.ShouldPurgeNow(test_contents)); } -TEST_F(TabManagerTest, ActivateTabResetPurgeAndSuspendState) { +TEST_F(TabManagerTest, ActivateTabResetPurgeState) { TabManager tab_manager; TabStripDummyDelegate delegate; TabStripModel tabstrip(&delegate, profile()); tabstrip.AddObserver(&tab_manager); + tab_manager.test_tab_strip_models_.push_back( + TabManager::TestTabStripModel(&tabstrip, false /* !is_app */)); + + base::SimpleTestTickClock test_clock; + tab_manager.set_test_tick_clock(&test_clock); WebContents* tab1 = CreateWebContents(); WebContents* tab2 = CreateWebContents(); tabstrip.AppendWebContents(tab1, true); tabstrip.AppendWebContents(tab2, false); - base::SimpleTestTickClock test_clock; + tab_manager.GetWebContentsData(tab2)->SetLastInactiveTime( + test_clock.NowTicks()); + static_cast<content::MockRenderProcessHost*>(tab2->GetRenderProcessHost()) + ->set_is_process_backgrounded(true); + EXPECT_TRUE(tab2->GetRenderProcessHost()->IsProcessBackgrounded()); - // Initially PurgeAndSuspend state should be RUNNING. - EXPECT_EQ(TabManager::RUNNING, - tab_manager.GetWebContentsData(tab2)->GetPurgeAndSuspendState()); + // Initially PurgeAndSuspend state should be NOT_PURGED. + EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged()); + tab_manager.GetWebContentsData(tab2)->set_time_to_purge( + base::TimeDelta::FromMinutes(1)); + test_clock.Advance(base::TimeDelta::FromMinutes(2)); + tab_manager.PurgeBackgroundedTabsIfNeeded(); + // Since tab2 is kept inactive and background for more than time-to-purge, + // tab2 should be purged. + EXPECT_TRUE(tab_manager.GetWebContentsData(tab2)->is_purged()); - tab_manager.GetWebContentsData(tab2)->SetPurgeAndSuspendState( - TabManager::SUSPENDED); - tab_manager.GetWebContentsData(tab2) - ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks()); - - // Activate tab2. Tab2's PurgeAndSuspend state should be RUNNING. + // Activate tab2. Tab2's PurgeAndSuspend state should be NOT_PURGED. tabstrip.ActivateTabAt(1, true /* user_gesture */); - EXPECT_EQ(TabManager::RUNNING, - tab_manager.GetWebContentsData(tab2)->GetPurgeAndSuspendState()); - - tab_manager.GetWebContentsData(tab1)->SetPurgeAndSuspendState( - TabManager::RESUMED); - tab_manager.GetWebContentsData(tab1) - ->SetLastPurgeAndSuspendModifiedTimeForTesting(test_clock.NowTicks()); - - // Activate tab1. Tab1's PurgeAndSuspend state should be RUNNING. - tabstrip.ActivateTabAt(0, true /* user_gesture */); - EXPECT_EQ(TabManager::RUNNING, - tab_manager.GetWebContentsData(tab1)->GetPurgeAndSuspendState()); - - // Clean up the tabstrip. - tabstrip.CloseAllTabs(); - EXPECT_TRUE(tabstrip.empty()); + EXPECT_FALSE(tab_manager.GetWebContentsData(tab2)->is_purged()); } } // namespace memory
diff --git a/chrome/browser/memory/tab_manager_web_contents_data.cc b/chrome/browser/memory/tab_manager_web_contents_data.cc index 92ebc88..b585167 100644 --- a/chrome/browser/memory/tab_manager_web_contents_data.cc +++ b/chrome/browser/memory/tab_manager_web_contents_data.cc
@@ -22,9 +22,8 @@ TabManager::WebContentsData::WebContentsData(content::WebContents* web_contents) : WebContentsObserver(web_contents), test_tick_clock_(nullptr), - last_purge_and_suspend_modified_time_( - base::TimeTicks::FromInternalValue(0)), - purge_and_suspend_state_(RUNNING) {} + time_to_purge_(base::TimeDelta::FromMinutes(30)), + is_purged_(false) {} TabManager::WebContentsData::~WebContentsData() {} @@ -202,25 +201,4 @@ return tab_data_.is_auto_discardable; } -void TabManager::WebContentsData::SetPurgeAndSuspendState( - PurgeAndSuspendState state) { - last_purge_and_suspend_modified_time_ = NowTicks(); - purge_and_suspend_state_ = state; -} - -base::TimeTicks TabManager::WebContentsData::LastPurgeAndSuspendModifiedTime() - const { - return last_purge_and_suspend_modified_time_; -} - -void TabManager::WebContentsData::SetLastPurgeAndSuspendModifiedTimeForTesting( - base::TimeTicks timestamp) { - last_purge_and_suspend_modified_time_ = timestamp; -} - -TabManager::PurgeAndSuspendState -TabManager::WebContentsData::GetPurgeAndSuspendState() const { - return purge_and_suspend_state_; -} - } // namespace memory
diff --git a/chrome/browser/memory/tab_manager_web_contents_data.h b/chrome/browser/memory/tab_manager_web_contents_data.h index 8f68c12..3e50914 100644 --- a/chrome/browser/memory/tab_manager_web_contents_data.h +++ b/chrome/browser/memory/tab_manager_web_contents_data.h
@@ -79,25 +79,23 @@ // Sets/clears the auto-discardable state of the tab. void SetAutoDiscardableState(bool state); - // Sets the current purge-and-suspend state, and update the timestamp, - // i.e. the last purge-and-suspend modified time. - // TODO(tasak): remove this after PurgeAndSuspend code is moved into + // Sets the current purge state. + // TODO(tasak): remove this after the logic is moved into // MemoryCoordinator. - void SetPurgeAndSuspendState(TabManager::PurgeAndSuspendState state); + void set_is_purged(bool state) { is_purged_ = state; } - // Returns the last purge-and-suspend modified time. - // TODO(tasak): remove this after PurgeAndSuspend code is moved into + // Returns the current state of purge. + // TODO(tasak): remove this after the logic is moved into // MemoryCoordinator. - base::TimeTicks LastPurgeAndSuspendModifiedTime() const; + bool is_purged() const { return is_purged_; } - // Sets the timestamp of the last modified time the purge-and-suspend state - // of the tab was changed for testing. - void SetLastPurgeAndSuspendModifiedTimeForTesting(base::TimeTicks timestamp); + // Sets the time to purge after the tab is backgrounded. + void set_time_to_purge(const base::TimeDelta& time_to_purge) { + time_to_purge_ = time_to_purge; + } - // Returns the current state of purge-and-suspend. - // TODO(tasak): remove this after PurgeAndSuspend code is moved into - // MemoryCoordinator. - TabManager::PurgeAndSuspendState GetPurgeAndSuspendState() const; + // Returns the time to first purge after the tab is backgrounded. + base::TimeDelta time_to_purge() const { return time_to_purge_; } private: // Needed to access tab_data_. @@ -141,11 +139,11 @@ // this test clock. Otherwise it returns the system clock's value. base::TickClock* test_tick_clock_; - // The last time purge-and-suspend state was modified. - base::TimeTicks last_purge_and_suspend_modified_time_; + // The time to purge after the tab is backgrounded. + base::TimeDelta time_to_purge_; - // The current state of purge-and-suspend. - PurgeAndSuspendState purge_and_suspend_state_; + // True if the tab has been purged. + bool is_purged_; DISALLOW_COPY_AND_ASSIGN(WebContentsData); };
diff --git a/chrome/browser/memory/tab_manager_web_contents_data_unittest.cc b/chrome/browser/memory/tab_manager_web_contents_data_unittest.cc index d98afa8..e8adeefb 100644 --- a/chrome/browser/memory/tab_manager_web_contents_data_unittest.cc +++ b/chrome/browser/memory/tab_manager_web_contents_data_unittest.cc
@@ -204,25 +204,4 @@ histograms.ExpectBucketCount(kHistogramName, 12000, 1); } -TEST_F(TabManagerWebContentsDataTest, PurgeAndSuspendState) { - EXPECT_EQ(TabManager::RUNNING, tab_data()->GetPurgeAndSuspendState()); - base::TimeTicks last_modified = tab_data()->LastPurgeAndSuspendModifiedTime(); - test_clock().Advance(base::TimeDelta::FromSeconds(5)); - tab_data()->SetPurgeAndSuspendState(TabManager::SUSPENDED); - EXPECT_EQ(TabManager::SUSPENDED, tab_data()->GetPurgeAndSuspendState()); - EXPECT_GT(tab_data()->LastPurgeAndSuspendModifiedTime(), last_modified); - - last_modified = tab_data()->LastPurgeAndSuspendModifiedTime(); - test_clock().Advance(base::TimeDelta::FromSeconds(5)); - tab_data()->SetPurgeAndSuspendState(TabManager::RESUMED); - EXPECT_EQ(TabManager::RESUMED, tab_data()->GetPurgeAndSuspendState()); - EXPECT_GT(tab_data()->LastPurgeAndSuspendModifiedTime(), last_modified); - - last_modified = tab_data()->LastPurgeAndSuspendModifiedTime(); - test_clock().Advance(base::TimeDelta::FromSeconds(5)); - tab_data()->SetPurgeAndSuspendState(TabManager::RUNNING); - EXPECT_EQ(TabManager::RUNNING, tab_data()->GetPurgeAndSuspendState()); - EXPECT_GT(tab_data()->LastPurgeAndSuspendModifiedTime(), last_modified); -} - } // namespace memory
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 7c1d1be..9719d1d3 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1775,13 +1775,13 @@ "../browser/ui/location_bar/location_bar_browsertest.cc", "../browser/ui/login/login_handler_browsertest.cc", "../browser/ui/native_window_tracker_browsertest.cc", + "../browser/ui/passwords/manage_passwords_test.cc", "../browser/ui/permission_bubble/mock_permission_prompt.cc", "../browser/ui/permission_bubble/mock_permission_prompt.h", "../browser/ui/permission_bubble/mock_permission_prompt_factory.cc", "../browser/ui/permission_bubble/mock_permission_prompt_factory.h", "../browser/ui/permission_bubble/permission_bubble_browser_test_util.cc", "../browser/ui/permission_bubble/permission_bubble_browser_test_util.h", - "../browser/ui/passwords/manage_passwords_test.cc", "../browser/ui/prefs/prefs_tab_helper_browsertest.cc", "../browser/ui/profile_error_browsertest.cc", "../browser/ui/search/new_tab_page_interceptor_browsertest.cc", @@ -3373,13 +3373,13 @@ "../browser/ui/find_bar/find_backend_unittest.cc", "../browser/ui/login/login_handler_unittest.cc", "../browser/ui/page_info/website_settings_unittest.cc", + "../browser/ui/passwords/manage_passwords_state_unittest.cc", + "../browser/ui/passwords/manage_passwords_view_utils_unittest.cc", + "../browser/ui/passwords/password_manager_presenter_unittest.cc", "../browser/ui/permission_bubble/mock_permission_prompt.cc", "../browser/ui/permission_bubble/mock_permission_prompt.h", "../browser/ui/permission_bubble/mock_permission_prompt_factory.cc", "../browser/ui/permission_bubble/mock_permission_prompt_factory.h", - "../browser/ui/passwords/manage_passwords_state_unittest.cc", - "../browser/ui/passwords/manage_passwords_view_utils_unittest.cc", - "../browser/ui/passwords/password_manager_presenter_unittest.cc", "../browser/ui/search_engines/keyword_editor_controller_unittest.cc", "../browser/ui/sync/profile_signin_confirmation_helper_unittest.cc", "../browser/ui/sync/sync_promo_ui_unittest.cc", @@ -4127,6 +4127,7 @@ deps += [ "//chrome/common/extensions/api", "//extensions:extensions_resources", + "//extensions/common", "//extensions/strings", "//media/cast:test_support",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc index 6523e80..06c9bf6 100644 --- a/chrome/utility/chrome_content_utility_client.cc +++ b/chrome/utility/chrome_content_utility_client.cc
@@ -273,6 +273,8 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) extensions::ExtensionsHandler::ExposeInterfacesToBrowser( registry, utility_process_running_elevated_); + extensions::UtilityHandler::ExposeInterfacesToBrowser( + registry, utility_process_running_elevated_); #endif // If our process runs with elevated privileges, only add elevated Mojo // services to the interface registry.
diff --git a/chrome/utility/extensions/extensions_handler.cc b/chrome/utility/extensions/extensions_handler.cc index 4cb3fa8..5c93970 100644 --- a/chrome/utility/extensions/extensions_handler.cc +++ b/chrome/utility/extensions/extensions_handler.cc
@@ -176,8 +176,7 @@ ExtensionsClient::Set(ChromeExtensionsClient::GetInstance()); } -ExtensionsHandler::~ExtensionsHandler() { -} +ExtensionsHandler::~ExtensionsHandler() = default; // static void ExtensionsHandler::PreSandboxStartup() {
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn index d32de66..6141d10a 100644 --- a/components/arc/BUILD.gn +++ b/components/arc/BUILD.gn
@@ -154,6 +154,7 @@ "common/scale_factor.mojom", "common/screen_rect.mojom", "common/storage_manager.mojom", + "common/tracing.mojom", "common/tts.mojom", "common/video.mojom", "common/video_accelerator.mojom",
diff --git a/components/arc/arc_bridge_host_impl.cc b/components/arc/arc_bridge_host_impl.cc index 437aa1a4..521f1179 100644 --- a/components/arc/arc_bridge_host_impl.cc +++ b/components/arc/arc_bridge_host_impl.cc
@@ -198,6 +198,11 @@ std::move(storage_manager_ptr)); } +void ArcBridgeHostImpl::OnTracingInstanceReady( + mojom::TracingInstancePtr tracing_ptr) { + OnInstanceReady(arc_bridge_service_->tracing(), std::move(tracing_ptr)); +} + void ArcBridgeHostImpl::OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) { OnInstanceReady(arc_bridge_service_->tts(), std::move(tts_ptr)); }
diff --git a/components/arc/arc_bridge_host_impl.h b/components/arc/arc_bridge_host_impl.h index 9ee3664..e72bbb7c 100644 --- a/components/arc/arc_bridge_host_impl.h +++ b/components/arc/arc_bridge_host_impl.h
@@ -71,6 +71,7 @@ void OnProcessInstanceReady(mojom::ProcessInstancePtr process_ptr) override; void OnStorageManagerInstanceReady( mojom::StorageManagerInstancePtr storage_manager_ptr) override; + void OnTracingInstanceReady(mojom::TracingInstancePtr trace_ptr) override; void OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) override; void OnVideoInstanceReady(mojom::VideoInstancePtr video_ptr) override; void OnWallpaperInstanceReady(
diff --git a/components/arc/arc_bridge_service.h b/components/arc/arc_bridge_service.h index 60abe9d5..a7377f95 100644 --- a/components/arc/arc_bridge_service.h +++ b/components/arc/arc_bridge_service.h
@@ -36,6 +36,7 @@ class PrintInstance; class ProcessInstance; class StorageManagerInstance; +class TracingInstance; class TtsInstance; class VideoInstance; class WallpaperInstance; @@ -89,6 +90,7 @@ InstanceHolder<mojom::StorageManagerInstance>* storage_manager() { return &storage_manager_; } + InstanceHolder<mojom::TracingInstance>* tracing() { return &tracing_; } InstanceHolder<mojom::TtsInstance>* tts() { return &tts_; } InstanceHolder<mojom::VideoInstance>* video() { return &video_; } InstanceHolder<mojom::WallpaperInstance>* wallpaper() { return &wallpaper_; } @@ -116,6 +118,7 @@ InstanceHolder<mojom::PrintInstance> print_; InstanceHolder<mojom::ProcessInstance> process_; InstanceHolder<mojom::StorageManagerInstance> storage_manager_; + InstanceHolder<mojom::TracingInstance> tracing_; InstanceHolder<mojom::TtsInstance> tts_; InstanceHolder<mojom::VideoInstance> video_; InstanceHolder<mojom::WallpaperInstance> wallpaper_;
diff --git a/components/arc/common/arc_bridge.mojom b/components/arc/common/arc_bridge.mojom index 7b8a9c52..1ad0fe37 100644 --- a/components/arc/common/arc_bridge.mojom +++ b/components/arc/common/arc_bridge.mojom
@@ -26,13 +26,14 @@ import "print.mojom"; import "process.mojom"; import "storage_manager.mojom"; +import "tracing.mojom"; import "tts.mojom"; import "video.mojom"; import "wallpaper.mojom"; -// Next MinVersion: 22 +// Next MinVersion: 23 // Deprecated method IDs: 101, 105 -// Next method ID: 128 +// Next method ID: 129 interface ArcBridgeHost { // Keep the entries alphabetical. In order to do so without breaking // compatibility with the ARC instance, explicitly assign each interface a @@ -110,6 +111,9 @@ // Notifies Chrome that the StorageManagerInstance interface is ready. [MinVersion=12] OnStorageManagerInstanceReady@118(StorageManagerInstance instance_ptr); + // Notifies Chrome that the TracingInstance interface is ready. + [MinVersion=22] OnTracingInstanceReady@128(TracingInstance instance_ptr); + // Notifies Chrome that the TtsInstance interface is ready. [MinVersion=17] OnTtsInstanceReady@123(TtsInstance instance_ptr);
diff --git a/components/arc/common/tracing.mojom b/components/arc/common/tracing.mojom new file mode 100644 index 0000000..e27d89b --- /dev/null +++ b/components/arc/common/tracing.mojom
@@ -0,0 +1,18 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Next MinVersion: 1 + +module arc.mojom; + +interface TracingInstance { + // Queries available tracing categories in the container. + QueryAvailableCategories@0() => (array<string> categories); + + // Starts tracing in the container with the given categories. + StartTracing@1(array<string> categories) => (bool success); + + // Stops tracing in the container. + StopTracing@2() => (bool success); +};
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc index aab03f3..5e082ed 100644 --- a/content/app/content_main_runner.cc +++ b/content/app/content_main_runner.cc
@@ -27,7 +27,6 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/scoped_vector.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram_base.h" #include "base/metrics/statistics_recorder.h"
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 3bd6eb0..673a49c2 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1379,6 +1379,8 @@ "streams/stream_write_observer.h", "theme_helper_mac.h", "theme_helper_mac.mm", + "tracing/arc_tracing_agent.cc", + "tracing/arc_tracing_agent.h", "tracing/background_tracing_config_impl.cc", "tracing/background_tracing_config_impl.h", "tracing/background_tracing_manager_impl.cc",
diff --git a/content/browser/loader/reload_cache_control_browsertest.cc b/content/browser/loader/reload_cache_control_browsertest.cc index 05e8481..411f7658 100644 --- a/content/browser/loader/reload_cache_control_browsertest.cc +++ b/content/browser/loader/reload_cache_control_browsertest.cc
@@ -157,6 +157,12 @@ // Test if bypassing reload issues requests with proper cache control flags. IN_PROC_BROWSER_TEST_F(ReloadCacheControlBrowserTest, BypassingReload) { + // TODO(crbug.com/671545): This test gets to be unstable if browser-side + // navigation is enabled. This is because we can not ensure which of frame and + // image requests hits the network first. + if (IsBrowserSideNavigationEnabled()) + return; + GURL url(embedded_test_server()->GetURL(kReloadTestPath)); EXPECT_TRUE(NavigateToURL(shell(), url));
diff --git a/content/browser/media/android/browser_media_player_manager.cc b/content/browser/media/android/browser_media_player_manager.cc index 1ea8bf1..c890820 100644 --- a/content/browser/media/android/browser_media_player_manager.cc +++ b/content/browser/media/android/browser_media_player_manager.cc
@@ -88,18 +88,16 @@ case MEDIA_PLAYER_TYPE_REMOTE_ONLY: case MEDIA_PLAYER_TYPE_URL: { const std::string user_agent = GetContentClient()->GetUserAgent(); - std::unique_ptr<MediaPlayerAndroid> media_player_bridge( - new MediaPlayerBridge( - media_player_params.player_id, media_player_params.url, - media_player_params.first_party_for_cookies, user_agent, - hide_url_log, this, - base::Bind(&BrowserMediaPlayerManager::OnDecoderResourcesReleased, - weak_ptr_factory_.GetWeakPtr()), - media_player_params.frame_url, - media_player_params.allow_credentials)); + auto media_player_bridge = base::MakeUnique<MediaPlayerBridge>( + media_player_params.player_id, media_player_params.url, + media_player_params.first_party_for_cookies, user_agent, hide_url_log, + this, + base::Bind(&BrowserMediaPlayerManager::OnDecoderResourcesReleased, + weak_ptr_factory_.GetWeakPtr()), + media_player_params.frame_url, media_player_params.allow_credentials); if (media_player_params.type == MEDIA_PLAYER_TYPE_REMOTE_ONLY) - return media_player_bridge; + return std::move(media_player_bridge); bool should_block = false; bool extract_metadata = @@ -122,10 +120,9 @@ OnMediaMetadataChanged(media_player_params.player_id, base::TimeDelta(), 0, 0, false); } else if (!should_block) { - static_cast<MediaPlayerBridge*>(media_player_bridge.get()) - ->Initialize(); + media_player_bridge->Initialize(); } - return media_player_bridge; + return std::move(media_player_bridge); } }
diff --git a/content/browser/renderer_host/ime_adapter_android.cc b/content/browser/renderer_host/ime_adapter_android.cc index 3721d364..bb8a938 100644 --- a/content/browser/renderer_host/ime_adapter_android.cc +++ b/content/browser/renderer_host/ime_adapter_android.cc
@@ -46,7 +46,7 @@ const base::android::JavaRef<jobject>& java_key_event, int type, int modifiers, - long time_ms, + jlong time_ms, int key_code, int scan_code, bool is_system_key, @@ -123,14 +123,14 @@ const JavaParamRef<jobject>& original_key_event, int type, int modifiers, - long time_ms, + jlong time_ms, int key_code, int scan_code, bool is_system_key, int unicode_char) { NativeWebKeyboardEvent event = NativeWebKeyboardEventFromKeyEvent( - env, original_key_event, type, modifiers, - time_ms / 1000.0, key_code, scan_code, is_system_key, unicode_char); + env, original_key_event, type, modifiers, time_ms, key_code, scan_code, + is_system_key, unicode_char); rwhva_->SendKeyEvent(event); return true; }
diff --git a/content/browser/renderer_host/ime_adapter_android.h b/content/browser/renderer_host/ime_adapter_android.h index 71ffef2..72ab6d3 100644 --- a/content/browser/renderer_host/ime_adapter_android.h +++ b/content/browser/renderer_host/ime_adapter_android.h
@@ -43,7 +43,7 @@ const base::android::JavaParamRef<jobject>& original_key_event, int type, int modifiers, - long event_time, + jlong time_ms, int key_code, int scan_code, bool is_system_key,
diff --git a/content/browser/tracing/arc_tracing_agent.cc b/content/browser/tracing/arc_tracing_agent.cc new file mode 100644 index 0000000..d71cd647 --- /dev/null +++ b/content/browser/tracing/arc_tracing_agent.cc
@@ -0,0 +1,100 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/tracing/arc_tracing_agent.h" + +#include <string> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/threading/thread_checker.h" +#include "base/threading/thread_task_runner_handle.h" + +namespace content { + +namespace { + +constexpr char kArcTracingAgentName[] = "arc"; +constexpr char kArcTraceLabel[] = "ArcTraceEvents"; + +void OnStopTracing(bool success) { + DLOG_IF(WARNING, !success) << "Failed to stop ARC tracing."; +} + +class ArcTracingAgentImpl : public ArcTracingAgent { + public: + // base::trace_event::TracingAgent overrides: + std::string GetTracingAgentName() override { return kArcTracingAgentName; } + + std::string GetTraceEventLabel() override { return kArcTraceLabel; } + + void StartAgentTracing(const base::trace_event::TraceConfig& trace_config, + const StartAgentTracingCallback& callback) override { + DCHECK(thread_checker_.CalledOnValidThread()); + // delegate_ may be nullptr if ARC is not enabled on the system. In such + // case, simply do nothing. + if (!delegate_) { + // Use PostTask as the convention of TracingAgent. The caller expects + // callback to be called after this function returns. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, GetTracingAgentName(), false)); + return; + } + + delegate_->StartTracing(trace_config, + base::Bind(callback, GetTracingAgentName())); + } + + void StopAgentTracing(const StopAgentTracingCallback& callback) override { + DCHECK(thread_checker_.CalledOnValidThread()); + if (delegate_) + delegate_->StopTracing(base::Bind(OnStopTracing)); + + // Trace data is collect via systrace (debugd) in dev-mode. Simply + // return empty data here. + std::string no_data; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(callback, GetTracingAgentName(), GetTraceEventLabel(), + base::RefCountedString::TakeString(&no_data))); + } + + // ArcTracingAgent overrides: + void SetDelegate(Delegate* delegate) override { + DCHECK(thread_checker_.CalledOnValidThread()); + delegate_ = delegate; + } + + static ArcTracingAgentImpl* GetInstance() { + return base::Singleton<ArcTracingAgentImpl>::get(); + } + + private: + // This allows constructor and destructor to be private and usable only + // by the Singleton class. + friend struct base::DefaultSingletonTraits<ArcTracingAgentImpl>; + + ArcTracingAgentImpl() = default; + ~ArcTracingAgentImpl() override = default; + + Delegate* delegate_ = nullptr; // Owned by ArcServiceLauncher. + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(ArcTracingAgentImpl); +}; + +} // namespace + +// static +ArcTracingAgent* ArcTracingAgent::GetInstance() { + return ArcTracingAgentImpl::GetInstance(); +} + +ArcTracingAgent::ArcTracingAgent() = default; +ArcTracingAgent::~ArcTracingAgent() = default; + +ArcTracingAgent::Delegate::~Delegate() = default; + +} // namespace content
diff --git a/content/browser/tracing/arc_tracing_agent.h b/content/browser/tracing/arc_tracing_agent.h new file mode 100644 index 0000000..43cff41 --- /dev/null +++ b/content/browser/tracing/arc_tracing_agent.h
@@ -0,0 +1,58 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_TRACING_ARC_TRACING_AGENT_H_ +#define CONTENT_BROWSER_TRACING_ARC_TRACING_AGENT_H_ + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/tracing_agent.h" +#include "content/common/content_export.h" + +namespace content { + +// A wrapper for controlling tracing in ARC container. +// The overriden Stop/StartAgentTracing functions are implemented by the +// Delegate object to interact with ARC bridge. All the functions are expected +// to be called on the browser's UI thread. +class CONTENT_EXPORT ArcTracingAgent : public base::trace_event::TracingAgent { + public: + class Delegate { + public: + // Callback for StartTracing, takes one boolean argument to indicate + // success or failure. + using StartTracingCallback = base::Callback<void(bool)>; + // Callback for StopTracing, takes one boolean argument to indicate + // success or failure. + using StopTracingCallback = base::Callback<void(bool)>; + + virtual ~Delegate(); + + // Starts tracing in ARC container. + virtual void StartTracing( + const base::trace_event::TraceConfig& trace_config, + const StartTracingCallback& callback) = 0; + + // Stops tracing in ARC container. + virtual void StopTracing(const StopTracingCallback& callback) = 0; + }; + + ArcTracingAgent(); + ~ArcTracingAgent() override; + + static ArcTracingAgent* GetInstance(); + + // Sets Delegate instance, which should implement the communication + // using ARC bridge. If set to nullptr, calling of Start/StopAgentTracing + // does nothing. Ownership of |delegate| remains with the caller. + virtual void SetDelegate(Delegate* delegate) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ArcTracingAgent); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_TRACING_ARC_TRACING_AGENT_H_
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc index 5fc480346..7115cc5 100644 --- a/content/browser/tracing/tracing_controller_impl.cc +++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -3,6 +3,10 @@ // found in the LICENSE file. #include "content/browser/tracing/tracing_controller_impl.h" +#include <algorithm> +#include <memory> +#include <utility> + #include "base/bind.h" #include "base/cpu.h" #include "base/files/file_util.h" @@ -45,6 +49,7 @@ #if defined(OS_CHROMEOS) #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/debug_daemon_client.h" +#include "content/browser/tracing/arc_tracing_agent.h" #endif #if defined(OS_WIN) @@ -63,6 +68,7 @@ const char kChromeTracingAgentName[] = "chrome"; const char kETWTracingAgentName[] = "etw"; +const char kArcTracingAgentName[] = "arc"; const char kChromeTraceLabel[] = "traceEvents"; const int kStartTracingTimeoutSeconds = 30; @@ -289,6 +295,12 @@ base::Unretained(this))); ++pending_start_tracing_ack_count_; } + + ArcTracingAgent::GetInstance()->StartAgentTracing( + trace_config, + base::Bind(&TracingControllerImpl::OnStartAgentTracingAcked, + base::Unretained(this))); + ++pending_start_tracing_ack_count_; #elif defined(OS_WIN) EtwTracingAgent::GetInstance()->StartAgentTracing( trace_config, @@ -563,6 +575,11 @@ BrowserThread::GetBlockingPool()); return; } + + if (agent_name == kArcTracingAgentName) { + additional_tracing_agents_.push_back(ArcTracingAgent::GetInstance()); + return; + } #elif defined(OS_WIN) auto* etw_agent = EtwTracingAgent::GetInstance(); if (agent_name == etw_agent->GetTracingAgentName()) { @@ -667,15 +684,17 @@ if (trace_data_sink_.get() && events_str_ptr && !events_str_ptr->data().empty()) { - std::string json_string; if (agent_name == kETWTracingAgentName) { // The Windows kernel events are kept into a JSON format stored as string // and must not be escaped. - json_string = events_str_ptr->data(); - } else { - json_string = base::GetQuotedJSONString(events_str_ptr->data()); + trace_data_sink_->AddAgentTrace(events_label, events_str_ptr->data()); + } else if (agent_name != kArcTracingAgentName) { + // ARC trace data is obtained via systrace. Ignore the empty data. + // Quote other trace data as JSON strings and merge them into + // |trace_data_sink_|. + trace_data_sink_->AddAgentTrace( + events_label, base::GetQuotedJSONString(events_str_ptr->data())); } - trace_data_sink_->AddAgentTrace(events_label, json_string); } std::vector<std::string> category_groups; OnStopTracingAcked(NULL, category_groups); @@ -830,7 +849,7 @@ void TracingControllerImpl::IssueClockSyncMarker() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(pending_clock_sync_ack_count_ == 0); + DCHECK_EQ(0, pending_clock_sync_ack_count_); for (auto* it : additional_tracing_agents_) { if (it->SupportsExplicitClockSync()) { @@ -870,7 +889,7 @@ return; // Stop tracing only if all agents report back. - if(--pending_clock_sync_ack_count_ == 0) { + if (--pending_clock_sync_ack_count_ == 0) { clock_sync_timer_.Stop(); StopTracingAfterClockSync(); }
diff --git a/content/child/blob_storage/blob_transport_controller.cc b/content/child/blob_storage/blob_transport_controller.cc index 4f6d145..11b5a3e 100644 --- a/content/child/blob_storage/blob_transport_controller.cc +++ b/content/child/blob_storage/blob_transport_controller.cc
@@ -16,7 +16,6 @@ #include "base/lazy_instance.h" #include "base/location.h" #include "base/memory/ptr_util.h" -#include "base/memory/scoped_vector.h" #include "base/memory/shared_memory.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_conversions.h" @@ -210,8 +209,8 @@ // Since we can be writing to the same shared memory handle from multiple // requests, we keep them in a vector and lazily create them. - ScopedVector<SharedMemory> opened_memory; - opened_memory.resize(memory_handles->size()); + std::vector<std::unique_ptr<SharedMemory>> opened_memory( + memory_handles->size()); // We need to calculate how much memory we expect to be writing to the memory // segments so we can correctly map it the first time. @@ -252,13 +251,11 @@ } case IPCBlobItemRequestStrategy::SHARED_MEMORY: { responses.push_back(BlobItemBytesResponse(request.request_number)); - SharedMemory* memory = opened_memory[request.handle_index]; - if (!memory) { + if (!opened_memory[request.handle_index]) { SharedMemoryHandle& handle = (*memory_handles)[request.handle_index]; size_t size = shared_memory_sizes[request.handle_index]; DCHECK(SharedMemory::IsHandleValid(handle)); - std::unique_ptr<SharedMemory> shared_memory( - new SharedMemory(handle, false)); + auto shared_memory = base::MakeUnique<SharedMemory>(handle, false); if (!shared_memory->Map(size)) { // This would happen if the renderer process doesn't have enough @@ -269,14 +266,15 @@ << "."; return; } - memory = shared_memory.get(); - opened_memory[request.handle_index] = shared_memory.release(); + opened_memory[request.handle_index] = std::move(shared_memory); } - CHECK(memory->memory()) << "Couldn't map memory for blob transfer."; + CHECK(opened_memory[request.handle_index]->memory()) + << "Couldn't map memory for blob transfer."; ReadStatus status = consolidation->ReadMemory( request.renderer_item_index, request.renderer_item_offset, request.size, - static_cast<char*>(memory->memory()) + request.handle_offset); + static_cast<char*>(opened_memory[request.handle_index]->memory()) + + request.handle_offset); DCHECK(status == ReadStatus::OK) << "Error reading from consolidated blob: " << static_cast<int>(status);
diff --git a/content/child/web_data_consumer_handle_impl.cc b/content/child/web_data_consumer_handle_impl.cc index b40f479..2defcb6 100644 --- a/content/child/web_data_consumer_handle_impl.cc +++ b/content/child/web_data_consumer_handle_impl.cc
@@ -62,20 +62,13 @@ // Even if there is unread data available, mojo::ReadDataRaw() returns // FAILED_PRECONDITION when |size| is 0 and the producer handle was closed. // But in this case, WebDataConsumerHandle::Reader::read() must return Ok. - // So we use mojo::Wait() with 0 deadline to check whether readable or not. - MojoResult wait_result = mojo::Wait( - context_->handle().get(), MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr); - switch (wait_result) { - case MOJO_RESULT_OK: - return Ok; - case MOJO_RESULT_FAILED_PRECONDITION: - return Done; - case MOJO_RESULT_DEADLINE_EXCEEDED: - return ShouldWait; - default: - NOTREACHED(); - return UnexpectedError; - } + // So we query the signals state directly. + mojo::HandleSignalsState state = context_->handle()->QuerySignalsState(); + if (state.readable()) + return Ok; + if (state.never_readable()) + return Done; + return ShouldWait; } uint32_t size_to_pass = size;
diff --git a/content/common/input/input_param_traits_unittest.cc b/content/common/input/input_param_traits_unittest.cc index c5c70a3b..fa274b9 100644 --- a/content/common/input/input_param_traits_unittest.cc +++ b/content/common/input/input_param_traits_unittest.cc
@@ -5,7 +5,10 @@ #include "content/common/input/input_param_traits.h" #include <stddef.h> + +#include <memory> #include <utility> +#include <vector> #include "base/memory/ptr_util.h" #include "content/common/input/input_event.h" @@ -27,7 +30,7 @@ namespace content { namespace { -typedef ScopedVector<InputEvent> InputEvents; +typedef std::vector<std::unique_ptr<InputEvent>> InputEvents; class InputParamTraitsTest : public testing::Test { protected: @@ -44,7 +47,7 @@ static void Compare(const InputEvents* a, const InputEvents* b) { for (size_t i = 0; i < a->size(); ++i) - Compare((*a)[i], (*b)[i]); + Compare((*a)[i].get(), (*b)[i].get()); } static void Compare(const SyntheticSmoothScrollGestureParams* a, @@ -201,35 +204,35 @@ blink::WebInputEvent::NoModifiers, blink::WebInputEvent::TimeStampForTesting); key_event.nativeKeyCode = 5; - events.push_back(new InputEvent(key_event, latency)); + events.push_back(base::MakeUnique<InputEvent>(key_event, latency)); blink::WebMouseWheelEvent wheel_event( blink::WebInputEvent::MouseWheel, blink::WebInputEvent::NoModifiers, blink::WebInputEvent::TimeStampForTesting); wheel_event.deltaX = 10; latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 1, 1); - events.push_back(new InputEvent(wheel_event, latency)); + events.push_back(base::MakeUnique<InputEvent>(wheel_event, latency)); blink::WebMouseEvent mouse_event(blink::WebInputEvent::MouseDown, blink::WebInputEvent::NoModifiers, blink::WebInputEvent::TimeStampForTesting); mouse_event.x = 10; latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT, 2, 2); - events.push_back(new InputEvent(mouse_event, latency)); + events.push_back(base::MakeUnique<InputEvent>(mouse_event, latency)); blink::WebGestureEvent gesture_event( blink::WebInputEvent::GestureScrollBegin, blink::WebInputEvent::NoModifiers, blink::WebInputEvent::TimeStampForTesting); gesture_event.x = -1; - events.push_back(new InputEvent(gesture_event, latency)); + events.push_back(base::MakeUnique<InputEvent>(gesture_event, latency)); blink::WebTouchEvent touch_event(blink::WebInputEvent::TouchStart, blink::WebInputEvent::NoModifiers, blink::WebInputEvent::TimeStampForTesting); touch_event.touchesLength = 1; touch_event.touches[0].radiusX = 1; - events.push_back(new InputEvent(touch_event, latency)); + events.push_back(base::MakeUnique<InputEvent>(touch_event, latency)); Verify(events); }
diff --git a/content/public/browser/web_ui.h b/content/public/browser/web_ui.h index 1c677877..f871576 100644 --- a/content/public/browser/web_ui.h +++ b/content/public/browser/web_ui.h
@@ -8,7 +8,6 @@ #include <vector> #include "base/callback.h" -#include "base/memory/scoped_vector.h" #include "base/strings/string16.h" #include "content/common/content_export.h" #include "ui/base/page_transition_types.h"
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc index 7ea1a745..a5eb8785 100644 --- a/content/public/test/mock_render_process_host.cc +++ b/content/public/test/mock_render_process_host.cc
@@ -10,6 +10,7 @@ #include "base/lazy_instance.h" #include "base/location.h" +#include "base/memory/ptr_util.h" #include "base/process/process_handle.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" @@ -377,28 +378,24 @@ MockRenderProcessHostFactory::~MockRenderProcessHostFactory() { // Detach this object from MockRenderProcesses to prevent STLDeleteElements() // from calling MockRenderProcessHostFactory::Remove(). - for (ScopedVector<MockRenderProcessHost>::iterator it = processes_.begin(); - it != processes_.end(); ++it) { - (*it)->SetFactory(NULL); - } + for (const auto& process : processes_) + process->SetFactory(nullptr); } RenderProcessHost* MockRenderProcessHostFactory::CreateRenderProcessHost( BrowserContext* browser_context, SiteInstance* site_instance) const { - MockRenderProcessHost* host = new MockRenderProcessHost(browser_context); - if (host) { - processes_.push_back(host); - host->SetFactory(this); - } - return host; + processes_.push_back( + base::MakeUnique<MockRenderProcessHost>(browser_context)); + processes_.back()->SetFactory(this); + return processes_.back().get(); } void MockRenderProcessHostFactory::Remove(MockRenderProcessHost* host) const { - for (ScopedVector<MockRenderProcessHost>::iterator it = processes_.begin(); - it != processes_.end(); ++it) { - if (*it == host) { - processes_.weak_erase(it); + for (auto it = processes_.begin(); it != processes_.end(); ++it) { + if (it->get() == host) { + it->release(); + processes_.erase(it); break; } }
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h index 10ef44d0..8126ff8 100644 --- a/content/public/test/mock_render_process_host.h +++ b/content/public/test/mock_render_process_host.h
@@ -9,9 +9,9 @@ #include <stdint.h> #include <memory> #include <utility> +#include <vector> #include "base/macros.h" -#include "base/memory/scoped_vector.h" #include "base/metrics/persistent_memory_allocator.h" #include "base/observer_list.h" #include "content/public/browser/render_process_host.h" @@ -192,7 +192,7 @@ // A list of MockRenderProcessHosts created by this object. This list is used // for deleting all MockRenderProcessHosts that have not deleted by a test in // the destructor and prevent them from being leaked. - mutable ScopedVector<MockRenderProcessHost> processes_; + mutable std::vector<std::unique_ptr<MockRenderProcessHost>> processes_; DISALLOW_COPY_AND_ASSIGN(MockRenderProcessHostFactory); };
diff --git a/content/public/test/test_file_system_context.h b/content/public/test/test_file_system_context.h index a9c1b5f3..903ffc2 100644 --- a/content/public/test/test_file_system_context.h +++ b/content/public/test/test_file_system_context.h
@@ -6,7 +6,6 @@ #define CONTENT_PUBLIC_TEST_TEST_FILE_SYSTEM_CONTEXT_H_ #include "base/files/file_path.h" -#include "base/memory/scoped_vector.h" #include "storage/browser/fileapi/file_system_context.h" namespace storage {
diff --git a/content/public/test/test_web_contents_factory.h b/content/public/test/test_web_contents_factory.h index 55e01ede..c2a71af7 100644 --- a/content/public/test/test_web_contents_factory.h +++ b/content/public/test/test_web_contents_factory.h
@@ -6,9 +6,9 @@ #define CONTENT_PUBLIC_TEST_TEST_WEB_CONTENTS_FACTORY_H_ #include <memory> +#include <vector> #include "base/macros.h" -#include "base/memory/scoped_vector.h" namespace content { class BrowserContext; @@ -41,7 +41,7 @@ std::unique_ptr<RenderViewHostTestEnabler> rvh_enabler_; // The vector of web contents that this class created. - ScopedVector<WebContents> web_contents_; + std::vector<std::unique_ptr<WebContents>> web_contents_; DISALLOW_COPY_AND_ASSIGN(TestWebContentsFactory); };
diff --git a/content/test/test_web_contents_factory.cc b/content/test/test_web_contents_factory.cc index 0cbbbe8..d27e6a8 100644 --- a/content/test/test_web_contents_factory.cc +++ b/content/test/test_web_contents_factory.cc
@@ -4,6 +4,7 @@ #include "content/public/test/test_web_contents_factory.h" +#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "content/public/browser/web_contents.h" #include "content/public/test/test_renderer_host.h" @@ -29,10 +30,10 @@ WebContents* TestWebContentsFactory::CreateWebContents( BrowserContext* context) { - web_contents_.push_back( - WebContentsTester::CreateTestWebContents(context, nullptr)); + web_contents_.push_back(base::WrapUnique( + WebContentsTester::CreateTestWebContents(context, nullptr))); DCHECK(web_contents_.back()); - return web_contents_.back(); + return web_contents_.back().get(); } } // namespace content
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn index 4e85c5b..ea676f6 100644 --- a/extensions/BUILD.gn +++ b/extensions/BUILD.gn
@@ -117,6 +117,8 @@ "test/extension_test_notification_observer.h", "test/result_catcher.cc", "test/result_catcher.h", + "test/test_content_browser_client.cc", + "test/test_content_browser_client.h", "test/test_content_utility_client.cc", "test/test_content_utility_client.h", "test/test_extensions_client.cc",
diff --git a/extensions/DEPS b/extensions/DEPS index f79d2406..390904a 100644 --- a/extensions/DEPS +++ b/extensions/DEPS
@@ -16,6 +16,7 @@ "+extensions/grit/extensions_resources.h", "+extensions/test", "+mojo/public", + "+services/service_manager/public", "+testing", "+third_party/skia/include",
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index 350e845..b037388 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn
@@ -295,7 +295,7 @@ "//components/variations", "//crypto:platform", "//extensions:extensions_browser_resources", - "//extensions/common:mojo", + "//extensions/common", "//services/service_manager/public/cpp", ] }
diff --git a/extensions/browser/extensions_test.cc b/extensions/browser/extensions_test.cc index 4d1ccf30..f9f88a10 100644 --- a/extensions/browser/extensions_test.cc +++ b/extensions/browser/extensions_test.cc
@@ -9,6 +9,7 @@ #include "content/public/common/content_client.h" #include "content/public/test/test_browser_context.h" #include "extensions/browser/test_extensions_browser_client.h" +#include "extensions/test/test_content_browser_client.h" #include "extensions/test/test_content_utility_client.h" namespace extensions { @@ -22,10 +23,12 @@ // } // }; ExtensionsTest::ExtensionsTest() - : content_utility_client_(new TestContentUtilityClient), + : content_browser_client_(new TestContentBrowserClient), + content_utility_client_(new TestContentUtilityClient), browser_context_(new content::TestBrowserContext), extensions_browser_client_( new TestExtensionsBrowserClient(browser_context_.get())) { + content::SetBrowserClientForTesting(content_browser_client_.get()); content::SetUtilityClientForTesting(content_utility_client_.get()); ExtensionsBrowserClient::Set(extensions_browser_client_.get()); extensions_browser_client_->set_extension_system_factory(
diff --git a/extensions/browser/extensions_test.h b/extensions/browser/extensions_test.h index 1c2bf61..22a9c06 100644 --- a/extensions/browser/extensions_test.h +++ b/extensions/browser/extensions_test.h
@@ -16,6 +16,7 @@ namespace content { class BrowserContext; +class ContentBrowserClient; class ContentUtilityClient; class RenderViewHostTestEnabler; } @@ -52,6 +53,7 @@ private: content::TestContentClientInitializer content_client_initializer_; + std::unique_ptr<content::ContentBrowserClient> content_browser_client_; std::unique_ptr<content::ContentUtilityClient> content_utility_client_; std::unique_ptr<content::BrowserContext> browser_context_; std::unique_ptr<TestExtensionsBrowserClient> extensions_browser_client_;
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc index 043dcd6..5f36c51e4 100644 --- a/extensions/browser/sandboxed_unpacker.cc +++ b/extensions/browser/sandboxed_unpacker.cc
@@ -14,23 +14,19 @@ #include "base/command_line.h" #include "base/files/file_util.h" #include "base/json/json_string_value_serializer.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" -#include "base/numerics/safe_conversions.h" #include "base/path_service.h" #include "base/sequenced_task_runner.h" -#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/sequenced_worker_pool.h" #include "build/build_config.h" #include "components/crx_file/crx_file.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/utility_process_host.h" -#include "content/public/common/common_param_traits.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" #include "extensions/common/extension_l10n_util.h" -#include "extensions/common/extension_utility_messages.h" +#include "extensions/common/extension_unpacker.mojom.h" +#include "extensions/common/extension_utility_types.h" #include "extensions/common/extensions_client.h" #include "extensions/common/file_util.h" #include "extensions/common/manifest_constants.h" @@ -43,7 +39,6 @@ using base::ASCIIToUTF16; using content::BrowserThread; -using content::UtilityProcessHost; using crx_file::CrxFile; // The following macro makes histograms that record the length of paths @@ -129,21 +124,25 @@ LOG(ERROR) << temp_dir->value() << " is not writable"; return false; } + // NormalizeFilePath requires a non-empty file, so write some data. // If you change the exit points of this function please make sure all // exit points delete this temp file! - if (base::WriteFile(temp_file, ".", 1) != 1) + if (base::WriteFile(temp_file, ".", 1) != 1) { + base::DeleteFile(temp_file, false); return false; + } base::FilePath normalized_temp_file; bool normalized = base::NormalizeFilePath(temp_file, &normalized_temp_file); if (!normalized) { - // If |temp_file| contains a link, the sandbox will block al file system - // operations, and the install will fail. + // If |temp_file| contains a link, the sandbox will block all file + // system operations, and the install will fail. LOG(ERROR) << temp_dir->value() << " seem to be on remote drive."; } else { *temp_dir = normalized_temp_file.DirName(); } + // Clean up the temp file. base::DeleteFile(temp_file, false); @@ -224,11 +223,9 @@ SandboxedUnpackerClient* client) : client_(client), extensions_dir_(extensions_dir), - got_response_(false), location_(location), creation_flags_(creation_flags), - unpacker_io_task_runner_(unpacker_io_task_runner), - utility_wrapper_(new UtilityHostWrapper) { + unpacker_io_task_runner_(unpacker_io_task_runner) { // Tracking for crbug.com/692069. The location must be valid. If it's invalid, // the utility process kills itself for a bad IPC. CHECK_GT(location, Manifest::INVALID_LOCATION); @@ -259,8 +256,8 @@ } void SandboxedUnpacker::StartWithCrx(const CRXFileInfo& crx_info) { - // We assume that we are started on the thread that the client wants us to do - // file IO on. + // We assume that we are started on the thread that the client wants us + // to do file IO on. CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); crx_unpack_start_time_ = base::TimeTicks::Now(); @@ -314,12 +311,13 @@ l10n_util::GetStringUTF16(IDS_EXTENSION_UNPACK_FAILED)); return; } + PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength", link_free_crx_path); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&SandboxedUnpacker::StartUnzipOnIOThread, - this, link_free_crx_path)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SandboxedUnpacker::Unzip, this, link_free_crx_path)); } void SandboxedUnpacker::StartWithDirectory(const std::string& extension_id, @@ -342,9 +340,9 @@ return; } - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&SandboxedUnpacker::StartUnpackOnIOThread, - this, extension_root_)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SandboxedUnpacker::Unpack, this, extension_root_)); } SandboxedUnpacker::~SandboxedUnpacker() { @@ -353,28 +351,28 @@ temp_dir_.Take(); } -bool SandboxedUnpacker::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(SandboxedUnpacker, message) - IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Succeeded, - OnUnzipToDirSucceeded) - IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Failed, - OnUnzipToDirFailed) - IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Succeeded, - OnUnpackExtensionSucceeded) - IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Failed, - OnUnpackExtensionFailed) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} +void SandboxedUnpacker::StartUtilityProcessIfNeeded() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); -void SandboxedUnpacker::OnProcessCrashed(int exit_code) { - // Don't report crashes if they happen after we got a response. - if (got_response_) + if (utility_process_mojo_client_) return; - // Utility process crashed while trying to install. + utility_process_mojo_client_ = base::MakeUnique< + content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>( + l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME)); + utility_process_mojo_client_->set_error_callback( + base::Bind(&SandboxedUnpacker::UtilityProcessCrashed, this)); + + utility_process_mojo_client_->set_exposed_directory(temp_dir_.GetPath()); + + utility_process_mojo_client_->Start(); +} + +void SandboxedUnpacker::UtilityProcessCrashed() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + utility_process_mojo_client_.reset(); + ReportFailure( UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL, l10n_util::GetStringFUTF16( @@ -384,60 +382,73 @@ l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_PROCESS_CRASHED)); } -void SandboxedUnpacker::StartUnzipOnIOThread(const base::FilePath& crx_path) { - if (!utility_wrapper_->StartIfNeeded(temp_dir_.GetPath(), this, - unpacker_io_task_runner_)) { - ReportFailure( - COULD_NOT_START_UTILITY_PROCESS, - l10n_util::GetStringFUTF16( - IDS_EXTENSION_PACKAGE_INSTALL_ERROR, - FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS))); - return; - } +void SandboxedUnpacker::Unzip(const base::FilePath& crx_path) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + StartUtilityProcessIfNeeded(); + DCHECK(crx_path.DirName() == temp_dir_.GetPath()); base::FilePath unzipped_dir = crx_path.DirName().AppendASCII(kTempExtensionName); - utility_wrapper_->host()->Send( - new ExtensionUtilityMsg_UnzipToDir(crx_path, unzipped_dir)); + + utility_process_mojo_client_->service()->Unzip( + crx_path, unzipped_dir, + base::Bind(&SandboxedUnpacker::UnzipDone, this, unzipped_dir)); } -void SandboxedUnpacker::StartUnpackOnIOThread( - const base::FilePath& directory_path) { - if (!utility_wrapper_->StartIfNeeded(temp_dir_.GetPath(), this, - unpacker_io_task_runner_)) { - ReportFailure( - COULD_NOT_START_UTILITY_PROCESS, - l10n_util::GetStringFUTF16( - IDS_EXTENSION_PACKAGE_INSTALL_ERROR, - FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS))); +void SandboxedUnpacker::UnzipDone(const base::FilePath& directory, + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!success) { + utility_process_mojo_client_.reset(); + ReportFailure(UNZIP_FAILED, + l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR)); return; } - DCHECK(directory_path.DirName() == temp_dir_.GetPath()); - utility_wrapper_->host()->Send(new ExtensionUtilityMsg_UnpackExtension( - directory_path, extension_id_, location_, creation_flags_)); -} -void SandboxedUnpacker::OnUnzipToDirSucceeded(const base::FilePath& directory) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&SandboxedUnpacker::StartUnpackOnIOThread, this, directory)); + base::Bind(&SandboxedUnpacker::Unpack, this, directory)); } -void SandboxedUnpacker::OnUnzipToDirFailed(const std::string& error) { - got_response_ = true; - utility_wrapper_ = nullptr; - ReportFailure(UNZIP_FAILED, - l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR)); +void SandboxedUnpacker::Unpack(const base::FilePath& directory) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + StartUtilityProcessIfNeeded(); + + DCHECK(directory.DirName() == temp_dir_.GetPath()); + + utility_process_mojo_client_->service()->Unpack( + directory, extension_id_, location_, creation_flags_, + base::Bind(&SandboxedUnpacker::UnpackDone, this)); } -void SandboxedUnpacker::OnUnpackExtensionSucceeded( - const base::DictionaryValue& manifest) { +void SandboxedUnpacker::UnpackDone( + const base::string16& error, + std::unique_ptr<base::DictionaryValue> manifest) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + utility_process_mojo_client_.reset(); + + if (!error.empty()) { + unpacker_io_task_runner_->PostTask( + FROM_HERE, + base::Bind(&SandboxedUnpacker::UnpackExtensionFailed, this, error)); + return; + } + + unpacker_io_task_runner_->PostTask( + FROM_HERE, base::Bind(&SandboxedUnpacker::UnpackExtensionSucceeded, this, + base::Passed(&manifest))); +} + +void SandboxedUnpacker::UnpackExtensionSucceeded( + std::unique_ptr<base::DictionaryValue> manifest) { CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); - got_response_ = true; - utility_wrapper_ = nullptr; std::unique_ptr<base::DictionaryValue> final_manifest( - RewriteManifestFile(manifest)); + RewriteManifestFile(*manifest)); if (!final_manifest) return; @@ -477,13 +488,12 @@ if (!RewriteCatalogFiles()) return; - ReportSuccess(manifest, install_icon); + ReportSuccess(std::move(manifest), install_icon); } -void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) { +void SandboxedUnpacker::UnpackExtensionFailed(const base::string16& error) { CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); - got_response_ = true; - utility_wrapper_ = nullptr; + ReportFailure( UNPACKER_CLIENT_FAILED, l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error)); @@ -572,13 +582,12 @@ return ASCIIToUTF16("UNZIP_FAILED"); case DIRECTORY_MOVE_FAILED: return ASCIIToUTF16("DIRECTORY_MOVE_FAILED"); - case COULD_NOT_START_UTILITY_PROCESS: - return ASCIIToUTF16("COULD_NOT_START_UTILITY_PROCESS"); case NUM_FAILURE_REASONS: NOTREACHED(); return base::string16(); } + NOTREACHED(); return base::string16(); } @@ -643,12 +652,12 @@ FailWithPackageError(CRX_HASH_VERIFICATION_FAILED); break; } + return false; } void SandboxedUnpacker::ReportFailure(FailureReason reason, const base::string16& error) { - utility_wrapper_ = nullptr; UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason", reason, NUM_FAILURE_REASONS); if (!crx_unpack_start_time_.is_null()) @@ -665,9 +674,8 @@ } void SandboxedUnpacker::ReportSuccess( - const base::DictionaryValue& original_manifest, + std::unique_ptr<base::DictionaryValue> original_manifest, const SkBitmap& install_icon) { - utility_wrapper_ = nullptr; UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1); if (!crx_unpack_start_time_.is_null()) @@ -677,8 +685,11 @@ DCHECK(!temp_dir_.GetPath().empty()); // Client takes ownership of temporary directory and extension. + // TODO(https://crbug.com/699528): we should consider transferring the + // ownership of original_manifest to the client as well. client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, - &original_manifest, extension_.get(), install_icon); + original_manifest.get(), extension_.get(), + install_icon); extension_ = NULL; } @@ -719,6 +730,7 @@ bool SandboxedUnpacker::RewriteImageFiles(SkBitmap* install_icon) { DCHECK(!temp_dir_.GetPath().empty()); + DecodedImages images; if (!ReadImagesFromFile(temp_dir_.GetPath(), &images)) { // Couldn't read image data from disk. @@ -794,6 +806,7 @@ ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE"))); return false; } + base::FilePath path = extension_root_.Append(path_suffix); std::vector<unsigned char> image_data; @@ -860,6 +873,7 @@ ASCIIToUTF16("INVALID_PATH_FOR_CATALOG"))); return false; } + base::FilePath path = extension_root_.Append(relative_path); std::string catalog_json; @@ -898,42 +912,4 @@ } } -SandboxedUnpacker::UtilityHostWrapper::UtilityHostWrapper() {} - -bool SandboxedUnpacker::UtilityHostWrapper::StartIfNeeded( - const base::FilePath& exposed_dir, - const scoped_refptr<UtilityProcessHostClient>& client, - const scoped_refptr<base::SequencedTaskRunner>& client_task_runner) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!utility_host_) { - utility_host_ = - UtilityProcessHost::Create(client, client_task_runner)->AsWeakPtr(); - utility_host_->SetName( - l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME)); - - // Grant the subprocess access to our temp dir so it can write out files. - DCHECK(!exposed_dir.empty()); - utility_host_->SetExposedDir(exposed_dir); - if (!utility_host_->StartBatchMode()) { - utility_host_.reset(); - return false; - } - } - return true; -} - -content::UtilityProcessHost* SandboxedUnpacker::UtilityHostWrapper::host() - const { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - return utility_host_.get(); -} - -SandboxedUnpacker::UtilityHostWrapper::~UtilityHostWrapper() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (utility_host_) { - utility_host_->EndBatchMode(); - utility_host_.reset(); - } -} - } // namespace extensions
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h index 47d07b27..9007a47 100644 --- a/extensions/browser/sandboxed_unpacker.h +++ b/extensions/browser/sandboxed_unpacker.h
@@ -13,8 +13,7 @@ #include "base/memory/ref_counted_delete_on_sequence.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/utility_process_host_client.h" +#include "content/public/browser/utility_process_mojo_client.h" #include "extensions/browser/crx_file_info.h" #include "extensions/browser/install/crx_install_error.h" #include "extensions/common/manifest.h" @@ -26,13 +25,13 @@ class SequencedTaskRunner; } -namespace content { -class UtilityProcessHost; -} - namespace extensions { class Extension; +namespace mojom { +class ExtensionUnpacker; +} + class SandboxedUnpackerClient : public base::RefCountedDeleteOnSequence<SandboxedUnpackerClient> { public: @@ -63,32 +62,31 @@ friend class base::RefCountedDeleteOnSequence<SandboxedUnpackerClient>; friend class base::DeleteHelper<SandboxedUnpackerClient>; - virtual ~SandboxedUnpackerClient() {} + virtual ~SandboxedUnpackerClient() = default; }; // SandboxedUnpacker does work to optionally unpack and then validate/sanitize -// an extension, either starting from a crx file or an already unzipped -// directory (eg from differential update). This is done in a sandboxed -// subprocess to protect the browser process from parsing complex formats like -// JPEG or JSON from untrusted sources. +// an extension, either starting from a crx file, or else an already unzipped +// directory (eg., from a differential update). This is done in a sandboxed +// subprocess to protect the browser process from parsing complex data formats +// like JPEG or JSON from untrusted sources. // -// Unpacking an extension using this class makes minor changes to its source, -// such as transcoding all images to PNG, parsing all message catalogs -// and rewriting the manifest JSON. As such, it should not be used when the -// output is not intended to be given back to the author. -// +// Unpacking an extension using this class makes changes to its source, such as +// transcoding all images to PNG, parsing all message catalogs, and rewriting +// the manifest JSON. As such, it should not be used when the output is not +// intended to be given back to the author. // // Lifetime management: // // This class is ref-counted by each call it makes to itself on another thread, -// and by UtilityProcessHost. +// and by UtilityProcessMojoClient. // -// Additionally, we hold a reference to our own client so that it lives at least +// Additionally, we hold a reference to our own client so that the client lives // long enough to receive the result of unpacking. // +// NOTE: This class should only be used on the FILE thread. // -// NOTE: This class should only be used on the file thread. -class SandboxedUnpacker : public content::UtilityProcessHostClient { +class SandboxedUnpacker : public base::RefCountedThreadSafe<SandboxedUnpacker> { public: // Creates a SanboxedUnpacker that will do work to unpack an extension, // passing the |location| and |creation_flags| to Extension::Create. The @@ -111,7 +109,7 @@ const base::FilePath& directory); private: - class ProcessHostClient; + friend class base::RefCountedThreadSafe<SandboxedUnpacker>; // Enumerate all the ways unpacking can fail. Calls to ReportFailure() // take a failure reason as an argument, and put it in histogram @@ -125,14 +123,14 @@ FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY, COULD_NOT_GET_SANDBOX_FRIENDLY_PATH, - // SandboxedUnpacker::OnUnpackExtensionSucceeded() + // SandboxedUnpacker::UnpackExtensionSucceeded() COULD_NOT_LOCALIZE_EXTENSION, INVALID_MANIFEST, - // SandboxedUnpacker::OnUnpackExtensionFailed() + // SandboxedUnpacker::UnpackExtensionFailed() UNPACKER_CLIENT_FAILED, - // SandboxedUnpacker::OnProcessCrashed() + // SandboxedUnpacker::UtilityProcessCrashed() UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL, // SandboxedUnpacker::ValidateSignature() @@ -174,45 +172,48 @@ UNZIP_FAILED, DIRECTORY_MOVE_FAILED, - COULD_NOT_START_UTILITY_PROCESS, NUM_FAILURE_REASONS }; - friend class ProcessHostClient; friend class SandboxedUnpackerTest; - ~SandboxedUnpacker() override; + ~SandboxedUnpacker(); - // Set |temp_dir_| as a temporary directory to unpack the extension in. - // Return true on success. - virtual bool CreateTempDirectory(); + // Create |temp_dir_| used to unzip or unpack the extension in. + bool CreateTempDirectory(); - // Helper functions to simplify calls to ReportFailure. + // Helper functions to simplify calling ReportFailure. base::string16 FailureReasonToString16(FailureReason reason); void FailWithPackageError(FailureReason reason); // Validates the signature of the extension and extract the key to - // |public_key_|. Returns true if the signature validates, false otherwise. + // |public_key_|. True if the signature validates, false otherwise. bool ValidateSignature(const base::FilePath& crx_path, const std::string& expected_hash); - void StartUnzipOnIOThread(const base::FilePath& crx_path); - void StartUnpackOnIOThread(const base::FilePath& directory_path); + // Ensures the utility process is created. + void StartUtilityProcessIfNeeded(); - // UtilityProcessHostClient - bool OnMessageReceived(const IPC::Message& message) override; - void OnProcessCrashed(int exit_code) override; + // Utility process crashed or failed while trying to install. + void UtilityProcessCrashed(); - // IPC message handlers. - void OnUnzipToDirSucceeded(const base::FilePath& directory); - void OnUnzipToDirFailed(const std::string& error); - void OnUnpackExtensionSucceeded(const base::DictionaryValue& manifest); - void OnUnpackExtensionFailed(const base::string16& error_message); + // Unzips the extension into directory. + void Unzip(const base::FilePath& crx_path); + void UnzipDone(const base::FilePath& directory, bool success); - void ReportFailure(FailureReason reason, const base::string16& message); - void ReportSuccess(const base::DictionaryValue& original_manifest, + // Unpacks the extension in directory and returns the manifest. + void Unpack(const base::FilePath& directory); + void UnpackDone(const base::string16& error, + std::unique_ptr<base::DictionaryValue> manifest); + void UnpackExtensionSucceeded( + std::unique_ptr<base::DictionaryValue> manifest); + void UnpackExtensionFailed(const base::string16& error); + + // Reports unpack success or failure, or unzip failure. + void ReportSuccess(std::unique_ptr<base::DictionaryValue> original_manifest, const SkBitmap& install_icon); + void ReportFailure(FailureReason reason, const base::string16& error); // Overwrites original manifest with safe result from utility process. // Returns NULL on error. Caller owns the returned object. @@ -227,83 +228,49 @@ // Cleans up temp directory artifacts. void Cleanup(); - // This is a helper class to make it easier to keep track of the lifecycle of - // a UtilityProcessHost, including automatic begin and end of batch mode. - class UtilityHostWrapper : public base::RefCountedThreadSafe< - UtilityHostWrapper, - content::BrowserThread::DeleteOnIOThread> { - public: - UtilityHostWrapper(); - - // Start up the utility process if it is not already started, putting it - // into batch mode and giving it access to |exposed_dir|. This should only - // be called on the IO thread. Returns false if there was an error starting - // the utility process or putting it into batch mode. - bool StartIfNeeded( - const base::FilePath& exposed_dir, - const scoped_refptr<UtilityProcessHostClient>& client, - const scoped_refptr<base::SequencedTaskRunner>& client_task_runner); - - // This should only be called on the IO thread. - content::UtilityProcessHost* host() const; - - private: - friend struct content::BrowserThread::DeleteOnThread< - content::BrowserThread::IO>; - friend class base::DeleteHelper<UtilityHostWrapper>; - ~UtilityHostWrapper(); - - // Should only be used on the IO thread. - base::WeakPtr<content::UtilityProcessHost> utility_host_; - - DISALLOW_COPY_AND_ASSIGN(UtilityHostWrapper); - }; - - // If we unpacked a crx file, we hold on to the path for use in various - // histograms. + // If we unpacked a CRX file, we hold on to the path name for use + // in various histograms. base::FilePath crx_path_for_histograms_; - // Our client. + // Our unpacker client. scoped_refptr<SandboxedUnpackerClient> client_; // The Extensions directory inside the profile. base::FilePath extensions_dir_; - // A temporary directory to use for unpacking. + // Temporary directory to use for unpacking. base::ScopedTempDir temp_dir_; - // The root directory of the unpacked extension. This is a child of temp_dir_. + // Root directory of the unpacked extension (a child of temp_dir_). base::FilePath extension_root_; // Represents the extension we're unpacking. scoped_refptr<Extension> extension_; - // Whether we've received a response from the utility process yet. - bool got_response_; - // The public key that was extracted from the CRX header. std::string public_key_; - // The extension's ID. This will be calculated from the public key in the crx - // header. + // The extension's ID. This will be calculated from the public key + // in the CRX header. std::string extension_id_; - // If we unpacked a .crx file, the time at which unpacking started. Used to - // compute the time unpacking takes. + // If we unpacked a CRX file, the time at which unpacking started. + // Used to compute the time unpacking takes. base::TimeTicks crx_unpack_start_time_; // Location to use for the unpacked extension. Manifest::Location location_; - // Creation flags to use for the extension. These flags will be used - // when calling Extenion::Create() by the crx installer. + // Creation flags to use for the extension. These flags will be used + // when calling Extenion::Create() by the CRX installer. int creation_flags_; - // Sequenced task runner where file I/O operations will be performed at. + // Sequenced task runner where file I/O operations will be performed. scoped_refptr<base::SequencedTaskRunner> unpacker_io_task_runner_; - // Used for sending tasks to the utility process. - scoped_refptr<UtilityHostWrapper> utility_wrapper_; + // Utility client used for sending tasks to the utility process. + std::unique_ptr<content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>> + utility_process_mojo_client_; DISALLOW_COPY_AND_ASSIGN(SandboxedUnpacker); };
diff --git a/extensions/browser/sandboxed_unpacker_unittest.cc b/extensions/browser/sandboxed_unpacker_unittest.cc index ebd01aa..e39f7b2 100644 --- a/extensions/browser/sandboxed_unpacker_unittest.cc +++ b/extensions/browser/sandboxed_unpacker_unittest.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "extensions/browser/sandboxed_unpacker.h" + #include "base/base64.h" #include "base/bind.h" #include "base/command_line.h" @@ -16,7 +18,6 @@ #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "extensions/browser/extensions_test.h" -#include "extensions/browser/sandboxed_unpacker.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" #include "extensions/common/extension_paths.h"
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn index 97f392b9..48ac33a8 100644 --- a/extensions/common/BUILD.gn +++ b/extensions/common/BUILD.gn
@@ -19,12 +19,18 @@ if (enable_extensions) { mojom("mojo") { sources = [ + "extension_unpacker.mojom", "mojo/app_window.mojom", "mojo/keep_alive.mojom", ] + if (proprietary_codecs && enable_wifi_display) { sources += [ "mojo/wifi_display_session_service.mojom" ] } + + public_deps = [ + "//mojo/common:common_custom_types", + ] } # This must be a static library because extensions common depends on @@ -93,6 +99,7 @@ "extension_urls.cc", "extension_urls.h", "extension_utility_messages.h", + "extension_utility_types.h", "extensions_aliases.cc", "extensions_aliases.h", "extensions_client.cc", @@ -181,6 +188,8 @@ "manifest_handlers/web_accessible_resources_info.h", "manifest_handlers/webview_info.cc", "manifest_handlers/webview_info.h", + "manifest_location_param_traits.cc", + "manifest_location_param_traits.h", "manifest_url_handlers.cc", "manifest_url_handlers.h", "message_bundle.cc",
diff --git a/extensions/common/OWNERS b/extensions/common/OWNERS index f1f3a15..7327a7b 100644 --- a/extensions/common/OWNERS +++ b/extensions/common/OWNERS
@@ -2,3 +2,12 @@ per-file *_messages*.h=file://ipc/SECURITY_OWNERS per-file *_messages.cc=set noparent per-file *_messages.cc=file://ipc/SECURITY_OWNERS +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS +per-file *_param_traits*.*=set noparent +per-file *_param_traits*.*=file://ipc/SECURITY_OWNERS +per-file *.typemap=set noparent +per-file *.typemap=file://ipc/SECURITY_OWNERS +per-file *_struct_traits*.*=set noparent +per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS +
diff --git a/extensions/common/extension_messages.cc b/extensions/common/extension_messages.cc index 34b15ed..8e56f09 100644 --- a/extensions/common/extension_messages.cc +++ b/extensions/common/extension_messages.cc
@@ -13,6 +13,7 @@ #include "extensions/common/extension.h" #include "extensions/common/manifest.h" #include "extensions/common/manifest_handler.h" +#include "extensions/common/manifest_location_param_traits.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/permissions/permissions_info.h" @@ -101,29 +102,6 @@ namespace IPC { -template <> -struct ParamTraits<Manifest::Location> { - typedef Manifest::Location param_type; - static void Write(base::Pickle* m, const param_type& p) { - int val = static_cast<int>(p); - WriteParam(m, val); - } - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* p) { - int val = 0; - if (!ReadParam(m, iter, &val) || - val < Manifest::INVALID_LOCATION || - val >= Manifest::NUM_LOCATIONS) - return false; - *p = static_cast<param_type>(val); - return true; - } - static void Log(const param_type& p, std::string* l) { - ParamTraits<int>::Log(static_cast<int>(p), l); - } -}; - void ParamTraits<URLPattern>::GetSize(base::PickleSizer* s, const param_type& p) { GetParamSize(s, p.valid_schemes());
diff --git a/extensions/common/extension_unpacker.mojom b/extensions/common/extension_unpacker.mojom new file mode 100644 index 0000000..c2321c54 --- /dev/null +++ b/extensions/common/extension_unpacker.mojom
@@ -0,0 +1,36 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Safe chrome extension unpacker service provided by the utility process +// and exposed by mojo policy to the chrome browser process. + +module extensions.mojom; + +import "mojo/common/file_path.mojom"; +import "mojo/common/string16.mojom"; +import "mojo/common/values.mojom"; + +interface ExtensionUnpacker { + // Unzip |file| into the directory |path|. + Unzip(mojo.common.mojom.FilePath file, + mojo.common.mojom.FilePath path) => (bool success); + + // Unpack and sanitize the extension in directory |path|, and return its + // parsed manifest.json file in |manifest|. The supplied |location|, and + // and the |creation_flags| defined by Extension::InitFromValueFlags are + // passed into Extension::Create() when unpacking the extension. Decoded + // image and message catalog data from the extension is written to files + // kDecodedImagesFilename and kDecodedMessageCatalogsFilename in |path|. + // If Unpack() fails for any reason, |error| contains a user-displayable + // explanation of what went wrong. + Unpack(mojo.common.mojom.FilePath path, + string extension_id, + ManifestLocation location, + int32 creation_flags) + => (mojo.common.mojom.String16 error, + mojo.common.mojom.DictionaryValue? manifest); +}; + +[Native] +enum ManifestLocation;
diff --git a/extensions/common/extension_utility_messages.h b/extensions/common/extension_utility_messages.h index 363392f..5ce4033 100644 --- a/extensions/common/extension_utility_messages.h +++ b/extensions/common/extension_utility_messages.h
@@ -5,23 +5,13 @@ // Multiply-included message file, so no include guard. #include <string> -#include <tuple> #include "extensions/common/update_manifest.h" #include "ipc/ipc_message_macros.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h" #include "url/ipc/url_param_traits.h" #define IPC_MESSAGE_START ExtensionUtilityMsgStart -#ifndef EXTENSIONS_COMMON_EXTENSION_UTILITY_MESSAGES_H_ -#define EXTENSIONS_COMMON_EXTENSION_UTILITY_MESSAGES_H_ - -using DecodedImages = std::vector<std::tuple<SkBitmap, base::FilePath>>; - -#endif // EXTENSIONS_COMMON_EXTENSION_UTILITY_MESSAGES_H_ - IPC_STRUCT_TRAITS_BEGIN(UpdateManifest::Result) IPC_STRUCT_TRAITS_MEMBER(extension_id) IPC_STRUCT_TRAITS_MEMBER(version) @@ -43,20 +33,6 @@ IPC_MESSAGE_CONTROL1(ExtensionUtilityMsg_ParseUpdateManifest, std::string /* xml document contents */) -// Tell the utility process to unzip the zipfile at a given path into a -// directory at the second given path. -IPC_MESSAGE_CONTROL2(ExtensionUtilityMsg_UnzipToDir, - base::FilePath /* zip_file */, - base::FilePath /* dir */) - -// Tells the utility process to validate and sanitize the extension in a -// directory. -IPC_MESSAGE_CONTROL4(ExtensionUtilityMsg_UnpackExtension, - base::FilePath /* directory_path */, - std::string /* extension_id */, - int /* Manifest::Location */, - int /* InitFromValue flags */) - //------------------------------------------------------------------------------ // Utility process host messages: // These are messages from the utility process to the browser. @@ -70,28 +46,3 @@ // is a description of what went wrong suitable for logging. IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_ParseUpdateManifest_Failed, std::string /* error_message, if any */) - -// Reply when the utility process is done unzipping a file. |unpacked_path| -// is the directory which contains the unzipped contents. -IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_UnzipToDir_Succeeded, - base::FilePath /* unpacked_path */) - -// Reply when the utility process failed to unzip a file. |error| contains -// an error string to be reported to the user. -IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_UnzipToDir_Failed, - std::string /* error */) - -// Reply when the utility process is done unpacking an extension. |manifest| -// is the parsed manifest.json file. -// The unpacker should also have written out files containing the decoded -// images and message catalogs from the extension. The data is written into a -// DecodedImages struct into a file named kDecodedImagesFilename in the -// directory that was passed in. This is done because the data is too large to -// pass over IPC. -IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_UnpackExtension_Succeeded, - base::DictionaryValue /* manifest */) - -// Reply when the utility process has failed while unpacking an extension. -// |error_message| is a user-displayable explanation of what went wrong. -IPC_MESSAGE_CONTROL1(ExtensionUtilityHostMsg_UnpackExtension_Failed, - base::string16 /* error_message, if any */)
diff --git a/extensions/common/extension_utility_types.h b/extensions/common/extension_utility_types.h new file mode 100644 index 0000000..941a338 --- /dev/null +++ b/extensions/common/extension_utility_types.h
@@ -0,0 +1,25 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_COMMON_EXTENSION_UTILITY_TYPES_H_ +#define EXTENSIONS_COMMON_EXTENSION_UTILITY_TYPES_H_ + +#include <tuple> +#include <vector> + +#include "ui/gfx/ipc/skia/gfx_skia_param_traits.h" + +class SkBitmap; + +namespace base { +class FilePath; +} + +namespace extensions { + +using DecodedImages = std::vector<std::tuple<SkBitmap, base::FilePath>>; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_EXTENSION_UTILITY_TYPES_H_
diff --git a/extensions/common/manifest_location.typemap b/extensions/common/manifest_location.typemap new file mode 100644 index 0000000..b4c1da53 --- /dev/null +++ b/extensions/common/manifest_location.typemap
@@ -0,0 +1,12 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +mojom = "//extensions/common/extension_unpacker.mojom" + +public_headers = [ "//extensions/common/manifest.h" ] + +traits_headers = [ "//extensions/common/manifest_location_param_traits.h" ] + +type_mappings = + [ "extensions.mojom.ManifestLocation=extensions::Manifest::Location" ]
diff --git a/extensions/common/manifest_location_param_traits.cc b/extensions/common/manifest_location_param_traits.cc new file mode 100644 index 0000000..047461e --- /dev/null +++ b/extensions/common/manifest_location_param_traits.cc
@@ -0,0 +1,32 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/common/manifest_location_param_traits.h" + +// Auto-generate IPC::ParamTraits<> code specializations for sending and +// receiving enum extensions::Manifest::Location over IPC. + +#include "ipc/param_traits_size_macros.h" +namespace IPC { +#undef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_ +#include "extensions/common/manifest_location_param_traits.h" +} // namespace IPC + +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#undef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_ +#include "extensions/common/manifest_location_param_traits.h" +} // namespace IPC + +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#undef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_ +#include "extensions/common/manifest_location_param_traits.h" +} // namespace IPC + +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#undef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_ +#include "extensions/common/manifest_location_param_traits.h" +} // namespace IPC
diff --git a/extensions/common/manifest_location_param_traits.h b/extensions/common/manifest_location_param_traits.h new file mode 100644 index 0000000..15f7dde --- /dev/null +++ b/extensions/common/manifest_location_param_traits.h
@@ -0,0 +1,16 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_ +#define EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_ + +#include "extensions/common/manifest.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/param_traits_macros.h" + +IPC_ENUM_TRAITS_MIN_MAX_VALUE(extensions::Manifest::Location, + extensions::Manifest::INVALID_LOCATION, + extensions::Manifest::NUM_LOCATIONS - 1) + +#endif // EXTENSIONS_COMMON_MANIFEST_LOCATION_PARAM_TRAITS_H_
diff --git a/extensions/common/typemaps.gni b/extensions/common/typemaps.gni new file mode 100644 index 0000000..3ba2210 --- /dev/null +++ b/extensions/common/typemaps.gni
@@ -0,0 +1,5 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +typemaps = [ "//extensions/common/manifest_location.typemap" ]
diff --git a/extensions/test/data/extensions_test_content_utility_manifest_overlay.json b/extensions/test/data/extensions_test_content_utility_manifest_overlay.json new file mode 100644 index 0000000..7614da85 --- /dev/null +++ b/extensions/test/data/extensions_test_content_utility_manifest_overlay.json
@@ -0,0 +1,12 @@ +{ + "name": "content_utility", + "interface_provider_specs": { + "service_manager:connector": { + "provides": { + "browser": [ + "extensions::mojom::ExtensionUnpacker" + ] + } + } + } +}
diff --git a/extensions/test/test_content_browser_client.cc b/extensions/test/test_content_browser_client.cc new file mode 100644 index 0000000..4b83f11 --- /dev/null +++ b/extensions/test/test_content_browser_client.cc
@@ -0,0 +1,37 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/test/test_content_browser_client.h" + +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/path_service.h" +#include "base/values.h" +#include "content/public/common/service_names.mojom.h" +#include "extensions/common/extension_paths.h" + +namespace extensions { + +TestContentBrowserClient::TestContentBrowserClient() = default; + +TestContentBrowserClient::~TestContentBrowserClient() = default; + +std::unique_ptr<base::Value> +TestContentBrowserClient::GetServiceManifestOverlay(base::StringPiece name) { + if (name != content::mojom::kUtilityServiceName) + return nullptr; + + base::FilePath extensions_test_data_dir; + CHECK(base::PathService::Get(DIR_TEST_DATA, &extensions_test_data_dir)); + + std::string contents; + CHECK(base::ReadFileToString( + extensions_test_data_dir.AppendASCII( + "extensions_test_content_utility_manifest_overlay.json"), + &contents)); + + return base::JSONReader::Read(contents); +} + +} // namespace extensions
diff --git a/extensions/test/test_content_browser_client.h b/extensions/test/test_content_browser_client.h new file mode 100644 index 0000000..9ae3f01 --- /dev/null +++ b/extensions/test/test_content_browser_client.h
@@ -0,0 +1,31 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_ +#define EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_ + +#include <memory> + +#include "base/strings/string_piece.h" +#include "content/public/browser/content_browser_client.h" + +namespace base { +class Value; +} + +namespace extensions { + +class TestContentBrowserClient : public content::ContentBrowserClient { + public: + TestContentBrowserClient(); + ~TestContentBrowserClient() override; + + // content::ContentBrowserClient: + std::unique_ptr<base::Value> GetServiceManifestOverlay( + base::StringPiece name) override; +}; + +} // namespace extensions + +#endif // EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_
diff --git a/extensions/test/test_content_utility_client.cc b/extensions/test/test_content_utility_client.cc index 93f907e6..56aee9fc 100644 --- a/extensions/test/test_content_utility_client.cc +++ b/extensions/test/test_content_utility_client.cc
@@ -6,19 +6,21 @@ namespace extensions { -TestContentUtilityClient::TestContentUtilityClient() { -} +TestContentUtilityClient::TestContentUtilityClient() = default; -TestContentUtilityClient::~TestContentUtilityClient() { -} +TestContentUtilityClient::~TestContentUtilityClient() = default; void TestContentUtilityClient::UtilityThreadStarted() { UtilityHandler::UtilityThreadStarted(); } +void TestContentUtilityClient::ExposeInterfacesToBrowser( + service_manager::InterfaceRegistry* registry) { + UtilityHandler::ExposeInterfacesToBrowser(registry, false); +} + bool TestContentUtilityClient::OnMessageReceived(const IPC::Message& message) { return utility_handler_.OnMessageReceived(message); } - } // namespace extensions
diff --git a/extensions/test/test_content_utility_client.h b/extensions/test/test_content_utility_client.h index 518806a6..813569f 100644 --- a/extensions/test/test_content_utility_client.h +++ b/extensions/test/test_content_utility_client.h
@@ -15,8 +15,10 @@ TestContentUtilityClient(); ~TestContentUtilityClient() override; - // ContentUtilityClient implementation + // content::ContentUtilityClient: void UtilityThreadStarted() override; + void ExposeInterfacesToBrowser( + service_manager::InterfaceRegistry* registry) override; bool OnMessageReceived(const IPC::Message& message) override; private:
diff --git a/extensions/utility/unpacker.cc b/extensions/utility/unpacker.cc index bab2f98..e946013 100644 --- a/extensions/utility/unpacker.cc +++ b/extensions/utility/unpacker.cc
@@ -25,7 +25,7 @@ #include "extensions/common/constants.h" #include "extensions/common/extension.h" #include "extensions/common/extension_l10n_util.h" -#include "extensions/common/extension_utility_messages.h" +#include "extensions/common/extension_utility_types.h" #include "extensions/common/extensions_client.h" #include "extensions/common/file_util.h" #include "extensions/common/manifest.h"
diff --git a/extensions/utility/unpacker.h b/extensions/utility/unpacker.h index fa77b69f..63b20be 100644 --- a/extensions/utility/unpacker.h +++ b/extensions/utility/unpacker.h
@@ -53,6 +53,10 @@ base::DictionaryValue* parsed_manifest() { return parsed_manifest_.get(); } base::DictionaryValue* parsed_catalogs() { return parsed_catalogs_.get(); } + std::unique_ptr<base::DictionaryValue> TakeParsedManifest() { + return std::move(parsed_manifest_); + } + private: // Write the decoded images to kDecodedImagesFilename. We do this instead // of sending them over IPC, since they are so large. Returns true on
diff --git a/extensions/utility/utility_handler.cc b/extensions/utility/utility_handler.cc index 59fcdfe..96a4445 100644 --- a/extensions/utility/utility_handler.cc +++ b/extensions/utility/utility_handler.cc
@@ -6,12 +6,10 @@ #include "base/command_line.h" #include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/i18n/rtl.h" #include "content/public/utility/utility_thread.h" #include "extensions/common/constants.h" -#include "extensions/common/extension.h" #include "extensions/common/extension_l10n_util.h" +#include "extensions/common/extension_unpacker.mojom.h" #include "extensions/common/extension_utility_messages.h" #include "extensions/common/extensions_client.h" #include "extensions/common/manifest.h" @@ -20,6 +18,8 @@ #include "extensions/utility/unpacker.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/service_manager/public/cpp/interface_registry.h" #include "third_party/zlib/google/zip.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/ui_base_switches.h" @@ -28,24 +28,84 @@ namespace { -bool Send(IPC::Message* message) { - return content::UtilityThread::Get()->Send(message); -} +class ExtensionUnpackerImpl : public extensions::mojom::ExtensionUnpacker { + public: + ExtensionUnpackerImpl() = default; + ~ExtensionUnpackerImpl() override = default; -void ReleaseProcessIfNeeded() { - content::UtilityThread::Get()->ReleaseProcessIfNeeded(); -} + static void Create(extensions::mojom::ExtensionUnpackerRequest request) { + mojo::MakeStrongBinding(base::MakeUnique<ExtensionUnpackerImpl>(), + std::move(request)); + } -const char kExtensionHandlerUnzipError[] = - "Could not unzip extension for install."; + private: + // extensions::mojom::ExtensionUnpacker: + void Unzip(const base::FilePath& file, + const base::FilePath& path, + const UnzipCallback& callback) override { + std::unique_ptr<base::DictionaryValue> manifest; + if (UnzipFileManifestIntoPath(file, path, &manifest)) { + callback.Run(UnzipFileIntoPath(file, path, std::move(manifest))); + } else { + callback.Run(false); + } + } + + void Unpack(const base::FilePath& path, + const std::string& extension_id, + Manifest::Location location, + int32_t creation_flags, + const UnpackCallback& callback) override { + CHECK_GT(location, Manifest::INVALID_LOCATION); + CHECK_LT(location, Manifest::NUM_LOCATIONS); + DCHECK(ExtensionsClient::Get()); + + content::UtilityThread::Get()->EnsureBlinkInitialized(); + + Unpacker unpacker(path.DirName(), path, extension_id, location, + creation_flags); + if (unpacker.Run()) { + callback.Run(base::string16(), unpacker.TakeParsedManifest()); + } else { + callback.Run(unpacker.error_message(), nullptr); + } + } + + static bool UnzipFileManifestIntoPath( + const base::FilePath& file, + const base::FilePath& path, + std::unique_ptr<base::DictionaryValue>* manifest) { + if (zip::UnzipWithFilterCallback( + file, path, base::Bind(&Unpacker::IsManifestFile), false)) { + std::string error; + *manifest = Unpacker::ReadManifest(path, &error); + return error.empty() && manifest->get(); + } + + return false; + } + + static bool UnzipFileIntoPath( + const base::FilePath& file, + const base::FilePath& path, + std::unique_ptr<base::DictionaryValue> manifest) { + Manifest internal(Manifest::INTERNAL, std::move(manifest)); + // TODO(crbug.com/645263): This silently ignores blocked file types. + // Add install warnings. + return zip::UnzipWithFilterCallback( + file, path, + base::Bind(&Unpacker::ShouldExtractFile, internal.is_theme()), + true /* log_skipped_files */); + } + + DISALLOW_COPY_AND_ASSIGN(ExtensionUnpackerImpl); +}; } // namespace -UtilityHandler::UtilityHandler() { -} +UtilityHandler::UtilityHandler() = default; -UtilityHandler::~UtilityHandler() { -} +UtilityHandler::~UtilityHandler() = default; // static void UtilityHandler::UtilityThreadStarted() { @@ -55,13 +115,23 @@ extension_l10n_util::SetProcessLocale(lang); } +// static +void UtilityHandler::ExposeInterfacesToBrowser( + service_manager::InterfaceRegistry* registry, + bool running_elevated) { + // If our process runs with elevated privileges, only add elevated Mojo + // services to the interface registry. + if (running_elevated) + return; + + registry->AddInterface(base::Bind(&ExtensionUnpackerImpl::Create)); +} + bool UtilityHandler::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(UtilityHandler, message) IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_ParseUpdateManifest, OnParseUpdateManifest) - IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_UnzipToDir, OnUnzipToDir) - IPC_MESSAGE_HANDLER(ExtensionUtilityMsg_UnpackExtension, OnUnpackExtension) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -70,73 +140,15 @@ void UtilityHandler::OnParseUpdateManifest(const std::string& xml) { UpdateManifest manifest; if (!manifest.Parse(xml)) { - Send(new ExtensionUtilityHostMsg_ParseUpdateManifest_Failed( - manifest.errors())); + content::UtilityThread::Get()->Send( + new ExtensionUtilityHostMsg_ParseUpdateManifest_Failed( + manifest.errors())); } else { - Send(new ExtensionUtilityHostMsg_ParseUpdateManifest_Succeeded( - manifest.results())); + content::UtilityThread::Get()->Send( + new ExtensionUtilityHostMsg_ParseUpdateManifest_Succeeded( + manifest.results())); } - ReleaseProcessIfNeeded(); + content::UtilityThread::Get()->ReleaseProcessIfNeeded(); } -void UtilityHandler::OnUnzipToDir(const base::FilePath& zip_path, - const base::FilePath& dir) { - // First extract only the manifest to determine the extension type. - if (!zip::UnzipWithFilterCallback(zip_path, dir, - base::Bind(&Unpacker::IsManifestFile), - false /* log_skipped_files */)) { - Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed( - std::string(kExtensionHandlerUnzipError))); - ReleaseProcessIfNeeded(); - return; - } - - // Load the manifest. - std::string error; - std::unique_ptr<base::DictionaryValue> dict = - Unpacker::ReadManifest(dir, &error); - if (!dict.get()) { - Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed( - std::string(kExtensionHandlerUnzipError))); - ReleaseProcessIfNeeded(); - return; - } - - Manifest manifest(Manifest::INTERNAL, std::move(dict)); - base::Callback<bool(const base::FilePath&)> filetype_filter_cb = - base::Bind(&Unpacker::ShouldExtractFile, manifest.is_theme()); - - // TODO(crbug.com/645263): This silently ignores blocked file types. - // Add install warnings. - if (!zip::UnzipWithFilterCallback(zip_path, dir, filetype_filter_cb, - true /* log_skipped_files */)) { - Send(new ExtensionUtilityHostMsg_UnzipToDir_Failed( - std::string(kExtensionHandlerUnzipError))); - } else { - Send(new ExtensionUtilityHostMsg_UnzipToDir_Succeeded(dir)); - } - ReleaseProcessIfNeeded(); -} - -void UtilityHandler::OnUnpackExtension(const base::FilePath& directory_path, - const std::string& extension_id, - int location, - int creation_flags) { - CHECK_GT(location, Manifest::INVALID_LOCATION); - CHECK_LT(location, Manifest::NUM_LOCATIONS); - DCHECK(ExtensionsClient::Get()); - content::UtilityThread::Get()->EnsureBlinkInitialized(); - Unpacker unpacker(directory_path.DirName(), directory_path, extension_id, - static_cast<Manifest::Location>(location), creation_flags); - if (unpacker.Run()) { - Send(new ExtensionUtilityHostMsg_UnpackExtension_Succeeded( - *unpacker.parsed_manifest())); - } else { - Send(new ExtensionUtilityHostMsg_UnpackExtension_Failed( - unpacker.error_message())); - } - ReleaseProcessIfNeeded(); -} - - } // namespace extensions
diff --git a/extensions/utility/utility_handler.h b/extensions/utility/utility_handler.h index 2bc12f4..2378dcd 100644 --- a/extensions/utility/utility_handler.h +++ b/extensions/utility/utility_handler.h
@@ -7,17 +7,16 @@ #include <string> -#include "base/callback.h" #include "base/macros.h" -namespace base { -class FilePath; -} - namespace IPC { class Message; } +namespace service_manager { +class InterfaceRegistry; +} + namespace extensions { // A handler for extensions-related IPC from within utility processes. @@ -28,16 +27,15 @@ static void UtilityThreadStarted(); + static void ExposeInterfacesToBrowser( + service_manager::InterfaceRegistry* registry, + bool running_elevated); + bool OnMessageReceived(const IPC::Message& message); private: // IPC message handlers. void OnParseUpdateManifest(const std::string& xml); - void OnUnzipToDir(const base::FilePath& zip_path, const base::FilePath& dir); - void OnUnpackExtension(const base::FilePath& directory_path, - const std::string& extension_id, - int location, - int creation_flags); DISALLOW_COPY_AND_ASSIGN(UtilityHandler); };
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm index 45ddedc..a7fff71 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_collection_view_controller.mm
@@ -120,7 +120,7 @@ // Stops observing the ReadingListModel. - (void)stopObservingReadingListModel; // Returns the ReadingListEntry associated with the |item|. If there is not such -// an entry, returns nullptr and reloads the UI. +// an entry, returns nullptr. - (const ReadingListEntry*)readingListEntryForItem: (ReadingListCollectionViewItem*)item; // Updates the toolbar state according to the selected items. @@ -619,9 +619,12 @@ attributesProvider:self.attributesProvider url:url distillationState:entry.DistilledState()]; - NSString* urlString = base::SysUTF16ToNSString(url_formatter::FormatUrl(url)); + NSString* fullUrlString = + base::SysUTF16ToNSString(url_formatter::FormatUrl(url)); + NSString* urlString = + base::SysUTF16ToNSString(url_formatter::FormatUrl(url.GetOrigin())); NSString* title = base::SysUTF8ToNSString(entry.Title()); - item.text = [title length] ? title : urlString; + item.text = [title length] ? title : fullUrlString; item.detailText = urlString; item.faviconPageURL = entry.DistilledURL().is_valid() ? entry.DistilledURL() : url; @@ -695,12 +698,6 @@ const ReadingListEntry* readingListEntry = self.readingListModel->GetEntryByURL(item.url); - if (!readingListEntry) { - // The entry has been removed from the model, reload all data to synchronize - // the UI with the model. - [self reloadData]; - } - return readingListEntry; }
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm index 02f700d9d..46eb0e4 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -13,6 +13,7 @@ #import "ios/chrome/browser/ui/commands/generic_chrome_command.h" #include "ios/chrome/browser/ui/commands/ios_command_ids.h" #import "ios/chrome/browser/ui/reading_list/reading_list_collection_view_item.h" +#import "ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h" #include "ios/chrome/browser/ui/ui_util.h" #include "ios/chrome/grit/ios_strings.h" #include "ios/chrome/grit/ios_theme_resources.h" @@ -195,6 +196,12 @@ } return size; } + +// Returns a match for the Reading List Empty Collection Background. +id<GREYMatcher> EmptyBackground() { + return grey_accessibilityID( + [ReadingListEmptyCollectionBackground accessibilityIdentifier]); +} } // namespace // Test class for the Reading List menu. @@ -528,4 +535,29 @@ GetReadingListModel()->unread_size()); } +// Tests that you can delete multiple read items in the Reading List without +// creating a crash (crbug.com/701956). +- (void)testDeleteMultipleItems { + // Add entries. + ReadingListModel* model = GetReadingListModel(); + for (int i = 0; i < 11; i++) { + std::string increment = std::to_string(i); + model->AddEntry(GURL(kReadURL + increment), + std::string(kReadTitle + increment), + reading_list::ADDED_VIA_CURRENT_APP); + model->SetReadStatus(GURL(kReadURL + increment), true); + } + + // Delete them from the Reading List view. + OpenReadingList(); + [[EarlGrey selectElementWithMatcher:EmptyBackground()] + assertWithMatcher:grey_nil()]; + TapButtonWithID(IDS_IOS_READING_LIST_EDIT_BUTTON); + TapButtonWithID(IDS_IOS_READING_LIST_DELETE_ALL_READ_BUTTON); + + // Verify the background string is displayed. + [[EarlGrey selectElementWithMatcher:EmptyBackground()] + assertWithMatcher:grey_notNil()]; +} + @end
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h b/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h index 4ce426d7..edd28f6 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h +++ b/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.h
@@ -14,6 +14,8 @@ - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE; - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; ++ (NSString*)accessibilityIdentifier; + @end #endif // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_EMPTY_COLLECTION_BACKGROUND_H_
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.mm b/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.mm index 4c8ac93..7b7e141 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_empty_collection_background.mm
@@ -64,6 +64,8 @@ @implementation ReadingListEmptyCollectionBackground +#pragma mark - Public + - (instancetype)init { self = [super initWithFrame:CGRectZero]; if (self) { @@ -172,6 +174,8 @@ label.numberOfLines = 0; label.textAlignment = NSTextAlignmentCenter; label.accessibilityLabel = accessibilityLabel; + label.accessibilityIdentifier = + [ReadingListEmptyCollectionBackground accessibilityIdentifier]; [label setTranslatesAutoresizingMaskIntoConstraints:NO]; [self addSubview:label]; @@ -185,6 +189,12 @@ return self; } ++ (NSString*)accessibilityIdentifier { + return @"ReadingListBackgroundViewIdentifier"; +} + +#pragma mark - Private + - (void)attachIconNamed:(NSString*)iconName toString:(NSMutableAttributedString*)instructionString withCaret:(NSMutableAttributedString*)caret
diff --git a/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java index 6783c0903..1f8de94 100644 --- a/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java +++ b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java
@@ -5,7 +5,7 @@ package org.chromium.mojo; import org.chromium.mojo.system.Core; -import org.chromium.mojo.system.Core.WaitResult; +import org.chromium.mojo.system.Core.HandleSignalsState; import org.chromium.mojo.system.DataPipe; import org.chromium.mojo.system.DataPipe.ConsumerHandle; import org.chromium.mojo.system.DataPipe.ProducerHandle; @@ -35,14 +35,11 @@ } /** - * @see Handle#wait(Core.HandleSignals, long) + * @see Handle#querySignalsState() */ @Override - public WaitResult wait(Core.HandleSignals signals, long deadline) { - // Do nothing. - WaitResult result = new WaitResult(); - result.setMojoResult(MojoResult.OK); - return result; + public HandleSignalsState querySignalsState() { + return null; } /**
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java index 5affb8f..6aa1726 100644 --- a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java +++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
@@ -12,7 +12,6 @@ import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiverWithResponder; import org.chromium.mojo.system.Core; import org.chromium.mojo.system.Core.HandleSignals; -import org.chromium.mojo.system.Core.WaitResult; import org.chromium.mojo.system.Handle; import org.chromium.mojo.system.MessagePipeHandle; import org.chromium.mojo.system.MojoResult; @@ -227,8 +226,6 @@ // Confirm that the pipe was closed on the Router side. HandleSignals closedFlag = HandleSignals.none().setPeerClosed(true); - WaitResult result = mHandle.wait(closedFlag, 0); - assertEquals(MojoResult.OK, result.getMojoResult()); - assertEquals(closedFlag, result.getHandleSignalsState().getSatisfiedSignals()); + assertEquals(closedFlag, mHandle.querySignalsState().getSatisfiedSignals()); } }
diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java index 77a9bda..5120198 100644 --- a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java +++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
@@ -9,9 +9,6 @@ import org.chromium.mojo.MojoTestCase; import org.chromium.mojo.system.Core; import org.chromium.mojo.system.Core.HandleSignals; -import org.chromium.mojo.system.Core.HandleSignalsState; -import org.chromium.mojo.system.Core.WaitManyResult; -import org.chromium.mojo.system.Core.WaitResult; import org.chromium.mojo.system.DataPipe; import org.chromium.mojo.system.Handle; import org.chromium.mojo.system.InvalidHandle; @@ -30,7 +27,6 @@ import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; /** * Testing the core API. @@ -76,22 +72,6 @@ mHandlesToClose.add(handles.second); } - /** - * Runnable that will close the given handle. - */ - private static class CloseHandle implements Runnable { - private Handle mHandle; - - CloseHandle(Handle handle) { - mHandle = handle; - } - - @Override - public void run() { - mHandle.close(); - } - } - private static void checkSendingMessage(MessagePipeHandle in, MessagePipeHandle out) { Random random = new Random(); @@ -184,46 +164,6 @@ } /** - * Testing {@link Core#waitMany(List, long)}. - */ - @SmallTest - public void testWaitMany() { - Core core = CoreImpl.getInstance(); - Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); - addHandlePairToClose(handles); - - // Test waiting on handles of a newly created message pipe - each should be writable, but - // not readable. - List<Pair<Handle, Core.HandleSignals>> handlesToWaitOn = - new ArrayList<Pair<Handle, Core.HandleSignals>>(); - handlesToWaitOn.add( - new Pair<Handle, Core.HandleSignals>(handles.second, Core.HandleSignals.READABLE)); - handlesToWaitOn.add( - new Pair<Handle, Core.HandleSignals>(handles.first, Core.HandleSignals.WRITABLE)); - WaitManyResult result = core.waitMany(handlesToWaitOn, 0); - assertEquals(MojoResult.OK, result.getMojoResult()); - assertEquals(1, result.getHandleIndex()); - for (HandleSignalsState state : result.getSignalStates()) { - assertEquals(HandleSignals.WRITABLE, state.getSatisfiedSignals()); - assertEquals(ALL_SIGNALS, state.getSatisfiableSignals()); - } - - // Same test, but swap the handles around. - handlesToWaitOn.clear(); - handlesToWaitOn.add( - new Pair<Handle, Core.HandleSignals>(handles.first, Core.HandleSignals.WRITABLE)); - handlesToWaitOn.add( - new Pair<Handle, Core.HandleSignals>(handles.second, Core.HandleSignals.READABLE)); - result = core.waitMany(handlesToWaitOn, 0); - assertEquals(MojoResult.OK, result.getMojoResult()); - assertEquals(0, result.getHandleIndex()); - for (HandleSignalsState state : result.getSignalStates()) { - assertEquals(HandleSignals.WRITABLE, state.getSatisfiedSignals()); - assertEquals(ALL_SIGNALS, state.getSatisfiableSignals()); - } - } - - /** * Testing that Core can be retrieved from a handle. */ @SmallTest @@ -274,53 +214,14 @@ Core core = CoreImpl.getInstance(); Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); addHandlePairToClose(handles); - // Test waiting on handles of a newly created message pipe. - WaitResult waitResult = handles.first.wait( - Core.HandleSignals.none().setReadable(true).setWritable(true), 0); - assertEquals(MojoResult.OK, waitResult.getMojoResult()); - assertEquals( - HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals()); - assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals()); - - waitResult = handles.first.wait(Core.HandleSignals.WRITABLE, 0); - assertEquals(MojoResult.OK, waitResult.getMojoResult()); - assertEquals( - HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals()); - assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals()); - - waitResult = handles.first.wait(Core.HandleSignals.READABLE, 0); - assertEquals(MojoResult.DEADLINE_EXCEEDED, waitResult.getMojoResult()); - assertEquals( - HandleSignals.WRITABLE, waitResult.getHandleSignalsState().getSatisfiedSignals()); - assertEquals(ALL_SIGNALS, waitResult.getHandleSignalsState().getSatisfiableSignals()); // Testing read on an empty pipe. ResultAnd<MessagePipeHandle.ReadMessageResult> readResult = handles.first.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE); assertEquals(MojoResult.SHOULD_WAIT, readResult.getMojoResult()); - // Closing a pipe while waiting. - WORKER.schedule(new CloseHandle(handles.first), 10, TimeUnit.MILLISECONDS); - waitResult = handles.first.wait(Core.HandleSignals.READABLE, 1000000L); - assertEquals(MojoResult.CANCELLED, waitResult.getMojoResult()); - assertEquals( - HandleSignals.none(), waitResult.getHandleSignalsState().getSatisfiedSignals()); - assertEquals( - HandleSignals.none(), waitResult.getHandleSignalsState().getSatisfiableSignals()); - - handles = core.createMessagePipe(null); - addHandlePairToClose(handles); - - // Closing the other pipe while waiting. - WORKER.schedule(new CloseHandle(handles.first), 10, TimeUnit.MILLISECONDS); - waitResult = handles.second.wait(Core.HandleSignals.READABLE, 1000000L); - assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult()); - - // Waiting on a closed pipe. - waitResult = handles.second.wait(Core.HandleSignals.READABLE, 0); - assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult()); - waitResult = handles.second.wait(Core.HandleSignals.WRITABLE, 0); - assertEquals(MojoResult.FAILED_PRECONDITION, waitResult.getMojoResult()); + handles.first.close(); + handles.second.close(); } /** @@ -540,29 +441,6 @@ Core core = CoreImpl.getInstance(); Handle handle = InvalidHandle.INSTANCE; - // Checking wait. - boolean exception = false; - try { - core.wait(handle, Core.HandleSignals.WRITABLE, 0); - } catch (MojoException e) { - assertEquals(MojoResult.INVALID_ARGUMENT, e.getMojoResult()); - exception = true; - } - assertTrue(exception); - - // Checking waitMany. - exception = false; - try { - List<Pair<Handle, Core.HandleSignals>> handles = - new ArrayList<Pair<Handle, Core.HandleSignals>>(); - handles.add(Pair.create(handle, Core.HandleSignals.WRITABLE)); - core.waitMany(handles, 0); - } catch (MojoException e) { - assertEquals(MojoResult.INVALID_ARGUMENT, e.getMojoResult()); - exception = true; - } - assertTrue(exception); - // Checking sending an invalid handle. // Until the behavior is changed on the C++ side, handle gracefully 2 different use case: // - Receive a INVALID_ARGUMENT exception
diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc index 5cbd754..e53ed75c 100644 --- a/mojo/android/system/core_impl.cc +++ b/mojo/android/system/core_impl.cc
@@ -26,41 +26,6 @@ return MojoGetTimeTicksNow(); } -static jint WaitMany(JNIEnv* env, - const JavaParamRef<jobject>& jcaller, - const JavaParamRef<jobject>& buffer, - jlong deadline) { - // |buffer| contains, in this order - // input: The array of N handles (MojoHandle, 4 bytes each) - // input: The array of N signals (MojoHandleSignals, 4 bytes each) - // space for output: The array of N handle states (MojoHandleSignalsState, 8 - // bytes each) - // space for output: The result index (uint32_t, 4 bytes) - uint8_t* buffer_start = - static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); - DCHECK(buffer_start); - DCHECK_EQ(reinterpret_cast<uintptr_t>(buffer_start) % 8, 0u); - // Each handle of the input array contributes 4 (MojoHandle) + 4 - // (MojoHandleSignals) + 8 (MojoHandleSignalsState) = 16 bytes to the size of - // the buffer. - const size_t size_per_handle = 16; - const size_t buffer_size = env->GetDirectBufferCapacity(buffer); - DCHECK_EQ((buffer_size - 4) % size_per_handle, 0u); - - const size_t nb_handles = (buffer_size - 4) / size_per_handle; - const MojoHandle* handle_start = - reinterpret_cast<const MojoHandle*>(buffer_start); - const MojoHandleSignals* signals_start = - reinterpret_cast<const MojoHandleSignals*>(buffer_start + 4 * nb_handles); - MojoHandleSignalsState* states_start = - reinterpret_cast<MojoHandleSignalsState*>(buffer_start + 8 * nb_handles); - uint32_t* result_index = - reinterpret_cast<uint32_t*>(buffer_start + 16 * nb_handles); - *result_index = static_cast<uint32_t>(-1); - return MojoWaitMany(handle_start, signals_start, nb_handles, deadline, - result_index, states_start); -} - static ScopedJavaLocalRef<jobject> CreateMessagePipe( JNIEnv* env, const JavaParamRef<jobject>& jcaller, @@ -127,21 +92,16 @@ return MojoClose(mojo_handle); } -static jint Wait(JNIEnv* env, - const JavaParamRef<jobject>& jcaller, - const JavaParamRef<jobject>& buffer, - jint mojo_handle, - jint signals, - jlong deadline) { - // Buffer contains space for the MojoHandleSignalsState - void* buffer_start = env->GetDirectBufferAddress(buffer); - DCHECK(buffer_start); - DCHECK_EQ(reinterpret_cast<const uintptr_t>(buffer_start) % 8, 0u); - DCHECK_EQ(sizeof(struct MojoHandleSignalsState), +static jint QueryHandleSignalsState(JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + jint mojo_handle, + const JavaParamRef<jobject>& buffer) { + MojoHandleSignalsState* signals_state = + static_cast<MojoHandleSignalsState*>(env->GetDirectBufferAddress(buffer)); + DCHECK(signals_state); + DCHECK_EQ(sizeof(MojoHandleSignalsState), static_cast<size_t>(env->GetDirectBufferCapacity(buffer))); - struct MojoHandleSignalsState* signals_state = - static_cast<struct MojoHandleSignalsState*>(buffer_start); - return MojoWait(mojo_handle, signals, deadline, signals_state); + return MojoQueryHandleSignalsState(mojo_handle, signals_state); } static jint WriteMessage(JNIEnv* env,
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java index 8330586..173f801 100644 --- a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java +++ b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
@@ -8,6 +8,7 @@ import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.MainDex; import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.Core.HandleSignalsState; import org.chromium.mojo.system.DataPipe; import org.chromium.mojo.system.DataPipe.ConsumerHandle; import org.chromium.mojo.system.DataPipe.ProducerHandle; @@ -27,7 +28,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -91,61 +91,6 @@ } /** - * @see Core#waitMany(List, long) - */ - @Override - public WaitManyResult waitMany(List<Pair<Handle, HandleSignals>> handles, long deadline) { - // Allocate a direct buffer to allow native code not to reach back to java. The buffer - // layout will be: - // input: The array of handles (int, 4 bytes each) - // input: The array of signals (int, 4 bytes each) - // space for output: The array of handle states (2 ints, 8 bytes each) - // Space for output: The result index (int, 4 bytes) - // The handles and signals will be filled before calling the native method. When the native - // method returns, the handle states and the index will have been set. - ByteBuffer buffer = allocateDirectBuffer(handles.size() * 16 + 4); - int index = 0; - for (Pair<Handle, HandleSignals> handle : handles) { - buffer.putInt(HANDLE_SIZE * index, getMojoHandle(handle.first)); - buffer.putInt( - HANDLE_SIZE * handles.size() + FLAG_SIZE * index, handle.second.getFlags()); - index++; - } - int code = nativeWaitMany(buffer, deadline); - WaitManyResult result = new WaitManyResult(); - result.setMojoResult(filterMojoResultForWait(code)); - result.setHandleIndex(buffer.getInt(handles.size() * 16)); - if (result.getMojoResult() != MojoResult.INVALID_ARGUMENT - && result.getMojoResult() != MojoResult.RESOURCE_EXHAUSTED) { - HandleSignalsState[] states = new HandleSignalsState[handles.size()]; - for (int i = 0; i < handles.size(); ++i) { - states[i] = new HandleSignalsState( - new HandleSignals(buffer.getInt(8 * (handles.size() + i))), - new HandleSignals(buffer.getInt(8 * (handles.size() + i) + 4))); - } - result.setSignalStates(Arrays.asList(states)); - } - return result; - } - - /** - * @see Core#wait(Handle, HandleSignals, long) - */ - @Override - public WaitResult wait(Handle handle, HandleSignals signals, long deadline) { - // Allocate a direct buffer to allow native code not to reach back to java. Buffer will - // contain spaces to write the handle state. - ByteBuffer buffer = allocateDirectBuffer(8); - WaitResult result = new WaitResult(); - result.setMojoResult(filterMojoResultForWait( - nativeWait(buffer, getMojoHandle(handle), signals.getFlags(), deadline))); - HandleSignalsState signalsState = new HandleSignalsState( - new HandleSignals(buffer.getInt(0)), new HandleSignals(buffer.getInt(4))); - result.setHandleSignalsState(signalsState); - return result; - } - - /** * @see Core#createMessagePipe(MessagePipeHandle.CreateOptions) */ @Override @@ -262,6 +207,14 @@ } } + HandleSignalsState queryHandleSignalsState(int mojoHandle) { + ByteBuffer buffer = allocateDirectBuffer(8); + int result = nativeQueryHandleSignalsState(mojoHandle, buffer); + if (result != MojoResult.OK) throw new MojoException(result); + return new HandleSignalsState( + new HandleSignals(buffer.getInt(0)), new HandleSignals(buffer.getInt(4))); + } + /** * @see MessagePipeHandle#writeMessage(ByteBuffer, List, MessagePipeHandle.WriteFlags) */ @@ -525,8 +478,6 @@ private native long nativeGetTimeTicksNow(); - private native int nativeWaitMany(ByteBuffer buffer, long deadline); - private native ResultAnd<IntegerPair> nativeCreateMessagePipe(ByteBuffer optionsBuffer); private native ResultAnd<IntegerPair> nativeCreateDataPipe(ByteBuffer optionsBuffer); @@ -536,7 +487,7 @@ private native int nativeClose(int mojoHandle); - private native int nativeWait(ByteBuffer buffer, int mojoHandle, int signals, long deadline); + private native int nativeQueryHandleSignalsState(int mojoHandle, ByteBuffer signalsStateBuffer); private native int nativeWriteMessage( int mojoHandle, ByteBuffer bytes, int numBytes, ByteBuffer handlesBuffer, int flags);
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java b/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java index a8870a8..4d149a4 100644 --- a/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java +++ b/mojo/android/system/src/org/chromium/mojo/system/impl/HandleBase.java
@@ -7,8 +7,7 @@ import android.util.Log; import org.chromium.mojo.system.Core; -import org.chromium.mojo.system.Core.HandleSignals; -import org.chromium.mojo.system.Core.WaitResult; +import org.chromium.mojo.system.Core.HandleSignalsState; import org.chromium.mojo.system.Handle; import org.chromium.mojo.system.UntypedHandle; @@ -63,11 +62,11 @@ } /** - * @see org.chromium.mojo.system.Handle#wait(HandleSignals, long) + * @see org.chromium.mojo.system.Handle#querySignalsState() */ @Override - public WaitResult wait(HandleSignals signals, long deadline) { - return mCore.wait(this, signals, deadline); + public HandleSignalsState querySignalsState() { + return mCore.queryHandleSignalsState(mMojoHandle); } /**
diff --git a/mojo/edk/embedder/entrypoints.cc b/mojo/edk/embedder/entrypoints.cc index ecf1630..5c149d9 100644 --- a/mojo/edk/embedder/entrypoints.cc +++ b/mojo/edk/embedder/entrypoints.cc
@@ -28,6 +28,12 @@ return g_core->Close(handle); } +MojoResult MojoQueryHandleSignalsStateImpl( + MojoHandle handle, + MojoHandleSignalsState* signals_state) { + return g_core->QueryHandleSignalsState(handle, signals_state); +} + MojoResult MojoWaitImpl(MojoHandle handle, MojoHandleSignals signals, MojoDeadline deadline, @@ -281,6 +287,7 @@ MojoSystemThunks system_thunks = {sizeof(MojoSystemThunks), MojoGetTimeTicksNowImpl, MojoCloseImpl, + MojoQueryHandleSignalsStateImpl, MojoWaitImpl, MojoWaitManyImpl, MojoCreateMessagePipeImpl,
diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc index f3eec8c..db9a395 100644 --- a/mojo/edk/js/core.cc +++ b/mojo/edk/js/core.cc
@@ -35,6 +35,20 @@ return MOJO_RESULT_OK; } +gin::Dictionary QueryHandleSignalsState(const gin::Arguments& args, + mojo::Handle handle) { + gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); + if (!handle.is_valid()) { + dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT); + } else { + HandleSignalsState state = handle.QuerySignalsState(); + dictionary.Set("result", MOJO_RESULT_OK); + dictionary.Set("satisfiedSignals", state.satisfied_signals); + dictionary.Set("satisfiableSignals", state.satisfiable_signals); + } + return dictionary; +} + gin::Dictionary WaitHandle(const gin::Arguments& args, mojo::Handle handle, MojoHandleSignals signals, @@ -388,6 +402,7 @@ // TODO(mpcomplete): Should these just be methods on the JS Handle // object? .SetMethod("close", CloseHandle) + .SetMethod("queryHandleSignalsState", QueryHandleSignalsState) .SetMethod("wait", WaitHandle) .SetMethod("waitMany", WaitMany) .SetMethod("createMessagePipe", CreateMessagePipe)
diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn index 3c94e153..6e3505dc 100644 --- a/mojo/edk/system/BUILD.gn +++ b/mojo/edk/system/BUILD.gn
@@ -165,6 +165,7 @@ "platform_handle_dispatcher_unittest.cc", "shared_buffer_dispatcher_unittest.cc", "shared_buffer_unittest.cc", + "signals_unittest.cc", "wait_set_dispatcher_unittest.cc", "waiter_test_utils.cc", "waiter_test_utils.h",
diff --git a/mojo/edk/system/awakable_list.cc b/mojo/edk/system/awakable_list.cc index 429e691..f38d580 100644 --- a/mojo/edk/system/awakable_list.cc +++ b/mojo/edk/system/awakable_list.cc
@@ -8,7 +8,6 @@ #include "base/logging.h" #include "mojo/edk/system/awakable.h" -#include "mojo/edk/system/handle_signals_state.h" namespace mojo { namespace edk {
diff --git a/mojo/edk/system/awakable_list.h b/mojo/edk/system/awakable_list.h index 34d6b06..d400b387 100644 --- a/mojo/edk/system/awakable_list.h +++ b/mojo/edk/system/awakable_list.h
@@ -13,12 +13,12 @@ #include "base/macros.h" #include "mojo/edk/system/system_impl_export.h" #include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/handle_signals_state.h" namespace mojo { namespace edk { class Awakable; -struct HandleSignalsState; // |AwakableList| tracks all the |Waiter|s that are waiting on a given // handle/|Dispatcher|. There should be a |AwakableList| for each handle that
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc index cfe01fa..263f9cd 100644 --- a/mojo/edk/system/core.cc +++ b/mojo/edk/system/core.cc
@@ -378,6 +378,17 @@ return MOJO_RESULT_OK; } +MojoResult Core::QueryHandleSignalsState( + MojoHandle handle, + MojoHandleSignalsState* signals_state) { + RequestContext request_context; + scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle); + if (!dispatcher || !signals_state) + return MOJO_RESULT_INVALID_ARGUMENT; + *signals_state = dispatcher->GetHandleSignalsState(); + return MOJO_RESULT_OK; +} + MojoResult Core::Wait(MojoHandle handle, MojoHandleSignals signals, MojoDeadline deadline,
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h index 2ef9b7f..ab94951b 100644 --- a/mojo/edk/system/core.h +++ b/mojo/edk/system/core.h
@@ -136,6 +136,8 @@ // "mojo/public/c/system/functions.h": MojoTimeTicks GetTimeTicksNow(); MojoResult Close(MojoHandle handle); + MojoResult QueryHandleSignalsState(MojoHandle handle, + MojoHandleSignalsState* signals_state); MojoResult Wait(MojoHandle handle, MojoHandleSignals signals, MojoDeadline deadline,
diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc index 11474962..b04bebd 100644 --- a/mojo/edk/system/data_pipe_unittest.cc +++ b/mojo/edk/system/data_pipe_unittest.cc
@@ -162,8 +162,7 @@ // Now wait for the other side to become readable. MojoHandleSignalsState state; ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfied_signals); @@ -249,8 +248,7 @@ // Wait. ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -337,19 +335,12 @@ Create(&options); MojoHandleSignalsState hss; - // Never readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); + // Never readable. Already writable. + hss = GetSignalsState(producer_); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - // Already writable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); - // Write two elements. int32_t elements[2] = {123, 456}; uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0])); @@ -358,8 +349,7 @@ // Wait for data to become available to the consumer. ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -419,11 +409,7 @@ // It should now be never-writable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); + WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); } @@ -444,8 +430,7 @@ // It should be signaled. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); } @@ -466,8 +451,7 @@ // It should be signaled. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); } @@ -485,8 +469,7 @@ // Never writable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); EXPECT_EQ(0u, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -500,8 +483,7 @@ // Wait for readability. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -516,8 +498,7 @@ // Should still be readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -534,8 +515,7 @@ // Should still be readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -559,8 +539,7 @@ // Waiting should now succeed. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -573,8 +552,7 @@ // Should still be readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_TRUE(hss.satisfied_signals & (MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -584,8 +562,7 @@ // Wait for the peer closed signal. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); @@ -605,8 +582,7 @@ // Should be never-readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); } @@ -625,11 +601,10 @@ EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); // The consumer handle should appear to be readable and have new data. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE)); + EXPECT_TRUE(GetSignalsState(consumer_).satisfied_signals & + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE); // Now try to read a minimum of 6 elements. int32_t read_elements[6]; @@ -638,35 +613,30 @@ MojoReadData(consumer_, read_elements, &num_read_bytes, MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - // The consumer should still appear to be readable, but not with new data. - EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr)); - EXPECT_EQ( - MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 0, nullptr)); + // The consumer should still appear to be readable but not with new data. + EXPECT_TRUE(GetSignalsState(consumer_).satisfied_signals & + MOJO_HANDLE_SIGNAL_READABLE); + EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals & + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE); // Write four more elements. EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true)); - // The consumer handle should once again appear to be readable with new data. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); + // The consumer handle should once again appear to be readable. EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE)); - // Read should succeed this time. + // Try again to read a minimum of 6 elements. Should succeed this time. EXPECT_EQ(MOJO_RESULT_OK, MojoReadData(consumer_, read_elements, &num_read_bytes, MOJO_READ_DATA_FLAG_ALL_OR_NONE)); - // And once again the consumer is unreadable. - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr)); - EXPECT_EQ( - MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 0, nullptr)); + // And now the consumer is unreadable. + EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals & + MOJO_HANDLE_SIGNAL_READABLE); + EXPECT_FALSE(GetSignalsState(consumer_).satisfied_signals & + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE); } // Test with two-phase APIs and also closing the producer with an active @@ -697,8 +667,7 @@ // Wait for readability. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -719,8 +688,7 @@ // Should still be readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -743,8 +711,7 @@ // Should be never-readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); } @@ -761,9 +728,7 @@ MojoHandleSignalsState hss; // It should be writable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); + hss = GetSignalsState(producer_); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); @@ -775,17 +740,13 @@ EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t))); // At this point, it shouldn't be writable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); + hss = GetSignalsState(producer_); ASSERT_EQ(0u, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); // It shouldn't be readable yet either (we'll wait later). - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); + hss = GetSignalsState(consumer_); ASSERT_EQ(0u, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -795,9 +756,7 @@ ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(1u * sizeof(int32_t))); // It should immediately be writable again. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); + hss = GetSignalsState(producer_); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); @@ -805,8 +764,7 @@ // It should become readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -824,8 +782,7 @@ // It should be readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -843,17 +800,13 @@ ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(int32_t)), num_bytes); // At this point, it should still be writable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); + hss = GetSignalsState(producer_); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); // But not readable. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); + hss = GetSignalsState(consumer_); ASSERT_EQ(0u, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -863,9 +816,7 @@ ASSERT_EQ(MOJO_RESULT_OK, EndReadData(0u)); // It should be readable again. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); + hss = GetSignalsState(consumer_); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -911,8 +862,7 @@ // of data to become available. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE | @@ -1002,8 +952,7 @@ // Wait. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, @@ -1067,8 +1016,7 @@ // Wait for data. ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_TRUE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -1095,8 +1043,7 @@ while (total_num_bytes < 90) { // Wait to write. ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); ASSERT_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_WRITABLE); ASSERT_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED); @@ -1231,8 +1178,7 @@ // TODO(vtl): (See corresponding TODO in AllOrNone.) hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -1252,8 +1198,7 @@ // Wait for producer to know that the consumer is closed. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(producer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); @@ -1323,8 +1268,7 @@ // must also know about all the data that was sent.) hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); @@ -1384,8 +1328,7 @@ // Wait for the data. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -1411,8 +1354,7 @@ // must also have received the extra data). hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, @@ -1499,8 +1441,7 @@ // TODO(vtl): (See corresponding TODO in AllOrNone.) hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -1569,8 +1510,7 @@ // Wait for the data. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -1594,8 +1534,8 @@ MojoWriteMessage(pipe0, nullptr, 0, &producer_, 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); producer_ = MOJO_HANDLE_INVALID; - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1, MOJO_HANDLE_SIGNAL_READABLE, &hss)); uint32_t num_handles = 1; ASSERT_EQ(MOJO_RESULT_OK, MojoReadMessage(pipe1, nullptr, 0, &producer_, &num_handles, @@ -1612,8 +1552,7 @@ // Wait for it. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | @@ -1653,8 +1592,7 @@ // Now wait for the other side to become readable and to see the peer closed. MojoHandleSignalsState state; ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &state)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfied_signals); @@ -1671,8 +1609,8 @@ MojoWriteMessage(pipe0, nullptr, 0, &consumer_, 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); consumer_ = MOJO_HANDLE_INVALID; - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1, MOJO_HANDLE_SIGNAL_READABLE, &state)); uint32_t num_handles = 1; ASSERT_EQ(MOJO_RESULT_OK, MojoReadMessage(pipe1, nullptr, 0, &consumer_, &num_handles, @@ -1680,8 +1618,7 @@ ASSERT_EQ(num_handles, 1u); ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &state)); + WaitForSignals(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfied_signals); @@ -1716,8 +1653,8 @@ } MojoHandleSignalsState hss = MojoHandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(producer, MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + EXPECT_EQ(MOJO_RESULT_OK, test::MojoTestBase::WaitForSignals( + producer, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); @@ -1754,8 +1691,8 @@ } MojoHandleSignalsState hss = MojoHandleSignalsState(); - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + EXPECT_EQ(MOJO_RESULT_OK, test::MojoTestBase::WaitForSignals( + consumer, MOJO_HANDLE_SIGNAL_READABLE, &hss)); // Peer could have become closed while we're still waiting for data. EXPECT_TRUE(MOJO_HANDLE_SIGNAL_READABLE & hss.satisfied_signals); EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); @@ -1814,8 +1751,8 @@ // Receive the consumer from the other side. producer_ = MOJO_HANDLE_INVALID; MojoHandleSignalsState hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(server_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); MojoHandle handles[2]; uint32_t num_handles = arraysize(handles); ASSERT_EQ(MOJO_RESULT_OK, @@ -1844,8 +1781,8 @@ // Receive the data pipe from the other side. MojoHandle consumer = MOJO_HANDLE_INVALID; MojoHandleSignalsState hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); MojoHandle handles[2]; uint32_t num_handles = arraysize(handles); ASSERT_EQ(MOJO_RESULT_OK, @@ -1880,8 +1817,8 @@ // Receive the producer from the other side. MojoHandle producer = MOJO_HANDLE_INVALID; hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(client_mp, MOJO_HANDLE_SIGNAL_READABLE, &hss)); num_handles = arraysize(handles); ASSERT_EQ(MOJO_RESULT_OK, MojoReadMessage(client_mp, nullptr, 0, handles, &num_handles, @@ -1921,8 +1858,7 @@ std::string expected_message = ReadMessageWithHandles(h, &c, 1); // Wait for the consumer to become readable. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE)); // Drain the consumer and expect to find the given message. uint32_t num_bytes = static_cast<uint32_t>(expected_message.size()); @@ -1989,8 +1925,7 @@ std::string expected_message = ReadMessageWithHandles(child, &c, 1); // Wait for the consumer to become readable. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE)); // Drain the consumer and expect to find the given message. uint32_t num_bytes = static_cast<uint32_t>(expected_message.size()); @@ -2017,15 +1952,13 @@ MojoHandle* producers = &handles[0]; MojoHandle* consumers = &handles[3]; - // Wait on producer 0 using MojoWait. + // Wait on producer 0 EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(producers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); + WaitForSignals(producers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); - // Wait on consumer 0 using MojoWait. + // Wait on consumer 0 EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(consumers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); + WaitForSignals(consumers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); base::MessageLoop message_loop;
diff --git a/mojo/edk/system/handle_signals_state.h b/mojo/edk/system/handle_signals_state.h index 1c47a28..f241278 100644 --- a/mojo/edk/system/handle_signals_state.h +++ b/mojo/edk/system/handle_signals_state.h
@@ -5,45 +5,9 @@ #ifndef MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_ #define MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_ -#include "mojo/edk/system/system_impl_export.h" -#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/handle_signals_state.h" -namespace mojo { -namespace edk { - -// Just "add" some constructors and methods to the C struct -// |MojoHandleSignalsState| (for convenience). This should add no overhead. -struct MOJO_SYSTEM_IMPL_EXPORT HandleSignalsState final - : public MojoHandleSignalsState { - HandleSignalsState() { - satisfied_signals = MOJO_HANDLE_SIGNAL_NONE; - satisfiable_signals = MOJO_HANDLE_SIGNAL_NONE; - } - HandleSignalsState(MojoHandleSignals satisfied, - MojoHandleSignals satisfiable) { - satisfied_signals = satisfied; - satisfiable_signals = satisfiable; - } - - bool equals(const HandleSignalsState& other) const { - return satisfied_signals == other.satisfied_signals && - satisfiable_signals == other.satisfiable_signals; - } - - bool satisfies(MojoHandleSignals signals) const { - return !!(satisfied_signals & signals); - } - - bool can_satisfy(MojoHandleSignals signals) const { - return !!(satisfiable_signals & signals); - } - - // (Copy and assignment allowed.) -}; -static_assert(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState), - "HandleSignalsState should add no overhead"); - -} // namespace edk -} // namespace mojo +// TODO(rockot): Remove this header and use the C++ system library type +// directly inside the EDK. #endif // MOJO_EDK_SYSTEM_HANDLE_SIGNALS_STATE_H_
diff --git a/mojo/edk/system/message_pipe_perftest.cc b/mojo/edk/system/message_pipe_perftest.cc index a6ce3709..9866c47 100644 --- a/mojo/edk/system/message_pipe_perftest.cc +++ b/mojo/edk/system/message_pipe_perftest.cc
@@ -47,8 +47,7 @@ 0, MOJO_WRITE_MESSAGE_FLAG_NONE), MOJO_RESULT_OK); HandleSignalsState hss; - CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, - &hss), + CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss), MOJO_RESULT_OK); uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer_.size()); CHECK_EQ(MojoReadMessage(mp, &read_buffer_[0], &read_buffer_size, nullptr, @@ -98,9 +97,7 @@ while (true) { // Wait for our end of the message pipe to be readable. HandleSignalsState hss; - MojoResult result = - MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss); + MojoResult result = WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss); if (result != MOJO_RESULT_OK) { rv = result; break;
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc index 8f48950..e6f1ff64 100644 --- a/mojo/edk/system/message_pipe_unittest.cc +++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -100,8 +100,8 @@ ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); // Read from port 0. buffer[0] = 123; @@ -124,8 +124,8 @@ buffer[1] = 0; ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0]))); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state)); // Read from port 1 with buffer size 0 (should get the size of next message). // Also test that giving a null buffer is okay when the buffer size is 0. @@ -154,8 +154,8 @@ ASSERT_EQ(123456789, buffer[0]); ASSERT_EQ(456, buffer[1]); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state)); // Read again from port 1. buffer[0] = 123; @@ -179,8 +179,8 @@ MojoClose(pipe0_); pipe0_ = MOJO_HANDLE_INVALID; - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state)); // Try to write from port 1 (to port 0). buffer[0] = 456789012; @@ -215,8 +215,8 @@ } MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); // Port 0 shouldn't be empty. buffer_size = 0; @@ -241,8 +241,8 @@ ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); MojoHandleSignalsState state; - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); // Read/discard from port 0 (no buffer); get size. buffer_size = 0; @@ -261,8 +261,8 @@ ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); // Read from port 0 (buffer big enough). buffer[0] = 123; @@ -283,8 +283,8 @@ buffer[1] = 0; ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); // Read/discard from port 0 (buffer too small); get size. buffer_size = 1; @@ -302,8 +302,8 @@ buffer[1] = 0; ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &state)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); // Discard from port 0. buffer_size = 1; @@ -323,41 +323,27 @@ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); uint32_t buffer_size; - // Always writable (until the other port is closed). - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_WRITABLE, 0, - &hss)); + // Always writable (until the other port is closed). Not yet readable. Peer + // not closed. + hss = GetSignalsState(pipe0_); ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); ASSERT_EQ(kAllSignals, hss.satisfiable_signals); hss = MojoHandleSignalsState(); - // Not yet readable. - ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - - // The peer is not closed. - hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(pipe0_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, 0, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); - ASSERT_EQ(kAllSignals, hss.satisfiable_signals); - // Write from port 0 (to port 1), to make port 1 readable. buffer[0] = 123456789; ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, kBufferSize)); // Port 1 should already be readable now. - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); ASSERT_EQ(kAllSignals, hss.satisfiable_signals); // ... and still writable. hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); ASSERT_EQ(kAllSignals, hss.satisfiable_signals); @@ -368,8 +354,8 @@ // Port 1 should be signaled with peer closed. hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, &hss)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, @@ -379,8 +365,7 @@ hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, @@ -388,8 +373,8 @@ // But it should still be readable. hss = MojoHandleSignalsState(); - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + ASSERT_EQ(MOJO_RESULT_OK, + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, @@ -404,8 +389,7 @@ // Now port 1 should no longer be readable. hss = MojoHandleSignalsState(); ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); } @@ -453,9 +437,7 @@ EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessageNew(a, message, MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(b, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, - nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE)); uint32_t num_bytes = 0; uint32_t num_handles = 0; EXPECT_EQ(MOJO_RESULT_OK, @@ -489,8 +471,7 @@ WriteMessageWithHandles(h, "", handles, kPingPongHandlesPerIteration); } - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE)); char msg[4]; uint32_t num_bytes = 4; EXPECT_EQ(MOJO_RESULT_OK, ReadMessage(h, msg, &num_bytes)); @@ -675,9 +656,7 @@ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); - + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); } @@ -700,9 +679,7 @@ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); EXPECT_EQ(kTestMessage, ReadMessage(d)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); - + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); }
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc index f8e29d63..3b521ccf 100644 --- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc +++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -91,9 +91,7 @@ for (;; rv = (rv + 1) % 100) { // Wait for our end of the message pipe to be readable. HandleSignalsState hss; - MojoResult result = - MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss); + MojoResult result = WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss); if (result != MOJO_RESULT_OK) { // It was closed, probably. CHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION); @@ -139,8 +137,7 @@ HandleSignalsState hss; ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); // The child may or may not have closed its end of the message pipe and died // (and we may or may not know it yet), so our end may or may not appear as // writable. @@ -179,8 +176,7 @@ for (size_t i = 0; i < kNumMessages; i++) { HandleSignalsState hss; ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); // The child may or may not have closed its end of the message pipe and // died (and we may or may not know it yet), so our end may or may not // appear as writable. @@ -208,8 +204,7 @@ // "quitquitquit"). HandleSignalsState hss; ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); END_CHILD_AND_EXPECT_EXIT_CODE(static_cast<int>(kNumMessages % 100)); @@ -219,8 +214,7 @@ h) { // Wait for the first message from our parent. HandleSignalsState hss; - CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss), + CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss), MOJO_RESULT_OK); // In this test, the parent definitely doesn't close its end of the message // pipe before we do. @@ -265,8 +259,7 @@ // Now wait for our parent to send us a message. hss = HandleSignalsState(); - CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss), + CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss), MOJO_RESULT_OK); CHECK_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE); @@ -322,8 +315,7 @@ // Wait for a message from the child. HandleSignalsState hss; ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); @@ -359,8 +351,7 @@ // Wait for |h| to become readable, which should fail. hss = HandleSignalsState(); ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); END_CHILD() @@ -369,8 +360,7 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckPlatformHandleFile, MultiprocessMessagePipeTest, h) { HandleSignalsState hss; - CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss), + CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss), MOJO_RESULT_OK); CHECK_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE); @@ -455,8 +445,7 @@ // Wait for it to become readable, which should fail. HandleSignalsState hss; ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); END_CHILD() @@ -474,8 +463,7 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) { // Wait for the first message from our parent. HandleSignalsState hss; - CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss), + CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss), MOJO_RESULT_OK); // In this test, the parent definitely doesn't close its end of the message // pipe before we do. @@ -495,8 +483,7 @@ CHECK_EQ(num_handlers, 1u); // Read data from the received message pipe. - CHECK_EQ(MojoWait(handles[0], MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss), + CHECK_EQ(WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_READABLE, &hss), MOJO_RESULT_OK); CHECK_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE); @@ -547,8 +534,7 @@ // Wait for a message from the child. HandleSignalsState hss; ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(mp1, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); @@ -585,8 +571,7 @@ // Wait for a message from the child. HandleSignalsState hss; ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(mp1, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); @@ -604,8 +589,7 @@ DEFINE_TEST_CLIENT_WITH_PIPE(DataPipeConsumer, MultiprocessMessagePipeTest, h) { // Wait for the first message from our parent. HandleSignalsState hss; - CHECK_EQ(MojoWait(h, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss), + CHECK_EQ(WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss), MOJO_RESULT_OK); // In this test, the parent definitely doesn't close its end of the message // pipe before we do. @@ -625,8 +609,7 @@ CHECK_EQ(num_handlers, 1u); // Read data from the received message pipe. - CHECK_EQ(MojoWait(handles[0], MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss), + CHECK_EQ(WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_READABLE, &hss), MOJO_RESULT_OK); CHECK_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE); @@ -677,8 +660,7 @@ // Wait for a message from the child. HandleSignalsState hss; ASSERT_EQ(MOJO_RESULT_OK, - MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, &hss)); + WaitForSignals(mp1, MOJO_HANDLE_SIGNAL_READABLE, &hss)); EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE)); EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE)); @@ -924,9 +906,7 @@ EXPECT_EQ("bye", ReadMessage(p0)); // We should still be able to observe peer closure from the other end. - EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(p0, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(p0, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); } // Parses commands from the parent pipe and does whatever it's asked to do. @@ -1110,10 +1090,7 @@ MultiprocessMessagePipeTest, h) { MojoHandle p; EXPECT_EQ("foo", ReadMessageWithHandles(h, &p, 1)); - - auto result = MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr); - EXPECT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); } TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendPipeThenClosePeer) { @@ -1159,9 +1136,8 @@ ReadMessageWithHandles(application_client, &service_client, 1)); // Wait for the service client to signal closure. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(service_client, - MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + WaitForSignals(service_client, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(service_client)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(application_client)); @@ -1207,8 +1183,7 @@ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); // We should be able to detect peer closure on |a|. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(a, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); } DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteCloseSendPeerClient, @@ -1251,8 +1226,8 @@ EXPECT_EQ("qux", ReadMessage(p)); // Expect to have peer closure signaled. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + WaitForSignals(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); WriteMessage(h, "quit"); END_CHILD() @@ -1265,9 +1240,8 @@ MojoHandle handles[4]; EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 4)); - // Wait on handle 0 using MojoWait. - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); + EXPECT_EQ(MOJO_RESULT_OK, + WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); base::MessageLoop message_loop; @@ -1349,8 +1323,7 @@ WriteMessageWithHandles(child2, "hi", &d, 1); // Read a message from the pipe we sent to child1 and flag it as bad. - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(a, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); + ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE)); uint32_t num_bytes = 0; MojoMessageHandle message; ASSERT_EQ(MOJO_RESULT_OK, @@ -1362,8 +1335,7 @@ EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message)); // Read a message from the pipe we sent to child2 and flag it as bad. - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr)); + ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE)); ASSERT_EQ(MOJO_RESULT_OK, MojoReadMessageNew(c, &message, &num_bytes, nullptr, 0, MOJO_READ_MESSAGE_FLAG_NONE));
diff --git a/mojo/edk/system/signals_unittest.cc b/mojo/edk/system/signals_unittest.cc new file mode 100644 index 0000000..e8b0cd1 --- /dev/null +++ b/mojo/edk/system/signals_unittest.cc
@@ -0,0 +1,76 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/edk/test/mojo_test_base.h" +#include "mojo/public/c/system/buffer.h" +#include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/c/system/functions.h" +#include "mojo/public/c/system/message_pipe.h" +#include "mojo/public/c/system/types.h" + +namespace mojo { +namespace edk { +namespace { + +using SignalsTest = test::MojoTestBase; + +TEST_F(SignalsTest, QueryInvalidArguments) { + MojoHandleSignalsState state = {0, 0}; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoQueryHandleSignalsState(MOJO_HANDLE_INVALID, &state)); + + MojoHandle a, b; + CreateMessagePipe(&a, &b); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoQueryHandleSignalsState(a, nullptr)); +} + +TEST_F(SignalsTest, QueryMessagePipeSignals) { + MojoHandleSignalsState state = {0, 0}; + + MojoHandle a, b; + CreateMessagePipe(&a, &b); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(a, &state)); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfiable_signals); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state)); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfiable_signals); + + WriteMessage(a, "ok"); + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state)); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfiable_signals); + + EXPECT_EQ("ok", ReadMessage(b)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state)); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED, + state.satisfiable_signals); + + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); + + EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); + + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(b, &state)); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); +} + +} // namespace +} // namespace edk +} // namespace mojo
diff --git a/mojo/edk/test/mojo_test_base.cc b/mojo/edk/test/mojo_test_base.cc index f1032d7e..d7d3b2a0 100644 --- a/mojo/edk/test/mojo_test_base.cc +++ b/mojo/edk/test/mojo_test_base.cc
@@ -5,13 +5,16 @@ #include "mojo/edk/test/mojo_test_base.h" #include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/synchronization/waitable_event.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/system/handle_signals_state.h" #include "mojo/public/c/system/buffer.h" #include "mojo/public/c/system/data_pipe.h" #include "mojo/public/c/system/functions.h" +#include "mojo/public/c/system/watcher.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_MACOSX) && !defined(OS_IOS) @@ -22,6 +25,103 @@ namespace edk { namespace test { +namespace { + +class Waiter { + public: + Waiter() {} + ~Waiter() {} + + MojoResult Wait(MojoHandle handle, + MojoHandleSignals signals, + MojoHandleSignalsState* state) { + MojoHandle watcher; + MojoCreateWatcher(&Context::OnNotification, &watcher); + + context_ = new Context(); + + // Balanced by OnNotification in the |MOJO_RESULT_CANCELLED| case. + context_->AddRef(); + + MojoResult rv = MojoWatch(watcher, handle, signals, + reinterpret_cast<uintptr_t>(context_.get())); + DCHECK_EQ(MOJO_RESULT_OK, rv); + + uint32_t num_ready_contexts = 1; + uintptr_t ready_context; + MojoResult ready_result; + MojoHandleSignalsState ready_state; + rv = MojoArmWatcher(watcher, &num_ready_contexts, &ready_context, + &ready_result, &ready_state); + if (rv == MOJO_RESULT_FAILED_PRECONDITION) { + MojoClose(watcher); + DCHECK_EQ(1u, num_ready_contexts); + if (state) + *state = ready_state; + return ready_result; + } + + // Wait for the first notification. + context_->event().Wait(); + + ready_result = context_->wait_result(); + DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result); + + if (state) + *state = context_->wait_state(); + + MojoClose(watcher); + + return ready_result; + } + + private: + class Context : public base::RefCountedThreadSafe<Context> { + public: + Context() + : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED) {} + + base::WaitableEvent& event() { return event_; } + MojoResult wait_result() const { return wait_result_; } + MojoHandleSignalsState wait_state() const { return wait_state_; } + + static void OnNotification(uintptr_t context_value, + MojoResult result, + MojoHandleSignalsState state, + MojoWatcherNotificationFlags flags) { + auto* context = reinterpret_cast<Context*>(context_value); + context->Notify(result, state); + if (result == MOJO_RESULT_CANCELLED) + context->Release(); + } + + private: + friend class base::RefCountedThreadSafe<Context>; + + ~Context() {} + + void Notify(MojoResult result, MojoHandleSignalsState state) { + if (wait_result_ == MOJO_RESULT_UNKNOWN) { + wait_result_ = result; + wait_state_ = state; + } + event_.Signal(); + } + + base::WaitableEvent event_; + MojoResult wait_result_ = MOJO_RESULT_UNKNOWN; + MojoHandleSignalsState wait_state_ = {0, 0}; + + DISALLOW_COPY_AND_ASSIGN(Context); + }; + + scoped_refptr<Context> context_; + + DISALLOW_COPY_AND_ASSIGN(Waiter); +}; + +} // namespace #if defined(OS_MACOSX) && !defined(OS_IOS) namespace { @@ -130,9 +230,7 @@ MojoHandle mp, MojoHandle* handles, uint32_t expected_num_handles) { - CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, - nullptr), - MOJO_RESULT_OK); + CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK); uint32_t message_size = 0; uint32_t num_handles = 0; @@ -154,9 +252,7 @@ // static std::string MojoTestBase::ReadMessageWithOptionalHandle(MojoHandle mp, MojoHandle* handle) { - CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, - nullptr), - MOJO_RESULT_OK); + CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK); uint32_t message_size = 0; uint32_t num_handles = 0; @@ -191,9 +287,7 @@ void MojoTestBase::ReadMessage(MojoHandle mp, char* data, size_t num_bytes) { - CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, - nullptr), - MOJO_RESULT_OK); + CHECK_EQ(WaitForSignals(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK); uint32_t message_size = 0; uint32_t num_handles = 0; @@ -288,8 +382,7 @@ // static void MojoTestBase::WriteData(MojoHandle producer, const std::string& data) { - CHECK_EQ(MojoWait(producer, MOJO_HANDLE_SIGNAL_WRITABLE, - MOJO_DEADLINE_INDEFINITE, nullptr), + CHECK_EQ(WaitForSignals(producer, MOJO_HANDLE_SIGNAL_WRITABLE), MOJO_RESULT_OK); uint32_t num_bytes = static_cast<uint32_t>(data.size()); CHECK_EQ(MojoWriteData(producer, data.data(), &num_bytes, @@ -300,8 +393,7 @@ // static std::string MojoTestBase::ReadData(MojoHandle consumer, size_t size) { - CHECK_EQ(MojoWait(consumer, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, nullptr), + CHECK_EQ(WaitForSignals(consumer, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK); std::vector<char> buffer(size); uint32_t num_bytes = static_cast<uint32_t>(size); @@ -313,6 +405,21 @@ return std::string(buffer.data(), buffer.size()); } +// static +MojoHandleSignalsState MojoTestBase::GetSignalsState(MojoHandle handle) { + MojoHandleSignalsState signals_state; + CHECK_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(handle, &signals_state)); + return signals_state; +} + +// static +MojoResult MojoTestBase::WaitForSignals(MojoHandle handle, + MojoHandleSignals signals, + MojoHandleSignalsState* state) { + Waiter waiter; + return waiter.Wait(handle, signals, state); +} + } // namespace test } // namespace edk } // namespace mojo
diff --git a/mojo/edk/test/mojo_test_base.h b/mojo/edk/test/mojo_test_base.h index fa5b64ce..35e2c2b1 100644 --- a/mojo/edk/test/mojo_test_base.h +++ b/mojo/edk/test/mojo_test_base.h
@@ -30,8 +30,6 @@ ~MojoTestBase() override; using LaunchType = MultiprocessTestHelper::LaunchType; - - protected: using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>; class ClientController { @@ -152,6 +150,14 @@ // Reads data from a data pipe. static std::string ReadData(MojoHandle consumer, size_t size); + // Queries the signals state of |handle|. + static MojoHandleSignalsState GetSignalsState(MojoHandle handle); + + // Helper to block the calling thread waiting for signals to be raised. + static MojoResult WaitForSignals(MojoHandle handle, + MojoHandleSignals signals, + MojoHandleSignalsState* state = nullptr); + void set_launch_type(LaunchType launch_type) { launch_type_ = launch_type; } private:
diff --git a/mojo/public/c/system/functions.h b/mojo/public/c/system/functions.h index 750a29ee..4dad70b 100644 --- a/mojo/public/c/system/functions.h +++ b/mojo/public/c/system/functions.h
@@ -46,6 +46,21 @@ // fail with |MOJO_RESULT_INVALID_ARGUMENT| if they happen after. MOJO_SYSTEM_EXPORT MojoResult MojoClose(MojoHandle handle); +// Queries the last known signals state of a handle. +// +// Note that no guarantees can be made about the accuracy of the returned +// signals state by the time this returns, as other threads in the system may +// change the handle's state at any time. Use with appropriate discretion. +// +// Returns: +// |MOJO_RESULT_OK| on success. |*signals_state| is populated with the +// last known signals state of |handle|. +// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle or +// |signals_state| is null. +MOJO_SYSTEM_EXPORT MojoResult +MojoQueryHandleSignalsState(MojoHandle handle, + struct MojoHandleSignalsState* signals_state); + // Waits on the given handle until one of the following happens: // - A signal indicated by |signals| is satisfied. // - It becomes known that no signal indicated by |signals| will ever be
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc index 8a54380..ada99d4b 100644 --- a/mojo/public/c/system/tests/core_unittest.cc +++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -41,9 +41,9 @@ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(MOJO_HANDLE_INVALID)); // Wait: - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 1000000, - nullptr)); + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoWait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, 0, nullptr)); h0 = MOJO_HANDLE_INVALID; sig = ~MOJO_HANDLE_SIGNAL_NONE; @@ -98,16 +98,9 @@ EXPECT_NE(h0, MOJO_HANDLE_INVALID); EXPECT_NE(h1, MOJO_HANDLE_INVALID); - // Shouldn't be readable, we haven't written anything. + // Shouldn't be readable, we haven't written anything. Should be writable. MojoHandleSignalsState state; - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 0, &state)); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); - EXPECT_EQ(kSignalAll, state.satisfiable_signals); - - // Should be writable. - EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(h0, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state)); + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(h0, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); EXPECT_EQ(kSignalAll, state.satisfiable_signals); @@ -147,9 +140,7 @@ EXPECT_STREQ(kHello, buffer); // |h0| should no longer be readable. - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE, 10, &state)); - + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(h0, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); EXPECT_EQ(kSignalAll, state.satisfiable_signals); @@ -164,7 +155,7 @@ EXPECT_EQ( MOJO_RESULT_FAILED_PRECONDITION, MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - 1000, &state)); + MOJO_DEADLINE_INDEFINITE, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); @@ -188,18 +179,14 @@ // The consumer |hc| shouldn't be readable. MojoHandleSignalsState state; - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0, &state)); - + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(hc, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfiable_signals); // The producer |hp| should be writable. - EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(hp, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &state)); - + EXPECT_EQ(MOJO_RESULT_OK, MojoQueryHandleSignalsState(hp, &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, state.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); @@ -276,7 +263,8 @@ // |hc| should no longer be readable. EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 1000, &state)); + MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, + &state)); EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals);
diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c index fa3caa5..890bcfac 100644 --- a/mojo/public/c/system/tests/core_unittest_pure_c.c +++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -54,6 +54,9 @@ EXPECT_NE(MOJO_RESULT_OK, MojoClose(handle0)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + MojoQueryHandleSignalsState(handle0, NULL)); + + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE, NULL));
diff --git a/mojo/public/c/system/thunks.cc b/mojo/public/c/system/thunks.cc index 1e92954..877ab8f 100644 --- a/mojo/public/c/system/thunks.cc +++ b/mojo/public/c/system/thunks.cc
@@ -22,6 +22,13 @@ return g_thunks.Close(handle); } +MojoResult MojoQueryHandleSignalsState( + MojoHandle handle, + struct MojoHandleSignalsState* signals_state) { + assert(g_thunks.QueryHandleSignalsState); + return g_thunks.QueryHandleSignalsState(handle, signals_state); +} + MojoResult MojoWait(MojoHandle handle, MojoHandleSignals signals, MojoDeadline deadline,
diff --git a/mojo/public/c/system/thunks.h b/mojo/public/c/system/thunks.h index 31d2289..70417b8 100644 --- a/mojo/public/c/system/thunks.h +++ b/mojo/public/c/system/thunks.h
@@ -22,6 +22,9 @@ size_t size; // Should be set to sizeof(MojoSystemThunks). MojoTimeTicks (*GetTimeTicksNow)(); MojoResult (*Close)(MojoHandle handle); + MojoResult (*QueryHandleSignalsState)( + MojoHandle handle, + struct MojoHandleSignalsState* signals_state); MojoResult (*Wait)(MojoHandle handle, MojoHandleSignals signals, MojoDeadline deadline,
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn index 2fc9075..81654d0 100644 --- a/mojo/public/cpp/system/BUILD.gn +++ b/mojo/public/cpp/system/BUILD.gn
@@ -29,6 +29,7 @@ "data_pipe.h", "functions.h", "handle.h", + "handle_signals_state.h", "message.h", "message_pipe.h", "platform_handle.cc",
diff --git a/mojo/public/cpp/system/handle.h b/mojo/public/cpp/system/handle.h index 5b2eb7bb..3f949e7 100644 --- a/mojo/public/cpp/system/handle.h +++ b/mojo/public/cpp/system/handle.h
@@ -13,6 +13,7 @@ #include "base/macros.h" #include "mojo/public/c/system/functions.h" #include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/handle_signals_state.h" namespace mojo { @@ -170,6 +171,14 @@ DCHECK_EQ(MOJO_RESULT_OK, result); } + HandleSignalsState QuerySignalsState() const { + HandleSignalsState signals_state; + MojoResult result = MojoQueryHandleSignalsState( + value_, static_cast<MojoHandleSignalsState*>(&signals_state)); + DCHECK_EQ(MOJO_RESULT_OK, result); + return signals_state; + } + private: MojoHandle value_;
diff --git a/mojo/public/cpp/system/handle_signals_state.h b/mojo/public/cpp/system/handle_signals_state.h new file mode 100644 index 0000000..9e2430f --- /dev/null +++ b/mojo/public/cpp/system/handle_signals_state.h
@@ -0,0 +1,83 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_ + +#include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/system/system_export.h" + +namespace mojo { + +// A convenience wrapper around the MojoHandleSignalsState struct. +struct MOJO_CPP_SYSTEM_EXPORT HandleSignalsState final + : public MojoHandleSignalsState { + HandleSignalsState() { + satisfied_signals = MOJO_HANDLE_SIGNAL_NONE; + satisfiable_signals = MOJO_HANDLE_SIGNAL_NONE; + } + + HandleSignalsState(MojoHandleSignals satisfied, + MojoHandleSignals satisfiable) { + satisfied_signals = satisfied; + satisfiable_signals = satisfiable; + } + + bool operator==(const HandleSignalsState& other) const { + return satisfied_signals == other.satisfied_signals && + satisfiable_signals == other.satisfiable_signals; + } + + // TODO(rockot): Remove this in favor of operator==. + bool equals(const HandleSignalsState& other) const { + return satisfied_signals == other.satisfied_signals && + satisfiable_signals == other.satisfiable_signals; + } + + bool satisfies(MojoHandleSignals signals) const { + return !!(satisfied_signals & signals); + } + + bool can_satisfy(MojoHandleSignals signals) const { + return !!(satisfiable_signals & signals); + } + + // The handle is currently readable. May apply to a message pipe handle or + // data pipe consumer handle. + bool readable() const { return satisfies(MOJO_HANDLE_SIGNAL_READABLE); } + + // The handle is currently writable. May apply to a message pipe handle or + // data pipe producer handle. + bool writable() const { return satisfies(MOJO_HANDLE_SIGNAL_WRITABLE); } + + // The handle's peer is closed. May apply to any message pipe or data pipe + // handle. + bool peer_closed() const { return satisfies(MOJO_HANDLE_SIGNAL_PEER_CLOSED); } + + // The handle will never be |readable()| again. + bool never_readable() const { + return !can_satisfy(MOJO_HANDLE_SIGNAL_READABLE); + } + + // The handle will never be |writable()| again. + bool never_writable() const { + return !can_satisfy(MOJO_HANDLE_SIGNAL_WRITABLE); + } + + // The handle can never indicate |peer_closed()|. Never true for message pipe + // or data pipe handles (they can always signal peer closure), but always true + // for other types of handles (they have no peer.) + bool never_peer_closed() const { + return !can_satisfy(MOJO_HANDLE_SIGNAL_PEER_CLOSED); + } + + // (Copy and assignment allowed.) +}; + +static_assert(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState), + "HandleSignalsState should add no overhead"); + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_SYSTEM_HANDLE_SIGNALS_STATE_H_
diff --git a/mojo/public/cpp/system/tests/BUILD.gn b/mojo/public/cpp/system/tests/BUILD.gn index 3c3d410..c6e05e8 100644 --- a/mojo/public/cpp/system/tests/BUILD.gn +++ b/mojo/public/cpp/system/tests/BUILD.gn
@@ -7,6 +7,7 @@ sources = [ "core_unittest.cc", + "handle_signals_state_unittest.cc", "simple_watcher_unittest.cc", ]
diff --git a/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc b/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc new file mode 100644 index 0000000..82f538e1 --- /dev/null +++ b/mojo/public/cpp/system/tests/handle_signals_state_unittest.cc
@@ -0,0 +1,42 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/system/handle_signals_state.h" + +#include "mojo/public/c/system/types.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +using HandleSignalsStateTest = testing::Test; + +TEST_F(HandleSignalsStateTest, SanityCheck) { + // There's not much to test here. Just a quick sanity check to make sure the + // code compiles and the helper methods do what they're supposed to do. + + HandleSignalsState empty_signals(MOJO_HANDLE_SIGNAL_NONE, + MOJO_HANDLE_SIGNAL_NONE); + EXPECT_FALSE(empty_signals.readable()); + EXPECT_FALSE(empty_signals.writable()); + EXPECT_FALSE(empty_signals.peer_closed()); + EXPECT_TRUE(empty_signals.never_readable()); + EXPECT_TRUE(empty_signals.never_writable()); + EXPECT_TRUE(empty_signals.never_peer_closed()); + + HandleSignalsState all_signals( + MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | + MOJO_HANDLE_SIGNAL_PEER_CLOSED); + EXPECT_TRUE(all_signals.readable()); + EXPECT_TRUE(all_signals.writable()); + EXPECT_TRUE(all_signals.peer_closed()); + EXPECT_FALSE(all_signals.never_readable()); + EXPECT_FALSE(all_signals.never_writable()); + EXPECT_FALSE(all_signals.never_peer_closed()); +} + +} // namespace +} // namespace mojo
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java index e5c6d08..40e4be36 100644 --- a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java +++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -4,8 +4,6 @@ package org.chromium.mojo.system; -import java.util.List; - /** * Core mojo interface giving access to the base operations. See |src/mojo/public/c/system/core.h| * for the underlying api. @@ -129,142 +127,6 @@ } /** - * Result for the |wait| method. - */ - public static class WaitResult { - /** - * The result of the wait method. - * <p> - * |MojoResult.OK| if some signal in |signals| was satisfied (or is already satisfied). - * <p> - * |MojoResult.DEADLINE_EXCEEDED| if the deadline has passed without any of the signals - * being satisfied. - * <p> - * |MojoResult.CANCELLED| if |handle| is closed concurrently by another thread. - * <p> - * |MojoResult.FAILED_PRECONDITION| if it is or becomes impossible that any flag in - * |signals| will ever be satisfied (for example, if the other endpoint is closed). - */ - private int mMojoResult; - - /** - * The signaling state of handles. - */ - private HandleSignalsState mHandleSignalsState; - - /** - * Returns the mojoResult. - */ - public int getMojoResult() { - return mMojoResult; - } - - /** - * @param mojoResult the mojoResult to set - */ - public void setMojoResult(int mojoResult) { - mMojoResult = mojoResult; - } - - /** - * Returns the handleSignalsState. - */ - public HandleSignalsState getHandleSignalsState() { - return mHandleSignalsState; - } - - /** - * @param handleSignalsState the handleSignalsState to set - */ - public void setHandleSignalsState(HandleSignalsState handleSignalsState) { - mHandleSignalsState = handleSignalsState; - } - } - - /** - * Waits on the given |handle| until the state indicated by |signals| is satisfied or until - * |deadline| has passed. - * - * @return a |WaitResult|. - */ - public WaitResult wait(Handle handle, HandleSignals signals, long deadline); - - /** - * Result for the |waitMany| method. - */ - public static class WaitManyResult { - - /** - * See |wait| for the different possible values. - */ - private int mMojoResult; - - /** - * If |mojoResult| is |MojoResult.OK|, |handleIndex| is the index of the handle for which - * some flag was satisfied (or is already satisfied). If |mojoResult| is - * |MojoResult.CANCELLED| or |MojoResult.FAILED_PRECONDITION|, |handleIndex| is the index of - * the handle for which the issue occurred. - */ - private int mHandleIndex; - - /** - * The signaling state of handles. Will not be set if |mojoResult| is - * |MOJO_RESULT_INVALID_ARGUMENT| or |MOJO_RESULT_RESOURCE_EXHAUSTED| - */ - private List<HandleSignalsState> mSignalStates; - - /** - * Returns the mojoResult. - */ - public int getMojoResult() { - return mMojoResult; - } - - /** - * @param mojoResult the mojoResult to set - */ - public void setMojoResult(int mojoResult) { - mMojoResult = mojoResult; - } - - /** - * Returns the handleIndex. - */ - public int getHandleIndex() { - return mHandleIndex; - } - - /** - * @param handleIndex the handleIndex to set - */ - public void setHandleIndex(int handleIndex) { - mHandleIndex = handleIndex; - } - - /** - * Returns the signalStates. - */ - public List<HandleSignalsState> getSignalStates() { - return mSignalStates; - } - - /** - * @param signalStates the signalStates to set - */ - public void setSignalStates(List<HandleSignalsState> signalStates) { - mSignalStates = signalStates; - } - } - - /** - * Waits on handle in |handles| for at least one of them to satisfy the associated - * |HandleSignals|, or until |deadline| has passed. - * - * @returns a |WaitManyResult|. - */ - public WaitManyResult waitMany(List<Pair<Handle, HandleSignals>> handles, long deadline); - - /** * Creates a message pipe, which is a bidirectional communication channel for framed data (i.e., * messages), with the given options. Messages can contain plain data and/or Mojo handles. *
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java index 6181669d..903f36d 100644 --- a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java +++ b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
@@ -4,7 +4,7 @@ package org.chromium.mojo.system; -import org.chromium.mojo.system.Core.WaitResult; +import org.chromium.mojo.system.Core.HandleSignalsState; import java.io.Closeable; @@ -25,9 +25,9 @@ public void close(); /** - * @see Core#wait(Handle, Core.HandleSignals, long) + * @return the last known signaling state of the handle. */ - public WaitResult wait(Core.HandleSignals signals, long deadline); + public HandleSignalsState querySignalsState(); /** * @return whether the handle is valid. A handle is valid until it has been explicitly closed or
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java index 9c20fdd..f8b99c6d 100644 --- a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java +++ b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
@@ -4,8 +4,7 @@ package org.chromium.mojo.system; -import org.chromium.mojo.system.Core.HandleSignals; -import org.chromium.mojo.system.Core.WaitResult; +import org.chromium.mojo.system.Core.HandleSignalsState; import org.chromium.mojo.system.DataPipe.ConsumerHandle; import org.chromium.mojo.system.DataPipe.ProducerHandle; @@ -38,10 +37,10 @@ } /** - * @see Handle#wait(Core.HandleSignals, long) + * @see Handle#querySignalsState() */ @Override - public WaitResult wait(HandleSignals signals, long deadline) { + public HandleSignalsState querySignalsState() { throw new MojoException(MojoResult.INVALID_ARGUMENT); }
diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js index ef480ee..28fdcb77 100644 --- a/mojo/public/js/core.js +++ b/mojo/public/js/core.js
@@ -142,6 +142,18 @@ function close(handle) { [native code] } /** + * Queries the last known signaling state of |handle|. + * + * @param {MojoHandle} handle Handle to query. + * @return {object} An object of the form { + * result, // MOJO_RESULT_OK or MOJO_RESULT_INVALID_ARGUMENT + * satisfiedSignals, // MojoHandleSignals (see above) + * satisfiableSignals, // MojoHandleSignals + * } + */ +function queryHandleSignalsState(handle) { [native code] } + +/** * Waits on the given handle until a signal indicated by |signals| is * satisfied or until |deadline| is passed. See MojoWait for more information. *
diff --git a/mojo/public/js/tests/core_unittest.js b/mojo/public/js/tests/core_unittest.js index 395ed05..7c77713 100644 --- a/mojo/public/js/tests/core_unittest.js +++ b/mojo/public/js/tests/core_unittest.js
@@ -89,35 +89,15 @@ } function testReadAndWriteMessage(pipe) { - var wait = core.waitMany([], [], 0); - expect(wait.result).toBe(core.RESULT_INVALID_ARGUMENT); - expect(wait.index).toBe(null); - expect(wait.signalsState).toBe(null); + var state0 = core.queryHandleSignalsState(pipe.handle0); + expect(state0.result).toBe(core.RESULT_OK); + expect(state0.satisfiedSignals).toBe(core.HANDLE_SIGNAL_WRITABLE); + expect(state0.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_READABLE, 0); - expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED); - expect(wait.signalsState.satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - - wait = core.waitMany( - [pipe.handle0, pipe.handle1], - [core.HANDLE_SIGNAL_READABLE,core.HANDLE_SIGNAL_READABLE], - 0); - expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED); - expect(wait.index).toBe(null); - expect(wait.signalsState[0].satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState[0].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - expect(wait.signalsState[1].satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState[1].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - - wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0); - expect(wait.result).toBe(core.RESULT_OK); - expect(wait.signalsState.satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); + var state1 = core.queryHandleSignalsState(pipe.handle1); + expect(state1.result).toBe(core.RESULT_OK); + expect(state1.satisfiedSignals).toBe(core.HANDLE_SIGNAL_WRITABLE); + expect(state1.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); var senderData = new Uint8Array(42); for (var i = 0; i < senderData.length; ++i) { @@ -130,14 +110,13 @@ expect(result).toBe(core.RESULT_OK); - wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0); - expect(wait.result).toBe(core.RESULT_OK); - expect(wait.signalsState.satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); + state0 = core.queryHandleSignalsState(pipe.handle0); + expect(state0.result).toBe(core.RESULT_OK); + expect(state0.satisfiedSignals).toBe(core.HANDLE_SIGNAL_WRITABLE); + expect(state0.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - wait = core.wait(pipe.handle1, core.HANDLE_SIGNAL_READABLE, - core.DEADLINE_INDEFINITE); + var wait = core.wait(pipe.handle1, core.HANDLE_SIGNAL_READABLE, + core.DEADLINE_INDEFINITE); expect(wait.result).toBe(core.RESULT_OK); expect(wait.signalsState.satisfiedSignals).toBe(HANDLE_SIGNAL_READWRITABLE); expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni index 831157f..5a1cf2a 100644 --- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni +++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -21,6 +21,7 @@ "//device/gamepad/public/interfaces/typemaps.gni", "//device/generic_sensor/public/interfaces/typemaps.gni", "//device/usb/public/interfaces/typemaps.gni", + "//extensions/common/typemaps.gni", "//gpu/ipc/common/typemaps.gni", "//media/capture/mojo/typemaps.gni", "//media/mojo/interfaces/typemaps.gni",
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn index 291d7ec0..f550f52 100644 --- a/testing/libfuzzer/fuzzers/BUILD.gn +++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -19,16 +19,6 @@ additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ] } -fuzzer_test("brotli_fuzzer") { - sources = [ - "brotli_fuzzer.cc", - ] - deps = [ - "//third_party/brotli:dec", - ] - libfuzzer_options = [ "max_len=1280" ] -} - fuzzer_test("courgette_fuzzer") { sources = [ "courgette_fuzzer.cc",
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window-expected.txt new file mode 100644 index 0000000..d1d429f --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL The form resubmission should be blocked after the redirect assert_unreached: The form submission wasn't blocked. Reached unreachable code +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window.html new file mode 100644 index 0000000..779db7c5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked-in-new-window.html
@@ -0,0 +1,59 @@ +<!DOCTYPE html> +<!-- + TODO(mkwst, arthursonzogni). This test fails. See https://crbug.com/700964 +--> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <meta http-equiv="Content-Security-Policy" content="form-action 127.0.0.1:8000"> +</head> +<body> + <form + action="/resources/redirection-response.php?host=localhost:8000&status=302&target=/security/resources/post-done-to-opener.html" + target="namedWindow" + method="post"> + <input type='submit' id='submit'> + </form> + + <script> + async_test(t => { + // #1 Open a new window with the name matching the form.target attribute + // above. + var namedWindow = window.open('/security/resources/empty.html', 'namedWindow') + + // #2 Wait the window to be loaded. It prevents the document url to still + // be about:blank and to have inherited from its opener's CSP. + t.step_timeout(function() { + window.addEventListener('message', t.step_func(e => { + if (e.source == namedWindow && e.data == "done") + assert_unreached("The form submission wasn't blocked."); + })); + + // The navigation should be blocked, either in the current window 1) or + // in the new window 2). + + // 1) The navigation is blocked in the current window. + window.addEventListener('securitypolicyviolation', t.step_func(e => { + assert_equals(e.effectiveDirective, "form-action"); + assert_equals(e.blockedURI, "localhost:8000/resources/post-done-to-opener.html"); + namedWindow.close(); + t.done(); + })); + + // 2) The navigation is blocked in the new window. + t.step_timeout(t.step_func(() => { + namedWindow.close(); + t.done(); + }), 1000); + + // #3 Make a form submission with a redirect. It should be blocked by + // the form-action directive after the redirect. + document.getElementById('submit').click(); + + }, 1000); + }, "The form resubmission should be blocked after the redirect"); + + </script> +</body> +</html>
diff --git a/third_party/WebKit/Source/bindings/scripts/code_generator_v8.py b/third_party/WebKit/Source/bindings/scripts/code_generator_v8.py index 29feb03..477de5c 100644 --- a/third_party/WebKit/Source/bindings/scripts/code_generator_v8.py +++ b/third_party/WebKit/Source/bindings/scripts/code_generator_v8.py
@@ -287,12 +287,20 @@ def __init__(self, info_provider, cache_dir, output_dir, target_component): CodeGeneratorBase.__init__(self, MODULE_PYNAME, info_provider, cache_dir, output_dir) self.target_component = target_component + # The code below duplicates parts of TypedefResolver. We do not use it + # directly because IdlUnionType is not a type defined in + # idl_definitions.py. What we do instead is to resolve typedefs in + # _generate_container_code() whenever a new union file is generated. + self.typedefs = {} + for name, typedef in self.info_provider.typedefs.iteritems(): + self.typedefs[name] = typedef.idl_type def _generate_container_code(self, union_type): + union_type = union_type.resolve_typedefs(self.typedefs) header_template = self.jinja_env.get_template('union_container.h.tmpl') cpp_template = self.jinja_env.get_template('union_container.cpp.tmpl') template_context = v8_union.container_context( - union_type, self.info_provider.interfaces_info) + union_type, self.info_provider) template_context['header_includes'].append( self.info_provider.include_path_for_export) template_context['header_includes'] = normalize_and_sort_includes(
diff --git a/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_individual.py b/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_individual.py index 3947ff1..e7d4098 100755 --- a/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_individual.py +++ b/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_individual.py
@@ -276,10 +276,6 @@ partial_include_paths = [] if this_include_path: partial_include_paths.append(this_include_path) - for union_type in this_union_types: - name = shorten_union_name(union_type) - partial_include_paths.append( - 'bindings/%s/v8/%s.h' % (component, name)) self.add_paths_to_partials_dict(definition.name, full_path, partial_include_paths) # Collects C++ header paths which should be included from generated # .cpp files. The resulting structure is as follows.
diff --git a/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_overall.py b/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_overall.py index eb412e7..5045270 100755 --- a/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_overall.py +++ b/third_party/WebKit/Source/bindings/scripts/compute_interfaces_info_overall.py
@@ -285,11 +285,6 @@ else: dependencies_other_component_include_paths.append(include_path) - for union_type in interface_info.get('union_types', []): - name = shorten_union_name(union_type) - dependencies_include_paths.append( - 'bindings/%s/v8/%s.h' % (component, name)) - interface_info.update({ 'dependencies_full_paths': dependencies_full_paths, 'dependencies_include_paths': dependencies_include_paths,
diff --git a/third_party/WebKit/Source/bindings/scripts/idl_types.py b/third_party/WebKit/Source/bindings/scripts/idl_types.py index d218ab0..104e020 100644 --- a/third_party/WebKit/Source/bindings/scripts/idl_types.py +++ b/third_party/WebKit/Source/bindings/scripts/idl_types.py
@@ -379,7 +379,7 @@ def resolve_typedefs(self, typedefs): self.member_types = [ - typedefs.get(member_type, member_type) + member_type.resolve_typedefs(typedefs) for member_type in self.member_types] return self
diff --git a/third_party/WebKit/Source/bindings/scripts/idl_types_test.py b/third_party/WebKit/Source/bindings/scripts/idl_types_test.py index c92fb42f..e360d5a 100644 --- a/third_party/WebKit/Source/bindings/scripts/idl_types_test.py +++ b/third_party/WebKit/Source/bindings/scripts/idl_types_test.py
@@ -136,3 +136,39 @@ set([IdlType('Node'), IdlSequenceType(IdlType('long')), IdlType('Event'), IdlType('XMLHttpRequest'), IdlType('DOMString'), IdlSequenceType(IdlUnionType([IdlSequenceType(IdlType('double')), IdlType('NodeList')]))])) + + def test_resolve_typedefs(self): + # This is a simplification of the typedef mechanism to avoid having to + # use idl_definitions and use actual nodes from //tools/idl_parser. + typedefs = { + 'Foo': IdlType('unsigned short'), + 'MyBooleanType': IdlType('boolean'), + 'SomeInterfaceT': IdlType('MyInterface'), + } + + # (long long or MyBooleanType) + union = IdlUnionType([IdlType('long long'), IdlType('MyBooleanType')]).resolve_typedefs(typedefs) + self.assertEqual(union.name, 'LongLongOrBoolean') + self.assertEqual(union.member_types[0].name, 'LongLong') + self.assertEqual(union.member_types[1].name, 'Boolean') + + # (Foo or SomeInterfaceT) + union = IdlUnionType([IdlType('Foo'), IdlType('SomeInterfaceT')]).resolve_typedefs(typedefs) + self.assertEqual(union.name, 'UnsignedShortOrMyInterface') + self.assertEqual(union.member_types[0].name, 'UnsignedShort') + self.assertEqual(union.member_types[1].name, 'MyInterface') + + # (Foo or sequence<(MyBooleanType or double)>) + union = IdlUnionType([ + IdlType('Foo'), + IdlSequenceType(IdlUnionType([IdlType('MyBooleanType'), + IdlType('double')]))]).resolve_typedefs(typedefs) + self.assertEqual(union.name, 'UnsignedShortOrBooleanOrDoubleSequence') + self.assertEqual(union.member_types[0].name, 'UnsignedShort') + self.assertEqual(union.member_types[1].name, 'BooleanOrDoubleSequence') + self.assertEqual(union.member_types[1].element_type.name, 'BooleanOrDouble') + self.assertEqual(union.member_types[1].element_type.member_types[0].name, + 'Boolean') + self.assertEqual(union.member_types[1].element_type.member_types[1].name, + 'Double') + self.assertEqual(2, len(union.flattened_member_types))
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_union.py b/third_party/WebKit/Source/bindings/scripts/v8_union.py index c277fb59..da19308 100644 --- a/third_party/WebKit/Source/bindings/scripts/v8_union.py +++ b/third_party/WebKit/Source/bindings/scripts/v8_union.py
@@ -34,7 +34,7 @@ header_includes = set() -def container_context(union_type, interfaces_info): +def container_context(union_type, info_provider): cpp_includes.clear() header_forward_decls.clear() header_includes.clear() @@ -55,7 +55,7 @@ record_type = None string_type = None for member in sorted(union_type.flattened_member_types, key=lambda m: m.name): - context = member_context(member, interfaces_info) + context = member_context(member, info_provider) members.append(context) if member.base_type == 'ArrayBuffer': if array_buffer_type: @@ -123,7 +123,8 @@ } -def _update_includes_and_forward_decls(member, interface_info): +def _update_includes_and_forward_decls(member, info_provider): + interface_info = info_provider.interfaces_info.get(member.name, None) if interface_info: cpp_includes.update(interface_info.get( 'dependencies_include_paths', [])) @@ -135,16 +136,22 @@ header_forward_decls.add(member.implemented_as) else: if member.is_record_type: - # The headers for both T and U must be present when - # Vector<std::pair<T, U>> is declared. - header_includes.update(member.includes_for_type()) + _update_includes_and_forward_decls(member.key_type, info_provider) + _update_includes_and_forward_decls(member.value_type, info_provider) + elif member.is_array_or_sequence_type: + _update_includes_and_forward_decls(member.element_type, info_provider) else: - cpp_includes.update(member.includes_for_type()) + if member.is_union_type: + # Reaching this block means we have a union that is inside a + # record or sequence. + header_forward_decls.add(member.name) + cpp_includes.update([info_provider.include_path_for_union_types(member)]) + else: + cpp_includes.update(member.includes_for_type()) -def member_context(member, interfaces_info): - interface_info = interfaces_info.get(member.name, None) - _update_includes_and_forward_decls(member, interface_info) +def member_context(member, info_provider): + _update_includes_and_forward_decls(member, info_provider) if member.is_nullable: member = member.inner_type return {
diff --git a/third_party/WebKit/Source/bindings/templates/callback_interface.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/callback_interface.cpp.tmpl index a101dda..420c276 100644 --- a/third_party/WebKit/Source/bindings/templates/callback_interface.cpp.tmpl +++ b/third_party/WebKit/Source/bindings/templates/callback_interface.cpp.tmpl
@@ -30,10 +30,12 @@ {{return_default}}; if (!m_scriptState->contextIsValid()) {{return_default}}; + ScriptState::Scope scope(m_scriptState.get()); {% if method.call_with_this_handle %} v8::Local<v8::Value> thisHandle = thisValue.v8Value(); {% endif %} + {% for argument in method.arguments %} v8::Local<v8::Value> {{argument.handle}} = {{argument.cpp_value_to_v8_value}}; {% endfor %} @@ -44,15 +46,27 @@ v8::Local<v8::Value> *argv = 0; {% endif %} - {% set this_handle_parameter = 'thisHandle, ' if method.call_with_this_handle else 'v8::Undefined(m_scriptState->isolate()), ' %} + v8::Isolate* isolate = m_scriptState->isolate(); + {% set this_handle_parameter = 'thisHandle' + if method.call_with_this_handle else 'v8::Undefined(isolate)' %} {% if method.idl_type == 'boolean' %} - v8::TryCatch exceptionCatcher(m_scriptState->isolate()); + v8::TryCatch exceptionCatcher(isolate); exceptionCatcher.SetVerbose(true); - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), {{this_handle_parameter}}{{method.arguments | length}}, argv, m_scriptState->isolate()); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + executionContext, + {{this_handle_parameter}}, + {{method.arguments | length}}, + argv, + isolate); return !exceptionCatcher.HasCaught(); {% else %}{# void #} - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), {{this_handle_parameter}}{{method.arguments | length}}, argv, m_scriptState->isolate()); - {% endif %} + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + m_scriptState->getExecutionContext(), + {{this_handle_parameter}}, + {{method.arguments | length}}, + argv, + isolate); + {% endif %} } {% endfor %}
diff --git a/third_party/WebKit/Source/bindings/templates/callback_interface.h.tmpl b/third_party/WebKit/Source/bindings/templates/callback_interface.h.tmpl index 54dbfc1..e8d14121 100644 --- a/third_party/WebKit/Source/bindings/templates/callback_interface.h.tmpl +++ b/third_party/WebKit/Source/bindings/templates/callback_interface.h.tmpl
@@ -23,6 +23,7 @@ {% for method in methods %} {{method.cpp_type}} {{method.name}}({{method.argument_declarations | join(', ')}}) override; {% endfor %} + private: {{exported}}{{v8_class}}(v8::Local<v8::Function>, ScriptState*);
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl index 0a188884..ef58931 100644 --- a/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl +++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestDictionary.idl
@@ -39,4 +39,5 @@ [PrefixGet] object prefixGetMember; record<ByteString, byte> recordMember; record<USVString, TestObject> garbageCollectedRecordMember; + (Float or BooleanType) unionWithTypedefs; };
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl index 1022ee7..4e24d28 100644 --- a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl +++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
@@ -331,6 +331,10 @@ void voidMethodDoubleOrNullOrDOMStringArg((double? or DOMString) arg); void voidMethodDOMStringOrArrayBufferOrArrayBufferViewArg((DOMString or ArrayBuffer or ArrayBufferView) arg); void voidMethodArrayBufferOrArrayBufferViewOrDictionaryArg((ArrayBuffer or ArrayBufferView or Dictionary) arg); + // Unions with types that need extra includes/forward declarations in container types + void voidMethodBooleanOrElementSequenceArg((BooleanType or sequence<Element>) arg); + void voidMethodDoubleOrLongOrBooleanSequenceArg((double or sequence<(long or boolean)>) arg); + void voidMethodElementSequenceOrByteStringDoubleOrStringRecord((sequence<Element> or record<ByteString, (double or DOMString)>) arg); // Array of Union types void voidMethodArrayOfDoubleOrDOMStringArg((double or DOMString)... arg); // Currently only used on interface type arguments
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl index 3eca6f3..f82283f6 100644 --- a/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl +++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestTypedefs.idl
@@ -32,6 +32,7 @@ // as typedefs are resolved during IR construction (after parsing, // before code generation), and thus the code generator never sees them. +typedef boolean BooleanType; typedef float Float; typedef unsigned long long ULongLong; typedef TestInterfaceEmpty TestInterfaceEmptyType; @@ -46,6 +47,7 @@ typedef record<USVString, TestObject> RecordWithOilpanValueType; typedef (sequence<sequence<ByteString>> or record<ByteString, ByteString>) UnionWithRecord; typedef (Node or (sequence<long> or Event) or (XMLHttpRequest or DOMString)? or record<DOMString, (ByteString or NodeList)>) NestedUnionType; +typedef (ULongLong or (BooleanType or TestCallbackInterfaceType)) UnionWithTypedef; [ Constructor(String stringArg), @@ -72,4 +74,6 @@ record<DOMString, boolean> methodThatReturnsRecord(); void voidMethodNestedUnionType(NestedUnionType arg); + + void voidMethodUnionWithTypedef(UnionWithTypedef arg); };
diff --git a/third_party/WebKit/Source/bindings/tests/idls/modules/TestInterfacePartial3.idl b/third_party/WebKit/Source/bindings/tests/idls/modules/TestInterfacePartial3.idl index 946f9cf0..9501a5d 100644 --- a/third_party/WebKit/Source/bindings/tests/idls/modules/TestInterfacePartial3.idl +++ b/third_party/WebKit/Source/bindings/tests/idls/modules/TestInterfacePartial3.idl
@@ -45,4 +45,7 @@ static void partial2StaticVoidMethod(DOMString value); [Unscopable] void unscopableVoidMethod(); + + // UnionWithTypedef is a typedef declared in core/TestTypedefs.idl. + UnionWithTypedef unionWithTypedefMethod(); };
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.cpp b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.cpp new file mode 100644 index 0000000..c94eb1a1 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.cpp
@@ -0,0 +1,116 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl + +// clang-format off +#include "BooleanOrElementSequence.h" + +#include "bindings/core/v8/IDLTypes.h" +#include "bindings/core/v8/NativeValueTraitsImpl.h" +#include "bindings/core/v8/ToV8.h" +#include "bindings/core/v8/V8Element.h" +#include "core/animation/ElementAnimation.h" +#include "core/dom/ChildNode.h" +#include "core/dom/ElementFullscreen.h" +#include "core/dom/NonDocumentTypeChildNode.h" +#include "core/dom/ParentNode.h" + +namespace blink { + +BooleanOrElementSequence::BooleanOrElementSequence() : m_type(SpecificTypeNone) {} + +bool BooleanOrElementSequence::getAsBoolean() const { + DCHECK(isBoolean()); + return m_boolean; +} + +void BooleanOrElementSequence::setBoolean(bool value) { + DCHECK(isNull()); + m_boolean = value; + m_type = SpecificTypeBoolean; +} + +BooleanOrElementSequence BooleanOrElementSequence::fromBoolean(bool value) { + BooleanOrElementSequence container; + container.setBoolean(value); + return container; +} + +const HeapVector<Member<Element>>& BooleanOrElementSequence::getAsElementSequence() const { + DCHECK(isElementSequence()); + return m_elementSequence; +} + +void BooleanOrElementSequence::setElementSequence(const HeapVector<Member<Element>>& value) { + DCHECK(isNull()); + m_elementSequence = value; + m_type = SpecificTypeElementSequence; +} + +BooleanOrElementSequence BooleanOrElementSequence::fromElementSequence(const HeapVector<Member<Element>>& value) { + BooleanOrElementSequence container; + container.setElementSequence(value); + return container; +} + +BooleanOrElementSequence::BooleanOrElementSequence(const BooleanOrElementSequence&) = default; +BooleanOrElementSequence::~BooleanOrElementSequence() = default; +BooleanOrElementSequence& BooleanOrElementSequence::operator=(const BooleanOrElementSequence&) = default; + +DEFINE_TRACE(BooleanOrElementSequence) { + visitor->trace(m_elementSequence); +} + +void V8BooleanOrElementSequence::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, BooleanOrElementSequence& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) { + if (v8Value.IsEmpty()) + return; + + if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value)) + return; + + if (v8Value->IsArray()) { + HeapVector<Member<Element>> cppValue = toMemberNativeArray<Element>(v8Value, 0, isolate, exceptionState); + if (exceptionState.hadException()) + return; + impl.setElementSequence(cppValue); + return; + } + + if (v8Value->IsBoolean()) { + impl.setBoolean(v8Value.As<v8::Boolean>()->Value()); + return; + } + + { + impl.setBoolean(v8Value->BooleanValue()); + return; + } +} + +v8::Local<v8::Value> ToV8(const BooleanOrElementSequence& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) { + switch (impl.m_type) { + case BooleanOrElementSequence::SpecificTypeNone: + return v8::Null(isolate); + case BooleanOrElementSequence::SpecificTypeBoolean: + return v8Boolean(impl.getAsBoolean(), isolate); + case BooleanOrElementSequence::SpecificTypeElementSequence: + return ToV8(impl.getAsElementSequence(), creationContext, isolate); + default: + NOTREACHED(); + } + return v8::Local<v8::Value>(); +} + +BooleanOrElementSequence NativeValueTraits<BooleanOrElementSequence>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) { + BooleanOrElementSequence impl; + V8BooleanOrElementSequence::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState); + return impl; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.h b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.h new file mode 100644 index 0000000..fa3b024 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrElementSequence.h
@@ -0,0 +1,96 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl + +// clang-format off +#ifndef BooleanOrElementSequence_h +#define BooleanOrElementSequence_h + +#include "bindings/core/v8/Dictionary.h" +#include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/NativeValueTraits.h" +#include "bindings/core/v8/V8Binding.h" +#include "core/CoreExport.h" +#include "platform/heap/Handle.h" + +namespace blink { + +class Element; + +class CORE_EXPORT BooleanOrElementSequence final { + DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); + public: + BooleanOrElementSequence(); + bool isNull() const { return m_type == SpecificTypeNone; } + + bool isBoolean() const { return m_type == SpecificTypeBoolean; } + bool getAsBoolean() const; + void setBoolean(bool); + static BooleanOrElementSequence fromBoolean(bool); + + bool isElementSequence() const { return m_type == SpecificTypeElementSequence; } + const HeapVector<Member<Element>>& getAsElementSequence() const; + void setElementSequence(const HeapVector<Member<Element>>&); + static BooleanOrElementSequence fromElementSequence(const HeapVector<Member<Element>>&); + + BooleanOrElementSequence(const BooleanOrElementSequence&); + ~BooleanOrElementSequence(); + BooleanOrElementSequence& operator=(const BooleanOrElementSequence&); + DECLARE_TRACE(); + + private: + enum SpecificTypes { + SpecificTypeNone, + SpecificTypeBoolean, + SpecificTypeElementSequence, + }; + SpecificTypes m_type; + + bool m_boolean; + HeapVector<Member<Element>> m_elementSequence; + + friend CORE_EXPORT v8::Local<v8::Value> ToV8(const BooleanOrElementSequence&, v8::Local<v8::Object>, v8::Isolate*); +}; + +class V8BooleanOrElementSequence final { + public: + CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, BooleanOrElementSequence&, UnionTypeConversionMode, ExceptionState&); +}; + +CORE_EXPORT v8::Local<v8::Value> ToV8(const BooleanOrElementSequence&, v8::Local<v8::Object>, v8::Isolate*); + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, BooleanOrElementSequence& impl) { + v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate())); +} + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, BooleanOrElementSequence& impl, v8::Local<v8::Object> creationContext) { + v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate())); +} + +template <> +struct NativeValueTraits<BooleanOrElementSequence> : public NativeValueTraitsBase<BooleanOrElementSequence> { + CORE_EXPORT static BooleanOrElementSequence nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&); +}; + +template <> +struct V8TypeOf<BooleanOrElementSequence> { + typedef V8BooleanOrElementSequence Type; +}; + +} // namespace blink + +// We need to set canInitializeWithMemset=true because HeapVector supports +// items that can initialize with memset or have a vtable. It is safe to +// set canInitializeWithMemset=true for a union type object in practice. +// See https://codereview.chromium.org/1118993002/#msg5 for more details. +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::BooleanOrElementSequence); + +#endif // BooleanOrElementSequence_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.cpp new file mode 100644 index 0000000..8421b50 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.cpp
@@ -0,0 +1,109 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl + +// clang-format off +#include "BooleanOrTestCallbackInterface.h" + +#include "bindings/core/v8/IDLTypes.h" +#include "bindings/core/v8/NativeValueTraitsImpl.h" +#include "bindings/core/v8/ToV8.h" +#include "bindings/core/v8/V8TestCallbackInterface.h" + +namespace blink { + +BooleanOrTestCallbackInterface::BooleanOrTestCallbackInterface() : m_type(SpecificTypeNone) {} + +bool BooleanOrTestCallbackInterface::getAsBoolean() const { + DCHECK(isBoolean()); + return m_boolean; +} + +void BooleanOrTestCallbackInterface::setBoolean(bool value) { + DCHECK(isNull()); + m_boolean = value; + m_type = SpecificTypeBoolean; +} + +BooleanOrTestCallbackInterface BooleanOrTestCallbackInterface::fromBoolean(bool value) { + BooleanOrTestCallbackInterface container; + container.setBoolean(value); + return container; +} + +TestCallbackInterface* BooleanOrTestCallbackInterface::getAsTestCallbackInterface() const { + DCHECK(isTestCallbackInterface()); + return m_testCallbackInterface; +} + +void BooleanOrTestCallbackInterface::setTestCallbackInterface(TestCallbackInterface* value) { + DCHECK(isNull()); + m_testCallbackInterface = value; + m_type = SpecificTypeTestCallbackInterface; +} + +BooleanOrTestCallbackInterface BooleanOrTestCallbackInterface::fromTestCallbackInterface(TestCallbackInterface* value) { + BooleanOrTestCallbackInterface container; + container.setTestCallbackInterface(value); + return container; +} + +BooleanOrTestCallbackInterface::BooleanOrTestCallbackInterface(const BooleanOrTestCallbackInterface&) = default; +BooleanOrTestCallbackInterface::~BooleanOrTestCallbackInterface() = default; +BooleanOrTestCallbackInterface& BooleanOrTestCallbackInterface::operator=(const BooleanOrTestCallbackInterface&) = default; + +DEFINE_TRACE(BooleanOrTestCallbackInterface) { + visitor->trace(m_testCallbackInterface); +} + +void V8BooleanOrTestCallbackInterface::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, BooleanOrTestCallbackInterface& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) { + if (v8Value.IsEmpty()) + return; + + if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value)) + return; + + if (V8TestCallbackInterface::hasInstance(v8Value, isolate)) { + TestCallbackInterface* cppValue = V8TestCallbackInterface::toImpl(v8::Local<v8::Object>::Cast(v8Value)); + impl.setTestCallbackInterface(cppValue); + return; + } + + if (v8Value->IsBoolean()) { + impl.setBoolean(v8Value.As<v8::Boolean>()->Value()); + return; + } + + { + impl.setBoolean(v8Value->BooleanValue()); + return; + } +} + +v8::Local<v8::Value> ToV8(const BooleanOrTestCallbackInterface& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) { + switch (impl.m_type) { + case BooleanOrTestCallbackInterface::SpecificTypeNone: + return v8::Null(isolate); + case BooleanOrTestCallbackInterface::SpecificTypeBoolean: + return v8Boolean(impl.getAsBoolean(), isolate); + case BooleanOrTestCallbackInterface::SpecificTypeTestCallbackInterface: + return ToV8(impl.getAsTestCallbackInterface(), creationContext, isolate); + default: + NOTREACHED(); + } + return v8::Local<v8::Value>(); +} + +BooleanOrTestCallbackInterface NativeValueTraits<BooleanOrTestCallbackInterface>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) { + BooleanOrTestCallbackInterface impl; + V8BooleanOrTestCallbackInterface::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState); + return impl; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.h b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.h new file mode 100644 index 0000000..e8c3bd36 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/BooleanOrTestCallbackInterface.h
@@ -0,0 +1,96 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl + +// clang-format off +#ifndef BooleanOrTestCallbackInterface_h +#define BooleanOrTestCallbackInterface_h + +#include "bindings/core/v8/Dictionary.h" +#include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/NativeValueTraits.h" +#include "bindings/core/v8/V8Binding.h" +#include "core/CoreExport.h" +#include "platform/heap/Handle.h" + +namespace blink { + +class TestCallbackInterface; + +class CORE_EXPORT BooleanOrTestCallbackInterface final { + DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); + public: + BooleanOrTestCallbackInterface(); + bool isNull() const { return m_type == SpecificTypeNone; } + + bool isBoolean() const { return m_type == SpecificTypeBoolean; } + bool getAsBoolean() const; + void setBoolean(bool); + static BooleanOrTestCallbackInterface fromBoolean(bool); + + bool isTestCallbackInterface() const { return m_type == SpecificTypeTestCallbackInterface; } + TestCallbackInterface* getAsTestCallbackInterface() const; + void setTestCallbackInterface(TestCallbackInterface*); + static BooleanOrTestCallbackInterface fromTestCallbackInterface(TestCallbackInterface*); + + BooleanOrTestCallbackInterface(const BooleanOrTestCallbackInterface&); + ~BooleanOrTestCallbackInterface(); + BooleanOrTestCallbackInterface& operator=(const BooleanOrTestCallbackInterface&); + DECLARE_TRACE(); + + private: + enum SpecificTypes { + SpecificTypeNone, + SpecificTypeBoolean, + SpecificTypeTestCallbackInterface, + }; + SpecificTypes m_type; + + bool m_boolean; + Member<TestCallbackInterface> m_testCallbackInterface; + + friend CORE_EXPORT v8::Local<v8::Value> ToV8(const BooleanOrTestCallbackInterface&, v8::Local<v8::Object>, v8::Isolate*); +}; + +class V8BooleanOrTestCallbackInterface final { + public: + CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, BooleanOrTestCallbackInterface&, UnionTypeConversionMode, ExceptionState&); +}; + +CORE_EXPORT v8::Local<v8::Value> ToV8(const BooleanOrTestCallbackInterface&, v8::Local<v8::Object>, v8::Isolate*); + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, BooleanOrTestCallbackInterface& impl) { + v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate())); +} + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, BooleanOrTestCallbackInterface& impl, v8::Local<v8::Object> creationContext) { + v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate())); +} + +template <> +struct NativeValueTraits<BooleanOrTestCallbackInterface> : public NativeValueTraitsBase<BooleanOrTestCallbackInterface> { + CORE_EXPORT static BooleanOrTestCallbackInterface nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&); +}; + +template <> +struct V8TypeOf<BooleanOrTestCallbackInterface> { + typedef V8BooleanOrTestCallbackInterface Type; +}; + +} // namespace blink + +// We need to set canInitializeWithMemset=true because HeapVector supports +// items that can initialize with memset or have a vtable. It is safe to +// set canInitializeWithMemset=true for a union type object in practice. +// See https://codereview.chromium.org/1118993002/#msg5 for more details. +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::BooleanOrTestCallbackInterface); + +#endif // BooleanOrTestCallbackInterface_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h b/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h index 009bd64..c30afda4 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h +++ b/third_party/WebKit/Source/bindings/tests/results/core/ByteStringSequenceSequenceOrByteStringByteStringRecord.h
@@ -14,9 +14,7 @@ #include "bindings/core/v8/Dictionary.h" #include "bindings/core/v8/ExceptionState.h" -#include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraits.h" -#include "bindings/core/v8/NativeValueTraitsImpl.h" #include "bindings/core/v8/V8Binding.h" #include "core/CoreExport.h" #include "platform/heap/Handle.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.cpp b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.cpp new file mode 100644 index 0000000..cd135c3 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.cpp
@@ -0,0 +1,117 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl + +// clang-format off +#include "DoubleOrLongOrBooleanSequence.h" + +#include "bindings/core/v8/IDLTypes.h" +#include "bindings/core/v8/LongOrBoolean.h" +#include "bindings/core/v8/NativeValueTraitsImpl.h" +#include "bindings/core/v8/ToV8.h" + +namespace blink { + +DoubleOrLongOrBooleanSequence::DoubleOrLongOrBooleanSequence() : m_type(SpecificTypeNone) {} + +double DoubleOrLongOrBooleanSequence::getAsDouble() const { + DCHECK(isDouble()); + return m_double; +} + +void DoubleOrLongOrBooleanSequence::setDouble(double value) { + DCHECK(isNull()); + m_double = value; + m_type = SpecificTypeDouble; +} + +DoubleOrLongOrBooleanSequence DoubleOrLongOrBooleanSequence::fromDouble(double value) { + DoubleOrLongOrBooleanSequence container; + container.setDouble(value); + return container; +} + +const HeapVector<LongOrBoolean>& DoubleOrLongOrBooleanSequence::getAsLongOrBooleanSequence() const { + DCHECK(isLongOrBooleanSequence()); + return m_longOrBooleanSequence; +} + +void DoubleOrLongOrBooleanSequence::setLongOrBooleanSequence(const HeapVector<LongOrBoolean>& value) { + DCHECK(isNull()); + m_longOrBooleanSequence = value; + m_type = SpecificTypeLongOrBooleanSequence; +} + +DoubleOrLongOrBooleanSequence DoubleOrLongOrBooleanSequence::fromLongOrBooleanSequence(const HeapVector<LongOrBoolean>& value) { + DoubleOrLongOrBooleanSequence container; + container.setLongOrBooleanSequence(value); + return container; +} + +DoubleOrLongOrBooleanSequence::DoubleOrLongOrBooleanSequence(const DoubleOrLongOrBooleanSequence&) = default; +DoubleOrLongOrBooleanSequence::~DoubleOrLongOrBooleanSequence() = default; +DoubleOrLongOrBooleanSequence& DoubleOrLongOrBooleanSequence::operator=(const DoubleOrLongOrBooleanSequence&) = default; + +DEFINE_TRACE(DoubleOrLongOrBooleanSequence) { + visitor->trace(m_longOrBooleanSequence); +} + +void V8DoubleOrLongOrBooleanSequence::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, DoubleOrLongOrBooleanSequence& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) { + if (v8Value.IsEmpty()) + return; + + if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value)) + return; + + if (v8Value->IsArray()) { + HeapVector<LongOrBoolean> cppValue = toImplArray<HeapVector<LongOrBoolean>>(v8Value, 0, isolate, exceptionState); + if (exceptionState.hadException()) + return; + impl.setLongOrBooleanSequence(cppValue); + return; + } + + if (v8Value->IsNumber()) { + double cppValue = NativeValueTraits<IDLDouble>::nativeValue(isolate, v8Value, exceptionState); + if (exceptionState.hadException()) + return; + impl.setDouble(cppValue); + return; + } + + { + double cppValue = NativeValueTraits<IDLDouble>::nativeValue(isolate, v8Value, exceptionState); + if (exceptionState.hadException()) + return; + impl.setDouble(cppValue); + return; + } +} + +v8::Local<v8::Value> ToV8(const DoubleOrLongOrBooleanSequence& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) { + switch (impl.m_type) { + case DoubleOrLongOrBooleanSequence::SpecificTypeNone: + return v8::Null(isolate); + case DoubleOrLongOrBooleanSequence::SpecificTypeDouble: + return v8::Number::New(isolate, impl.getAsDouble()); + case DoubleOrLongOrBooleanSequence::SpecificTypeLongOrBooleanSequence: + return ToV8(impl.getAsLongOrBooleanSequence(), creationContext, isolate); + default: + NOTREACHED(); + } + return v8::Local<v8::Value>(); +} + +DoubleOrLongOrBooleanSequence NativeValueTraits<DoubleOrLongOrBooleanSequence>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) { + DoubleOrLongOrBooleanSequence impl; + V8DoubleOrLongOrBooleanSequence::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState); + return impl; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.h b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.h new file mode 100644 index 0000000..8ed5bc33 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrLongOrBooleanSequence.h
@@ -0,0 +1,96 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl + +// clang-format off +#ifndef DoubleOrLongOrBooleanSequence_h +#define DoubleOrLongOrBooleanSequence_h + +#include "bindings/core/v8/Dictionary.h" +#include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/NativeValueTraits.h" +#include "bindings/core/v8/V8Binding.h" +#include "core/CoreExport.h" +#include "platform/heap/Handle.h" + +namespace blink { + +class LongOrBoolean; + +class CORE_EXPORT DoubleOrLongOrBooleanSequence final { + DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); + public: + DoubleOrLongOrBooleanSequence(); + bool isNull() const { return m_type == SpecificTypeNone; } + + bool isDouble() const { return m_type == SpecificTypeDouble; } + double getAsDouble() const; + void setDouble(double); + static DoubleOrLongOrBooleanSequence fromDouble(double); + + bool isLongOrBooleanSequence() const { return m_type == SpecificTypeLongOrBooleanSequence; } + const HeapVector<LongOrBoolean>& getAsLongOrBooleanSequence() const; + void setLongOrBooleanSequence(const HeapVector<LongOrBoolean>&); + static DoubleOrLongOrBooleanSequence fromLongOrBooleanSequence(const HeapVector<LongOrBoolean>&); + + DoubleOrLongOrBooleanSequence(const DoubleOrLongOrBooleanSequence&); + ~DoubleOrLongOrBooleanSequence(); + DoubleOrLongOrBooleanSequence& operator=(const DoubleOrLongOrBooleanSequence&); + DECLARE_TRACE(); + + private: + enum SpecificTypes { + SpecificTypeNone, + SpecificTypeDouble, + SpecificTypeLongOrBooleanSequence, + }; + SpecificTypes m_type; + + double m_double; + HeapVector<LongOrBoolean> m_longOrBooleanSequence; + + friend CORE_EXPORT v8::Local<v8::Value> ToV8(const DoubleOrLongOrBooleanSequence&, v8::Local<v8::Object>, v8::Isolate*); +}; + +class V8DoubleOrLongOrBooleanSequence final { + public: + CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, DoubleOrLongOrBooleanSequence&, UnionTypeConversionMode, ExceptionState&); +}; + +CORE_EXPORT v8::Local<v8::Value> ToV8(const DoubleOrLongOrBooleanSequence&, v8::Local<v8::Object>, v8::Isolate*); + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, DoubleOrLongOrBooleanSequence& impl) { + v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate())); +} + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, DoubleOrLongOrBooleanSequence& impl, v8::Local<v8::Object> creationContext) { + v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate())); +} + +template <> +struct NativeValueTraits<DoubleOrLongOrBooleanSequence> : public NativeValueTraitsBase<DoubleOrLongOrBooleanSequence> { + CORE_EXPORT static DoubleOrLongOrBooleanSequence nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&); +}; + +template <> +struct V8TypeOf<DoubleOrLongOrBooleanSequence> { + typedef V8DoubleOrLongOrBooleanSequence Type; +}; + +} // namespace blink + +// We need to set canInitializeWithMemset=true because HeapVector supports +// items that can initialize with memset or have a vtable. It is safe to +// set canInitializeWithMemset=true for a union type object in practice. +// See https://codereview.chromium.org/1118993002/#msg5 for more details. +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::DoubleOrLongOrBooleanSequence); + +#endif // DoubleOrLongOrBooleanSequence_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.cpp b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.cpp index d7cc2ad..a6eef7f85 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.cpp
@@ -11,6 +11,7 @@ // clang-format off #include "DoubleOrStringOrDoubleOrStringSequence.h" +#include "bindings/core/v8/DoubleOrString.h" #include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" #include "bindings/core/v8/ToV8.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.h b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.h index 4c725ef..6b723db 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.h +++ b/third_party/WebKit/Source/bindings/tests/results/core/DoubleOrStringOrDoubleOrStringSequence.h
@@ -21,6 +21,8 @@ namespace blink { +class DoubleOrString; + class CORE_EXPORT DoubleOrStringOrDoubleOrStringSequence final { DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); public:
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.cpp b/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.cpp new file mode 100644 index 0000000..e511eaa --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.cpp
@@ -0,0 +1,118 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl + +// clang-format off +#include "ElementSequenceOrByteStringDoubleOrStringRecord.h" + +#include "bindings/core/v8/DoubleOrString.h" +#include "bindings/core/v8/IDLTypes.h" +#include "bindings/core/v8/NativeValueTraitsImpl.h" +#include "bindings/core/v8/ToV8.h" +#include "bindings/core/v8/V8Element.h" +#include "core/animation/ElementAnimation.h" +#include "core/dom/ChildNode.h" +#include "core/dom/ElementFullscreen.h" +#include "core/dom/NonDocumentTypeChildNode.h" +#include "core/dom/ParentNode.h" + +namespace blink { + +ElementSequenceOrByteStringDoubleOrStringRecord::ElementSequenceOrByteStringDoubleOrStringRecord() : m_type(SpecificTypeNone) {} + +const HeapVector<std::pair<String, DoubleOrString>>& ElementSequenceOrByteStringDoubleOrStringRecord::getAsByteStringDoubleOrStringRecord() const { + DCHECK(isByteStringDoubleOrStringRecord()); + return m_byteStringDoubleOrStringRecord; +} + +void ElementSequenceOrByteStringDoubleOrStringRecord::setByteStringDoubleOrStringRecord(const HeapVector<std::pair<String, DoubleOrString>>& value) { + DCHECK(isNull()); + m_byteStringDoubleOrStringRecord = value; + m_type = SpecificTypeByteStringDoubleOrStringRecord; +} + +ElementSequenceOrByteStringDoubleOrStringRecord ElementSequenceOrByteStringDoubleOrStringRecord::fromByteStringDoubleOrStringRecord(const HeapVector<std::pair<String, DoubleOrString>>& value) { + ElementSequenceOrByteStringDoubleOrStringRecord container; + container.setByteStringDoubleOrStringRecord(value); + return container; +} + +const HeapVector<Member<Element>>& ElementSequenceOrByteStringDoubleOrStringRecord::getAsElementSequence() const { + DCHECK(isElementSequence()); + return m_elementSequence; +} + +void ElementSequenceOrByteStringDoubleOrStringRecord::setElementSequence(const HeapVector<Member<Element>>& value) { + DCHECK(isNull()); + m_elementSequence = value; + m_type = SpecificTypeElementSequence; +} + +ElementSequenceOrByteStringDoubleOrStringRecord ElementSequenceOrByteStringDoubleOrStringRecord::fromElementSequence(const HeapVector<Member<Element>>& value) { + ElementSequenceOrByteStringDoubleOrStringRecord container; + container.setElementSequence(value); + return container; +} + +ElementSequenceOrByteStringDoubleOrStringRecord::ElementSequenceOrByteStringDoubleOrStringRecord(const ElementSequenceOrByteStringDoubleOrStringRecord&) = default; +ElementSequenceOrByteStringDoubleOrStringRecord::~ElementSequenceOrByteStringDoubleOrStringRecord() = default; +ElementSequenceOrByteStringDoubleOrStringRecord& ElementSequenceOrByteStringDoubleOrStringRecord::operator=(const ElementSequenceOrByteStringDoubleOrStringRecord&) = default; + +DEFINE_TRACE(ElementSequenceOrByteStringDoubleOrStringRecord) { + visitor->trace(m_byteStringDoubleOrStringRecord); + visitor->trace(m_elementSequence); +} + +void V8ElementSequenceOrByteStringDoubleOrStringRecord::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, ElementSequenceOrByteStringDoubleOrStringRecord& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) { + if (v8Value.IsEmpty()) + return; + + if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value)) + return; + + if (v8Value->IsArray()) { + HeapVector<Member<Element>> cppValue = toMemberNativeArray<Element>(v8Value, 0, isolate, exceptionState); + if (exceptionState.hadException()) + return; + impl.setElementSequence(cppValue); + return; + } + + if (v8Value->IsObject()) { + HeapVector<std::pair<String, DoubleOrString>> cppValue = NativeValueTraits<IDLRecord<IDLByteString, DoubleOrString>>::nativeValue(isolate, v8Value, exceptionState); + if (exceptionState.hadException()) + return; + impl.setByteStringDoubleOrStringRecord(cppValue); + return; + } + + exceptionState.throwTypeError("The provided value is not of type '(sequence<Element> or record<ByteString, (double or DOMString)>)'"); +} + +v8::Local<v8::Value> ToV8(const ElementSequenceOrByteStringDoubleOrStringRecord& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) { + switch (impl.m_type) { + case ElementSequenceOrByteStringDoubleOrStringRecord::SpecificTypeNone: + return v8::Null(isolate); + case ElementSequenceOrByteStringDoubleOrStringRecord::SpecificTypeByteStringDoubleOrStringRecord: + return ToV8(impl.getAsByteStringDoubleOrStringRecord(), creationContext, isolate); + case ElementSequenceOrByteStringDoubleOrStringRecord::SpecificTypeElementSequence: + return ToV8(impl.getAsElementSequence(), creationContext, isolate); + default: + NOTREACHED(); + } + return v8::Local<v8::Value>(); +} + +ElementSequenceOrByteStringDoubleOrStringRecord NativeValueTraits<ElementSequenceOrByteStringDoubleOrStringRecord>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) { + ElementSequenceOrByteStringDoubleOrStringRecord impl; + V8ElementSequenceOrByteStringDoubleOrStringRecord::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState); + return impl; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.h b/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.h new file mode 100644 index 0000000..29775c1 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/ElementSequenceOrByteStringDoubleOrStringRecord.h
@@ -0,0 +1,97 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl + +// clang-format off +#ifndef ElementSequenceOrByteStringDoubleOrStringRecord_h +#define ElementSequenceOrByteStringDoubleOrStringRecord_h + +#include "bindings/core/v8/Dictionary.h" +#include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/NativeValueTraits.h" +#include "bindings/core/v8/V8Binding.h" +#include "core/CoreExport.h" +#include "platform/heap/Handle.h" + +namespace blink { + +class DoubleOrString; +class Element; + +class CORE_EXPORT ElementSequenceOrByteStringDoubleOrStringRecord final { + DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); + public: + ElementSequenceOrByteStringDoubleOrStringRecord(); + bool isNull() const { return m_type == SpecificTypeNone; } + + bool isByteStringDoubleOrStringRecord() const { return m_type == SpecificTypeByteStringDoubleOrStringRecord; } + const HeapVector<std::pair<String, DoubleOrString>>& getAsByteStringDoubleOrStringRecord() const; + void setByteStringDoubleOrStringRecord(const HeapVector<std::pair<String, DoubleOrString>>&); + static ElementSequenceOrByteStringDoubleOrStringRecord fromByteStringDoubleOrStringRecord(const HeapVector<std::pair<String, DoubleOrString>>&); + + bool isElementSequence() const { return m_type == SpecificTypeElementSequence; } + const HeapVector<Member<Element>>& getAsElementSequence() const; + void setElementSequence(const HeapVector<Member<Element>>&); + static ElementSequenceOrByteStringDoubleOrStringRecord fromElementSequence(const HeapVector<Member<Element>>&); + + ElementSequenceOrByteStringDoubleOrStringRecord(const ElementSequenceOrByteStringDoubleOrStringRecord&); + ~ElementSequenceOrByteStringDoubleOrStringRecord(); + ElementSequenceOrByteStringDoubleOrStringRecord& operator=(const ElementSequenceOrByteStringDoubleOrStringRecord&); + DECLARE_TRACE(); + + private: + enum SpecificTypes { + SpecificTypeNone, + SpecificTypeByteStringDoubleOrStringRecord, + SpecificTypeElementSequence, + }; + SpecificTypes m_type; + + HeapVector<std::pair<String, DoubleOrString>> m_byteStringDoubleOrStringRecord; + HeapVector<Member<Element>> m_elementSequence; + + friend CORE_EXPORT v8::Local<v8::Value> ToV8(const ElementSequenceOrByteStringDoubleOrStringRecord&, v8::Local<v8::Object>, v8::Isolate*); +}; + +class V8ElementSequenceOrByteStringDoubleOrStringRecord final { + public: + CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, ElementSequenceOrByteStringDoubleOrStringRecord&, UnionTypeConversionMode, ExceptionState&); +}; + +CORE_EXPORT v8::Local<v8::Value> ToV8(const ElementSequenceOrByteStringDoubleOrStringRecord&, v8::Local<v8::Object>, v8::Isolate*); + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ElementSequenceOrByteStringDoubleOrStringRecord& impl) { + v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate())); +} + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ElementSequenceOrByteStringDoubleOrStringRecord& impl, v8::Local<v8::Object> creationContext) { + v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate())); +} + +template <> +struct NativeValueTraits<ElementSequenceOrByteStringDoubleOrStringRecord> : public NativeValueTraitsBase<ElementSequenceOrByteStringDoubleOrStringRecord> { + CORE_EXPORT static ElementSequenceOrByteStringDoubleOrStringRecord nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&); +}; + +template <> +struct V8TypeOf<ElementSequenceOrByteStringDoubleOrStringRecord> { + typedef V8ElementSequenceOrByteStringDoubleOrStringRecord Type; +}; + +} // namespace blink + +// We need to set canInitializeWithMemset=true because HeapVector supports +// items that can initialize with memset or have a vtable. It is safe to +// set canInitializeWithMemset=true for a union type object in practice. +// See https://codereview.chromium.org/1118993002/#msg5 for more details. +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::ElementSequenceOrByteStringDoubleOrStringRecord); + +#endif // ElementSequenceOrByteStringDoubleOrStringRecord_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.cpp b/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.cpp new file mode 100644 index 0000000..b42f06f7 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.cpp
@@ -0,0 +1,112 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl + +// clang-format off +#include "FloatOrBoolean.h" + +#include "bindings/core/v8/IDLTypes.h" +#include "bindings/core/v8/NativeValueTraitsImpl.h" +#include "bindings/core/v8/ToV8.h" + +namespace blink { + +FloatOrBoolean::FloatOrBoolean() : m_type(SpecificTypeNone) {} + +bool FloatOrBoolean::getAsBoolean() const { + DCHECK(isBoolean()); + return m_boolean; +} + +void FloatOrBoolean::setBoolean(bool value) { + DCHECK(isNull()); + m_boolean = value; + m_type = SpecificTypeBoolean; +} + +FloatOrBoolean FloatOrBoolean::fromBoolean(bool value) { + FloatOrBoolean container; + container.setBoolean(value); + return container; +} + +float FloatOrBoolean::getAsFloat() const { + DCHECK(isFloat()); + return m_float; +} + +void FloatOrBoolean::setFloat(float value) { + DCHECK(isNull()); + m_float = value; + m_type = SpecificTypeFloat; +} + +FloatOrBoolean FloatOrBoolean::fromFloat(float value) { + FloatOrBoolean container; + container.setFloat(value); + return container; +} + +FloatOrBoolean::FloatOrBoolean(const FloatOrBoolean&) = default; +FloatOrBoolean::~FloatOrBoolean() = default; +FloatOrBoolean& FloatOrBoolean::operator=(const FloatOrBoolean&) = default; + +DEFINE_TRACE(FloatOrBoolean) { +} + +void V8FloatOrBoolean::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, FloatOrBoolean& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) { + if (v8Value.IsEmpty()) + return; + + if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value)) + return; + + if (v8Value->IsBoolean()) { + impl.setBoolean(v8Value.As<v8::Boolean>()->Value()); + return; + } + + if (v8Value->IsNumber()) { + float cppValue = NativeValueTraits<IDLFloat>::nativeValue(isolate, v8Value, exceptionState); + if (exceptionState.hadException()) + return; + impl.setFloat(cppValue); + return; + } + + { + float cppValue = NativeValueTraits<IDLFloat>::nativeValue(isolate, v8Value, exceptionState); + if (exceptionState.hadException()) + return; + impl.setFloat(cppValue); + return; + } +} + +v8::Local<v8::Value> ToV8(const FloatOrBoolean& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) { + switch (impl.m_type) { + case FloatOrBoolean::SpecificTypeNone: + return v8::Null(isolate); + case FloatOrBoolean::SpecificTypeBoolean: + return v8Boolean(impl.getAsBoolean(), isolate); + case FloatOrBoolean::SpecificTypeFloat: + return v8::Number::New(isolate, impl.getAsFloat()); + default: + NOTREACHED(); + } + return v8::Local<v8::Value>(); +} + +FloatOrBoolean NativeValueTraits<FloatOrBoolean>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) { + FloatOrBoolean impl; + V8FloatOrBoolean::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState); + return impl; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.h b/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.h new file mode 100644 index 0000000..2402414 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/FloatOrBoolean.h
@@ -0,0 +1,94 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl + +// clang-format off +#ifndef FloatOrBoolean_h +#define FloatOrBoolean_h + +#include "bindings/core/v8/Dictionary.h" +#include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/NativeValueTraits.h" +#include "bindings/core/v8/V8Binding.h" +#include "core/CoreExport.h" +#include "platform/heap/Handle.h" + +namespace blink { + +class CORE_EXPORT FloatOrBoolean final { + DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); + public: + FloatOrBoolean(); + bool isNull() const { return m_type == SpecificTypeNone; } + + bool isBoolean() const { return m_type == SpecificTypeBoolean; } + bool getAsBoolean() const; + void setBoolean(bool); + static FloatOrBoolean fromBoolean(bool); + + bool isFloat() const { return m_type == SpecificTypeFloat; } + float getAsFloat() const; + void setFloat(float); + static FloatOrBoolean fromFloat(float); + + FloatOrBoolean(const FloatOrBoolean&); + ~FloatOrBoolean(); + FloatOrBoolean& operator=(const FloatOrBoolean&); + DECLARE_TRACE(); + + private: + enum SpecificTypes { + SpecificTypeNone, + SpecificTypeBoolean, + SpecificTypeFloat, + }; + SpecificTypes m_type; + + bool m_boolean; + float m_float; + + friend CORE_EXPORT v8::Local<v8::Value> ToV8(const FloatOrBoolean&, v8::Local<v8::Object>, v8::Isolate*); +}; + +class V8FloatOrBoolean final { + public: + CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, FloatOrBoolean&, UnionTypeConversionMode, ExceptionState&); +}; + +CORE_EXPORT v8::Local<v8::Value> ToV8(const FloatOrBoolean&, v8::Local<v8::Object>, v8::Isolate*); + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, FloatOrBoolean& impl) { + v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate())); +} + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, FloatOrBoolean& impl, v8::Local<v8::Object> creationContext) { + v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate())); +} + +template <> +struct NativeValueTraits<FloatOrBoolean> : public NativeValueTraitsBase<FloatOrBoolean> { + CORE_EXPORT static FloatOrBoolean nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&); +}; + +template <> +struct V8TypeOf<FloatOrBoolean> { + typedef V8FloatOrBoolean Type; +}; + +} // namespace blink + +// We need to set canInitializeWithMemset=true because HeapVector supports +// items that can initialize with memset or have a vtable. It is safe to +// set canInitializeWithMemset=true for a union type object in practice. +// See https://codereview.chromium.org/1118993002/#msg5 for more details. +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::FloatOrBoolean); + +#endif // FloatOrBoolean_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.cpp b/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.cpp new file mode 100644 index 0000000..efcfd08 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.cpp
@@ -0,0 +1,112 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl + +// clang-format off +#include "LongOrBoolean.h" + +#include "bindings/core/v8/IDLTypes.h" +#include "bindings/core/v8/NativeValueTraitsImpl.h" +#include "bindings/core/v8/ToV8.h" + +namespace blink { + +LongOrBoolean::LongOrBoolean() : m_type(SpecificTypeNone) {} + +bool LongOrBoolean::getAsBoolean() const { + DCHECK(isBoolean()); + return m_boolean; +} + +void LongOrBoolean::setBoolean(bool value) { + DCHECK(isNull()); + m_boolean = value; + m_type = SpecificTypeBoolean; +} + +LongOrBoolean LongOrBoolean::fromBoolean(bool value) { + LongOrBoolean container; + container.setBoolean(value); + return container; +} + +int32_t LongOrBoolean::getAsLong() const { + DCHECK(isLong()); + return m_long; +} + +void LongOrBoolean::setLong(int32_t value) { + DCHECK(isNull()); + m_long = value; + m_type = SpecificTypeLong; +} + +LongOrBoolean LongOrBoolean::fromLong(int32_t value) { + LongOrBoolean container; + container.setLong(value); + return container; +} + +LongOrBoolean::LongOrBoolean(const LongOrBoolean&) = default; +LongOrBoolean::~LongOrBoolean() = default; +LongOrBoolean& LongOrBoolean::operator=(const LongOrBoolean&) = default; + +DEFINE_TRACE(LongOrBoolean) { +} + +void V8LongOrBoolean::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, LongOrBoolean& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) { + if (v8Value.IsEmpty()) + return; + + if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value)) + return; + + if (v8Value->IsBoolean()) { + impl.setBoolean(v8Value.As<v8::Boolean>()->Value()); + return; + } + + if (v8Value->IsNumber()) { + int32_t cppValue = NativeValueTraits<IDLLong>::nativeValue(isolate, v8Value, exceptionState, NormalConversion); + if (exceptionState.hadException()) + return; + impl.setLong(cppValue); + return; + } + + { + int32_t cppValue = NativeValueTraits<IDLLong>::nativeValue(isolate, v8Value, exceptionState, NormalConversion); + if (exceptionState.hadException()) + return; + impl.setLong(cppValue); + return; + } +} + +v8::Local<v8::Value> ToV8(const LongOrBoolean& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) { + switch (impl.m_type) { + case LongOrBoolean::SpecificTypeNone: + return v8::Null(isolate); + case LongOrBoolean::SpecificTypeBoolean: + return v8Boolean(impl.getAsBoolean(), isolate); + case LongOrBoolean::SpecificTypeLong: + return v8::Integer::New(isolate, impl.getAsLong()); + default: + NOTREACHED(); + } + return v8::Local<v8::Value>(); +} + +LongOrBoolean NativeValueTraits<LongOrBoolean>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) { + LongOrBoolean impl; + V8LongOrBoolean::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState); + return impl; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.h b/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.h new file mode 100644 index 0000000..689e050 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/LongOrBoolean.h
@@ -0,0 +1,94 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl + +// clang-format off +#ifndef LongOrBoolean_h +#define LongOrBoolean_h + +#include "bindings/core/v8/Dictionary.h" +#include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/NativeValueTraits.h" +#include "bindings/core/v8/V8Binding.h" +#include "core/CoreExport.h" +#include "platform/heap/Handle.h" + +namespace blink { + +class CORE_EXPORT LongOrBoolean final { + DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); + public: + LongOrBoolean(); + bool isNull() const { return m_type == SpecificTypeNone; } + + bool isBoolean() const { return m_type == SpecificTypeBoolean; } + bool getAsBoolean() const; + void setBoolean(bool); + static LongOrBoolean fromBoolean(bool); + + bool isLong() const { return m_type == SpecificTypeLong; } + int32_t getAsLong() const; + void setLong(int32_t); + static LongOrBoolean fromLong(int32_t); + + LongOrBoolean(const LongOrBoolean&); + ~LongOrBoolean(); + LongOrBoolean& operator=(const LongOrBoolean&); + DECLARE_TRACE(); + + private: + enum SpecificTypes { + SpecificTypeNone, + SpecificTypeBoolean, + SpecificTypeLong, + }; + SpecificTypes m_type; + + bool m_boolean; + int32_t m_long; + + friend CORE_EXPORT v8::Local<v8::Value> ToV8(const LongOrBoolean&, v8::Local<v8::Object>, v8::Isolate*); +}; + +class V8LongOrBoolean final { + public: + CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, LongOrBoolean&, UnionTypeConversionMode, ExceptionState&); +}; + +CORE_EXPORT v8::Local<v8::Value> ToV8(const LongOrBoolean&, v8::Local<v8::Object>, v8::Isolate*); + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, LongOrBoolean& impl) { + v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate())); +} + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, LongOrBoolean& impl, v8::Local<v8::Object> creationContext) { + v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate())); +} + +template <> +struct NativeValueTraits<LongOrBoolean> : public NativeValueTraitsBase<LongOrBoolean> { + CORE_EXPORT static LongOrBoolean nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&); +}; + +template <> +struct V8TypeOf<LongOrBoolean> { + typedef V8LongOrBoolean Type; +}; + +} // namespace blink + +// We need to set canInitializeWithMemset=true because HeapVector supports +// items that can initialize with memset or have a vtable. It is safe to +// set canInitializeWithMemset=true for a union type object in practice. +// See https://codereview.chromium.org/1118993002/#msg5 for more details. +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::LongOrBoolean); + +#endif // LongOrBoolean_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/LongOrTestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/LongOrTestDictionary.cpp index 8a7037d..08e3311 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/LongOrTestDictionary.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/LongOrTestDictionary.cpp
@@ -11,10 +11,8 @@ // clang-format off #include "LongOrTestDictionary.h" -#include "bindings/core/v8/DoubleOrString.h" #include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" -#include "bindings/core/v8/TestInterface2OrUint8Array.h" #include "bindings/core/v8/ToV8.h" namespace blink {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.cpp b/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.cpp index 1e8f7ec..228e52d 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.cpp
@@ -11,7 +11,7 @@ // clang-format off #include "NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h" -#include "bindings/core/v8/ArrayBufferOrArrayBufferViewOrBlobOrDocumentOrStringOrFormDataOrURLSearchParams.h" +#include "bindings/core/v8/ByteStringOrNodeList.h" #include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" #include "bindings/core/v8/ToV8.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h b/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h index 6f9239c..9557458b 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h +++ b/third_party/WebKit/Source/bindings/tests/results/core/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h
@@ -14,20 +14,14 @@ #include "bindings/core/v8/Dictionary.h" #include "bindings/core/v8/ExceptionState.h" -#include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraits.h" -#include "bindings/core/v8/NativeValueTraitsImpl.h" #include "bindings/core/v8/V8Binding.h" -#include "bindings/core/v8/V8NodeList.h" #include "core/CoreExport.h" -#include "core/dom/NameNodeList.h" -#include "core/dom/NodeList.h" -#include "core/dom/StaticNodeList.h" -#include "core/html/LabelsNodeList.h" #include "platform/heap/Handle.h" namespace blink { +class ByteStringOrNodeList; class Event; class Node; class XMLHttpRequest;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp index 75e4953..e8357a8 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.cpp
@@ -11,8 +11,6 @@ // clang-format off #include "TestDictionary.h" -#include "bindings/core/v8/DoubleOrString.h" -#include "bindings/core/v8/TestInterface2OrUint8Array.h" #include "bindings/tests/idls/core/TestInterfaceGarbageCollected.h" #include "bindings/tests/idls/core/TestInterfaceImplementation.h" #include "bindings/tests/idls/core/TestObject.h" @@ -397,6 +395,15 @@ void TestDictionary::setUint8ArrayMember(DOMUint8Array* value) { m_uint8ArrayMember = value; } +bool TestDictionary::hasUnionWithTypedefs() const { + return !m_unionWithTypedefs.isNull(); +} +const FloatOrBoolean& TestDictionary::unionWithTypedefs() const { + return m_unionWithTypedefs; +} +void TestDictionary::setUnionWithTypedefs(const FloatOrBoolean& value) { + m_unionWithTypedefs = value; +} bool TestDictionary::hasUnrestrictedDoubleMember() const { return m_hasUnrestrictedDoubleMember; } @@ -426,6 +433,7 @@ visitor->trace(m_testInterfaceSequenceMember); visitor->trace(m_testObjectSequenceMember); visitor->trace(m_uint8ArrayMember); + visitor->trace(m_unionWithTypedefs); IDLDictionaryBase::trace(visitor); }
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h index 2f925294..e88e977 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h +++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
@@ -14,6 +14,7 @@ #include "bindings/core/v8/Dictionary.h" #include "bindings/core/v8/DoubleOrString.h" +#include "bindings/core/v8/FloatOrBoolean.h" #include "bindings/core/v8/IDLDictionaryBase.h" #include "bindings/core/v8/ScriptValue.h" #include "bindings/core/v8/TestInterface2OrUint8Array.h" @@ -183,6 +184,10 @@ DOMUint8Array* uint8ArrayMember() const; void setUint8ArrayMember(DOMUint8Array*); + bool hasUnionWithTypedefs() const; + const FloatOrBoolean& unionWithTypedefs() const; + void setUnionWithTypedefs(const FloatOrBoolean&); + bool hasUnrestrictedDoubleMember() const; double unrestrictedDoubleMember() const; void setUnrestrictedDoubleMember(double); @@ -241,6 +246,7 @@ bool m_hasTestObjectSequenceMember = false; HeapVector<Member<TestObject>> m_testObjectSequenceMember; Member<DOMUint8Array> m_uint8ArrayMember; + FloatOrBoolean m_unionWithTypedefs; bool m_hasUnrestrictedDoubleMember = false; double m_unrestrictedDoubleMember;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.cpp new file mode 100644 index 0000000..c19e694 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.cpp
@@ -0,0 +1,139 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.cpp.tmpl + +// clang-format off +#include "UnsignedLongLongOrBooleanOrTestCallbackInterface.h" + +#include "bindings/core/v8/IDLTypes.h" +#include "bindings/core/v8/NativeValueTraitsImpl.h" +#include "bindings/core/v8/ToV8.h" +#include "bindings/core/v8/V8TestCallbackInterface.h" + +namespace blink { + +UnsignedLongLongOrBooleanOrTestCallbackInterface::UnsignedLongLongOrBooleanOrTestCallbackInterface() : m_type(SpecificTypeNone) {} + +bool UnsignedLongLongOrBooleanOrTestCallbackInterface::getAsBoolean() const { + DCHECK(isBoolean()); + return m_boolean; +} + +void UnsignedLongLongOrBooleanOrTestCallbackInterface::setBoolean(bool value) { + DCHECK(isNull()); + m_boolean = value; + m_type = SpecificTypeBoolean; +} + +UnsignedLongLongOrBooleanOrTestCallbackInterface UnsignedLongLongOrBooleanOrTestCallbackInterface::fromBoolean(bool value) { + UnsignedLongLongOrBooleanOrTestCallbackInterface container; + container.setBoolean(value); + return container; +} + +TestCallbackInterface* UnsignedLongLongOrBooleanOrTestCallbackInterface::getAsTestCallbackInterface() const { + DCHECK(isTestCallbackInterface()); + return m_testCallbackInterface; +} + +void UnsignedLongLongOrBooleanOrTestCallbackInterface::setTestCallbackInterface(TestCallbackInterface* value) { + DCHECK(isNull()); + m_testCallbackInterface = value; + m_type = SpecificTypeTestCallbackInterface; +} + +UnsignedLongLongOrBooleanOrTestCallbackInterface UnsignedLongLongOrBooleanOrTestCallbackInterface::fromTestCallbackInterface(TestCallbackInterface* value) { + UnsignedLongLongOrBooleanOrTestCallbackInterface container; + container.setTestCallbackInterface(value); + return container; +} + +uint64_t UnsignedLongLongOrBooleanOrTestCallbackInterface::getAsUnsignedLongLong() const { + DCHECK(isUnsignedLongLong()); + return m_unsignedLongLong; +} + +void UnsignedLongLongOrBooleanOrTestCallbackInterface::setUnsignedLongLong(uint64_t value) { + DCHECK(isNull()); + m_unsignedLongLong = value; + m_type = SpecificTypeUnsignedLongLong; +} + +UnsignedLongLongOrBooleanOrTestCallbackInterface UnsignedLongLongOrBooleanOrTestCallbackInterface::fromUnsignedLongLong(uint64_t value) { + UnsignedLongLongOrBooleanOrTestCallbackInterface container; + container.setUnsignedLongLong(value); + return container; +} + +UnsignedLongLongOrBooleanOrTestCallbackInterface::UnsignedLongLongOrBooleanOrTestCallbackInterface(const UnsignedLongLongOrBooleanOrTestCallbackInterface&) = default; +UnsignedLongLongOrBooleanOrTestCallbackInterface::~UnsignedLongLongOrBooleanOrTestCallbackInterface() = default; +UnsignedLongLongOrBooleanOrTestCallbackInterface& UnsignedLongLongOrBooleanOrTestCallbackInterface::operator=(const UnsignedLongLongOrBooleanOrTestCallbackInterface&) = default; + +DEFINE_TRACE(UnsignedLongLongOrBooleanOrTestCallbackInterface) { + visitor->trace(m_testCallbackInterface); +} + +void V8UnsignedLongLongOrBooleanOrTestCallbackInterface::toImpl(v8::Isolate* isolate, v8::Local<v8::Value> v8Value, UnsignedLongLongOrBooleanOrTestCallbackInterface& impl, UnionTypeConversionMode conversionMode, ExceptionState& exceptionState) { + if (v8Value.IsEmpty()) + return; + + if (conversionMode == UnionTypeConversionMode::Nullable && isUndefinedOrNull(v8Value)) + return; + + if (V8TestCallbackInterface::hasInstance(v8Value, isolate)) { + TestCallbackInterface* cppValue = V8TestCallbackInterface::toImpl(v8::Local<v8::Object>::Cast(v8Value)); + impl.setTestCallbackInterface(cppValue); + return; + } + + if (v8Value->IsBoolean()) { + impl.setBoolean(v8Value.As<v8::Boolean>()->Value()); + return; + } + + if (v8Value->IsNumber()) { + uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::nativeValue(isolate, v8Value, exceptionState, NormalConversion); + if (exceptionState.hadException()) + return; + impl.setUnsignedLongLong(cppValue); + return; + } + + { + uint64_t cppValue = NativeValueTraits<IDLUnsignedLongLong>::nativeValue(isolate, v8Value, exceptionState, NormalConversion); + if (exceptionState.hadException()) + return; + impl.setUnsignedLongLong(cppValue); + return; + } +} + +v8::Local<v8::Value> ToV8(const UnsignedLongLongOrBooleanOrTestCallbackInterface& impl, v8::Local<v8::Object> creationContext, v8::Isolate* isolate) { + switch (impl.m_type) { + case UnsignedLongLongOrBooleanOrTestCallbackInterface::SpecificTypeNone: + return v8::Null(isolate); + case UnsignedLongLongOrBooleanOrTestCallbackInterface::SpecificTypeBoolean: + return v8Boolean(impl.getAsBoolean(), isolate); + case UnsignedLongLongOrBooleanOrTestCallbackInterface::SpecificTypeTestCallbackInterface: + return ToV8(impl.getAsTestCallbackInterface(), creationContext, isolate); + case UnsignedLongLongOrBooleanOrTestCallbackInterface::SpecificTypeUnsignedLongLong: + return v8::Number::New(isolate, static_cast<double>(impl.getAsUnsignedLongLong())); + default: + NOTREACHED(); + } + return v8::Local<v8::Value>(); +} + +UnsignedLongLongOrBooleanOrTestCallbackInterface NativeValueTraits<UnsignedLongLongOrBooleanOrTestCallbackInterface>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) { + UnsignedLongLongOrBooleanOrTestCallbackInterface impl; + V8UnsignedLongLongOrBooleanOrTestCallbackInterface::toImpl(isolate, value, impl, UnionTypeConversionMode::NotNullable, exceptionState); + return impl; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.h b/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.h new file mode 100644 index 0000000..8399452 --- /dev/null +++ b/third_party/WebKit/Source/bindings/tests/results/core/UnsignedLongLongOrBooleanOrTestCallbackInterface.h
@@ -0,0 +1,103 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has been auto-generated by code_generator_v8.py. +// DO NOT MODIFY! + +// This file has been generated from the Jinja2 template in +// third_party/WebKit/Source/bindings/templates/union_container.h.tmpl + +// clang-format off +#ifndef UnsignedLongLongOrBooleanOrTestCallbackInterface_h +#define UnsignedLongLongOrBooleanOrTestCallbackInterface_h + +#include "bindings/core/v8/Dictionary.h" +#include "bindings/core/v8/ExceptionState.h" +#include "bindings/core/v8/NativeValueTraits.h" +#include "bindings/core/v8/V8Binding.h" +#include "core/CoreExport.h" +#include "platform/heap/Handle.h" + +namespace blink { + +class TestCallbackInterface; + +class CORE_EXPORT UnsignedLongLongOrBooleanOrTestCallbackInterface final { + DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); + public: + UnsignedLongLongOrBooleanOrTestCallbackInterface(); + bool isNull() const { return m_type == SpecificTypeNone; } + + bool isBoolean() const { return m_type == SpecificTypeBoolean; } + bool getAsBoolean() const; + void setBoolean(bool); + static UnsignedLongLongOrBooleanOrTestCallbackInterface fromBoolean(bool); + + bool isTestCallbackInterface() const { return m_type == SpecificTypeTestCallbackInterface; } + TestCallbackInterface* getAsTestCallbackInterface() const; + void setTestCallbackInterface(TestCallbackInterface*); + static UnsignedLongLongOrBooleanOrTestCallbackInterface fromTestCallbackInterface(TestCallbackInterface*); + + bool isUnsignedLongLong() const { return m_type == SpecificTypeUnsignedLongLong; } + uint64_t getAsUnsignedLongLong() const; + void setUnsignedLongLong(uint64_t); + static UnsignedLongLongOrBooleanOrTestCallbackInterface fromUnsignedLongLong(uint64_t); + + UnsignedLongLongOrBooleanOrTestCallbackInterface(const UnsignedLongLongOrBooleanOrTestCallbackInterface&); + ~UnsignedLongLongOrBooleanOrTestCallbackInterface(); + UnsignedLongLongOrBooleanOrTestCallbackInterface& operator=(const UnsignedLongLongOrBooleanOrTestCallbackInterface&); + DECLARE_TRACE(); + + private: + enum SpecificTypes { + SpecificTypeNone, + SpecificTypeBoolean, + SpecificTypeTestCallbackInterface, + SpecificTypeUnsignedLongLong, + }; + SpecificTypes m_type; + + bool m_boolean; + Member<TestCallbackInterface> m_testCallbackInterface; + uint64_t m_unsignedLongLong; + + friend CORE_EXPORT v8::Local<v8::Value> ToV8(const UnsignedLongLongOrBooleanOrTestCallbackInterface&, v8::Local<v8::Object>, v8::Isolate*); +}; + +class V8UnsignedLongLongOrBooleanOrTestCallbackInterface final { + public: + CORE_EXPORT static void toImpl(v8::Isolate*, v8::Local<v8::Value>, UnsignedLongLongOrBooleanOrTestCallbackInterface&, UnionTypeConversionMode, ExceptionState&); +}; + +CORE_EXPORT v8::Local<v8::Value> ToV8(const UnsignedLongLongOrBooleanOrTestCallbackInterface&, v8::Local<v8::Object>, v8::Isolate*); + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, UnsignedLongLongOrBooleanOrTestCallbackInterface& impl) { + v8SetReturnValue(callbackInfo, ToV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate())); +} + +template <class CallbackInfo> +inline void v8SetReturnValue(const CallbackInfo& callbackInfo, UnsignedLongLongOrBooleanOrTestCallbackInterface& impl, v8::Local<v8::Object> creationContext) { + v8SetReturnValue(callbackInfo, ToV8(impl, creationContext, callbackInfo.GetIsolate())); +} + +template <> +struct NativeValueTraits<UnsignedLongLongOrBooleanOrTestCallbackInterface> : public NativeValueTraitsBase<UnsignedLongLongOrBooleanOrTestCallbackInterface> { + CORE_EXPORT static UnsignedLongLongOrBooleanOrTestCallbackInterface nativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&); +}; + +template <> +struct V8TypeOf<UnsignedLongLongOrBooleanOrTestCallbackInterface> { + typedef V8UnsignedLongLongOrBooleanOrTestCallbackInterface Type; +}; + +} // namespace blink + +// We need to set canInitializeWithMemset=true because HeapVector supports +// items that can initialize with memset or have a vtable. It is safe to +// set canInitializeWithMemset=true for a union type object in practice. +// See https://codereview.chromium.org/1118993002/#msg5 for more details. +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::UnsignedLongLongOrBooleanOrTestCallbackInterface); + +#endif // UnsignedLongLongOrBooleanOrTestCallbackInterface_h
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.cpp index 8c51693..9d785420 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.cpp
@@ -41,10 +41,18 @@ return; if (!m_scriptState->contextIsValid()) return; + ScriptState::Scope scope(m_scriptState.get()); + v8::Local<v8::Value> *argv = 0; - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 0, argv, m_scriptState->isolate()); + v8::Isolate* isolate = m_scriptState->isolate(); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + m_scriptState->getExecutionContext(), + v8::Undefined(isolate), + 0, + argv, + isolate); } bool V8TestCallbackInterface::booleanMethod() { @@ -54,12 +62,20 @@ return true; if (!m_scriptState->contextIsValid()) return true; + ScriptState::Scope scope(m_scriptState.get()); + v8::Local<v8::Value> *argv = 0; - v8::TryCatch exceptionCatcher(m_scriptState->isolate()); + v8::Isolate* isolate = m_scriptState->isolate(); + v8::TryCatch exceptionCatcher(isolate); exceptionCatcher.SetVerbose(true); - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 0, argv, m_scriptState->isolate()); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + executionContext, + v8::Undefined(isolate), + 0, + argv, + isolate); return !exceptionCatcher.HasCaught(); } @@ -70,11 +86,19 @@ return; if (!m_scriptState->contextIsValid()) return; + ScriptState::Scope scope(m_scriptState.get()); + v8::Local<v8::Value> boolArgHandle = v8Boolean(boolArg, m_scriptState->isolate()); v8::Local<v8::Value> argv[] = { boolArgHandle }; - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 1, argv, m_scriptState->isolate()); + v8::Isolate* isolate = m_scriptState->isolate(); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + m_scriptState->getExecutionContext(), + v8::Undefined(isolate), + 1, + argv, + isolate); } void V8TestCallbackInterface::voidMethodSequenceArg(const HeapVector<Member<TestInterfaceEmpty>>& sequenceArg) { @@ -84,11 +108,19 @@ return; if (!m_scriptState->contextIsValid()) return; + ScriptState::Scope scope(m_scriptState.get()); + v8::Local<v8::Value> sequenceArgHandle = ToV8(sequenceArg, m_scriptState->context()->Global(), m_scriptState->isolate()); v8::Local<v8::Value> argv[] = { sequenceArgHandle }; - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 1, argv, m_scriptState->isolate()); + v8::Isolate* isolate = m_scriptState->isolate(); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + m_scriptState->getExecutionContext(), + v8::Undefined(isolate), + 1, + argv, + isolate); } void V8TestCallbackInterface::voidMethodFloatArg(float floatArg) { @@ -98,11 +130,19 @@ return; if (!m_scriptState->contextIsValid()) return; + ScriptState::Scope scope(m_scriptState.get()); + v8::Local<v8::Value> floatArgHandle = v8::Number::New(m_scriptState->isolate(), floatArg); v8::Local<v8::Value> argv[] = { floatArgHandle }; - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 1, argv, m_scriptState->isolate()); + v8::Isolate* isolate = m_scriptState->isolate(); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + m_scriptState->getExecutionContext(), + v8::Undefined(isolate), + 1, + argv, + isolate); } void V8TestCallbackInterface::voidMethodTestInterfaceEmptyArg(TestInterfaceEmpty* testInterfaceEmptyArg) { @@ -112,11 +152,19 @@ return; if (!m_scriptState->contextIsValid()) return; + ScriptState::Scope scope(m_scriptState.get()); + v8::Local<v8::Value> testInterfaceEmptyArgHandle = ToV8(testInterfaceEmptyArg, m_scriptState->context()->Global(), m_scriptState->isolate()); v8::Local<v8::Value> argv[] = { testInterfaceEmptyArgHandle }; - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 1, argv, m_scriptState->isolate()); + v8::Isolate* isolate = m_scriptState->isolate(); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + m_scriptState->getExecutionContext(), + v8::Undefined(isolate), + 1, + argv, + isolate); } void V8TestCallbackInterface::voidMethodTestInterfaceEmptyStringArg(TestInterfaceEmpty* testInterfaceEmptyArg, const String& stringArg) { @@ -126,12 +174,20 @@ return; if (!m_scriptState->contextIsValid()) return; + ScriptState::Scope scope(m_scriptState.get()); + v8::Local<v8::Value> testInterfaceEmptyArgHandle = ToV8(testInterfaceEmptyArg, m_scriptState->context()->Global(), m_scriptState->isolate()); v8::Local<v8::Value> stringArgHandle = v8String(m_scriptState->isolate(), stringArg); v8::Local<v8::Value> argv[] = { testInterfaceEmptyArgHandle, stringArgHandle }; - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), v8::Undefined(m_scriptState->isolate()), 2, argv, m_scriptState->isolate()); + v8::Isolate* isolate = m_scriptState->isolate(); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + m_scriptState->getExecutionContext(), + v8::Undefined(isolate), + 2, + argv, + isolate); } void V8TestCallbackInterface::callbackWithThisValueVoidMethodStringArg(ScriptValue thisValue, const String& stringArg) { @@ -141,12 +197,20 @@ return; if (!m_scriptState->contextIsValid()) return; + ScriptState::Scope scope(m_scriptState.get()); v8::Local<v8::Value> thisHandle = thisValue.v8Value(); + v8::Local<v8::Value> stringArgHandle = v8String(m_scriptState->isolate(), stringArg); v8::Local<v8::Value> argv[] = { stringArgHandle }; - V8ScriptRunner::callFunction(m_callback.newLocal(m_scriptState->isolate()), m_scriptState->getExecutionContext(), thisHandle, 1, argv, m_scriptState->isolate()); + v8::Isolate* isolate = m_scriptState->isolate(); + V8ScriptRunner::callFunction(m_callback.newLocal(isolate), + m_scriptState->getExecutionContext(), + thisHandle, + 1, + argv, + isolate); } } // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.h index af5a4326..26c3497 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.h +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackInterface.h
@@ -38,6 +38,7 @@ void voidMethodTestInterfaceEmptyStringArg(TestInterfaceEmpty* testInterfaceEmptyArg, const String& stringArg) override; void callbackWithThisValueVoidMethodStringArg(ScriptValue thisValue, const String& stringArg) override; void customVoidMethodTestInterfaceEmptyArg(TestInterfaceEmpty* testInterfaceEmptyArg) override; + private: CORE_EXPORT V8TestCallbackInterface(v8::Local<v8::Function>, ScriptState*);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp index 77c16c242..20a79e9 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestDictionary.cpp
@@ -12,12 +12,10 @@ #include "V8TestDictionary.h" #include "bindings/core/v8/Dictionary.h" -#include "bindings/core/v8/DoubleOrString.h" #include "bindings/core/v8/ExceptionState.h" #include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" #include "bindings/core/v8/ScriptValue.h" -#include "bindings/core/v8/TestInterface2OrUint8Array.h" #include "bindings/core/v8/V8ArrayBufferView.h" #include "bindings/core/v8/V8Element.h" #include "bindings/core/v8/V8EventTarget.h" @@ -594,6 +592,21 @@ impl.setUint8ArrayMember(uint8ArrayMember); } + v8::Local<v8::Value> unionWithTypedefsValue; + if (!v8Object->Get(isolate->GetCurrentContext(), v8AtomicString(isolate, "unionWithTypedefs")).ToLocal(&unionWithTypedefsValue)) { + exceptionState.rethrowV8Exception(block.Exception()); + return; + } + if (unionWithTypedefsValue.IsEmpty() || unionWithTypedefsValue->IsUndefined()) { + // Do nothing. + } else { + FloatOrBoolean unionWithTypedefs; + V8FloatOrBoolean::toImpl(isolate, unionWithTypedefsValue, unionWithTypedefs, UnionTypeConversionMode::NotNullable, exceptionState); + if (exceptionState.hadException()) + return; + impl.setUnionWithTypedefs(unionWithTypedefs); + } + v8::Local<v8::Value> unrestrictedDoubleMemberValue; if (!v8Object->Get(isolate->GetCurrentContext(), v8AtomicString(isolate, "unrestrictedDoubleMember")).ToLocal(&unrestrictedDoubleMemberValue)) { exceptionState.rethrowV8Exception(block.Exception()); @@ -841,6 +854,11 @@ return false; } + if (impl.hasUnionWithTypedefs()) { + if (!v8CallBoolean(dictionary->CreateDataProperty(isolate->GetCurrentContext(), v8AtomicString(isolate, "unionWithTypedefs"), ToV8(impl.unionWithTypedefs(), creationContext, isolate)))) + return false; + } + if (impl.hasUnrestrictedDoubleMember()) { if (!v8CallBoolean(dictionary->CreateDataProperty(isolate->GetCurrentContext(), v8AtomicString(isolate, "unrestrictedDoubleMember"), v8::Number::New(isolate, impl.unrestrictedDoubleMember())))) return false;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp index be04ade..013b701 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
@@ -14,7 +14,6 @@ #include "bindings/core/v8/Dictionary.h" #include "bindings/core/v8/ExceptionState.h" #include "bindings/core/v8/IDLTypes.h" -#include "bindings/core/v8/LongOrTestDictionary.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" #include "bindings/core/v8/V8DOMConfiguration.h" #include "bindings/core/v8/V8ObjectConstructor.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp index 23b58c13..4775774 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -11,12 +11,8 @@ // clang-format off #include "V8TestObject.h" -#include "bindings/core/v8/ArrayBufferOrArrayBufferViewOrDictionary.h" #include "bindings/core/v8/BindingSecurity.h" -#include "bindings/core/v8/BooleanOrStringOrUnrestrictedDouble.h" #include "bindings/core/v8/Dictionary.h" -#include "bindings/core/v8/DoubleOrString.h" -#include "bindings/core/v8/DoubleOrStringOrDoubleOrStringSequence.h" #include "bindings/core/v8/ExceptionState.h" #include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" @@ -26,13 +22,7 @@ #include "bindings/core/v8/ScriptValue.h" #include "bindings/core/v8/SerializedScriptValue.h" #include "bindings/core/v8/SerializedScriptValueFactory.h" -#include "bindings/core/v8/StringOrArrayBufferOrArrayBufferView.h" -#include "bindings/core/v8/StringOrStringSequence.h" -#include "bindings/core/v8/TestEnumOrDouble.h" -#include "bindings/core/v8/TestInterfaceGarbageCollectedOrString.h" -#include "bindings/core/v8/TestInterfaceOrLong.h" #include "bindings/core/v8/Transferables.h" -#include "bindings/core/v8/UnrestrictedDoubleOrString.h" #include "bindings/core/v8/V8AbstractEventListener.h" #include "bindings/core/v8/V8ArrayBuffer.h" #include "bindings/core/v8/V8ArrayBufferView.h" @@ -4780,6 +4770,60 @@ impl->voidMethodArrayBufferOrArrayBufferViewOrDictionaryArg(arg); } +static void voidMethodBooleanOrElementSequenceArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { + ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestObject", "voidMethodBooleanOrElementSequenceArg"); + + TestObject* impl = V8TestObject::toImpl(info.Holder()); + + if (UNLIKELY(info.Length() < 1)) { + exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length())); + return; + } + + BooleanOrElementSequence arg; + V8BooleanOrElementSequence::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState); + if (exceptionState.hadException()) + return; + + impl->voidMethodBooleanOrElementSequenceArg(arg); +} + +static void voidMethodDoubleOrLongOrBooleanSequenceArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { + ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestObject", "voidMethodDoubleOrLongOrBooleanSequenceArg"); + + TestObject* impl = V8TestObject::toImpl(info.Holder()); + + if (UNLIKELY(info.Length() < 1)) { + exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length())); + return; + } + + DoubleOrLongOrBooleanSequence arg; + V8DoubleOrLongOrBooleanSequence::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState); + if (exceptionState.hadException()) + return; + + impl->voidMethodDoubleOrLongOrBooleanSequenceArg(arg); +} + +static void voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { + ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestObject", "voidMethodElementSequenceOrByteStringDoubleOrStringRecord"); + + TestObject* impl = V8TestObject::toImpl(info.Holder()); + + if (UNLIKELY(info.Length() < 1)) { + exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length())); + return; + } + + ElementSequenceOrByteStringDoubleOrStringRecord arg; + V8ElementSequenceOrByteStringDoubleOrStringRecord::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState); + if (exceptionState.hadException()) + return; + + impl->voidMethodElementSequenceOrByteStringDoubleOrStringRecord(arg); +} + static void voidMethodArrayOfDoubleOrDOMStringArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestObject", "voidMethodArrayOfDoubleOrDOMStringArg"); @@ -10555,6 +10599,18 @@ TestObjectV8Internal::voidMethodArrayBufferOrArrayBufferViewOrDictionaryArgMethod(info); } +void V8TestObject::voidMethodBooleanOrElementSequenceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { + TestObjectV8Internal::voidMethodBooleanOrElementSequenceArgMethod(info); +} + +void V8TestObject::voidMethodDoubleOrLongOrBooleanSequenceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { + TestObjectV8Internal::voidMethodDoubleOrLongOrBooleanSequenceArgMethod(info); +} + +void V8TestObject::voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { + TestObjectV8Internal::voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethod(info); +} + void V8TestObject::voidMethodArrayOfDoubleOrDOMStringArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { TestObjectV8Internal::voidMethodArrayOfDoubleOrDOMStringArgMethod(info); } @@ -11516,6 +11572,9 @@ {"voidMethodDoubleOrNullOrDOMStringArg", V8TestObject::voidMethodDoubleOrNullOrDOMStringArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, {"voidMethodDOMStringOrArrayBufferOrArrayBufferViewArg", V8TestObject::voidMethodDOMStringOrArrayBufferOrArrayBufferViewArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, {"voidMethodArrayBufferOrArrayBufferViewOrDictionaryArg", V8TestObject::voidMethodArrayBufferOrArrayBufferViewOrDictionaryArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, + {"voidMethodBooleanOrElementSequenceArg", V8TestObject::voidMethodBooleanOrElementSequenceArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, + {"voidMethodDoubleOrLongOrBooleanSequenceArg", V8TestObject::voidMethodDoubleOrLongOrBooleanSequenceArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, + {"voidMethodElementSequenceOrByteStringDoubleOrStringRecord", V8TestObject::voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, {"voidMethodArrayOfDoubleOrDOMStringArg", V8TestObject::voidMethodArrayOfDoubleOrDOMStringArgMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, {"voidMethodTestInterfaceEmptyOrNullArg", V8TestObject::voidMethodTestInterfaceEmptyOrNullArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, {"voidMethodTestCallbackInterfaceArg", V8TestObject::voidMethodTestCallbackInterfaceArgMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess},
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h index 205626c..ca99306 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.h
@@ -13,9 +13,12 @@ #define V8TestObject_h #include "bindings/core/v8/ArrayBufferOrArrayBufferViewOrDictionary.h" +#include "bindings/core/v8/BooleanOrElementSequence.h" #include "bindings/core/v8/BooleanOrStringOrUnrestrictedDouble.h" +#include "bindings/core/v8/DoubleOrLongOrBooleanSequence.h" #include "bindings/core/v8/DoubleOrString.h" #include "bindings/core/v8/DoubleOrStringOrDoubleOrStringSequence.h" +#include "bindings/core/v8/ElementSequenceOrByteStringDoubleOrStringRecord.h" #include "bindings/core/v8/GeneratedCodeHelper.h" #include "bindings/core/v8/NativeValueTraits.h" #include "bindings/core/v8/ScriptWrappable.h" @@ -462,6 +465,9 @@ CORE_EXPORT static void voidMethodDoubleOrNullOrDOMStringArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void voidMethodDOMStringOrArrayBufferOrArrayBufferViewArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void voidMethodArrayBufferOrArrayBufferViewOrDictionaryArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); + CORE_EXPORT static void voidMethodBooleanOrElementSequenceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); + CORE_EXPORT static void voidMethodDoubleOrLongOrBooleanSequenceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); + CORE_EXPORT static void voidMethodElementSequenceOrByteStringDoubleOrStringRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void voidMethodArrayOfDoubleOrDOMStringArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void voidMethodTestInterfaceEmptyOrNullArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void voidMethodTestCallbackInterfaceArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp index aeb9413..56fef453 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
@@ -14,7 +14,6 @@ #include "bindings/core/v8/ExceptionState.h" #include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" -#include "bindings/core/v8/NodeOrNodeList.h" #include "bindings/core/v8/V8DOMConfiguration.h" #include "bindings/core/v8/V8Node.h" #include "bindings/core/v8/V8NodeList.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp index f8c99941..2d7e39d 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
@@ -11,15 +11,9 @@ // clang-format off #include "V8TestTypedefs.h" -#include "bindings/core/v8/ByteStringOrNodeList.h" -#include "bindings/core/v8/ByteStringSequenceSequenceOrByteStringByteStringRecord.h" #include "bindings/core/v8/ExceptionState.h" #include "bindings/core/v8/IDLTypes.h" -#include "bindings/core/v8/LongSequenceOrEvent.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" -#include "bindings/core/v8/NodeOrLongSequenceOrEventOrXMLHttpRequestOrStringOrStringByteStringOrNodeListRecord.h" -#include "bindings/core/v8/StringOrDouble.h" -#include "bindings/core/v8/TestInterfaceOrTestInterfaceEmpty.h" #include "bindings/core/v8/V8DOMConfiguration.h" #include "bindings/core/v8/V8Event.h" #include "bindings/core/v8/V8Node.h" @@ -30,7 +24,6 @@ #include "bindings/core/v8/V8TestInterfaceEmpty.h" #include "bindings/core/v8/V8TestObject.h" #include "bindings/core/v8/V8XMLHttpRequest.h" -#include "bindings/core/v8/XMLHttpRequestOrString.h" #include "core/dom/Document.h" #include "core/dom/NameNodeList.h" #include "core/dom/NodeList.h" @@ -334,6 +327,24 @@ impl->voidMethodNestedUnionType(arg); } +static void voidMethodUnionWithTypedefMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { + ExceptionState exceptionState(info.GetIsolate(), ExceptionState::ExecutionContext, "TestTypedefs", "voidMethodUnionWithTypedef"); + + TestTypedefs* impl = V8TestTypedefs::toImpl(info.Holder()); + + if (UNLIKELY(info.Length() < 1)) { + exceptionState.throwTypeError(ExceptionMessages::notEnoughArguments(1, info.Length())); + return; + } + + UnsignedLongLongOrBooleanOrTestCallbackInterface arg; + V8UnsignedLongLongOrBooleanOrTestCallbackInterface::toImpl(info.GetIsolate(), info[0], arg, UnionTypeConversionMode::NotNullable, exceptionState); + if (exceptionState.hadException()) + return; + + impl->voidMethodUnionWithTypedef(arg); +} + static void constructor(const v8::FunctionCallbackInfo<v8::Value>& info) { if (UNLIKELY(info.Length() < 1)) { V8ThrowException::throwTypeError(info.GetIsolate(), ExceptionMessages::failedToConstruct("TestTypedefs", ExceptionMessages::notEnoughArguments(1, info.Length()))); @@ -425,6 +436,10 @@ TestTypedefsV8Internal::voidMethodNestedUnionTypeMethod(info); } +void V8TestTypedefs::voidMethodUnionWithTypedefMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { + TestTypedefsV8Internal::voidMethodUnionWithTypedefMethod(info); +} + // Suppress warning: global constructors, because AttributeConfiguration is trivial // and does not depend on another global objects. #if defined(COMPONENT_BUILD) && defined(WIN32) && COMPILER(CLANG) @@ -457,6 +472,7 @@ {"unionWithRecordMethod", V8TestTypedefs::unionWithRecordMethodMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, {"methodThatReturnsRecord", V8TestTypedefs::methodThatReturnsRecordMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, {"voidMethodNestedUnionType", V8TestTypedefs::voidMethodNestedUnionTypeMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, + {"voidMethodUnionWithTypedef", V8TestTypedefs::voidMethodUnionWithTypedefMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, }; void V8TestTypedefs::constructorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h index f070985..fff6bbc7 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.h
@@ -20,6 +20,7 @@ #include "bindings/core/v8/StringOrDouble.h" #include "bindings/core/v8/TestInterfaceOrTestInterfaceEmpty.h" #include "bindings/core/v8/ToV8.h" +#include "bindings/core/v8/UnsignedLongLongOrBooleanOrTestCallbackInterface.h" #include "bindings/core/v8/V8Binding.h" #include "bindings/core/v8/V8DOMWrapper.h" #include "bindings/core/v8/WrapperTypeInfo.h" @@ -69,6 +70,7 @@ CORE_EXPORT static void unionWithRecordMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void methodThatReturnsRecordMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void voidMethodNestedUnionTypeMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); + CORE_EXPORT static void voidMethodUnionWithTypedefMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); }; template <>
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/XMLHttpRequestOrString.cpp b/third_party/WebKit/Source/bindings/tests/results/core/XMLHttpRequestOrString.cpp index cb4aa5f..7eac9bb 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/XMLHttpRequestOrString.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/XMLHttpRequestOrString.cpp
@@ -11,7 +11,6 @@ // clang-format off #include "XMLHttpRequestOrString.h" -#include "bindings/core/v8/ArrayBufferOrArrayBufferViewOrBlobOrDocumentOrStringOrFormDataOrURLSearchParams.h" #include "bindings/core/v8/IDLTypes.h" #include "bindings/core/v8/NativeValueTraitsImpl.h" #include "bindings/core/v8/ToV8.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp index 9c15a99..b05b93f 100644 --- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
@@ -17,8 +17,6 @@ #include "bindings/core/v8/V8DOMConfiguration.h" #include "bindings/core/v8/V8ObjectConstructor.h" #include "bindings/core/v8/V8TestInterfaceEmpty.h" -#include "bindings/modules/v8/BooleanOrString.h" -#include "bindings/modules/v8/DoubleOrString.h" #include "bindings/modules/v8/V8TestInterface5.h" #include "bindings/modules/v8/VoidCallbackFunctionModules.h" #include "core/dom/Document.h"
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp index 49c48ba2..0dc9ef59 100644 --- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp
@@ -20,6 +20,7 @@ #include "bindings/core/v8/V8Document.h" #include "bindings/core/v8/V8Node.h" #include "bindings/core/v8/V8ObjectConstructor.h" +#include "bindings/core/v8/V8TestCallbackInterface.h" #include "bindings/core/v8/V8TestInterface.h" #include "bindings/tests/idls/modules/TestInterfacePartial3Implementation.h" #include "bindings/tests/idls/modules/TestInterfacePartial4.h" @@ -328,6 +329,14 @@ TestInterfacePartial3Implementation::unscopableVoidMethod(*impl); } +static void unionWithTypedefMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { + TestInterfaceImplementation* impl = V8TestInterface::toImpl(info.Holder()); + + UnsignedLongLongOrBooleanOrTestCallbackInterface result; + TestInterfacePartial3Implementation::unionWithTypedefMethod(*impl, result); + v8SetReturnValue(info, result); +} + static void partial4VoidMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { TestInterfaceImplementation* impl = V8TestInterface::toImpl(info.Holder()); @@ -368,6 +377,10 @@ TestInterfaceImplementationPartialV8Internal::unscopableVoidMethodMethod(info); } +void V8TestInterfacePartial::unionWithTypedefMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { + TestInterfaceImplementationPartialV8Internal::unionWithTypedefMethodMethod(info); +} + void V8TestInterfacePartial::partial4VoidMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { TestInterfaceImplementationPartialV8Internal::partial4VoidMethodMethod(info); } @@ -379,6 +392,7 @@ const V8DOMConfiguration::MethodConfiguration V8TestInterfaceMethods[] = { {"partialVoidTestEnumModulesArgMethod", V8TestInterfacePartial::partialVoidTestEnumModulesArgMethodMethodCallback, nullptr, 1, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, {"unscopableVoidMethod", V8TestInterfacePartial::unscopableVoidMethodMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, + {"unionWithTypedefMethod", V8TestInterfacePartial::unionWithTypedefMethodMethodCallback, nullptr, 0, v8::None, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder, V8DOMConfiguration::DoNotCheckAccess}, }; void V8TestInterfacePartial::installV8TestInterfaceTemplate(v8::Isolate* isolate, const DOMWrapperWorld& world, v8::Local<v8::FunctionTemplate> interfaceTemplate) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.h b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.h index c98b5558..a3d2667 100644 --- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.h +++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.h
@@ -16,6 +16,7 @@ #include "bindings/core/v8/NativeValueTraits.h" #include "bindings/core/v8/ScriptWrappable.h" #include "bindings/core/v8/ToV8.h" +#include "bindings/core/v8/UnsignedLongLongOrBooleanOrTestCallbackInterface.h" #include "bindings/core/v8/V8Binding.h" #include "bindings/core/v8/V8DOMWrapper.h" #include "bindings/core/v8/WrapperTypeInfo.h" @@ -45,6 +46,7 @@ static void partialVoidTestEnumModulesArgMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info); static void unscopableVoidMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info); + static void unionWithTypedefMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info); static void partial4VoidMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info); static void partial4StaticVoidMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp index 73df10b..8043437 100644 --- a/third_party/WebKit/Source/core/dom/Element.cpp +++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -1328,12 +1328,6 @@ invalidateNodeListCachesInAncestors(&name, this); - // If there is currently no StyleResolver, we can't be sure that this - // attribute change won't affect style. - if (!document().styleResolver()) - setNeedsStyleRecalc(SubtreeStyleChange, - StyleChangeReasonForTracing::fromAttribute(name)); - if (isConnected()) { if (AXObjectCache* cache = document().existingAXObjectCache()) cache->handleAttributeChanged(name, this); @@ -2355,9 +2349,6 @@ return; if (!inActiveDocument()) return; - if (!document().styleResolver()) - return; - if (!style || (styleAffectedByEmpty() && (!style->emptyState() || hasChildren()))) pseudoStateChanged(CSSSelector::PseudoEmpty);
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp index 36e75476..779ffa0 100644 --- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp +++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -606,7 +606,7 @@ } bool StyleEngine::shouldSkipInvalidationFor(const Element& element) const { - if (!resolver()) + if (document().getStyleChangeType() >= SubtreeStyleChange) return true; if (!element.inActiveDocument()) return true;
diff --git a/third_party/WebKit/Source/core/dom/StyleEngineTest.cpp b/third_party/WebKit/Source/core/dom/StyleEngineTest.cpp index 60f4c16..097aeee 100644 --- a/third_party/WebKit/Source/core/dom/StyleEngineTest.cpp +++ b/third_party/WebKit/Source/core/dom/StyleEngineTest.cpp
@@ -450,4 +450,40 @@ t1->computedStyle()->visitedDependentColor(CSSPropertyColor)); } +TEST_F(StyleEngineTest, ScheduleInvaldationAfterSubtreeRecalc) { + document().body()->setInnerHTML( + "<style>" + " .t1 span { color: green }x" + " .t2 span { color: green }" + "</style>" + "<div id='t1'></div>" + "<div id='t2'></div>"); + document().view()->updateAllLifecyclePhases(); + + Element* t1 = document().getElementById("t1"); + Element* t2 = document().getElementById("t1"); + ASSERT_TRUE(t1); + ASSERT_TRUE(t2); + + // Sanity test. + t1->setAttribute(blink::HTMLNames::classAttr, "t1"); + EXPECT_FALSE(document().needsStyleInvalidation()); + EXPECT_TRUE(document().childNeedsStyleInvalidation()); + + document().view()->updateAllLifecyclePhases(); + + // platformColorsChanged() triggers SubtreeStyleChange on document(). If that + // for some reason should change, this test will start failing and the + // SubtreeStyleChange must be set another way. + // Calling setNeedsStyleRecalc() explicitly with an arbitrary reason instead + // requires us to CORE_EXPORT the reason strings. + styleEngine().platformColorsChanged(); + + // Check that no invalidations sets are scheduled when the document node is + // already SubtreeStyleChange. + t2->setAttribute(blink::HTMLNames::classAttr, "t2"); + EXPECT_FALSE(document().needsStyleInvalidation()); + EXPECT_FALSE(document().childNeedsStyleInvalidation()); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp index f1e1a4f..5d0c85686 100644 --- a/third_party/WebKit/Source/core/editing/Editor.cpp +++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -181,13 +181,9 @@ if (textControlOfTarget && (selection.start().isNull() || textControlOfTarget != textControlOfSelectionStart)) { - if (Range* range = textControlOfTarget->selection()) { - return createVisibleSelection( - SelectionInDOMTree::Builder() - .setBaseAndExtent(EphemeralRange(range)) - .setIsDirectional(selection.isDirectional()) - .build()); - } + const SelectionInDOMTree& select = textControlOfTarget->selection(); + if (!select.isNone()) + return createVisibleSelection(select); } return selection; }
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp index b53e47f..c253d60a8 100644 --- a/third_party/WebKit/Source/core/editing/SelectionController.cpp +++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -387,6 +387,9 @@ } else { basePosition = selection().computeVisibleSelectionInFlatTree().base(); } + if (basePosition.isNull()) + return; + const SelectionInFlatTree& appliedSelection = applySelectAll( basePosition, targetPosition.deepEquivalent(), mousePressNode, dragStartPos, target, hitTestResult.localPoint());
diff --git a/third_party/WebKit/Source/core/editing/SelectionEditor.cpp b/third_party/WebKit/Source/core/editing/SelectionEditor.cpp index 14f7509..615c5d7 100644 --- a/third_party/WebKit/Source/core/editing/SelectionEditor.cpp +++ b/third_party/WebKit/Source/core/editing/SelectionEditor.cpp
@@ -370,7 +370,13 @@ if (!needsUpdateVisibleSelection()) return; + m_styleVersion = document().styleVersion(); + m_cacheIsDirty = false; m_cachedVisibleSelectionInDOMTree = createVisibleSelection(m_selection); + if (m_cachedVisibleSelectionInDOMTree.isNone()) { + m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree(); + return; + } m_cachedVisibleSelectionInFlatTree = createVisibleSelection( SelectionInFlatTree::Builder() .setBaseAndExtent(toPositionInFlatTree(m_selection.base()), @@ -380,8 +386,6 @@ .setGranularity(m_selection.granularity()) .setIsDirectional(m_selection.isDirectional()) .build()); - m_styleVersion = document().styleVersion(); - m_cacheIsDirty = false; } void SelectionEditor::cacheRangeOfDocument(Range* range) {
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp index d35c225..251dd1f 100644 --- a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp +++ b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
@@ -1831,70 +1831,71 @@ PositionAnchorType candidateType = position.anchorType(); int candidateOffset = position.computeEditingOffset(); - Node* prevousNodeIterator = startNode; - while (prevousNodeIterator) { + Node* previousNodeIterator = startNode; + while (previousNodeIterator) { if (boundaryCrossingRule == CannotCrossEditingBoundary && - !nodeIsUserSelectAll(prevousNodeIterator) && - hasEditableStyle(*prevousNodeIterator) != startNodeIsEditable) + !nodeIsUserSelectAll(previousNodeIterator) && + hasEditableStyle(*previousNodeIterator) != startNodeIsEditable) break; if (boundaryCrossingRule == CanSkipOverEditingBoundary) { - while (prevousNodeIterator && - hasEditableStyle(*prevousNodeIterator) != startNodeIsEditable) - prevousNodeIterator = - Strategy::previousPostOrder(*prevousNodeIterator, startBlock); - if (!prevousNodeIterator || - !prevousNodeIterator->isDescendantOf(highestRoot)) + while (previousNodeIterator && + hasEditableStyle(*previousNodeIterator) != startNodeIsEditable) { + previousNodeIterator = + Strategy::previousPostOrder(*previousNodeIterator, startBlock); + } + if (!previousNodeIterator || + !previousNodeIterator->isDescendantOf(highestRoot)) break; } const LayoutItem layoutItem = - LayoutItem(prevousNodeIterator->layoutObject()); + LayoutItem(previousNodeIterator->layoutObject()); if (layoutItem.isNull()) { - prevousNodeIterator = - Strategy::previousPostOrder(*prevousNodeIterator, startBlock); + previousNodeIterator = + Strategy::previousPostOrder(*previousNodeIterator, startBlock); continue; } const ComputedStyle& style = layoutItem.styleRef(); if (style.visibility() != EVisibility::kVisible) { - prevousNodeIterator = - Strategy::previousPostOrder(*prevousNodeIterator, startBlock); + previousNodeIterator = + Strategy::previousPostOrder(*previousNodeIterator, startBlock); continue; } - if (layoutItem.isBR() || isEnclosingBlock(prevousNodeIterator)) + if (layoutItem.isBR() || isEnclosingBlock(previousNodeIterator)) break; if (layoutItem.isText() && - toLayoutText(prevousNodeIterator->layoutObject()) + toLayoutText(previousNodeIterator->layoutObject()) ->resolvedTextLength()) { - SECURITY_DCHECK(prevousNodeIterator->isTextNode()); + SECURITY_DCHECK(previousNodeIterator->isTextNode()); if (style.preserveNewline()) { - LayoutText* text = toLayoutText(prevousNodeIterator->layoutObject()); + LayoutText* text = toLayoutText(previousNodeIterator->layoutObject()); int index = text->textLength(); - if (prevousNodeIterator == startNode && candidateOffset < index) + if (previousNodeIterator == startNode && candidateOffset < index) index = max(0, candidateOffset); while (--index >= 0) { if ((*text)[index] == '\n') - return PositionTemplate<Strategy>(toText(prevousNodeIterator), + return PositionTemplate<Strategy>(toText(previousNodeIterator), index + 1); } } - candidateNode = prevousNodeIterator; + candidateNode = previousNodeIterator; candidateType = PositionAnchorType::OffsetInAnchor; candidateOffset = 0; - prevousNodeIterator = - Strategy::previousPostOrder(*prevousNodeIterator, startBlock); - } else if (editingIgnoresContent(*prevousNodeIterator) || - isDisplayInsideTable(prevousNodeIterator)) { - candidateNode = prevousNodeIterator; + previousNodeIterator = + Strategy::previousPostOrder(*previousNodeIterator, startBlock); + } else if (editingIgnoresContent(*previousNodeIterator) || + isDisplayInsideTable(previousNodeIterator)) { + candidateNode = previousNodeIterator; candidateType = PositionAnchorType::BeforeAnchor; - prevousNodeIterator = - prevousNodeIterator->previousSibling() - ? prevousNodeIterator->previousSibling() - : Strategy::previousPostOrder(*prevousNodeIterator, startBlock); + previousNodeIterator = + previousNodeIterator->previousSibling() + ? previousNodeIterator->previousSibling() + : Strategy::previousPostOrder(*previousNodeIterator, startBlock); } else { - prevousNodeIterator = - Strategy::previousPostOrder(*prevousNodeIterator, startBlock); + previousNodeIterator = + Strategy::previousPostOrder(*previousNodeIterator, startBlock); } } @@ -1948,63 +1949,63 @@ PositionAnchorType candidateType = position.anchorType(); int candidateOffset = position.computeEditingOffset(); - Node* nextNodeItreator = startNode; - while (nextNodeItreator) { + Node* nextNodeIterator = startNode; + while (nextNodeIterator) { if (boundaryCrossingRule == CannotCrossEditingBoundary && - !nodeIsUserSelectAll(nextNodeItreator) && - hasEditableStyle(*nextNodeItreator) != startNodeIsEditable) + !nodeIsUserSelectAll(nextNodeIterator) && + hasEditableStyle(*nextNodeIterator) != startNodeIsEditable) break; if (boundaryCrossingRule == CanSkipOverEditingBoundary) { - while (nextNodeItreator && - hasEditableStyle(*nextNodeItreator) != startNodeIsEditable) - nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock); - if (!nextNodeItreator || !nextNodeItreator->isDescendantOf(highestRoot)) + while (nextNodeIterator && + hasEditableStyle(*nextNodeIterator) != startNodeIsEditable) + nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock); + if (!nextNodeIterator || !nextNodeIterator->isDescendantOf(highestRoot)) break; } - LayoutObject* const layoutObject = nextNodeItreator->layoutObject(); + LayoutObject* const layoutObject = nextNodeIterator->layoutObject(); if (!layoutObject) { - nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock); + nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock); continue; } const ComputedStyle& style = layoutObject->styleRef(); if (style.visibility() != EVisibility::kVisible) { - nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock); + nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock); continue; } - if (layoutObject->isBR() || isEnclosingBlock(nextNodeItreator)) + if (layoutObject->isBR() || isEnclosingBlock(nextNodeIterator)) break; // FIXME: We avoid returning a position where the layoutObject can't accept // the caret. if (layoutObject->isText() && toLayoutText(layoutObject)->resolvedTextLength()) { - SECURITY_DCHECK(nextNodeItreator->isTextNode()); + SECURITY_DCHECK(nextNodeIterator->isTextNode()); LayoutText* const text = toLayoutText(layoutObject); if (style.preserveNewline()) { const int length = toLayoutText(layoutObject)->textLength(); - for (int i = (nextNodeItreator == startNode ? candidateOffset : 0); + for (int i = (nextNodeIterator == startNode ? candidateOffset : 0); i < length; ++i) { if ((*text)[i] == '\n') - return PositionTemplate<Strategy>(toText(nextNodeItreator), + return PositionTemplate<Strategy>(toText(nextNodeIterator), i + text->textStartOffset()); } } - candidateNode = nextNodeItreator; + candidateNode = nextNodeIterator; candidateType = PositionAnchorType::OffsetInAnchor; candidateOffset = layoutObject->caretMaxOffset() + text->textStartOffset(); - nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock); - } else if (editingIgnoresContent(*nextNodeItreator) || - isDisplayInsideTable(nextNodeItreator)) { - candidateNode = nextNodeItreator; + nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock); + } else if (editingIgnoresContent(*nextNodeIterator) || + isDisplayInsideTable(nextNodeIterator)) { + candidateNode = nextNodeIterator; candidateType = PositionAnchorType::AfterAnchor; - nextNodeItreator = - Strategy::nextSkippingChildren(*nextNodeItreator, startBlock); + nextNodeIterator = + Strategy::nextSkippingChildren(*nextNodeIterator, startBlock); } else { - nextNodeItreator = Strategy::next(*nextNodeItreator, startBlock); + nextNodeIterator = Strategy::next(*nextNodeIterator, startBlock); } }
diff --git a/third_party/WebKit/Source/core/html/TextControlElement.cpp b/third_party/WebKit/Source/core/html/TextControlElement.cpp index fabc2fd..07b02c6 100644 --- a/third_party/WebKit/Source/core/html/TextControlElement.cpp +++ b/third_party/WebKit/Source/core/html/TextControlElement.cpp
@@ -609,9 +609,9 @@ } } -Range* TextControlElement::selection() const { +SelectionInDOMTree TextControlElement::selection() const { if (!layoutObject() || !isTextControl()) - return nullptr; + return SelectionInDOMTree(); int start = m_cachedSelectionStart; int end = m_cachedSelectionEnd; @@ -619,10 +619,14 @@ DCHECK_LE(start, end); HTMLElement* innerText = innerEditorElement(); if (!innerText) - return nullptr; + return SelectionInDOMTree(); - if (!innerText->hasChildren()) - return Range::create(document(), innerText, 0, innerText, 0); + if (!innerText->hasChildren()) { + return SelectionInDOMTree::Builder() + .collapse(Position(innerText, 0)) + .setIsDirectional(selectionDirection() != "none") + .build(); + } int offset = 0; Node* startNode = 0; @@ -644,9 +648,12 @@ } if (!startNode || !endNode) - return nullptr; + return SelectionInDOMTree(); - return Range::create(document(), startNode, start, endNode, end); + return SelectionInDOMTree::Builder() + .setBaseAndExtent(Position(startNode, start), Position(endNode, end)) + .setIsDirectional(selectionDirection() != "none") + .build(); } const AtomicString& TextControlElement::autocapitalize() const {
diff --git a/third_party/WebKit/Source/core/html/TextControlElement.h b/third_party/WebKit/Source/core/html/TextControlElement.h index 8667c1f..b40cb00 100644 --- a/third_party/WebKit/Source/core/html/TextControlElement.h +++ b/third_party/WebKit/Source/core/html/TextControlElement.h
@@ -28,13 +28,13 @@ #include "base/gtest_prod_util.h" #include "core/CoreExport.h" +#include "core/editing/SelectionTemplate.h" #include "core/editing/VisiblePosition.h" #include "core/html/HTMLFormControlElementWithState.h" namespace blink { class ExceptionState; -class Range; enum TextFieldSelectionDirection { SelectionHasNoDirection, @@ -100,7 +100,7 @@ bool setSelectionRange(unsigned start, unsigned end, TextFieldSelectionDirection = SelectionHasNoDirection); - Range* selection() const; + SelectionInDOMTree selection() const; virtual bool supportsAutocapitalize() const = 0; virtual const AtomicString& defaultAutocapitalize() const = 0;
diff --git a/third_party/brotli/BUILD.gn b/third_party/brotli/BUILD.gn index 2acb54b..4391133 100644 --- a/third_party/brotli/BUILD.gn +++ b/third_party/brotli/BUILD.gn
@@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//testing/libfuzzer/fuzzer_test.gni") + if (is_win) { import("//build/config/win/visual_studio_version.gni") } @@ -128,3 +130,13 @@ } } } + +fuzzer_test("brotli_fuzzer") { + sources = [ + "fuzz/decode_fuzzer.cc", + ] + deps = [ + ":dec", + ] + libfuzzer_options = [ "max_len=1280" ] +}
diff --git a/third_party/brotli/README.chromium b/third_party/brotli/README.chromium index 0c51a386..d8980318 100644 --- a/third_party/brotli/README.chromium +++ b/third_party/brotli/README.chromium
@@ -11,9 +11,10 @@ to decode WOFF 2.0 fonts. Local Modifications: -- This only includes the enc/, dec/ and tools/ directories, the README.md and - the LICENSE files, removing unneeded directories such as docs, tests, and - tools. +- This only includes the common/, dec/, enc/, fuzz/ and tools/ directories, the + README.md and the LICENSE files, removing unneeded directories such as docs, + java, and tests. +- Auxiliary fuzzer runners removed from fuzz/ - Line 19 of enc/cluster_inc.h was modified to eliminate build error on Windows. - BUILD.gn: Added. - brotli.gyp: Added.
diff --git a/third_party/brotli/fuzz/DEPS b/third_party/brotli/fuzz/DEPS new file mode 100644 index 0000000..2e39551 --- /dev/null +++ b/third_party/brotli/fuzz/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+third_party/brotli/dec", +]
diff --git a/testing/libfuzzer/fuzzers/brotli_fuzzer.cc b/third_party/brotli/fuzz/decode_fuzzer.cc similarity index 78% rename from testing/libfuzzer/fuzzers/brotli_fuzzer.cc rename to third_party/brotli/fuzz/decode_fuzzer.cc index 4689416a..60c6f8e4 100644 --- a/testing/libfuzzer/fuzzers/brotli_fuzzer.cc +++ b/third_party/brotli/fuzz/decode_fuzzer.cc
@@ -5,7 +5,7 @@ #include <stddef.h> #include <stdint.h> -#include "third_party/brotli/include/brotli/decode.h" +#include <brotli/decode.h> // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { @@ -16,6 +16,11 @@ const int kBufferSize = 1024; uint8_t* buffer = new uint8_t[kBufferSize]; + /* The biggest "magic number" in brotli is 16MiB - 16, so no need to check + the cases with much longer output. */ + const size_t total_out_limit = (addend == 0) ? (1 << 26) : (1 << 24); + size_t total_out = 0; + BrotliDecoderState* state = BrotliDecoderCreateInstance(0, 0, 0); if (addend == 0) @@ -31,10 +36,13 @@ while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { size_t avail_out = kBufferSize; uint8_t* next_out = buffer; - size_t total_out; result = BrotliDecoderDecompressStream( state, &avail_in, &next_in, &avail_out, &next_out, &total_out); + if (total_out > total_out_limit) + break; } + if (total_out > total_out_limit) + break; if (result != BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) break; }
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json index 977ad9d..e82828e 100644 --- a/third_party/polymer/v1_0/bower.json +++ b/third_party/polymer/v1_0/bower.json
@@ -18,7 +18,7 @@ "iron-iconset-svg": "PolymerElements/iron-iconset-svg#1.0.11", "iron-icons": "PolymerElements/iron-icons#1.1.3", "iron-input": "PolymerElements/iron-input#1.0.10", - "iron-list": "PolymerElements/iron-list#1.3.12", + "iron-list": "PolymerElements/iron-list#1.4.4", "iron-location": "PolymerElements/iron-location#0.8.8", "iron-media-query": "PolymerElements/iron-media-query#1.0.8", "iron-menu-behavior": "PolymerElements/iron-menu-behavior#1.1.10",
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/bower.json b/third_party/polymer/v1_0/components-chromium/iron-list/bower.json index 92d98b1..b6ad0999 100644 --- a/third_party/polymer/v1_0/components-chromium/iron-list/bower.json +++ b/third_party/polymer/v1_0/components-chromium/iron-list/bower.json
@@ -7,7 +7,7 @@ "list", "virtual-list" ], - "version": "1.3.12", + "version": "1.4.4", "homepage": "https://github.com/PolymerElements/iron-list", "authors": [ "The Polymer Authors" @@ -21,8 +21,8 @@ "ignore": [], "dependencies": { "polymer": "Polymer/polymer#^1.1.0", - "iron-resizable-behavior": "polymerelements/iron-resizable-behavior#^1.0.0", - "iron-a11y-keys-behavior": "polymerelements/iron-a11y-keys-behavior#^1.0.0", + "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0", + "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0", "iron-scroll-target-behavior": "PolymerElements/iron-scroll-target-behavior#^1.1.0" }, "devDependencies": {
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js index 92a3550..9e9ec02 100644 --- a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js +++ b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
@@ -112,6 +112,20 @@ multiSelection: { type: Boolean, value: false + }, + + /** + * The offset top from the scrolling element to the iron-list element. + * This value can be computed using the position returned by `getBoundingClientRect()` + * although it's preferred to use a constant value when possible. + * + * This property is useful when an external scrolling element is used and there's + * some offset between the scrolling element and the list. + * For example: a header is placed above the list. + */ + scrollOffset: { + type: Number, + value: 0 } }, @@ -119,7 +133,7 @@ '_itemsChanged(items.*)', '_selectionEnabledChanged(selectionEnabled)', '_multiSelectionChanged(multiSelection)', - '_setOverflow(scrollTarget)' + '_setOverflow(scrollTarget, scrollOffset)' ], behaviors: [ @@ -311,10 +325,17 @@ }, /** + * The parent node for the _userTemplate. + */ + get _itemsParent() { + return Polymer.dom(Polymer.dom(this._userTemplate).parentNode); + }, + + /** * The maximum scroll top value. */ get _maxScrollTop() { - return this._estScrollHeight - this._viewportHeight + this._scrollerPaddingTop; + return this._estScrollHeight - this._viewportHeight + this._scrollOffset; }, /** @@ -335,7 +356,11 @@ _virtualStartVal: 0, set _virtualStart(val) { - this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val)); + val = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val)); + if (this.grid) { + val = val - (val % this._itemsPerRow); + } + this._virtualStartVal = val; }, get _virtualStart() { @@ -348,10 +373,14 @@ _physicalStartVal: 0, set _physicalStart(val) { - this._physicalStartVal = val % this._physicalCount; - if (this._physicalStartVal < 0) { - this._physicalStartVal = this._physicalCount + this._physicalStartVal; + val = val % this._physicalCount; + if (val < 0) { + val = this._physicalCount + val; } + if (this.grid) { + val = val - (val % this._itemsPerRow); + } + this._physicalStartVal = val; this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount; }, @@ -389,7 +418,7 @@ if (this.grid) { return this._estRowsInView * this._rowHeight * this._maxPages; } - return this._viewportHeight * this._maxPages; + return this._viewportHeight === 0 ? Infinity : this._viewportHeight * this._maxPages; }, /** @@ -405,23 +434,24 @@ * @type {number} */ get firstVisibleIndex() { - if (this._firstVisibleIndexVal === null) { - var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddingTop); + var idx = this._firstVisibleIndexVal; + if (idx == null) { + var physicalOffset = this._physicalTop + this._scrollOffset; - this._firstVisibleIndexVal = this._iterateItems( - function(pidx, vidx) { - physicalOffset += this._getPhysicalSizeIncrement(pidx); + idx = this._iterateItems(function(pidx, vidx) { + physicalOffset += this._getPhysicalSizeIncrement(pidx); - if (physicalOffset > this._scrollPosition) { - return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx; - } - // Handle a partially rendered final row in grid mode - if (this.grid && this._virtualCount - 1 === vidx) { - return vidx - (vidx % this._itemsPerRow); - } - }) || 0; + if (physicalOffset > this._scrollPosition) { + return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx; + } + // Handle a partially rendered final row in grid mode + if (this.grid && this._virtualCount - 1 === vidx) { + return vidx - (vidx % this._itemsPerRow); + } + }) || 0; + this._firstVisibleIndexVal = idx; } - return this._firstVisibleIndexVal; + return idx; }, /** @@ -430,24 +460,23 @@ * @type {number} */ get lastVisibleIndex() { - if (this._lastVisibleIndexVal === null) { + var idx = this._lastVisibleIndexVal; + if (idx == null) { if (this.grid) { - var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._itemsPerRow - 1; - this._lastVisibleIndexVal = Math.min(this._virtualCount, lastIndex); + idx = Math.min(this._virtualCount, + this.firstVisibleIndex + this._estRowsInView * this._itemsPerRow - 1); } else { - var physicalOffset = this._physicalTop; + var physicalOffset = this._physicalTop + this._scrollOffset; this._iterateItems(function(pidx, vidx) { if (physicalOffset < this._scrollBottom) { - this._lastVisibleIndexVal = vidx; - } else { - // Break _iterateItems - return true; + idx = vidx; } physicalOffset += this._getPhysicalSizeIncrement(pidx); }); } + this._lastVisibleIndexVal = idx; } - return this._lastVisibleIndexVal; + return idx; }, get _defaultScrollTarget() { @@ -466,6 +495,10 @@ return Math.ceil(this._physicalCount / this._itemsPerRow); }, + get _scrollOffset() { + return this._scrollerPaddingTop + this.scrollOffset; + }, + ready: function() { this.addEventListener('focus', this._didFocus.bind(this), true); }, @@ -489,6 +522,10 @@ _setOverflow: function(scrollTarget) { this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; this.style.overflow = scrollTarget === this ? 'auto' : ''; + // Clear cache. + this._lastVisibleIndexVal = null; + this._firstVisibleIndexVal = null; + this._debounceTemplate(this._render); }, /** @@ -498,8 +535,10 @@ * @method updateViewportBoundaries */ updateViewportBoundaries: function() { + var styles = window.getComputedStyle(this); this._scrollerPaddingTop = this.scrollTarget === this ? 0 : - parseInt(window.getComputedStyle(this)['padding-top'], 10); + parseInt(styles['padding-top'], 10); + this._isRTL = Boolean(styles.direction === 'rtl'); this._viewportWidth = this.$.items.offsetWidth; this._viewportHeight = this._scrollTargetHeight; this.grid && this._updateGridMetrics(); @@ -520,7 +559,8 @@ // Random access. if (Math.abs(delta) > this._physicalSize) { - var idxAdjustment = Math.round(delta / this._physicalAverage) * this._itemsPerRow + delta = delta - this._scrollOffset; + var idxAdjustment = Math.round(delta / this._physicalAverage) * this._itemsPerRow; this._physicalTop = this._physicalTop + delta; this._virtualStart = this._virtualStart + idxAdjustment; this._physicalStart = this._physicalStart + idxAdjustment; @@ -556,18 +596,19 @@ var virtualStart = this._virtualStart; var virtualEnd = this._virtualEnd; var physicalCount = this._physicalCount; - var physicalTop = this._physicalTop + this._scrollerPaddingTop; + var top = this._physicalTop + this._scrollOffset; + var bottom = this._physicalBottom + this._scrollOffset; var scrollTop = this._scrollTop; var scrollBottom = this._scrollBottom; if (fromTop) { ith = this._physicalStart; lastIth = this._physicalEnd; - offsetContent = scrollTop - physicalTop; + offsetContent = scrollTop - top; } else { ith = this._physicalEnd; lastIth = this._physicalStart; - offsetContent = this._physicalBottom - scrollBottom; + offsetContent = bottom - scrollBottom; } while (true) { physicalItemHeight = this._getPhysicalSizeIncrement(ith); @@ -581,11 +622,11 @@ break; } // Check that the index is not visible. - if (physicalTop + physicalItemHeight >= scrollTop) { + if (top + physicalItemHeight >= scrollTop - this._scrollOffset) { break; } idxs.push(ith); - physicalTop = physicalTop + physicalItemHeight; + top = top + physicalItemHeight; ith = (ith + 1) % physicalCount; } else { // Check that index is within the valid range. @@ -593,15 +634,15 @@ break; } // Check that the index is not visible. - if (physicalTop + this._physicalSize - physicalItemHeight <= scrollBottom) { + if (top + this._physicalSize - physicalItemHeight <= scrollBottom) { break; } idxs.push(ith); - physicalTop = physicalTop - physicalItemHeight; + top = top - physicalItemHeight; ith = (ith === 0) ? physicalCount - 1 : ith - 1; } } - return { indexes: idxs, physicalTop: physicalTop - this._scrollerPaddingTop }; + return { indexes: idxs, physicalTop: top - this._scrollOffset }; }, /** @@ -643,7 +684,7 @@ // First element child is item; Safari doesn't support children[0] // on a doc fragment. physicalItems[i] = inst.root.querySelector('*'); - Polymer.dom(this).appendChild(inst.root); + this._itemsParent.appendChild(inst.root); } return physicalItems; }, @@ -654,15 +695,10 @@ * @return {boolean} True if the pool was increased. */ _increasePoolIfNeeded: function() { - // Base case 1: the list has no height. - if (this._viewportHeight === 0) { - return false; - } var self = this; - var isClientFull = this._physicalBottom >= this._scrollBottom && - this._physicalTop <= this._scrollPosition; - - // Base case 2: if the physical size is optimal and the list's client height is full + var isClientFull = this._physicalBottom + this._scrollOffset >= this._scrollBottom && + this._physicalTop - this._scrollOffset <= this._scrollPosition; + // Base case 1: if the physical size is optimal and the list's client height is full // with physical items, don't increase the pool. if (this._physicalSize >= this._optPhysicalSize && isClientFull) { return false; @@ -756,7 +792,7 @@ props[this.selectedAs] = true; props.tabIndex = true; this._instanceProps = props; - this._userTemplate = Polymer.dom(this).querySelector('template'); + this._userTemplate = this.queryEffectiveChildren('template'); if (this._userTemplate) { this.templatize(this._userTemplate); @@ -791,11 +827,13 @@ * notifying parent path change on each row. */ _forwardParentProp: function(prop, value) { - if (this._physicalItems) { - this._physicalItems.forEach(function(item) { - item._templateInstance[prop] = value; - }, this); - } + (this._physicalItems || []) + .concat([this._offscreenFocusedItem, this._focusBackfillItem]) + .forEach(function(item) { + if (item) { + item._templateInstance[prop] = value; + } + }); }, /** @@ -804,11 +842,13 @@ * notifying parent.<path> path change on each row. */ _forwardParentPath: function(path, value) { - if (this._physicalItems) { - this._physicalItems.forEach(function(item) { - item._templateInstance.notifyPath(path, value, true); - }, this); - } + (this._physicalItems || []) + .concat([this._offscreenFocusedItem, this._focusBackfillItem]) + .forEach(function(item) { + if (item) { + item._templateInstance.notifyPath(path, value, true); + } + }); }, /** @@ -866,7 +906,9 @@ this._physicalItems = this._physicalItems || []; this._physicalSizes = this._physicalSizes || []; this._physicalStart = 0; - this._resetScrollPosition(0); + if (this._scrollTop > this._scrollOffset) { + this._resetScrollPosition(0); + } this._removeFocusedItem(); this._debounceTemplate(this._render); @@ -875,7 +917,6 @@ this._virtualCount = this.items ? this.items.length : 0; this._debounceTemplate(this._render); - } else { this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.value); } @@ -1040,6 +1081,9 @@ this._iterateItems(function(pidx, vidx) { var modulus = vidx % this._itemsPerRow; var x = Math.floor((modulus * this._itemWidth) + rowOffset); + if (this._isRTL) { + x = x * -1; + } this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]); if (this._shouldRenderNextRow(vidx)) { y += this._rowHeight; @@ -1081,12 +1125,13 @@ _adjustScrollPosition: function() { var deltaHeight = this._virtualStart === 0 ? this._physicalTop : Math.min(this._scrollPosition + this._physicalTop, 0); - - if (deltaHeight) { + // Note: the delta can be positive or negative. + if (deltaHeight !== 0) { this._physicalTop = this._physicalTop - deltaHeight; + var scrollTop = this._scrollTop; // juking scroll position during interial scrolling on iOS is no bueno - if (!IOS_TOUCH_SCROLLING && this._physicalTop !== 0) { - this._resetScrollPosition(this._scrollTop - deltaHeight); + if (!IOS_TOUCH_SCROLLING && scrollTop > 0) { + this._resetScrollPosition(scrollTop - deltaHeight); } } }, @@ -1095,7 +1140,7 @@ * Sets the position of the scroll. */ _resetScrollPosition: function(pos) { - if (this.scrollTarget) { + if (this.scrollTarget && pos >= 0) { this._scrollTop = pos; this._scrollPosition = this._scrollTop; } @@ -1147,7 +1192,6 @@ if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) { return; } - Polymer.dom.flush(); // Items should have been rendered prior scrolling to an index. if (this._physicalCount === 0) { @@ -1176,7 +1220,7 @@ } this._updateScrollerSize(true); this._positionItems(); - this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + targetOffsetTop); + this._resetScrollPosition(this._physicalTop + this._scrollOffset + targetOffsetTop); this._increasePoolIfNeeded(); // clear cached visible index. this._firstVisibleIndexVal = null; @@ -1196,27 +1240,23 @@ * when the element is resized. */ _resizeHandler: function() { - // iOS fires the resize event when the address bar slides up - var delta = Math.abs(this._viewportHeight - this._scrollTargetHeight); - if (IOS && delta > 0 && delta < 100) { - return; - } - // In Desktop Safari 9.0.3, if the scroll bars are always shown, - // changing the scroll position from a resize handler would result in - // the scroll position being reset. Waiting 1ms fixes the issue. - Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() { + this._debounceTemplate(function() { + // Skip the resize event on touch devices when the address bar slides up. + var delta = Math.abs(this._viewportHeight - this._scrollTargetHeight); this.updateViewportBoundaries(); - this._render(); + if (('ontouchstart' in window || navigator.maxTouchPoints > 0) && delta > 0 && delta < 100) { + return; + } if (this._isVisible) { + // Reinstall the scroll event listener. this.toggleScrollListener(true); - if (this._physicalCount > 0) { - this._resetAverage(); - this.scrollToIndex(this.firstVisibleIndex); - } + this._resetAverage(); + this._render(); } else { + // Uninstall the scroll event listener. this.toggleScrollListener(false); } - }.bind(this), 1)); + }.bind(this)); }, _getModelFromItem: function(item) { @@ -1343,7 +1383,8 @@ } var modelTabIndex, activeElTabIndex; var target = Polymer.dom(e).path[0]; - var activeEl = Polymer.dom(this.domHost ? this.domHost.root : document).activeElement; + var itemsHost = this._itemsParent.node.domHost; + var activeEl = Polymer.dom(itemsHost ? itemsHost.root : document).activeElement; var physicalItem = this._physicalItems[this._getPhysicalIndex(model[this.indexAs])]; // Safari does not focus certain form controls via mouse // https://bugs.webkit.org/show_bug.cgi?id=118043 @@ -1456,7 +1497,7 @@ _removeFocusedItem: function() { if (this._offscreenFocusedItem) { - Polymer.dom(this).removeChild(this._offscreenFocusedItem); + this._itemsParent.removeChild(this._offscreenFocusedItem); } this._offscreenFocusedItem = null; this._focusBackfillItem = null; @@ -1475,7 +1516,7 @@ // Create a physical item. var stampedTemplate = this.stamp(null); this._focusBackfillItem = stampedTemplate.root.querySelector('*'); - Polymer.dom(this).appendChild(stampedTemplate.root); + this._itemsParent.appendChild(stampedTemplate.root); } // Set the offcreen focused physical item. this._offscreenFocusedItem = this._physicalItems[pidx]; @@ -1496,17 +1537,27 @@ // Get the new physical index for the focused index. pidx = this._getPhysicalIndex(fidx); - if (pidx != null) { + var onScreenItem = this._physicalItems[pidx]; + if (!onScreenItem) { + return; + } + var onScreenInstance = onScreenItem._templateInstance; + var offScreenInstance = this._offscreenFocusedItem._templateInstance; + // Restores the physical item only when it has the same model + // as the offscreen one. Use key for comparison since users can set + // a new item via set('items.idx'). + if (onScreenInstance.__key__ === offScreenInstance.__key__) { // Flip the focus backfill. - this._focusBackfillItem = this._physicalItems[pidx]; - this._focusBackfillItem._templateInstance.tabIndex = -1; + this._focusBackfillItem = onScreenItem; + onScreenInstance.tabIndex = -1; // Restore the focused physical item. this._physicalItems[pidx] = this._offscreenFocusedItem; - // Reset the offscreen focused item. - this._offscreenFocusedItem = null; // Hide the physical item that backfills. this.translate3d(0, HIDDEN_Y, 0, this._focusBackfillItem); + } else { + this._focusBackfillItem = null; } + this._offscreenFocusedItem = null; }, _didFocus: function(e) {
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html index 33e8bad..a13d8b08 100644 --- a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html +++ b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list.html
@@ -48,11 +48,12 @@ flex: 1 1 auto; } </style> - <app-toolbar>App name</app-toolbar> <iron-list items="[[items]]"> <template> - ... + <div> + ... + </div> </template> </iron-list> </template> @@ -71,7 +72,9 @@ </style> <iron-list items="[[items]]"> <template> - ... + <div> + ... + </div> </template> </iron-list> </template> @@ -103,15 +106,20 @@ <body> <template is="dom-bind"> <app-toolbar>App name</app-toolbar> - <iron-list target="document" items="[[items]]"> + <iron-list scroll-target="document" items="[[items]]"> <template> - ... + <div> + ... + </div> </template> </iron-list> </template> </body> ``` +`iron-list` must be given a `<template>` which contains exactly one element. In the examples +above we used a `<div>`, but you can provide any element (including custom elements). + ### Template model List item templates should bind to template models of the following structure: @@ -210,13 +218,13 @@ ### When should `<iron-list>` be used? `iron-list` should be used when a page has significantly more DOM nodes than the ones -visible on the screen. e.g. the page has 500 nodes, but only 20 are visible at the time. +visible on the screen. e.g. the page has 500 nodes, but only 20 are visible at a time. This is why we refer to it as a `virtual` list. In this case, a `dom-repeat` will still create 500 nodes which could slow down the web app, but `iron-list` will only create 20. However, having an `iron-list` does not mean that you can load all the data at once. -Say, you have a million records in the database, you want to split the data into pages -so you can bring a page at the time. The page could contain 500 items, and iron-list +Say you have a million records in the database, you want to split the data into pages +so you can bring in a page at the time. The page could contain 500 items, and iron-list will only render 20. @group Iron Element @@ -230,7 +238,6 @@ <style> :host { display: block; - position: relative; } @media only screen and (-webkit-max-device-pixel-ratio: 1) { @@ -246,7 +253,7 @@ :host(:not([grid])) #items > ::content > * { width: 100%; - }; + } #items > ::content > * { box-sizing: border-box;
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt index a4e7b0be..6ef41c5a 100644 --- a/third_party/polymer/v1_0/components_summary.txt +++ b/third_party/polymer/v1_0/components_summary.txt
@@ -96,9 +96,9 @@ Name: iron-list Repository: https://github.com/PolymerElements/iron-list.git -Tree: v1.3.12 -Revision: 856ebcf90b2834aae728fc295b5dd5aa5ebc18ca -Tree link: https://github.com/PolymerElements/iron-list/tree/v1.3.12 +Tree: v1.4.4 +Revision: 93490bcb5baba88d642d1f550dad3ed52b2c864f +Tree link: https://github.com/PolymerElements/iron-list/tree/v1.4.4 Name: iron-location Repository: https://github.com/PolymerElements/iron-location.git