diff --git a/.gitignore b/.gitignore index d9f918d109bc..3f1279eebf42 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ /dist/ /external_binaries/ /out/ -/vendor/brightray/vendor/download/ +/vendor/download/ /vendor/debian_wheezy_amd64-sysroot/ /vendor/debian_wheezy_arm-sysroot/ /vendor/debian_wheezy_i386-sysroot/ @@ -27,3 +27,10 @@ node_modules/ *.vcxproj.filters *.sln *.log +/brightray/brightray.opensdf +/brightray/brightray.sdf +/brightray/brightray.sln +/brightray/brightray.vcxproj* +/brightray/brightray.suo +/brightray/brightray.v12.suo +/brightray/brightray.xcodeproj/ diff --git a/.gitmodules b/.gitmodules index 0cabff390f47..73b12931d77f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "vendor/brightray"] - path = vendor/brightray - url = https://github.com/electron/brightray.git [submodule "vendor/node"] path = vendor/node url = https://github.com/electron/node.git @@ -25,3 +22,9 @@ [submodule "vendor/pdf_viewer"] path = vendor/pdf_viewer url = https://github.com/electron/pdf-viewer.git +[submodule "vendor/gyp"] + path = vendor/gyp + url = https://github.com/electron/gyp +[submodule "vendor/libchromiumcontent"] + path = vendor/libchromiumcontent + url = https://github.com/electron/libchromiumcontent diff --git a/brightray/CPPLINT.cfg b/brightray/CPPLINT.cfg new file mode 100644 index 000000000000..01e99482f2b0 --- /dev/null +++ b/brightray/CPPLINT.cfg @@ -0,0 +1 @@ +filter=-build/header_guard,-build/include_what_you_use,-legal/copyright,-runtime/references diff --git a/brightray/LICENSE b/brightray/LICENSE new file mode 100644 index 000000000000..fe24a3c65fbd --- /dev/null +++ b/brightray/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2014 Adam Roben + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/brightray/LICENSE-CHROMIUM b/brightray/LICENSE-CHROMIUM new file mode 100644 index 000000000000..3d0f7d3edfd8 --- /dev/null +++ b/brightray/LICENSE-CHROMIUM @@ -0,0 +1,27 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/brightray/brightray.gyp b/brightray/brightray.gyp new file mode 100644 index 000000000000..efa0f433ba82 --- /dev/null +++ b/brightray/brightray.gyp @@ -0,0 +1,397 @@ +{ + 'variables': { + # The libraries brightray will be compiled to. + 'linux_system_libraries': 'gtk+-2.0 dbus-1 x11 x11-xcb xcb xi xcursor xdamage xrandr xcomposite xext xfixes xrender xtst xscrnsaver gconf-2.0 gmodule-2.0 nss' + }, + 'includes': [ + 'filenames.gypi', + ], + 'targets': [ + { + 'target_name': 'brightray', + 'type': 'static_library', + 'include_dirs': [ + '.', + '<(libchromiumcontent_src_dir)', + '<(libchromiumcontent_src_dir)/skia/config', + '<(libchromiumcontent_src_dir)/third_party/boringssl/src/include', + '<(libchromiumcontent_src_dir)/third_party/skia/include/core', + '<(libchromiumcontent_src_dir)/third_party/mojo/src', + '<(libchromiumcontent_src_dir)/third_party/WebKit', + '<(libchromiumcontent_dir)/gen', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '.', + '../vendor', + '<(libchromiumcontent_src_dir)', + '<(libchromiumcontent_src_dir)/gpu', + '<(libchromiumcontent_src_dir)/skia/config', + '<(libchromiumcontent_src_dir)/third_party/boringssl/src/include', + '<(libchromiumcontent_src_dir)/third_party/skia/include/core', + '<(libchromiumcontent_src_dir)/third_party/skia/include/config', + '<(libchromiumcontent_src_dir)/third_party/icu/source/common', + '<(libchromiumcontent_src_dir)/third_party/mojo/src', + '<(libchromiumcontent_src_dir)/third_party/khronos', + '<(libchromiumcontent_src_dir)/third_party/WebKit', + '<(libchromiumcontent_dir)/gen', + '<(libchromiumcontent_dir)/gen/third_party/WebKit', + ], + }, + 'sources': [ '<@(brightray_sources)' ], + 'conditions': [ + # Link with libraries of libchromiumcontent. + ['OS=="linux" and libchromiumcontent_component==0', { + # On Linux we have to use "--whole-archive" to force executable + # to include all symbols, otherwise we will have plenty of + # unresolved symbols errors. + 'direct_dependent_settings': { + 'ldflags': [ + '-Wl,--whole-archive', + '<@(libchromiumcontent_libraries)', + '-Wl,--no-whole-archive', + ], + } + }, { # (Release build on Linux) + 'link_settings': { + 'libraries': [ '<@(libchromiumcontent_libraries)' ] + }, + }], # (Normal builds) + # Linux specific link settings. + ['OS=="linux"', { + 'link_settings': { + 'ldflags': [ + '* additional_schemes) { + additional_schemes->push_back(content::kChromeDevToolsScheme); + additional_schemes->push_back(content::kChromeUIScheme); +} + +net::NetLog* BrowserClient::GetNetLog() { + return &net_log_; +} + +base::FilePath BrowserClient::GetDefaultDownloadDirectory() { + // ~/Downloads + base::FilePath path; + if (PathService::Get(base::DIR_HOME, &path)) + path = path.Append(FILE_PATH_LITERAL("Downloads")); + + return path; +} + +content::DevToolsManagerDelegate* BrowserClient::GetDevToolsManagerDelegate() { + return new DevToolsManagerDelegate; +} + +} // namespace brightray diff --git a/brightray/browser/browser_client.h b/brightray/browser/browser_client.h new file mode 100644 index 000000000000..574103b91ca3 --- /dev/null +++ b/brightray/browser/browser_client.h @@ -0,0 +1,69 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_BROWSER_CLIENT_H_ +#define BRIGHTRAY_BROWSER_BROWSER_CLIENT_H_ + +#include "browser/net_log.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/content_browser_client.h" + +namespace brightray { + +class BrowserContext; +class BrowserMainParts; +class NotificationPresenter; +class PlatformNotificationService; + +class BrowserClient : public content::ContentBrowserClient { + public: + static BrowserClient* Get(); + + BrowserClient(); + ~BrowserClient(); + + BrowserMainParts* browser_main_parts() { return browser_main_parts_; } + + NotificationPresenter* GetNotificationPresenter(); + + // Subclasses should override this to enable or disable WebNotification. + virtual void WebNotificationAllowed( + int render_process_id, + const base::Callback& callback) { + callback.Run(false, true); + } + + // Subclasses that override this (e.g., to provide their own protocol + // handlers) should call this implementation after doing their own work. + content::BrowserMainParts* CreateBrowserMainParts( + const content::MainFunctionParams&) override; + content::MediaObserver* GetMediaObserver() override; + content::PlatformNotificationService* GetPlatformNotificationService() + override; + void GetAdditionalAllowedSchemesForFileSystem( + std::vector* additional_schemes) override; + net::NetLog* GetNetLog() override; + base::FilePath GetDefaultDownloadDirectory() override; + content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override; + + protected: + // Subclasses should override this to provide their own BrowserMainParts + // implementation. The lifetime of the returned instance is managed by the + // caller. + virtual BrowserMainParts* OverrideCreateBrowserMainParts( + const content::MainFunctionParams&); + + private: + BrowserMainParts* browser_main_parts_; + NetLog net_log_; + + std::unique_ptr notification_service_; + std::unique_ptr notification_presenter_; + + DISALLOW_COPY_AND_ASSIGN(BrowserClient); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/browser_context.cc b/brightray/browser/browser_context.cc new file mode 100644 index 000000000000..46f76302d74b --- /dev/null +++ b/brightray/browser/browser_context.cc @@ -0,0 +1,240 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "browser/browser_context.h" + +#include "browser/media/media_device_id_salt.h" +#include "browser/brightray_paths.h" +#include "browser/browser_client.h" +#include "browser/inspectable_web_contents_impl.h" +#include "browser/network_delegate.h" +#include "browser/permission_manager.h" +#include "browser/special_storage_policy.h" +#include "browser/zoom_level_delegate.h" +#include "common/application_info.h" + +#include "base/files/file_path.h" +#include "base/path_service.h" + +#include "components/prefs/json_pref_store.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/pref_service_factory.h" + +#include "base/strings/string_util.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_context.h" +#include "content/public/browser/storage_partition.h" +#include "net/base/escape.h" + +using content::BrowserThread; + +namespace brightray { + +namespace { + +// Convert string to lower case and escape it. +std::string MakePartitionName(const std::string& input) { + return net::EscapePath(base::ToLowerASCII(input)); +} + +} // namespace + +class BrowserContext::ResourceContext : public content::ResourceContext { + public: + ResourceContext() : getter_(nullptr) {} + + void set_url_request_context_getter(URLRequestContextGetter* getter) { + getter_ = getter; + } + + private: + net::HostResolver* GetHostResolver() override { + return getter_->host_resolver(); + } + + net::URLRequestContext* GetRequestContext() override { + return getter_->GetURLRequestContext(); + } + + std::string GetMediaDeviceIDSalt() override { + auto media_device_id_salt_ = getter_->GetMediaDeviceIDSalt(); + if (media_device_id_salt_) + return media_device_id_salt_->GetSalt(); + return content::ResourceContext::GetMediaDeviceIDSalt(); + } + + URLRequestContextGetter* getter_; +}; + +// static +BrowserContext::BrowserContextMap BrowserContext::browser_context_map_; + +// static +scoped_refptr BrowserContext::Get( + const std::string& partition, bool in_memory) { + PartitionKey key(partition, in_memory); + if (browser_context_map_[key].get()) + return make_scoped_refptr(browser_context_map_[key].get()); + + return nullptr; +} + +BrowserContext::BrowserContext(const std::string& partition, bool in_memory) + : in_memory_(in_memory), + resource_context_(new ResourceContext), + storage_policy_(new SpecialStoragePolicy), + weak_factory_(this) { + if (!PathService::Get(DIR_USER_DATA, &path_)) { + PathService::Get(DIR_APP_DATA, &path_); + path_ = path_.Append(base::FilePath::FromUTF8Unsafe(GetApplicationName())); + PathService::Override(DIR_USER_DATA, path_); + } + + if (!in_memory_ && !partition.empty()) + path_ = path_.Append(FILE_PATH_LITERAL("Partitions")) + .Append(base::FilePath::FromUTF8Unsafe( + MakePartitionName(partition))); + + content::BrowserContext::Initialize(this, path_); + + browser_context_map_[PartitionKey(partition, in_memory)] = GetWeakPtr(); +} + +BrowserContext::~BrowserContext() { + NotifyWillBeDestroyed(this); + ShutdownStoragePartitions(); + BrowserThread::DeleteSoon(BrowserThread::IO, + FROM_HERE, + resource_context_.release()); +} + +void BrowserContext::InitPrefs() { + auto prefs_path = GetPath().Append(FILE_PATH_LITERAL("Preferences")); + PrefServiceFactory prefs_factory; + prefs_factory.SetUserPrefsFile(prefs_path, + JsonPrefStore::GetTaskRunnerForFile( + prefs_path, BrowserThread::GetBlockingPool()).get()); + + auto registry = make_scoped_refptr(new PrefRegistrySimple); + RegisterInternalPrefs(registry.get()); + RegisterPrefs(registry.get()); + + prefs_ = prefs_factory.Create(registry.get()); +} + +void BrowserContext::RegisterInternalPrefs(PrefRegistrySimple* registry) { + InspectableWebContentsImpl::RegisterPrefs(registry); + MediaDeviceIDSalt::RegisterPrefs(registry); + ZoomLevelDelegate::RegisterPrefs(registry); +} + +URLRequestContextGetter* BrowserContext::GetRequestContext() { + return static_cast( + GetDefaultStoragePartition(this)->GetURLRequestContext()); +} + +net::URLRequestContextGetter* BrowserContext::CreateRequestContext( + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector protocol_interceptors) { + DCHECK(!url_request_getter_.get()); + url_request_getter_ = new URLRequestContextGetter( + this, + network_controller_handle(), + static_cast(BrowserClient::Get()->GetNetLog()), + GetPath(), + in_memory_, + BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), + BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE), + protocol_handlers, + std::move(protocol_interceptors)); + resource_context_->set_url_request_context_getter(url_request_getter_.get()); + return url_request_getter_.get(); +} + +net::NetworkDelegate* BrowserContext::CreateNetworkDelegate() { + return new NetworkDelegate; +} + +MediaDeviceIDSalt* BrowserContext::GetMediaDeviceIDSalt() { + if (IsOffTheRecord()) + return nullptr; + if (!media_device_id_salt_.get()) + media_device_id_salt_.reset(new MediaDeviceIDSalt(prefs_.get())); + return media_device_id_salt_.get(); +} + +base::FilePath BrowserContext::GetPath() const { + return path_; +} + +std::unique_ptr +BrowserContext::CreateZoomLevelDelegate(const base::FilePath& partition_path) { + if (!IsOffTheRecord()) { + return base::MakeUnique(prefs(), partition_path); + } + return std::unique_ptr(); +} + +bool BrowserContext::IsOffTheRecord() const { + return in_memory_; +} + +content::ResourceContext* BrowserContext::GetResourceContext() { + return resource_context_.get(); +} + +content::DownloadManagerDelegate* BrowserContext::GetDownloadManagerDelegate() { + return nullptr; +} + +content::BrowserPluginGuestManager* BrowserContext::GetGuestManager() { + return nullptr; +} + +storage::SpecialStoragePolicy* BrowserContext::GetSpecialStoragePolicy() { + return storage_policy_.get(); +} + +content::PushMessagingService* BrowserContext::GetPushMessagingService() { + return nullptr; +} + +content::SSLHostStateDelegate* BrowserContext::GetSSLHostStateDelegate() { + return nullptr; +} + +content::PermissionManager* BrowserContext::GetPermissionManager() { + if (!permission_manager_.get()) + permission_manager_.reset(new PermissionManager); + return permission_manager_.get(); +} + +content::BackgroundSyncController* +BrowserContext::GetBackgroundSyncController() { + return nullptr; +} + +net::URLRequestContextGetter* +BrowserContext::CreateRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory, + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector request_interceptors) { + return nullptr; +} + +net::URLRequestContextGetter* +BrowserContext::CreateMediaRequestContext() { + return url_request_getter_.get(); +} + +net::URLRequestContextGetter* +BrowserContext::CreateMediaRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory) { + return nullptr; +} + +} // namespace brightray diff --git a/brightray/browser/browser_context.h b/brightray/browser/browser_context.h new file mode 100644 index 000000000000..498fe9940ada --- /dev/null +++ b/brightray/browser/browser_context.h @@ -0,0 +1,142 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_BROWSER_CONTEXT_H_ +#define BRIGHTRAY_BROWSER_BROWSER_CONTEXT_H_ + +#include + +#include "browser/net/devtools_network_controller_handle.h" +#include "browser/permission_manager.h" +#include "browser/url_request_context_getter.h" + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/browser_context.h" + +class PrefRegistrySimple; +class PrefService; + +namespace storage { +class SpecialStoragePolicy; +} + +namespace brightray { + +class MediaDeviceIDSalt; +class PermissionManager; + +class BrowserContext : public base::RefCounted, + public content::BrowserContext, + public brightray::URLRequestContextGetter::Delegate { + public: + // Get the BrowserContext according to its |partition| and |in_memory|, + // empty pointer when be returned when there is no matching BrowserContext. + static scoped_refptr Get( + const std::string& partition, bool in_memory); + + base::WeakPtr GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + // Get the request context, if there is no one, create it. + URLRequestContextGetter* GetRequestContext(); + + // content::BrowserContext: + std::unique_ptr CreateZoomLevelDelegate( + const base::FilePath& partition_path) override; + bool IsOffTheRecord() const override; + content::ResourceContext* GetResourceContext() override; + content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; + content::BrowserPluginGuestManager* GetGuestManager() override; + storage::SpecialStoragePolicy* GetSpecialStoragePolicy() override; + content::PushMessagingService* GetPushMessagingService() override; + content::SSLHostStateDelegate* GetSSLHostStateDelegate() override; + content::PermissionManager* GetPermissionManager() override; + content::BackgroundSyncController* GetBackgroundSyncController() override; + net::URLRequestContextGetter* CreateRequestContext( + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector request_interceptors) override; + net::URLRequestContextGetter* CreateRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory, + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector request_interceptors) override; + net::URLRequestContextGetter* CreateMediaRequestContext() override; + net::URLRequestContextGetter* CreateMediaRequestContextForStoragePartition( + const base::FilePath& partition_path, + bool in_memory) override; + + URLRequestContextGetter* url_request_context_getter() const { + return url_request_getter_.get(); + } + + DevToolsNetworkControllerHandle* network_controller_handle() { + return &network_controller_handle_; + } + + void InitPrefs(); + PrefService* prefs() { return prefs_.get(); } + + protected: + BrowserContext(const std::string& partition, bool in_memory); + ~BrowserContext() override; + + // Subclasses should override this to register custom preferences. + virtual void RegisterPrefs(PrefRegistrySimple* pref_registry) {} + + // URLRequestContextGetter::Delegate: + net::NetworkDelegate* CreateNetworkDelegate() override; + MediaDeviceIDSalt* GetMediaDeviceIDSalt() override; + + base::FilePath GetPath() const override; + + private: + friend class base::RefCounted; + class ResourceContext; + + void RegisterInternalPrefs(PrefRegistrySimple* pref_registry); + + // partition_id => browser_context + struct PartitionKey { + std::string partition; + bool in_memory; + + PartitionKey(const std::string& partition, bool in_memory) + : partition(partition), in_memory(in_memory) {} + + bool operator<(const PartitionKey& other) const { + if (partition == other.partition) + return in_memory < other.in_memory; + return partition < other.partition; + } + + bool operator==(const PartitionKey& other) const { + return (partition == other.partition) && (in_memory == other.in_memory); + } + }; + using BrowserContextMap = + std::map>; + static BrowserContextMap browser_context_map_; + + base::FilePath path_; + bool in_memory_; + + DevToolsNetworkControllerHandle network_controller_handle_; + + std::unique_ptr resource_context_; + scoped_refptr url_request_getter_; + scoped_refptr storage_policy_; + std::unique_ptr prefs_; + std::unique_ptr permission_manager_; + std::unique_ptr media_device_id_salt_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(BrowserContext); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/browser_main_parts.cc b/brightray/browser/browser_main_parts.cc new file mode 100644 index 000000000000..8d46b4af4b4c --- /dev/null +++ b/brightray/browser/browser_main_parts.cc @@ -0,0 +1,261 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "browser/browser_main_parts.h" + +#include "browser/browser_context.h" +#include "browser/devtools_manager_delegate.h" +#include "browser/web_ui_controller_factory.h" +#include "common/main_delegate.h" + +#include "base/command_line.h" +#include "base/feature_list.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/common/content_switches.h" +#include "media/base/localized_strings.h" +#include "net/proxy/proxy_resolver_v8.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" + +#if defined(USE_AURA) +#include "ui/display/display.h" +#include "ui/display/screen.h" +#include "ui/views/widget/desktop_aura/desktop_screen.h" +#include "ui/wm/core/wm_state.h" +#endif + +#if defined(TOOLKIT_VIEWS) +#include "browser/views/views_delegate.h" +#endif + +#if defined(USE_X11) +#include "base/environment.h" +#include "base/path_service.h" +#include "base/nix/xdg_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "browser/brightray_paths.h" +#include "chrome/browser/ui/libgtkui/gtk_ui.h" +#include "ui/base/x/x11_util.h" +#include "ui/base/x/x11_util_internal.h" +#include "ui/views/linux_ui/linux_ui.h" +#endif + +#if defined(OS_WIN) +#include "ui/base/cursor/cursor_loader_win.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/gfx/platform_font_win.h" +#endif + +#if defined(OS_LINUX) +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/dbus/dbus_bluez_manager_wrapper_linux.h" +#endif + +namespace brightray { + +namespace { + +#if defined(OS_WIN) +// gfx::Font callbacks +void AdjustUIFont(LOGFONT* logfont) { + l10n_util::AdjustUIFont(logfont); +} + +int GetMinimumFontSize() { + return 10; +} +#endif + +#if defined(USE_X11) +// Indicates that we're currently responding to an IO error (by shutting down). +bool g_in_x11_io_error_handler = false; + +// Number of seconds to wait for UI thread to get an IO error if we get it on +// the background thread. +const int kWaitForUIThreadSeconds = 10; + +void OverrideLinuxAppDataPath() { + base::FilePath path; + if (PathService::Get(DIR_APP_DATA, &path)) + return; + std::unique_ptr env(base::Environment::Create()); + path = base::nix::GetXDGDirectory(env.get(), + base::nix::kXdgConfigHomeEnvVar, + base::nix::kDotConfigDir); + PathService::Override(DIR_APP_DATA, path); +} + +int BrowserX11ErrorHandler(Display* d, XErrorEvent* error) { + if (!g_in_x11_io_error_handler) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&ui::LogErrorEventDescription, d, *error)); + } + return 0; +} + +// This function is used to help us diagnose crash dumps that happen +// during the shutdown process. +NOINLINE void WaitingForUIThreadToHandleIOError() { + // Ensure function isn't optimized away. + asm(""); + sleep(kWaitForUIThreadSeconds); +} + +int BrowserX11IOErrorHandler(Display* d) { + if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { + // Wait for the UI thread (which has a different connection to the X server) + // to get the error. We can't call shutdown from this thread without + // tripping an error. Doing it through a function so that we'll be able + // to see it in any crash dumps. + WaitingForUIThreadToHandleIOError(); + return 0; + } + + // If there's an IO error it likely means the X server has gone away. + // If this CHECK fails, then that means SessionEnding() below triggered some + // code that tried to talk to the X server, resulting in yet another error. + CHECK(!g_in_x11_io_error_handler); + + g_in_x11_io_error_handler = true; + LOG(ERROR) << "X IO error received (X server probably went away)"; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + + return 0; +} + +int X11EmptyErrorHandler(Display* d, XErrorEvent* error) { + return 0; +} + +int X11EmptyIOErrorHandler(Display* d) { + return 0; +} +#endif + +base::string16 MediaStringProvider(media::MessageId id) { + switch (id) { + case media::DEFAULT_AUDIO_DEVICE_NAME: + return base::ASCIIToUTF16("Default"); +#if defined(OS_WIN) + case media::COMMUNICATIONS_AUDIO_DEVICE_NAME: + return base::ASCIIToUTF16("Communications"); +#endif + default: + return base::string16(); + } +} + +} // namespace + +BrowserMainParts::BrowserMainParts() { +} + +BrowserMainParts::~BrowserMainParts() { +} + +void BrowserMainParts::PreEarlyInitialization() { + std::unique_ptr feature_list(new base::FeatureList); + feature_list->InitializeFromCommandLine("", ""); + base::FeatureList::SetInstance(std::move(feature_list)); + +#if defined(USE_X11) + views::LinuxUI::SetInstance(BuildGtkUi()); + OverrideLinuxAppDataPath(); + + // Installs the X11 error handlers for the browser process used during + // startup. They simply print error messages and exit because + // we can't shutdown properly while creating and initializing services. + ui::SetX11ErrorHandlers(nullptr, nullptr); +#endif +} + +void BrowserMainParts::ToolkitInitialized() { + ui::MaterialDesignController::Initialize(); + +#if defined(USE_AURA) && defined(USE_X11) + views::LinuxUI::instance()->Initialize(); +#endif + +#if defined(USE_AURA) + wm_state_.reset(new wm::WMState); +#endif + +#if defined(TOOLKIT_VIEWS) + views_delegate_.reset(new ViewsDelegate); +#endif + +#if defined(OS_WIN) + gfx::PlatformFontWin::adjust_font_callback = &AdjustUIFont; + gfx::PlatformFontWin::get_minimum_font_size_callback = &GetMinimumFontSize; + + wchar_t module_name[MAX_PATH] = { 0 }; + if (GetModuleFileName(NULL, module_name, MAX_PATH)) + ui::CursorLoaderWin::SetCursorResourceModule(module_name); +#endif +} + +void BrowserMainParts::PreMainMessageLoopStart() { +#if defined(OS_MACOSX) + l10n_util::OverrideLocaleWithCocoaLocale(); +#endif + InitializeResourceBundle(""); +#if defined(OS_MACOSX) + InitializeMainNib(); +#endif + media::SetLocalizedStringProvider(MediaStringProvider); +} + +void BrowserMainParts::PreMainMessageLoopRun() { + content::WebUIControllerFactory::RegisterFactory( + WebUIControllerFactory::GetInstance()); + + // --remote-debugging-port + auto command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kRemoteDebuggingPort)) + DevToolsManagerDelegate::StartHttpHandler(); +} + +void BrowserMainParts::PostMainMessageLoopStart() { +#if defined(USE_X11) + // Installs the X11 error handlers for the browser process after the + // main message loop has started. This will allow us to exit cleanly + // if X exits before us. + ui::SetX11ErrorHandlers(BrowserX11ErrorHandler, BrowserX11IOErrorHandler); +#endif +#if defined(OS_LINUX) + bluez::DBusBluezManagerWrapperLinux::Initialize(); +#endif +} + +void BrowserMainParts::PostMainMessageLoopRun() { +#if defined(USE_X11) + // Unset the X11 error handlers. The X11 error handlers log the errors using a + // |PostTask()| on the message-loop. But since the message-loop is in the + // process of terminating, this can cause errors. + ui::SetX11ErrorHandlers(X11EmptyErrorHandler, X11EmptyIOErrorHandler); +#endif +} + +int BrowserMainParts::PreCreateThreads() { +#if defined(USE_AURA) + display::Screen* screen = views::CreateDesktopScreen(); + display::Screen::SetScreenInstance(screen); +#if defined(USE_X11) + views::LinuxUI::instance()->UpdateDeviceScaleFactor(); +#endif +#endif + return 0; +} + +void BrowserMainParts::PostDestroyThreads() { +#if defined(OS_LINUX) + device::BluetoothAdapterFactory::Shutdown(); + bluez::DBusBluezManagerWrapperLinux::Shutdown(); +#endif +} + +} // namespace brightray diff --git a/brightray/browser/browser_main_parts.h b/brightray/browser/browser_main_parts.h new file mode 100644 index 000000000000..ab9b086d786b --- /dev/null +++ b/brightray/browser/browser_main_parts.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_BROWSER_MAIN_PARTS_H_ +#define BRIGHTRAY_BROWSER_BROWSER_MAIN_PARTS_H_ + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "content/public/browser/browser_main_parts.h" + +#if defined(TOOLKIT_VIEWS) +namespace brightray { +class ViewsDelegate; +} +#endif + +#if defined(USE_AURA) +namespace wm { +class WMState; +} +#endif + +namespace brightray { + +class BrowserMainParts : public content::BrowserMainParts { + public: + BrowserMainParts(); + ~BrowserMainParts(); + + protected: + // content::BrowserMainParts: + void PreEarlyInitialization() override; + void ToolkitInitialized() override; + void PreMainMessageLoopStart() override; + void PreMainMessageLoopRun() override; + void PostMainMessageLoopStart() override; + void PostMainMessageLoopRun() override; + int PreCreateThreads() override; + void PostDestroyThreads() override; + + private: +#if defined(OS_MACOSX) + void InitializeMainNib(); +#endif + +#if defined(TOOLKIT_VIEWS) + std::unique_ptr views_delegate_; +#endif + +#if defined(USE_AURA) + std::unique_ptr wm_state_; +#endif + + DISALLOW_COPY_AND_ASSIGN(BrowserMainParts); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/browser_main_parts_mac.mm b/brightray/browser/browser_main_parts_mac.mm new file mode 100644 index 000000000000..1df09f20e79c --- /dev/null +++ b/brightray/browser/browser_main_parts_mac.mm @@ -0,0 +1,22 @@ +#import "browser_main_parts.h" + +#import "base/logging.h" +#import "base/mac/bundle_locations.h" +#import + +namespace brightray { + +// Replicates NSApplicationMain, but doesn't start a run loop. +void BrowserMainParts::InitializeMainNib() { + auto infoDictionary = base::mac::OuterBundle().infoDictionary; + + auto principalClass = NSClassFromString([infoDictionary objectForKey:@"NSPrincipalClass"]); + auto application = [principalClass sharedApplication]; + + NSString *mainNibName = [infoDictionary objectForKey:@"NSMainNibFile"]; + auto mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:base::mac::FrameworkBundle()]; + [mainNib instantiateWithOwner:application topLevelObjects:nil]; + [mainNib release]; +} + +} // namespace brightray diff --git a/brightray/browser/devtools_contents_resizing_strategy.cc b/brightray/browser/devtools_contents_resizing_strategy.cc new file mode 100644 index 000000000000..30ee028669e3 --- /dev/null +++ b/brightray/browser/devtools_contents_resizing_strategy.cc @@ -0,0 +1,53 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/devtools_contents_resizing_strategy.h" + +#include + +DevToolsContentsResizingStrategy::DevToolsContentsResizingStrategy() + : hide_inspected_contents_(false) { +} + +DevToolsContentsResizingStrategy::DevToolsContentsResizingStrategy( + const gfx::Rect& bounds) + : bounds_(bounds), + hide_inspected_contents_(bounds_.IsEmpty() && !bounds_.x() && + !bounds_.y()) { +} + + +void DevToolsContentsResizingStrategy::CopyFrom( + const DevToolsContentsResizingStrategy& strategy) { + bounds_ = strategy.bounds(); + hide_inspected_contents_ = strategy.hide_inspected_contents(); +} + +bool DevToolsContentsResizingStrategy::Equals( + const DevToolsContentsResizingStrategy& strategy) { + return bounds_ == strategy.bounds() && + hide_inspected_contents_ == strategy.hide_inspected_contents(); +} + +void ApplyDevToolsContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy, + const gfx::Size& container_size, + gfx::Rect* new_devtools_bounds, + gfx::Rect* new_contents_bounds) { + new_devtools_bounds->SetRect( + 0, 0, container_size.width(), container_size.height()); + + const gfx::Rect& bounds = strategy.bounds(); + if (bounds.size().IsEmpty() && !strategy.hide_inspected_contents()) { + new_contents_bounds->SetRect( + 0, 0, container_size.width(), container_size.height()); + return; + } + + int left = std::min(bounds.x(), container_size.width()); + int top = std::min(bounds.y(), container_size.height()); + int width = std::min(bounds.width(), container_size.width() - left); + int height = std::min(bounds.height(), container_size.height() - top); + new_contents_bounds->SetRect(left, top, width, height); +} diff --git a/brightray/browser/devtools_contents_resizing_strategy.h b/brightray/browser/devtools_contents_resizing_strategy.h new file mode 100644 index 000000000000..c48272f79774 --- /dev/null +++ b/brightray/browser/devtools_contents_resizing_strategy.h @@ -0,0 +1,48 @@ +// 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. + +#ifndef BRIGHTRAY_BROWSER_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_ +#define BRIGHTRAY_BROWSER_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_ + +#include "base/macros.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +// This class knows how to resize both DevTools and inspected WebContents +// inside a browser window hierarchy. +class DevToolsContentsResizingStrategy { + public: + DevToolsContentsResizingStrategy(); + explicit DevToolsContentsResizingStrategy( + const gfx::Rect& bounds); + + void CopyFrom(const DevToolsContentsResizingStrategy& strategy); + bool Equals(const DevToolsContentsResizingStrategy& strategy); + + const gfx::Rect& bounds() const { return bounds_; } + bool hide_inspected_contents() const { return hide_inspected_contents_; } + + private: + // Contents bounds. When non-empty, used instead of insets. + gfx::Rect bounds_; + + // Determines whether inspected contents is visible. + bool hide_inspected_contents_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsContentsResizingStrategy); +}; + +// Applies contents resizing strategy, producing bounds for devtools and +// page contents views. Generally, page contents view is placed atop of devtools +// inside a common parent view, which size should be passed in |container_size|. +// When unknown, providing empty rect as previous devtools and contents bounds +// is allowed. +void ApplyDevToolsContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy, + const gfx::Size& container_size, + gfx::Rect* new_devtools_bounds, + gfx::Rect* new_contents_bounds); + +#endif // BRIGHTRAY_BROWSER_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_ diff --git a/brightray/browser/devtools_embedder_message_dispatcher.cc b/brightray/browser/devtools_embedder_message_dispatcher.cc new file mode 100644 index 000000000000..13c54c4c7eca --- /dev/null +++ b/brightray/browser/devtools_embedder_message_dispatcher.cc @@ -0,0 +1,207 @@ +// Copyright 2013 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-CHROMIUM file. + +#include "browser/devtools_embedder_message_dispatcher.h" + +#include "base/bind.h" +#include "base/values.h" + +namespace brightray { + +namespace { + +using DispatchCallback = DevToolsEmbedderMessageDispatcher::DispatchCallback; + +bool GetValue(const base::Value& value, std::string* result) { + return value.GetAsString(result); +} + +bool GetValue(const base::Value& value, int* result) { + return value.GetAsInteger(result); +} + +bool GetValue(const base::Value& value, bool* result) { + return value.GetAsBoolean(result); +} + +bool GetValue(const base::Value& value, gfx::Rect* rect) { + const base::DictionaryValue* dict; + if (!value.GetAsDictionary(&dict)) + return false; + int x = 0; + int y = 0; + int width = 0; + int height = 0; + if (!dict->GetInteger("x", &x) || + !dict->GetInteger("y", &y) || + !dict->GetInteger("width", &width) || + !dict->GetInteger("height", &height)) + return false; + rect->SetRect(x, y, width, height); + return true; +} + +template +struct StorageTraits { + using StorageType = T; +}; + +template +struct StorageTraits { + using StorageType = T; +}; + +template +struct ParamTuple { + bool Parse(const base::ListValue& list, + const base::ListValue::const_iterator& it) { + return it == list.end(); + } + + template + void Apply(const H& handler, As... args) { + handler.Run(args...); + } +}; + +template +struct ParamTuple { + bool Parse(const base::ListValue& list, + const base::ListValue::const_iterator& it) { + return it != list.end() && GetValue(**it, &head) && + tail.Parse(list, it + 1); + } + + template + void Apply(const H& handler, As... args) { + tail.template Apply(handler, args..., head); + } + + typename StorageTraits::StorageType head; + ParamTuple tail; +}; + +template +bool ParseAndHandle(const base::Callback& handler, + const DispatchCallback& callback, + const base::ListValue& list) { + ParamTuple tuple; + if (!tuple.Parse(list, list.begin())) + return false; + tuple.Apply(handler); + return true; +} + +template +bool ParseAndHandleWithCallback( + const base::Callback& handler, + const DispatchCallback& callback, + const base::ListValue& list) { + ParamTuple tuple; + if (!tuple.Parse(list, list.begin())) + return false; + tuple.Apply(handler, callback); + return true; +} + +} // namespace + +/** + * Dispatcher for messages sent from the frontend running in an + * isolated renderer (chrome-devtools:// or chrome://inspect) to the embedder + * in the browser. + * + * The messages are sent via InspectorFrontendHost.sendMessageToEmbedder or + * chrome.send method accordingly. + */ +class DispatcherImpl : public DevToolsEmbedderMessageDispatcher { + public: + ~DispatcherImpl() override {} + + bool Dispatch(const DispatchCallback& callback, + const std::string& method, + const base::ListValue* params) override { + auto it = handlers_.find(method); + return it != handlers_.end() && it->second.Run(callback, *params); + } + + template + void RegisterHandler(const std::string& method, + void (Delegate::*handler)(As...), + Delegate* delegate) { + handlers_[method] = base::Bind(&ParseAndHandle, + base::Bind(handler, + base::Unretained(delegate))); + } + + template + void RegisterHandlerWithCallback( + const std::string& method, + void (Delegate::*handler)(const DispatchCallback&, As...), + Delegate* delegate) { + handlers_[method] = base::Bind(&ParseAndHandleWithCallback, + base::Bind(handler, + base::Unretained(delegate))); + } + + + private: + using Handler = base::Callback; + using HandlerMap = std::map; + HandlerMap handlers_; +}; + +// static +DevToolsEmbedderMessageDispatcher* +DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend( + Delegate* delegate) { + auto* d = new DispatcherImpl(); + + d->RegisterHandler("bringToFront", &Delegate::ActivateWindow, delegate); + d->RegisterHandler("closeWindow", &Delegate::CloseWindow, delegate); + d->RegisterHandler("loadCompleted", &Delegate::LoadCompleted, delegate); + d->RegisterHandler("setInspectedPageBounds", + &Delegate::SetInspectedPageBounds, delegate); + d->RegisterHandler("inspectElementCompleted", + &Delegate::InspectElementCompleted, delegate); + d->RegisterHandler("inspectedURLChanged", + &Delegate::InspectedURLChanged, delegate); + d->RegisterHandlerWithCallback("setIsDocked", + &Delegate::SetIsDocked, delegate); + d->RegisterHandler("openInNewTab", &Delegate::OpenInNewTab, delegate); + d->RegisterHandler("save", &Delegate::SaveToFile, delegate); + d->RegisterHandler("append", &Delegate::AppendToFile, delegate); + d->RegisterHandler("requestFileSystems", + &Delegate::RequestFileSystems, delegate); + d->RegisterHandler("addFileSystem", &Delegate::AddFileSystem, delegate); + d->RegisterHandler("removeFileSystem", &Delegate::RemoveFileSystem, delegate); + d->RegisterHandler("upgradeDraggedFileSystemPermissions", + &Delegate::UpgradeDraggedFileSystemPermissions, delegate); + d->RegisterHandler("indexPath", &Delegate::IndexPath, delegate); + d->RegisterHandlerWithCallback("loadNetworkResource", + &Delegate::LoadNetworkResource, delegate); + d->RegisterHandler("stopIndexing", &Delegate::StopIndexing, delegate); + d->RegisterHandler("searchInPath", &Delegate::SearchInPath, delegate); + d->RegisterHandler("setWhitelistedShortcuts", + &Delegate::SetWhitelistedShortcuts, delegate); + d->RegisterHandler("zoomIn", &Delegate::ZoomIn, delegate); + d->RegisterHandler("zoomOut", &Delegate::ZoomOut, delegate); + d->RegisterHandler("resetZoom", &Delegate::ResetZoom, delegate); + d->RegisterHandler("setDevicesUpdatesEnabled", + &Delegate::SetDevicesUpdatesEnabled, delegate); + d->RegisterHandler("dispatchProtocolMessage", + &Delegate::DispatchProtocolMessageFromDevToolsFrontend, + delegate); + d->RegisterHandlerWithCallback("sendJsonRequest", + &Delegate::SendJsonRequest, delegate); + d->RegisterHandlerWithCallback("getPreferences", + &Delegate::GetPreferences, delegate); + d->RegisterHandler("setPreference", &Delegate::SetPreference, delegate); + d->RegisterHandler("removePreference", &Delegate::RemovePreference, delegate); + d->RegisterHandler("clearPreferences", &Delegate::ClearPreferences, delegate); + return d; +} + +} // namespace brightray diff --git a/brightray/browser/devtools_embedder_message_dispatcher.h b/brightray/browser/devtools_embedder_message_dispatcher.h new file mode 100644 index 000000000000..b77a83fc07c1 --- /dev/null +++ b/brightray/browser/devtools_embedder_message_dispatcher.h @@ -0,0 +1,96 @@ +// Copyright 2013 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_ +#define BRIGHTRAY_BROWSER_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_ + +#include +#include + +#include "base/callback.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace base { +class ListValue; +class Value; +} + +namespace brightray { + +/** + * Dispatcher for messages sent from the DevTools frontend running in an + * isolated renderer (on chrome-devtools://) to the embedder in the browser. + * + * The messages are sent via InspectorFrontendHost.sendMessageToEmbedder method. + */ +class DevToolsEmbedderMessageDispatcher { + public: + class Delegate { + public: + using DispatchCallback = base::Callback; + + virtual ~Delegate() {} + + virtual void ActivateWindow() = 0; + virtual void CloseWindow() = 0; + virtual void LoadCompleted() = 0; + virtual void SetInspectedPageBounds(const gfx::Rect& rect) = 0; + virtual void InspectElementCompleted() = 0; + virtual void InspectedURLChanged(const std::string& url) = 0; + virtual void SetIsDocked(const DispatchCallback& callback, + bool is_docked) = 0; + virtual void OpenInNewTab(const std::string& url) = 0; + virtual void SaveToFile(const std::string& url, + const std::string& content, + bool save_as) = 0; + virtual void AppendToFile(const std::string& url, + const std::string& content) = 0; + virtual void RequestFileSystems() = 0; + virtual void AddFileSystem(const std::string& file_system_path) = 0; + virtual void RemoveFileSystem(const std::string& file_system_path) = 0; + virtual void UpgradeDraggedFileSystemPermissions( + const std::string& file_system_url) = 0; + virtual void IndexPath(int index_request_id, + const std::string& file_system_path) = 0; + virtual void StopIndexing(int index_request_id) = 0; + virtual void LoadNetworkResource(const DispatchCallback& callback, + const std::string& url, + const std::string& headers, + int stream_id) = 0; + virtual void SearchInPath(int search_request_id, + const std::string& file_system_path, + const std::string& query) = 0; + virtual void SetWhitelistedShortcuts(const std::string& message) = 0; + virtual void ZoomIn() = 0; + virtual void ZoomOut() = 0; + virtual void ResetZoom() = 0; + virtual void SetDevicesUpdatesEnabled(bool enabled) = 0; + virtual void DispatchProtocolMessageFromDevToolsFrontend( + const std::string& message) = 0; + virtual void SendJsonRequest(const DispatchCallback& callback, + const std::string& browser_id, + const std::string& url) = 0; + virtual void GetPreferences(const DispatchCallback& callback) = 0; + virtual void SetPreference(const std::string& name, + const std::string& value) = 0; + virtual void RemovePreference(const std::string& name) = 0; + virtual void ClearPreferences() = 0; + }; + + using DispatchCallback = Delegate::DispatchCallback; + + virtual ~DevToolsEmbedderMessageDispatcher() {} + virtual bool Dispatch(const DispatchCallback& callback, + const std::string& method, + const base::ListValue* params) = 0; + + static DevToolsEmbedderMessageDispatcher* CreateForDevToolsFrontend( + Delegate* delegate); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_ diff --git a/brightray/browser/devtools_file_system_indexer.cc b/brightray/browser/devtools_file_system_indexer.cc new file mode 100644 index 000000000000..e41830258fdc --- /dev/null +++ b/brightray/browser/devtools_file_system_indexer.cc @@ -0,0 +1,495 @@ +// Copyright 2013 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 "browser/devtools_file_system_indexer.h" + +#include + +#include + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" +#include "base/files/file_util_proxy.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" + +using base::Bind; +using base::Callback; +using base::FileEnumerator; +using base::FilePath; +using base::Time; +using base::TimeDelta; +using base::TimeTicks; +using content::BrowserThread; +using std::map; +using std::set; +using std::string; +using std::vector; + +namespace brightray { + +namespace { + +typedef int32_t Trigram; +typedef char TrigramChar; +typedef uint16_t FileId; + +const int kMinTimeoutBetweenWorkedNotification = 200; +// Trigram characters include all ASCII printable characters (32-126) except for +// the capital letters, because the index is case insensitive. +const size_t kTrigramCharacterCount = 126 - 'Z' - 1 + 'A' - ' ' + 1; +const size_t kTrigramCount = + kTrigramCharacterCount * kTrigramCharacterCount * kTrigramCharacterCount; +const int kMaxReadLength = 10 * 1024; +const TrigramChar kUndefinedTrigramChar = -1; +const TrigramChar kBinaryTrigramChar = -2; +const Trigram kUndefinedTrigram = -1; + +template +bool IsAsciiUpper(Char c) { + return c >= 'A' && c <= 'Z'; +} + +class Index { + public: + Index(); + Time LastModifiedTimeForFile(const FilePath& file_path); + void SetTrigramsForFile(const FilePath& file_path, + const vector& index, + const Time& time); + vector Search(string query); + void PrintStats(); + void NormalizeVectors(); + + private: + ~Index(); + + FileId GetFileId(const FilePath& file_path); + + typedef map FileIdsMap; + FileIdsMap file_ids_; + FileId last_file_id_; + // The index in this vector is the trigram id. + vector > index_; + typedef map IndexedFilesMap; + IndexedFilesMap index_times_; + vector is_normalized_; + + DISALLOW_COPY_AND_ASSIGN(Index); +}; + +base::LazyInstance::Leaky g_trigram_index = LAZY_INSTANCE_INITIALIZER; + +TrigramChar TrigramCharForChar(char c) { + static TrigramChar* trigram_chars = nullptr; + if (!trigram_chars) { + trigram_chars = new TrigramChar[256]; + for (size_t i = 0; i < 256; ++i) { + if (i > 127) { + trigram_chars[i] = kUndefinedTrigramChar; + continue; + } + char ch = static_cast(i); + if (ch == '\t') + ch = ' '; + if (IsAsciiUpper(ch)) + ch = ch - 'A' + 'a'; + + bool is_binary_char = ch < 9 || (ch >= 14 && ch < 32) || ch == 127; + if (is_binary_char) { + trigram_chars[i] = kBinaryTrigramChar; + continue; + } + + if (ch < ' ') { + trigram_chars[i] = kUndefinedTrigramChar; + continue; + } + + if (ch >= 'Z') + ch = ch - 'Z' - 1 + 'A'; + ch -= ' '; + char signed_trigram_count = static_cast(kTrigramCharacterCount); + CHECK(ch >= 0 && ch < signed_trigram_count); + trigram_chars[i] = ch; + } + } + unsigned char uc = static_cast(c); + return trigram_chars[uc]; +} + +Trigram TrigramAtIndex(const vector& trigram_chars, size_t index) { + static int kTrigramCharacterCountSquared = + kTrigramCharacterCount * kTrigramCharacterCount; + if (trigram_chars[index] == kUndefinedTrigramChar || + trigram_chars[index + 1] == kUndefinedTrigramChar || + trigram_chars[index + 2] == kUndefinedTrigramChar) + return kUndefinedTrigram; + Trigram trigram = kTrigramCharacterCountSquared * trigram_chars[index] + + kTrigramCharacterCount * trigram_chars[index + 1] + + trigram_chars[index + 2]; + return trigram; +} + +Index::Index() : last_file_id_(0) { + index_.resize(kTrigramCount); + is_normalized_.resize(kTrigramCount); + std::fill(is_normalized_.begin(), is_normalized_.end(), true); +} + +Index::~Index() {} + +Time Index::LastModifiedTimeForFile(const FilePath& file_path) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + Time last_modified_time; + if (index_times_.find(file_path) != index_times_.end()) + last_modified_time = index_times_[file_path]; + return last_modified_time; +} + +void Index::SetTrigramsForFile(const FilePath& file_path, + const vector& index, + const Time& time) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + FileId file_id = GetFileId(file_path); + auto it = index.begin(); + for (; it != index.end(); ++it) { + Trigram trigram = *it; + index_[trigram].push_back(file_id); + is_normalized_[trigram] = false; + } + index_times_[file_path] = time; +} + +vector Index::Search(string query) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + const char* data = query.c_str(); + vector trigram_chars; + trigram_chars.reserve(query.size()); + for (size_t i = 0; i < query.size(); ++i) { + TrigramChar trigram_char = TrigramCharForChar(data[i]); + if (trigram_char == kBinaryTrigramChar) + trigram_char = kUndefinedTrigramChar; + trigram_chars.push_back(trigram_char); + } + vector trigrams; + for (size_t i = 0; i + 2 < query.size(); ++i) { + Trigram trigram = TrigramAtIndex(trigram_chars, i); + if (trigram != kUndefinedTrigram) + trigrams.push_back(trigram); + } + set file_ids; + bool first = true; + vector::const_iterator it = trigrams.begin(); + for (; it != trigrams.end(); ++it) { + Trigram trigram = *it; + if (first) { + std::copy(index_[trigram].begin(), + index_[trigram].end(), + std::inserter(file_ids, file_ids.begin())); + first = false; + continue; + } + set intersection = base::STLSetIntersection >( + file_ids, index_[trigram]); + file_ids.swap(intersection); + } + vector result; + FileIdsMap::const_iterator ids_it = file_ids_.begin(); + for (; ids_it != file_ids_.end(); ++ids_it) { + if (trigrams.empty() || + file_ids.find(ids_it->second) != file_ids.end()) { + result.push_back(ids_it->first); + } + } + return result; +} + +FileId Index::GetFileId(const FilePath& file_path) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + string file_path_str = file_path.AsUTF8Unsafe(); + if (file_ids_.find(file_path) != file_ids_.end()) + return file_ids_[file_path]; + file_ids_[file_path] = ++last_file_id_; + return last_file_id_; +} + +void Index::NormalizeVectors() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + for (size_t i = 0; i < kTrigramCount; ++i) { + if (!is_normalized_[i]) { + std::sort(index_[i].begin(), index_[i].end()); + if (index_[i].capacity() > index_[i].size()) + vector(index_[i]).swap(index_[i]); + is_normalized_[i] = true; + } + } +} + +void Index::PrintStats() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + LOG(ERROR) << "Index stats:"; + size_t size = 0; + size_t maxSize = 0; + size_t capacity = 0; + for (size_t i = 0; i < kTrigramCount; ++i) { + if (index_[i].size() > maxSize) + maxSize = index_[i].size(); + size += index_[i].size(); + capacity += index_[i].capacity(); + } + LOG(ERROR) << " - total trigram count: " << size; + LOG(ERROR) << " - max file count per trigram: " << maxSize; + LOG(ERROR) << " - total vectors capacity " << capacity; + size_t total_index_size = + capacity * sizeof(FileId) + sizeof(vector) * kTrigramCount; + LOG(ERROR) << " - estimated total index size " << total_index_size; +} + +typedef Callback&)> IndexerCallback; + +} // namespace + +DevToolsFileSystemIndexer::FileSystemIndexingJob::FileSystemIndexingJob( + const FilePath& file_system_path, + const TotalWorkCallback& total_work_callback, + const WorkedCallback& worked_callback, + const DoneCallback& done_callback) + : file_system_path_(file_system_path), + total_work_callback_(total_work_callback), + worked_callback_(worked_callback), + done_callback_(done_callback), + current_file_( + BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get()), + files_indexed_(0), + stopped_(false) { + current_trigrams_set_.resize(kTrigramCount); + current_trigrams_.reserve(kTrigramCount); +} + +DevToolsFileSystemIndexer::FileSystemIndexingJob::~FileSystemIndexingJob() {} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::Start() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + Bind(&FileSystemIndexingJob::CollectFilesToIndex, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::Stop() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask(BrowserThread::FILE, + FROM_HERE, + Bind(&FileSystemIndexingJob::StopOnFileThread, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::StopOnFileThread() { + stopped_ = true; +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::CollectFilesToIndex() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + if (stopped_) + return; + if (!file_enumerator_) { + file_enumerator_.reset( + new FileEnumerator(file_system_path_, true, FileEnumerator::FILES)); + } + FilePath file_path = file_enumerator_->Next(); + if (file_path.empty()) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + Bind(total_work_callback_, file_path_times_.size())); + indexing_it_ = file_path_times_.begin(); + IndexFiles(); + return; + } + Time saved_last_modified_time = + g_trigram_index.Get().LastModifiedTimeForFile(file_path); + FileEnumerator::FileInfo file_info = file_enumerator_->GetInfo(); + Time current_last_modified_time = file_info.GetLastModifiedTime(); + if (current_last_modified_time > saved_last_modified_time) { + file_path_times_[file_path] = current_last_modified_time; + } + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + Bind(&FileSystemIndexingJob::CollectFilesToIndex, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::IndexFiles() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + if (stopped_) + return; + if (indexing_it_ == file_path_times_.end()) { + g_trigram_index.Get().NormalizeVectors(); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_callback_); + return; + } + FilePath file_path = indexing_it_->first; + current_file_.CreateOrOpen( + file_path, + base::File::FLAG_OPEN | base::File::FLAG_READ, + Bind(&FileSystemIndexingJob::StartFileIndexing, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::StartFileIndexing( + base::File::Error error) { + if (!current_file_.IsValid()) { + FinishFileIndexing(false); + return; + } + current_file_offset_ = 0; + current_trigrams_.clear(); + std::fill(current_trigrams_set_.begin(), current_trigrams_set_.end(), false); + ReadFromFile(); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::ReadFromFile() { + if (stopped_) { + CloseFile(); + return; + } + current_file_.Read(current_file_offset_, kMaxReadLength, + Bind(&FileSystemIndexingJob::OnRead, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::OnRead( + base::File::Error error, + const char* data, + int bytes_read) { + if (error != base::File::FILE_OK) { + FinishFileIndexing(false); + return; + } + + if (!bytes_read || bytes_read < 3) { + FinishFileIndexing(true); + return; + } + + size_t size = static_cast(bytes_read); + vector trigram_chars; + trigram_chars.reserve(size); + for (size_t i = 0; i < size; ++i) { + TrigramChar trigram_char = TrigramCharForChar(data[i]); + if (trigram_char == kBinaryTrigramChar) { + current_trigrams_.clear(); + FinishFileIndexing(true); + return; + } + trigram_chars.push_back(trigram_char); + } + + for (size_t i = 0; i + 2 < size; ++i) { + Trigram trigram = TrigramAtIndex(trigram_chars, i); + if ((trigram != kUndefinedTrigram) && !current_trigrams_set_[trigram]) { + current_trigrams_set_[trigram] = true; + current_trigrams_.push_back(trigram); + } + } + current_file_offset_ += bytes_read - 2; + ReadFromFile(); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::FinishFileIndexing( + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + CloseFile(); + if (success) { + FilePath file_path = indexing_it_->first; + g_trigram_index.Get().SetTrigramsForFile( + file_path, current_trigrams_, file_path_times_[file_path]); + } + ReportWorked(); + ++indexing_it_; + IndexFiles(); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::CloseFile() { + if (current_file_.IsValid()) + current_file_.Close(Bind(&FileSystemIndexingJob::CloseCallback, this)); +} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::CloseCallback( + base::File::Error error) {} + +void DevToolsFileSystemIndexer::FileSystemIndexingJob::ReportWorked() { + TimeTicks current_time = TimeTicks::Now(); + bool should_send_worked_nitification = true; + if (!last_worked_notification_time_.is_null()) { + TimeDelta delta = current_time - last_worked_notification_time_; + if (delta.InMilliseconds() < kMinTimeoutBetweenWorkedNotification) + should_send_worked_nitification = false; + } + ++files_indexed_; + if (should_send_worked_nitification) { + last_worked_notification_time_ = current_time; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, Bind(worked_callback_, files_indexed_)); + files_indexed_ = 0; + } +} + +DevToolsFileSystemIndexer::DevToolsFileSystemIndexer() { +} + +DevToolsFileSystemIndexer::~DevToolsFileSystemIndexer() {} + +scoped_refptr +DevToolsFileSystemIndexer::IndexPath( + const string& file_system_path, + const TotalWorkCallback& total_work_callback, + const WorkedCallback& worked_callback, + const DoneCallback& done_callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + scoped_refptr indexing_job = + new FileSystemIndexingJob(FilePath::FromUTF8Unsafe(file_system_path), + total_work_callback, + worked_callback, + done_callback); + indexing_job->Start(); + return indexing_job; +} + +void DevToolsFileSystemIndexer::SearchInPath(const string& file_system_path, + const string& query, + const SearchCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + Bind(&DevToolsFileSystemIndexer::SearchInPathOnFileThread, + this, + file_system_path, + query, + callback)); +} + +void DevToolsFileSystemIndexer::SearchInPathOnFileThread( + const string& file_system_path, + const string& query, + const SearchCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + vector file_paths = g_trigram_index.Get().Search(query); + vector result; + FilePath path = FilePath::FromUTF8Unsafe(file_system_path); + vector::const_iterator it = file_paths.begin(); + for (; it != file_paths.end(); ++it) { + if (path.IsParent(*it)) + result.push_back(it->AsUTF8Unsafe()); + } + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, Bind(callback, result)); +} + +} // namespace brightray diff --git a/brightray/browser/devtools_file_system_indexer.h b/brightray/browser/devtools_file_system_indexer.h new file mode 100644 index 000000000000..bb1acefab52a --- /dev/null +++ b/brightray/browser/devtools_file_system_indexer.h @@ -0,0 +1,115 @@ +// Copyright 2013 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 BROWSER_DEVTOOLS_FILE_SYSTEM_INDEXER_H_ +#define BROWSER_DEVTOOLS_FILE_SYSTEM_INDEXER_H_ + +#include + +#include +#include +#include +#include + +#include "base/callback.h" +#include "base/files/file_proxy.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace base { +class FilePath; +class FileEnumerator; +class Time; +} + +namespace content { +class WebContents; +} + +namespace brightray { + +class DevToolsFileSystemIndexer + : public base::RefCountedThreadSafe { + public: + typedef base::Callback TotalWorkCallback; + typedef base::Callback WorkedCallback; + typedef base::Callback DoneCallback; + typedef base::Callback&)> SearchCallback; + + class FileSystemIndexingJob : public base::RefCounted { + public: + void Stop(); + + private: + friend class base::RefCounted; + friend class DevToolsFileSystemIndexer; + FileSystemIndexingJob(const base::FilePath& file_system_path, + const TotalWorkCallback& total_work_callback, + const WorkedCallback& worked_callback, + const DoneCallback& done_callback); + virtual ~FileSystemIndexingJob(); + + void Start(); + void StopOnFileThread(); + void CollectFilesToIndex(); + void IndexFiles(); + void StartFileIndexing(base::File::Error error); + void ReadFromFile(); + void OnRead(base::File::Error error, + const char* data, + int bytes_read); + void FinishFileIndexing(bool success); + void CloseFile(); + void CloseCallback(base::File::Error error); + void ReportWorked(); + + base::FilePath file_system_path_; + TotalWorkCallback total_work_callback_; + WorkedCallback worked_callback_; + DoneCallback done_callback_; + std::unique_ptr file_enumerator_; + typedef std::map FilePathTimesMap; + FilePathTimesMap file_path_times_; + FilePathTimesMap::const_iterator indexing_it_; + base::FileProxy current_file_; + int64_t current_file_offset_; + typedef int32_t Trigram; + std::vector current_trigrams_; + // The index in this vector is the trigram id. + std::vector current_trigrams_set_; + base::TimeTicks last_worked_notification_time_; + int files_indexed_; + bool stopped_; + }; + + DevToolsFileSystemIndexer(); + + // Performs file system indexing for given |file_system_path| and sends + // progress callbacks. + scoped_refptr IndexPath( + const std::string& file_system_path, + const TotalWorkCallback& total_work_callback, + const WorkedCallback& worked_callback, + const DoneCallback& done_callback); + + // Performs trigram search for given |query| in |file_system_path|. + void SearchInPath(const std::string& file_system_path, + const std::string& query, + const SearchCallback& callback); + + private: + friend class base::RefCountedThreadSafe; + + virtual ~DevToolsFileSystemIndexer(); + + void SearchInPathOnFileThread(const std::string& file_system_path, + const std::string& query, + const SearchCallback& callback); + + DISALLOW_COPY_AND_ASSIGN(DevToolsFileSystemIndexer); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_FILE_SYSTEM_INDEXER_H_ diff --git a/brightray/browser/devtools_manager_delegate.cc b/brightray/browser/devtools_manager_delegate.cc new file mode 100644 index 000000000000..7d4ae4d7d459 --- /dev/null +++ b/brightray/browser/devtools_manager_delegate.cc @@ -0,0 +1,131 @@ +// Copyright (c) 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-CHROMIUM file. + +#include "browser/devtools_manager_delegate.h" + +#include + +#include "browser/net/devtools_network_protocol_handler.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "common/content_client.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/devtools_frontend_host.h" +#include "content/public/browser/devtools_socket_factory.h" +#include "content/public/browser/favicon_status.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" +#include "content/public/common/user_agent.h" +#include "content/shell/grit/shell_resources.h" +#include "net/base/net_errors.h" +#include "net/socket/tcp_server_socket.h" +#include "net/socket/stream_socket.h" +#include "ui/base/resource/resource_bundle.h" + + +namespace brightray { + +namespace { + +class TCPServerSocketFactory : public content::DevToolsSocketFactory { + public: + TCPServerSocketFactory(const std::string& address, int port) + : address_(address), port_(port) { + } + + private: + // content::ServerSocketFactory. + std::unique_ptr CreateForHttpServer() override { + std::unique_ptr socket( + new net::TCPServerSocket(nullptr, net::NetLogSource())); + if (socket->ListenWithAddressAndPort(address_, port_, 10) != net::OK) + return std::unique_ptr(); + + return socket; + } + std::unique_ptr CreateForTethering( + std::string* name) override { + return std::unique_ptr(); + } + + std::string address_; + uint16_t port_; + + DISALLOW_COPY_AND_ASSIGN(TCPServerSocketFactory); +}; + +std::unique_ptr +CreateSocketFactory() { + auto& command_line = *base::CommandLine::ForCurrentProcess(); + // See if the user specified a port on the command line (useful for + // automation). If not, use an ephemeral port by specifying 0. + int port = 0; + if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) { + int temp_port; + std::string port_str = + command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort); + if (base::StringToInt(port_str, &temp_port) && + temp_port > 0 && temp_port < 65535) { + port = temp_port; + } else { + DLOG(WARNING) << "Invalid http debugger port number " << temp_port; + } + } + return std::unique_ptr( + new TCPServerSocketFactory("127.0.0.1", port)); +} + +} // namespace + +// DevToolsManagerDelegate --------------------------------------------------- + +// static +void DevToolsManagerDelegate::StartHttpHandler() { + content::DevToolsAgentHost::StartRemoteDebuggingServer( + CreateSocketFactory(), + std::string(), + base::FilePath(), + base::FilePath(), + std::string(), + GetBrightrayUserAgent()); +} + +DevToolsManagerDelegate::DevToolsManagerDelegate() + : handler_(new DevToolsNetworkProtocolHandler) { +} + +DevToolsManagerDelegate::~DevToolsManagerDelegate() { +} + +void DevToolsManagerDelegate::Inspect(content::DevToolsAgentHost* agent_host) { +} + +base::DictionaryValue* DevToolsManagerDelegate::HandleCommand( + content::DevToolsAgentHost* agent_host, + base::DictionaryValue* command) { + return handler_->HandleCommand(agent_host, command); +} + +scoped_refptr +DevToolsManagerDelegate::CreateNewTarget(const GURL& url) { + return nullptr; +} + +std::string DevToolsManagerDelegate::GetDiscoveryPageHTML() { + return ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE).as_string(); +} + +std::string DevToolsManagerDelegate::GetFrontendResource( + const std::string& path) { + return content::DevToolsFrontendHost::GetFrontendResource(path).as_string(); +} + +} // namespace brightray diff --git a/brightray/browser/devtools_manager_delegate.h b/brightray/browser/devtools_manager_delegate.h new file mode 100644 index 000000000000..2e2e09f43f5c --- /dev/null +++ b/brightray/browser/devtools_manager_delegate.h @@ -0,0 +1,42 @@ +// Copyright (c) 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-CHROMIUM file. + +#ifndef BROWSER_DEVTOOLS_MANAGER_DELEGATE_H_ +#define BROWSER_DEVTOOLS_MANAGER_DELEGATE_H_ + +#include "base/macros.h" +#include "base/compiler_specific.h" +#include "content/browser/devtools/devtools_http_handler.h" +#include "content/public/browser/devtools_manager_delegate.h" + +namespace brightray { + +class DevToolsNetworkProtocolHandler; + +class DevToolsManagerDelegate : public content::DevToolsManagerDelegate { + public: + static void StartHttpHandler(); + + DevToolsManagerDelegate(); + virtual ~DevToolsManagerDelegate(); + + // DevToolsManagerDelegate implementation. + void Inspect(content::DevToolsAgentHost* agent_host) override; + base::DictionaryValue* HandleCommand( + content::DevToolsAgentHost* agent_host, + base::DictionaryValue* command) override; + scoped_refptr CreateNewTarget( + const GURL& url) override; + std::string GetDiscoveryPageHTML() override; + std::string GetFrontendResource(const std::string& path) override; + + private: + std::unique_ptr handler_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsManagerDelegate); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_MANAGER_DELEGATE_H_ diff --git a/brightray/browser/devtools_ui.cc b/brightray/browser/devtools_ui.cc new file mode 100644 index 000000000000..9764886d571a --- /dev/null +++ b/brightray/browser/devtools_ui.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "browser/devtools_ui.h" + +#include + +#include "base/memory/ref_counted_memory.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "content/public/browser/devtools_frontend_host.h" +#include "content/public/browser/url_data_source.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_ui.h" + +namespace brightray { + +namespace { + +const char kChromeUIDevToolsHost[] = "devtools"; +const char kChromeUIDevToolsBundledPath[] = "bundled"; + +std::string PathWithoutParams(const std::string& path) { + return GURL(std::string("chrome-devtools://devtools/") + path) + .path().substr(1); +} + +std::string GetMimeTypeForPath(const std::string& path) { + std::string filename = PathWithoutParams(path); + if (base::EndsWith(filename, ".html", base::CompareCase::INSENSITIVE_ASCII)) { + return "text/html"; + } else if (base::EndsWith(filename, ".css", + base::CompareCase::INSENSITIVE_ASCII)) { + return "text/css"; + } else if (base::EndsWith(filename, ".js", + base::CompareCase::INSENSITIVE_ASCII)) { + return "application/javascript"; + } else if (base::EndsWith(filename, ".png", + base::CompareCase::INSENSITIVE_ASCII)) { + return "image/png"; + } else if (base::EndsWith(filename, ".gif", + base::CompareCase::INSENSITIVE_ASCII)) { + return "image/gif"; + } else if (base::EndsWith(filename, ".svg", + base::CompareCase::INSENSITIVE_ASCII)) { + return "image/svg+xml"; + } else if (base::EndsWith(filename, ".manifest", + base::CompareCase::INSENSITIVE_ASCII)) { + return "text/cache-manifest"; + } + return "text/html"; +} + +class BundledDataSource : public content::URLDataSource { + public: + BundledDataSource() {} + + // content::URLDataSource implementation. + std::string GetSource() const override { + return kChromeUIDevToolsHost; + } + + void StartDataRequest( + const std::string& path, + const content::ResourceRequestInfo::WebContentsGetter& wc_getter, + const GotDataCallback& callback) override { + // Serve request from local bundle. + std::string bundled_path_prefix(kChromeUIDevToolsBundledPath); + bundled_path_prefix += "/"; + if (base::StartsWith(path, bundled_path_prefix, + base::CompareCase::INSENSITIVE_ASCII)) { + StartBundledDataRequest(path.substr(bundled_path_prefix.length()), + callback); + return; + } + + // We do not handle remote and custom requests. + callback.Run(nullptr); + } + + std::string GetMimeType(const std::string& path) const override { + return GetMimeTypeForPath(path); + } + + bool ShouldAddContentSecurityPolicy() const override { + return false; + } + + bool ShouldDenyXFrameOptions() const override { + return false; + } + + bool ShouldServeMimeTypeAsContentTypeHeader() const override { + return true; + } + + void StartBundledDataRequest(const std::string& path, + const GotDataCallback& callback) { + std::string filename = PathWithoutParams(path); + base::StringPiece resource = + content::DevToolsFrontendHost::GetFrontendResource(filename); + + DLOG_IF(WARNING, resource.empty()) + << "Unable to find dev tool resource: " << filename + << ". If you compiled with debug_devtools=1, try running with " + "--debug-devtools."; + scoped_refptr bytes( + new base::RefCountedStaticMemory(resource.data(), resource.length())); + callback.Run(bytes.get()); + } + + private: + ~BundledDataSource() override {} + DISALLOW_COPY_AND_ASSIGN(BundledDataSource); +}; + +} // namespace + +DevToolsUI::DevToolsUI(content::BrowserContext* browser_context, + content::WebUI* web_ui) + : WebUIController(web_ui) { + web_ui->SetBindings(0); + content::URLDataSource::Add(browser_context, new BundledDataSource()); +} + +} // namespace brightray diff --git a/brightray/browser/devtools_ui.h b/brightray/browser/devtools_ui.h new file mode 100644 index 000000000000..176de9b71bad --- /dev/null +++ b/brightray/browser/devtools_ui.h @@ -0,0 +1,27 @@ +// Copyright (c) 2011 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_DEVTOOLS_UI_H_ +#define BRIGHTRAY_BROWSER_DEVTOOLS_UI_H_ + +#include "base/compiler_specific.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/web_ui_controller.h" + +namespace brightray { + +class BrowserContext; + +class DevToolsUI : public content::WebUIController { + public: + explicit DevToolsUI(content::BrowserContext* browser_context, + content::WebUI* web_ui); + + private: + DISALLOW_COPY_AND_ASSIGN(DevToolsUI); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/inspectable_web_contents.cc b/brightray/browser/inspectable_web_contents.cc new file mode 100644 index 000000000000..74aeac671136 --- /dev/null +++ b/brightray/browser/inspectable_web_contents.cc @@ -0,0 +1,18 @@ +#include "browser/inspectable_web_contents.h" + +#include "browser/inspectable_web_contents_impl.h" + +namespace brightray { + +InspectableWebContents* InspectableWebContents::Create( + const content::WebContents::CreateParams& create_params) { + auto contents = content::WebContents::Create(create_params); + return Create(contents); +} + +InspectableWebContents* InspectableWebContents::Create( + content::WebContents* web_contents) { + return new InspectableWebContentsImpl(web_contents); +} + +} // namespace brightray diff --git a/brightray/browser/inspectable_web_contents.h b/brightray/browser/inspectable_web_contents.h new file mode 100644 index 000000000000..44c0c22ec647 --- /dev/null +++ b/brightray/browser/inspectable_web_contents.h @@ -0,0 +1,53 @@ +#ifndef BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_H_ +#define BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_H_ + +#include "content/public/browser/web_contents.h" + +namespace base { +class Value; +} + +namespace content { +class DevToolsAgentHost; +} + +namespace brightray { + +class InspectableWebContentsDelegate; +class InspectableWebContentsView; + +class InspectableWebContents { + public: + static InspectableWebContents* Create( + const content::WebContents::CreateParams&); + + // The returned InspectableWebContents takes ownership of the passed-in + // WebContents. + static InspectableWebContents* Create(content::WebContents*); + + virtual ~InspectableWebContents() {} + + virtual InspectableWebContentsView* GetView() const = 0; + virtual content::WebContents* GetWebContents() const = 0; + virtual content::WebContents* GetDevToolsWebContents() const = 0; + + // The delegate manages its own life. + virtual void SetDelegate(InspectableWebContentsDelegate* delegate) = 0; + virtual InspectableWebContentsDelegate* GetDelegate() const = 0; + + virtual void SetDockState(const std::string& state) = 0; + virtual void ShowDevTools() = 0; + virtual void CloseDevTools() = 0; + virtual bool IsDevToolsViewShowing() = 0; + virtual void AttachTo(scoped_refptr) = 0; + virtual void Detach() = 0; + virtual void CallClientFunction(const std::string& function_name, + const base::Value* arg1 = nullptr, + const base::Value* arg2 = nullptr, + const base::Value* arg3 = nullptr) = 0; + virtual void InspectElement(int x, int y) = 0; +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/inspectable_web_contents_delegate.h b/brightray/browser/inspectable_web_contents_delegate.h new file mode 100644 index 000000000000..a94bcac44d52 --- /dev/null +++ b/brightray/browser/inspectable_web_contents_delegate.h @@ -0,0 +1,34 @@ +#ifndef BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_DELEGATE_H_ +#define BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_DELEGATE_H_ + +#include + +namespace brightray { + +class InspectableWebContentsDelegate { + public: + virtual ~InspectableWebContentsDelegate() {} + + // Requested by WebContents of devtools. + virtual void DevToolsReloadPage() {} + virtual void DevToolsSaveToFile( + const std::string& url, const std::string& content, bool save_as) {} + virtual void DevToolsAppendToFile( + const std::string& url, const std::string& content) {} + virtual void DevToolsRequestFileSystems() {} + virtual void DevToolsAddFileSystem( + const base::FilePath& file_system_path) {} + virtual void DevToolsRemoveFileSystem( + const base::FilePath& file_system_path) {} + virtual void DevToolsIndexPath( + int request_id, const std::string& file_system_path) {} + virtual void DevToolsStopIndexing(int request_id) {} + virtual void DevToolsSearchInPath( + int request_id, + const std::string& file_system_path, + const std::string& query) {} +}; + +} // namespace brightray + +#endif // BRIGHTRAY_INSPECTABLE_WEB_CONTENTS_DELEGATE_H_ diff --git a/brightray/browser/inspectable_web_contents_impl.cc b/brightray/browser/inspectable_web_contents_impl.cc new file mode 100644 index 000000000000..20ee3081558a --- /dev/null +++ b/brightray/browser/inspectable_web_contents_impl.cc @@ -0,0 +1,782 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/inspectable_web_contents_impl.h" + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/metrics/histogram.h" +#include "base/strings/pattern.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "browser/browser_client.h" +#include "browser/browser_context.h" +#include "browser/browser_main_parts.h" +#include "browser/inspectable_web_contents_delegate.h" +#include "browser/inspectable_web_contents_view.h" +#include "browser/inspectable_web_contents_view_delegate.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/prefs/pref_registry_simple.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/host_zoom_map.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/common/user_agent.h" +#include "ipc/ipc_channel.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_fetcher_response_writer.h" +#include "ui/display/display.h" +#include "ui/display/screen.h" + +namespace brightray { + +namespace { + +const double kPresetZoomFactors[] = { 0.25, 0.333, 0.5, 0.666, 0.75, 0.9, 1.0, + 1.1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, + 5.0 }; + +const char kChromeUIDevToolsURL[] = + "chrome-devtools://devtools/bundled/inspector.html?" + "remoteBase=%s&" + "can_dock=%s&" + "toolbarColor=rgba(223,223,223,1)&" + "textColor=rgba(0,0,0,1)&" + "experiments=true"; +const char kChromeUIDevToolsRemoteFrontendBase[] = + "https://chrome-devtools-frontend.appspot.com/"; +const char kChromeUIDevToolsRemoteFrontendPath[] = "serve_file"; + +const char kDevToolsBoundsPref[] = "brightray.devtools.bounds"; +const char kDevToolsZoomPref[] = "brightray.devtools.zoom"; +const char kDevToolsPreferences[] = "brightray.devtools.preferences"; + +const char kFrontendHostId[] = "id"; +const char kFrontendHostMethod[] = "method"; +const char kFrontendHostParams[] = "params"; +const char kTitleFormat[] = "Developer Tools - %s"; + +const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4; + +void RectToDictionary(const gfx::Rect& bounds, base::DictionaryValue* dict) { + dict->SetInteger("x", bounds.x()); + dict->SetInteger("y", bounds.y()); + dict->SetInteger("width", bounds.width()); + dict->SetInteger("height", bounds.height()); +} + +void DictionaryToRect(const base::DictionaryValue& dict, gfx::Rect* bounds) { + int x = 0, y = 0, width = 800, height = 600; + dict.GetInteger("x", &x); + dict.GetInteger("y", &y); + dict.GetInteger("width", &width); + dict.GetInteger("height", &height); + *bounds = gfx::Rect(x, y, width, height); +} + +bool IsPointInRect(const gfx::Point& point, const gfx::Rect& rect) { + return point.x() > rect.x() && point.x() < (rect.width() + rect.x()) && + point.y() > rect.y() && point.y() < (rect.height() + rect.y()); +} + +bool IsPointInScreen(const gfx::Point& point) { + for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) { + if (IsPointInRect(point, display.bounds())) + return true; + } + return false; +} + +void SetZoomLevelForWebContents(content::WebContents* web_contents, + double level) { + content::HostZoomMap::SetZoomLevel(web_contents, level); +} + +double GetNextZoomLevel(double level, bool out) { + double factor = content::ZoomLevelToZoomFactor(level); + size_t size = arraysize(kPresetZoomFactors); + for (size_t i = 0; i < size; ++i) { + if (!content::ZoomValuesEqual(kPresetZoomFactors[i], factor)) + continue; + if (out && i > 0) + return content::ZoomFactorToZoomLevel(kPresetZoomFactors[i - 1]); + if (!out && i != size - 1) + return content::ZoomFactorToZoomLevel(kPresetZoomFactors[i + 1]); + } + return level; +} + +GURL GetRemoteBaseURL() { + return GURL(base::StringPrintf( + "%s%s/%s/", + kChromeUIDevToolsRemoteFrontendBase, + kChromeUIDevToolsRemoteFrontendPath, + content::GetWebKitRevision().c_str())); +} + +GURL GetDevToolsURL(bool can_dock) { + auto url_string = + base::StringPrintf(kChromeUIDevToolsURL, + GetRemoteBaseURL().spec().c_str(), + can_dock ? "true" : ""); + return GURL(url_string); +} + +// ResponseWriter ------------------------------------------------------------- + +class ResponseWriter : public net::URLFetcherResponseWriter { + public: + ResponseWriter(base::WeakPtr bindings, + int stream_id); + ~ResponseWriter() override; + + // URLFetcherResponseWriter overrides: + int Initialize(const net::CompletionCallback& callback) override; + int Write(net::IOBuffer* buffer, + int num_bytes, + const net::CompletionCallback& callback) override; + int Finish(int net_error, const net::CompletionCallback& callback) override; + + private: + base::WeakPtr bindings_; + int stream_id_; + + DISALLOW_COPY_AND_ASSIGN(ResponseWriter); +}; + +ResponseWriter::ResponseWriter( + base::WeakPtr bindings, + int stream_id) + : bindings_(bindings), + stream_id_(stream_id) { +} + +ResponseWriter::~ResponseWriter() { +} + +int ResponseWriter::Initialize(const net::CompletionCallback& callback) { + return net::OK; +} + +int ResponseWriter::Write(net::IOBuffer* buffer, + int num_bytes, + const net::CompletionCallback& callback) { + auto* id = new base::Value(stream_id_); + base::StringValue* chunk = + new base::StringValue(std::string(buffer->data(), num_bytes)); + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&InspectableWebContentsImpl::CallClientFunction, + bindings_, "DevToolsAPI.streamWrite", + base::Owned(id), base::Owned(chunk), nullptr)); + return num_bytes; +} + +int ResponseWriter::Finish(int net_error, + const net::CompletionCallback& callback) { + return net::OK; +} + +} // namespace + +// Implemented separately on each platform. +InspectableWebContentsView* CreateInspectableContentsView( + InspectableWebContentsImpl* inspectable_web_contents_impl); + +void InspectableWebContentsImpl::RegisterPrefs(PrefRegistrySimple* registry) { + std::unique_ptr bounds_dict(new base::DictionaryValue); + RectToDictionary(gfx::Rect(0, 0, 800, 600), bounds_dict.get()); + registry->RegisterDictionaryPref(kDevToolsBoundsPref, bounds_dict.release()); + registry->RegisterDoublePref(kDevToolsZoomPref, 0.); + registry->RegisterDictionaryPref(kDevToolsPreferences); +} + +InspectableWebContentsImpl::InspectableWebContentsImpl( + content::WebContents* web_contents) + : frontend_loaded_(false), + can_dock_(true), + delegate_(nullptr), + web_contents_(web_contents), + weak_factory_(this) { + auto context = + static_cast(web_contents_->GetBrowserContext()); + pref_service_ = context->prefs(); + auto bounds_dict = pref_service_->GetDictionary(kDevToolsBoundsPref); + if (bounds_dict) { + DictionaryToRect(*bounds_dict, &devtools_bounds_); + // Sometimes the devtools window is out of screen or has too small size. + if (devtools_bounds_.height() < 100 || devtools_bounds_.width() < 100) { + devtools_bounds_.set_height(600); + devtools_bounds_.set_width(800); + } + if (!IsPointInScreen(devtools_bounds_.origin())) { + gfx::Rect display; + if (web_contents->GetNativeView()) { + display = display::Screen::GetScreen()-> + GetDisplayNearestWindow(web_contents->GetNativeView()).bounds(); + } else { + display = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); + } + + devtools_bounds_.set_x(display.x() + + (display.width() - devtools_bounds_.width()) / 2); + devtools_bounds_.set_y( + display.y() + (display.height() - devtools_bounds_.height()) / 2); + } + } + + view_.reset(CreateInspectableContentsView(this)); +} + +InspectableWebContentsImpl::~InspectableWebContentsImpl() { + // Unsubscribe from devtools and Clean up resources. + if (devtools_web_contents_) { + devtools_web_contents_->SetDelegate(nullptr); + // Calling this also unsubscribes the observer, so WebContentsDestroyed + // won't be called again. + WebContentsDestroyed(); + } + // Let destructor destroy devtools_web_contents_. +} + +InspectableWebContentsView* InspectableWebContentsImpl::GetView() const { + return view_.get(); +} + +content::WebContents* InspectableWebContentsImpl::GetWebContents() const { + return web_contents_.get(); +} + +content::WebContents* InspectableWebContentsImpl::GetDevToolsWebContents() + const { + return devtools_web_contents_.get(); +} + +void InspectableWebContentsImpl::InspectElement(int x, int y) { + if (agent_host_.get()) + agent_host_->InspectElement(this, x, y); +} + +void InspectableWebContentsImpl::SetDelegate( + InspectableWebContentsDelegate* delegate) { + delegate_ = delegate; +} + +InspectableWebContentsDelegate* InspectableWebContentsImpl::GetDelegate() + const { + return delegate_; +} + +void InspectableWebContentsImpl::SetDockState(const std::string& state) { + if (state == "detach") { + can_dock_ = false; + } else { + can_dock_ = true; + dock_state_ = state; + } +} + +void InspectableWebContentsImpl::ShowDevTools() { + // Show devtools only after it has done loading, this is to make sure the + // SetIsDocked is called *BEFORE* ShowDevTools. + if (!devtools_web_contents_) { + embedder_message_dispatcher_.reset( + DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this)); + + content::WebContents::CreateParams create_params( + web_contents_->GetBrowserContext()); + devtools_web_contents_.reset(content::WebContents::Create(create_params)); + + Observe(devtools_web_contents_.get()); + devtools_web_contents_->SetDelegate(this); + + AttachTo(content::DevToolsAgentHost::GetOrCreateFor(web_contents_.get())); + + devtools_web_contents_->GetController().LoadURL( + GetDevToolsURL(can_dock_), + content::Referrer(), + ui::PAGE_TRANSITION_AUTO_TOPLEVEL, + std::string()); + } else { + view_->ShowDevTools(); + } +} + +void InspectableWebContentsImpl::CloseDevTools() { + if (devtools_web_contents_) { + frontend_loaded_ = false; + view_->CloseDevTools(); + devtools_web_contents_.reset(); + web_contents_->Focus(); + } +} + +bool InspectableWebContentsImpl::IsDevToolsViewShowing() { + return devtools_web_contents_ && view_->IsDevToolsViewShowing(); +} + +void InspectableWebContentsImpl::AttachTo( + scoped_refptr host) { + if (agent_host_.get()) + Detach(); + agent_host_ = std::move(host); + // Terminate existing debugging connections and start debugging. + agent_host_->ForceAttachClient(this); +} + +void InspectableWebContentsImpl::Detach() { + if (agent_host_.get()) + agent_host_->DetachClient(this); + agent_host_ = nullptr; +} + +void InspectableWebContentsImpl::CallClientFunction( + const std::string& function_name, + const base::Value* arg1, + const base::Value* arg2, + const base::Value* arg3) { + if (!devtools_web_contents_) + return; + + std::string javascript = function_name + "("; + if (arg1) { + std::string json; + base::JSONWriter::Write(*arg1, &json); + javascript.append(json); + if (arg2) { + base::JSONWriter::Write(*arg2, &json); + javascript.append(", ").append(json); + if (arg3) { + base::JSONWriter::Write(*arg3, &json); + javascript.append(", ").append(json); + } + } + } + javascript.append(");"); + devtools_web_contents_->GetMainFrame()->ExecuteJavaScript( + base::UTF8ToUTF16(javascript)); +} + +gfx::Rect InspectableWebContentsImpl::GetDevToolsBounds() const { + return devtools_bounds_; +} + +void InspectableWebContentsImpl::SaveDevToolsBounds(const gfx::Rect& bounds) { + base::DictionaryValue bounds_dict; + RectToDictionary(bounds, &bounds_dict); + pref_service_->Set(kDevToolsBoundsPref, bounds_dict); + devtools_bounds_ = bounds; +} + +double InspectableWebContentsImpl::GetDevToolsZoomLevel() const { + return pref_service_->GetDouble(kDevToolsZoomPref); +} + +void InspectableWebContentsImpl::UpdateDevToolsZoomLevel(double level) { + pref_service_->SetDouble(kDevToolsZoomPref, level); +} + +void InspectableWebContentsImpl::ActivateWindow() { + // Set the zoom level. + SetZoomLevelForWebContents(GetDevToolsWebContents(), + GetDevToolsZoomLevel()); +} + +void InspectableWebContentsImpl::CloseWindow() { + GetDevToolsWebContents()->DispatchBeforeUnload(); +} + +void InspectableWebContentsImpl::LoadCompleted() { + frontend_loaded_ = true; + view_->ShowDevTools(); + + // If the devtools can dock, "SetIsDocked" will be called by devtools itself. + if (!can_dock_) { + SetIsDocked(DispatchCallback(), false); + } else { + if (dock_state_.empty()) { + const base::DictionaryValue* prefs = pref_service_->GetDictionary( + kDevToolsPreferences); + std::string current_dock_state; + prefs->GetString("currentDockState", ¤t_dock_state); + base::RemoveChars(current_dock_state, "\"", &dock_state_); + } + base::string16 javascript = base::UTF8ToUTF16( + "Components.dockController.setDockSide(\"" + dock_state_ + "\");"); + devtools_web_contents_->GetMainFrame()->ExecuteJavaScript(javascript); + } + + if (view_->GetDelegate()) + view_->GetDelegate()->DevToolsOpened(); +} + +void InspectableWebContentsImpl::SetInspectedPageBounds(const gfx::Rect& rect) { + DevToolsContentsResizingStrategy strategy(rect); + if (contents_resizing_strategy_.Equals(strategy)) + return; + + contents_resizing_strategy_.CopyFrom(strategy); + view_->SetContentsResizingStrategy(contents_resizing_strategy_); +} + +void InspectableWebContentsImpl::InspectElementCompleted() { +} + +void InspectableWebContentsImpl::InspectedURLChanged(const std::string& url) { + view_->SetTitle(base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, + url.c_str()))); +} + +void InspectableWebContentsImpl::LoadNetworkResource( + const DispatchCallback& callback, + const std::string& url, + const std::string& headers, + int stream_id) { + GURL gurl(url); + if (!gurl.is_valid()) { + base::DictionaryValue response; + response.SetInteger("statusCode", 404); + callback.Run(&response); + return; + } + + auto browser_context = + static_cast(devtools_web_contents_->GetBrowserContext()); + + net::URLFetcher* fetcher = + (net::URLFetcher::Create(gurl, net::URLFetcher::GET, this)).release(); + pending_requests_[fetcher] = callback; + fetcher->SetRequestContext(browser_context->url_request_context_getter()); + fetcher->SetExtraRequestHeaders(headers); + fetcher->SaveResponseWithWriter( + std::unique_ptr( + new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id))); + fetcher->Start(); +} + +void InspectableWebContentsImpl::SetIsDocked(const DispatchCallback& callback, + bool docked) { + view_->SetIsDocked(docked); + if (!callback.is_null()) + callback.Run(nullptr); +} + +void InspectableWebContentsImpl::OpenInNewTab(const std::string& url) { +} + +void InspectableWebContentsImpl::SaveToFile( + const std::string& url, const std::string& content, bool save_as) { + if (delegate_) + delegate_->DevToolsSaveToFile(url, content, save_as); +} + +void InspectableWebContentsImpl::AppendToFile( + const std::string& url, const std::string& content) { + if (delegate_) + delegate_->DevToolsAppendToFile(url, content); +} + +void InspectableWebContentsImpl::RequestFileSystems() { + if (delegate_) + delegate_->DevToolsRequestFileSystems(); +} + +void InspectableWebContentsImpl::AddFileSystem( + const std::string& file_system_path) { + if (delegate_) + delegate_->DevToolsAddFileSystem( + base::FilePath::FromUTF8Unsafe(file_system_path)); +} + +void InspectableWebContentsImpl::RemoveFileSystem( + const std::string& file_system_path) { + if (delegate_) + delegate_->DevToolsRemoveFileSystem( + base::FilePath::FromUTF8Unsafe(file_system_path)); +} + +void InspectableWebContentsImpl::UpgradeDraggedFileSystemPermissions( + const std::string& file_system_url) { +} + +void InspectableWebContentsImpl::IndexPath( + int request_id, const std::string& file_system_path) { + if (delegate_) + delegate_->DevToolsIndexPath(request_id, file_system_path); +} + +void InspectableWebContentsImpl::StopIndexing(int request_id) { + if (delegate_) + delegate_->DevToolsStopIndexing(request_id); +} + +void InspectableWebContentsImpl::SearchInPath( + int request_id, + const std::string& file_system_path, + const std::string& query) { + if (delegate_) + delegate_->DevToolsSearchInPath(request_id, file_system_path, query); +} + +void InspectableWebContentsImpl::SetWhitelistedShortcuts( + const std::string& message) { +} + +void InspectableWebContentsImpl::ZoomIn() { + double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), false); + SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level); + UpdateDevToolsZoomLevel(new_level); +} + +void InspectableWebContentsImpl::ZoomOut() { + double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), true); + SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level); + UpdateDevToolsZoomLevel(new_level); +} + +void InspectableWebContentsImpl::ResetZoom() { + SetZoomLevelForWebContents(GetDevToolsWebContents(), 0.); + UpdateDevToolsZoomLevel(0.); +} + +void InspectableWebContentsImpl::SetDevicesUpdatesEnabled(bool enabled) { +} + +void InspectableWebContentsImpl::DispatchProtocolMessageFromDevToolsFrontend( + const std::string& message) { + // If the devtools wants to reload the page, hijack the message and handle it + // to the delegate. + if (base::MatchPattern(message, "{\"id\":*," + "\"method\":\"Page.reload\"," + "\"params\":*}")) { + if (delegate_) + delegate_->DevToolsReloadPage(); + return; + } + + if (agent_host_.get()) + agent_host_->DispatchProtocolMessage(this, message); +} + +void InspectableWebContentsImpl::SendJsonRequest( + const DispatchCallback& callback, + const std::string& browser_id, + const std::string& url) { + callback.Run(nullptr); +} + +void InspectableWebContentsImpl::GetPreferences( + const DispatchCallback& callback) { + const base::DictionaryValue* prefs = pref_service_->GetDictionary( + kDevToolsPreferences); + callback.Run(prefs); +} + +void InspectableWebContentsImpl::SetPreference(const std::string& name, + const std::string& value) { + DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences); + update.Get()->SetStringWithoutPathExpansion(name, value); +} + +void InspectableWebContentsImpl::RemovePreference(const std::string& name) { + DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences); + update.Get()->RemoveWithoutPathExpansion(name, nullptr); +} + +void InspectableWebContentsImpl::ClearPreferences() { + DictionaryPrefUpdate update(pref_service_, kDevToolsPreferences); + update.Get()->Clear(); +} + +void InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend( + const std::string& message) { + std::string method; + base::ListValue empty_params; + base::ListValue* params = &empty_params; + + base::DictionaryValue* dict = nullptr; + std::unique_ptr parsed_message(base::JSONReader::Read(message)); + if (!parsed_message || + !parsed_message->GetAsDictionary(&dict) || + !dict->GetString(kFrontendHostMethod, &method) || + (dict->HasKey(kFrontendHostParams) && + !dict->GetList(kFrontendHostParams, ¶ms))) { + LOG(ERROR) << "Invalid message was sent to embedder: " << message; + return; + } + int id = 0; + dict->GetInteger(kFrontendHostId, &id); + embedder_message_dispatcher_->Dispatch( + base::Bind(&InspectableWebContentsImpl::SendMessageAck, + weak_factory_.GetWeakPtr(), + id), + method, + params); +} + +void InspectableWebContentsImpl::DispatchProtocolMessage( + content::DevToolsAgentHost* agent_host, const std::string& message) { + if (!frontend_loaded_) + return; + + if (message.length() < kMaxMessageChunkSize) { + base::string16 javascript = base::UTF8ToUTF16( + "DevToolsAPI.dispatchMessage(" + message + ");"); + devtools_web_contents_->GetMainFrame()->ExecuteJavaScript(javascript); + return; + } + + base::Value total_size(static_cast(message.length())); + for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) { + base::StringValue message_value(message.substr(pos, kMaxMessageChunkSize)); + CallClientFunction("DevToolsAPI.dispatchMessageChunk", + &message_value, pos ? nullptr : &total_size, nullptr); + } +} + +void InspectableWebContentsImpl::AgentHostClosed( + content::DevToolsAgentHost* agent_host, bool replaced) { +} + +void InspectableWebContentsImpl::RenderFrameHostChanged( + content::RenderFrameHost* old_host, + content::RenderFrameHost* new_host) { + if (new_host->GetParent()) + return; + frontend_host_.reset(content::DevToolsFrontendHost::Create( + new_host, + base::Bind(&InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend, + base::Unretained(this)))); +} + +void InspectableWebContentsImpl::WebContentsDestroyed() { + frontend_loaded_ = false; + Observe(nullptr); + Detach(); + + for (const auto& pair : pending_requests_) + delete pair.first; + + if (view_ && view_->GetDelegate()) + view_->GetDelegate()->DevToolsClosed(); +} + +bool InspectableWebContentsImpl::DidAddMessageToConsole( + content::WebContents* source, + int32_t level, + const base::string16& message, + int32_t line_no, + const base::string16& source_id) { + logging::LogMessage("CONSOLE", line_no, level).stream() << "\"" << + message << "\", source: " << source_id << " (" << line_no << ")"; + return true; +} + +bool InspectableWebContentsImpl::ShouldCreateWebContents( + content::WebContents* web_contents, + content::SiteInstance* source_site_instance, + int32_t route_id, + int32_t main_frame_route_id, + int32_t main_frame_widget_route_id, + content::mojom::WindowContainerType window_container_type, + const GURL& opener_url, + const std::string& frame_name, + const GURL& target_url, + const std::string& partition_id, + content::SessionStorageNamespace* session_storage_namespace) { + return false; +} + +void InspectableWebContentsImpl::HandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event) { + auto delegate = web_contents_->GetDelegate(); + if (delegate) + delegate->HandleKeyboardEvent(source, event); +} + +void InspectableWebContentsImpl::CloseContents(content::WebContents* source) { + // This is where the devtools closes itself (by clicking the x button). + CloseDevTools(); +} + +content::ColorChooser* InspectableWebContentsImpl::OpenColorChooser( + content::WebContents* source, + SkColor color, + const std::vector& suggestions) { + auto delegate = web_contents_->GetDelegate(); + if (delegate) + return delegate->OpenColorChooser(source, color, suggestions); + return nullptr; +} + +void InspectableWebContentsImpl::RunFileChooser( + content::RenderFrameHost* render_frame_host, + const content::FileChooserParams& params) { + auto delegate = web_contents_->GetDelegate(); + if (delegate) + delegate->RunFileChooser(render_frame_host, params); +} + +void InspectableWebContentsImpl::EnumerateDirectory( + content::WebContents* source, + int request_id, + const base::FilePath& path) { + auto delegate = web_contents_->GetDelegate(); + if (delegate) + delegate->EnumerateDirectory(source, request_id, path); +} + +void InspectableWebContentsImpl::OnWebContentsFocused() { +#if defined(TOOLKIT_VIEWS) + if (view_->GetDelegate()) + view_->GetDelegate()->DevToolsFocused(); +#endif +} + +void InspectableWebContentsImpl::DidStartNavigationToPendingEntry( + const GURL& url, + content::ReloadType reload_type) { + frontend_host_.reset(content::DevToolsFrontendHost::Create( + web_contents()->GetMainFrame(), + base::Bind(&InspectableWebContentsImpl::HandleMessageFromDevToolsFrontend, + base::Unretained(this)))); +} + +void InspectableWebContentsImpl::OnURLFetchComplete( + const net::URLFetcher* source) { + DCHECK(source); + auto it = pending_requests_.find(source); + DCHECK(it != pending_requests_.end()); + + base::DictionaryValue response; + auto* headers = new base::DictionaryValue(); + net::HttpResponseHeaders* rh = source->GetResponseHeaders(); + response.SetInteger("statusCode", rh ? rh->response_code() : 200); + response.Set("headers", headers); + + size_t iterator = 0; + std::string name; + std::string value; + while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value)) + headers->SetString(name, value); + + it->second.Run(&response); + pending_requests_.erase(it); + delete source; +} + +void InspectableWebContentsImpl::SendMessageAck(int request_id, + const base::Value* arg) { + base::Value id_value(request_id); + CallClientFunction("DevToolsAPI.embedderMessageAck", + &id_value, arg, nullptr); +} + +} // namespace brightray diff --git a/brightray/browser/inspectable_web_contents_impl.h b/brightray/browser/inspectable_web_contents_impl.h new file mode 100644 index 000000000000..25cffa957aff --- /dev/null +++ b/brightray/browser/inspectable_web_contents_impl.h @@ -0,0 +1,202 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_IMPL_H_ +#define BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_IMPL_H_ + +#include "browser/inspectable_web_contents.h" + +#include "browser/devtools_contents_resizing_strategy.h" +#include "browser/devtools_embedder_message_dispatcher.h" + +#include "base/memory/weak_ptr.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/devtools_frontend_host.h" +#include "content/public/browser/web_contents_delegate.h" +#include "content/public/browser/web_contents_observer.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "ui/gfx/geometry/rect.h" + +class PrefService; +class PrefRegistrySimple; + +namespace content { +class DevToolsAgentHost; +} + +namespace brightray { + +class InspectableWebContentsDelegate; +class InspectableWebContentsView; + +class InspectableWebContentsImpl : + public InspectableWebContents, + public content::DevToolsAgentHostClient, + public content::WebContentsObserver, + public content::WebContentsDelegate, + public DevToolsEmbedderMessageDispatcher::Delegate, + public net::URLFetcherDelegate { + public: + static void RegisterPrefs(PrefRegistrySimple* pref_registry); + + explicit InspectableWebContentsImpl(content::WebContents*); + virtual ~InspectableWebContentsImpl(); + + InspectableWebContentsView* GetView() const override; + content::WebContents* GetWebContents() const override; + content::WebContents* GetDevToolsWebContents() const override; + + void SetDelegate(InspectableWebContentsDelegate* delegate) override; + InspectableWebContentsDelegate* GetDelegate() const override; + void SetDockState(const std::string& state) override; + void ShowDevTools() override; + void CloseDevTools() override; + bool IsDevToolsViewShowing() override; + void AttachTo(scoped_refptr) override; + void Detach() override; + void CallClientFunction(const std::string& function_name, + const base::Value* arg1, + const base::Value* arg2, + const base::Value* arg3) override; + void InspectElement(int x, int y) override; + + // Return the last position and size of devtools window. + gfx::Rect GetDevToolsBounds() const; + void SaveDevToolsBounds(const gfx::Rect& bounds); + + // Return the last set zoom level of devtools window. + double GetDevToolsZoomLevel() const; + void UpdateDevToolsZoomLevel(double level); + + private: + // DevToolsEmbedderMessageDispacher::Delegate + void ActivateWindow() override; + void CloseWindow() override; + void LoadCompleted() override; + void SetInspectedPageBounds(const gfx::Rect& rect) override; + void InspectElementCompleted() override; + void InspectedURLChanged(const std::string& url) override; + void LoadNetworkResource(const DispatchCallback& callback, + const std::string& url, + const std::string& headers, + int stream_id) override; + void SetIsDocked(const DispatchCallback& callback, bool is_docked) override; + void OpenInNewTab(const std::string& url) override; + void SaveToFile(const std::string& url, + const std::string& content, + bool save_as) override; + void AppendToFile(const std::string& url, + const std::string& content) override; + void RequestFileSystems() override; + void AddFileSystem(const std::string& file_system_path) override; + void RemoveFileSystem(const std::string& file_system_path) override; + void UpgradeDraggedFileSystemPermissions( + const std::string& file_system_url) override; + void IndexPath(int index_request_id, + const std::string& file_system_path) override; + void StopIndexing(int index_request_id) override; + void SearchInPath(int search_request_id, + const std::string& file_system_path, + const std::string& query) override; + void SetWhitelistedShortcuts(const std::string& message) override; + void ZoomIn() override; + void ZoomOut() override; + void ResetZoom() override; + void SetDevicesUpdatesEnabled(bool enabled) override; + void DispatchProtocolMessageFromDevToolsFrontend( + const std::string& message) override; + void SendJsonRequest(const DispatchCallback& callback, + const std::string& browser_id, + const std::string& url) override; + void GetPreferences(const DispatchCallback& callback) override; + void SetPreference(const std::string& name, + const std::string& value) override; + void RemovePreference(const std::string& name) override; + void ClearPreferences() override; + + // content::DevToolsFrontendHostDelegate: + void HandleMessageFromDevToolsFrontend(const std::string& message); + + // content::DevToolsAgentHostClient: + void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host, + const std::string& message) override; + void AgentHostClosed(content::DevToolsAgentHost* agent_host, + bool replaced) override; + + // content::WebContentsObserver: + void RenderFrameHostChanged(content::RenderFrameHost* old_host, + content::RenderFrameHost* new_host) override; + void WebContentsDestroyed() override; + void OnWebContentsFocused() override; + void DidStartNavigationToPendingEntry( + const GURL& url, + content::ReloadType reload_type) override; + + // content::WebContentsDelegate: + bool DidAddMessageToConsole(content::WebContents* source, + int32_t level, + const base::string16& message, + int32_t line_no, + const base::string16& source_id) override; + bool ShouldCreateWebContents( + content::WebContents* web_contents, + content::SiteInstance* source_site_instance, + int32_t route_id, + int32_t main_frame_route_id, + int32_t main_frame_widget_route_id, + content::mojom::WindowContainerType window_container_type, + const GURL& opener_url, + const std::string& frame_name, + const GURL& target_url, + const std::string& partition_id, + content::SessionStorageNamespace* session_storage_namespace) override; + void HandleKeyboardEvent( + content::WebContents*, const content::NativeWebKeyboardEvent&) override; + void CloseContents(content::WebContents* source) override; + content::ColorChooser* OpenColorChooser( + content::WebContents* source, + SkColor color, + const std::vector& suggestions) override; + void RunFileChooser(content::RenderFrameHost* render_frame_host, + const content::FileChooserParams& params) override; + void EnumerateDirectory(content::WebContents* source, + int request_id, + const base::FilePath& path) override; + + // net::URLFetcherDelegate: + void OnURLFetchComplete(const net::URLFetcher* source) override; + + void SendMessageAck(int request_id, + const base::Value* arg1); + + bool frontend_loaded_; + scoped_refptr agent_host_; + std::unique_ptr frontend_host_; + std::unique_ptr + embedder_message_dispatcher_; + + DevToolsContentsResizingStrategy contents_resizing_strategy_; + gfx::Rect devtools_bounds_; + bool can_dock_; + std::string dock_state_; + + using PendingRequestsMap = std::map; + PendingRequestsMap pending_requests_; + InspectableWebContentsDelegate* delegate_; // weak references. + + PrefService* pref_service_; // weak reference. + + std::unique_ptr web_contents_; + std::unique_ptr devtools_web_contents_; + std::unique_ptr view_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(InspectableWebContentsImpl); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/inspectable_web_contents_view.h b/brightray/browser/inspectable_web_contents_view.h new file mode 100644 index 000000000000..2fabf5d2ba30 --- /dev/null +++ b/brightray/browser/inspectable_web_contents_view.h @@ -0,0 +1,59 @@ +#ifndef BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_H_ +#define BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_H_ + +#include "base/strings/string16.h" +#include "ui/gfx/native_widget_types.h" + +class DevToolsContentsResizingStrategy; + +#if defined(TOOLKIT_VIEWS) +namespace views { +class View; +} +#endif + +namespace brightray { + +class InspectableWebContentsViewDelegate; + +class InspectableWebContentsView { + public: + InspectableWebContentsView() : delegate_(nullptr) {} + virtual ~InspectableWebContentsView() {} + + // The delegate manages its own life. + void SetDelegate(InspectableWebContentsViewDelegate* delegate) { + delegate_ = delegate; + } + InspectableWebContentsViewDelegate* GetDelegate() const { + return delegate_; + } + +#if defined(TOOLKIT_VIEWS) + // Returns the container control, which has devtools view attached. + virtual views::View* GetView() = 0; + + // Returns the web view control, which can be used by the + // GetInitiallyFocusedView() to set initial focus to web view. + virtual views::View* GetWebView() = 0; +#else + virtual gfx::NativeView GetNativeView() const = 0; +#endif + + virtual void ShowDevTools() = 0; + // Hide the DevTools view. + virtual void CloseDevTools() = 0; + virtual bool IsDevToolsViewShowing() = 0; + virtual bool IsDevToolsViewFocused() = 0; + virtual void SetIsDocked(bool docked) = 0; + virtual void SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) = 0; + virtual void SetTitle(const base::string16& title) = 0; + + private: + InspectableWebContentsViewDelegate* delegate_; // weak references. +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/inspectable_web_contents_view_delegate.cc b/brightray/browser/inspectable_web_contents_view_delegate.cc new file mode 100644 index 000000000000..5906499da8c4 --- /dev/null +++ b/brightray/browser/inspectable_web_contents_view_delegate.cc @@ -0,0 +1,10 @@ +#include "browser/inspectable_web_contents_view_delegate.h" + +namespace brightray { + +gfx::ImageSkia InspectableWebContentsViewDelegate::GetDevToolsWindowIcon() { + return gfx::ImageSkia(); +} + +} // namespace brightray + diff --git a/brightray/browser/inspectable_web_contents_view_delegate.h b/brightray/browser/inspectable_web_contents_view_delegate.h new file mode 100644 index 000000000000..900857c7a108 --- /dev/null +++ b/brightray/browser/inspectable_web_contents_view_delegate.h @@ -0,0 +1,28 @@ +#ifndef BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_DELEGATE_H_ +#define BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_DELEGATE_H_ + +#include "ui/gfx/image/image_skia.h" + +namespace brightray { + +class InspectableWebContentsViewDelegate { + public: + virtual ~InspectableWebContentsViewDelegate() {} + + virtual void DevToolsFocused() {} + virtual void DevToolsOpened() {} + virtual void DevToolsClosed() {} + + // Returns the icon of devtools window. + virtual gfx::ImageSkia GetDevToolsWindowIcon(); + +#if defined(USE_X11) + // Called when creating devtools window. + virtual void GetDevToolsWindowWMClass( + std::string* name, std::string* class_name) {} +#endif +}; + +} // namespace brightray + +#endif // BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_DELEGATE_H_ diff --git a/brightray/browser/inspectable_web_contents_view_mac.h b/brightray/browser/inspectable_web_contents_view_mac.h new file mode 100644 index 000000000000..e7ad57fbee8c --- /dev/null +++ b/brightray/browser/inspectable_web_contents_view_mac.h @@ -0,0 +1,45 @@ +#ifndef BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_MAC_H_ +#define BRIGHTRAY_BROWSER_INSPECTABLE_WEB_CONTENTS_VIEW_MAC_H_ + +#include "browser/inspectable_web_contents_view.h" + +#include "base/mac/scoped_nsobject.h" + +@class BRYInspectableWebContentsView; + +namespace brightray { + +class InspectableWebContentsImpl; + +class InspectableWebContentsViewMac : public InspectableWebContentsView { + public: + explicit InspectableWebContentsViewMac( + InspectableWebContentsImpl* inspectable_web_contents_impl); + virtual ~InspectableWebContentsViewMac(); + + gfx::NativeView GetNativeView() const override; + void ShowDevTools() override; + void CloseDevTools() override; + bool IsDevToolsViewShowing() override; + bool IsDevToolsViewFocused() override; + void SetIsDocked(bool docked) override; + void SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) override; + void SetTitle(const base::string16& title) override; + + InspectableWebContentsImpl* inspectable_web_contents() { + return inspectable_web_contents_; + } + + private: + // Owns us. + InspectableWebContentsImpl* inspectable_web_contents_; + + base::scoped_nsobject view_; + + DISALLOW_COPY_AND_ASSIGN(InspectableWebContentsViewMac); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/inspectable_web_contents_view_mac.mm b/brightray/browser/inspectable_web_contents_view_mac.mm new file mode 100644 index 000000000000..94edc1c1a716 --- /dev/null +++ b/brightray/browser/inspectable_web_contents_view_mac.mm @@ -0,0 +1,59 @@ +#include "browser/inspectable_web_contents_view_mac.h" + +#import + +#include "base/strings/sys_string_conversions.h" +#include "browser/inspectable_web_contents.h" +#include "browser/inspectable_web_contents_view_delegate.h" +#import "browser/mac/bry_inspectable_web_contents_view.h" + +namespace brightray { + +InspectableWebContentsView* CreateInspectableContentsView(InspectableWebContentsImpl* inspectable_web_contents) { + return new InspectableWebContentsViewMac(inspectable_web_contents); +} + +InspectableWebContentsViewMac::InspectableWebContentsViewMac(InspectableWebContentsImpl* inspectable_web_contents) + : inspectable_web_contents_(inspectable_web_contents), + view_([[BRYInspectableWebContentsView alloc] initWithInspectableWebContentsViewMac:this]) { +} + +InspectableWebContentsViewMac::~InspectableWebContentsViewMac() { + [view_ removeObservers]; + CloseDevTools(); +} + +gfx::NativeView InspectableWebContentsViewMac::GetNativeView() const { + return view_.get(); +} + +void InspectableWebContentsViewMac::ShowDevTools() { + [view_ setDevToolsVisible:YES]; +} + +void InspectableWebContentsViewMac::CloseDevTools() { + [view_ setDevToolsVisible:NO]; +} + +bool InspectableWebContentsViewMac::IsDevToolsViewShowing() { + return [view_ isDevToolsVisible]; +} + +bool InspectableWebContentsViewMac::IsDevToolsViewFocused() { + return [view_ isDevToolsFocused]; +} + +void InspectableWebContentsViewMac::SetIsDocked(bool docked) { + [view_ setIsDocked:docked]; +} + +void InspectableWebContentsViewMac::SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) { + [view_ setContentsResizingStrategy:strategy]; +} + +void InspectableWebContentsViewMac::SetTitle(const base::string16& title) { + [view_ setTitle:base::SysUTF16ToNSString(title)]; +} + +} // namespace brightray diff --git a/brightray/browser/linux/libnotify_loader.cc b/brightray/browser/linux/libnotify_loader.cc new file mode 100644 index 000000000000..5510bc23cf3b --- /dev/null +++ b/brightray/browser/linux/libnotify_loader.cc @@ -0,0 +1,133 @@ +// This is generated file. Do not modify directly. +// Path to the code generator: +// tools/generate_library_loader/generate_library_loader.py . + +#include "browser/linux/libnotify_loader.h" + +#include + +LibNotifyLoader::LibNotifyLoader() : loaded_(false) { +} + +LibNotifyLoader::~LibNotifyLoader() { + CleanUp(loaded_); +} + +bool LibNotifyLoader::Load(const std::string& library_name) { + if (loaded_) + return false; + + library_ = dlopen(library_name.c_str(), RTLD_LAZY); + if (!library_) + return false; + + notify_is_initted = + reinterpret_castnotify_is_initted)>( + dlsym(library_, "notify_is_initted")); + if (!notify_is_initted) { + CleanUp(true); + return false; + } + + notify_init = + reinterpret_castnotify_init)>( + dlsym(library_, "notify_init")); + if (!notify_init) { + CleanUp(true); + return false; + } + + notify_get_server_info = + reinterpret_castnotify_get_server_info)>( + dlsym(library_, "notify_get_server_info")); + if (!notify_get_server_info) { + CleanUp(true); + return false; + } + + notify_get_server_caps = + reinterpret_castnotify_get_server_caps)>( + dlsym(library_, "notify_get_server_caps")); + if (!notify_get_server_caps) { + CleanUp(true); + return false; + } + + notify_notification_new = + reinterpret_castnotify_notification_new)>( + dlsym(library_, "notify_notification_new")); + if (!notify_notification_new) { + CleanUp(true); + return false; + } + + notify_notification_add_action = + reinterpret_castnotify_notification_add_action)>( + dlsym(library_, "notify_notification_add_action")); + if (!notify_notification_add_action) { + CleanUp(true); + return false; + } + + notify_notification_set_image_from_pixbuf = reinterpret_castnotify_notification_set_image_from_pixbuf)>( + dlsym(library_, "notify_notification_set_image_from_pixbuf")); + if (!notify_notification_set_image_from_pixbuf) { + CleanUp(true); + return false; + } + + notify_notification_set_timeout = + reinterpret_castnotify_notification_set_timeout)>( + dlsym(library_, "notify_notification_set_timeout")); + if (!notify_notification_set_timeout) { + CleanUp(true); + return false; + } + + notify_notification_set_hint_string = + reinterpret_castnotify_notification_set_hint_string)>( + dlsym(library_, "notify_notification_set_hint_string")); + if (!notify_notification_set_hint_string) { + CleanUp(true); + return false; + } + + notify_notification_show = + reinterpret_castnotify_notification_show)>( + dlsym(library_, "notify_notification_show")); + if (!notify_notification_show) { + CleanUp(true); + return false; + } + + notify_notification_close = + reinterpret_castnotify_notification_close)>( + dlsym(library_, "notify_notification_close")); + if (!notify_notification_close) { + CleanUp(true); + return false; + } + + loaded_ = true; + return true; +} + +void LibNotifyLoader::CleanUp(bool unload) { + if (unload) { + dlclose(library_); + library_ = NULL; + } + loaded_ = false; + notify_is_initted = NULL; + notify_init = NULL; + notify_get_server_info = NULL; + notify_get_server_caps = NULL; + notify_notification_new = NULL; + notify_notification_add_action = NULL; + notify_notification_set_image_from_pixbuf = NULL; + notify_notification_set_timeout = NULL; + notify_notification_set_hint_string = NULL; + notify_notification_show = NULL; + notify_notification_close = NULL; +} diff --git a/brightray/browser/linux/libnotify_loader.h b/brightray/browser/linux/libnotify_loader.h new file mode 100644 index 000000000000..a48f4794147f --- /dev/null +++ b/brightray/browser/linux/libnotify_loader.h @@ -0,0 +1,47 @@ +// This is generated file. Do not modify directly. +// Path to the code generator: +// tools/generate_library_loader/generate_library_loader.py . + +#ifndef BRIGHTRAY_BROWSER_LINUX_LIBNOTIFY_LOADER_H_ +#define BRIGHTRAY_BROWSER_LINUX_LIBNOTIFY_LOADER_H_ + +#include + +#include + +class LibNotifyLoader { + public: + LibNotifyLoader(); + ~LibNotifyLoader(); + + bool Load(const std::string& library_name) + __attribute__((warn_unused_result)); + + bool loaded() const { return loaded_; } + + decltype(&::notify_is_initted) notify_is_initted; + decltype(&::notify_init) notify_init; + decltype(&::notify_get_server_caps) notify_get_server_caps; + decltype(&::notify_get_server_info) notify_get_server_info; + decltype(&::notify_notification_new) notify_notification_new; + decltype(&::notify_notification_add_action) notify_notification_add_action; + decltype(&::notify_notification_set_image_from_pixbuf) + notify_notification_set_image_from_pixbuf; + decltype(&::notify_notification_set_timeout) notify_notification_set_timeout; + decltype(&::notify_notification_set_hint_string) + notify_notification_set_hint_string; + decltype(&::notify_notification_show) notify_notification_show; + decltype(&::notify_notification_close) notify_notification_close; + + private: + void CleanUp(bool unload); + + void* library_; + bool loaded_; + + // Disallow copy constructor and assignment operator. + LibNotifyLoader(const LibNotifyLoader&); + void operator=(const LibNotifyLoader&); +}; + +#endif // BRIGHTRAY_BROWSER_LINUX_LIBNOTIFY_LOADER_H_ diff --git a/brightray/browser/linux/libnotify_notification.cc b/brightray/browser/linux/libnotify_notification.cc new file mode 100644 index 000000000000..dad3acb4335d --- /dev/null +++ b/brightray/browser/linux/libnotify_notification.cc @@ -0,0 +1,167 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/linux/libnotify_notification.h" + +#include "base/files/file_enumerator.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "browser/notification_delegate.h" +#include "chrome/browser/ui/libgtkui/skia_utils_gtk.h" +#include "common/application_info.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace brightray { + +namespace { + +LibNotifyLoader libnotify_loader_; + +bool HasCapability(const std::string& capability) { + bool result = false; + GList* capabilities = libnotify_loader_.notify_get_server_caps(); + + if (g_list_find_custom(capabilities, capability.c_str(), + (GCompareFunc)g_strcmp0) != NULL) + result = true; + + g_list_free_full(capabilities, g_free); + + return result; +} + +bool NotifierSupportsActions() { + if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER")) + return false; + + static bool notify_has_result = false; + static bool notify_result = false; + + if (notify_has_result) + return notify_result; + + notify_result = HasCapability("actions"); + return notify_result; +} + +void log_and_clear_error(GError* error, const char* context) { + LOG(ERROR) << context + << ": domain=" << error->domain + << " code=" << error->code + << " message=\"" << error->message << '"'; + g_error_free(error); +} + +} // namespace + +// static +bool LibnotifyNotification::Initialize() { + if (!libnotify_loader_.Load("libnotify.so.4") && // most common one + !libnotify_loader_.Load("libnotify.so.5") && + !libnotify_loader_.Load("libnotify.so.1") && + !libnotify_loader_.Load("libnotify.so")) { + return false; + } + if (!libnotify_loader_.notify_is_initted() && + !libnotify_loader_.notify_init(GetApplicationName().c_str())) { + return false; + } + return true; +} + +LibnotifyNotification::LibnotifyNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter) + : Notification(delegate, presenter), + notification_(nullptr) { +} + +LibnotifyNotification::~LibnotifyNotification() { + if (notification_) { + g_signal_handlers_disconnect_by_data(notification_, this); + g_object_unref(notification_); + } +} + +void LibnotifyNotification::Show(const base::string16& title, + const base::string16& body, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) { + notification_ = libnotify_loader_.notify_notification_new( + base::UTF16ToUTF8(title).c_str(), + base::UTF16ToUTF8(body).c_str(), + nullptr); + + g_signal_connect( + notification_, "closed", G_CALLBACK(OnNotificationClosedThunk), this); + + // NB: On Unity and on any other DE using Notify-OSD, adding a notification + // action will cause the notification to display as a modal dialog box. + if (NotifierSupportsActions()) { + libnotify_loader_.notify_notification_add_action( + notification_, "default", "View", OnNotificationViewThunk, this, + nullptr); + } + + if (!icon.drawsNothing()) { + GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(icon); + libnotify_loader_.notify_notification_set_image_from_pixbuf( + notification_, pixbuf); + libnotify_loader_.notify_notification_set_timeout( + notification_, NOTIFY_EXPIRES_DEFAULT); + g_object_unref(pixbuf); + } + + if (!tag.empty()) { + GQuark id = g_quark_from_string(tag.c_str()); + g_object_set(G_OBJECT(notification_), "id", id, NULL); + } + + // Always try to append notifications. + // Unique tags can be used to prevent this. + if (HasCapability("append")) { + libnotify_loader_.notify_notification_set_hint_string( + notification_, "append", "true"); + } else if (HasCapability("x-canonical-append")) { + libnotify_loader_.notify_notification_set_hint_string( + notification_, "x-canonical-append", "true"); + } + + GError* error = nullptr; + libnotify_loader_.notify_notification_show(notification_, &error); + if (error) { + log_and_clear_error(error, "notify_notification_show"); + NotificationFailed(); + return; + } + + delegate()->NotificationDisplayed(); +} + +void LibnotifyNotification::Dismiss() { + if (!notification_) { + Destroy(); + return; + } + + GError* error = nullptr; + libnotify_loader_.notify_notification_close(notification_, &error); + if (error) { + log_and_clear_error(error, "notify_notification_close"); + Destroy(); + } +} + +void LibnotifyNotification::OnNotificationClosed( + NotifyNotification* notification) { + NotificationDismissed(); +} + +void LibnotifyNotification::OnNotificationView( + NotifyNotification* notification, char* action) { + NotificationClicked(); +} + +} // namespace brightray diff --git a/brightray/browser/linux/libnotify_notification.h b/brightray/browser/linux/libnotify_notification.h new file mode 100644 index 000000000000..cb9384cb0e0c --- /dev/null +++ b/brightray/browser/linux/libnotify_notification.h @@ -0,0 +1,44 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ +#define BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ + +#include "browser/linux/libnotify_loader.h" +#include "browser/notification.h" +#include "ui/base/glib/glib_signal.h" + +namespace brightray { + +class LibnotifyNotification : public Notification { + public: + LibnotifyNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + virtual ~LibnotifyNotification(); + + static bool Initialize(); + + // Notification: + void Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) override; + void Dismiss() override; + + private: + CHROMEG_CALLBACK_0(LibnotifyNotification, void, OnNotificationClosed, + NotifyNotification*); + CHROMEG_CALLBACK_1(LibnotifyNotification, void, OnNotificationView, + NotifyNotification*, char*); + + NotifyNotification* notification_; + + DISALLOW_COPY_AND_ASSIGN(LibnotifyNotification); +}; + +} // namespace brightray + +#endif // BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ diff --git a/brightray/browser/linux/notification_presenter_linux.cc b/brightray/browser/linux/notification_presenter_linux.cc new file mode 100644 index 000000000000..6df5ebaaec90 --- /dev/null +++ b/brightray/browser/linux/notification_presenter_linux.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/linux/notification_presenter_linux.h" + +#include "browser/linux/libnotify_notification.h" + +namespace brightray { + +// static +NotificationPresenter* NotificationPresenter::Create() { + if (!LibnotifyNotification::Initialize()) + return nullptr; + return new NotificationPresenterLinux; +} + +NotificationPresenterLinux::NotificationPresenterLinux() { +} + +NotificationPresenterLinux::~NotificationPresenterLinux() { +} + +Notification* NotificationPresenterLinux::CreateNotificationObject( + NotificationDelegate* delegate) { + return new LibnotifyNotification(delegate, this); +} + +} // namespace brightray diff --git a/brightray/browser/linux/notification_presenter_linux.h b/brightray/browser/linux/notification_presenter_linux.h new file mode 100644 index 000000000000..a90f31ea60ef --- /dev/null +++ b/brightray/browser/linux/notification_presenter_linux.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_LINUX_H_ +#define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_LINUX_H_ + +#include "browser/notification_presenter.h" + +namespace brightray { + +class NotificationPresenterLinux : public NotificationPresenter { + public: + NotificationPresenterLinux(); + ~NotificationPresenterLinux(); + + private: + Notification* CreateNotificationObject( + NotificationDelegate* delegate) override; + + DISALLOW_COPY_AND_ASSIGN(NotificationPresenterLinux); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/mac/bry_application.h b/brightray/browser/mac/bry_application.h new file mode 100644 index 000000000000..98594861496b --- /dev/null +++ b/brightray/browser/mac/bry_application.h @@ -0,0 +1,7 @@ +#import "base/mac/scoped_sending_event.h" + +@interface BRYApplication : NSApplication { + BOOL _handlingSendEvent; +} + +@end diff --git a/brightray/browser/mac/bry_application.mm b/brightray/browser/mac/bry_application.mm new file mode 100644 index 000000000000..aca8392bc35c --- /dev/null +++ b/brightray/browser/mac/bry_application.mm @@ -0,0 +1,19 @@ +#import "bry_application.h" + +@interface BRYApplication () + +@property (nonatomic, assign, getter = isHandlingSendEvent) BOOL handlingSendEvent; + +@end + +@implementation BRYApplication + +@synthesize handlingSendEvent = _handlingSendEvent; + +- (void)sendEvent:(NSEvent *)theEvent +{ + base::mac::ScopedSendingEvent scopedSendingEvent; + [super sendEvent:theEvent]; +} + +@end diff --git a/brightray/browser/mac/bry_inspectable_web_contents_view.h b/brightray/browser/mac/bry_inspectable_web_contents_view.h new file mode 100644 index 000000000000..9819f2d767e4 --- /dev/null +++ b/brightray/browser/mac/bry_inspectable_web_contents_view.h @@ -0,0 +1,36 @@ +#import + +#include "browser/devtools_contents_resizing_strategy.h" + +#include "base/mac/scoped_nsobject.h" +#include "ui/base/cocoa/base_view.h" + +namespace brightray { +class InspectableWebContentsViewMac; +} + +using brightray::InspectableWebContentsViewMac; + +@interface BRYInspectableWebContentsView : BaseView { +@private + brightray::InspectableWebContentsViewMac* inspectableWebContentsView_; + + base::scoped_nsobject devtools_window_; + BOOL devtools_visible_; + BOOL devtools_docked_; + BOOL devtools_is_first_responder_; + + DevToolsContentsResizingStrategy strategy_; +} + +- (instancetype)initWithInspectableWebContentsViewMac:(InspectableWebContentsViewMac*)view; +- (void)removeObservers; +- (void)notifyDevToolsFocused; +- (void)setDevToolsVisible:(BOOL)visible; +- (BOOL)isDevToolsVisible; +- (BOOL)isDevToolsFocused; +- (void)setIsDocked:(BOOL)docked; +- (void)setContentsResizingStrategy:(const DevToolsContentsResizingStrategy&)strategy; +- (void)setTitle:(NSString*)title; + +@end diff --git a/brightray/browser/mac/bry_inspectable_web_contents_view.mm b/brightray/browser/mac/bry_inspectable_web_contents_view.mm new file mode 100644 index 000000000000..25e7083948ba --- /dev/null +++ b/brightray/browser/mac/bry_inspectable_web_contents_view.mm @@ -0,0 +1,252 @@ +#include "browser/mac/bry_inspectable_web_contents_view.h" + +#include "browser/inspectable_web_contents_impl.h" +#include "browser/inspectable_web_contents_view_delegate.h" +#include "browser/inspectable_web_contents_view_mac.h" +#include "browser/mac/event_dispatching_window.h" + +#include "content/public/browser/render_widget_host_view.h" +#include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h" + +@implementation BRYInspectableWebContentsView + +- (instancetype)initWithInspectableWebContentsViewMac:(InspectableWebContentsViewMac*)view { + self = [super init]; + if (!self) + return nil; + + inspectableWebContentsView_ = view; + devtools_visible_ = NO; + devtools_docked_ = NO; + devtools_is_first_responder_ = NO; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(viewDidBecomeFirstResponder:) + name:kViewDidBecomeFirstResponder + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(parentWindowBecameMain:) + name:NSWindowDidBecomeMainNotification + object:nil]; + + auto contents = inspectableWebContentsView_->inspectable_web_contents()->GetWebContents(); + auto contentsView = contents->GetNativeView(); + [contentsView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [self addSubview:contentsView]; + + // See https://code.google.com/p/chromium/issues/detail?id=348490. + [self setWantsLayer:YES]; + + return self; +} + +- (void)removeObservers { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize { + [self adjustSubviews]; +} + +- (IBAction)showDevTools:(id)sender { + inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(); +} + +- (void)notifyDevToolsFocused { + if (inspectableWebContentsView_->GetDelegate()) + inspectableWebContentsView_->GetDelegate()->DevToolsFocused(); +} + +- (void)setDevToolsVisible:(BOOL)visible { + if (visible == devtools_visible_) + return; + + auto inspectable_web_contents = inspectableWebContentsView_->inspectable_web_contents(); + auto webContents = inspectable_web_contents->GetWebContents(); + auto devToolsWebContents = inspectable_web_contents->GetDevToolsWebContents(); + auto devToolsView = devToolsWebContents->GetNativeView(); + + if (visible && devtools_docked_) { + webContents->SetAllowOtherViews(true); + devToolsWebContents->SetAllowOtherViews(true); + } else { + webContents->SetAllowOtherViews(false); + } + + devtools_visible_ = visible; + if (devtools_docked_) { + if (visible) { + // Place the devToolsView under contentsView, notice that we didn't set + // sizes for them until the setContentsResizingStrategy message. + [self addSubview:devToolsView positioned:NSWindowBelow relativeTo:nil]; + [self adjustSubviews]; + + // Focus on web view. + devToolsWebContents->RestoreFocus(); + } else { + gfx::ScopedCocoaDisableScreenUpdates disabler; + [devToolsView removeFromSuperview]; + [self adjustSubviews]; + } + } else { + if (visible) { + [devtools_window_ makeKeyAndOrderFront:nil]; + } else { + [[self window] makeKeyAndOrderFront:nil]; + [devtools_window_ setDelegate:nil]; + [devtools_window_ close]; + devtools_window_.reset(); + } + } +} + +- (BOOL)isDevToolsVisible { + return devtools_visible_; +} + +- (BOOL)isDevToolsFocused { + if (devtools_docked_) { + return [[self window] isKeyWindow] && devtools_is_first_responder_; + } else { + return [devtools_window_ isKeyWindow]; + } +} + +- (void)setIsDocked:(BOOL)docked { + // Revert to no-devtools state. + [self setDevToolsVisible:NO]; + + // Switch to new state. + devtools_docked_ = docked; + if (!docked) { + auto inspectable_web_contents = inspectableWebContentsView_->inspectable_web_contents(); + auto devToolsWebContents = inspectable_web_contents->GetDevToolsWebContents(); + auto devToolsView = devToolsWebContents->GetNativeView(); + + auto styleMask = NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask | + NSTexturedBackgroundWindowMask | + NSUnifiedTitleAndToolbarWindowMask; + devtools_window_.reset([[EventDispatchingWindow alloc] + initWithContentRect:NSMakeRect(0, 0, 800, 600) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:YES]); + [devtools_window_ setDelegate:self]; + [devtools_window_ setFrameAutosaveName:@"brightray.devtools"]; + [devtools_window_ setTitle:@"Developer Tools"]; + [devtools_window_ setReleasedWhenClosed:NO]; + [devtools_window_ setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; + [devtools_window_ setContentBorderThickness:24 forEdge:NSMaxYEdge]; + + NSView* contentView = [devtools_window_ contentView]; + devToolsView.frame = contentView.bounds; + devToolsView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + + [contentView addSubview:devToolsView]; + } + [self setDevToolsVisible:YES]; +} + +- (void)setContentsResizingStrategy:(const DevToolsContentsResizingStrategy&)strategy { + strategy_.CopyFrom(strategy); + [self adjustSubviews]; +} + +- (void)adjustSubviews { + if (![[self subviews] count]) + return; + + if (![self isDevToolsVisible] || devtools_window_) { + DCHECK_EQ(1u, [[self subviews] count]); + NSView* contents = [[self subviews] objectAtIndex:0]; + [contents setFrame:[self bounds]]; + return; + } + + NSView* devToolsView = [[self subviews] objectAtIndex:0]; + NSView* contentsView = [[self subviews] objectAtIndex:1]; + + DCHECK_EQ(2u, [[self subviews] count]); + + gfx::Rect new_devtools_bounds; + gfx::Rect new_contents_bounds; + ApplyDevToolsContentsResizingStrategy( + strategy_, gfx::Size(NSSizeToCGSize([self bounds].size)), + &new_devtools_bounds, &new_contents_bounds); + [devToolsView setFrame:[self flipRectToNSRect:new_devtools_bounds]]; + [contentsView setFrame:[self flipRectToNSRect:new_contents_bounds]]; +} + +- (void)setTitle:(NSString*)title { + [devtools_window_ setTitle:title]; +} + +- (void)viewDidBecomeFirstResponder:(NSNotification*)notification { + auto inspectable_web_contents = inspectableWebContentsView_->inspectable_web_contents(); + if (!inspectable_web_contents) + return; + auto webContents = inspectable_web_contents->GetWebContents(); + auto webContentsView = webContents->GetNativeView(); + + NSView* view = [notification object]; + if ([[webContentsView subviews] containsObject:view]) { + devtools_is_first_responder_ = NO; + return; + } + + auto devToolsWebContents = inspectable_web_contents->GetDevToolsWebContents(); + if (!devToolsWebContents) + return; + auto devToolsView = devToolsWebContents->GetNativeView(); + + if ([[devToolsView subviews] containsObject:view]) { + devtools_is_first_responder_ = YES; + [self notifyDevToolsFocused]; + } +} + +- (void)parentWindowBecameMain:(NSNotification*)notification { + NSWindow* parentWindow = [notification object]; + if ([self window] == parentWindow && devtools_docked_ && devtools_is_first_responder_) + [self notifyDevToolsFocused]; +} + +#pragma mark - NSWindowDelegate + +- (void)windowWillClose:(NSNotification*)notification { + inspectableWebContentsView_->inspectable_web_contents()->CloseDevTools(); +} + +- (void)windowDidBecomeMain:(NSNotification*)notification { + content::WebContents* web_contents = + inspectableWebContentsView_->inspectable_web_contents()->GetDevToolsWebContents(); + if (!web_contents) + return; + + web_contents->RestoreFocus(); + + content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView(); + if (rwhv) + rwhv->SetActive(true); + + [self notifyDevToolsFocused]; +} + +- (void)windowDidResignMain:(NSNotification*)notification { + content::WebContents* web_contents = + inspectableWebContentsView_->inspectable_web_contents()->GetDevToolsWebContents(); + if (!web_contents) + return; + + web_contents->StoreFocus(); + + content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView(); + if (rwhv) + rwhv->SetActive(false); +} + +@end diff --git a/brightray/browser/mac/cocoa_notification.h b/brightray/browser/mac/cocoa_notification.h new file mode 100644 index 000000000000..f6ec9e9da6b0 --- /dev/null +++ b/brightray/browser/mac/cocoa_notification.h @@ -0,0 +1,42 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_MAC_COCOA_NOTIFICATION_H_ +#define BROWSER_MAC_COCOA_NOTIFICATION_H_ + +#import + +#include "base/mac/scoped_nsobject.h" +#include "browser/notification.h" + +namespace brightray { + +class CocoaNotification : public Notification { + public: + CocoaNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + ~CocoaNotification(); + + // Notification: + void Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) override; + void Dismiss() override; + + void NotificationDisplayed(); + + NSUserNotification* notification() const { return notification_; } + + private: + base::scoped_nsobject notification_; + + DISALLOW_COPY_AND_ASSIGN(CocoaNotification); +}; + +} // namespace brightray + +#endif // BROWSER_MAC_COCOA_NOTIFICATION_H_ diff --git a/brightray/browser/mac/cocoa_notification.mm b/brightray/browser/mac/cocoa_notification.mm new file mode 100644 index 000000000000..f4599487c846 --- /dev/null +++ b/brightray/browser/mac/cocoa_notification.mm @@ -0,0 +1,64 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/mac/cocoa_notification.h" + +#include "base/mac/mac_util.h" +#include "base/strings/sys_string_conversions.h" +#include "browser/notification_delegate.h" +#include "browser/notification_presenter.h" +#include "skia/ext/skia_utils_mac.h" + +namespace brightray { + +CocoaNotification::CocoaNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter) + : Notification(delegate, presenter) { +} + +CocoaNotification::~CocoaNotification() { + if (notification_) + [NSUserNotificationCenter.defaultUserNotificationCenter + removeDeliveredNotification:notification_]; +} + +void CocoaNotification::Show(const base::string16& title, + const base::string16& body, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) { + notification_.reset([[NSUserNotification alloc] init]); + [notification_ setTitle:base::SysUTF16ToNSString(title)]; + [notification_ setInformativeText:base::SysUTF16ToNSString(body)]; + + if ([notification_ respondsToSelector:@selector(setContentImage:)] && + !icon.drawsNothing()) { + NSImage* image = skia::SkBitmapToNSImageWithColorSpace( + icon, base::mac::GetGenericRGBColorSpace()); + [notification_ setContentImage:image]; + } + + if (silent) { + [notification_ setSoundName:nil]; + } else { + [notification_ setSoundName:NSUserNotificationDefaultSoundName]; + } + + [NSUserNotificationCenter.defaultUserNotificationCenter + deliverNotification:notification_]; +} + +void CocoaNotification::Dismiss() { + if (notification_) + [NSUserNotificationCenter.defaultUserNotificationCenter + removeDeliveredNotification:notification_]; + NotificationDismissed(); +} + +void CocoaNotification::NotificationDisplayed() { + delegate()->NotificationDisplayed(); +} + +} // namespace brightray diff --git a/brightray/browser/mac/event_dispatching_window.h b/brightray/browser/mac/event_dispatching_window.h new file mode 100644 index 000000000000..27ed9e6bf2f9 --- /dev/null +++ b/brightray/browser/mac/event_dispatching_window.h @@ -0,0 +1,19 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_EVENT_DISPATCHING_WINDOW_H_ +#define BROWSER_EVENT_DISPATCHING_WINDOW_H_ + +#import "ui/base/cocoa/underlay_opengl_hosting_window.h" + +@interface EventDispatchingWindow : UnderlayOpenGLHostingWindow { + @private + BOOL redispatchingEvent_; +} + +- (void)redispatchKeyEvent:(NSEvent*)event; + +@end + +#endif // BROWSER_EVENT_DISPATCHING_WINDOW_H_ diff --git a/brightray/browser/mac/event_dispatching_window.mm b/brightray/browser/mac/event_dispatching_window.mm new file mode 100644 index 000000000000..08e8edebb0b9 --- /dev/null +++ b/brightray/browser/mac/event_dispatching_window.mm @@ -0,0 +1,34 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/mac/event_dispatching_window.h" + +@implementation EventDispatchingWindow + +- (void)sendEvent:(NSEvent*)event { + if (!redispatchingEvent_) + [super sendEvent:event]; +} + +- (BOOL)performKeyEquivalent:(NSEvent*)event { + if (redispatchingEvent_) + return NO; + else + return [super performKeyEquivalent:event]; + } + +- (void)redispatchKeyEvent:(NSEvent*)event { + NSEventType eventType = [event type]; + if (eventType != NSKeyDown && eventType != NSKeyUp && + eventType != NSFlagsChanged) { + return; + } + + // Redispatch the event. + redispatchingEvent_ = YES; + [NSApp sendEvent:event]; + redispatchingEvent_ = NO; +} + +@end diff --git a/brightray/browser/mac/notification_center_delegate.h b/brightray/browser/mac/notification_center_delegate.h new file mode 100644 index 000000000000..6bee83d411b4 --- /dev/null +++ b/brightray/browser/mac/notification_center_delegate.h @@ -0,0 +1,22 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_MAC_NOTIFICATION_DELEGATE_H_ +#define BROWSER_MAC_NOTIFICATION_DELEGATE_H_ + +#import + +namespace brightray { +class NotificationPresenterMac; +} + +@interface NotificationCenterDelegate : + NSObject { + @private + brightray::NotificationPresenterMac* presenter_; +} +- (instancetype)initWithPresenter:(brightray::NotificationPresenterMac*)presenter; +@end + +#endif // BROWSER_MAC_NOTIFICATION_DELEGATE_H_ diff --git a/brightray/browser/mac/notification_center_delegate.mm b/brightray/browser/mac/notification_center_delegate.mm new file mode 100644 index 000000000000..fd77e0685e4b --- /dev/null +++ b/brightray/browser/mac/notification_center_delegate.mm @@ -0,0 +1,41 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/mac/notification_center_delegate.h" + +#include "browser/mac/cocoa_notification.h" +#include "browser/mac/notification_presenter_mac.h" + +@implementation NotificationCenterDelegate + +- (instancetype)initWithPresenter:(brightray::NotificationPresenterMac*)presenter { + self = [super init]; + if (!self) + return nil; + + presenter_ = presenter; + return self; +} + +- (void)userNotificationCenter:(NSUserNotificationCenter*)center + didDeliverNotification:(NSUserNotification*)notif { + auto notification = presenter_->GetNotification(notif); + if (notification) + notification->NotificationDisplayed(); +} + +- (void)userNotificationCenter:(NSUserNotificationCenter*)center + didActivateNotification:(NSUserNotification *)notif { + auto notification = presenter_->GetNotification(notif); + if (notification) + notification->NotificationClicked(); +} + +- (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center + shouldPresentNotification:(NSUserNotification*)notification { + // Display notifications even if the app is active. + return YES; +} + +@end diff --git a/brightray/browser/mac/notification_presenter_mac.h b/brightray/browser/mac/notification_presenter_mac.h new file mode 100644 index 000000000000..514a27625eab --- /dev/null +++ b/brightray/browser/mac/notification_presenter_mac.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_MAC_H_ +#define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_MAC_H_ + +#include "base/mac/scoped_nsobject.h" +#include "browser/mac/notification_center_delegate.h" +#include "browser/notification_presenter.h" + +namespace brightray { + +class CocoaNotification; + +class NotificationPresenterMac : public NotificationPresenter { + public: + CocoaNotification* GetNotification(NSUserNotification* notif); + + NotificationPresenterMac(); + ~NotificationPresenterMac(); + + private: + Notification* CreateNotificationObject( + NotificationDelegate* delegate) override; + + base::scoped_nsobject + notification_center_delegate_; + + DISALLOW_COPY_AND_ASSIGN(NotificationPresenterMac); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/mac/notification_presenter_mac.mm b/brightray/browser/mac/notification_presenter_mac.mm new file mode 100644 index 000000000000..fec4c1a05394 --- /dev/null +++ b/brightray/browser/mac/notification_presenter_mac.mm @@ -0,0 +1,43 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/mac/notification_presenter_mac.h" + +#include "browser/mac/cocoa_notification.h" +#include "browser/mac/notification_center_delegate.h" + +namespace brightray { + +// static +NotificationPresenter* NotificationPresenter::Create() { + return new NotificationPresenterMac; +} + +CocoaNotification* NotificationPresenterMac::GetNotification( + NSUserNotification* ns_notification) { + for (Notification* notification : notifications()) { + auto native_notification = static_cast(notification); + if ([native_notification->notification() isEqual:ns_notification]) + return native_notification; + } + return nullptr; +} + +NotificationPresenterMac::NotificationPresenterMac() + : notification_center_delegate_( + [[NotificationCenterDelegate alloc] initWithPresenter:this]) { + NSUserNotificationCenter.defaultUserNotificationCenter.delegate = + notification_center_delegate_; +} + +NotificationPresenterMac::~NotificationPresenterMac() { + NSUserNotificationCenter.defaultUserNotificationCenter.delegate = nil; +} + +Notification* NotificationPresenterMac::CreateNotificationObject( + NotificationDelegate* delegate) { + return new CocoaNotification(delegate, this); +} + +} // namespace brightray diff --git a/brightray/browser/media/media_capture_devices_dispatcher.cc b/brightray/browser/media/media_capture_devices_dispatcher.cc new file mode 100644 index 000000000000..93ec53557d33 --- /dev/null +++ b/brightray/browser/media/media_capture_devices_dispatcher.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "browser/media/media_capture_devices_dispatcher.h" + +#include "base/logging.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/media_capture_devices.h" +#include "content/public/common/media_stream_request.h" + +namespace brightray { + +using content::BrowserThread; +using content::MediaStreamDevices; + +namespace { + +// Finds a device in |devices| that has |device_id|, or NULL if not found. +const content::MediaStreamDevice* FindDeviceWithId( + const content::MediaStreamDevices& devices, + const std::string& device_id) { + auto iter = devices.begin(); + for (; iter != devices.end(); ++iter) { + if (iter->id == device_id) { + return &(*iter); + } + } + return nullptr; +} + +const MediaStreamDevices& EmptyDevices() { + static MediaStreamDevices* devices = new MediaStreamDevices; + return *devices; +} + +} // namespace + +MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { + return base::Singleton::get(); +} + +MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() + : is_device_enumeration_disabled_(false) { + // MediaCaptureDevicesDispatcher is a singleton. It should be created on + // UI thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} + +const MediaStreamDevices& +MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (is_device_enumeration_disabled_) + return EmptyDevices(); + return content::MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(); +} + +const MediaStreamDevices& +MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (is_device_enumeration_disabled_) + return EmptyDevices(); + return content::MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); +} + +void MediaCaptureDevicesDispatcher::GetDefaultDevices( + bool audio, + bool video, + content::MediaStreamDevices* devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(audio || video); + + if (audio) { + const content::MediaStreamDevice* device = GetFirstAvailableAudioDevice(); + if (device) + devices->push_back(*device); + } + + if (video) { + const content::MediaStreamDevice* device = GetFirstAvailableVideoDevice(); + if (device) + devices->push_back(*device); + } +} + +const content::MediaStreamDevice* +MediaCaptureDevicesDispatcher::GetRequestedAudioDevice( + const std::string& requested_audio_device_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices(); + const content::MediaStreamDevice* const device = + FindDeviceWithId(audio_devices, requested_audio_device_id); + return device; +} + +const content::MediaStreamDevice* +MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices(); + if (audio_devices.empty()) + return nullptr; + return &(*audio_devices.begin()); +} + +const content::MediaStreamDevice* +MediaCaptureDevicesDispatcher::GetRequestedVideoDevice( + const std::string& requested_video_device_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices(); + const content::MediaStreamDevice* const device = + FindDeviceWithId(video_devices, requested_video_device_id); + return device; +} + +const content::MediaStreamDevice* +MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices(); + if (video_devices.empty()) + return nullptr; + return &(*video_devices.begin()); +} + +void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() { + is_device_enumeration_disabled_ = true; +} + +void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() { +} + +void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() { +} + +void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged( + int render_process_id, + int render_view_id, + int page_request_id, + const GURL& security_origin, + content::MediaStreamType stream_type, + content::MediaRequestState state) { +} + +void MediaCaptureDevicesDispatcher::OnCreatingAudioStream( + int render_process_id, + int render_view_id) { +} + +void MediaCaptureDevicesDispatcher::OnSetCapturingLinkSecured( + int render_process_id, + int render_frame_id, + int page_request_id, + content::MediaStreamType stream_type, + bool is_secure) { +} + +} // namespace brightray diff --git a/brightray/browser/media/media_capture_devices_dispatcher.h b/brightray/browser/media/media_capture_devices_dispatcher.h new file mode 100644 index 000000000000..dbbd66d25800 --- /dev/null +++ b/brightray/browser/media/media_capture_devices_dispatcher.h @@ -0,0 +1,84 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ +#define BRIGHTRAY_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ + +#include "base/callback.h" +#include "base/memory/singleton.h" +#include "content/public/browser/media_observer.h" +#include "content/public/browser/web_contents_delegate.h" +#include "content/public/common/media_stream_request.h" + +namespace brightray { + +// This singleton is used to receive updates about media events from the content +// layer. +class MediaCaptureDevicesDispatcher : public content::MediaObserver { + public: + static MediaCaptureDevicesDispatcher* GetInstance(); + + // Methods for observers. Called on UI thread. + const content::MediaStreamDevices& GetAudioCaptureDevices(); + const content::MediaStreamDevices& GetVideoCaptureDevices(); + + // Helper to get the default devices which can be used by the media request. + // Uses the first available devices if the default devices are not available. + // If the return list is empty, it means there is no available device on the + // OS. + // Called on the UI thread. + void GetDefaultDevices(bool audio, + bool video, + content::MediaStreamDevices* devices); + + // Helpers for picking particular requested devices, identified by raw id. + // If the device requested is not available it will return NULL. + const content::MediaStreamDevice* + GetRequestedAudioDevice(const std::string& requested_audio_device_id); + const content::MediaStreamDevice* + GetRequestedVideoDevice(const std::string& requested_video_device_id); + + // Returns the first available audio or video device, or NULL if no devices + // are available. + const content::MediaStreamDevice* GetFirstAvailableAudioDevice(); + const content::MediaStreamDevice* GetFirstAvailableVideoDevice(); + + // Unittests that do not require actual device enumeration should call this + // API on the singleton. It is safe to call this multiple times on the + // signleton. + void DisableDeviceEnumerationForTesting(); + + // Overridden from content::MediaObserver: + void OnAudioCaptureDevicesChanged() override; + void OnVideoCaptureDevicesChanged() override; + void OnMediaRequestStateChanged( + int render_process_id, + int render_view_id, + int page_request_id, + const GURL& security_origin, + content::MediaStreamType stream_type, + content::MediaRequestState state) override; + void OnCreatingAudioStream(int render_process_id, + int render_view_id) override; + void OnSetCapturingLinkSecured(int render_process_id, + int render_frame_id, + int page_request_id, + content::MediaStreamType stream_type, + bool is_secure) override; + + private: + friend struct base::DefaultSingletonTraits; + + MediaCaptureDevicesDispatcher(); + virtual ~MediaCaptureDevicesDispatcher(); + + // Flag used by unittests to disable device enumeration. + bool is_device_enumeration_disabled_; + + DISALLOW_COPY_AND_ASSIGN(MediaCaptureDevicesDispatcher); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_ diff --git a/brightray/browser/media/media_device_id_salt.cc b/brightray/browser/media/media_device_id_salt.cc new file mode 100644 index 000000000000..612449b25de8 --- /dev/null +++ b/brightray/browser/media/media_device_id_salt.cc @@ -0,0 +1,53 @@ +// Copyright 2013 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 "browser/media/media_device_id_salt.h" + +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_context.h" + +using content::BrowserThread; + +namespace brightray { + +namespace { + +const char kMediaDeviceIdSalt[] = "brightray.media.device_id_salt"; + +} // namespace + +MediaDeviceIDSalt::MediaDeviceIDSalt(PrefService* pref_service) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + media_device_id_salt_.Init(kMediaDeviceIdSalt, pref_service); + if (media_device_id_salt_.GetValue().empty()) { + media_device_id_salt_.SetValue( + content::ResourceContext::CreateRandomMediaDeviceIDSalt()); + } +} + +MediaDeviceIDSalt::~MediaDeviceIDSalt() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + media_device_id_salt_.Destroy(); +} + +std::string MediaDeviceIDSalt::GetSalt() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return media_device_id_salt_.GetValue(); +} + +// static +void MediaDeviceIDSalt::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterStringPref(kMediaDeviceIdSalt, std::string()); +} + +// static +void MediaDeviceIDSalt::Reset(PrefService* pref_service) { + pref_service->SetString( + kMediaDeviceIdSalt, + content::ResourceContext::CreateRandomMediaDeviceIDSalt()); +} + +} // namespace brightray diff --git a/brightray/browser/media/media_device_id_salt.h b/brightray/browser/media/media_device_id_salt.h new file mode 100644 index 000000000000..7941af8a563d --- /dev/null +++ b/brightray/browser/media/media_device_id_salt.h @@ -0,0 +1,40 @@ +// Copyright 2013 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 BRIGHTRAY_BROWSER_MEDIA_MEDIA_DEVICE_ID_SALT_H_ +#define BRIGHTRAY_BROWSER_MEDIA_MEDIA_DEVICE_ID_SALT_H_ + +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/prefs/pref_member.h" + +class PrefRegistrySimple; +class PrefService; + +namespace brightray { + +// MediaDeviceIDSalt is responsible for creating and retrieving a salt string +// that is used for creating MediaSource IDs that can be cached by a web +// service. If the cache is cleared, the MediaSourceIds are invalidated. +class MediaDeviceIDSalt { + public: + explicit MediaDeviceIDSalt(PrefService* pref_service); + ~MediaDeviceIDSalt(); + + std::string GetSalt(); + + static void RegisterPrefs(PrefRegistrySimple* pref_registry); + static void Reset(PrefService* pref_service); + + private: + StringPrefMember media_device_id_salt_; + + DISALLOW_COPY_AND_ASSIGN(MediaDeviceIDSalt); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_MEDIA_MEDIA_DEVICE_ID_SALT_H_ diff --git a/brightray/browser/media/media_stream_devices_controller.cc b/brightray/browser/media/media_stream_devices_controller.cc new file mode 100644 index 000000000000..c01749ab43d2 --- /dev/null +++ b/brightray/browser/media/media_stream_devices_controller.cc @@ -0,0 +1,198 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "browser/media/media_stream_devices_controller.h" + +#include "browser/media/media_capture_devices_dispatcher.h" + +#include "content/public/browser/desktop_media_id.h" +#include "content/public/common/media_stream_request.h" + +namespace brightray { + +namespace { + +bool HasAnyAvailableDevice() { + const content::MediaStreamDevices& audio_devices = + MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices(); + const content::MediaStreamDevices& video_devices = + MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices(); + + return !audio_devices.empty() || !video_devices.empty(); +} + +} // namespace + +MediaStreamDevicesController::MediaStreamDevicesController( + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) + : request_(request), + callback_(callback), + // For MEDIA_OPEN_DEVICE requests (Pepper) we always request both webcam + // and microphone to avoid popping two infobars. + microphone_requested_( + request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE || + request.request_type == content::MEDIA_OPEN_DEVICE_PEPPER_ONLY), + webcam_requested_( + request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE || + request.request_type == content::MEDIA_OPEN_DEVICE_PEPPER_ONLY) { +} + +MediaStreamDevicesController::~MediaStreamDevicesController() { + if (!callback_.is_null()) { + callback_.Run(content::MediaStreamDevices(), + content::MEDIA_DEVICE_INVALID_STATE, + std::unique_ptr()); + } +} + +bool MediaStreamDevicesController::TakeAction() { + // Do special handling of desktop screen cast. + if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE || + request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE || + request_.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE || + request_.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) { + HandleUserMediaRequest(); + return true; + } + + // Deny the request if there is no device attached to the OS. + if (!HasAnyAvailableDevice()) { + Deny(content::MEDIA_DEVICE_NO_HARDWARE); + return true; + } + + Accept(); + return true; +} + +void MediaStreamDevicesController::Accept() { + // Get the default devices for the request. + content::MediaStreamDevices devices; + if (microphone_requested_ || webcam_requested_) { + switch (request_.request_type) { + case content::MEDIA_OPEN_DEVICE_PEPPER_ONLY: { + const content::MediaStreamDevice* device = nullptr; + // For open device request pick the desired device or fall back to the + // first available of the given type. + if (request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) { + device = MediaCaptureDevicesDispatcher::GetInstance()-> + GetRequestedAudioDevice(request_.requested_audio_device_id); + // TODO(wjia): Confirm this is the intended behavior. + if (!device) { + device = MediaCaptureDevicesDispatcher::GetInstance()-> + GetFirstAvailableAudioDevice(); + } + } else if (request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { + // Pepper API opens only one device at a time. + device = MediaCaptureDevicesDispatcher::GetInstance()-> + GetRequestedVideoDevice(request_.requested_video_device_id); + // TODO(wjia): Confirm this is the intended behavior. + if (!device) { + device = MediaCaptureDevicesDispatcher::GetInstance()-> + GetFirstAvailableVideoDevice(); + } + } + if (device) + devices.push_back(*device); + break; + } case content::MEDIA_GENERATE_STREAM: { + bool needs_audio_device = microphone_requested_; + bool needs_video_device = webcam_requested_; + + // Get the exact audio or video device if an id is specified. + if (!request_.requested_audio_device_id.empty()) { + const content::MediaStreamDevice* audio_device = + MediaCaptureDevicesDispatcher::GetInstance()-> + GetRequestedAudioDevice(request_.requested_audio_device_id); + if (audio_device) { + devices.push_back(*audio_device); + needs_audio_device = false; + } + } + if (!request_.requested_video_device_id.empty()) { + const content::MediaStreamDevice* video_device = + MediaCaptureDevicesDispatcher::GetInstance()-> + GetRequestedVideoDevice(request_.requested_video_device_id); + if (video_device) { + devices.push_back(*video_device); + needs_video_device = false; + } + } + + // If either or both audio and video devices were requested but not + // specified by id, get the default devices. + if (needs_audio_device || needs_video_device) { + MediaCaptureDevicesDispatcher::GetInstance()-> + GetDefaultDevices(needs_audio_device, + needs_video_device, + &devices); + } + break; + } case content::MEDIA_DEVICE_ACCESS: + // Get the default devices for the request. + MediaCaptureDevicesDispatcher::GetInstance()-> + GetDefaultDevices(microphone_requested_, + webcam_requested_, + &devices); + break; + } + } + + content::MediaResponseCallback cb = callback_; + callback_.Reset(); + cb.Run(devices, content::MEDIA_DEVICE_OK, + std::unique_ptr()); +} + +void MediaStreamDevicesController::Deny( + content::MediaStreamRequestResult result) { + content::MediaResponseCallback cb = callback_; + callback_.Reset(); + cb.Run(content::MediaStreamDevices(), + result, + std::unique_ptr()); +} + +void MediaStreamDevicesController::HandleUserMediaRequest() { + content::MediaStreamDevices devices; + + if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { + devices.push_back(content::MediaStreamDevice( + content::MEDIA_TAB_AUDIO_CAPTURE, "", "")); + } + if (request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) { + devices.push_back(content::MediaStreamDevice( + content::MEDIA_TAB_VIDEO_CAPTURE, "", "")); + } + if (request_.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) { + devices.push_back(content::MediaStreamDevice( + content::MEDIA_DESKTOP_AUDIO_CAPTURE, "loopback", "System Audio")); + } + if (request_.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) { + content::DesktopMediaID screen_id; + // If the device id wasn't specified then this is a screen capture request + // (i.e. chooseDesktopMedia() API wasn't used to generate device id). + if (request_.requested_video_device_id.empty()) { + screen_id = content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, + -1 /* kFullDesktopScreenId */); + } else { + screen_id = + content::DesktopMediaID::Parse(request_.requested_video_device_id); + } + + devices.push_back( + content::MediaStreamDevice(content::MEDIA_DESKTOP_VIDEO_CAPTURE, + screen_id.ToString(), "Screen")); + } + + content::MediaResponseCallback cb = callback_; + callback_.Reset(); + cb.Run(devices, + devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : + content::MEDIA_DEVICE_OK, + std::unique_ptr()); +} + +} // namespace brightray diff --git a/brightray/browser/media/media_stream_devices_controller.h b/brightray/browser/media/media_stream_devices_controller.h new file mode 100644 index 000000000000..47aacfb66b64 --- /dev/null +++ b/brightray/browser/media/media_stream_devices_controller.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_ +#define BRIGHTRAY_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_ + +#include + +#include "content/public/browser/web_contents_delegate.h" + +namespace brightray { + +class MediaStreamDevicesController { + public: + MediaStreamDevicesController(const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback); + + virtual ~MediaStreamDevicesController(); + + // Accept or deny the request based on the default policy. + bool TakeAction(); + + // Explicitly accept or deny the request. + void Accept(); + void Deny(content::MediaStreamRequestResult result); + + private: + // Handle the request of desktop or tab screen cast. + void HandleUserMediaRequest(); + + // The original request for access to devices. + const content::MediaStreamRequest request_; + + // The callback that needs to be Run to notify WebRTC of whether access to + // audio/video devices was granted or not. + content::MediaResponseCallback callback_; + + bool microphone_requested_; + bool webcam_requested_; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamDevicesController); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_ diff --git a/brightray/browser/net/devtools_network_conditions.cc b/brightray/browser/net/devtools_network_conditions.cc new file mode 100644 index 000000000000..0005c7f56c74 --- /dev/null +++ b/brightray/browser/net/devtools_network_conditions.cc @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/net/devtools_network_conditions.h" + +namespace brightray { + +DevToolsNetworkConditions::DevToolsNetworkConditions(bool offline) + : offline_(offline), + latency_(0), + download_throughput_(0), + upload_throughput_(0) { +} + +DevToolsNetworkConditions::DevToolsNetworkConditions( + bool offline, + double latency, + double download_throughput, + double upload_throughput) + : offline_(offline), + latency_(latency), + download_throughput_(download_throughput), + upload_throughput_(upload_throughput) { +} + +DevToolsNetworkConditions::~DevToolsNetworkConditions() { +} + +bool DevToolsNetworkConditions::IsThrottling() const { + return !offline_ && ((latency_ != 0.0) || (download_throughput_ != 0.0) || + (upload_throughput_ != 0.0)); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_conditions.h b/brightray/browser/net/devtools_network_conditions.h new file mode 100644 index 000000000000..81aae86f8d49 --- /dev/null +++ b/brightray/browser/net/devtools_network_conditions.h @@ -0,0 +1,43 @@ +// 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. + +#ifndef BROWSER_DEVTOOLS_NETWORK_CONDITIONS_H_ +#define BROWSER_DEVTOOLS_NETWORK_CONDITIONS_H_ + +#include +#include + +#include "base/macros.h" +#include "url/gurl.h" + +namespace brightray { + +class DevToolsNetworkConditions { + public: + explicit DevToolsNetworkConditions(bool offline); + DevToolsNetworkConditions(bool offline, + double latency, + double download_throughput, + double upload_throughput); + ~DevToolsNetworkConditions(); + + bool IsThrottling() const; + + bool offline() const { return offline_; } + double latency() const { return latency_; } + double download_throughput() const { return download_throughput_; } + double upload_throughput() const { return upload_throughput_; } + + private: + const bool offline_; + const double latency_; + const double download_throughput_; + const double upload_throughput_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkConditions); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_CONDITIONS_H_ diff --git a/brightray/browser/net/devtools_network_controller.cc b/brightray/browser/net/devtools_network_controller.cc new file mode 100644 index 000000000000..8bf411ac1a4e --- /dev/null +++ b/brightray/browser/net/devtools_network_controller.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_controller.h" + +#include "browser/net/devtools_network_conditions.h" +#include "browser/net/devtools_network_interceptor.h" +#include "browser/net/devtools_network_transaction.h" + +#include "base/bind.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace brightray { + +DevToolsNetworkController::DevToolsNetworkController() + : appcache_interceptor_(new DevToolsNetworkInterceptor) { +} + +DevToolsNetworkController::~DevToolsNetworkController() { +} + +void DevToolsNetworkController::SetNetworkState( + const std::string& client_id, + std::unique_ptr conditions) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + auto it = interceptors_.find(client_id); + if (it == interceptors_.end()) { + if (!conditions) + return; + std::unique_ptr new_interceptor( + new DevToolsNetworkInterceptor); + new_interceptor->UpdateConditions(std::move(conditions)); + interceptors_[client_id] = std::move(new_interceptor); + } else { + if (!conditions) { + std::unique_ptr online_conditions( + new DevToolsNetworkConditions(false)); + it->second->UpdateConditions(std::move(online_conditions)); + interceptors_.erase(client_id); + } else { + it->second->UpdateConditions(std::move(conditions)); + } + } + + bool has_offline_interceptors = false; + for (const auto& interceptor : interceptors_) { + if (interceptor.second->IsOffline()) { + has_offline_interceptors = true; + break; + } + } + + bool is_appcache_offline = appcache_interceptor_->IsOffline(); + if (is_appcache_offline != has_offline_interceptors) { + std::unique_ptr appcache_conditions( + new DevToolsNetworkConditions(has_offline_interceptors)); + appcache_interceptor_->UpdateConditions(std::move(appcache_conditions)); + } +} + +DevToolsNetworkInterceptor* +DevToolsNetworkController::GetInterceptor(const std::string& client_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (interceptors_.empty() || client_id.empty()) + return nullptr; + + auto it = interceptors_.find(client_id); + if (it == interceptors_.end()) + return nullptr; + + return it->second.get(); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_controller.h b/brightray/browser/net/devtools_network_controller.h new file mode 100644 index 000000000000..c36d888df110 --- /dev/null +++ b/brightray/browser/net/devtools_network_controller.h @@ -0,0 +1,44 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_DEVTOOLS_NETWORK_CONTROLLER_H_ +#define BROWSER_DEVTOOLS_NETWORK_CONTROLLER_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "base/threading/thread_checker.h" + +namespace brightray { + +class DevToolsNetworkConditions; +class DevToolsNetworkInterceptor; +class DevToolsNetworkTransaction; + +class DevToolsNetworkController { + public: + DevToolsNetworkController(); + virtual ~DevToolsNetworkController(); + + void SetNetworkState(const std::string& client_id, + std::unique_ptr conditions); + + DevToolsNetworkInterceptor* GetInterceptor(const std::string& client_id); + + private: + using InterceptorMap = + std::unordered_map>; + + std::unique_ptr appcache_interceptor_; + InterceptorMap interceptors_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkController); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_CONTROLLER_H_ diff --git a/brightray/browser/net/devtools_network_controller_handle.cc b/brightray/browser/net/devtools_network_controller_handle.cc new file mode 100644 index 000000000000..44b7b9c6c14d --- /dev/null +++ b/brightray/browser/net/devtools_network_controller_handle.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_controller_handle.h" + +#include "base/bind.h" +#include "browser/net/devtools_network_conditions.h" +#include "browser/net/devtools_network_controller.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace brightray { + +DevToolsNetworkControllerHandle::DevToolsNetworkControllerHandle() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +DevToolsNetworkControllerHandle::~DevToolsNetworkControllerHandle() { + BrowserThread::DeleteSoon(BrowserThread::IO, + FROM_HERE, + controller_.release()); +} + +void DevToolsNetworkControllerHandle::SetNetworkState( + const std::string& client_id, + std::unique_ptr conditions) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&DevToolsNetworkControllerHandle::SetNetworkStateOnIO, + base::Unretained(this), client_id, base::Passed(&conditions))); +} + +DevToolsNetworkController* DevToolsNetworkControllerHandle::GetController() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + LazyInitialize(); + return controller_.get(); +} + +void DevToolsNetworkControllerHandle::LazyInitialize() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!controller_) + controller_.reset(new DevToolsNetworkController); +} + +void DevToolsNetworkControllerHandle::SetNetworkStateOnIO( + const std::string& client_id, + std::unique_ptr conditions) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + LazyInitialize(); + controller_->SetNetworkState(client_id, std::move(conditions)); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_controller_handle.h b/brightray/browser/net/devtools_network_controller_handle.h new file mode 100644 index 000000000000..2d038e837f80 --- /dev/null +++ b/brightray/browser/net/devtools_network_controller_handle.h @@ -0,0 +1,45 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_ +#define BROWSER_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_ + +#include +#include + +#include "base/macros.h" + +namespace brightray { + +class DevToolsNetworkConditions; +class DevToolsNetworkController; + +// A handle to manage an IO-thread DevToolsNetworkController on the IO thread +// while allowing SetNetworkState to be called from the UI thread. +class DevToolsNetworkControllerHandle { + public: + DevToolsNetworkControllerHandle(); + ~DevToolsNetworkControllerHandle(); + + // Called on the UI thread. + void SetNetworkState(const std::string& client_id, + std::unique_ptr conditions); + + // Called on the IO thread. + DevToolsNetworkController* GetController(); + + private: + void LazyInitialize(); + void SetNetworkStateOnIO( + const std::string& client_id, + std::unique_ptr conditions); + + std::unique_ptr controller_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkControllerHandle); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_ diff --git a/brightray/browser/net/devtools_network_interceptor.cc b/brightray/browser/net/devtools_network_interceptor.cc new file mode 100644 index 000000000000..73e8538310db --- /dev/null +++ b/brightray/browser/net/devtools_network_interceptor.cc @@ -0,0 +1,293 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/net/devtools_network_interceptor.h" + +#include +#include + +#include "base/time/time.h" +#include "browser/net/devtools_network_conditions.h" +#include "net/base/net_errors.h" + +namespace brightray { + +namespace { + +int64_t kPacketSize = 1500; + +base::TimeDelta CalculateTickLength(double throughput) { + if (!throughput) + return base::TimeDelta(); + + int64_t us_tick_length = (1000000L * kPacketSize) / throughput; + if (us_tick_length == 0) + us_tick_length = 1; + return base::TimeDelta::FromMicroseconds(us_tick_length); +} + +} // namespace + +DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord() { +} + +DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord( + const ThrottleRecord& other) = default; + +DevToolsNetworkInterceptor::ThrottleRecord::~ThrottleRecord() { +} + +DevToolsNetworkInterceptor::DevToolsNetworkInterceptor() + : conditions_(new DevToolsNetworkConditions(false)), + download_last_tick_(0), + upload_last_tick_(0), + weak_ptr_factory_(this) { +} + +DevToolsNetworkInterceptor::~DevToolsNetworkInterceptor() { +} + +base::WeakPtr +DevToolsNetworkInterceptor::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +void DevToolsNetworkInterceptor::FinishRecords( + ThrottleRecords* records, bool offline) { + ThrottleRecords temp; + temp.swap(*records); + for (const ThrottleRecord& record : temp) { + bool failed = offline && !record.is_upload; + record.callback.Run( + failed ? net::ERR_INTERNET_DISCONNECTED : record.result, + record.bytes); + } +} + +void DevToolsNetworkInterceptor::UpdateConditions( + std::unique_ptr conditions) { + DCHECK(conditions); + base::TimeTicks now = base::TimeTicks::Now(); + if (conditions_->IsThrottling()) + UpdateThrottled(now); + + conditions_ = std::move(conditions); + + bool offline = conditions_->offline(); + if (offline || !conditions_->IsThrottling()) { + timer_.Stop(); + FinishRecords(&download_, offline); + FinishRecords(&upload_, offline); + FinishRecords(&suspended_, offline); + return; + } + + // Throttling. + DCHECK(conditions_->download_throughput() != 0 || + conditions_->upload_throughput() != 0); + offset_ = now; + + download_last_tick_ = 0; + download_tick_length_ = CalculateTickLength( + conditions_->download_throughput()); + + upload_last_tick_ = 0; + upload_tick_length_ = CalculateTickLength(conditions_->upload_throughput()); + + latency_length_ = base::TimeDelta(); + double latency = conditions_->latency(); + if (latency > 0) + latency_length_ = base::TimeDelta::FromMillisecondsD(latency); + ArmTimer(now); +} + +uint64_t DevToolsNetworkInterceptor::UpdateThrottledRecords( + base::TimeTicks now, + ThrottleRecords* records, + uint64_t last_tick, + base::TimeDelta tick_length) { + if (tick_length.is_zero()) { + DCHECK(records->empty()); + return last_tick; + } + + int64_t new_tick = (now - offset_) / tick_length; + int64_t ticks = new_tick - last_tick; + + int64_t length = records->size(); + if (!length) + return new_tick; + + int64_t shift = ticks % length; + for (int64_t i = 0; i < length; ++i) { + (*records)[i].bytes -= + (ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0); + } + std::rotate(records->begin(), records->begin() + shift, records->end()); + return new_tick; +} + +void DevToolsNetworkInterceptor::UpdateThrottled(base::TimeTicks now) { + download_last_tick_ = UpdateThrottledRecords( + now, &download_, download_last_tick_, download_tick_length_); + upload_last_tick_ = UpdateThrottledRecords( + now, &upload_, upload_last_tick_, upload_tick_length_); + UpdateSuspended(now); +} + +void DevToolsNetworkInterceptor::UpdateSuspended(base::TimeTicks now) { + int64_t activation_baseline = + (now - latency_length_ - base::TimeTicks()).InMicroseconds(); + ThrottleRecords suspended; + for (const ThrottleRecord& record : suspended_) { + if (record.send_end <= activation_baseline) { + if (record.is_upload) + upload_.push_back(record); + else + download_.push_back(record); + } else { + suspended.push_back(record); + } + } + suspended_.swap(suspended); +} + +void DevToolsNetworkInterceptor::CollectFinished( + ThrottleRecords* records, ThrottleRecords* finished) { + ThrottleRecords active; + for (const ThrottleRecord& record : *records) { + if (record.bytes < 0) + finished->push_back(record); + else + active.push_back(record); + } + records->swap(active); +} + +void DevToolsNetworkInterceptor::OnTimer() { + base::TimeTicks now = base::TimeTicks::Now(); + UpdateThrottled(now); + + ThrottleRecords finished; + CollectFinished(&download_, &finished); + CollectFinished(&upload_, &finished); + for (const ThrottleRecord& record : finished) + record.callback.Run(record.result, record.bytes); + + ArmTimer(now); +} + +base::TimeTicks DevToolsNetworkInterceptor::CalculateDesiredTime( + const ThrottleRecords& records, + uint64_t last_tick, + base::TimeDelta tick_length) { + int64_t min_ticks_left = 0x10000L; + size_t count = records.size(); + for (size_t i = 0; i < count; ++i) { + int64_t packets_left = (records[i].bytes + kPacketSize - 1) / kPacketSize; + int64_t ticks_left = (i + 1) + count * (packets_left - 1); + if (i == 0 || ticks_left < min_ticks_left) + min_ticks_left = ticks_left; + } + return offset_ + tick_length * (last_tick + min_ticks_left); +} + +void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) { + size_t suspend_count = suspended_.size(); + if (download_.empty() && upload_.empty() && !suspend_count) { + timer_.Stop(); + return; + } + + base::TimeTicks desired_time = CalculateDesiredTime( + download_, download_last_tick_, download_tick_length_); + if (desired_time == offset_) { + FinishRecords(&download_, false); + } + + base::TimeTicks upload_time = CalculateDesiredTime( + upload_, upload_last_tick_, upload_tick_length_); + if (upload_time != offset_ && upload_time < desired_time) + desired_time = upload_time; + + int64_t min_baseline = std::numeric_limits::max(); + for (size_t i = 0; i < suspend_count; ++i) { + if (suspended_[i].send_end < min_baseline) + min_baseline = suspended_[i].send_end; + } + if (suspend_count) { + base::TimeTicks activation_time = base::TimeTicks() + + base::TimeDelta::FromMicroseconds(min_baseline) + latency_length_; + if (activation_time < desired_time) + desired_time = activation_time; + } + + timer_.Start( + FROM_HERE, (desired_time - now).magnitude(), + base::Bind(&DevToolsNetworkInterceptor::OnTimer, base::Unretained(this))); +} + +int DevToolsNetworkInterceptor::StartThrottle( + int result, + int64_t bytes, + base::TimeTicks send_end, + bool start, + bool is_upload, + const ThrottleCallback& callback) { + if (result < 0) + return result; + + if (conditions_->offline()) + return is_upload ? result : net::ERR_INTERNET_DISCONNECTED; + + if ((is_upload && !conditions_->upload_throughput()) || + (!is_upload && !conditions_->download_throughput())) { + return result; + } + + ThrottleRecord record; + record.result = result; + record.bytes = bytes; + record.callback = callback; + record.is_upload = is_upload; + + base::TimeTicks now = base::TimeTicks::Now(); + UpdateThrottled(now); + if (start && latency_length_ != base::TimeDelta()) { + record.send_end = (send_end - base::TimeTicks()).InMicroseconds(); + suspended_.push_back(record); + UpdateSuspended(now); + } else { + if (is_upload) + upload_.push_back(record); + else + download_.push_back(record); + } + ArmTimer(now); + + return net::ERR_IO_PENDING; +} + +void DevToolsNetworkInterceptor::StopThrottle( + const ThrottleCallback& callback) { + RemoveRecord(&download_, callback); + RemoveRecord(&upload_, callback); + RemoveRecord(&suspended_, callback); +} + +void DevToolsNetworkInterceptor::RemoveRecord( + ThrottleRecords* records, const ThrottleCallback& callback) { + records->erase( + std::remove_if(records->begin(), records->end(), + [&callback](const ThrottleRecord& record){ + return record.callback.Equals(callback); + }), + records->end()); +} + +bool DevToolsNetworkInterceptor::IsOffline() { + return conditions_->offline(); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_interceptor.h b/brightray/browser/net/devtools_network_interceptor.h new file mode 100644 index 000000000000..4a04876b6f17 --- /dev/null +++ b/brightray/browser/net/devtools_network_interceptor.h @@ -0,0 +1,107 @@ +// 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. + +#ifndef BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_ +#define BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "base/timer/timer.h" + +namespace base { +class TimeDelta; +class TimeTicks; +} + +namespace brightray { + +class DevToolsNetworkConditions; +class DevToolsNetworkTransaction; + +class DevToolsNetworkInterceptor { + public: + using ThrottleCallback = base::Callback; + + DevToolsNetworkInterceptor(); + virtual ~DevToolsNetworkInterceptor(); + + base::WeakPtr GetWeakPtr(); + + // Applies network emulation configuration. + void UpdateConditions(std::unique_ptr conditions); + + // Throttles with |is_upload == true| always succeed, even in offline mode. + int StartThrottle(int result, + int64_t bytes, + base::TimeTicks send_end, + bool start, + bool is_upload, + const ThrottleCallback& callback); + void StopThrottle(const ThrottleCallback& callback); + + bool IsOffline(); + + private: + struct ThrottleRecord { + public: + ThrottleRecord(); + ThrottleRecord(const ThrottleRecord& other); + ~ThrottleRecord(); + + int result; + int64_t bytes; + int64_t send_end; + bool is_upload; + ThrottleCallback callback; + }; + + using ThrottleRecords = std::vector; + + void FinishRecords(ThrottleRecords* records, bool offline); + + uint64_t UpdateThrottledRecords(base::TimeTicks now, + ThrottleRecords* records, + uint64_t last_tick, + base::TimeDelta tick_length); + void UpdateThrottled(base::TimeTicks now); + void UpdateSuspended(base::TimeTicks now); + + void CollectFinished(ThrottleRecords* records, ThrottleRecords* finished); + void OnTimer(); + + base::TimeTicks CalculateDesiredTime(const ThrottleRecords& records, + uint64_t last_tick, + base::TimeDelta tick_length); + void ArmTimer(base::TimeTicks now); + + void RemoveRecord(ThrottleRecords* records, const ThrottleCallback& callback); + + std::unique_ptr conditions_; + + // Throttables suspended for a "latency" period. + ThrottleRecords suspended_; + + // Throttables waiting for certain amount of transfer to be "accounted". + ThrottleRecords download_; + ThrottleRecords upload_; + + base::OneShotTimer timer_; + base::TimeTicks offset_; + base::TimeDelta download_tick_length_; + base::TimeDelta upload_tick_length_; + base::TimeDelta latency_length_; + uint64_t download_last_tick_; + uint64_t upload_last_tick_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkInterceptor); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_INTERCEPTOR_H_ diff --git a/brightray/browser/net/devtools_network_protocol_handler.cc b/brightray/browser/net/devtools_network_protocol_handler.cc new file mode 100644 index 000000000000..58988577b432 --- /dev/null +++ b/brightray/browser/net/devtools_network_protocol_handler.cc @@ -0,0 +1,172 @@ +// Copyright (c) 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-CHROMIUM file. + +#include "browser/net/devtools_network_protocol_handler.h" + +#include "browser/browser_context.h" +#include "browser/net/devtools_network_conditions.h" +#include "browser/net/devtools_network_controller.h" + +#include "base/strings/stringprintf.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/devtools_agent_host.h" + + +namespace brightray { + +namespace { + +namespace params { + +const char kDownloadThroughput[] = "downloadThroughput"; +const char kLatency[] = "latency"; +const char kOffline[] = "offline"; +const char kUploadThroughput[] = "uploadThroughput"; +const char kResult[] = "result"; +const char kErrorCode[] = "code"; +const char kErrorMessage[] = "message"; + +} // namespace params + +const char kEmulateNetworkConditions[] = "Network.emulateNetworkConditions"; +const char kCanEmulateNetworkConditions[] = + "Network.canEmulateNetworkConditions"; +const char kId[] = "id"; +const char kMethod[] = "method"; +const char kParams[] = "params"; +const char kError[] = "error"; +// JSON RPC 2.0 spec: http://www.jsonrpc.org/specification#error_object +const int kErrorInvalidParams = -32602; + + +bool ParseCommand(const base::DictionaryValue* command, + int* id, + std::string* method, + const base::DictionaryValue** params) { + if (!command) + return false; + + if (!command->GetInteger(kId, id) || *id < 0) + return false; + + if (!command->GetString(kMethod, method)) + return false; + + if (!command->GetDictionary(kParams, params)) + *params = nullptr; + + return true; +} + +std::unique_ptr +CreateSuccessResponse(int id, std::unique_ptr result) { + std::unique_ptr response(new base::DictionaryValue); + response->SetInteger(kId, id); + response->Set(params::kResult, result.release()); + return response; +} + +std::unique_ptr +CreateFailureResponse(int id, const std::string& param) { + std::unique_ptr response(new base::DictionaryValue); + auto error_object = new base::DictionaryValue; + response->Set(kError, error_object); + error_object->SetInteger(params::kErrorCode, kErrorInvalidParams); + error_object->SetString(params::kErrorMessage, + base::StringPrintf("Missing or Invalid '%s' parameter", param.c_str())); + return response; +} + +} // namespace + +DevToolsNetworkProtocolHandler::DevToolsNetworkProtocolHandler() { +} + +DevToolsNetworkProtocolHandler::~DevToolsNetworkProtocolHandler() { +} + +base::DictionaryValue* DevToolsNetworkProtocolHandler::HandleCommand( + content::DevToolsAgentHost* agent_host, + base::DictionaryValue* command) { + int id = 0; + std::string method; + const base::DictionaryValue* params = nullptr; + + if (!ParseCommand(command, &id, &method, ¶ms)) + return nullptr; + + if (method == kEmulateNetworkConditions) + return EmulateNetworkConditions(agent_host, id, params).release(); + + if (method == kCanEmulateNetworkConditions) + return CanEmulateNetworkConditions(agent_host, id, params).release(); + + return nullptr; +} + +void DevToolsNetworkProtocolHandler::DevToolsAgentStateChanged( + content::DevToolsAgentHost* agent_host, + bool attached) { + std::unique_ptr conditions; + if (attached) + conditions.reset(new DevToolsNetworkConditions(false)); + UpdateNetworkState(agent_host, std::move(conditions)); +} + +std::unique_ptr +DevToolsNetworkProtocolHandler::CanEmulateNetworkConditions( + content::DevToolsAgentHost* agent_host, + int id, + const base::DictionaryValue* params) { + std::unique_ptr result(new base::DictionaryValue); + result->SetBoolean(params::kResult, true); + return CreateSuccessResponse(id, std::move(result)); +} + +std::unique_ptr +DevToolsNetworkProtocolHandler::EmulateNetworkConditions( + content::DevToolsAgentHost* agent_host, + int id, + const base::DictionaryValue* params) { + bool offline = false; + if (!params || !params->GetBoolean(params::kOffline, &offline)) + return CreateFailureResponse(id, params::kOffline); + + double latency = 0.0; + if (!params->GetDouble(params::kLatency, &latency)) + return CreateFailureResponse(id, params::kLatency); + if (latency < 0.0) + latency = 0.0; + + double download_throughput = 0.0; + if (!params->GetDouble(params::kDownloadThroughput, &download_throughput)) + return CreateFailureResponse(id, params::kDownloadThroughput); + if (download_throughput < 0.0) + download_throughput = 0.0; + + double upload_throughput = 0.0; + if (!params->GetDouble(params::kUploadThroughput, &upload_throughput)) + return CreateFailureResponse(id, params::kUploadThroughput); + if (upload_throughput < 0.0) + upload_throughput = 0.0; + + std::unique_ptr conditions( + new DevToolsNetworkConditions(offline, + latency, + download_throughput, + upload_throughput)); + UpdateNetworkState(agent_host, std::move(conditions)); + return std::unique_ptr(); +} + +void DevToolsNetworkProtocolHandler::UpdateNetworkState( + content::DevToolsAgentHost* agent_host, + std::unique_ptr conditions) { + auto browser_context = + static_cast(agent_host->GetBrowserContext()); + browser_context->network_controller_handle()->SetNetworkState( + agent_host->GetId(), std::move(conditions)); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_protocol_handler.h b/brightray/browser/net/devtools_network_protocol_handler.h new file mode 100644 index 000000000000..9ec577814c37 --- /dev/null +++ b/brightray/browser/net/devtools_network_protocol_handler.h @@ -0,0 +1,48 @@ +// 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. + +#ifndef BROWSER_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_ +#define BROWSER_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_ + +#include "base/macros.h" +#include "base/values.h" + +namespace content { +class DevToolsAgentHost; +} + +namespace brightray { + +class DevToolsNetworkConditions; + +class DevToolsNetworkProtocolHandler { + public: + DevToolsNetworkProtocolHandler(); + ~DevToolsNetworkProtocolHandler(); + + base::DictionaryValue* HandleCommand( + content::DevToolsAgentHost* agent_host, + base::DictionaryValue* command); + void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host, + bool attached); + + private: + std::unique_ptr CanEmulateNetworkConditions( + content::DevToolsAgentHost* agent_host, + int command_id, + const base::DictionaryValue* params); + std::unique_ptr EmulateNetworkConditions( + content::DevToolsAgentHost* agent_host, + int command_id, + const base::DictionaryValue* params); + void UpdateNetworkState( + content::DevToolsAgentHost* agent_host, + std::unique_ptr conditions); + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkProtocolHandler); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_ diff --git a/brightray/browser/net/devtools_network_transaction.cc b/brightray/browser/net/devtools_network_transaction.cc new file mode 100644 index 000000000000..ced70acc5e4a --- /dev/null +++ b/brightray/browser/net/devtools_network_transaction.cc @@ -0,0 +1,299 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_transaction.h" + +#include "browser/net/devtools_network_controller.h" +#include "browser/net/devtools_network_upload_data_stream.h" +#include "net/base/load_timing_info.h" +#include "net/base/net_errors.h" +#include "net/base/upload_progress.h" +#include "net/http/http_network_transaction.h" +#include "net/http/http_request_info.h" +#include "net/socket/connection_attempts.h" + +namespace brightray { + +// static +const char + DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId[] = + "X-DevTools-Emulate-Network-Conditions-Client-Id"; + +DevToolsNetworkTransaction::DevToolsNetworkTransaction( + DevToolsNetworkController* controller, + std::unique_ptr transaction) + : throttled_byte_count_(0), + controller_(controller), + transaction_(std::move(transaction)), + request_(nullptr), + failed_(false) { + DCHECK(controller); +} + +DevToolsNetworkTransaction::~DevToolsNetworkTransaction() { + if (interceptor_ && !throttle_callback_.is_null()) + interceptor_->StopThrottle(throttle_callback_); +} + +void DevToolsNetworkTransaction::IOCallback( + const net::CompletionCallback& callback, bool start, int result) { + result = Throttle(callback, start, result); + if (result != net::ERR_IO_PENDING) + callback.Run(result); +} + +int DevToolsNetworkTransaction::Throttle( + const net::CompletionCallback& callback, bool start, int result) { + if (failed_) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_ || result < 0) + return result; + + base::TimeTicks send_end; + if (start) { + throttled_byte_count_ += transaction_->GetTotalReceivedBytes(); + net::LoadTimingInfo load_timing_info; + if (GetLoadTimingInfo(&load_timing_info)) + send_end = load_timing_info.send_end; + if (send_end.is_null()) + send_end = base::TimeTicks::Now(); + } + if (result > 0) + throttled_byte_count_ += result; + + throttle_callback_ = base::Bind(&DevToolsNetworkTransaction::ThrottleCallback, + base::Unretained(this), + callback); + int rv = interceptor_->StartThrottle(result, throttled_byte_count_, send_end, + start, false, throttle_callback_); + if (rv != net::ERR_IO_PENDING) + throttle_callback_.Reset(); + if (rv == net::ERR_INTERNET_DISCONNECTED) + Fail(); + return rv; +} + +void DevToolsNetworkTransaction::ThrottleCallback( + const net::CompletionCallback& callback, int result, int64_t bytes) { + DCHECK(!throttle_callback_.is_null()); + throttle_callback_.Reset(); + if (result == net::ERR_INTERNET_DISCONNECTED) + Fail(); + throttled_byte_count_ = bytes; + callback.Run(result); +} + +void DevToolsNetworkTransaction::Fail() { + DCHECK(request_); + DCHECK(!failed_); + failed_ = true; + transaction_->SetBeforeNetworkStartCallback(BeforeNetworkStartCallback()); + if (interceptor_) + interceptor_.reset(); +} + +bool DevToolsNetworkTransaction::CheckFailed() { + if (failed_) + return true; + if (interceptor_ && interceptor_->IsOffline()) { + Fail(); + return true; + } + return false; +} + +int DevToolsNetworkTransaction::Start(const net::HttpRequestInfo* request, + const net::CompletionCallback& callback, + const net::NetLogWithSource& net_log) { + DCHECK(request); + request_ = request; + + std::string client_id; + bool has_devtools_client_id = request_->extra_headers.HasHeader( + kDevToolsEmulateNetworkConditionsClientId); + if (has_devtools_client_id) { + custom_request_.reset(new net::HttpRequestInfo(*request_)); + custom_request_->extra_headers.GetHeader( + kDevToolsEmulateNetworkConditionsClientId, &client_id); + custom_request_->extra_headers.RemoveHeader( + kDevToolsEmulateNetworkConditionsClientId); + + if (request_->upload_data_stream) { + custom_upload_data_stream_.reset( + new DevToolsNetworkUploadDataStream(request_->upload_data_stream)); + custom_request_->upload_data_stream = custom_upload_data_stream_.get(); + } + + request_ = custom_request_.get(); + } + + DevToolsNetworkInterceptor* interceptor = + controller_->GetInterceptor(client_id); + if (interceptor) { + interceptor_ = interceptor->GetWeakPtr(); + if (custom_upload_data_stream_) + custom_upload_data_stream_->SetInterceptor(interceptor); + } + + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + + if (!interceptor_) + return transaction_->Start(request_, callback, net_log); + + int result = transaction_->Start(request_, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true), + net_log); + return Throttle(callback, true, result); +} + +int DevToolsNetworkTransaction::RestartIgnoringLastError( + const net::CompletionCallback& callback) { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_) + return transaction_->RestartIgnoringLastError(callback); + + int result = transaction_->RestartIgnoringLastError( + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); +} + +int DevToolsNetworkTransaction::RestartWithCertificate( + net::X509Certificate* client_cert, + net::SSLPrivateKey* client_private_key, + const net::CompletionCallback& callback) { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_) { + return transaction_->RestartWithCertificate( + client_cert, client_private_key, callback); + } + + int result = transaction_->RestartWithCertificate( + client_cert, client_private_key, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); +} + +int DevToolsNetworkTransaction::RestartWithAuth( + const net::AuthCredentials& credentials, + const net::CompletionCallback& callback) { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_) + return transaction_->RestartWithAuth(credentials, callback); + + int result = transaction_->RestartWithAuth(credentials, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, true)); + return Throttle(callback, true, result); +} + +bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() { + return transaction_->IsReadyToRestartForAuth(); +} + +int DevToolsNetworkTransaction::Read( + net::IOBuffer* buf, + int buf_len, + const net::CompletionCallback& callback) { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + if (!interceptor_) + return transaction_->Read(buf, buf_len, callback); + + int result = transaction_->Read(buf, buf_len, + base::Bind(&DevToolsNetworkTransaction::IOCallback, + base::Unretained(this), callback, false)); + // URLRequestJob relies on synchronous end-of-stream notification. + if (result == 0) + return result; + return Throttle(callback, false, result); +} + +void DevToolsNetworkTransaction::StopCaching() { + transaction_->StopCaching(); +} + +bool DevToolsNetworkTransaction::GetFullRequestHeaders( + net::HttpRequestHeaders* headers) const { + return transaction_->GetFullRequestHeaders(headers); +} + +int64_t DevToolsNetworkTransaction::GetTotalReceivedBytes() const { + return transaction_->GetTotalReceivedBytes(); +} + +int64_t DevToolsNetworkTransaction::GetTotalSentBytes() const { + return transaction_->GetTotalSentBytes(); +} + +void DevToolsNetworkTransaction::DoneReading() { + transaction_->DoneReading(); +} + +const net::HttpResponseInfo* +DevToolsNetworkTransaction::GetResponseInfo() const { + return transaction_->GetResponseInfo(); +} + +net::LoadState DevToolsNetworkTransaction::GetLoadState() const { + return transaction_->GetLoadState(); +} + +void DevToolsNetworkTransaction::SetQuicServerInfo( + net::QuicServerInfo* info) { + transaction_->SetQuicServerInfo(info); +} + +bool DevToolsNetworkTransaction::GetLoadTimingInfo( + net::LoadTimingInfo* info) const { + return transaction_->GetLoadTimingInfo(info); +} + +bool DevToolsNetworkTransaction::GetRemoteEndpoint( + net::IPEndPoint* endpoint) const { + return transaction_->GetRemoteEndpoint(endpoint); +} + +void DevToolsNetworkTransaction::PopulateNetErrorDetails( + net::NetErrorDetails* details) const { + return transaction_->PopulateNetErrorDetails(details); +} + +void DevToolsNetworkTransaction::SetPriority(net::RequestPriority priority) { + transaction_->SetPriority(priority); +} + +void DevToolsNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper( + net::WebSocketHandshakeStreamBase::CreateHelper* helper) { + transaction_->SetWebSocketHandshakeStreamCreateHelper(helper); +} + +void DevToolsNetworkTransaction::SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) { + transaction_->SetBeforeNetworkStartCallback(callback); +} + +void DevToolsNetworkTransaction::SetBeforeHeadersSentCallback( + const BeforeHeadersSentCallback& callback) { + transaction_->SetBeforeHeadersSentCallback(callback); +} + +int DevToolsNetworkTransaction::ResumeNetworkStart() { + if (CheckFailed()) + return net::ERR_INTERNET_DISCONNECTED; + return transaction_->ResumeNetworkStart(); +} + +void DevToolsNetworkTransaction::GetConnectionAttempts( + net::ConnectionAttempts* out) const { + transaction_->GetConnectionAttempts(out); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_transaction.h b/brightray/browser/net/devtools_network_transaction.h new file mode 100644 index 000000000000..7f01d73cad1e --- /dev/null +++ b/brightray/browser/net/devtools_network_transaction.h @@ -0,0 +1,108 @@ +// 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. + +#ifndef BROWSER_DEVTOOLS_NETWORK_TRANSACTION_H_ +#define BROWSER_DEVTOOLS_NETWORK_TRANSACTION_H_ + +#include + +#include "base/memory/weak_ptr.h" +#include "browser/net/devtools_network_interceptor.h" +#include "net/base/completion_callback.h" +#include "net/base/load_states.h" +#include "net/base/request_priority.h" +#include "net/http/http_transaction.h" +#include "net/websockets/websocket_handshake_stream_base.h" + +namespace brightray { + +class DevToolsNetworkController; +class DevToolsNetworkUploadDataStream; + +class DevToolsNetworkTransaction : public net::HttpTransaction { + public: + static const char kDevToolsEmulateNetworkConditionsClientId[]; + + DevToolsNetworkTransaction( + DevToolsNetworkController* controller, + std::unique_ptr network_transaction); + ~DevToolsNetworkTransaction() override; + + // HttpTransaction methods: + int Start(const net::HttpRequestInfo* request, + const net::CompletionCallback& callback, + const net::NetLogWithSource& net_log) override; + int RestartIgnoringLastError( + const net::CompletionCallback& callback) override; + int RestartWithCertificate(net::X509Certificate* client_cert, + net::SSLPrivateKey* client_private_key, + const net::CompletionCallback& callback) override; + int RestartWithAuth(const net::AuthCredentials& credentials, + const net::CompletionCallback& callback) override; + bool IsReadyToRestartForAuth() override; + + int Read(net::IOBuffer* buf, + int buf_len, + const net::CompletionCallback& callback) override; + void StopCaching() override; + bool GetFullRequestHeaders(net::HttpRequestHeaders* headers) const override; + int64_t GetTotalReceivedBytes() const override; + int64_t GetTotalSentBytes() const override; + void DoneReading() override; + const net::HttpResponseInfo* GetResponseInfo() const override; + net::LoadState GetLoadState() const override; + void SetQuicServerInfo(net::QuicServerInfo* quic_server_info) override; + bool GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override; + bool GetRemoteEndpoint(net::IPEndPoint* endpoint) const override; + void PopulateNetErrorDetails(net::NetErrorDetails* details) const override; + void SetPriority(net::RequestPriority priority) override; + void SetWebSocketHandshakeStreamCreateHelper( + net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) override; + void SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) override; + void SetBeforeHeadersSentCallback( + const BeforeHeadersSentCallback& callback) override; + int ResumeNetworkStart() override; + void GetConnectionAttempts(net::ConnectionAttempts* out) const override; + + private: + void Fail(); + bool CheckFailed(); + + void IOCallback(const net::CompletionCallback& callback, + bool start, + int result); + int Throttle(const net::CompletionCallback& callback, + bool start, + int result); + void ThrottleCallback(const net::CompletionCallback& callback, + int result, + int64_t bytes); + + DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_; + int64_t throttled_byte_count_; + + DevToolsNetworkController* controller_; + base::WeakPtr interceptor_; + + // Modified upload data stream. Should be destructed after |custom_request_|. + std::unique_ptr custom_upload_data_stream_; + + // Modified request. Should be destructed after |transaction_|. + std::unique_ptr custom_request_; + + // Original network transaction. + std::unique_ptr transaction_; + + const net::HttpRequestInfo* request_; + + // True if Fail was already invoked. + bool failed_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkTransaction); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_TRANSACTION_H_ diff --git a/brightray/browser/net/devtools_network_transaction_factory.cc b/brightray/browser/net/devtools_network_transaction_factory.cc new file mode 100644 index 000000000000..7373c14ddeff --- /dev/null +++ b/brightray/browser/net/devtools_network_transaction_factory.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/net/devtools_network_transaction_factory.h" + +#include "browser/net/devtools_network_controller.h" +#include "browser/net/devtools_network_transaction.h" + +#include "content/public/browser/service_worker_context.h" +#include "net/base/net_errors.h" +#include "net/http/http_network_layer.h" +#include "net/http/http_network_transaction.h" + +namespace brightray { + +DevToolsNetworkTransactionFactory::DevToolsNetworkTransactionFactory( + DevToolsNetworkController* controller, + net::HttpNetworkSession* session) + : controller_(controller), + network_layer_(new net::HttpNetworkLayer(session)) { + std::set headers; + headers.insert( + DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId); + content::ServiceWorkerContext::AddExcludedHeadersForFetchEvent(headers); +} + +DevToolsNetworkTransactionFactory::~DevToolsNetworkTransactionFactory() { +} + +int DevToolsNetworkTransactionFactory::CreateTransaction( + net::RequestPriority priority, + std::unique_ptr* transaction) { + std::unique_ptr new_transaction; + int rv = network_layer_->CreateTransaction(priority, &new_transaction); + if (rv != net::OK) + return rv; + transaction->reset( + new DevToolsNetworkTransaction(controller_, std::move(new_transaction))); + return net::OK; +} + +net::HttpCache* DevToolsNetworkTransactionFactory::GetCache() { + return network_layer_->GetCache(); +} + +net::HttpNetworkSession* DevToolsNetworkTransactionFactory::GetSession() { + return network_layer_->GetSession(); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_transaction_factory.h b/brightray/browser/net/devtools_network_transaction_factory.h new file mode 100644 index 000000000000..49286a04742d --- /dev/null +++ b/brightray/browser/net/devtools_network_transaction_factory.h @@ -0,0 +1,39 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_ +#define BROWSER_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_ + +#include "base/macros.h" +#include "net/base/request_priority.h" +#include "net/http/http_transaction_factory.h" + +namespace brightray { + +class DevToolsNetworkController; + +class DevToolsNetworkTransactionFactory : public net::HttpTransactionFactory { + public: + explicit DevToolsNetworkTransactionFactory( + DevToolsNetworkController* controller, + net::HttpNetworkSession* session); + ~DevToolsNetworkTransactionFactory() override; + + // net::HttpTransactionFactory: + int CreateTransaction( + net::RequestPriority priority, + std::unique_ptr* transaction) override; + net::HttpCache* GetCache() override; + net::HttpNetworkSession* GetSession() override; + + private: + DevToolsNetworkController* controller_; + std::unique_ptr network_layer_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkTransactionFactory); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_ diff --git a/brightray/browser/net/devtools_network_upload_data_stream.cc b/brightray/browser/net/devtools_network_upload_data_stream.cc new file mode 100644 index 000000000000..6687038b097f --- /dev/null +++ b/brightray/browser/net/devtools_network_upload_data_stream.cc @@ -0,0 +1,96 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/net/devtools_network_upload_data_stream.h" + +#include "net/base/net_errors.h" + +namespace brightray { + +DevToolsNetworkUploadDataStream::DevToolsNetworkUploadDataStream( + net::UploadDataStream* upload_data_stream) + : net::UploadDataStream(upload_data_stream->is_chunked(), + upload_data_stream->identifier()), + throttle_callback_( + base::Bind(&DevToolsNetworkUploadDataStream::ThrottleCallback, + base::Unretained(this))), + throttled_byte_count_(0), + upload_data_stream_(upload_data_stream) { +} + +DevToolsNetworkUploadDataStream::~DevToolsNetworkUploadDataStream() { + if (interceptor_) + interceptor_->StopThrottle(throttle_callback_); +} + +void DevToolsNetworkUploadDataStream::SetInterceptor( + DevToolsNetworkInterceptor* interceptor) { + DCHECK(!interceptor_); + if (interceptor) + interceptor_ = interceptor->GetWeakPtr(); +} + +bool DevToolsNetworkUploadDataStream::IsInMemory() const { + return false; +} + +int DevToolsNetworkUploadDataStream::InitInternal( + const net::NetLogWithSource& net_log) { + throttled_byte_count_ = 0; + int result = upload_data_stream_->Init( + base::Bind(&DevToolsNetworkUploadDataStream::StreamInitCallback, + base::Unretained(this)), + net_log); + if (result == net::OK && !is_chunked()) + SetSize(upload_data_stream_->size()); + return result; +} + +void DevToolsNetworkUploadDataStream::StreamInitCallback(int result) { + if (!is_chunked()) + SetSize(upload_data_stream_->size()); + OnInitCompleted(result); +} + +int DevToolsNetworkUploadDataStream::ReadInternal( + net::IOBuffer* buf, int buf_len) { + int result = upload_data_stream_->Read(buf, buf_len, + base::Bind(&DevToolsNetworkUploadDataStream::StreamReadCallback, + base::Unretained(this))); + return ThrottleRead(result); +} + +void DevToolsNetworkUploadDataStream::StreamReadCallback(int result) { + result = ThrottleRead(result); + if (result != net::ERR_IO_PENDING) + OnReadCompleted(result); +} + +int DevToolsNetworkUploadDataStream::ThrottleRead(int result) { + if (is_chunked() && upload_data_stream_->IsEOF()) + SetIsFinalChunk(); + + if (!interceptor_ || result < 0) + return result; + + if (result > 0) + throttled_byte_count_ += result; + return interceptor_->StartThrottle(result, throttled_byte_count_, + base::TimeTicks(), false, true, throttle_callback_); +} + +void DevToolsNetworkUploadDataStream::ThrottleCallback( + int result, int64_t bytes) { + throttled_byte_count_ = bytes; + OnReadCompleted(result); +} + +void DevToolsNetworkUploadDataStream::ResetInternal() { + upload_data_stream_->Reset(); + throttled_byte_count_ = 0; + if (interceptor_) + interceptor_->StopThrottle(throttle_callback_); +} + +} // namespace brightray diff --git a/brightray/browser/net/devtools_network_upload_data_stream.h b/brightray/browser/net/devtools_network_upload_data_stream.h new file mode 100644 index 000000000000..c3753e8286f5 --- /dev/null +++ b/brightray/browser/net/devtools_network_upload_data_stream.h @@ -0,0 +1,51 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ +#define BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ + +#include + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "browser/net/devtools_network_interceptor.h" +#include "net/base/completion_callback.h" +#include "net/base/upload_data_stream.h" + +namespace brightray { + +class DevToolsNetworkUploadDataStream : public net::UploadDataStream { + public: + // Supplied |upload_data_stream| must outlive this object. + explicit DevToolsNetworkUploadDataStream( + net::UploadDataStream* upload_data_stream); + ~DevToolsNetworkUploadDataStream() override; + + void SetInterceptor(DevToolsNetworkInterceptor* interceptor); + + private: + // net::UploadDataStream implementation. + bool IsInMemory() const override; + int InitInternal(const net::NetLogWithSource& net_log) override; + int ReadInternal(net::IOBuffer* buf, int buf_len) override; + void ResetInternal() override; + + void StreamInitCallback(int result); + void StreamReadCallback(int result); + + int ThrottleRead(int result); + void ThrottleCallback(int result, int64_t bytes); + + DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_; + int64_t throttled_byte_count_; + + net::UploadDataStream* upload_data_stream_; + base::WeakPtr interceptor_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkUploadDataStream); +}; + +} // namespace brightray + +#endif // BROWSER_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_ diff --git a/brightray/browser/net_log.cc b/brightray/browser/net_log.cc new file mode 100644 index 000000000000..6b41eb738c1b --- /dev/null +++ b/brightray/browser/net_log.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/net_log.h" + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/values.h" +#include "content/public/common/content_switches.h" +#include "net/log/net_log_util.h" + +namespace brightray { + +namespace { + +std::unique_ptr GetConstants() { + std::unique_ptr constants = net::GetNetConstants(); + + // Adding client information to constants dictionary. + auto* client_info = new base::DictionaryValue(); + client_info->SetString( + "command_line", + base::CommandLine::ForCurrentProcess()->GetCommandLineString()); + + constants->Set("clientInfo", client_info); + return constants; +} + +} // namespace + +NetLog::NetLog() { +} + +NetLog::~NetLog() { +} + +void NetLog::StartLogging(net::URLRequestContext* url_request_context) { + auto command_line = base::CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch(switches::kLogNetLog)) + return; + + base::FilePath log_path = + command_line->GetSwitchValuePath(switches::kLogNetLog); +#if defined(OS_WIN) + log_file_.reset(_wfopen(log_path.value().c_str(), L"w")); +#elif defined(OS_POSIX) + log_file_.reset(fopen(log_path.value().c_str(), "w")); +#endif + + if (!log_file_) { + LOG(ERROR) << "Could not open file: " << log_path.value() + << "for net logging"; + return; + } + + std::unique_ptr constants(GetConstants()); + write_to_file_observer_.StartObserving(this, + std::move(log_file_), + constants.get(), + url_request_context); +} + +} // namespace brightray diff --git a/brightray/browser/net_log.h b/brightray/browser/net_log.h new file mode 100644 index 000000000000..e62c335350c7 --- /dev/null +++ b/brightray/browser/net_log.h @@ -0,0 +1,30 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_NET_LOG_H_ +#define BROWSER_NET_LOG_H_ + +#include "base/files/scoped_file.h" +#include "net/log/net_log.h" +#include "net/log/write_to_file_net_log_observer.h" + +namespace brightray { + +class NetLog : public net::NetLog { + public: + NetLog(); + ~NetLog() override; + + void StartLogging(net::URLRequestContext* url_request_context); + + private: + base::ScopedFILE log_file_; + net::WriteToFileNetLogObserver write_to_file_observer_; + + DISALLOW_COPY_AND_ASSIGN(NetLog); +}; + +} // namespace brightray + +#endif // BROWSER_NET_LOG_H_ diff --git a/brightray/browser/network_delegate.cc b/brightray/browser/network_delegate.cc new file mode 100644 index 000000000000..f7be1b028b68 --- /dev/null +++ b/brightray/browser/network_delegate.cc @@ -0,0 +1,148 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "browser/network_delegate.h" + +#include +#include + +#include "base/command_line.h" +#include "base/strings/string_split.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request.h" + +namespace brightray { + +namespace { + +// Ignore the limit of 6 connections per host. +const char kIgnoreConnectionsLimit[] = "ignore-connections-limit"; + +} // namespace + +NetworkDelegate::NetworkDelegate() { + auto command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(kIgnoreConnectionsLimit)) { + std::string value = + command_line->GetSwitchValueASCII(kIgnoreConnectionsLimit); + ignore_connections_limit_domains_ = base::SplitString( + value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + } +} + +NetworkDelegate::~NetworkDelegate() { +} + +int NetworkDelegate::OnBeforeURLRequest( + net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) { + for (const auto& domain : ignore_connections_limit_domains_) { + if (request->url().DomainIs(domain)) { + // Allow unlimited concurrent connections. + request->SetPriority(net::MAXIMUM_PRIORITY); + request->SetLoadFlags(request->load_flags() | net::LOAD_IGNORE_LIMITS); + break; + } + } + + return net::OK; +} + +int NetworkDelegate::OnBeforeStartTransaction( + net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) { + return net::OK; +} + +void NetworkDelegate::OnStartTransaction( + net::URLRequest* request, + const net::HttpRequestHeaders& headers) { +} + +void NetworkDelegate::OnBeforeSendHeaders( + net::URLRequest* request, + const net::ProxyInfo& proxy_info, + const net::ProxyRetryInfoMap& proxy_retry_info, + net::HttpRequestHeaders* headers) { +} + +int NetworkDelegate::OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr* override_response_headers, + GURL* allowed_unsafe_redirect_url) { + return net::OK; +} + +void NetworkDelegate::OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) { +} + +void NetworkDelegate::OnResponseStarted(net::URLRequest* request) { +} + +void NetworkDelegate::OnNetworkBytesReceived(net::URLRequest* request, + int64_t bytes_read) { +} + +void NetworkDelegate::OnNetworkBytesSent(net::URLRequest* request, + int64_t bytes_sent) { +} + +void NetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { +} + +void NetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) { +} + +void NetworkDelegate::OnPACScriptError(int line_number, + const base::string16& error) { +} + +NetworkDelegate::AuthRequiredResponse NetworkDelegate::OnAuthRequired( + net::URLRequest* request, + const net::AuthChallengeInfo& auth_info, + const AuthCallback& callback, + net::AuthCredentials* credentials) { + return AUTH_REQUIRED_RESPONSE_NO_ACTION; +} + +bool NetworkDelegate::OnCanGetCookies(const net::URLRequest& request, + const net::CookieList& cookie_list) { + return true; +} + +bool NetworkDelegate::OnCanSetCookie(const net::URLRequest& request, + const std::string& cookie_line, + net::CookieOptions* options) { + return true; +} + +bool NetworkDelegate::OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const { + return true; +} + +bool NetworkDelegate::OnCanEnablePrivacyMode( + const GURL& url, + const GURL& first_party_for_cookies) const { + return false; +} + +bool NetworkDelegate::OnAreExperimentalCookieFeaturesEnabled() const { + return true; +} + +bool NetworkDelegate::OnCancelURLRequestWithPolicyViolatingReferrerHeader( + const net::URLRequest& request, + const GURL& target_url, + const GURL& referrer_url) const { + return false; +} + +} // namespace brightray diff --git a/brightray/browser/network_delegate.h b/brightray/browser/network_delegate.h new file mode 100644 index 000000000000..b8a8b5225368 --- /dev/null +++ b/brightray/browser/network_delegate.h @@ -0,0 +1,80 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_NETWORK_DELEGATE_H_ +#define BRIGHTRAY_BROWSER_NETWORK_DELEGATE_H_ + +#include +#include + +#include "net/base/network_delegate.h" +#include "net/proxy/proxy_server.h" + +namespace brightray { + +class NetworkDelegate : public net::NetworkDelegate { + public: + NetworkDelegate(); + virtual ~NetworkDelegate(); + + protected: + int OnBeforeURLRequest(net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) override; + int OnBeforeStartTransaction(net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) override; + void OnBeforeSendHeaders(net::URLRequest* request, + const net::ProxyInfo& proxy_info, + const net::ProxyRetryInfoMap& proxy_retry_info, + net::HttpRequestHeaders* headers) override; + void OnStartTransaction(net::URLRequest* request, + const net::HttpRequestHeaders& headers) override; + int OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr* override_response_headers, + GURL* allowed_unsafe_redirect_url) override; + void OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) override; + void OnResponseStarted(net::URLRequest* request) override; + void OnNetworkBytesReceived(net::URLRequest* request, + int64_t bytes_read) override; + void OnNetworkBytesSent(net::URLRequest* request, + int64_t bytes_sent) override; + void OnCompleted(net::URLRequest* request, bool started) override; + void OnURLRequestDestroyed(net::URLRequest* request) override; + void OnPACScriptError(int line_number, + const base::string16& error) override; + AuthRequiredResponse OnAuthRequired( + net::URLRequest* request, + const net::AuthChallengeInfo& auth_info, + const AuthCallback& callback, + net::AuthCredentials* credentials) override; + bool OnCanGetCookies(const net::URLRequest& request, + const net::CookieList& cookie_list) override; + bool OnCanSetCookie(const net::URLRequest& request, + const std::string& cookie_line, + net::CookieOptions* options) override; + bool OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const override; + bool OnCanEnablePrivacyMode( + const GURL& url, + const GURL& first_party_for_cookies) const override; + bool OnAreExperimentalCookieFeaturesEnabled() const override; + bool OnCancelURLRequestWithPolicyViolatingReferrerHeader( + const net::URLRequest& request, + const GURL& target_url, + const GURL& referrer_url) const override; + + private: + std::vector ignore_connections_limit_domains_; + + DISALLOW_COPY_AND_ASSIGN(NetworkDelegate); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/notification.cc b/brightray/browser/notification.cc new file mode 100644 index 000000000000..ba9df5446f30 --- /dev/null +++ b/brightray/browser/notification.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/notification.h" + +#include "browser/notification_delegate.h" +#include "browser/notification_presenter.h" + +namespace brightray { + +Notification::Notification(NotificationDelegate* delegate, + NotificationPresenter* presenter) + : delegate_(delegate), + presenter_(presenter), + weak_factory_(this) { +} + +Notification::~Notification() { + delegate()->NotificationDestroyed(); +} + +void Notification::NotificationClicked() { + delegate()->NotificationClick(); + Destroy(); +} + +void Notification::NotificationDismissed() { + delegate()->NotificationClosed(); + Destroy(); +} + +void Notification::NotificationFailed() { + delegate()->NotificationFailed(); + Destroy(); +} + +void Notification::Destroy() { + presenter()->RemoveNotification(this); +} + +} // namespace brightray diff --git a/brightray/browser/notification.h b/brightray/browser/notification.h new file mode 100644 index 000000000000..a330bc25d1b8 --- /dev/null +++ b/brightray/browser/notification.h @@ -0,0 +1,65 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_NOTIFICATION_H_ +#define BROWSER_NOTIFICATION_H_ + +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" + +class GURL; +class SkBitmap; + +namespace brightray { + +class NotificationDelegate; +class NotificationPresenter; + +class Notification { + public: + // Shows the notification. + virtual void Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) = 0; + // Closes the notification, this instance will be destroyed after the + // notification gets closed. + virtual void Dismiss() = 0; + + // Should be called by derived classes. + void NotificationClicked(); + void NotificationDismissed(); + void NotificationFailed(); + + // delete this. + void Destroy(); + + base::WeakPtr GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + NotificationDelegate* delegate() const { return delegate_; } + NotificationPresenter* presenter() const { return presenter_; } + + protected: + Notification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + + public: + virtual ~Notification(); + + private: + NotificationDelegate* delegate_; + NotificationPresenter* presenter_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Notification); +}; + +} // namespace brightray + +#endif // BROWSER_NOTIFICATION_H_ diff --git a/brightray/browser/notification_delegate.h b/brightray/browser/notification_delegate.h new file mode 100644 index 000000000000..93512f71753d --- /dev/null +++ b/brightray/browser/notification_delegate.h @@ -0,0 +1,23 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_NOTIFICATION_DELEGATE_H_ +#define BROWSER_NOTIFICATION_DELEGATE_H_ + +#include "content/public/browser/desktop_notification_delegate.h" + +namespace brightray { + +class NotificationDelegate : public content::DesktopNotificationDelegate { + public: + // The native Notification object is destroyed. + virtual void NotificationDestroyed() {} + + // Failed to send the notification. + virtual void NotificationFailed() {} +}; + +} // namespace brightray + +#endif // BROWSER_NOTIFICATION_DELEGATE_H_ diff --git a/brightray/browser/notification_delegate_adapter.cc b/brightray/browser/notification_delegate_adapter.cc new file mode 100644 index 000000000000..c0ad3d03f066 --- /dev/null +++ b/brightray/browser/notification_delegate_adapter.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/notification_delegate_adapter.h" + +namespace brightray { + +NotificationDelegateAdapter::NotificationDelegateAdapter( + std::unique_ptr delegate) + : delegate_(std::move(delegate)) { +} + +NotificationDelegateAdapter::~NotificationDelegateAdapter() { +} + +void NotificationDelegateAdapter::NotificationDestroyed() { + delete this; +} + +void NotificationDelegateAdapter::NotificationDisplayed() { + delegate_->NotificationDisplayed(); +} + +void NotificationDelegateAdapter::NotificationClosed() { + delegate_->NotificationClosed(); +} + +void NotificationDelegateAdapter::NotificationClick() { + delegate_->NotificationClick(); +} + +} // namespace brightray diff --git a/brightray/browser/notification_delegate_adapter.h b/brightray/browser/notification_delegate_adapter.h new file mode 100644 index 000000000000..01f3284e2b85 --- /dev/null +++ b/brightray/browser/notification_delegate_adapter.h @@ -0,0 +1,38 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_NOTIFICATION_DELEGATE_ADAPTER_H_ +#define BROWSER_NOTIFICATION_DELEGATE_ADAPTER_H_ + +#include + +#include "base/macros.h" +#include "browser/notification_delegate.h" + +namespace brightray { + +// Adapt the content::DesktopNotificationDelegate to NotificationDelegate. +class NotificationDelegateAdapter : public NotificationDelegate { + public: + explicit NotificationDelegateAdapter( + std::unique_ptr delegate); + ~NotificationDelegateAdapter() override; + + // NotificationDelegate: + void NotificationDestroyed() override; + + // content::DesktopNotificationDelegate: + void NotificationDisplayed() override; + void NotificationClosed() override; + void NotificationClick() override; + + private: + std::unique_ptr delegate_; + + DISALLOW_COPY_AND_ASSIGN(NotificationDelegateAdapter); +}; + +} // namespace brightray + +#endif // BROWSER_NOTIFICATION_DELEGATE_ADAPTER_H_ diff --git a/brightray/browser/notification_presenter.cc b/brightray/browser/notification_presenter.cc new file mode 100644 index 000000000000..30ef3b16b4b2 --- /dev/null +++ b/brightray/browser/notification_presenter.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/notification_presenter.h" + +#include "browser/notification.h" + +namespace brightray { + +NotificationPresenter::NotificationPresenter() { +} + +NotificationPresenter::~NotificationPresenter() { + for (Notification* notification : notifications_) + delete notification; +} + +base::WeakPtr NotificationPresenter::CreateNotification( + NotificationDelegate* delegate) { + Notification* notification = CreateNotificationObject(delegate); + notifications_.insert(notification); + return notification->GetWeakPtr(); +} + +void NotificationPresenter::RemoveNotification(Notification* notification) { + notifications_.erase(notification); + delete notification; +} + +} // namespace brightray diff --git a/brightray/browser/notification_presenter.h b/brightray/browser/notification_presenter.h new file mode 100644 index 000000000000..721ba92ace4a --- /dev/null +++ b/brightray/browser/notification_presenter.h @@ -0,0 +1,45 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_H_ +#define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_H_ + +#include + +#include "base/memory/weak_ptr.h" + +namespace brightray { + +class Notification; +class NotificationDelegate; + +class NotificationPresenter { + public: + static NotificationPresenter* Create(); + + virtual ~NotificationPresenter(); + + base::WeakPtr CreateNotification( + NotificationDelegate* delegate); + + std::set notifications() const { return notifications_; } + + protected: + NotificationPresenter(); + virtual Notification* CreateNotificationObject( + NotificationDelegate* delegate) = 0; + + private: + friend class Notification; + + void RemoveNotification(Notification* notification); + + std::set notifications_; + + DISALLOW_COPY_AND_ASSIGN(NotificationPresenter); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/permission_manager.cc b/brightray/browser/permission_manager.cc new file mode 100644 index 000000000000..198e35d8c38c --- /dev/null +++ b/brightray/browser/permission_manager.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/permission_manager.h" + +#include "base/callback.h" +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/permission_type.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" + +namespace brightray { + +PermissionManager::PermissionManager() { +} + +PermissionManager::~PermissionManager() { +} + +int PermissionManager::RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback& callback) { + if (permission == content::PermissionType::MIDI_SYSEX) { + content::ChildProcessSecurityPolicy::GetInstance()-> + GrantSendMidiSysExMessage(render_frame_host->GetProcess()->GetID()); + } + callback.Run(blink::mojom::PermissionStatus::GRANTED); + return kNoPendingOperation; +} + +int PermissionManager::RequestPermissions( + const std::vector& permissions, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback&)>& callback) { + std::vector permissionStatuses; + + for (auto permission : permissions) { + if (permission == content::PermissionType::MIDI_SYSEX) { + content::ChildProcessSecurityPolicy::GetInstance()-> + GrantSendMidiSysExMessage(render_frame_host->GetProcess()->GetID()); + } + + permissionStatuses.push_back(blink::mojom::PermissionStatus::GRANTED); + } + + callback.Run(permissionStatuses); + return kNoPendingOperation; +} + +void PermissionManager::CancelPermissionRequest(int request_id) { +} + +void PermissionManager::ResetPermission( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { +} + +blink::mojom::PermissionStatus PermissionManager::GetPermissionStatus( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) { + return blink::mojom::PermissionStatus::GRANTED; +} + +int PermissionManager::SubscribePermissionStatusChange( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin, + const base::Callback& callback) { + return -1; +} + +void PermissionManager::UnsubscribePermissionStatusChange(int subscription_id) { +} + +} // namespace brightray diff --git a/brightray/browser/permission_manager.h b/brightray/browser/permission_manager.h new file mode 100644 index 000000000000..734529f06eb4 --- /dev/null +++ b/brightray/browser/permission_manager.h @@ -0,0 +1,57 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_PERMISSION_MANAGER_H_ +#define BROWSER_PERMISSION_MANAGER_H_ + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "content/public/browser/permission_manager.h" + +namespace brightray { + +class PermissionManager : public content::PermissionManager { + public: + PermissionManager(); + ~PermissionManager() override; + + // content::PermissionManager: + int RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback& callback) + override; + int RequestPermissions( + const std::vector& permissions, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback< + void(const std::vector&)>& callback) + override; + void CancelPermissionRequest(int request_id) override; + void ResetPermission(content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + blink::mojom::PermissionStatus GetPermissionStatus( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override; + int SubscribePermissionStatusChange( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin, + const base::Callback& callback) + override; + void UnsubscribePermissionStatusChange(int subscription_id) override; + + private: + DISALLOW_COPY_AND_ASSIGN(PermissionManager); +}; + +} // namespace brightray + +#endif // BROWSER_PERMISSION_MANAGER_H_ diff --git a/brightray/browser/platform_notification_service.cc b/brightray/browser/platform_notification_service.cc new file mode 100644 index 000000000000..36c515153537 --- /dev/null +++ b/brightray/browser/platform_notification_service.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/platform_notification_service.h" + +#include "browser/browser_client.h" +#include "browser/notification.h" +#include "browser/notification_delegate_adapter.h" +#include "browser/notification_presenter.h" +#include "content/public/common/platform_notification_data.h" +#include "content/public/common/notification_resources.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace brightray { + +namespace { + +void RemoveNotification(base::WeakPtr notification) { + if (notification) + notification->Dismiss(); +} + +void OnWebNotificationAllowed(base::WeakPtr notification, + const SkBitmap& icon, + const content::PlatformNotificationData& data, + bool audio_muted, + bool allowed) { + if (!notification) + return; + if (allowed) + notification->Show(data.title, data.body, data.tag, data.icon, icon, + audio_muted ? true : data.silent); + else + notification->Destroy(); +} + +} // namespace + +PlatformNotificationService::PlatformNotificationService( + BrowserClient* browser_client) + : browser_client_(browser_client), + render_process_id_(-1) { +} + +PlatformNotificationService::~PlatformNotificationService() {} + +blink::mojom::PermissionStatus +PlatformNotificationService::CheckPermissionOnUIThread( + content::BrowserContext* browser_context, + const GURL& origin, + int render_process_id) { + render_process_id_ = render_process_id; + return blink::mojom::PermissionStatus::GRANTED; +} + +blink::mojom::PermissionStatus +PlatformNotificationService::CheckPermissionOnIOThread( + content::ResourceContext* resource_context, + const GURL& origin, + int render_process_id) { + return blink::mojom::PermissionStatus::GRANTED; +} + +void PlatformNotificationService::DisplayNotification( + content::BrowserContext* browser_context, + const std::string& notification_id, + const GURL& origin, + const content::PlatformNotificationData& notification_data, + const content::NotificationResources& notification_resources, + std::unique_ptr delegate, + base::Closure* cancel_callback) { + auto presenter = browser_client_->GetNotificationPresenter(); + if (!presenter) + return; + std::unique_ptr adapter( + new NotificationDelegateAdapter(std::move(delegate))); + auto notification = presenter->CreateNotification(adapter.get()); + if (notification) { + ignore_result(adapter.release()); // it will release itself automatically. + *cancel_callback = base::Bind(&RemoveNotification, notification); + browser_client_->WebNotificationAllowed( + render_process_id_, base::Bind(&OnWebNotificationAllowed, notification, + notification_resources.notification_icon, + notification_data)); + } +} + +void PlatformNotificationService::DisplayPersistentNotification( + content::BrowserContext* browser_context, + const std::string& notification_id, + const GURL& service_worker_scope, + const GURL& origin, + const content::PlatformNotificationData& notification_data, + const content::NotificationResources& notification_resources) { +} + +void PlatformNotificationService::ClosePersistentNotification( + content::BrowserContext* browser_context, + const std::string& notification_id) { +} + +bool PlatformNotificationService::GetDisplayedNotifications( + content::BrowserContext* browser_context, + std::set* displayed_notifications) { + return false; +} + +} // namespace brightray diff --git a/brightray/browser/platform_notification_service.h b/brightray/browser/platform_notification_service.h new file mode 100644 index 000000000000..f9b09c64bdff --- /dev/null +++ b/brightray/browser/platform_notification_service.h @@ -0,0 +1,61 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BROWSER_PLATFORM_NOTIFICATION_SERVICE_H_ +#define BROWSER_PLATFORM_NOTIFICATION_SERVICE_H_ + +#include "content/public/browser/browser_context.h" +#include "content/public/browser/platform_notification_service.h" + +namespace brightray { + +class BrowserClient; + +class PlatformNotificationService + : public content::PlatformNotificationService { + public: + explicit PlatformNotificationService(BrowserClient* browser_client); + ~PlatformNotificationService() override; + + protected: + // content::PlatformNotificationService: + blink::mojom::PermissionStatus CheckPermissionOnUIThread( + content::BrowserContext* browser_context, + const GURL& origin, + int render_process_id) override; + blink::mojom::PermissionStatus CheckPermissionOnIOThread( + content::ResourceContext* resource_context, + const GURL& origin, + int render_process_id) override; + void DisplayNotification( + content::BrowserContext* browser_context, + const std::string& notification_id, + const GURL& origin, + const content::PlatformNotificationData& notification_data, + const content::NotificationResources& notification_resources, + std::unique_ptr delegate, + base::Closure* cancel_callback) override; + void DisplayPersistentNotification( + content::BrowserContext* browser_context, + const std::string& notification_id, + const GURL& service_worker_scope, + const GURL& origin, + const content::PlatformNotificationData& notification_data, + const content::NotificationResources& notification_resources) override; + void ClosePersistentNotification(content::BrowserContext* browser_context, + const std::string& notification_id) override; + bool GetDisplayedNotifications( + content::BrowserContext* browser_context, + std::set* displayed_notifications) override; + + private: + BrowserClient* browser_client_; + int render_process_id_; + + DISALLOW_COPY_AND_ASSIGN(PlatformNotificationService); +}; + +} // namespace brightray + +#endif // BROWSER_PLATFORM_NOTIFICATION_SERVICE_H_ diff --git a/brightray/browser/special_storage_policy.cc b/brightray/browser/special_storage_policy.cc new file mode 100644 index 000000000000..2010dbd1a041 --- /dev/null +++ b/brightray/browser/special_storage_policy.cc @@ -0,0 +1,39 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/special_storage_policy.h" + +namespace brightray { + +SpecialStoragePolicy::SpecialStoragePolicy() { +} + +SpecialStoragePolicy::~SpecialStoragePolicy() { +} + +bool SpecialStoragePolicy::IsStorageProtected(const GURL& origin) { + return true; +} + +bool SpecialStoragePolicy::IsStorageUnlimited(const GURL& origin) { + return true; +} + +bool SpecialStoragePolicy::IsStorageDurable(const GURL& origin) { + return true; +} + +bool SpecialStoragePolicy::HasIsolatedStorage(const GURL& origin) { + return false; +} + +bool SpecialStoragePolicy::IsStorageSessionOnly(const GURL& origin) { + return false; +} + +bool SpecialStoragePolicy::HasSessionOnlyOrigins() { + return false; +} + +} // namespace brightray diff --git a/brightray/browser/special_storage_policy.h b/brightray/browser/special_storage_policy.h new file mode 100644 index 000000000000..0c89db2d4c51 --- /dev/null +++ b/brightray/browser/special_storage_policy.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef BRIGHTRAY_BROWSER_SPECIAL_STORAGE_POLICY_H_ +#define BRIGHTRAY_BROWSER_SPECIAL_STORAGE_POLICY_H_ + +#include "storage/browser/quota/special_storage_policy.h" + +namespace brightray { + +class SpecialStoragePolicy : public storage::SpecialStoragePolicy { + public: + SpecialStoragePolicy(); + + // storage::SpecialStoragePolicy implementation. + bool IsStorageProtected(const GURL& origin) override; + bool IsStorageUnlimited(const GURL& origin) override; + bool IsStorageDurable(const GURL& origin) override; + bool HasIsolatedStorage(const GURL& origin) override; + bool IsStorageSessionOnly(const GURL& origin) override; + bool HasSessionOnlyOrigins() override; + + protected: + ~SpecialStoragePolicy() override; +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_SPECIAL_STORAGE_POLICY_H_ diff --git a/brightray/browser/url_request_context_getter.cc b/brightray/browser/url_request_context_getter.cc new file mode 100644 index 000000000000..2e483d62ae5f --- /dev/null +++ b/brightray/browser/url_request_context_getter.cc @@ -0,0 +1,371 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "browser/url_request_context_getter.h" + +#include + +#include "browser/net/devtools_network_controller_handle.h" +#include "browser/net/devtools_network_transaction_factory.h" +#include "browser/net_log.h" +#include "browser/network_delegate.h" +#include "common/switches.h" + +#include "base/command_line.h" +#include "base/memory/ptr_util.h" +#include "base/strings/string_util.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/worker_pool.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/cookie_store_factory.h" +#include "content/public/common/content_switches.h" +#include "net/base/host_mapping_rules.h" +#include "net/cert/cert_verifier.h" +#include "net/cert/ct_known_logs.h" +#include "net/cert/ct_log_verifier.h" +#include "net/cert/ct_policy_enforcer.h" +#include "net/cert/multi_log_ct_verifier.h" +#include "net/cookies/cookie_monster.h" +#include "net/dns/mapped_host_resolver.h" +#include "net/http/http_auth_filter.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_auth_preferences.h" +#include "net/http/http_server_properties_impl.h" +#include "net/log/net_log.h" +#include "net/proxy/dhcp_proxy_script_fetcher_factory.h" +#include "net/proxy/proxy_config.h" +#include "net/proxy/proxy_config_service.h" +#include "net/proxy/proxy_script_fetcher_impl.h" +#include "net/proxy/proxy_service.h" +#include "net/proxy/proxy_service_v8.h" +#include "net/ssl/channel_id_service.h" +#include "net/ssl/default_channel_id_store.h" +#include "net/ssl/ssl_config_service_defaults.h" +#include "net/url_request/data_protocol_handler.h" +#include "net/url_request/file_protocol_handler.h" +#include "net/url_request/static_http_user_agent_settings.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" +#include "net/url_request/url_request_context_storage.h" +#include "net/url_request/url_request_intercepting_job_factory.h" +#include "net/url_request/url_request_job_factory_impl.h" +#include "ui/base/l10n/l10n_util.h" +#include "url/url_constants.h" +#include "storage/browser/quota/special_storage_policy.h" + +#if defined(USE_NSS_CERTS) +#include "net/cert_net/nss_ocsp.h" +#endif + +using content::BrowserThread; + +namespace brightray { + +std::string URLRequestContextGetter::Delegate::GetUserAgent() { + return base::EmptyString(); +} + +std::unique_ptr +URLRequestContextGetter::Delegate::CreateURLRequestJobFactory( + content::ProtocolHandlerMap* protocol_handlers) { + std::unique_ptr job_factory( + new net::URLRequestJobFactoryImpl); + + for (auto& it : *protocol_handlers) { + job_factory->SetProtocolHandler( + it.first, base::WrapUnique(it.second.release())); + } + protocol_handlers->clear(); + + job_factory->SetProtocolHandler( + url::kDataScheme, base::WrapUnique(new net::DataProtocolHandler)); + job_factory->SetProtocolHandler( + url::kFileScheme, + base::WrapUnique(new net::FileProtocolHandler( + BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)))); + + return std::move(job_factory); +} + +net::HttpCache::BackendFactory* +URLRequestContextGetter::Delegate::CreateHttpCacheBackendFactory( + const base::FilePath& base_path) { + base::FilePath cache_path = base_path.Append(FILE_PATH_LITERAL("Cache")); + return new net::HttpCache::DefaultBackend( + net::DISK_CACHE, + net::CACHE_BACKEND_DEFAULT, + cache_path, + 0, + BrowserThread::GetTaskRunnerForThread(BrowserThread::CACHE)); +} + +std::unique_ptr +URLRequestContextGetter::Delegate::CreateCertVerifier() { + return net::CertVerifier::CreateDefault(); +} + +net::SSLConfigService* +URLRequestContextGetter::Delegate::CreateSSLConfigService() { + return new net::SSLConfigServiceDefaults; +} + +std::vector +URLRequestContextGetter::Delegate::GetCookieableSchemes() { + return { "http", "https", "ws", "wss" }; +} + +URLRequestContextGetter::URLRequestContextGetter( + Delegate* delegate, + DevToolsNetworkControllerHandle* handle, + NetLog* net_log, + const base::FilePath& base_path, + bool in_memory, + scoped_refptr io_task_runner, + scoped_refptr file_task_runner, + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector protocol_interceptors) + : delegate_(delegate), + network_controller_handle_(handle), + net_log_(net_log), + base_path_(base_path), + in_memory_(in_memory), + io_task_runner_(io_task_runner), + file_task_runner_(file_task_runner), + protocol_interceptors_(std::move(protocol_interceptors)), + job_factory_(nullptr) { + // Must first be created on the UI thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (protocol_handlers) + std::swap(protocol_handlers_, *protocol_handlers); + + if (delegate_) + user_agent_ = delegate_->GetUserAgent(); + + // We must create the proxy config service on the UI loop on Linux because it + // must synchronously run on the glib message loop. This will be passed to + // the URLRequestContextStorage on the IO thread in GetURLRequestContext(). + proxy_config_service_ = net::ProxyService::CreateSystemProxyConfigService( + io_task_runner_, file_task_runner_); +} + +URLRequestContextGetter::~URLRequestContextGetter() { +#if defined(USE_NSS_CERTS) + net::SetURLRequestContextForNSSHttpIO(NULL); +#endif +} + +net::HostResolver* URLRequestContextGetter::host_resolver() { + return url_request_context_->host_resolver(); +} + +net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!url_request_context_.get()) { + auto& command_line = *base::CommandLine::ForCurrentProcess(); + url_request_context_.reset(new net::URLRequestContext); + +#if defined(USE_NSS_CERTS) + net::SetURLRequestContextForNSSHttpIO(url_request_context_.get()); +#endif + + // --log-net-log + if (net_log_) { + net_log_->StartLogging(url_request_context_.get()); + url_request_context_->set_net_log(net_log_); + } + + network_delegate_.reset(delegate_->CreateNetworkDelegate()); + url_request_context_->set_network_delegate(network_delegate_.get()); + + storage_.reset( + new net::URLRequestContextStorage(url_request_context_.get())); + + auto cookie_path = in_memory_ ? + base::FilePath() : base_path_.Append(FILE_PATH_LITERAL("Cookies")); + auto cookie_config = content::CookieStoreConfig( + cookie_path, + content::CookieStoreConfig::EPHEMERAL_SESSION_COOKIES, + nullptr, + delegate_->CreateCookieDelegate()); + cookie_config.cookieable_schemes = delegate_->GetCookieableSchemes(); + std::unique_ptr cookie_store = + content::CreateCookieStore(cookie_config); + storage_->set_cookie_store(std::move(cookie_store)); + storage_->set_channel_id_service(base::MakeUnique( + new net::DefaultChannelIDStore(nullptr))); + + std::string accept_lang = l10n_util::GetApplicationLocale(""); + storage_->set_http_user_agent_settings(base::WrapUnique( + new net::StaticHttpUserAgentSettings( + net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang), + user_agent_))); + + std::unique_ptr host_resolver( + net::HostResolver::CreateDefaultResolver(nullptr)); + + // --host-resolver-rules + if (command_line.HasSwitch(::switches::kHostResolverRules)) { + std::unique_ptr remapped_resolver( + new net::MappedHostResolver(std::move(host_resolver))); + remapped_resolver->SetRulesFromString( + command_line.GetSwitchValueASCII(::switches::kHostResolverRules)); + host_resolver = std::move(remapped_resolver); + } + + // --proxy-server + net::DhcpProxyScriptFetcherFactory dhcp_factory; + if (command_line.HasSwitch(switches::kNoProxyServer)) { + storage_->set_proxy_service(net::ProxyService::CreateDirect()); + } else if (command_line.HasSwitch(switches::kProxyServer)) { + net::ProxyConfig proxy_config; + proxy_config.proxy_rules().ParseFromString( + command_line.GetSwitchValueASCII(switches::kProxyServer)); + proxy_config.proxy_rules().bypass_rules.ParseFromString( + command_line.GetSwitchValueASCII(switches::kProxyBypassList)); + storage_->set_proxy_service(net::ProxyService::CreateFixed(proxy_config)); + } else if (command_line.HasSwitch(switches::kProxyPacUrl)) { + auto proxy_config = net::ProxyConfig::CreateFromCustomPacURL( + GURL(command_line.GetSwitchValueASCII(switches::kProxyPacUrl))); + proxy_config.set_pac_mandatory(true); + storage_->set_proxy_service(net::ProxyService::CreateFixed( + proxy_config)); + } else { + storage_->set_proxy_service( + net::CreateProxyServiceUsingV8ProxyResolver( + std::move(proxy_config_service_), + new net::ProxyScriptFetcherImpl(url_request_context_.get()), + dhcp_factory.Create(url_request_context_.get()), + host_resolver.get(), + nullptr, + url_request_context_->network_delegate())); + } + + std::vector schemes; + schemes.push_back(std::string("basic")); + schemes.push_back(std::string("digest")); + schemes.push_back(std::string("ntlm")); + schemes.push_back(std::string("negotiate")); +#if defined(OS_POSIX) + http_auth_preferences_.reset(new net::HttpAuthPreferences(schemes, + std::string())); +#else + http_auth_preferences_.reset(new net::HttpAuthPreferences(schemes)); +#endif + + // --auth-server-whitelist + if (command_line.HasSwitch(switches::kAuthServerWhitelist)) { + http_auth_preferences_->set_server_whitelist( + command_line.GetSwitchValueASCII(switches::kAuthServerWhitelist)); + } + + // --auth-negotiate-delegate-whitelist + if (command_line.HasSwitch(switches::kAuthNegotiateDelegateWhitelist)) { + http_auth_preferences_->set_delegate_whitelist( + command_line.GetSwitchValueASCII( + switches::kAuthNegotiateDelegateWhitelist)); + } + + auto auth_handler_factory = + net::HttpAuthHandlerRegistryFactory::Create( + http_auth_preferences_.get(), host_resolver.get()); + + std::unique_ptr transport_security_state = + base::WrapUnique(new net::TransportSecurityState); + transport_security_state->SetRequireCTDelegate( + delegate_->GetRequireCTDelegate()); + storage_->set_transport_security_state(std::move(transport_security_state)); + storage_->set_cert_verifier(delegate_->CreateCertVerifier()); + storage_->set_ssl_config_service(delegate_->CreateSSLConfigService()); + storage_->set_http_auth_handler_factory(std::move(auth_handler_factory)); + std::unique_ptr server_properties( + new net::HttpServerPropertiesImpl); + storage_->set_http_server_properties(std::move(server_properties)); + + std::unique_ptr ct_verifier = + base::MakeUnique(); + ct_verifier->AddLogs(net::ct::CreateLogVerifiersForKnownLogs()); + storage_->set_cert_transparency_verifier(std::move(ct_verifier)); + storage_->set_ct_policy_enforcer(base::MakeUnique()); + + net::HttpNetworkSession::Params network_session_params; + net::URLRequestContextBuilder::SetHttpNetworkSessionComponents( + url_request_context_.get(), &network_session_params); + network_session_params.ignore_certificate_errors = false; + + // --disable-http2 + if (command_line.HasSwitch(switches::kDisableHttp2)) + network_session_params.enable_http2 = false; + + // --ignore-certificate-errors + if (command_line.HasSwitch(switches::kIgnoreCertificateErrors)) + network_session_params.ignore_certificate_errors = true; + + // --host-rules + if (command_line.HasSwitch(switches::kHostRules)) { + host_mapping_rules_.reset(new net::HostMappingRules); + host_mapping_rules_->SetRulesFromString( + command_line.GetSwitchValueASCII(switches::kHostRules)); + network_session_params.host_mapping_rules = host_mapping_rules_.get(); + } + + // Give |storage_| ownership at the end in case it's |mapped_host_resolver|. + storage_->set_host_resolver(std::move(host_resolver)); + network_session_params.host_resolver = + url_request_context_->host_resolver(); + + http_network_session_.reset( + new net::HttpNetworkSession(network_session_params)); + std::unique_ptr backend; + if (in_memory_) { + backend = net::HttpCache::DefaultBackend::InMemory(0); + } else { + backend.reset(delegate_->CreateHttpCacheBackendFactory(base_path_)); + } + + if (network_controller_handle_) { + storage_->set_http_transaction_factory(base::WrapUnique( + new net::HttpCache( + base::WrapUnique(new DevToolsNetworkTransactionFactory( + network_controller_handle_->GetController(), + http_network_session_.get())), + std::move(backend), + false))); + } else { + storage_->set_http_transaction_factory(base::WrapUnique( + new net::HttpCache(http_network_session_.get(), + std::move(backend), + false))); + } + + std::unique_ptr job_factory = + delegate_->CreateURLRequestJobFactory(&protocol_handlers_); + job_factory_ = job_factory.get(); + + // Set up interceptors in the reverse order. + std::unique_ptr top_job_factory = + std::move(job_factory); + content::URLRequestInterceptorScopedVector::reverse_iterator it; + for (it = protocol_interceptors_.rbegin(); + it != protocol_interceptors_.rend(); + ++it) { + top_job_factory.reset(new net::URLRequestInterceptingJobFactory( + std::move(top_job_factory), std::move(*it))); + } + protocol_interceptors_.clear(); + + storage_->set_job_factory(std::move(top_job_factory)); + } + + return url_request_context_.get(); +} + +scoped_refptr +URLRequestContextGetter::GetNetworkTaskRunner() const { + return BrowserThread::GetTaskRunnerForThread(BrowserThread::IO); +} + +} // namespace brightray diff --git a/brightray/browser/url_request_context_getter.h b/brightray/browser/url_request_context_getter.h new file mode 100644 index 000000000000..c09824d44bea --- /dev/null +++ b/brightray/browser/url_request_context_getter.h @@ -0,0 +1,115 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_URL_REQUEST_CONTEXT_GETTER_H_ +#define BRIGHTRAY_BROWSER_URL_REQUEST_CONTEXT_GETTER_H_ + +#include "base/files/file_path.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/content_browser_client.h" +#include "net/cookies/cookie_monster.h" +#include "net/http/http_cache.h" +#include "net/http/transport_security_state.h" +#include "net/http/url_security_manager.h" +#include "net/url_request/url_request_context_getter.h" + +namespace base { +class MessageLoop; +} + +namespace net { +class HostMappingRules; +class HostResolver; +class HttpAuthPreferences; +class NetworkDelegate; +class ProxyConfigService; +class URLRequestContextStorage; +class URLRequestJobFactory; +} + +namespace brightray { + +class DevToolsNetworkControllerHandle; +class MediaDeviceIDSalt; +class NetLog; + +class URLRequestContextGetter : public net::URLRequestContextGetter { + public: + class Delegate { + public: + Delegate() {} + virtual ~Delegate() {} + + virtual net::NetworkDelegate* CreateNetworkDelegate() { return nullptr; } + virtual net::CookieMonsterDelegate* CreateCookieDelegate() { + return nullptr; + } + virtual std::string GetUserAgent(); + virtual std::unique_ptr + CreateURLRequestJobFactory(content::ProtocolHandlerMap* protocol_handlers); + virtual net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory( + const base::FilePath& base_path); + virtual std::unique_ptr CreateCertVerifier(); + virtual net::SSLConfigService* CreateSSLConfigService(); + virtual std::vector GetCookieableSchemes(); + virtual net::TransportSecurityState::RequireCTDelegate* + GetRequireCTDelegate() { + return nullptr; + } + virtual MediaDeviceIDSalt* GetMediaDeviceIDSalt() { return nullptr; } + }; + + URLRequestContextGetter( + Delegate* delegate, + DevToolsNetworkControllerHandle* handle, + NetLog* net_log, + const base::FilePath& base_path, + bool in_memory, + scoped_refptr io_task_runner, + scoped_refptr file_task_runner, + content::ProtocolHandlerMap* protocol_handlers, + content::URLRequestInterceptorScopedVector protocol_interceptors); + virtual ~URLRequestContextGetter(); + + // net::URLRequestContextGetter: + net::URLRequestContext* GetURLRequestContext() override; + scoped_refptr GetNetworkTaskRunner() + const override; + + net::HostResolver* host_resolver(); + net::URLRequestJobFactory* job_factory() const { return job_factory_; } + MediaDeviceIDSalt* GetMediaDeviceIDSalt() const { + return delegate_->GetMediaDeviceIDSalt(); + } + + private: + Delegate* delegate_; + + DevToolsNetworkControllerHandle* network_controller_handle_; + NetLog* net_log_; + base::FilePath base_path_; + bool in_memory_; + scoped_refptr io_task_runner_; + scoped_refptr file_task_runner_; + + std::string user_agent_; + + std::unique_ptr proxy_config_service_; + std::unique_ptr network_delegate_; + std::unique_ptr storage_; + std::unique_ptr url_request_context_; + std::unique_ptr host_mapping_rules_; + std::unique_ptr http_auth_preferences_; + std::unique_ptr http_network_session_; + content::ProtocolHandlerMap protocol_handlers_; + content::URLRequestInterceptorScopedVector protocol_interceptors_; + + net::URLRequestJobFactory* job_factory_; // weak ref + + DISALLOW_COPY_AND_ASSIGN(URLRequestContextGetter); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/views/inspectable_web_contents_view_views.cc b/brightray/browser/views/inspectable_web_contents_view_views.cc new file mode 100644 index 000000000000..8f52ca569952 --- /dev/null +++ b/brightray/browser/views/inspectable_web_contents_view_views.cc @@ -0,0 +1,229 @@ +#include "browser/views/inspectable_web_contents_view_views.h" + +#include "browser/inspectable_web_contents_delegate.h" +#include "browser/inspectable_web_contents_impl.h" +#include "browser/inspectable_web_contents_view_delegate.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/webview/webview.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/window/client_view.h" + +namespace brightray { + +namespace { + +class DevToolsWindowDelegate : public views::ClientView, + public views::WidgetDelegate { + public: + DevToolsWindowDelegate(InspectableWebContentsViewViews* shell, + views::View* view, + views::Widget* widget) + : views::ClientView(widget, view), + shell_(shell), + view_(view), + widget_(widget) { + // A WidgetDelegate should be deleted on DeleteDelegate. + set_owned_by_client(); + + if (shell->GetDelegate()) + icon_ = shell->GetDelegate()->GetDevToolsWindowIcon(); + } + virtual ~DevToolsWindowDelegate() {} + + // views::WidgetDelegate: + void DeleteDelegate() override { delete this; } + views::View* GetInitiallyFocusedView() override { return view_; } + bool CanResize() const override { return true; } + bool CanMaximize() const override { return true; } + bool CanMinimize() const override { return true; } + base::string16 GetWindowTitle() const override { return shell_->GetTitle(); } + gfx::ImageSkia GetWindowAppIcon() override { return GetWindowIcon(); } + gfx::ImageSkia GetWindowIcon() override { return icon_; } + views::Widget* GetWidget() override { return widget_; } + const views::Widget* GetWidget() const override { return widget_; } + views::View* GetContentsView() override { return view_; } + views::ClientView* CreateClientView(views::Widget* widget) override { + return this; + } + + // views::ClientView: + bool CanClose() override { + shell_->inspectable_web_contents()->CloseDevTools(); + return false; + } + + private: + InspectableWebContentsViewViews* shell_; + views::View* view_; + views::Widget* widget_; + gfx::ImageSkia icon_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsWindowDelegate); +}; + +} // namespace + +InspectableWebContentsView* CreateInspectableContentsView( + InspectableWebContentsImpl* inspectable_web_contents) { + return new InspectableWebContentsViewViews(inspectable_web_contents); +} + +InspectableWebContentsViewViews::InspectableWebContentsViewViews( + InspectableWebContentsImpl* inspectable_web_contents) + : inspectable_web_contents_(inspectable_web_contents), + devtools_window_web_view_(nullptr), + contents_web_view_(nullptr), + devtools_web_view_(new views::WebView(nullptr)), + devtools_visible_(false), + devtools_window_delegate_(nullptr), + title_(base::ASCIIToUTF16("Developer Tools")) { + set_owned_by_client(); + + if (inspectable_web_contents_->GetWebContents()->GetNativeView()) { + views::WebView* contents_web_view = new views::WebView(nullptr); + contents_web_view->SetWebContents( + inspectable_web_contents_->GetWebContents()); + contents_web_view_ = contents_web_view; + } else { + contents_web_view_ = new views::Label( + base::ASCIIToUTF16("No content under offscreen mode")); + } + + devtools_web_view_->SetVisible(false); + AddChildView(devtools_web_view_); + AddChildView(contents_web_view_); +} + +InspectableWebContentsViewViews::~InspectableWebContentsViewViews() { + if (devtools_window_) + inspectable_web_contents()->SaveDevToolsBounds( + devtools_window_->GetWindowBoundsInScreen()); +} + +views::View* InspectableWebContentsViewViews::GetView() { + return this; +} + +views::View* InspectableWebContentsViewViews::GetWebView() { + return contents_web_view_; +} + +void InspectableWebContentsViewViews::ShowDevTools() { + if (devtools_visible_) + return; + + devtools_visible_ = true; + if (devtools_window_) { + devtools_window_web_view_->SetWebContents( + inspectable_web_contents_->GetDevToolsWebContents()); + devtools_window_->SetBounds( + inspectable_web_contents()->GetDevToolsBounds()); + devtools_window_->Show(); + } else { + devtools_web_view_->SetVisible(true); + devtools_web_view_->SetWebContents( + inspectable_web_contents_->GetDevToolsWebContents()); + devtools_web_view_->RequestFocus(); + Layout(); + } +} + +void InspectableWebContentsViewViews::CloseDevTools() { + if (!devtools_visible_) + return; + + devtools_visible_ = false; + if (devtools_window_) { + inspectable_web_contents()->SaveDevToolsBounds( + devtools_window_->GetWindowBoundsInScreen()); + devtools_window_.reset(); + devtools_window_web_view_ = nullptr; + devtools_window_delegate_ = nullptr; + } else { + devtools_web_view_->SetVisible(false); + devtools_web_view_->SetWebContents(NULL); + Layout(); + } +} + +bool InspectableWebContentsViewViews::IsDevToolsViewShowing() { + return devtools_visible_; +} + +bool InspectableWebContentsViewViews::IsDevToolsViewFocused() { + if (devtools_window_web_view_) + return devtools_window_web_view_->HasFocus(); + else if (devtools_web_view_) + return devtools_web_view_->HasFocus(); + else + return false; +} + +void InspectableWebContentsViewViews::SetIsDocked(bool docked) { + CloseDevTools(); + + if (!docked) { + devtools_window_.reset(new views::Widget); + devtools_window_web_view_ = new views::WebView(NULL); + devtools_window_delegate_ = new DevToolsWindowDelegate( + this, + devtools_window_web_view_, + devtools_window_.get()); + + views::Widget::InitParams params; + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.delegate = devtools_window_delegate_; + params.bounds = inspectable_web_contents()->GetDevToolsBounds(); + +#if defined(USE_X11) + params.wm_role_name = "devtools"; + if (GetDelegate()) + GetDelegate()->GetDevToolsWindowWMClass(¶ms.wm_class_name, + ¶ms.wm_class_class); +#endif + + devtools_window_->Init(params); + devtools_window_->UpdateWindowIcon(); + } + + ShowDevTools(); +} + +void InspectableWebContentsViewViews::SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) { + strategy_.CopyFrom(strategy); + Layout(); +} + +void InspectableWebContentsViewViews::SetTitle(const base::string16& title) { + if (devtools_window_) { + title_ = title; + devtools_window_->UpdateWindowTitle(); + } +} + +void InspectableWebContentsViewViews::Layout() { + if (!devtools_web_view_->visible()) { + contents_web_view_->SetBoundsRect(GetContentsBounds()); + return; + } + + gfx::Size container_size(width(), height()); + gfx::Rect new_devtools_bounds; + gfx::Rect new_contents_bounds; + ApplyDevToolsContentsResizingStrategy(strategy_, container_size, + &new_devtools_bounds, &new_contents_bounds); + + // DevTools cares about the specific position, so we have to compensate RTL + // layout here. + new_devtools_bounds.set_x(GetMirroredXForRect(new_devtools_bounds)); + new_contents_bounds.set_x(GetMirroredXForRect(new_contents_bounds)); + + devtools_web_view_->SetBoundsRect(new_devtools_bounds); + contents_web_view_->SetBoundsRect(new_contents_bounds); +} + +} // namespace brightray diff --git a/brightray/browser/views/inspectable_web_contents_view_views.h b/brightray/browser/views/inspectable_web_contents_view_views.h new file mode 100644 index 000000000000..9094b9f35dc9 --- /dev/null +++ b/brightray/browser/views/inspectable_web_contents_view_views.h @@ -0,0 +1,67 @@ +#ifndef BROWSER_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_ +#define BROWSER_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_ + +#include "browser/devtools_contents_resizing_strategy.h" +#include "browser/inspectable_web_contents_view.h" + +#include "base/compiler_specific.h" +#include "ui/views/view.h" + +namespace views { +class WebView; +class Widget; +class WidgetDelegate; +} + +namespace brightray { + +class InspectableWebContentsImpl; + +class InspectableWebContentsViewViews : public InspectableWebContentsView, + public views::View { + public: + explicit InspectableWebContentsViewViews( + InspectableWebContentsImpl* inspectable_web_contents_impl); + ~InspectableWebContentsViewViews(); + + // InspectableWebContentsView: + views::View* GetView() override; + views::View* GetWebView() override; + void ShowDevTools() override; + void CloseDevTools() override; + bool IsDevToolsViewShowing() override; + bool IsDevToolsViewFocused() override; + void SetIsDocked(bool docked) override; + void SetContentsResizingStrategy( + const DevToolsContentsResizingStrategy& strategy) override; + void SetTitle(const base::string16& title) override; + + InspectableWebContentsImpl* inspectable_web_contents() { + return inspectable_web_contents_; + } + + const base::string16& GetTitle() const { return title_; } + + private: + // views::View: + void Layout() override; + + // Owns us. + InspectableWebContentsImpl* inspectable_web_contents_; + + std::unique_ptr devtools_window_; + views::WebView* devtools_window_web_view_; + views::View* contents_web_view_; + views::WebView* devtools_web_view_; + + DevToolsContentsResizingStrategy strategy_; + bool devtools_visible_; + views::WidgetDelegate* devtools_window_delegate_; + base::string16 title_; + + DISALLOW_COPY_AND_ASSIGN(InspectableWebContentsViewViews); +}; + +} // namespace brightray + +#endif // BROWSER_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_ diff --git a/brightray/browser/views/views_delegate.cc b/brightray/browser/views/views_delegate.cc new file mode 100644 index 000000000000..99797dea4adc --- /dev/null +++ b/brightray/browser/views/views_delegate.cc @@ -0,0 +1,115 @@ +// Copyright (c) 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-CHROMIUM file. + +#include "browser/views/views_delegate.h" + +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/native_widget_aura.h" + +#if defined(OS_LINUX) +#include "ui/views/linux_ui/linux_ui.h" +#endif + +namespace brightray { + +ViewsDelegate::ViewsDelegate() { +} + +ViewsDelegate::~ViewsDelegate() { +} + +void ViewsDelegate::SaveWindowPlacement(const views::Widget* window, + const std::string& window_name, + const gfx::Rect& bounds, + ui::WindowShowState show_state) { +} + +bool ViewsDelegate::GetSavedWindowPlacement( + const views::Widget* widget, + const std::string& window_name, + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + return false; +} + +void ViewsDelegate::NotifyAccessibilityEvent( + views::View* view, ui::AXEvent event_type) { +} + +void ViewsDelegate::NotifyMenuItemFocused( + const base::string16& menu_name, + const base::string16& menu_item_name, + int item_index, + int item_count, + bool has_submenu) { +} + +#if defined(OS_WIN) +HICON ViewsDelegate::GetDefaultWindowIcon() const { + // Use current exe's icon as default window icon. + return LoadIcon(GetModuleHandle(NULL), + MAKEINTRESOURCE(1 /* IDR_MAINFRAME */)); +} + +HICON ViewsDelegate::GetSmallWindowIcon() const { + return GetDefaultWindowIcon(); +} + +bool ViewsDelegate::IsWindowInMetro(gfx::NativeWindow window) const { + return false; +} + +#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) +gfx::ImageSkia* ViewsDelegate::GetDefaultWindowIcon() const { + return NULL; +} +#endif + +views::NonClientFrameView* ViewsDelegate::CreateDefaultNonClientFrameView( + views::Widget* widget) { + return NULL; +} + +void ViewsDelegate::AddRef() { +} + +void ViewsDelegate::ReleaseRef() { +} + +content::WebContents* ViewsDelegate::CreateWebContents( + content::BrowserContext* browser_context, + content::SiteInstance* site_instance) { + return NULL; +} + +void ViewsDelegate::OnBeforeWidgetInit( + views::Widget::InitParams* params, + views::internal::NativeWidgetDelegate* delegate) { + // If we already have a native_widget, we don't have to try to come + // up with one. + if (params->native_widget) + return; + + if (params->parent && + params->type != views::Widget::InitParams::TYPE_MENU && + params->type != views::Widget::InitParams::TYPE_TOOLTIP) { + params->native_widget = new views::NativeWidgetAura(delegate); + } else { + params->native_widget = new views::DesktopNativeWidgetAura(delegate); + } +} + + +bool ViewsDelegate::WindowManagerProvidesTitleBar(bool maximized) { +#if defined(OS_LINUX) + // On Ubuntu Unity, the system always provides a title bar for maximized + // windows. + views::LinuxUI* ui = views::LinuxUI::instance(); + return maximized && ui && ui->UnityIsRunning(); +#else + return false; +#endif +} + +} // namespace brightray diff --git a/brightray/browser/views/views_delegate.h b/brightray/browser/views/views_delegate.h new file mode 100644 index 000000000000..d63eeb533d67 --- /dev/null +++ b/brightray/browser/views/views_delegate.h @@ -0,0 +1,64 @@ +// Copyright (c) 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_VIEWS_VIEWS_DELEGATE_H_ +#define BRIGHTRAY_BROWSER_VIEWS_VIEWS_DELEGATE_H_ + +#include "base/compiler_specific.h" +#include "ui/views/views_delegate.h" + +namespace brightray { + +class ViewsDelegate : public views::ViewsDelegate { + public: + ViewsDelegate(); + virtual ~ViewsDelegate(); + + protected: + // views::ViewsDelegate: + void SaveWindowPlacement(const views::Widget* window, + const std::string& window_name, + const gfx::Rect& bounds, + ui::WindowShowState show_state) override; + bool GetSavedWindowPlacement( + const views::Widget* widget, + const std::string& window_name, + gfx::Rect* bounds, + ui::WindowShowState* show_state) const override; + void NotifyAccessibilityEvent( + views::View* view, ui::AXEvent event_type) override; + void NotifyMenuItemFocused(const base::string16& menu_name, + const base::string16& menu_item_name, + int item_index, + int item_count, + bool has_submenu) override; + +#if defined(OS_WIN) + HICON GetDefaultWindowIcon() const override; + HICON GetSmallWindowIcon() const override; + bool IsWindowInMetro(gfx::NativeWindow window) const override; +#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) + gfx::ImageSkia* GetDefaultWindowIcon() const override; +#endif + views::NonClientFrameView* CreateDefaultNonClientFrameView( + views::Widget* widget) override; + void AddRef() override; + void ReleaseRef() override; + content::WebContents* CreateWebContents( + content::BrowserContext* browser_context, + content::SiteInstance* site_instance) override; + void OnBeforeWidgetInit( + views::Widget::InitParams* params, + views::internal::NativeWidgetDelegate* delegate) override; + bool WindowManagerProvidesTitleBar(bool maximized) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ViewsDelegate); +}; + + + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_VIEWS_VIEWS_DELEGATE_H_ diff --git a/brightray/browser/web_ui_controller_factory.cc b/brightray/browser/web_ui_controller_factory.cc new file mode 100644 index 000000000000..565c0258883a --- /dev/null +++ b/brightray/browser/web_ui_controller_factory.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2013 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-CHROMIUM file. + +#include "browser/web_ui_controller_factory.h" + +#include "browser/devtools_ui.h" +#include "base/memory/singleton.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_ui.h" +#include "content/public/common/url_constants.h" + +namespace brightray { + +namespace { + +const char kChromeUIDevToolsBundledHost[] = "devtools"; + +} // namespace + +// static +WebUIControllerFactory* WebUIControllerFactory::GetInstance() { + return base::Singleton::get(); +} + +WebUIControllerFactory::WebUIControllerFactory() { +} + +WebUIControllerFactory::~WebUIControllerFactory() { +} + +content::WebUI::TypeID WebUIControllerFactory::GetWebUIType( + content::BrowserContext* browser_context, const GURL& url) const { + if (url.host() == kChromeUIDevToolsBundledHost) { + return const_cast(this); + } + + return content::WebUI::kNoWebUI; +} + +bool WebUIControllerFactory::UseWebUIForURL( + content::BrowserContext* browser_context, const GURL& url) const { + return GetWebUIType(browser_context, url) != content::WebUI::kNoWebUI; +} + +bool WebUIControllerFactory::UseWebUIBindingsForURL( + content::BrowserContext* browser_context, const GURL& url) const { + return UseWebUIForURL(browser_context, url); +} + +content::WebUIController* WebUIControllerFactory::CreateWebUIControllerForURL( + content::WebUI* web_ui, const GURL& url) const { + if (url.host() == kChromeUIDevToolsBundledHost) { + auto browser_context = web_ui->GetWebContents()->GetBrowserContext(); + return new DevToolsUI(browser_context, web_ui); + } + return nullptr; +} + +} // namespace brightray diff --git a/brightray/browser/web_ui_controller_factory.h b/brightray/browser/web_ui_controller_factory.h new file mode 100644 index 000000000000..ca86de6baa65 --- /dev/null +++ b/brightray/browser/web_ui_controller_factory.h @@ -0,0 +1,45 @@ +// Copyright (c) 2013 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-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_WEB_UI_CONTROLLER_FACTORY_H_ +#define BRIGHTRAY_BROWSER_WEB_UI_CONTROLLER_FACTORY_H_ + +#include "base/macros.h" +#include "content/public/browser/web_ui.h" +#include "content/public/browser/web_ui_controller_factory.h" + +namespace base { +template struct DefaultSingletonTraits; +} + +namespace brightray { + +class BrowserContext; + +class WebUIControllerFactory : public content::WebUIControllerFactory { + public: + static WebUIControllerFactory* GetInstance(); + + WebUIControllerFactory(); + virtual ~WebUIControllerFactory(); + + content::WebUI::TypeID GetWebUIType( + content::BrowserContext* browser_context, const GURL& url) const override; + bool UseWebUIForURL(content::BrowserContext* browser_context, + const GURL& url) const override; + bool UseWebUIBindingsForURL(content::BrowserContext* browser_context, + const GURL& url) const override; + content::WebUIController* CreateWebUIControllerForURL( + content::WebUI* web_ui, + const GURL& url) const override; + + private: + friend struct base::DefaultSingletonTraits; + + DISALLOW_COPY_AND_ASSIGN(WebUIControllerFactory); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/win/notification_presenter_win.cc b/brightray/browser/win/notification_presenter_win.cc new file mode 100644 index 000000000000..930775c792b2 --- /dev/null +++ b/brightray/browser/win/notification_presenter_win.cc @@ -0,0 +1,78 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and +// Jason Poon . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/win/notification_presenter_win.h" + +#include "base/files/file_util.h" +#include "base/md5.h" +#include "base/strings/utf_string_conversions.h" +#include "base/win/windows_version.h" +#include "browser/win/notification_presenter_win7.h" +#include "browser/win/windows_toast_notification.h" +#include "content/public/browser/desktop_notification_delegate.h" +#include "content/public/common/platform_notification_data.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/png_codec.h" + +#pragma comment(lib, "runtimeobject.lib") + +namespace brightray { + +namespace { + +bool SaveIconToPath(const SkBitmap& bitmap, const base::FilePath& path) { + std::vector png_data; + if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data)) + return false; + + char* data = reinterpret_cast(&png_data[0]); + int size = static_cast(png_data.size()); + return base::WriteFile(path, data, size) == size; +} + +} // namespace + +// static +NotificationPresenter* NotificationPresenter::Create() { + auto version = base::win::GetVersion(); + if (version < base::win::VERSION_WIN8) + return new NotificationPresenterWin7; + if (!WindowsToastNotification::Initialize()) + return nullptr; + std::unique_ptr presenter( + new NotificationPresenterWin); + if (!presenter->Init()) + return nullptr; + return presenter.release(); +} + +NotificationPresenterWin::NotificationPresenterWin() { +} + +NotificationPresenterWin::~NotificationPresenterWin() { +} + +bool NotificationPresenterWin::Init() { + return temp_dir_.CreateUniqueTempDir(); +} + +base::string16 NotificationPresenterWin::SaveIconToFilesystem( + const SkBitmap& icon, const GURL& origin) { + std::string filename = base::MD5String(origin.spec()) + ".png"; + base::FilePath path = temp_dir_.GetPath().Append(base::UTF8ToUTF16(filename)); + if (base::PathExists(path)) + return path.value(); + if (SaveIconToPath(icon, path)) + return path.value(); + return base::UTF8ToUTF16(origin.spec()); +} + +Notification* NotificationPresenterWin::CreateNotificationObject( + NotificationDelegate* delegate) { + return new WindowsToastNotification(delegate, this); +} + +} // namespace brightray diff --git a/brightray/browser/win/notification_presenter_win.h b/brightray/browser/win/notification_presenter_win.h new file mode 100644 index 000000000000..679490c18521 --- /dev/null +++ b/brightray/browser/win/notification_presenter_win.h @@ -0,0 +1,55 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and +// Jason Poon . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +// Usage Example (JavaScript: +// var windowsNotification = new Notification("Test Title", { +// body: "Hi, I'm an example body. How are you?", +// icon: "file:///C:/Path/To/Your/Image.png" +// }); + +// windowsNotification.onshow = function () { +// console.log("Notification shown") +// }; +// windowsNotification.onclick = function () { +// console.log("Notification clicked") +// }; +// windowsNotification.onclose = function () { +// console.log("Notification dismissed") +// }; + +#ifndef BRIGHTRAY_BROWSER_WIN_NOTIFICATION_PRESENTER_WIN_H_ +#define BRIGHTRAY_BROWSER_WIN_NOTIFICATION_PRESENTER_WIN_H_ + +#include "base/files/scoped_temp_dir.h" +#include "base/strings/string16.h" +#include "browser/notification_presenter.h" + +class GURL; +class SkBitmap; + +namespace brightray { + +class NotificationPresenterWin : public NotificationPresenter { + public: + NotificationPresenterWin(); + ~NotificationPresenterWin(); + + bool Init(); + + base::string16 SaveIconToFilesystem(const SkBitmap& icon, const GURL& origin); + + private: + Notification* CreateNotificationObject( + NotificationDelegate* delegate) override; + + base::ScopedTempDir temp_dir_; + + DISALLOW_COPY_AND_ASSIGN(NotificationPresenterWin); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_WIN_NOTIFICATION_PRESENTER_WIN_H_ diff --git a/brightray/browser/win/notification_presenter_win7.cc b/brightray/browser/win/notification_presenter_win7.cc new file mode 100644 index 000000000000..f002c476d5a9 --- /dev/null +++ b/brightray/browser/win/notification_presenter_win7.cc @@ -0,0 +1,45 @@ +#include "browser/win/notification_presenter_win7.h" +#include "browser/win/win32_notification.h" + +namespace brightray { + +brightray::Notification* NotificationPresenterWin7::CreateNotificationObject( + NotificationDelegate* delegate) { + return new Win32Notification(delegate, this); +} + +Win32Notification* NotificationPresenterWin7::GetNotificationObjectByRef( + const DesktopNotificationController::Notification& ref) { + for (auto n : this->notifications()) { + auto w32n = static_cast(n); + if (w32n->GetRef() == ref) + return w32n; + } + + return nullptr; +} + +Win32Notification* NotificationPresenterWin7::GetNotificationObjectByTag( + const std::string& tag) { + for (auto n : this->notifications()) { + auto w32n = static_cast(n); + if (w32n->GetTag() == tag) + return w32n; + } + + return nullptr; +} + +void NotificationPresenterWin7::OnNotificationClicked( + Notification& notification) { + auto n = GetNotificationObjectByRef(notification); + if (n) n->NotificationClicked(); +} + +void NotificationPresenterWin7::OnNotificationDismissed( + Notification& notification) { + auto n = GetNotificationObjectByRef(notification); + if (n) n->NotificationDismissed(); +} + +} // namespace brightray diff --git a/brightray/browser/win/notification_presenter_win7.h b/brightray/browser/win/notification_presenter_win7.h new file mode 100644 index 000000000000..c191439befcc --- /dev/null +++ b/brightray/browser/win/notification_presenter_win7.h @@ -0,0 +1,30 @@ +#pragma once +#include "browser/notification_presenter.h" +#include "browser/win/win32_desktop_notifications/desktop_notification_controller.h" + +namespace brightray { + +class Win32Notification; + +class NotificationPresenterWin7 : + public NotificationPresenter, + public DesktopNotificationController { + public: + NotificationPresenterWin7() = default; + + Win32Notification* GetNotificationObjectByRef( + const DesktopNotificationController::Notification& ref); + + Win32Notification* GetNotificationObjectByTag(const std::string& tag); + + private: + brightray::Notification* CreateNotificationObject( + NotificationDelegate* delegate) override; + + void OnNotificationClicked(Notification& notification) override; + void OnNotificationDismissed(Notification& notification) override; + + DISALLOW_COPY_AND_ASSIGN(NotificationPresenterWin7); +}; + +} // namespace brightray diff --git a/brightray/browser/win/scoped_hstring.cc b/brightray/browser/win/scoped_hstring.cc new file mode 100644 index 000000000000..082ebe762270 --- /dev/null +++ b/brightray/browser/win/scoped_hstring.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/win/scoped_hstring.h" + +#include + +ScopedHString::ScopedHString(const wchar_t* source) + : str_(nullptr) { + Reset(source); +} + +ScopedHString::ScopedHString(const std::wstring& source) + : str_(nullptr) { + Reset(source); +} + +ScopedHString::ScopedHString() : str_(nullptr) { +} + +ScopedHString::~ScopedHString() { + Reset(); +} + +void ScopedHString::Reset() { + if (str_) { + WindowsDeleteString(str_); + str_ = nullptr; + } +} + +void ScopedHString::Reset(const wchar_t* source) { + Reset(); + WindowsCreateString(source, wcslen(source), &str_); +} + +void ScopedHString::Reset(const std::wstring& source) { + Reset(); + WindowsCreateString(source.c_str(), source.length(), &str_); +} diff --git a/brightray/browser/win/scoped_hstring.h b/brightray/browser/win/scoped_hstring.h new file mode 100644 index 000000000000..879e62cd39b8 --- /dev/null +++ b/brightray/browser/win/scoped_hstring.h @@ -0,0 +1,41 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_WIN_SCOPED_HSTRING_H_ +#define BRIGHTRAY_BROWSER_WIN_SCOPED_HSTRING_H_ + +#include +#include + +#include + +#include "base/macros.h" + +class ScopedHString { + public: + // Copy from |source|. + explicit ScopedHString(const wchar_t* source); + explicit ScopedHString(const std::wstring& source); + // Create empty string. + ScopedHString(); + ~ScopedHString(); + + // Sets to |source|. + void Reset(); + void Reset(const wchar_t* source); + void Reset(const std::wstring& source); + + // Returns string. + operator HSTRING() const { return str_; } + + // Whether there is a string created. + bool success() const { return str_; } + + private: + HSTRING str_; + + DISALLOW_COPY_AND_ASSIGN(ScopedHString); +}; + +#endif // BRIGHTRAY_BROWSER_WIN_SCOPED_HSTRING_H_ diff --git a/brightray/browser/win/win32_desktop_notifications/common.h b/brightray/browser/win/win32_desktop_notifications/common.h new file mode 100644 index 000000000000..ef364ceb4ccb --- /dev/null +++ b/brightray/browser/win/win32_desktop_notifications/common.h @@ -0,0 +1,55 @@ +#pragma once +#include + +namespace brightray { + +struct NotificationData { + DesktopNotificationController* controller = nullptr; + + std::wstring caption; + std::wstring body_text; + HBITMAP image = NULL; + + + NotificationData() = default; + + ~NotificationData() { + if (image) DeleteObject(image); + } + + NotificationData(const NotificationData& other) = delete; + NotificationData& operator=(const NotificationData& other) = delete; +}; + +template +inline T ScaleForDpi(T value, unsigned dpi) { + return value * dpi / 96; +} + +struct ScreenMetrics { + UINT dpi_x, dpi_y; + + ScreenMetrics() { + typedef HRESULT WINAPI GetDpiForMonitor_t(HMONITOR, int, UINT*, UINT*); + + auto GetDpiForMonitor = reinterpret_cast( + GetProcAddress(GetModuleHandle(TEXT("shcore")), + "GetDpiForMonitor")); + + if (GetDpiForMonitor) { + auto monitor = MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY); + if (GetDpiForMonitor(monitor, 0, &dpi_x, &dpi_y) == S_OK) + return; + } + + HDC hdc = GetDC(NULL); + dpi_x = GetDeviceCaps(hdc, LOGPIXELSX); + dpi_y = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + } + + template T X(T value) const { return ScaleForDpi(value, dpi_x); } + template T Y(T value) const { return ScaleForDpi(value, dpi_y); } +}; + +} // namespace brightray diff --git a/brightray/browser/win/win32_desktop_notifications/desktop_notification_controller.cc b/brightray/browser/win/win32_desktop_notifications/desktop_notification_controller.cc new file mode 100644 index 000000000000..68068a6de26e --- /dev/null +++ b/brightray/browser/win/win32_desktop_notifications/desktop_notification_controller.cc @@ -0,0 +1,404 @@ +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include "browser/win/win32_desktop_notifications/desktop_notification_controller.h" +#include +#include +#include +#include "browser/win/win32_desktop_notifications/common.h" +#include "browser/win/win32_desktop_notifications/toast.h" + +using std::make_shared; +using std::shared_ptr; + +namespace brightray { + +HBITMAP CopyBitmap(HBITMAP bitmap) { + HBITMAP ret = NULL; + + BITMAP bm; + if (bitmap && GetObject(bitmap, sizeof(bm), &bm)) { + HDC hdc_screen = GetDC(NULL); + ret = CreateCompatibleBitmap(hdc_screen, bm.bmWidth, bm.bmHeight); + ReleaseDC(NULL, hdc_screen); + + if (ret) { + HDC hdc_src = CreateCompatibleDC(NULL); + HDC hdc_dst = CreateCompatibleDC(NULL); + SelectBitmap(hdc_src, bitmap); + SelectBitmap(hdc_dst, ret); + BitBlt(hdc_dst, 0, 0, bm.bmWidth, bm.bmHeight, + hdc_src, 0, 0, SRCCOPY); + DeleteDC(hdc_dst); + DeleteDC(hdc_src); + } + } + + return ret; +} + +HINSTANCE DesktopNotificationController::RegisterWndClasses() { + // We keep a static `module` variable which serves a dual purpose: + // 1. Stores the HINSTANCE where the window classes are registered, + // which can be passed to `CreateWindow` + // 2. Indicates whether we already attempted the registration so that + // we don't do it twice (we don't retry even if registration fails, + // as there is no point). + static HMODULE module = NULL; + + if (!module) { + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(&RegisterWndClasses), + &module)) { + Toast::Register(module); + + WNDCLASSEX wc = { sizeof(wc) }; + wc.lpfnWndProc = &WndProc; + wc.lpszClassName = class_name_; + wc.cbWndExtra = sizeof(DesktopNotificationController*); + wc.hInstance = module; + + RegisterClassEx(&wc); + } + } + + return module; +} + +DesktopNotificationController::DesktopNotificationController( + unsigned maximum_toasts) { + instances_.reserve(maximum_toasts); +} + +DesktopNotificationController::~DesktopNotificationController() { + for (auto&& inst : instances_) DestroyToast(inst); + if (hwnd_controller_) DestroyWindow(hwnd_controller_); + ClearAssets(); +} + +LRESULT CALLBACK DesktopNotificationController::WndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + switch (message) { + case WM_CREATE: + { + auto& cs = reinterpret_cast(lparam); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)cs->lpCreateParams); + } + break; + + case WM_TIMER: + if (wparam == TimerID_Animate) { + Get(hwnd)->AnimateAll(); + } + return 0; + + case WM_DISPLAYCHANGE: + { + auto inst = Get(hwnd); + inst->ClearAssets(); + inst->AnimateAll(); + } + break; + + case WM_SETTINGCHANGE: + if (wparam == SPI_SETWORKAREA) { + Get(hwnd)->AnimateAll(); + } + break; + } + + return DefWindowProc(hwnd, message, wparam, lparam); +} + +void DesktopNotificationController::StartAnimation() { + _ASSERT(hwnd_controller_); + + if (!is_animating_ && hwnd_controller_) { + // NOTE: 15ms is shorter than what we'd need for 60 fps, but since + // the timer is not accurate we must request a higher frame rate + // to get at least 60 + + SetTimer(hwnd_controller_, TimerID_Animate, 15, nullptr); + is_animating_ = true; + } +} + +HFONT DesktopNotificationController::GetCaptionFont() { + InitializeFonts(); + return caption_font_; +} + +HFONT DesktopNotificationController::GetBodyFont() { + InitializeFonts(); + return body_font_; +} + +void DesktopNotificationController::InitializeFonts() { + if (!body_font_) { + NONCLIENTMETRICS metrics = { sizeof(metrics) }; + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) { + auto baseHeight = metrics.lfMessageFont.lfHeight; + + HDC hdc = GetDC(NULL); + auto dpi_y = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + + metrics.lfMessageFont.lfHeight = + (LONG)ScaleForDpi(baseHeight * 1.1f, dpi_y); + body_font_ = CreateFontIndirect(&metrics.lfMessageFont); + + if (caption_font_) DeleteFont(caption_font_); + metrics.lfMessageFont.lfHeight = + (LONG)ScaleForDpi(baseHeight * 1.4f, dpi_y); + caption_font_ = CreateFontIndirect(&metrics.lfMessageFont); + } + } +} + +void DesktopNotificationController::ClearAssets() { + if (caption_font_) { DeleteFont(caption_font_); caption_font_ = NULL; } + if (body_font_) { DeleteFont(body_font_); body_font_ = NULL; } +} + +void DesktopNotificationController::AnimateAll() { + // NOTE: This function refreshes position and size of all toasts according + // to all current conditions. Animation time is only one of the variables + // influencing them. Screen resolution is another. + + bool keep_animating = false; + + if (!instances_.empty()) { + RECT work_area; + if (SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0)) { + ScreenMetrics metrics; + POINT origin = { work_area.right, + work_area.bottom - metrics.Y(toast_margin_) }; + + auto hdwp = + BeginDeferWindowPos(static_cast(instances_.size())); + + for (auto&& inst : instances_) { + if (!inst.hwnd) continue; + + auto notification = Toast::Get(inst.hwnd); + hdwp = notification->Animate(hdwp, origin); + if (!hdwp) break; + keep_animating |= notification->IsAnimationActive(); + } + + if (hdwp) EndDeferWindowPos(hdwp); + } + } + + if (!keep_animating) { + _ASSERT(hwnd_controller_); + if (hwnd_controller_) KillTimer(hwnd_controller_, TimerID_Animate); + is_animating_ = false; + } + + // Purge dismissed notifications and collapse the stack between + // items which are highlighted + if (!instances_.empty()) { + auto is_alive = [](ToastInstance& inst) { + return inst.hwnd && IsWindowVisible(inst.hwnd); + }; + + auto is_highlighted = [](ToastInstance& inst) { + return inst.hwnd && Toast::Get(inst.hwnd)->IsHighlighted(); + }; + + for (auto it = instances_.begin();; ++it) { + // find next highlighted item + auto it2 = find_if(it, instances_.end(), is_highlighted); + + // collapse the stack in front of the highlighted item + it = stable_partition(it, it2, is_alive); + + // purge the dead items + for_each(it, it2, [this](auto&& inst) { DestroyToast(inst); }); + + if (it2 == instances_.end()) { + instances_.erase(it, it2); + break; + } + + it = move(it2); + } + } + + // Set new toast positions + if (!instances_.empty()) { + ScreenMetrics metrics; + auto margin = metrics.Y(toast_margin_); + + int target_pos = 0; + for (auto&& inst : instances_) { + if (inst.hwnd) { + auto toast = Toast::Get(inst.hwnd); + + if (toast->IsHighlighted()) + target_pos = toast->GetVerticalPosition(); + else + toast->SetVerticalPosition(target_pos); + + target_pos += toast->GetHeight() + margin; + } + } + } + + // Create new toasts from the queue + CheckQueue(); +} + +DesktopNotificationController::Notification + DesktopNotificationController::AddNotification( + std::wstring caption, std::wstring body_text, HBITMAP image) { + NotificationLink data(this); + + data->caption = move(caption); + data->body_text = move(body_text); + data->image = CopyBitmap(image); + + // Enqueue new notification + Notification ret { *queue_.insert(queue_.end(), move(data)) }; + CheckQueue(); + return ret; +} + +void DesktopNotificationController::CloseNotification( + Notification& notification) { + // Remove it from the queue + auto it = find(queue_.begin(), queue_.end(), notification.data_); + if (it != queue_.end()) { + queue_.erase(it); + this->OnNotificationClosed(notification); + return; + } + + // Dismiss active toast + auto hwnd = GetToast(notification.data_.get()); + if (hwnd) { + auto toast = Toast::Get(hwnd); + toast->Dismiss(); + } +} + +void DesktopNotificationController::CheckQueue() { + while (instances_.size() < instances_.capacity() && !queue_.empty()) { + CreateToast(move(queue_.front())); + queue_.pop_front(); + } +} + +void DesktopNotificationController::CreateToast(NotificationLink&& data) { + auto hinstance = RegisterWndClasses(); + auto hwnd = Toast::Create(hinstance, data); + if (hwnd) { + int toast_pos = 0; + if (!instances_.empty()) { + auto& item = instances_.back(); + _ASSERT(item.hwnd); + + ScreenMetrics scr; + auto toast = Toast::Get(item.hwnd); + toast_pos = toast->GetVerticalPosition() + + toast->GetHeight() + + scr.Y(toast_margin_); + } + + instances_.push_back({ hwnd, move(data) }); + + if (!hwnd_controller_) { + // NOTE: We cannot use a message-only window because we need to + // receive system notifications + hwnd_controller_ = CreateWindow(class_name_, nullptr, 0, + 0, 0, 0, 0, + NULL, NULL, hinstance, this); + } + + auto toast = Toast::Get(hwnd); + toast->PopUp(toast_pos); + } +} + +HWND DesktopNotificationController::GetToast( + const NotificationData* data) const { + auto it = find_if(instances_.cbegin(), instances_.cend(), + [data](auto&& inst) { + auto toast = Toast::Get(inst.hwnd); + return data == toast->GetNotification().get(); + }); + + return (it != instances_.cend()) ? it->hwnd : NULL; +} + +void DesktopNotificationController::DestroyToast(ToastInstance& inst) { + if (inst.hwnd) { + auto data = Toast::Get(inst.hwnd)->GetNotification(); + + DestroyWindow(inst.hwnd); + inst.hwnd = NULL; + + Notification notification(data); + OnNotificationClosed(notification); + } +} + + +DesktopNotificationController::Notification::Notification( + const shared_ptr& data) : + data_(data) { + _ASSERT(data != nullptr); +} + +bool DesktopNotificationController::Notification::operator==( + const Notification& other) const { + return data_ == other.data_; +} + +void DesktopNotificationController::Notification::Close() { + // No business calling this when not pointing to a valid instance + _ASSERT(data_); + + if (data_->controller) + data_->controller->CloseNotification(*this); +} + +void DesktopNotificationController::Notification::Set( + std::wstring caption, std::wstring body_text, HBITMAP image) { + // No business calling this when not pointing to a valid instance + _ASSERT(data_); + + // Do nothing when the notification has been closed + if (!data_->controller) + return; + + if (data_->image) DeleteBitmap(data_->image); + + data_->caption = move(caption); + data_->body_text = move(body_text); + data_->image = CopyBitmap(image); + + auto hwnd = data_->controller->GetToast(data_.get()); + if (hwnd) { + auto toast = Toast::Get(hwnd); + toast->ResetContents(); + } + + // Change of contents can affect size and position of all toasts + data_->controller->StartAnimation(); +} + + +DesktopNotificationController::NotificationLink::NotificationLink( + DesktopNotificationController* controller) : + shared_ptr(make_shared()) { + get()->controller = controller; +} + +DesktopNotificationController::NotificationLink::~NotificationLink() { + auto p = get(); + if (p) p->controller = nullptr; +} + +} // namespace brightray diff --git a/brightray/browser/win/win32_desktop_notifications/desktop_notification_controller.h b/brightray/browser/win/win32_desktop_notifications/desktop_notification_controller.h new file mode 100644 index 000000000000..643a61f5331e --- /dev/null +++ b/brightray/browser/win/win32_desktop_notifications/desktop_notification_controller.h @@ -0,0 +1,106 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace brightray { + +struct NotificationData; + +class DesktopNotificationController { + public: + explicit DesktopNotificationController(unsigned maximum_toasts = 3); + ~DesktopNotificationController(); + + class Notification; + Notification AddNotification(std::wstring caption, std::wstring body_text, + HBITMAP image); + void CloseNotification(Notification& notification); + + // Event handlers -- override to receive the events + private: + virtual void OnNotificationClosed(Notification& notification) {} + virtual void OnNotificationClicked(Notification& notification) {} + virtual void OnNotificationDismissed(Notification& notification) {} + + private: + static HINSTANCE RegisterWndClasses(); + void StartAnimation(); + HFONT GetCaptionFont(); + HFONT GetBodyFont(); + + private: + enum TimerID { + TimerID_Animate = 1 + }; + + template + static constexpr T toast_margin_ = 20; + + // Wrapper around `NotificationData` which makes sure that + // the `controller` member is cleared when the controller object + // stops tracking the notification + struct NotificationLink : std::shared_ptr { + explicit NotificationLink(DesktopNotificationController* controller); + ~NotificationLink(); + + NotificationLink(NotificationLink&&) = default; + NotificationLink(const NotificationLink&) = delete; + NotificationLink& operator=(NotificationLink&&) = default; + }; + + struct ToastInstance { + HWND hwnd; + NotificationLink data; + }; + + class Toast; + + static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, + WPARAM wparam, LPARAM lparam); + static DesktopNotificationController* Get(HWND hwnd) { + return reinterpret_cast( + GetWindowLongPtr(hwnd, 0)); + } + + DesktopNotificationController( + const DesktopNotificationController&) = delete; + + void InitializeFonts(); + void ClearAssets(); + void AnimateAll(); + void CheckQueue(); + void CreateToast(NotificationLink&& data); + HWND GetToast(const NotificationData* data) const; + void DestroyToast(ToastInstance& inst); + + private: + static constexpr const TCHAR class_name_[] = + TEXT("DesktopNotificationController"); + + HWND hwnd_controller_ = NULL; + HFONT caption_font_ = NULL, body_font_ = NULL; + std::vector instances_; + std::deque queue_; + bool is_animating_ = false; +}; + +class DesktopNotificationController::Notification { + public: + Notification() = default; + explicit Notification(const std::shared_ptr& data); + + bool operator==(const Notification& other) const; + + void Close(); + void Set(std::wstring caption, std::wstring body_text, HBITMAP image); + + private: + std::shared_ptr data_; + + friend class DesktopNotificationController; +}; + +} // namespace brightray diff --git a/brightray/browser/win/win32_desktop_notifications/toast.cc b/brightray/browser/win/win32_desktop_notifications/toast.cc new file mode 100644 index 000000000000..1ab968c3ef0b --- /dev/null +++ b/brightray/browser/win/win32_desktop_notifications/toast.cc @@ -0,0 +1,822 @@ +#define NOMINMAX +#include "browser/win/win32_desktop_notifications/toast.h" +#include +#include +#include +#include "browser/win/win32_desktop_notifications/common.h" + +#pragma comment(lib, "msimg32.lib") +#pragma comment(lib, "uxtheme.lib") + +using std::min; +using std::shared_ptr; + +namespace brightray { + +static COLORREF GetAccentColor() { + bool success = false; + if (IsAppThemed()) { + HKEY hkey; + if (RegOpenKeyEx(HKEY_CURRENT_USER, + TEXT("SOFTWARE\\Microsoft\\Windows\\DWM"), 0, + KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) { + COLORREF color; + DWORD type, size; + if (RegQueryValueEx(hkey, TEXT("AccentColor"), nullptr, + &type, + reinterpret_cast(&color), + &(size = sizeof(color))) == ERROR_SUCCESS && + type == REG_DWORD) { + // convert from RGBA + color = RGB(GetRValue(color), + GetGValue(color), + GetBValue(color)); + success = true; + } else if ( + RegQueryValueEx(hkey, TEXT("ColorizationColor"), nullptr, + &type, + reinterpret_cast(&color), + &(size = sizeof(color))) == ERROR_SUCCESS && + type == REG_DWORD) { + // convert from BGRA + color = RGB(GetBValue(color), + GetGValue(color), + GetRValue(color)); + success = true; + } + + RegCloseKey(hkey); + + if (success) return color; + } + } + + return GetSysColor(COLOR_ACTIVECAPTION); +} + +// Stretches a bitmap to the specified size, preserves alpha channel +static HBITMAP StretchBitmap(HBITMAP bitmap, unsigned width, unsigned height) { + // We use StretchBlt for the scaling, but that discards the alpha channel. + // So we first create a separate grayscale bitmap from the alpha channel, + // scale that separately, and copy it back to the scaled color bitmap. + + BITMAP bm; + if (!GetObject(bitmap, sizeof(bm), &bm)) + return NULL; + + if (width == 0 || height == 0) + return NULL; + + HBITMAP result_bitmap = NULL; + + HDC hdc_screen = GetDC(NULL); + + HBITMAP alpha_src_bitmap; + { + BITMAPINFOHEADER bmi = { sizeof(BITMAPINFOHEADER) }; + bmi.biWidth = bm.bmWidth; + bmi.biHeight = bm.bmHeight; + bmi.biPlanes = bm.bmPlanes; + bmi.biBitCount = bm.bmBitsPixel; + bmi.biCompression = BI_RGB; + + void* alpha_src_bits; + alpha_src_bitmap = + CreateDIBSection(NULL, reinterpret_cast(&bmi), + DIB_RGB_COLORS, &alpha_src_bits, NULL, 0); + + if (alpha_src_bitmap) { + if (GetDIBits(hdc_screen, bitmap, 0, 0, 0, + reinterpret_cast(&bmi), + DIB_RGB_COLORS) && + bmi.biSizeImage > 0 && + (bmi.biSizeImage % 4) == 0) { + auto buf = reinterpret_cast( + _aligned_malloc(bmi.biSizeImage, sizeof(DWORD))); + + if (buf) { + GetDIBits(hdc_screen, bitmap, 0, bm.bmHeight, buf, + reinterpret_cast(&bmi), + DIB_RGB_COLORS); + + const DWORD *src = reinterpret_cast(buf); + const DWORD *end = + reinterpret_cast(buf + bmi.biSizeImage); + + BYTE* dest = reinterpret_cast(alpha_src_bits); + + for (; src != end; ++src, ++dest) { + BYTE a = *src >> 24; + *dest++ = a; + *dest++ = a; + *dest++ = a; + } + + _aligned_free(buf); + } + } + } + } + + if (alpha_src_bitmap) { + BITMAPINFOHEADER bmi = { sizeof(BITMAPINFOHEADER) }; + bmi.biWidth = width; + bmi.biHeight = height; + bmi.biPlanes = 1; + bmi.biBitCount = 32; + bmi.biCompression = BI_RGB; + + void* color_bits; + auto color_bitmap = + CreateDIBSection(NULL, reinterpret_cast(&bmi), + DIB_RGB_COLORS, &color_bits, NULL, 0); + + void* alpha_bits; + auto alpha_bitmap = + CreateDIBSection(NULL, reinterpret_cast(&bmi), + DIB_RGB_COLORS, &alpha_bits, NULL, 0); + + HDC hdc = CreateCompatibleDC(NULL); + HDC hdc_src = CreateCompatibleDC(NULL); + + if (color_bitmap && alpha_bitmap && hdc && hdc_src) { + SetStretchBltMode(hdc, HALFTONE); + + // resize color channels + SelectObject(hdc, color_bitmap); + SelectObject(hdc_src, bitmap); + StretchBlt(hdc, 0, 0, width, height, + hdc_src, 0, 0, bm.bmWidth, bm.bmHeight, + SRCCOPY); + + // resize alpha channel + SelectObject(hdc, alpha_bitmap); + SelectObject(hdc_src, alpha_src_bitmap); + StretchBlt(hdc, 0, 0, width, height, + hdc_src, 0, 0, bm.bmWidth, bm.bmHeight, + SRCCOPY); + + // flush before touching the bits + GdiFlush(); + + // apply the alpha channel + auto dest = reinterpret_cast(color_bits); + auto src = reinterpret_cast(alpha_bits); + auto end = src + (width * height * 4); + while (src != end) { + dest[3] = src[0]; + dest += 4; + src += 4; + } + + // create the resulting bitmap + result_bitmap = CreateDIBitmap(hdc_screen, &bmi, CBM_INIT, + color_bits, + reinterpret_cast(&bmi), + DIB_RGB_COLORS); + } + + if (hdc_src) DeleteDC(hdc_src); + if (hdc) DeleteDC(hdc); + + if (alpha_bitmap) DeleteObject(alpha_bitmap); + if (color_bitmap) DeleteObject(color_bitmap); + + DeleteObject(alpha_src_bitmap); + } + + ReleaseDC(NULL, hdc_screen); + + return result_bitmap; +} + +DesktopNotificationController::Toast::Toast( + HWND hwnd, shared_ptr* data) : + hwnd_(hwnd), data_(*data) { + HDC hdc_screen = GetDC(NULL); + hdc_ = CreateCompatibleDC(hdc_screen); + ReleaseDC(NULL, hdc_screen); +} + +DesktopNotificationController::Toast::~Toast() { + DeleteDC(hdc_); + if (bitmap_) DeleteBitmap(bitmap_); + if (scaled_image_) DeleteBitmap(scaled_image_); +} + +void DesktopNotificationController::Toast::Register(HINSTANCE hinstance) { + WNDCLASSEX wc = { sizeof(wc) }; + wc.lpfnWndProc = &Toast::WndProc; + wc.lpszClassName = class_name_; + wc.cbWndExtra = sizeof(Toast*); + wc.hInstance = hinstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + + RegisterClassEx(&wc); +} + +LRESULT DesktopNotificationController::Toast::WndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + switch (message) { + case WM_CREATE: + { + auto& cs = reinterpret_cast(lparam); + auto data = + static_cast*>(cs->lpCreateParams); + auto inst = new Toast(hwnd, data); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)inst); + } + break; + + case WM_NCDESTROY: + delete Get(hwnd); + SetWindowLongPtr(hwnd, 0, 0); + return 0; + + case WM_MOUSEACTIVATE: + return MA_NOACTIVATE; + + case WM_TIMER: + if (wparam == TimerID_AutoDismiss) { + Get(hwnd)->AutoDismiss(); + } + return 0; + + case WM_LBUTTONDOWN: + { + auto inst = Get(hwnd); + + inst->Dismiss(); + + Notification notification(inst->data_); + if (inst->is_close_hot_) + inst->data_->controller->OnNotificationDismissed(notification); + else + inst->data_->controller->OnNotificationClicked(notification); + } + return 0; + + case WM_MOUSEMOVE: + { + auto inst = Get(hwnd); + if (!inst->is_highlighted_) { + inst->is_highlighted_ = true; + + TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, hwnd }; + TrackMouseEvent(&tme); + } + + POINT cursor = { GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; + inst->is_close_hot_ = + (PtInRect(&inst->close_button_rect_, cursor) != FALSE); + + if (!inst->is_non_interactive_) + inst->CancelDismiss(); + + inst->UpdateContents(); + } + return 0; + + case WM_MOUSELEAVE: + { + auto inst = Get(hwnd); + inst->is_highlighted_ = false; + inst->is_close_hot_ = false; + inst->UpdateContents(); + + if (!inst->ease_out_active_ && inst->ease_in_pos_ == 1.0f) + inst->ScheduleDismissal(); + + // Make sure stack collapse happens if needed + inst->data_->controller->StartAnimation(); + } + return 0; + + case WM_WINDOWPOSCHANGED: + { + auto& wp = reinterpret_cast(lparam); + if (wp->flags & SWP_HIDEWINDOW) { + if (!IsWindowVisible(hwnd)) + Get(hwnd)->is_highlighted_ = false; + } + } + break; + } + + return DefWindowProc(hwnd, message, wparam, lparam); +} + +HWND DesktopNotificationController::Toast::Create( + HINSTANCE hinstance, shared_ptr& data) { + return CreateWindowEx(WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOPMOST, + class_name_, nullptr, WS_POPUP, 0, 0, 0, 0, + NULL, NULL, hinstance, &data); +} + +void DesktopNotificationController::Toast::Draw() { + const COLORREF accent = GetAccentColor(); + + COLORREF back_color; + { + // base background color is 2/3 of accent + // highlighted adds a bit of intensity to every channel + + int h = is_highlighted_ ? (0xff / 20) : 0; + + back_color = RGB(min(0xff, (GetRValue(accent) * 2 / 3) + h), + min(0xff, (GetGValue(accent) * 2 / 3) + h), + min(0xff, (GetBValue(accent) * 2 / 3) + h)); + } + + const float back_luma = + (GetRValue(back_color) * 0.299f / 255) + + (GetGValue(back_color) * 0.587f / 255) + + (GetBValue(back_color) * 0.114f / 255); + + const struct { float r, g, b; } back_f = { + GetRValue(back_color) / 255.0f, + GetGValue(back_color) / 255.0f, + GetBValue(back_color) / 255.0f, + }; + + COLORREF fore_color, dimmed_color; + { + // based on the lightness of background, we draw foreground in light + // or dark shades of gray blended onto the background with slight + // transparency to avoid sharp contrast + + constexpr float alpha = 0.9f; + constexpr float intensity_light[] = { (1.0f * alpha), (0.8f * alpha) }; + constexpr float intensity_dark[] = { (0.1f * alpha), (0.3f * alpha) }; + + // select foreground intensity values (light or dark) + auto& i = (back_luma < 0.6f) ? intensity_light : intensity_dark; + + float r, g, b; + + r = i[0] + back_f.r * (1 - alpha); + g = i[0] + back_f.g * (1 - alpha); + b = i[0] + back_f.b * (1 - alpha); + fore_color = RGB(r * 0xff, g * 0xff, b * 0xff); + + r = i[1] + back_f.r * (1 - alpha); + g = i[1] + back_f.g * (1 - alpha); + b = i[1] + back_f.b * (1 - alpha); + dimmed_color = RGB(r * 0xff, g * 0xff, b * 0xff); + } + + // Draw background + { + auto brush = CreateSolidBrush(back_color); + + RECT rc = { 0, 0, toast_size_.cx, toast_size_.cy }; + FillRect(hdc_, &rc, brush); + + DeleteBrush(brush); + } + + SetBkMode(hdc_, TRANSPARENT); + + const auto close = L'\x2715'; + auto caption_font = data_->controller->GetCaptionFont(); + auto body_font = data_->controller->GetBodyFont(); + + TEXTMETRIC tm_cap; + SelectFont(hdc_, caption_font); + GetTextMetrics(hdc_, &tm_cap); + + auto text_offset_x = margin_.cx; + + BITMAP image_info = {}; + if (scaled_image_) { + GetObject(scaled_image_, sizeof(image_info), &image_info); + + text_offset_x += margin_.cx + image_info.bmWidth; + } + + // calculate close button rect + POINT close_pos; + { + SIZE extent = {}; + GetTextExtentPoint32W(hdc_, &close, 1, &extent); + + close_button_rect_.right = toast_size_.cx; + close_button_rect_.top = 0; + + close_pos.x = close_button_rect_.right - margin_.cy - extent.cx; + close_pos.y = close_button_rect_.top + margin_.cy; + + close_button_rect_.left = close_pos.x - margin_.cy; + close_button_rect_.bottom = close_pos.y + extent.cy + margin_.cy; + } + + // image + if (scaled_image_) { + HDC hdc_image = CreateCompatibleDC(NULL); + SelectBitmap(hdc_image, scaled_image_); + BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + AlphaBlend(hdc_, margin_.cx, margin_.cy, + image_info.bmWidth, image_info.bmHeight, + hdc_image, 0, 0, + image_info.bmWidth, image_info.bmHeight, + blend); + DeleteDC(hdc_image); + } + + // caption + { + RECT rc = { + text_offset_x, + margin_.cy, + close_button_rect_.left, + toast_size_.cy + }; + + SelectFont(hdc_, caption_font); + SetTextColor(hdc_, fore_color); + DrawText(hdc_, data_->caption.data(), (UINT)data_->caption.length(), + &rc, DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX); + } + + // body text + if (!data_->body_text.empty()) { + RECT rc = { + text_offset_x, + 2 * margin_.cy + tm_cap.tmAscent, + toast_size_.cx - margin_.cx, + toast_size_.cy - margin_.cy + }; + + SelectFont(hdc_, body_font); + SetTextColor(hdc_, dimmed_color); + DrawText(hdc_, data_->body_text.data(), (UINT)data_->body_text.length(), + &rc, + DT_LEFT | DT_WORDBREAK | DT_NOPREFIX | + DT_END_ELLIPSIS | DT_EDITCONTROL); + } + + // close button + { + SelectFont(hdc_, caption_font); + SetTextColor(hdc_, is_close_hot_ ? fore_color : dimmed_color); + ExtTextOut(hdc_, close_pos.x, close_pos.y, 0, nullptr, + &close, 1, nullptr); + } + + is_content_updated_ = true; +} + +void DesktopNotificationController::Toast::Invalidate() { + is_content_updated_ = false; +} + +bool DesktopNotificationController::Toast::IsRedrawNeeded() const { + return !is_content_updated_; +} + +void DesktopNotificationController::Toast::UpdateBufferSize() { + if (hdc_) { + SIZE new_size; + { + TEXTMETRIC tm_cap = {}; + HFONT font = data_->controller->GetCaptionFont(); + if (font) { + SelectFont(hdc_, font); + if (!GetTextMetrics(hdc_, &tm_cap)) return; + } + + TEXTMETRIC tm_body = {}; + font = data_->controller->GetBodyFont(); + if (font) { + SelectFont(hdc_, font); + if (!GetTextMetrics(hdc_, &tm_body)) return; + } + + this->margin_ = { tm_cap.tmAveCharWidth * 2, tm_cap.tmAscent / 2 }; + + new_size.cx = + margin_.cx + (32 * tm_cap.tmAveCharWidth) + margin_.cx; + new_size.cy = + margin_.cy + (tm_cap.tmHeight) + margin_.cy; + + if (!data_->body_text.empty()) + new_size.cy += margin_.cy + (3 * tm_body.tmHeight); + + if (data_->image) { + BITMAP bm; + if (GetObject(data_->image, sizeof(bm), &bm)) { + // cap the image size + const int max_dim_size = 80; + + auto width = bm.bmWidth; + auto height = bm.bmHeight; + if (width < height) { + if (height > max_dim_size) { + width = width * max_dim_size / height; + height = max_dim_size; + } + } else { + if (width > max_dim_size) { + height = height * max_dim_size / width; + width = max_dim_size; + } + } + + ScreenMetrics scr; + SIZE image_draw_size = { scr.X(width), scr.Y(height) }; + + new_size.cx += image_draw_size.cx + margin_.cx; + + auto height_with_image = + margin_.cy + (image_draw_size.cy) + margin_.cy; + + if (new_size.cy < height_with_image) + new_size.cy = height_with_image; + + UpdateScaledImage(image_draw_size); + } + } + } + + if (new_size.cx != this->toast_size_.cx || + new_size.cy != this->toast_size_.cy) { + HDC hdc_screen = GetDC(NULL); + auto new_bitmap = CreateCompatibleBitmap(hdc_screen, + new_size.cx, new_size.cy); + ReleaseDC(NULL, hdc_screen); + + if (new_bitmap) { + if (SelectBitmap(hdc_, new_bitmap)) { + RECT dirty1 = {}, dirty2 = {}; + if (toast_size_.cx < new_size.cx) { + dirty1 = { toast_size_.cx, 0, + new_size.cx, toast_size_.cy }; + } + if (toast_size_.cy < new_size.cy) { + dirty2 = { 0, toast_size_.cy, + new_size.cx, new_size.cy }; + } + + if (this->bitmap_) DeleteBitmap(this->bitmap_); + this->bitmap_ = new_bitmap; + this->toast_size_ = new_size; + + Invalidate(); + + // Resize also the DWM buffer to prevent flicker during + // window resizing. Make sure any existing data is not + // overwritten by marking the dirty region. + { + POINT origin = { 0, 0 }; + + UPDATELAYEREDWINDOWINFO ulw; + ulw.cbSize = sizeof(ulw); + ulw.hdcDst = NULL; + ulw.pptDst = nullptr; + ulw.psize = &toast_size_; + ulw.hdcSrc = hdc_; + ulw.pptSrc = &origin; + ulw.crKey = 0; + ulw.pblend = nullptr; + ulw.dwFlags = 0; + ulw.prcDirty = &dirty1; + auto b1 = UpdateLayeredWindowIndirect(hwnd_, &ulw); + ulw.prcDirty = &dirty2; + auto b2 = UpdateLayeredWindowIndirect(hwnd_, &ulw); + _ASSERT(b1 && b2); + } + + return; + } + + DeleteBitmap(new_bitmap); + } + } + } +} + +void DesktopNotificationController::Toast::UpdateScaledImage(const SIZE& size) { + BITMAP bm; + if (!GetObject(scaled_image_, sizeof(bm), &bm) || + bm.bmWidth != size.cx || + bm.bmHeight != size.cy) { + if (scaled_image_) DeleteBitmap(scaled_image_); + scaled_image_ = StretchBitmap(data_->image, size.cx, size.cy); + } +} + +void DesktopNotificationController::Toast::UpdateContents() { + Draw(); + + if (IsWindowVisible(hwnd_)) { + RECT rc; + GetWindowRect(hwnd_, &rc); + POINT origin = { 0, 0 }; + SIZE size = { rc.right - rc.left, rc.bottom - rc.top }; + UpdateLayeredWindow(hwnd_, NULL, nullptr, &size, + hdc_, &origin, 0, nullptr, 0); + } +} + +void DesktopNotificationController::Toast::Dismiss() { + if (!is_non_interactive_) { + // Set a flag to prevent further interaction. We don't disable the HWND + // because we still want to receive mouse move messages in order to keep + // the toast under the cursor and not collapse it while dismissing. + is_non_interactive_ = true; + + AutoDismiss(); + } +} + +void DesktopNotificationController::Toast::AutoDismiss() { + KillTimer(hwnd_, TimerID_AutoDismiss); + StartEaseOut(); +} + +void DesktopNotificationController::Toast::CancelDismiss() { + KillTimer(hwnd_, TimerID_AutoDismiss); + ease_out_active_ = false; + ease_out_pos_ = 0; +} + +void DesktopNotificationController::Toast::ScheduleDismissal() { + SetTimer(hwnd_, TimerID_AutoDismiss, 4000, nullptr); +} + +void DesktopNotificationController::Toast::ResetContents() { + if (scaled_image_) { + DeleteBitmap(scaled_image_); + scaled_image_ = NULL; + } + + Invalidate(); +} + +void DesktopNotificationController::Toast::PopUp(int y) { + vertical_pos_target_ = vertical_pos_ = y; + StartEaseIn(); +} + +void DesktopNotificationController::Toast::SetVerticalPosition(int y) { + // Don't restart animation if current target is the same + if (y == vertical_pos_target_) + return; + + // Make sure the new animation's origin is at the current position + vertical_pos_ += static_cast( + (vertical_pos_target_ - vertical_pos_) * stack_collapse_pos_); + + // Set new target position and start the animation + vertical_pos_target_ = y; + stack_collapse_start_ = GetTickCount(); + data_->controller->StartAnimation(); +} + +HDWP DesktopNotificationController::Toast::Animate( + HDWP hdwp, const POINT& origin) { + UpdateBufferSize(); + + if (IsRedrawNeeded()) + Draw(); + + POINT src_origin = { 0, 0 }; + + UPDATELAYEREDWINDOWINFO ulw; + ulw.cbSize = sizeof(ulw); + ulw.hdcDst = NULL; + ulw.pptDst = nullptr; + ulw.psize = nullptr; + ulw.hdcSrc = hdc_; + ulw.pptSrc = &src_origin; + ulw.crKey = 0; + ulw.pblend = nullptr; + ulw.dwFlags = 0; + ulw.prcDirty = nullptr; + + POINT pt = { 0, 0 }; + SIZE size = { 0, 0 }; + BLENDFUNCTION blend; + UINT dwpFlags = SWP_NOACTIVATE | SWP_SHOWWINDOW | + SWP_NOREDRAW | SWP_NOCOPYBITS; + + auto ease_in_pos = AnimateEaseIn(); + auto ease_out_pos = AnimateEaseOut(); + auto stack_collapse_pos = AnimateStackCollapse(); + + auto y_offset = (vertical_pos_target_ - vertical_pos_) * stack_collapse_pos; + + size.cx = static_cast(toast_size_.cx * ease_in_pos); + size.cy = toast_size_.cy; + + pt.x = origin.x - size.cx; + pt.y = static_cast(origin.y - vertical_pos_ - y_offset - size.cy); + + ulw.pptDst = &pt; + ulw.psize = &size; + + if (ease_in_active_ && ease_in_pos == 1.0f) { + ease_in_active_ = false; + ScheduleDismissal(); + } + + this->ease_in_pos_ = ease_in_pos; + this->stack_collapse_pos_ = stack_collapse_pos; + + if (ease_out_pos != this->ease_out_pos_) { + blend.BlendOp = AC_SRC_OVER; + blend.BlendFlags = 0; + blend.SourceConstantAlpha = (BYTE)(255 * (1.0f - ease_out_pos)); + blend.AlphaFormat = 0; + + ulw.pblend = &blend; + ulw.dwFlags = ULW_ALPHA; + + this->ease_out_pos_ = ease_out_pos; + + if (ease_out_pos == 1.0f) { + ease_out_active_ = false; + + dwpFlags &= ~SWP_SHOWWINDOW; + dwpFlags |= SWP_HIDEWINDOW; + } + } + + if (stack_collapse_pos == 1.0f) { + vertical_pos_ = vertical_pos_target_; + } + + // `UpdateLayeredWindowIndirect` updates position, size, and transparency. + // `DeferWindowPos` updates z-order, and also position and size in case + // ULWI fails, which can happen when one of the dimensions is zero (e.g. + // at the beginning of ease-in). + + auto ulw_result = UpdateLayeredWindowIndirect(hwnd_, &ulw); + hdwp = DeferWindowPos(hdwp, hwnd_, HWND_TOPMOST, + pt.x, pt.y, size.cx, size.cy, dwpFlags); + return hdwp; +} + +void DesktopNotificationController::Toast::StartEaseIn() { + _ASSERT(!ease_in_active_); + ease_in_start_ = GetTickCount(); + ease_in_active_ = true; + data_->controller->StartAnimation(); +} + +void DesktopNotificationController::Toast::StartEaseOut() { + _ASSERT(!ease_out_active_); + ease_out_start_ = GetTickCount(); + ease_out_active_ = true; + data_->controller->StartAnimation(); +} + +bool DesktopNotificationController::Toast::IsStackCollapseActive() const { + return (vertical_pos_ != vertical_pos_target_); +} + +float DesktopNotificationController::Toast::AnimateEaseIn() { + if (!ease_in_active_) + return ease_in_pos_; + + constexpr float duration = 500.0f; + float elapsed = GetTickCount() - ease_in_start_; + float time = std::min(duration, elapsed) / duration; + + // decelerating exponential ease + const float a = -8.0f; + auto pos = (std::exp(a * time) - 1.0f) / (std::exp(a) - 1.0f); + + return pos; +} + +float DesktopNotificationController::Toast::AnimateEaseOut() { + if (!ease_out_active_) + return ease_out_pos_; + + constexpr float duration = 120.0f; + float elapsed = GetTickCount() - ease_out_start_; + float time = std::min(duration, elapsed) / duration; + + // accelerating circle ease + auto pos = 1.0f - std::sqrt(1 - time * time); + + return pos; +} + +float DesktopNotificationController::Toast::AnimateStackCollapse() { + if (!IsStackCollapseActive()) + return stack_collapse_pos_; + + constexpr float duration = 500.0f; + float elapsed = GetTickCount() - stack_collapse_start_; + float time = std::min(duration, elapsed) / duration; + + // decelerating exponential ease + const float a = -8.0f; + auto pos = (std::exp(a * time) - 1.0f) / (std::exp(a) - 1.0f); + + return pos; +} + +} // namespace brightray diff --git a/brightray/browser/win/win32_desktop_notifications/toast.h b/brightray/browser/win/win32_desktop_notifications/toast.h new file mode 100644 index 000000000000..bb9bd9fa4aed --- /dev/null +++ b/brightray/browser/win/win32_desktop_notifications/toast.h @@ -0,0 +1,97 @@ +#pragma once +#include "browser/win/win32_desktop_notifications/desktop_notification_controller.h" + +namespace brightray { + +class DesktopNotificationController::Toast { + public: + static void Register(HINSTANCE hinstance); + static HWND Create(HINSTANCE hinstance, + std::shared_ptr& data); + static Toast* Get(HWND hwnd) { + return reinterpret_cast(GetWindowLongPtr(hwnd, 0)); + } + + static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, + WPARAM wparam, LPARAM lparam); + + const std::shared_ptr& GetNotification() const { + return data_; + } + + void ResetContents(); + + void Dismiss(); + + void PopUp(int y); + void SetVerticalPosition(int y); + int GetVerticalPosition() const { + return vertical_pos_target_; + } + int GetHeight() const { + return toast_size_.cy; + } + HDWP Animate(HDWP hdwp, const POINT& origin); + bool IsAnimationActive() const { + return ease_in_active_ || ease_out_active_ || IsStackCollapseActive(); + } + bool IsHighlighted() const { + _ASSERT(!(is_highlighted_ && !IsWindowVisible(hwnd_))); + return is_highlighted_; + } + + private: + enum TimerID { + TimerID_AutoDismiss = 1 + }; + + Toast(HWND hwnd, std::shared_ptr* data); + ~Toast(); + + void UpdateBufferSize(); + void UpdateScaledImage(const SIZE& size); + void Draw(); + void Invalidate(); + bool IsRedrawNeeded() const; + void UpdateContents(); + + void AutoDismiss(); + void CancelDismiss(); + void ScheduleDismissal(); + + void StartEaseIn(); + void StartEaseOut(); + bool IsStackCollapseActive() const; + + float AnimateEaseIn(); + float AnimateEaseOut(); + float AnimateStackCollapse(); + + private: + static constexpr const TCHAR class_name_[] = + TEXT("DesktopNotificationToast"); + + const HWND hwnd_; + HDC hdc_; + HBITMAP bitmap_ = NULL; + + const std::shared_ptr data_; // never null + + SIZE toast_size_ = {}; + SIZE margin_ = {}; + RECT close_button_rect_ = {}; + HBITMAP scaled_image_ = NULL; + + int vertical_pos_ = 0; + int vertical_pos_target_ = 0; + bool is_non_interactive_ = false; + bool ease_in_active_ = false; + bool ease_out_active_ = false; + bool is_content_updated_ = false; + bool is_highlighted_ = false; + bool is_close_hot_ = false; + DWORD ease_in_start_, ease_out_start_, stack_collapse_start_; + float ease_in_pos_ = 0, ease_out_pos_ = 0, stack_collapse_pos_ = 0; +}; + +} // namespace brightray diff --git a/brightray/browser/win/win32_notification.cc b/brightray/browser/win/win32_notification.cc new file mode 100644 index 000000000000..5b6093658aac --- /dev/null +++ b/brightray/browser/win/win32_notification.cc @@ -0,0 +1,58 @@ +#define WIN32_LEAN_AND_MEAN +#include "browser/win/win32_notification.h" +#include +#include "third_party/skia/include/core/SkBitmap.h" + +namespace brightray { + +void Win32Notification::Show( + const base::string16& title, const base::string16& msg, + const std::string& tag, const GURL& icon_url, + const SkBitmap& icon, const bool silent) { + auto presenter = static_cast(this->presenter()); + if (!presenter) return; + + HBITMAP image = NULL; + + if (!icon.drawsNothing()) { + if (icon.colorType() == kBGRA_8888_SkColorType) { + icon.lockPixels(); + + BITMAPINFOHEADER bmi = { sizeof(BITMAPINFOHEADER) }; + bmi.biWidth = icon.width(); + bmi.biHeight = -icon.height(); + bmi.biPlanes = 1; + bmi.biBitCount = 32; + bmi.biCompression = BI_RGB; + + HDC hdcScreen = GetDC(NULL); + image = CreateDIBitmap(hdcScreen, &bmi, CBM_INIT, icon.getPixels(), + reinterpret_cast(&bmi), + DIB_RGB_COLORS); + ReleaseDC(NULL, hdcScreen); + + icon.unlockPixels(); + } + } + + Win32Notification* existing = nullptr; + if (!tag.empty()) existing = presenter->GetNotificationObjectByTag(tag); + + if (existing) { + existing->tag_.clear(); + this->notification_ref_ = std::move(existing->notification_ref_); + this->notification_ref_.Set(title, msg, image); + } else { + this->notification_ref_ = presenter->AddNotification(title, msg, image); + } + + this->tag_ = tag; + + if (image) DeleteObject(image); +} + +void Win32Notification::Dismiss() { + notification_ref_.Close(); +} + +} // namespace brightray diff --git a/brightray/browser/win/win32_notification.h b/brightray/browser/win/win32_notification.h new file mode 100644 index 000000000000..9a418cd50371 --- /dev/null +++ b/brightray/browser/win/win32_notification.h @@ -0,0 +1,33 @@ +#pragma once +#include "browser/notification.h" +#include "browser/win/notification_presenter_win7.h" + +namespace brightray { + +class Win32Notification : public brightray::Notification { + public: + Win32Notification(NotificationDelegate* delegate, + NotificationPresenterWin7* presenter) : + Notification(delegate, presenter) { + } + void Show(const base::string16& title, const base::string16& msg, + const std::string& tag, const GURL& icon_url, + const SkBitmap& icon, const bool silent) override; + void Dismiss() override; + + const DesktopNotificationController::Notification& GetRef() const { + return notification_ref_; + } + + const std::string& GetTag() const { + return tag_; + } + + private: + DesktopNotificationController::Notification notification_ref_; + std::string tag_; + + DISALLOW_COPY_AND_ASSIGN(Win32Notification); +}; + +} // namespace brightray diff --git a/brightray/browser/win/windows_toast_notification.cc b/brightray/browser/win/windows_toast_notification.cc new file mode 100644 index 000000000000..0e0c9249c11e --- /dev/null +++ b/brightray/browser/win/windows_toast_notification.cc @@ -0,0 +1,423 @@ +// Copyright (c) 2015 Felix Rieseberg and Jason Poon +// . All rights reserved. +// Copyright (c) 2015 Ryan McShane and Brandon Smith +// +// Thanks to both of those folks mentioned above who first thought up a bunch of +// this code +// and released it as MIT to the world. + +#include "browser/win/windows_toast_notification.h" + +#include + +#include "base/strings/utf_string_conversions.h" +#include "browser/notification_delegate.h" +#include "browser/win/scoped_hstring.h" +#include "browser/win/notification_presenter_win.h" +#include "common/application_info.h" +#include "content/public/browser/browser_thread.h" + +using ABI::Windows::Data::Xml::Dom::IXmlAttribute; +using ABI::Windows::Data::Xml::Dom::IXmlDocument; +using ABI::Windows::Data::Xml::Dom::IXmlElement; +using ABI::Windows::Data::Xml::Dom::IXmlNamedNodeMap; +using ABI::Windows::Data::Xml::Dom::IXmlNode; +using ABI::Windows::Data::Xml::Dom::IXmlNodeList; +using ABI::Windows::Data::Xml::Dom::IXmlText; + +namespace brightray { + +namespace { + +bool GetAppUserModelId(ScopedHString* app_id) { + PWSTR current_app_id; + if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(¤t_app_id))) { + app_id->Reset(current_app_id); + CoTaskMemFree(current_app_id); + } else { + app_id->Reset(base::UTF8ToUTF16(GetApplicationName())); + } + return app_id->success(); +} + +} // namespace + +// static +ComPtr + WindowsToastNotification::toast_manager_; + +// static +ComPtr + WindowsToastNotification::toast_notifier_; + +// static +bool WindowsToastNotification::Initialize() { + // Just initialize, don't care if it fails or already initialized. + Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); + + ScopedHString toast_manager_str( + RuntimeClass_Windows_UI_Notifications_ToastNotificationManager); + if (!toast_manager_str.success()) + return false; + if (FAILED(Windows::Foundation::GetActivationFactory(toast_manager_str, + &toast_manager_))) + return false; + + ScopedHString app_id; + if (!GetAppUserModelId(&app_id)) + return false; + + return SUCCEEDED( + toast_manager_->CreateToastNotifierWithId(app_id, &toast_notifier_)); +} + +WindowsToastNotification::WindowsToastNotification( + NotificationDelegate* delegate, + NotificationPresenter* presenter) + : Notification(delegate, presenter) {} + +WindowsToastNotification::~WindowsToastNotification() { + // Remove the notification on exit. + if (toast_notification_) { + RemoveCallbacks(toast_notification_.Get()); + Dismiss(); + } +} + +void WindowsToastNotification::Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) { + auto presenter_win = static_cast(presenter()); + std::wstring icon_path = presenter_win->SaveIconToFilesystem(icon, icon_url); + + ComPtr toast_xml; + if (FAILED(GetToastXml(toast_manager_.Get(), title, msg, icon_path, silent, + &toast_xml))) { + NotificationFailed(); + return; + } + + ScopedHString toast_str( + RuntimeClass_Windows_UI_Notifications_ToastNotification); + if (!toast_str.success()) { + NotificationFailed(); + return; + } + + ComPtr + toast_factory; + if (FAILED(Windows::Foundation::GetActivationFactory(toast_str, + &toast_factory))) { + NotificationFailed(); + return; + } + + if (FAILED(toast_factory->CreateToastNotification(toast_xml.Get(), + &toast_notification_))) { + NotificationFailed(); + return; + } + + if (!SetupCallbacks(toast_notification_.Get())) { + NotificationFailed(); + return; + } + + if (FAILED(toast_notifier_->Show(toast_notification_.Get()))) { + NotificationFailed(); + return; + } + + delegate()->NotificationDisplayed(); +} + +void WindowsToastNotification::Dismiss() { + toast_notifier_->Hide(toast_notification_.Get()); +} + +bool WindowsToastNotification::GetToastXml( + ABI::Windows::UI::Notifications::IToastNotificationManagerStatics* + toastManager, + const std::wstring& title, + const std::wstring& msg, + const std::wstring& icon_path, + const bool silent, + IXmlDocument** toast_xml) { + ABI::Windows::UI::Notifications::ToastTemplateType template_type; + if (title.empty() || msg.empty()) { + // Single line toast. + template_type = + icon_path.empty() + ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01 + : ABI::Windows::UI::Notifications:: + ToastTemplateType_ToastImageAndText01; + if (FAILED(toast_manager_->GetTemplateContent(template_type, toast_xml))) + return false; + if (!SetXmlText(*toast_xml, title.empty() ? msg : title)) + return false; + } else { + // Title and body toast. + template_type = + icon_path.empty() + ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText02 + : ABI::Windows::UI::Notifications:: + ToastTemplateType_ToastImageAndText02; + if (FAILED(toastManager->GetTemplateContent(template_type, toast_xml))) + return false; + if (!SetXmlText(*toast_xml, title, msg)) + return false; + } + + // Configure the toast's notification sound + if (silent) { + if (FAILED(SetXmlAudioSilent(*toast_xml))) + return false; + } + + // Configure the toast's image + if (!icon_path.empty()) + return SetXmlImage(*toast_xml, icon_path); + + return true; +} + +bool WindowsToastNotification::SetXmlAudioSilent(IXmlDocument* doc) { + ScopedHString tag(L"toast"); + if (!tag.success()) + return false; + + ComPtr node_list; + if (FAILED(doc->GetElementsByTagName(tag, &node_list))) + return false; + + ComPtr root; + if (FAILED(node_list->Item(0, &root))) + return false; + + ComPtr audio_element; + ScopedHString audio_str(L"audio"); + if (FAILED(doc->CreateElement(audio_str, &audio_element))) + return false; + + ComPtr audio_node_tmp; + if (FAILED(audio_element.As(&audio_node_tmp))) + return false; + + // Append audio node to toast xml + ComPtr audio_node; + if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node))) + return false; + + // Create silent attribute + ComPtr attributes; + if (FAILED(audio_node->get_Attributes(&attributes))) + return false; + + ComPtr silent_attribute; + ScopedHString silent_str(L"silent"); + if (FAILED(doc->CreateAttribute(silent_str, &silent_attribute))) + return false; + + ComPtr silent_attribute_node; + if (FAILED(silent_attribute.As(&silent_attribute_node))) + return false; + + // Set silent attribute to true + ScopedHString silent_value(L"true"); + if (!silent_value.success()) + return false; + + ComPtr silent_text; + if (FAILED(doc->CreateTextNode(silent_value, &silent_text))) + return false; + + ComPtr silent_node; + if (FAILED(silent_text.As(&silent_node))) + return false; + + ComPtr child_node; + if (FAILED( + silent_attribute_node->AppendChild(silent_node.Get(), &child_node))) + return false; + + ComPtr silent_attribute_pnode; + return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(), + &silent_attribute_pnode)); +} + +bool WindowsToastNotification::SetXmlText(IXmlDocument* doc, + const std::wstring& text) { + ScopedHString tag; + ComPtr node_list; + if (!GetTextNodeList(&tag, doc, &node_list, 1)) + return false; + + ComPtr node; + if (FAILED(node_list->Item(0, &node))) + return false; + + return AppendTextToXml(doc, node.Get(), text); +} + +bool WindowsToastNotification::SetXmlText(IXmlDocument* doc, + const std::wstring& title, + const std::wstring& body) { + ScopedHString tag; + ComPtr node_list; + if (!GetTextNodeList(&tag, doc, &node_list, 2)) + return false; + + ComPtr node; + if (FAILED(node_list->Item(0, &node))) + return false; + + if (!AppendTextToXml(doc, node.Get(), title)) + return false; + + if (FAILED(node_list->Item(1, &node))) + return false; + + return AppendTextToXml(doc, node.Get(), body); +} + +bool WindowsToastNotification::SetXmlImage(IXmlDocument* doc, + const std::wstring& icon_path) { + ScopedHString tag(L"image"); + if (!tag.success()) + return false; + + ComPtr node_list; + if (FAILED(doc->GetElementsByTagName(tag, &node_list))) + return false; + + ComPtr image_node; + if (FAILED(node_list->Item(0, &image_node))) + return false; + + ComPtr attrs; + if (FAILED(image_node->get_Attributes(&attrs))) + return false; + + ScopedHString src(L"src"); + if (!src.success()) + return false; + + ComPtr src_attr; + if (FAILED(attrs->GetNamedItem(src, &src_attr))) + return false; + + ScopedHString img_path(icon_path.c_str()); + if (!img_path.success()) + return false; + + ComPtr src_text; + if (FAILED(doc->CreateTextNode(img_path, &src_text))) + return false; + + ComPtr src_node; + if (FAILED(src_text.As(&src_node))) + return false; + + ComPtr child_node; + return SUCCEEDED(src_attr->AppendChild(src_node.Get(), &child_node)); +} + +bool WindowsToastNotification::GetTextNodeList(ScopedHString* tag, + IXmlDocument* doc, + IXmlNodeList** node_list, + uint32_t req_length) { + tag->Reset(L"text"); + if (!tag->success()) + return false; + + if (FAILED(doc->GetElementsByTagName(*tag, node_list))) + return false; + + uint32_t node_length; + if (FAILED((*node_list)->get_Length(&node_length))) + return false; + + return node_length >= req_length; +} + +bool WindowsToastNotification::AppendTextToXml(IXmlDocument* doc, + IXmlNode* node, + const std::wstring& text) { + ScopedHString str(text); + if (!str.success()) + return false; + + ComPtr xml_text; + if (FAILED(doc->CreateTextNode(str, &xml_text))) + return false; + + ComPtr text_node; + if (FAILED(xml_text.As(&text_node))) + return false; + + ComPtr append_node; + return SUCCEEDED(node->AppendChild(text_node.Get(), &append_node)); +} + +bool WindowsToastNotification::SetupCallbacks( + ABI::Windows::UI::Notifications::IToastNotification* toast) { + event_handler_ = Make(this); + if (FAILED(toast->add_Activated(event_handler_.Get(), &activated_token_))) + return false; + + if (FAILED(toast->add_Dismissed(event_handler_.Get(), &dismissed_token_))) + return false; + + return SUCCEEDED(toast->add_Failed(event_handler_.Get(), &failed_token_)); +} + +bool WindowsToastNotification::RemoveCallbacks( + ABI::Windows::UI::Notifications::IToastNotification* toast) { + if (FAILED(toast->remove_Activated(activated_token_))) + return false; + + if (FAILED(toast->remove_Dismissed(dismissed_token_))) + return false; + + return SUCCEEDED(toast->remove_Failed(failed_token_)); +} + +/* +/ Toast Event Handler +*/ +ToastEventHandler::ToastEventHandler(Notification* notification) + : notification_(notification->GetWeakPtr()) {} + +ToastEventHandler::~ToastEventHandler() {} + +IFACEMETHODIMP ToastEventHandler::Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, + IInspectable* args) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&Notification::NotificationClicked, notification_)); + return S_OK; +} + +IFACEMETHODIMP ToastEventHandler::Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, + ABI::Windows::UI::Notifications::IToastDismissedEventArgs* e) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&Notification::NotificationDismissed, notification_)); + return S_OK; +} + +IFACEMETHODIMP ToastEventHandler::Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, + ABI::Windows::UI::Notifications::IToastFailedEventArgs* e) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&Notification::NotificationFailed, notification_)); + return S_OK; +} + +} // namespace brightray diff --git a/brightray/browser/win/windows_toast_notification.h b/brightray/browser/win/windows_toast_notification.h new file mode 100644 index 000000000000..47c7d596a88b --- /dev/null +++ b/brightray/browser/win/windows_toast_notification.h @@ -0,0 +1,134 @@ +// Copyright (c) 2015 Felix Rieseberg and Jason Poon +// . All rights reserved. +// Copyright (c) 2015 Ryan McShane and Brandon Smith +// +// Thanks to both of those folks mentioned above who first thought up a bunch of +// this code +// and released it as MIT to the world. + +#ifndef BRIGHTRAY_BROWSER_WIN_WINDOWS_TOAST_NOTIFICATION_H_ +#define BRIGHTRAY_BROWSER_WIN_WINDOWS_TOAST_NOTIFICATION_H_ + +#include +#include +#include + +#include "browser/notification.h" + +using Microsoft::WRL::ClassicCom; +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::Make; +using Microsoft::WRL::RuntimeClass; +using Microsoft::WRL::RuntimeClassFlags; + +class ScopedHString; + +namespace brightray { + +using DesktopToastActivatedEventHandler = + ABI::Windows::Foundation::ITypedEventHandler< + ABI::Windows::UI::Notifications::ToastNotification*, + IInspectable*>; +using DesktopToastDismissedEventHandler = + ABI::Windows::Foundation::ITypedEventHandler< + ABI::Windows::UI::Notifications::ToastNotification*, + ABI::Windows::UI::Notifications::ToastDismissedEventArgs*>; +using DesktopToastFailedEventHandler = + ABI::Windows::Foundation::ITypedEventHandler< + ABI::Windows::UI::Notifications::ToastNotification*, + ABI::Windows::UI::Notifications::ToastFailedEventArgs*>; + +class WindowsToastNotification : public Notification { + public: + // Should only be called by NotificationPresenterWin. + static bool Initialize(); + + WindowsToastNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + ~WindowsToastNotification(); + + protected: + // Notification: + void Show(const base::string16& title, + const base::string16& msg, + const std::string& tag, + const GURL& icon_url, + const SkBitmap& icon, + const bool silent) override; + void Dismiss() override; + + private: + friend class ToastEventHandler; + + bool GetToastXml( + ABI::Windows::UI::Notifications::IToastNotificationManagerStatics* + toastManager, + const std::wstring& title, + const std::wstring& msg, + const std::wstring& icon_path, + const bool silent, + ABI::Windows::Data::Xml::Dom::IXmlDocument** toastXml); + bool SetXmlAudioSilent(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc); + bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + const std::wstring& text); + bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + const std::wstring& title, + const std::wstring& body); + bool SetXmlImage(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + const std::wstring& icon_path); + bool GetTextNodeList(ScopedHString* tag, + ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + ABI::Windows::Data::Xml::Dom::IXmlNodeList** nodeList, + uint32_t reqLength); + bool AppendTextToXml(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, + ABI::Windows::Data::Xml::Dom::IXmlNode* node, + const std::wstring& text); + bool SetupCallbacks( + ABI::Windows::UI::Notifications::IToastNotification* toast); + bool RemoveCallbacks( + ABI::Windows::UI::Notifications::IToastNotification* toast); + + static ComPtr< + ABI::Windows::UI::Notifications::IToastNotificationManagerStatics> + toast_manager_; + static ComPtr + toast_notifier_; + + EventRegistrationToken activated_token_; + EventRegistrationToken dismissed_token_; + EventRegistrationToken failed_token_; + + ComPtr event_handler_; + ComPtr + toast_notification_; + + DISALLOW_COPY_AND_ASSIGN(WindowsToastNotification); +}; + +class ToastEventHandler : public RuntimeClass, + DesktopToastActivatedEventHandler, + DesktopToastDismissedEventHandler, + DesktopToastFailedEventHandler> { + public: + explicit ToastEventHandler(Notification* notification); + ~ToastEventHandler(); + + IFACEMETHODIMP Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, + IInspectable* args); + IFACEMETHODIMP Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, + ABI::Windows::UI::Notifications::IToastDismissedEventArgs* e); + IFACEMETHODIMP Invoke( + ABI::Windows::UI::Notifications::IToastNotification* sender, + ABI::Windows::UI::Notifications::IToastFailedEventArgs* e); + + private: + base::WeakPtr notification_; // weak ref. + + DISALLOW_COPY_AND_ASSIGN(ToastEventHandler); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_WIN_WINDOWS_TOAST_NOTIFICATION_H_ diff --git a/brightray/browser/zoom_level_delegate.cc b/brightray/browser/zoom_level_delegate.cc new file mode 100644 index 000000000000..7148b18e18a4 --- /dev/null +++ b/brightray/browser/zoom_level_delegate.cc @@ -0,0 +1,176 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/zoom_level_delegate.h" + +#include + +#include "base/bind.h" +#include "base/strings/string_number_conversions.h" +#include "base/values.h" +#include "components/prefs/json_pref_store.h" +#include "components/prefs/pref_filter.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service_factory.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/common/page_zoom.h" + +namespace std { + +template <> +struct hash { + size_t operator()(const base::FilePath& f) const { + return hash()(f.value()); + } +}; + +} // namespace std + +namespace brightray { + +namespace { + +// Double that indicates the default zoom level. +const char kPartitionDefaultZoomLevel[] = "partition.default_zoom_level"; + +// Dictionary that maps hostnames to zoom levels. Hosts not in this pref will +// be displayed at the default zoom level. +const char kPartitionPerHostZoomLevels[] = "partition.per_host_zoom_levels"; + +std::string GetHash(const base::FilePath& partition_path) { + size_t int_key = std::hash()(partition_path); + return base::SizeTToString(int_key); +} + +} // namespace + +// static +void ZoomLevelDelegate::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterDictionaryPref(kPartitionDefaultZoomLevel); + registry->RegisterDictionaryPref(kPartitionPerHostZoomLevels); +} + +ZoomLevelDelegate::ZoomLevelDelegate(PrefService* pref_service, + const base::FilePath& partition_path) + : pref_service_(pref_service), host_zoom_map_(nullptr) { + DCHECK(pref_service_); + partition_key_ = GetHash(partition_path); +} + +ZoomLevelDelegate::~ZoomLevelDelegate() {} + +void ZoomLevelDelegate::SetDefaultZoomLevelPref(double level) { + if (content::ZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel())) + return; + + DictionaryPrefUpdate update(pref_service_, kPartitionDefaultZoomLevel); + update->SetDouble(partition_key_, level); + host_zoom_map_->SetDefaultZoomLevel(level); +} + +double ZoomLevelDelegate::GetDefaultZoomLevelPref() const { + double default_zoom_level = 0.0; + + const base::DictionaryValue* default_zoom_level_dictionary = + pref_service_->GetDictionary(kPartitionDefaultZoomLevel); + // If no default has been previously set, the default returned is the + // value used to initialize default_zoom_level in this function. + default_zoom_level_dictionary->GetDouble(partition_key_, &default_zoom_level); + return default_zoom_level; +} + +void ZoomLevelDelegate::OnZoomLevelChanged( + const content::HostZoomMap::ZoomLevelChange& change) { + if (change.mode != content::HostZoomMap::ZOOM_CHANGED_FOR_HOST) + return; + + double level = change.zoom_level; + DictionaryPrefUpdate update(pref_service_, kPartitionPerHostZoomLevels); + base::DictionaryValue* host_zoom_dictionaries = update.Get(); + DCHECK(host_zoom_dictionaries); + + bool modification_is_removal = + content::ZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel()); + + base::DictionaryValue* host_zoom_dictionary = nullptr; + if (!host_zoom_dictionaries->GetDictionary(partition_key_, + &host_zoom_dictionary)) { + host_zoom_dictionary = new base::DictionaryValue(); + host_zoom_dictionaries->Set(partition_key_, host_zoom_dictionary); + } + + if (modification_is_removal) + host_zoom_dictionary->RemoveWithoutPathExpansion(change.host, nullptr); + else + host_zoom_dictionary->SetDoubleWithoutPathExpansion(change.host, level); +} + +void ZoomLevelDelegate::ExtractPerHostZoomLevels( + const base::DictionaryValue* host_zoom_dictionary) { + std::vector keys_to_remove; + std::unique_ptr host_zoom_dictionary_copy = + host_zoom_dictionary->DeepCopyWithoutEmptyChildren(); + for (base::DictionaryValue::Iterator i(*host_zoom_dictionary_copy); + !i.IsAtEnd(); i.Advance()) { + const std::string& host(i.key()); + double zoom_level = 0; + + bool has_valid_zoom_level = i.value().GetAsDouble(&zoom_level); + + // Filter out A) the empty host, B) zoom levels equal to the default; and + // remember them, so that we can later erase them from Prefs. + // Values of type B could further have been stored before the default zoom + // level was set to its current value. In either case, SetZoomLevelForHost + // will ignore type B values, thus, to have consistency with HostZoomMap's + // internal state, these values must also be removed from Prefs. + if (host.empty() || !has_valid_zoom_level || + content::ZoomValuesEqual(zoom_level, + host_zoom_map_->GetDefaultZoomLevel())) { + keys_to_remove.push_back(host); + continue; + } + + host_zoom_map_->SetZoomLevelForHost(host, zoom_level); + } + + // Sanitize prefs to remove entries that match the default zoom level and/or + // have an empty host. + { + DictionaryPrefUpdate update(pref_service_, kPartitionPerHostZoomLevels); + base::DictionaryValue* host_zoom_dictionaries = update.Get(); + base::DictionaryValue* sanitized_host_zoom_dictionary = nullptr; + host_zoom_dictionaries->GetDictionary(partition_key_, + &sanitized_host_zoom_dictionary); + for (const std::string& s : keys_to_remove) + sanitized_host_zoom_dictionary->RemoveWithoutPathExpansion(s, nullptr); + } +} + +void ZoomLevelDelegate::InitHostZoomMap(content::HostZoomMap* host_zoom_map) { + // This init function must be called only once. + DCHECK(!host_zoom_map_); + DCHECK(host_zoom_map); + host_zoom_map_ = host_zoom_map; + + // Initialize the default zoom level. + host_zoom_map_->SetDefaultZoomLevel(GetDefaultZoomLevelPref()); + + // Initialize the HostZoomMap with per-host zoom levels from the persisted + // zoom-level preference values. + const base::DictionaryValue* host_zoom_dictionaries = + pref_service_->GetDictionary(kPartitionPerHostZoomLevels); + const base::DictionaryValue* host_zoom_dictionary = nullptr; + if (host_zoom_dictionaries->GetDictionary(partition_key_, + &host_zoom_dictionary)) { + // Since we're calling this before setting up zoom_subscription_ below we + // don't need to worry that host_zoom_dictionary is indirectly affected + // by calls to HostZoomMap::SetZoomLevelForHost(). + ExtractPerHostZoomLevels(host_zoom_dictionary); + } + zoom_subscription_ = host_zoom_map_->AddZoomLevelChangedCallback(base::Bind( + &ZoomLevelDelegate::OnZoomLevelChanged, base::Unretained(this))); +} + +} // namespace brightray diff --git a/brightray/browser/zoom_level_delegate.h b/brightray/browser/zoom_level_delegate.h new file mode 100644 index 000000000000..aed065d7ec66 --- /dev/null +++ b/brightray/browser/zoom_level_delegate.h @@ -0,0 +1,61 @@ +// 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. + +#ifndef BRIGHTRAY_BROWSER_ZOOM_LEVEL_DELEGATE_H_ +#define BRIGHTRAY_BROWSER_ZOOM_LEVEL_DELEGATE_H_ + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/host_zoom_map.h" +#include "content/public/browser/zoom_level_delegate.h" + +namespace base { +class DictionaryValue; +} + +class PrefRegistrySimple; + +namespace brightray { + +// A class to manage per-partition default and per-host zoom levels. +// It implements an interface between the content/ zoom +// levels in HostZoomMap and preference system. All changes +// to the per-partition default zoom levels flow through this +// class. Any changes to per-host levels are updated when HostZoomMap calls +// OnZoomLevelChanged. +class ZoomLevelDelegate : public content::ZoomLevelDelegate { + public: + static void RegisterPrefs(PrefRegistrySimple* pref_registry); + + ZoomLevelDelegate(PrefService* pref_service, + const base::FilePath& partition_path); + ~ZoomLevelDelegate() override; + + void SetDefaultZoomLevelPref(double level); + double GetDefaultZoomLevelPref() const; + + // content::ZoomLevelDelegate: + void InitHostZoomMap(content::HostZoomMap* host_zoom_map) override; + + private: + void ExtractPerHostZoomLevels( + const base::DictionaryValue* host_zoom_dictionary); + + // This is a callback function that receives notifications from HostZoomMap + // when per-host zoom levels change. It is used to update the per-host + // zoom levels (if any) managed by this class (for its associated partition). + void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change); + + PrefService* pref_service_; + content::HostZoomMap* host_zoom_map_; + std::unique_ptr zoom_subscription_; + std::string partition_key_; + + DISALLOW_COPY_AND_ASSIGN(ZoomLevelDelegate); +}; + +} // namespace brightray + +#endif // BRIGHTRAY_BROWSER_ZOOM_LEVEL_DELEGATE_H_ diff --git a/brightray/common/application_info.h b/brightray/common/application_info.h new file mode 100644 index 000000000000..25b02c0d2393 --- /dev/null +++ b/brightray/common/application_info.h @@ -0,0 +1,13 @@ +#ifndef BRIGHTRAY_COMMON_APPLICATION_INFO_H_ +#define BRIGHTRAY_COMMON_APPLICATION_INFO_H_ + +#include + +namespace brightray { + +std::string GetApplicationName(); +std::string GetApplicationVersion(); + +} + +#endif diff --git a/brightray/common/application_info_mac.mm b/brightray/common/application_info_mac.mm new file mode 100644 index 000000000000..6898b6376deb --- /dev/null +++ b/brightray/common/application_info_mac.mm @@ -0,0 +1,30 @@ +#import "common/application_info.h" + +#import "common/mac/foundation_util.h" +#import "common/mac/main_application_bundle.h" + +#import "base/strings/sys_string_conversions.h" + +namespace brightray { + +namespace { + +std::string ApplicationInfoDictionaryValue(NSString* key) { + return base::SysNSStringToUTF8([MainApplicationBundle().infoDictionary objectForKey:key]); +} + +std::string ApplicationInfoDictionaryValue(CFStringRef key) { + return ApplicationInfoDictionaryValue(base::mac::CFToNSCast(key)); +} + +} // namespace + +std::string GetApplicationName() { + return ApplicationInfoDictionaryValue(kCFBundleNameKey); +} + +std::string GetApplicationVersion() { + return ApplicationInfoDictionaryValue(@"CFBundleShortVersionString"); +} + +} // namespace brightray diff --git a/brightray/common/application_info_win.cc b/brightray/common/application_info_win.cc new file mode 100644 index 000000000000..22635711cb48 --- /dev/null +++ b/brightray/common/application_info_win.cc @@ -0,0 +1,24 @@ +#include + +#include "common/application_info.h" + +#include "base/file_version_info.h" +#include "base/strings/utf_string_conversions.h" + +namespace brightray { + +std::string GetApplicationName() { + auto module = GetModuleHandle(nullptr); + std::unique_ptr info( + FileVersionInfo::CreateFileVersionInfoForModule(module)); + return base::UTF16ToUTF8(info->product_name()); +} + +std::string GetApplicationVersion() { + auto module = GetModuleHandle(nullptr); + std::unique_ptr info( + FileVersionInfo::CreateFileVersionInfoForModule(module)); + return base::UTF16ToUTF8(info->product_version()); +} + +} // namespace brightray diff --git a/brightray/common/content_client.cc b/brightray/common/content_client.cc new file mode 100644 index 000000000000..ebe066be613d --- /dev/null +++ b/brightray/common/content_client.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "common/content_client.h" + +#include "common/application_info.h" + +#include "base/strings/stringprintf.h" +#include "base/strings/string_util.h" +#include "content/public/common/user_agent.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +namespace brightray { + +std::string GetProductInternal() { + auto name = GetApplicationName(); + base::RemoveChars(name, base::kWhitespaceASCII, &name); + return base::StringPrintf("%s/%s", + name.c_str(), GetApplicationVersion().c_str()); +} + +std::string GetBrightrayUserAgent() { + return content::BuildUserAgentFromProduct(GetProductInternal()); +} + +ContentClient::ContentClient() { +} + +ContentClient::~ContentClient() { +} + +std::string ContentClient::GetProduct() const { + return GetProductInternal(); +} + +std::string ContentClient::GetUserAgent() const { + return GetBrightrayUserAgent(); +} + +base::string16 ContentClient::GetLocalizedString(int message_id) const { + return l10n_util::GetStringUTF16(message_id); +} + +base::StringPiece ContentClient::GetDataResource( + int resource_id, ui::ScaleFactor scale_factor) const { + return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale( + resource_id, scale_factor); +} + +gfx::Image& ContentClient::GetNativeImageNamed(int resource_id) const { + return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( + resource_id); +} + +base::RefCountedMemory* ContentClient::GetDataResourceBytes( + int resource_id) const { + return ResourceBundle::GetSharedInstance().LoadDataResourceBytes(resource_id); +} + +} // namespace brightray diff --git a/brightray/common/content_client.h b/brightray/common/content_client.h new file mode 100644 index 000000000000..4d1d6238c954 --- /dev/null +++ b/brightray/common/content_client.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_COMMON_CONTENT_CLIENT_H_ +#define BRIGHTRAY_COMMON_CONTENT_CLIENT_H_ + +#include "base/compiler_specific.h" +#include "content/public/common/content_client.h" + +namespace brightray { + +std::string GetBrightrayUserAgent(); + +class ContentClient : public content::ContentClient { + public: + ContentClient(); + ~ContentClient(); + + private: + std::string GetProduct() const override; + std::string GetUserAgent() const override; + base::string16 GetLocalizedString(int message_id) const override; + base::StringPiece GetDataResource(int resource_id, + ui::ScaleFactor) const override; + gfx::Image& GetNativeImageNamed(int resource_id) const override; + base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override; + + DISALLOW_COPY_AND_ASSIGN(ContentClient); +}; + +} // namespace brightray + +#endif diff --git a/brightray/common/mac/foundation_util.h b/brightray/common/mac/foundation_util.h new file mode 100644 index 000000000000..3b7e6a66a95d --- /dev/null +++ b/brightray/common/mac/foundation_util.h @@ -0,0 +1,15 @@ +#ifndef BRIGHTRAY_COMMON_MAC_FOUNDATION_UTIL_H_ +#define BRIGHTRAY_COMMON_MAC_FOUNDATION_UTIL_H_ + +// This header exists to work around incompatibilities between +// base/mac/foundation_util.h and the 10.8 SDK. + +#import + +// base/mac/foundation_util.h contains an incompatible declaration of +// NSSearchPathDirectory, so here we #define it to be something else. +#define NSSearchPathDirectory NSSearchPathDirectory___PRE_10_8 +#import "base/mac/foundation_util.h" +#undef NSSearchPathDirectory + +#endif diff --git a/brightray/common/mac/main_application_bundle.h b/brightray/common/mac/main_application_bundle.h new file mode 100644 index 000000000000..78e6bc200b42 --- /dev/null +++ b/brightray/common/mac/main_application_bundle.h @@ -0,0 +1,21 @@ +#ifndef BRIGHTRAY_COMMON_MAC_MAIN_APPLICATION_BUNDLE_H_ +#define BRIGHTRAY_COMMON_MAC_MAIN_APPLICATION_BUNDLE_H_ + +@class NSBundle; + +namespace base { +class FilePath; +} + +namespace brightray { + +// The "main" application bundle is the outermost bundle for this logical +// application. E.g., if you have MyApp.app and +// MyApp.app/Contents/Frameworks/MyApp Helper.app, the main application bundle +// is MyApp.app, no matter which executable is currently running. +NSBundle* MainApplicationBundle(); +base::FilePath MainApplicationBundlePath(); + +} // namespace brightray + +#endif diff --git a/brightray/common/mac/main_application_bundle.mm b/brightray/common/mac/main_application_bundle.mm new file mode 100644 index 000000000000..3e386b40dc58 --- /dev/null +++ b/brightray/common/mac/main_application_bundle.mm @@ -0,0 +1,54 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#import "common/mac/main_application_bundle.h" + +#include "base/files/file_path.h" +#include "base/mac/bundle_locations.h" +#include "base/mac/foundation_util.h" +#include "base/path_service.h" +#include "base/strings/string_util.h" + +namespace brightray { + +namespace { + +bool HasMainProcessKey() { + NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary]; + return [[info_dictionary objectForKey:@"ElectronMainProcess"] boolValue] != NO; +} + +} // namespace + +base::FilePath MainApplicationBundlePath() { + // Start out with the path to the running executable. + base::FilePath path; + PathService::Get(base::FILE_EXE, &path); + + // Up to Contents. + if (!HasMainProcessKey() && + base::EndsWith(path.value(), " Helper", base::CompareCase::SENSITIVE)) { + // The running executable is the helper. Go up five steps: + // Contents/Frameworks/Helper.app/Contents/MacOS/Helper + // ^ to here ^ from here + path = path.DirName().DirName().DirName().DirName().DirName(); + } else { + // One step up to MacOS, another to Contents. + path = path.DirName().DirName(); + } + DCHECK_EQ(path.BaseName().value(), "Contents"); + + // Up one more level to the .app. + path = path.DirName(); + DCHECK_EQ(path.BaseName().Extension(), ".app"); + + return path; +} + +NSBundle* MainApplicationBundle() { + return [NSBundle bundleWithPath:base::mac::FilePathToNSString(MainApplicationBundlePath())]; +} + +} // namespace brightray diff --git a/brightray/common/main_delegate.cc b/brightray/common/main_delegate.cc new file mode 100644 index 000000000000..4551ddfa0aa1 --- /dev/null +++ b/brightray/common/main_delegate.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#include "common/main_delegate.h" + +#include + +#include "browser/browser_client.h" +#include "common/content_client.h" + +#include "base/command_line.h" +#include "base/path_service.h" +#include "content/public/common/content_switches.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_switches.h" + +namespace brightray { + +namespace { + +// Returns true if this subprocess type needs the ResourceBundle initialized +// and resources loaded. +bool SubprocessNeedsResourceBundle(const std::string& process_type) { + return +#if defined(OS_POSIX) && !defined(OS_MACOSX) + // The zygote process opens the resources for the renderers. + process_type == switches::kZygoteProcess || +#endif +#if defined(OS_MACOSX) + // Mac needs them too for scrollbar related images and for sandbox + // profiles. +#if !defined(DISABLE_NACL) + process_type == switches::kNaClLoaderProcess || +#endif + process_type == switches::kPpapiPluginProcess || + process_type == switches::kPpapiBrokerProcess || + process_type == switches::kGpuProcess || +#endif + process_type == switches::kRendererProcess || + process_type == switches::kUtilityProcess; +} + +} // namespace + +void InitializeResourceBundle(const std::string& locale) { + // Load locales. + ui::ResourceBundle::InitSharedInstanceWithLocale( + locale, nullptr, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); + + // Load other resource files. +#if defined(OS_MACOSX) + LoadCommonResources(); +#else + base::FilePath pak_dir; + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + PathService::Get(base::DIR_MODULE, &pak_dir); + bundle.AddDataPackFromPath( + pak_dir.Append(FILE_PATH_LITERAL("content_shell.pak")), + ui::GetSupportedScaleFactors()[0]); + bundle.AddDataPackFromPath( + pak_dir.Append(FILE_PATH_LITERAL("pdf_viewer_resources.pak")), + ui::GetSupportedScaleFactors()[0]); + bundle.AddDataPackFromPath(pak_dir.Append(FILE_PATH_LITERAL( + "blink_image_resources_200_percent.pak")), + ui::SCALE_FACTOR_200P); + bundle.AddDataPackFromPath( + pak_dir.Append(FILE_PATH_LITERAL("content_resources_200_percent.pak")), + ui::SCALE_FACTOR_200P); + bundle.AddDataPackFromPath( + pak_dir.Append(FILE_PATH_LITERAL("ui_resources_200_percent.pak")), + ui::SCALE_FACTOR_200P); + bundle.AddDataPackFromPath( + pak_dir.Append(FILE_PATH_LITERAL("views_resources_200_percent.pak")), + ui::SCALE_FACTOR_200P); +#endif +} + +MainDelegate::MainDelegate() { +} + +MainDelegate::~MainDelegate() { +} + +std::unique_ptr MainDelegate::CreateContentClient() { + return std::unique_ptr(new ContentClient); +} + +bool MainDelegate::BasicStartupComplete(int* exit_code) { + content_client_ = CreateContentClient(); + SetContentClient(content_client_.get()); +#if defined(OS_MACOSX) + OverrideChildProcessPath(); + OverrideFrameworkBundlePath(); +#endif + return false; +} + +void MainDelegate::PreSandboxStartup() { + auto cmd = *base::CommandLine::ForCurrentProcess(); + std::string process_type = cmd.GetSwitchValueASCII(switches::kProcessType); + + // Initialize ResourceBundle which handles files loaded from external + // sources. The language should have been passed in to us from the + // browser process as a command line flag. + if (SubprocessNeedsResourceBundle(process_type)) { + std::string locale = cmd.GetSwitchValueASCII(switches::kLang); + InitializeResourceBundle(locale); + } +} + +content::ContentBrowserClient* MainDelegate::CreateContentBrowserClient() { + browser_client_ = CreateBrowserClient(); + return browser_client_.get(); +} + +std::unique_ptr MainDelegate::CreateBrowserClient() { + return std::unique_ptr(new BrowserClient); +} + +} // namespace brightray diff --git a/brightray/common/main_delegate.h b/brightray/common/main_delegate.h new file mode 100644 index 000000000000..2f63a2fb1b1a --- /dev/null +++ b/brightray/common/main_delegate.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012 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-CHROMIUM file. + +#ifndef BRIGHTRAY_COMMON_MAIN_DELEGATE_H_ +#define BRIGHTRAY_COMMON_MAIN_DELEGATE_H_ + +#include + +#include "base/macros.h" +#include "content/public/app/content_main_delegate.h" + +namespace base { +class FilePath; +} + +namespace ui { +class ResourceBundle; +} + +namespace brightray { + +class BrowserClient; +class ContentClient; + +void InitializeResourceBundle(const std::string& locale); +void LoadCommonResources(); + +class MainDelegate : public content::ContentMainDelegate { + public: + MainDelegate(); + ~MainDelegate(); + + protected: + // Subclasses can override this to provide their own ContentClient + // implementation. + virtual std::unique_ptr CreateContentClient(); + + // Subclasses can override this to provide their own BrowserClient + // implementation. + virtual std::unique_ptr CreateBrowserClient(); + +#if defined(OS_MACOSX) + // Subclasses can override this to custom the paths of child process and + // framework bundle. + virtual void OverrideChildProcessPath(); + virtual void OverrideFrameworkBundlePath(); +#endif + + bool BasicStartupComplete(int* exit_code) override; + void PreSandboxStartup() override; + + private: + content::ContentBrowserClient* CreateContentBrowserClient() override; + + std::unique_ptr content_client_; + std::unique_ptr browser_client_; + + DISALLOW_COPY_AND_ASSIGN(MainDelegate); +}; + +} // namespace brightray +#endif diff --git a/brightray/common/main_delegate_mac.mm b/brightray/common/main_delegate_mac.mm new file mode 100644 index 000000000000..29040840ce33 --- /dev/null +++ b/brightray/common/main_delegate_mac.mm @@ -0,0 +1,59 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Adam Roben . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#import "main_delegate.h" + +#include "common/application_info.h" +#include "common/mac/foundation_util.h" +#include "common/mac/main_application_bundle.h" + +#include "base/command_line.h" +#include "base/mac/bundle_locations.h" +#include "base/path_service.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "content/public/common/content_paths.h" +#include "content/public/common/content_switches.h" +#include "ui/base/resource/resource_bundle.h" + +namespace brightray { + +namespace { + +base::FilePath GetFrameworksPath() { + return MainApplicationBundlePath().Append("Contents").Append("Frameworks"); +} + +base::FilePath GetResourcesPakFilePath(NSString* name) { + auto path = [base::mac::FrameworkBundle() pathForResource:name ofType:@"pak"]; + return base::mac::NSStringToFilePath(path); +} + +} // namespace + +void LoadCommonResources() { + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + bundle.AddDataPackFromPath(GetResourcesPakFilePath(@"content_shell"), + ui::GetSupportedScaleFactors()[0]); + bundle.AddDataPackFromPath(GetResourcesPakFilePath(@"pdf_viewer_resources"), + ui::GetSupportedScaleFactors()[0]); +} + +void MainDelegate::OverrideFrameworkBundlePath() { + base::FilePath helper_path = GetFrameworksPath().Append(GetApplicationName() + " Framework.framework"); + + base::mac::SetOverrideFrameworkBundlePath(helper_path); +} + +void MainDelegate::OverrideChildProcessPath() { + base::FilePath helper_path = GetFrameworksPath().Append(GetApplicationName() + " Helper.app") + .Append("Contents") + .Append("MacOS") + .Append(GetApplicationName() + " Helper"); + + PathService::Override(content::CHILD_PROCESS_EXE, helper_path); +} + +} // namespace brightray diff --git a/brightray/common/switches.cc b/brightray/common/switches.cc new file mode 100644 index 000000000000..1f0da2adfddf --- /dev/null +++ b/brightray/common/switches.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "common/switches.h" + +namespace brightray { + +namespace switches { + +// Comma-separated list of rules that control how hostnames are mapped. +// +// For example: +// "MAP * 127.0.0.1" --> Forces all hostnames to be mapped to 127.0.0.1 +// "MAP *.google.com proxy" --> Forces all google.com subdomains to be +// resolved to "proxy". +// "MAP test.com [::1]:77 --> Forces "test.com" to resolve to IPv6 loopback. +// Will also force the port of the resulting +// socket address to be 77. +// "MAP * baz, EXCLUDE www.google.com" --> Remaps everything to "baz", +// except for "www.google.com". +// +// These mappings apply to the endpoint host in a net::URLRequest (the TCP +// connect and host resolver in a direct connection, and the CONNECT in an http +// proxy connection, and the endpoint host in a SOCKS proxy connection). +const char kHostRules[] = "host-rules"; + +// Don't use a proxy server, always make direct connections. Overrides any +// other proxy server flags that are passed. +const char kNoProxyServer[] = "no-proxy-server"; + +// Uses a specified proxy server, overrides system settings. This switch only +// affects HTTP and HTTPS requests. +const char kProxyServer[] = "proxy-server"; + +// Bypass specified proxy for the given semi-colon-separated list of hosts. This +// flag has an effect only when --proxy-server is set. +const char kProxyBypassList[] = "proxy-bypass-list"; + +// Uses the pac script at the given URL. +const char kProxyPacUrl[] = "proxy-pac-url"; + +// Disable HTTP/2 and SPDY/3.1 protocols. +const char kDisableHttp2[] = "disable-http2"; + +// Whitelist containing servers for which Integrated Authentication is enabled. +const char kAuthServerWhitelist[] = "auth-server-whitelist"; + +// Whitelist containing servers for which Kerberos delegation is allowed. +const char kAuthNegotiateDelegateWhitelist[] = + "auth-negotiate-delegate-whitelist"; + +// Ignores certificate-related errors. +const char kIgnoreCertificateErrors[] = "ignore-certificate-errors"; + +} // namespace switches + +} // namespace brightray diff --git a/brightray/common/switches.h b/brightray/common/switches.h new file mode 100644 index 000000000000..3aad50656b74 --- /dev/null +++ b/brightray/common/switches.h @@ -0,0 +1,26 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BRIGHTRAY_COMMON_SWITCHES_H_ +#define BRIGHTRAY_COMMON_SWITCHES_H_ + +namespace brightray { + +namespace switches { + +extern const char kHostRules[]; +extern const char kNoProxyServer[]; +extern const char kProxyServer[]; +extern const char kProxyBypassList[]; +extern const char kProxyPacUrl[]; +extern const char kDisableHttp2[]; +extern const char kAuthServerWhitelist[]; +extern const char kAuthNegotiateDelegateWhitelist[]; +extern const char kIgnoreCertificateErrors[]; + +} // namespace switches + +} // namespace brightray + +#endif // BRIGHTRAY_COMMON_SWITCHES_H_ diff --git a/brightray/filename_rules.gypi b/brightray/filename_rules.gypi new file mode 100644 index 000000000000..e1ee46dddee8 --- /dev/null +++ b/brightray/filename_rules.gypi @@ -0,0 +1,77 @@ +# Copyright (c) 2012 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 gypi file defines the patterns used for determining whether a +# file is excluded from the build on a given platform. It is +# included by common.gypi for chromium_code. + +{ + 'target_conditions': [ + ['OS!="win"', { + 'sources/': [ ['exclude', '_win(_unittest)?\\.(h|cc)$'], + ['exclude', '(^|/)win/'], + ['exclude', '(^|/)win_[^/]*\\.(h|cc)$'] ], + }], + ['OS!="mac"', { + 'sources/': [ ['exclude', '_(cocoa|mac)(_unittest)?\\.(h|cc|mm?)$'], + ['exclude', '(^|/)(cocoa|mac)/'] ], + }], + ['OS!="ios"', { + 'sources/': [ ['exclude', '_ios(_unittest)?\\.(h|cc|mm?)$'], + ['exclude', '(^|/)ios/'] ], + }], + ['OS!="mac" and OS!="ios"', { + 'sources/': [ ['exclude', '\\.mm?$' ] ], + }], + # Do not exclude the linux files on *BSD since most of them can be + # shared at this point. + # In case a file is not needed, it is going to be excluded later on. + # TODO(evan): the above is not correct; we shouldn't build _linux + # files on non-linux. + ['OS!="linux" and OS!="openbsd" and OS!="freebsd"', { + 'sources/': [ + ['exclude', '(^|/)library_loaders/'], + ['exclude', '_linux(_unittest)?\\.(h|cc)$'], + ['exclude', '(^|/)linux_[^/]*\\.(h|cc)$'], + ['exclude', '(^|/)linux/'], + ['exclude', '_x11(_unittest)?\\.(h|cc)$'], + ['exclude', '(^|/)x11_[^/]*\\.(h|cc)$'], + ['exclude', '(^|/)x11/'], + ], + }], + ['OS!="android"', { + 'sources/': [ + ['exclude', '_android(_unittest)?\\.cc$'], + ['exclude', '(^|/)android/'], + ], + }], + ['OS=="win"', { + 'sources/': [ + ['exclude', '_posix(_unittest)?\\.(h|cc)$'], + ['exclude', '(^|/)posix/'], + ], + }], + ['OS!="linux" and OS!="openbsd" and OS!="freebsd"', { + 'sources/': [ + ['exclude', '_xdg(_unittest)?\\.(h|cc)$'], + ], + }], + ['OS!="linux" and OS!="openbsd" and OS!="freebsd"', { + 'sources/': [ + ['exclude', '_gtk(_browsertest|_unittest)?\\.(h|cc)$'], + ['exclude', '(^|/)gtk/'], + ['exclude', '(^|/)gtk_[^/]*\\.(h|cc)$'], + ['exclude', '(^|/)libgtk2ui/'], + ['exclude', '(^|/)x/'], + ], + }], + ['OS=="mac"', { + 'sources/': [ ['exclude', '_aura(_browsertest|_unittest)?\\.(h|cc)$'], + ['exclude', '(^|/)aura/'], + ['exclude', '_views\\.(h|cc)$'], + ['exclude', '(^|/)views/'], + ], + }], + ] +} diff --git a/brightray/filenames.gypi b/brightray/filenames.gypi new file mode 100644 index 000000000000..b7d44013b847 --- /dev/null +++ b/brightray/filenames.gypi @@ -0,0 +1,129 @@ +{ + 'variables': { + 'brightray_sources': [ + 'browser/brightray_paths.h', + 'browser/browser_client.cc', + 'browser/browser_client.h', + 'browser/browser_context.cc', + 'browser/browser_context.h', + 'browser/browser_main_parts.cc', + 'browser/browser_main_parts.h', + 'browser/browser_main_parts_mac.mm', + 'browser/devtools_contents_resizing_strategy.cc', + 'browser/devtools_contents_resizing_strategy.h', + 'browser/devtools_embedder_message_dispatcher.cc', + 'browser/devtools_embedder_message_dispatcher.h', + 'browser/devtools_file_system_indexer.cc', + 'browser/devtools_file_system_indexer.h', + 'browser/devtools_manager_delegate.cc', + 'browser/devtools_manager_delegate.h', + 'browser/devtools_ui.cc', + 'browser/devtools_ui.h', + 'browser/inspectable_web_contents.cc', + 'browser/inspectable_web_contents.h', + 'browser/inspectable_web_contents_delegate.h', + 'browser/inspectable_web_contents_view_delegate.cc', + 'browser/inspectable_web_contents_view_delegate.h', + 'browser/inspectable_web_contents_impl.cc', + 'browser/inspectable_web_contents_impl.h', + 'browser/inspectable_web_contents_view.h', + 'browser/inspectable_web_contents_view_mac.h', + 'browser/inspectable_web_contents_view_mac.mm', + 'browser/mac/bry_application.h', + 'browser/mac/bry_application.mm', + 'browser/mac/bry_inspectable_web_contents_view.h', + 'browser/mac/bry_inspectable_web_contents_view.mm', + 'browser/mac/cocoa_notification.h', + 'browser/mac/cocoa_notification.mm', + 'browser/mac/event_dispatching_window.h', + 'browser/mac/event_dispatching_window.mm', + 'browser/mac/notification_center_delegate.h', + 'browser/mac/notification_center_delegate.mm', + 'browser/mac/notification_presenter_mac.h', + 'browser/mac/notification_presenter_mac.mm', + 'browser/media/media_capture_devices_dispatcher.cc', + 'browser/media/media_capture_devices_dispatcher.h', + 'browser/media/media_device_id_salt.cc', + 'browser/media/media_device_id_salt.h', + 'browser/media/media_stream_devices_controller.cc', + 'browser/media/media_stream_devices_controller.h', + 'browser/net/devtools_network_conditions.cc', + 'browser/net/devtools_network_conditions.h', + 'browser/net/devtools_network_controller.cc', + 'browser/net/devtools_network_controller.h', + 'browser/net/devtools_network_controller_handle.cc', + 'browser/net/devtools_network_controller_handle.h', + 'browser/net/devtools_network_interceptor.cc', + 'browser/net/devtools_network_interceptor.h', + 'browser/net/devtools_network_protocol_handler.cc', + 'browser/net/devtools_network_protocol_handler.h', + 'browser/net/devtools_network_transaction_factory.cc', + 'browser/net/devtools_network_transaction_factory.h', + 'browser/net/devtools_network_transaction.cc', + 'browser/net/devtools_network_transaction.h', + 'browser/net/devtools_network_upload_data_stream.cc', + 'browser/net/devtools_network_upload_data_stream.h', + 'browser/net_log.cc', + 'browser/net_log.h', + 'browser/network_delegate.cc', + 'browser/network_delegate.h', + 'browser/notification_delegate.h', + 'browser/notification_delegate_adapter.cc', + 'browser/notification_delegate_adapter.h', + 'browser/notification_presenter.cc', + 'browser/notification_presenter.h', + 'browser/notification.cc', + 'browser/notification.h', + 'browser/permission_manager.cc', + 'browser/permission_manager.h', + 'browser/platform_notification_service.cc', + 'browser/platform_notification_service.h', + 'browser/linux/libnotify_loader.h', + 'browser/linux/libnotify_loader.cc', + 'browser/linux/libnotify_notification.h', + 'browser/linux/libnotify_notification.cc', + 'browser/linux/notification_presenter_linux.h', + 'browser/linux/notification_presenter_linux.cc', + 'browser/win/notification_presenter_win.cc', + 'browser/win/notification_presenter_win.h', + 'browser/win/notification_presenter_win7.cc', + 'browser/win/notification_presenter_win7.h', + 'browser/win/scoped_hstring.cc', + 'browser/win/scoped_hstring.h', + 'browser/win/win32_desktop_notifications/common.h', + 'browser/win/win32_desktop_notifications/desktop_notification_controller.cc', + 'browser/win/win32_desktop_notifications/desktop_notification_controller.h', + 'browser/win/win32_desktop_notifications/toast.cc', + 'browser/win/win32_desktop_notifications/toast.h', + 'browser/win/win32_notification.cc', + 'browser/win/win32_notification.h', + 'browser/win/windows_toast_notification.cc', + 'browser/win/windows_toast_notification.h', + 'browser/special_storage_policy.cc', + 'browser/special_storage_policy.h', + 'browser/url_request_context_getter.cc', + 'browser/url_request_context_getter.h', + 'browser/views/inspectable_web_contents_view_views.h', + 'browser/views/inspectable_web_contents_view_views.cc', + 'browser/views/views_delegate.cc', + 'browser/views/views_delegate.h', + 'browser/web_ui_controller_factory.cc', + 'browser/web_ui_controller_factory.h', + 'browser/zoom_level_delegate.cc', + 'browser/zoom_level_delegate.h', + 'common/application_info.h', + 'common/application_info_mac.mm', + 'common/application_info_win.cc', + 'common/content_client.cc', + 'common/content_client.h', + 'common/mac/foundation_util.h', + 'common/mac/main_application_bundle.h', + 'common/mac/main_application_bundle.mm', + 'common/main_delegate.cc', + 'common/main_delegate.h', + 'common/main_delegate_mac.mm', + 'common/switches.cc', + 'common/switches.h', + ], + }, +} diff --git a/common.gypi b/common.gypi index d4b7646c28ef..ee8b1e857680 100644 --- a/common.gypi +++ b/common.gypi @@ -1,7 +1,7 @@ { 'includes': [ 'toolchain.gypi', - 'vendor/brightray/brightray.gypi', + 'brightray/brightray.gypi', ], 'variables': { # Tell crashpad to build as external project. diff --git a/docs-translations/es/development/build-system-overview.md b/docs-translations/es/development/build-system-overview.md index 1e6a42da84bf..1996137495e8 100644 --- a/docs-translations/es/development/build-system-overview.md +++ b/docs-translations/es/development/build-system-overview.md @@ -6,8 +6,8 @@ los siguientes archivos `gyp` contienen las principales reglas para la contrucci * `atom.gyp` define en si como se compila en Electron. * `common.gypi` ajusta las configuraciones de generación de Node para construir junto con Chromium. - * `vendor/brightray/brightray.gyp` define cómo se construye `brightray` e incluye las configuraciones predeterminadas para linkear con Chromium. - * `vendor/brightray/brightray.gypi` incluye configuraciones de generación generales sobre la construcción. + * `brightray/brightray.gyp` define cómo se construye `brightray` e incluye las configuraciones predeterminadas para linkear con Chromium. + * `brightray/brightray.gypi` incluye configuraciones de generación generales sobre la construcción. #Construir un componente Desde Chromium es un proyecto bastante largo, la etapa de enlace final puede tomar pocos minutos, lo que hace que sea difícil para el desarrollo. Con el fin de resolver esto, Chromium introdujo el "componente de construcción", que se basa en construir cada componente como una libreria compartida por separado, haciendo que se enlace muy rápido, pero sacrificando el tamaño del archivo y el rendimiento. diff --git a/docs-translations/jp/development/atom-shell-vs-node-webkit.md b/docs-translations/jp/development/atom-shell-vs-node-webkit.md index 09a8621a24c9..aa5e2d45ef0d 100644 --- a/docs-translations/jp/development/atom-shell-vs-node-webkit.md +++ b/docs-translations/jp/development/atom-shell-vs-node-webkit.md @@ -16,7 +16,7 @@ Electron は Node.js ランタイムのように動作します。 Electron の __2. ビルドシステム__ -Chromium の全てのコードをビルドする複雑さを回避するため、Electron は [`libchromiumcontent`](https://github.com/brightray/libchromiumcontent) を通して Chromium の Content API にアクセスします。 +Chromium の全てのコードをビルドする複雑さを回避するため、Electron は [`libchromiumcontent`](https://github.com/electron/libchromiumcontent) を通して Chromium の Content API にアクセスします。 `libchromiumcontent` は Chromium の Content モジュールとそれに依存する全てを含んだ単一の共有ライブラリです。 おかげで Electron をビルドするためにパワフルなマシンを用意する必要はありません。 diff --git a/docs-translations/ko-KR/development/atom-shell-vs-node-webkit.md b/docs-translations/ko-KR/development/atom-shell-vs-node-webkit.md index 97ed0cbf7418..3dc71bf7a87a 100644 --- a/docs-translations/ko-KR/development/atom-shell-vs-node-webkit.md +++ b/docs-translations/ko-KR/development/atom-shell-vs-node-webkit.md @@ -24,7 +24,7 @@ Electron은 Node.js 런타임과 비슷하게 작동합니다. Electron의 API __2. 빌드 시스템__ Electron은 Chromium의 모든것을 빌드하는 복잡성을 피하기 위해 -[libchromiumcontent](https://github.com/brightray/libchromiumcontent)를 사용하여 +[libchromiumcontent](https://github.com/electron/libchromiumcontent)를 사용하여 Chromium의 Content API에 접근합니다. libchromiumcontent은 단일 공유 라이브러리이고 Chromium Content 모듈과 의존성 라이브러리들을 포함합니다. 유저는 Electron을 빌드 하기 위해 높은 사양의 빌드용 컴퓨터를 구비할 필요가 없습니다. diff --git a/docs-translations/ko-KR/development/build-system-overview.md b/docs-translations/ko-KR/development/build-system-overview.md index 26b4380d43cf..fd74ac57c12d 100644 --- a/docs-translations/ko-KR/development/build-system-overview.md +++ b/docs-translations/ko-KR/development/build-system-overview.md @@ -10,9 +10,9 @@ Electron을 빌드 할 때 `gyp` 파일들은 다음과 같은 규칙을 따릅 * `electron.gyp`는 Electron의 빌드 과정 자체를 정의합니다. * `common.gypi`는 Node가 Chromium과 함께 빌드될 수 있도록 조정한 빌드 설정입니다. -* `vendor/brightray/brightray.gyp`는 `brightray`의 빌드 과정을 정의하고 Chromium +* `brightray/brightray.gyp`는 `brightray`의 빌드 과정을 정의하고 Chromium 링킹에 대한 기본적인 설정을 포함합니다. -* `vendor/brightray/brightray.gypi`는 빌드에 대한 일반적인 설정이 포함되어 있습니다. +* `brightray/brightray.gypi`는 빌드에 대한 일반적인 설정이 포함되어 있습니다. ## 구성요소 빌드 diff --git a/docs-translations/ko-KR/development/debug-instructions-macos.md b/docs-translations/ko-KR/development/debug-instructions-macos.md index ce2cb98e01db..df42f99fdadc 100644 --- a/docs-translations/ko-KR/development/debug-instructions-macos.md +++ b/docs-translations/ko-KR/development/debug-instructions-macos.md @@ -38,7 +38,7 @@ LLDB 는 강력한 도구이며 코드 검사를 위한 다양한 전략을 제 명령의 C++ 부분에서 멈추길 원하며 그것은 Electron 소스 내에 있습니다. 관련 코드는 `./atom/` 에서 찾을 수 있으며 마찬가지로 Brightray 도 -`./vendor/brightray/browser` 와 `./vendor/brightray/common` 에서 찾을 수 +`./brightray/browser` 와 `./brightray/common` 에서 찾을 수 있습니다. 당신이 열정적이라면, Chromium 을 직접 디버깅할 수 있으며, `chromium_src` 에서 찾을 수 있습니다. diff --git a/docs-translations/ko-KR/development/debug-instructions-windows.md b/docs-translations/ko-KR/development/debug-instructions-windows.md index 31e814f3d9e7..b80e4d99a194 100644 --- a/docs-translations/ko-KR/development/debug-instructions-windows.md +++ b/docs-translations/ko-KR/development/debug-instructions-windows.md @@ -42,7 +42,7 @@ $ ./out/D/electron.exe ~/my-electron-app/ 중인 프로세스와 중단점을 자동으로 찾아냅니다. 관련된 코드 파일들은 `./atom/`에서 찾을 수 있으며 또한 Brightray 안 -`./vendor/brightray/browser`와 `./vendor/brightray/common`에서도 찾을 수 있습니다. +`./brightray/browser`와 `./brightray/common`에서도 찾을 수 있습니다. 만약 하드코어를 좋아한다면, Chromium을 직접 디버깅할 수도 있습니다. 확실히 `chromium_src` 안에서 찾을 수 있습니다. diff --git a/docs-translations/ko-KR/development/source-code-directory-structure.md b/docs-translations/ko-KR/development/source-code-directory-structure.md index 8d592ddc742b..050bc73793db 100644 --- a/docs-translations/ko-KR/development/source-code-directory-structure.md +++ b/docs-translations/ko-KR/development/source-code-directory-structure.md @@ -67,7 +67,7 @@ Electron 저장소는 몇 가지 외부 벤더 의존성을 가지고 있으며 ```sh $ git status - modified: vendor/brightray (new commits) + modified: brightray (new commits) modified: vendor/node (new commits) ``` diff --git a/docs-translations/zh-CN/development/atom-shell-vs-node-webkit.md b/docs-translations/zh-CN/development/atom-shell-vs-node-webkit.md index 68fddb11d237..c97fbae768d4 100644 --- a/docs-translations/zh-CN/development/atom-shell-vs-node-webkit.md +++ b/docs-translations/zh-CN/development/atom-shell-vs-node-webkit.md @@ -16,7 +16,7 @@ Electron 的工作方式更像 Node.js 运行时。 Electron 的 APIs 更加底 __2. 构建系统__ -为了避免构建整个 Chromium 带来的复杂度,Electron 通过 [`libchromiumcontent`](https://github.com/brightray/libchromiumcontent) 来访问 Chromium 的 Content API。`libchromiumcontent` 是一个独立的、引入了 Chromium Content 模块及其所有依赖的共享库。用户不需要一个强劲的机器来构建 Electron。 +为了避免构建整个 Chromium 带来的复杂度,Electron 通过 [`libchromiumcontent`](https://github.com/electron/libchromiumcontent) 来访问 Chromium 的 Content API。`libchromiumcontent` 是一个独立的、引入了 Chromium Content 模块及其所有依赖的共享库。用户不需要一个强劲的机器来构建 Electron。 __3. Node 集成__ diff --git a/docs-translations/zh-CN/development/build-system-overview.md b/docs-translations/zh-CN/development/build-system-overview.md index 6bd1452b8164..329dc74bd1fc 100644 --- a/docs-translations/zh-CN/development/build-system-overview.md +++ b/docs-translations/zh-CN/development/build-system-overview.md @@ -8,8 +8,8 @@ Electron 使用 [gyp](https://gyp.gsrc.io/) 来生成项目 ,使用 [ninja](ht * `atom.gyp` 定义了 Electron 它自己是怎样被构建的. * `common.gypi` 调整 node 的构建配置,来让它结合 Chromium 一起构建. -* `vendor/brightray/brightray.gyp` 定义了 `brightray` 是如何被构建的,并且包含了默认配置来连接到 Chromium. -* `vendor/brightray/brightray.gypi` 包含了常用的创建配置. +* `brightray/brightray.gyp` 定义了 `brightray` 是如何被构建的,并且包含了默认配置来连接到 Chromium. +* `brightray/brightray.gypi` 包含了常用的创建配置. ## 创建组件 diff --git a/docs-translations/zh-CN/development/debug-instructions-windows.md b/docs-translations/zh-CN/development/debug-instructions-windows.md index 0afa03463c5b..fd9f6069134b 100644 --- a/docs-translations/zh-CN/development/debug-instructions-windows.md +++ b/docs-translations/zh-CN/development/debug-instructions-windows.md @@ -22,7 +22,7 @@ $ ./out/D/electron.exe ~/my-electron-app/ 然后,打开 Visual Studio。 Electron 不是使用 Visual Studio 构建的,因此不包含项目文件 - 但是您可以打开源代码文件 "As File",这意味着 Visual Studio 将自己打开它们。 您仍然可以设置断点 - Visual Studio 将自动确定源代码与附加过程中运行的代码相匹配,并相应地中断。 -相关的代码文件可以在 `./atom/` 以及 Brightray 中找到, 找到 `./vendor/brightray/browser` 和 `./vendor/brightray/common`. 如果是内核,你也可以直接调试 Chromium,这显然在 `chromium_src` 中。 +相关的代码文件可以在 `./atom/` 以及 Brightray 中找到, 找到 `./brightray/browser` 和 `./brightray/common`. 如果是内核,你也可以直接调试 Chromium,这显然在 `chromium_src` 中。 ### 附加 diff --git a/docs-translations/zh-CN/development/debugging-instructions-macos.md b/docs-translations/zh-CN/development/debugging-instructions-macos.md index 0a7929e57e07..bad622438ffd 100644 --- a/docs-translations/zh-CN/development/debugging-instructions-macos.md +++ b/docs-translations/zh-CN/development/debugging-instructions-macos.md @@ -22,7 +22,7 @@ Current executable set to './out/D/Electron.app' (x86_64). LLDB是一个强大的工具,支持进行多种策略的代码检查。 在这做一个基本的介绍,让我们假设你从 JavaScript 调用一个不正常的命令 - 所以你想打断该命令的 C++ 对应的 Electron 源。 -相关的代码文件可以在 `./atom/` 以及 Brightray 中找到, 找到 `./vendor/brightray/browser` 和 `./vendor/brightray/common`. 如果是内核,你也可以直接调试 Chromium,这显然在 `chromium_src` 中。 +相关的代码文件可以在 `./atom/` 以及 Brightray 中找到, 找到 `./brightray/browser` 和 `./brightray/common`. 如果是内核,你也可以直接调试 Chromium,这显然在 `chromium_src` 中。 让我们假设你想调试 `app.setName()`, 在 `browser.cc` 中定义为 `Browser::SetName()`. 使用 `breakpoint` 命令进行断点,指定文件和断点位置:: diff --git a/docs/development/atom-shell-vs-node-webkit.md b/docs/development/atom-shell-vs-node-webkit.md index 0912a8d66e31..4c8cd4ef5fe8 100644 --- a/docs/development/atom-shell-vs-node-webkit.md +++ b/docs/development/atom-shell-vs-node-webkit.md @@ -25,7 +25,7 @@ so you can use it for browser testing in place of [PhantomJS](http://phantomjs.o __2. Build System__ -In order to avoid the complexity of building all of Chromium, Electron uses [`libchromiumcontent`](https://github.com/brightray/libchromiumcontent) to access +In order to avoid the complexity of building all of Chromium, Electron uses [`libchromiumcontent`](https://github.com/electron/libchromiumcontent) to access Chromium's Content API. `libchromiumcontent` is a single shared library that includes the Chromium Content module and all of its dependencies. Users don't need a powerful machine to build Electron. diff --git a/docs/development/build-system-overview.md b/docs/development/build-system-overview.md index df12df6ae970..083f52d0047d 100644 --- a/docs/development/build-system-overview.md +++ b/docs/development/build-system-overview.md @@ -11,9 +11,9 @@ Following `gyp` files contain the main rules for building Electron: * `electron.gyp` defines how Electron itself is built. * `common.gypi` adjusts the build configurations of Node to make it build together with Chromium. -* `vendor/brightray/brightray.gyp` defines how `brightray` is built and +* `brightray/brightray.gyp` defines how `brightray` is built and includes the default configurations for linking with Chromium. -* `vendor/brightray/brightray.gypi` includes general build configurations about +* `brightray/brightray.gypi` includes general build configurations about building. ## Component Build diff --git a/docs/development/debug-instructions-windows.md b/docs/development/debug-instructions-windows.md index b2d33473934a..440cb33e414b 100644 --- a/docs/development/debug-instructions-windows.md +++ b/docs/development/debug-instructions-windows.md @@ -47,7 +47,7 @@ source code matches the code running in the attached process and break accordingly. Relevant code files can be found in `./atom/` as well as in Brightray, found in -`./vendor/brightray/browser` and `./vendor/brightray/common`. If you're hardcore, +`./brightray/browser` and `./brightray/common`. If you're hardcore, you can also debug Chromium directly, which is obviously found in `chromium_src`. ### Attaching diff --git a/docs/development/debugging-instructions-macos.md b/docs/development/debugging-instructions-macos.md index e119db466a19..054909773f6b 100644 --- a/docs/development/debugging-instructions-macos.md +++ b/docs/development/debugging-instructions-macos.md @@ -18,8 +18,8 @@ to enable step-through debugging with breakpoints inside Electron's source code. tail calls, and other compiler optimizations. * **Xcode**: In addition to Xcode, also install the Xcode command line tools. - They include LLDB, the default debugger in Xcode on Mac OS X. It supports - debugging C, Objective-C and C++ on the desktop and iOS devices and simulator. + They include LLDB, the default debugger in Xcode on Mac OS X. It supports + debugging C, Objective-C and C++ on the desktop and iOS devices and simulator. ## Attaching to and Debugging Electron @@ -40,7 +40,7 @@ that isn't behaving correctly - so you'd like to break on that command's C++ counterpart inside the Electron source. Relevant code files can be found in `./atom/` as well as in Brightray, found in -`./vendor/brightray/browser` and `./vendor/brightray/common`. If you're hardcore, +`./brightray/browser` and `./brightray/common`. If you're hardcore, you can also debug Chromium directly, which is obviously found in `chromium_src`. Let's assume that you want to debug `app.setName()`, which is defined in `browser.cc` @@ -77,7 +77,7 @@ Process 25244 stopped ``` To show the arguments and local variables for the current frame, run `frame variable` (or `fr v`), -which will show you that the app is currently setting the name to "Electron". +which will show you that the app is currently setting the name to "Electron". ```bash (lldb) frame variable diff --git a/docs/development/source-code-directory-structure.md b/docs/development/source-code-directory-structure.md index cc74fe69427a..4af519120ee3 100644 --- a/docs/development/source-code-directory-structure.md +++ b/docs/development/source-code-directory-structure.md @@ -72,7 +72,7 @@ when running `git status`: ```sh $ git status - modified: vendor/brightray (new commits) + modified: vendor/libchromiumcontent (new commits) modified: vendor/node (new commits) ``` diff --git a/docs/development/upgrading-chrome.md b/docs/development/upgrading-chrome.md index 6e47a8b4f521..1004e6d36454 100644 --- a/docs/development/upgrading-chrome.md +++ b/docs/development/upgrading-chrome.md @@ -19,8 +19,6 @@ Chrome/Node API changes. - Upgrade `vendor/depot_tools` for any build tools changes needed - Update the `libchromiumcontent` SHA-1 to download in `script/lib/config.py` - Open a pull request on `electron/libchromiumcontent` with the changes -- Open a pull request on `electron/brightray` with the changes - - This should include upgrading the `vendor/libchromiumcontent` submodule - Open a pull request on `electron/electron` with the changes - This should include upgrading the submodules in `vendor/` as needed - Verify debug builds succeed on: diff --git a/docs/glossary.md b/docs/glossary.md index b923037f9f0a..b5397182387a 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -12,10 +12,8 @@ The ASAR format was created primarily to improve performance on Windows... TODO ### Brightray -[Brightray][brightray] is a static library that makes [libchromiumcontent] -easier to use in applications. It was created specifically for Electron, but can -be used to enable Chromium's renderer in native apps that are not based on -Electron. +Brightray is a static library that makes [libchromiumcontent] +easier to use in applications. Brightray is a low-level dependency of Electron that does not concern the majority of Electron users. @@ -142,7 +140,6 @@ embedded content. [addons]: https://nodejs.org/api/addons.html [asar]: https://github.com/electron/asar [autoUpdater]: api/auto-updater.md -[brightray]: https://github.com/electron/brightray [electron-builder]: https://github.com/electron-userland/electron-builder [libchromiumcontent]: #libchromiumcontent [Mac App Store Submission Guide]: tutorials/mac-app-store-submission-guide.md diff --git a/electron.gyp b/electron.gyp index dd40842bb86c..a8658b893602 100644 --- a/electron.gyp +++ b/electron.gyp @@ -81,7 +81,7 @@ # is marked for no PIE (ASLR). 'postbuild_name': 'Make More Helpers', 'action': [ - 'vendor/brightray/tools/mac/make_more_helpers.sh', + 'tools/mac/make_more_helpers.sh', 'Frameworks', '<(product_name)', ], @@ -220,7 +220,7 @@ 'dependencies': [ 'atom_js2c', 'vendor/pdf_viewer/pdf_viewer.gyp:pdf_viewer', - 'vendor/brightray/brightray.gyp:brightray', + 'brightray/brightray.gyp:brightray', 'vendor/node/node.gyp:node', ], 'defines': [ @@ -244,7 +244,7 @@ 'include_dirs': [ '.', 'chromium_src', - 'vendor/brightray', + 'brightray', 'vendor/native_mate', # Include atom_natives.h. '<(SHARED_INTERMEDIATE_DIR)', @@ -271,7 +271,7 @@ ], }, 'export_dependent_settings': [ - 'vendor/brightray/brightray.gyp:brightray', + 'brightray/brightray.gyp:brightray', ], 'conditions': [ ['libchromiumcontent_component', { diff --git a/script/bootstrap.py b/script/bootstrap.py index 08d84cff28e9..0feee9b50028 100755 --- a/script/bootstrap.py +++ b/script/bootstrap.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import argparse +import errno import os import subprocess import sys @@ -12,6 +13,7 @@ from lib.util import execute_stdout, get_electron_version, scoped_cwd SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) VENDOR_DIR = os.path.join(SOURCE_ROOT, 'vendor') +DOWNLOAD_DIR = os.path.join(VENDOR_DIR, 'download') PYTHON_26_URL = 'https://chromium.googlesource.com/chromium/deps/python_26' NPM = 'npm' @@ -40,8 +42,7 @@ def main(): # Redirect to use local libchromiumcontent build. if args.build_libchromiumcontent: build_libchromiumcontent(args.verbose, args.target_arch, defines) - dist_dir = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', - 'libchromiumcontent', 'dist', 'main') + dist_dir = os.path.join(VENDOR_DIR, 'libchromiumcontent', 'dist', 'main') libcc_source_path = os.path.join(dist_dir, 'src') libcc_shared_library_path = os.path.join(dist_dir, 'shared_library') libcc_static_library_path = os.path.join(dist_dir, 'static_library') @@ -53,9 +54,9 @@ def main(): setup_python_libs() update_node_modules('.') - bootstrap_brightray(args.dev, args.url, args.target_arch, - libcc_source_path, libcc_shared_library_path, - libcc_static_library_path) + setup_libchromiumcontent(args.dev, args.target_arch, args.url, + libcc_source_path, libcc_shared_library_path, + libcc_static_library_path) if PLATFORM == 'linux': download_sysroot(args.target_arch) @@ -135,24 +136,28 @@ def setup_python_libs(): execute_stdout([sys.executable, 'setup.py', 'build']) -def bootstrap_brightray(is_dev, url, target_arch, libcc_source_path, - libcc_shared_library_path, - libcc_static_library_path): - bootstrap = os.path.join(VENDOR_DIR, 'brightray', 'script', 'bootstrap') - args = [ - '--commit', LIBCHROMIUMCONTENT_COMMIT, - '--target_arch', target_arch, - url - ] - if is_dev: - args = ['--dev'] + args +def setup_libchromiumcontent(is_dev, target_arch, url, + libcc_source_path, + libcc_shared_library_path, + libcc_static_library_path): + target_dir = os.path.join(DOWNLOAD_DIR, 'libchromiumcontent') + download = os.path.join(VENDOR_DIR, 'libchromiumcontent', 'script', + 'download') + args = ['-f', '-c', LIBCHROMIUMCONTENT_COMMIT, '--target_arch', target_arch, + url, target_dir] if (libcc_source_path != None and libcc_shared_library_path != None and libcc_static_library_path != None): args += ['--libcc_source_path', libcc_source_path, - '--libcc_shared_library_path', libcc_shared_library_path, - '--libcc_static_library_path', libcc_static_library_path] - execute_stdout([sys.executable, bootstrap] + args) + '--libcc_shared_library_path', libcc_shared_library_path, + '--libcc_static_library_path', libcc_static_library_path] + mkdir_p(target_dir) + else: + mkdir_p(DOWNLOAD_DIR) + if is_dev: + subprocess.check_call([sys.executable, download] + args) + else: + subprocess.check_call([sys.executable, download, '-s'] + args) def set_clang_env(env): @@ -225,8 +230,7 @@ def download_sysroot(target_arch): '--arch', target_arch]) def create_chrome_version_h(): - version_file = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', - 'libchromiumcontent', 'VERSION') + version_file = os.path.join(VENDOR_DIR, 'libchromiumcontent', 'VERSION') target_file = os.path.join(SOURCE_ROOT, 'atom', 'common', 'chrome_version.h') template_file = os.path.join(SOURCE_ROOT, 'script', 'chrome_version.h.in') @@ -271,5 +275,13 @@ def create_node_headers(): '--version', get_electron_version()]) +def mkdir_p(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + if __name__ == '__main__': sys.exit(main()) diff --git a/script/build-libchromiumcontent.py b/script/build-libchromiumcontent.py index e0a95f69a465..63d7514de7b6 100755 --- a/script/build-libchromiumcontent.py +++ b/script/build-libchromiumcontent.py @@ -22,8 +22,8 @@ def main(): # ./script/update -t x64 --defines='' # ./script/build --no_shared_library -t x64 # ./script/create-dist -c static_library -t x64 --no_zip - script_dir = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', - 'libchromiumcontent', 'script') + script_dir = os.path.join(SOURCE_ROOT, 'vendor', 'libchromiumcontent', + 'script') bootstrap = os.path.join(script_dir, 'bootstrap') update = os.path.join(script_dir, 'update') build = os.path.join(script_dir, 'build') diff --git a/script/clean.py b/script/clean.py index c63bdbbadee2..d1ccaa252db3 100755 --- a/script/clean.py +++ b/script/clean.py @@ -22,8 +22,8 @@ def main(): remove_directory('node_modules') remove_directory('spec/node_modules') - remove_directory('vendor/brightray/vendor/download/libchromiumcontent') - remove_directory('vendor/brightray/vendor/libchromiumcontent/src') + remove_directory('vendor/download/libchromiumcontent') + remove_directory('vendor/libchromiumcontent/src') remove_directory(os.path.expanduser('~/.node-gyp')) diff --git a/script/cpplint.py b/script/cpplint.py index 6c715522f8ce..0c91dc06aea8 100755 --- a/script/cpplint.py +++ b/script/cpplint.py @@ -17,6 +17,15 @@ IGNORE_FILES = [ os.path.join('atom', 'common', 'api', 'api_messages.h'), os.path.join('atom', 'common', 'common_message_generator.cc'), os.path.join('atom', 'common', 'common_message_generator.h'), + os.path.join('brightray', 'browser', 'mac', + 'bry_inspectable_web_contents_view.h'), + os.path.join('brightray', 'browser', 'mac', 'event_dispatching_window.h'), + os.path.join('brightray', 'browser', 'mac', + 'notification_center_delegate.h'), + os.path.join('brightray', 'browser', 'win', 'win32_desktop_notifications', + 'desktop_notification_controller.cc'), + os.path.join('brightray', 'browser', 'win', 'win32_desktop_notifications', + 'desktop_notification_controller.h') ] SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) @@ -24,15 +33,20 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) def main(): os.chdir(SOURCE_ROOT) - files = list_files(['app', 'browser', 'common', 'renderer', 'utility'], - ['*.cc', '*.h']) - call_cpplint(list(set(files) - set(IGNORE_FILES))) + atom_files = list_files('atom', + ['app', 'browser', 'common', 'renderer', 'utility'], + ['*.cc', '*.h']) + call_cpplint(list(set(atom_files) - set(IGNORE_FILES))) + + brightray_files = list_files('brightray', ['browser', 'common'], + ['*.cc', '*.h']) + call_cpplint(list(set(brightray_files) - set(IGNORE_FILES))) -def list_files(directories, filters): +def list_files(parent, directories, filters): matches = [] for directory in directories: - for root, _, filenames, in os.walk(os.path.join('atom', directory)): + for root, _, filenames, in os.walk(os.path.join(parent, directory)): for f in filters: for filename in fnmatch.filter(filenames, f): matches.append(os.path.join(root, filename)) diff --git a/script/create-dist.py b/script/create-dist.py index 2602a9f943dc..3c61f06ac066 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -20,8 +20,8 @@ ELECTRON_VERSION = get_electron_version() SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) DIST_DIR = os.path.join(SOURCE_ROOT, 'dist') OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'R') -CHROMIUM_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', - 'download', 'libchromiumcontent', 'static_library') +CHROMIUM_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'download', + 'libchromiumcontent', 'static_library') PROJECT_NAME = electron_gyp()['project_name%'] PRODUCT_NAME = electron_gyp()['product_name%'] diff --git a/script/create-node-headers.py b/script/create-node-headers.py index baf9c86aba30..992cd1657514 100755 --- a/script/create-node-headers.py +++ b/script/create-node-headers.py @@ -74,8 +74,8 @@ def copy_headers(dist_headers_dir): dist_headers_dir) # Copy V8 headers from chromium's repository. - src = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', 'download', - 'libchromiumcontent', 'src') + src = os.path.join(SOURCE_ROOT, 'vendor', 'download', 'libchromiumcontent', + 'src') for dirpath, _, filenames in os.walk(os.path.join(src, 'v8')): for filename in filenames: extension = os.path.splitext(filename)[1] diff --git a/script/dump-symbols.py b/script/dump-symbols.py index 76949e95aa92..fdc90b613f66 100755 --- a/script/dump-symbols.py +++ b/script/dump-symbols.py @@ -10,8 +10,8 @@ from lib.util import electron_gyp, execute, rm_rf SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) DIST_DIR = os.path.join(SOURCE_ROOT, 'dist') OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'R') -CHROMIUM_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', - 'download', 'libchromiumcontent', 'static_library') +CHROMIUM_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'download', + 'libchromiumcontent', 'static_library') def main(destination): diff --git a/script/lib/config.py b/script/lib/config.py index c920f2acc22c..e23c18d9942a 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -31,8 +31,8 @@ def get_platform_key(): def get_target_arch(): try: target_arch_path = os.path.join(__file__, '..', '..', '..', 'vendor', - 'brightray', 'vendor', 'download', - 'libchromiumcontent', '.target_arch') + 'download', 'libchromiumcontent', + '.target_arch') with open(os.path.normpath(target_arch_path)) as f: return f.read().strip() except IOError as e: diff --git a/script/update.py b/script/update.py index 35331eb93626..b4f08bc28f2c 100755 --- a/script/update.py +++ b/script/update.py @@ -63,7 +63,7 @@ def run_gyp(target_arch, component): if sys.platform == 'cygwin': # Force using win32 python on cygwin. python = os.path.join('vendor', 'python_26', 'python.exe') - gyp = os.path.join('vendor', 'brightray', 'vendor', 'gyp', 'gyp_main.py') + gyp = os.path.join('vendor', 'gyp', 'gyp_main.py') gyp_pylib = os.path.join(os.path.dirname(gyp), 'pylib') # Avoid using the old gyp lib in system. env['PYTHONPATH'] = os.path.pathsep.join([gyp_pylib, diff --git a/script/upload-windows-pdb.py b/script/upload-windows-pdb.py index 6e8030337f3c..40c38b30739c 100755 --- a/script/upload-windows-pdb.py +++ b/script/upload-windows-pdb.py @@ -10,7 +10,7 @@ from lib.util import electron_gyp, execute, rm_rf, safe_mkdir, s3put SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) SYMBOLS_DIR = 'dist\\symbols' -DOWNLOAD_DIR = 'vendor\\brightray\\vendor\\download\\libchromiumcontent' +DOWNLOAD_DIR = 'vendor\\download\\libchromiumcontent' PROJECT_NAME = electron_gyp()['project_name%'] PRODUCT_NAME = electron_gyp()['product_name%'] diff --git a/script/verify-ffmpeg.py b/script/verify-ffmpeg.py index 9d9dba067030..1d78515a4ade 100755 --- a/script/verify-ffmpeg.py +++ b/script/verify-ffmpeg.py @@ -9,8 +9,8 @@ from lib.util import electron_gyp, rm_rf SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) -FFMPEG_LIBCC_PATH = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor', - 'download', 'libchromiumcontent', 'ffmpeg') +FFMPEG_LIBCC_PATH = os.path.join(SOURCE_ROOT, 'vendor', 'download', + 'libchromiumcontent', 'ffmpeg') PROJECT_NAME = electron_gyp()['project_name%'] PRODUCT_NAME = electron_gyp()['product_name%'] diff --git a/tools/mac/change_mach_o_flags.py b/tools/mac/change_mach_o_flags.py new file mode 100755 index 000000000000..4547cb75f7ab --- /dev/null +++ b/tools/mac/change_mach_o_flags.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python +# Copyright (c) 2011 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-CHROMIUM file. + +"""Usage: change_mach_o_flags.py [--executable-heap] [--no-pie] + +Arranges for the executable at |executable_path| to have its data (heap) +pages protected to prevent execution on Mac OS X 10.7 ("Lion"), and to have +the PIE (position independent executable) bit set to enable ASLR (address +space layout randomization). With --executable-heap or --no-pie, the +respective bits are cleared instead of set, making the heap executable or +disabling PIE/ASLR. + +This script is able to operate on thin (single-architecture) Mach-O files +and fat (universal, multi-architecture) files. When operating on fat files, +it will set or clear the bits for each architecture contained therein. + +NON-EXECUTABLE HEAP + +Traditionally in Mac OS X, 32-bit processes did not have data pages set to +prohibit execution. Although user programs could call mprotect and +mach_vm_protect to deny execution of code in data pages, the kernel would +silently ignore such requests without updating the page tables, and the +hardware would happily execute code on such pages. 64-bit processes were +always given proper hardware protection of data pages. This behavior was +controllable on a system-wide level via the vm.allow_data_exec sysctl, which +is set by default to 1. The bit with value 1 (set by default) allows code +execution on data pages for 32-bit processes, and the bit with value 2 +(clear by default) does the same for 64-bit processes. + +In Mac OS X 10.7, executables can "opt in" to having hardware protection +against code execution on data pages applied. This is done by setting a new +bit in the |flags| field of an executable's |mach_header|. When +MH_NO_HEAP_EXECUTION is set, proper protections will be applied, regardless +of the setting of vm.allow_data_exec. See xnu-1699.22.73/osfmk/vm/vm_map.c +override_nx and xnu-1699.22.73/bsd/kern/mach_loader.c load_machfile. + +The Apple toolchain has been revised to set the MH_NO_HEAP_EXECUTION when +producing executables, provided that -allow_heap_execute is not specified +at link time. Only linkers shipping with Xcode 4.0 and later (ld64-123.2 and +later) have this ability. See ld64-123.2.1/src/ld/Options.cpp +Options::reconfigureDefaults() and +ld64-123.2.1/src/ld/HeaderAndLoadCommands.hpp +HeaderAndLoadCommandsAtom::flags(). + +This script sets the MH_NO_HEAP_EXECUTION bit on Mach-O executables. It is +intended for use with executables produced by a linker that predates Apple's +modifications to set this bit itself. It is also useful for setting this bit +for non-i386 executables, including x86_64 executables. Apple's linker only +sets it for 32-bit i386 executables, presumably under the assumption that +the value of vm.allow_data_exec is set in stone. However, if someone were to +change vm.allow_data_exec to 2 or 3, 64-bit x86_64 executables would run +without hardware protection against code execution on data pages. This +script can set the bit for x86_64 executables, guaranteeing that they run +with appropriate protection even when vm.allow_data_exec has been tampered +with. + +POSITION-INDEPENDENT EXECUTABLES/ADDRESS SPACE LAYOUT RANDOMIZATION + +This script sets or clears the MH_PIE bit in an executable's Mach-O header, +enabling or disabling position independence on Mac OS X 10.5 and later. +Processes running position-independent executables have varying levels of +ASLR protection depending on the OS release. The main executable's load +address, shared library load addresess, and the heap and stack base +addresses may be randomized. Position-independent executables are produced +by supplying the -pie flag to the linker (or defeated by supplying -no_pie). +Executables linked with a deployment target of 10.7 or higher have PIE on +by default. + +This script is never strictly needed during the build to enable PIE, as all +linkers used are recent enough to support -pie. However, it's used to +disable the PIE bit as needed on already-linked executables. +""" + +import optparse +import os +import struct +import sys + + +# +FAT_MAGIC = 0xcafebabe +FAT_CIGAM = 0xbebafeca + +# +MH_MAGIC = 0xfeedface +MH_CIGAM = 0xcefaedfe +MH_MAGIC_64 = 0xfeedfacf +MH_CIGAM_64 = 0xcffaedfe +MH_EXECUTE = 0x2 +MH_PIE = 0x00200000 +MH_NO_HEAP_EXECUTION = 0x01000000 + + +class MachOError(Exception): + """A class for exceptions thrown by this module.""" + + pass + + +def CheckedSeek(file, offset): + """Seeks the file-like object at |file| to offset |offset| and raises a + MachOError if anything funny happens.""" + + file.seek(offset, os.SEEK_SET) + new_offset = file.tell() + if new_offset != offset: + raise MachOError, \ + 'seek: expected offset %d, observed %d' % (offset, new_offset) + + +def CheckedRead(file, count): + """Reads |count| bytes from the file-like |file| object, raising a + MachOError if any other number of bytes is read.""" + + bytes = file.read(count) + if len(bytes) != count: + raise MachOError, \ + 'read: expected length %d, observed %d' % (count, len(bytes)) + + return bytes + + +def ReadUInt32(file, endian): + """Reads an unsinged 32-bit integer from the file-like |file| object, + treating it as having endianness specified by |endian| (per the |struct| + module), and returns it as a number. Raises a MachOError if the proper + length of data can't be read from |file|.""" + + bytes = CheckedRead(file, 4) + + (uint32,) = struct.unpack(endian + 'I', bytes) + return uint32 + + +def ReadMachHeader(file, endian): + """Reads an entire |mach_header| structure () from the + file-like |file| object, treating it as having endianness specified by + |endian| (per the |struct| module), and returns a 7-tuple of its members + as numbers. Raises a MachOError if the proper length of data can't be read + from |file|.""" + + bytes = CheckedRead(file, 28) + + magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \ + struct.unpack(endian + '7I', bytes) + return magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags + + +def ReadFatArch(file): + """Reads an entire |fat_arch| structure () from the file-like + |file| object, treating it as having endianness specified by |endian| + (per the |struct| module), and returns a 5-tuple of its members as numbers. + Raises a MachOError if the proper length of data can't be read from + |file|.""" + + bytes = CheckedRead(file, 20) + + cputype, cpusubtype, offset, size, align = struct.unpack('>5I', bytes) + return cputype, cpusubtype, offset, size, align + + +def WriteUInt32(file, uint32, endian): + """Writes |uint32| as an unsinged 32-bit integer to the file-like |file| + object, treating it as having endianness specified by |endian| (per the + |struct| module).""" + + bytes = struct.pack(endian + 'I', uint32) + assert len(bytes) == 4 + + file.write(bytes) + + +def HandleMachOFile(file, options, offset=0): + """Seeks the file-like |file| object to |offset|, reads its |mach_header|, + and rewrites the header's |flags| field if appropriate. The header's + endianness is detected. Both 32-bit and 64-bit Mach-O headers are supported + (mach_header and mach_header_64). Raises MachOError if used on a header that + does not have a known magic number or is not of type MH_EXECUTE. The + MH_PIE and MH_NO_HEAP_EXECUTION bits are set or cleared in the |flags| field + according to |options| and written to |file| if any changes need to be made. + If already set or clear as specified by |options|, nothing is written.""" + + CheckedSeek(file, offset) + magic = ReadUInt32(file, '<') + if magic == MH_MAGIC or magic == MH_MAGIC_64: + endian = '<' + elif magic == MH_CIGAM or magic == MH_CIGAM_64: + endian = '>' + else: + raise MachOError, \ + 'Mach-O file at offset %d has illusion of magic' % offset + + CheckedSeek(file, offset) + magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \ + ReadMachHeader(file, endian) + assert magic == MH_MAGIC or magic == MH_MAGIC_64 + if filetype != MH_EXECUTE: + raise MachOError, \ + 'Mach-O file at offset %d is type 0x%x, expected MH_EXECUTE' % \ + (offset, filetype) + + original_flags = flags + + if options.no_heap_execution: + flags |= MH_NO_HEAP_EXECUTION + else: + flags &= ~MH_NO_HEAP_EXECUTION + + if options.pie: + flags |= MH_PIE + else: + flags &= ~MH_PIE + + if flags != original_flags: + CheckedSeek(file, offset + 24) + WriteUInt32(file, flags, endian) + + +def HandleFatFile(file, options, fat_offset=0): + """Seeks the file-like |file| object to |offset| and loops over its + |fat_header| entries, calling HandleMachOFile for each.""" + + CheckedSeek(file, fat_offset) + magic = ReadUInt32(file, '>') + assert magic == FAT_MAGIC + + nfat_arch = ReadUInt32(file, '>') + + for index in xrange(0, nfat_arch): + cputype, cpusubtype, offset, size, align = ReadFatArch(file) + assert size >= 28 + + # HandleMachOFile will seek around. Come back here after calling it, in + # case it sought. + fat_arch_offset = file.tell() + HandleMachOFile(file, options, offset) + CheckedSeek(file, fat_arch_offset) + + +def main(me, args): + parser = optparse.OptionParser('%prog [options] ') + parser.add_option('--executable-heap', action='store_false', + dest='no_heap_execution', default=True, + help='Clear the MH_NO_HEAP_EXECUTION bit') + parser.add_option('--no-pie', action='store_false', + dest='pie', default=True, + help='Clear the MH_PIE bit') + (options, loose_args) = parser.parse_args(args) + if len(loose_args) != 1: + parser.print_usage() + return 1 + + executable_path = loose_args[0] + executable_file = open(executable_path, 'rb+') + + magic = ReadUInt32(executable_file, '<') + if magic == FAT_CIGAM: + # Check FAT_CIGAM and not FAT_MAGIC because the read was little-endian. + HandleFatFile(executable_file, options) + elif magic == MH_MAGIC or magic == MH_CIGAM or \ + magic == MH_MAGIC_64 or magic == MH_CIGAM_64: + HandleMachOFile(executable_file, options) + else: + raise MachOError, '%s is not a Mach-O or fat file' % executable_file + + executable_file.close() + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[0], sys.argv[1:])) diff --git a/tools/mac/make_more_helpers.sh b/tools/mac/make_more_helpers.sh new file mode 100755 index 000000000000..ee4f21105b44 --- /dev/null +++ b/tools/mac/make_more_helpers.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# Copyright (c) 2012 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-CHROMIUM file. + +# Usage: make_more_helpers.sh +# +# This script creates additional helper .app bundles for Chromium, based on +# the existing helper .app bundle, changing their Mach-O header's flags to +# enable and disable various features. Based on Chromium Helper.app, it will +# create Chromium Helper EH.app, which has the MH_NO_HEAP_EXECUTION bit +# cleared to support Chromium child processes that require an executable heap, +# and Chromium Helper NP.app, which has the MH_PIE bit cleared to support +# Chromium child processes that cannot tolerate ASLR. +# +# This script expects to be called from the chrome_exe target as a postbuild, +# and operates directly within the built-up browser app's versioned directory. +# +# Each helper is adjusted by giving it the proper bundle name, renaming the +# executable, adjusting several Info.plist keys, and changing the executable's +# Mach-O flags. + +set -eu + +make_helper() { + local containing_dir="${1}" + local app_name="${2}" + local feature="${3}" + local flags="${4}" + + local helper_name="${app_name} Helper" + local helper_stem="${containing_dir}/${helper_name}" + local original_helper="${helper_stem}.app" + if [[ ! -d "${original_helper}" ]]; then + echo "${0}: error: ${original_helper} is a required directory" >& 2 + exit 1 + fi + local original_helper_exe="${original_helper}/Contents/MacOS/${helper_name}" + if [[ ! -f "${original_helper_exe}" ]]; then + echo "${0}: error: ${original_helper_exe} is a required file" >& 2 + exit 1 + fi + + local feature_helper="${helper_stem} ${feature}.app" + + rsync -acC --delete --include '*.so' "${original_helper}/" "${feature_helper}" + + local helper_feature="${helper_name} ${feature}" + local helper_feature_exe="${feature_helper}/Contents/MacOS/${helper_feature}" + mv "${feature_helper}/Contents/MacOS/${helper_name}" "${helper_feature_exe}" + + local change_flags="$(dirname "${0}")/change_mach_o_flags.py" + "${change_flags}" ${flags} "${helper_feature_exe}" + + local feature_info="${feature_helper}/Contents/Info" + local feature_info_plist="${feature_info}.plist" + + defaults write "${feature_info}" "CFBundleDisplayName" "${helper_feature}" + defaults write "${feature_info}" "CFBundleExecutable" "${helper_feature}" + + cfbundleid="$(defaults read "${feature_info}" "CFBundleIdentifier")" + feature_cfbundleid="${cfbundleid}.${feature}" + defaults write "${feature_info}" "CFBundleIdentifier" "${feature_cfbundleid}" + + cfbundlename="$(defaults read "${feature_info}" "CFBundleName")" + feature_cfbundlename="${cfbundlename} ${feature}" + defaults write "${feature_info}" "CFBundleName" "${feature_cfbundlename}" + + # As usual, defaults might have put the plist into whatever format excites + # it, but Info.plists get converted back to the expected XML format. + plutil -convert xml1 "${feature_info_plist}" + + # `defaults` also changes the file permissions, so make the file + # world-readable again. + chmod a+r "${feature_info_plist}" +} + +if [[ ${#} -ne 2 ]]; then + echo "usage: ${0} " >& 2 + exit 1 +fi + +DIRECTORY_WITHIN_CONTENTS="${1}" +APP_NAME="${2}" + +CONTENTS_DIR="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}" +CONTAINING_DIR="${CONTENTS_DIR}/${DIRECTORY_WITHIN_CONTENTS}" + +make_helper "${CONTAINING_DIR}" "${APP_NAME}" "EH" "--executable-heap" +make_helper "${CONTAINING_DIR}" "${APP_NAME}" "NP" "--no-pie" diff --git a/vendor/brightray b/vendor/brightray deleted file mode 160000 index 3d9ebb549990..000000000000 --- a/vendor/brightray +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3d9ebb549990d50ab67b705b8ff16433a3dbc546 diff --git a/vendor/gyp b/vendor/gyp new file mode 160000 index 000000000000..549e55a22765 --- /dev/null +++ b/vendor/gyp @@ -0,0 +1 @@ +Subproject commit 549e55a22765ccf99e46b419b2dff6338bcac64a diff --git a/vendor/libchromiumcontent b/vendor/libchromiumcontent new file mode 160000 index 000000000000..44a803957cb2 --- /dev/null +++ b/vendor/libchromiumcontent @@ -0,0 +1 @@ +Subproject commit 44a803957cb27610c38f7e859016ac257959aeb5