diff --git a/.gitignore b/.gitignore index d9f918d109b..3f1279eebf4 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 0cabff390f4..73b12931d77 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/README.md b/README.md index f32fbfab929..70107cb2317 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ contains documents describing how to build and contribute to Electron. - [Ukrainian](https://github.com/electron/electron/tree/master/docs-translations/uk-UA) - [Russian](https://github.com/electron/electron/tree/master/docs-translations/ru-RU) - [French](https://github.com/electron/electron/tree/master/docs-translations/fr-FR) +- [Indonesian](https://github.com/electron/electron/tree/master/docs-translations/id) ## Quick Start diff --git a/atom/app/atom_content_client.cc b/atom/app/atom_content_client.cc index 8e2a6c57309..09683211c58 100644 --- a/atom/app/atom_content_client.cc +++ b/atom/app/atom_content_client.cc @@ -93,9 +93,9 @@ content::PepperPluginInfo CreateWidevineCdmInfo(const base::FilePath& path, std::vector codecs; codecs.push_back(kCdmSupportedCodecVp8); codecs.push_back(kCdmSupportedCodecVp9); -#if defined(USE_PROPRIETARY_CODECS) +#if BUILDFLAG(USE_PROPRIETARY_CODECS) codecs.push_back(kCdmSupportedCodecAvc1); -#endif // defined(USE_PROPRIETARY_CODECS) +#endif // BUILDFLAG(USE_PROPRIETARY_CODECS) std::string codec_string = base::JoinString( codecs, std::string(1, kCdmSupportedCodecsValueDelimiter)); widevine_cdm_mime_type.additional_param_names.push_back( @@ -198,11 +198,19 @@ base::string16 AtomContentClient::GetLocalizedString(int message_id) const { return l10n_util::GetStringUTF16(message_id); } -void AtomContentClient::AddAdditionalSchemes( - std::vector* standard_schemes, - std::vector* referrer_schemes, - std::vector* savable_schemes) { - standard_schemes->push_back({"chrome-extension", url::SCHEME_WITHOUT_PORT}); +void AtomContentClient::AddAdditionalSchemes(Schemes* schemes) { + schemes->standard_schemes.push_back("chrome-extension"); + + std::vector splited; + ConvertStringWithSeparatorToVector(&splited, ",", + switches::kRegisterServiceWorkerSchemes); + for (const std::string& scheme : splited) + schemes->service_worker_schemes.push_back(scheme); + schemes->service_worker_schemes.push_back(url::kFileScheme); + + ConvertStringWithSeparatorToVector(&splited, ",", switches::kSecureSchemes); + for (const std::string& scheme : splited) + schemes->secure_schemes.push_back(scheme); } void AtomContentClient::AddPepperPlugins( @@ -214,25 +222,4 @@ void AtomContentClient::AddPepperPlugins( ComputeBuiltInPlugins(plugins); } -void AtomContentClient::AddServiceWorkerSchemes( - std::set* service_worker_schemes) { - std::vector schemes; - ConvertStringWithSeparatorToVector(&schemes, ",", - switches::kRegisterServiceWorkerSchemes); - for (const std::string& scheme : schemes) - service_worker_schemes->insert(scheme); - - service_worker_schemes->insert(url::kFileScheme); -} - -void AtomContentClient::AddSecureSchemesAndOrigins( - std::set* secure_schemes, - std::set* secure_origins) { - std::vector schemes; - ConvertStringWithSeparatorToVector(&schemes, ",", switches::kSecureSchemes); - for (const std::string& scheme : schemes) - secure_schemes->insert(scheme); -} - - } // namespace atom diff --git a/atom/app/atom_content_client.h b/atom/app/atom_content_client.h index e396dc23c8e..fcd26f84183 100644 --- a/atom/app/atom_content_client.h +++ b/atom/app/atom_content_client.h @@ -23,17 +23,9 @@ class AtomContentClient : public brightray::ContentClient { std::string GetProduct() const override; std::string GetUserAgent() const override; base::string16 GetLocalizedString(int message_id) const override; - void AddAdditionalSchemes( - std::vector* standard_schemes, - std::vector* referrer_schemes, - std::vector* savable_schemes) override; + void AddAdditionalSchemes(Schemes* schemes) override; void AddPepperPlugins( std::vector* plugins) override; - void AddServiceWorkerSchemes( - std::set* service_worker_schemes) override; - void AddSecureSchemesAndOrigins( - std::set* secure_schemes, - std::set* secure_origins) override; private: DISALLOW_COPY_AND_ASSIGN(AtomContentClient); diff --git a/atom/app/node_main.cc b/atom/app/node_main.cc index 9e80ef2e5d2..210d71d01e1 100644 --- a/atom/app/node_main.cc +++ b/atom/app/node_main.cc @@ -6,7 +6,6 @@ #include "atom/app/uv_task_runner.h" #include "atom/browser/javascript_environment.h" -#include "atom/browser/node_debugger.h" #include "atom/common/api/atom_bindings.h" #include "atom/common/crash_reporter/crash_reporter.h" #include "atom/common/native_mate_converters/string16_converter.h" @@ -51,11 +50,6 @@ int NodeMain(int argc, char *argv[]) { &isolate_data, gin_env.context(), argc, argv, exec_argc, exec_argv); - // Start our custom debugger implementation. - NodeDebugger node_debugger(gin_env.isolate()); - if (node_debugger.IsRunning()) - env->AssignToContext(v8::Debug::GetDebugContext(gin_env.isolate())); - mate::Dictionary process(gin_env.isolate(), env->process_object()); #if defined(OS_WIN) process.SetMethod("log", &AtomBindings::Log); diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 1024bb0c6cc..cf5cb4386cb 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -464,8 +464,8 @@ int ImportIntoCertStore( if (!cert_path.empty()) { if (base::ReadFileToString(base::FilePath(cert_path), &file_data)) { - auto module = model->cert_db()->GetPublicModule(); - rv = model->ImportFromPKCS12(module, + auto module = model->cert_db()->GetPrivateSlot(); + rv = model->ImportFromPKCS12(module.get(), file_data, password, true, diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc index 2ce9e12f6c5..9f58922fd0a 100644 --- a/atom/browser/api/atom_api_cookies.cc +++ b/atom/browser/api/atom_api_cookies.cc @@ -62,6 +62,10 @@ struct Converter { switch (val) { case net::CookieStore::ChangeCause::INSERTED: case net::CookieStore::ChangeCause::EXPLICIT: + case net::CookieStore::ChangeCause::EXPLICIT_DELETE_BETWEEN: + case net::CookieStore::ChangeCause::EXPLICIT_DELETE_PREDICATE: + case net::CookieStore::ChangeCause::EXPLICIT_DELETE_SINGLE: + case net::CookieStore::ChangeCause::EXPLICIT_DELETE_CANONICAL: return mate::StringToV8(isolate, "explicit"); case net::CookieStore::ChangeCause::OVERWRITE: return mate::StringToV8(isolate, "overwrite"); @@ -228,8 +232,8 @@ void SetCookieOnIO(scoped_refptr getter, GetCookieStore(getter)->SetCookieWithDetailsAsync( GURL(url), name, value, domain, path, creation_time, expiration_time, last_access_time, secure, http_only, - net::CookieSameSite::DEFAULT_MODE, false, - net::COOKIE_PRIORITY_DEFAULT, base::Bind(OnSetCookie, callback)); + net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT, + base::Bind(OnSetCookie, callback)); } } // namespace diff --git a/atom/browser/api/atom_api_desktop_capturer.cc b/atom/browser/api/atom_api_desktop_capturer.cc index 118b3e3bdc6..79feb7520b0 100644 --- a/atom/browser/api/atom_api_desktop_capturer.cc +++ b/atom/browser/api/atom_api_desktop_capturer.cc @@ -4,6 +4,8 @@ #include "atom/browser/api/atom_api_desktop_capturer.h" +using base::PlatformThreadRef; + #include "atom/common/api/atom_api_native_image.h" #include "atom/common/native_mate_converters/gfx_converter.h" #include "base/strings/utf_string_conversions.h" diff --git a/atom/browser/api/atom_api_protocol.cc b/atom/browser/api/atom_api_protocol.cc index e3b15ed4329..4ff8c5c6bab 100644 --- a/atom/browser/api/atom_api_protocol.cc +++ b/atom/browser/api/atom_api_protocol.cc @@ -50,20 +50,25 @@ void RegisterStandardSchemes(const std::vector& schemes, mate::Arguments* args) { g_standard_schemes = schemes; + mate::Dictionary opts; + bool secure = false; + args->GetNext(&opts) && opts.Get("secure", &secure); + + // Dynamically register the schemes. auto* policy = content::ChildProcessSecurityPolicy::GetInstance(); for (const std::string& scheme : schemes) { url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITHOUT_PORT); + if (secure) { + url::AddSecureScheme(scheme.c_str()); + } policy->RegisterWebSafeScheme(scheme); } - // add switches to register as standard + // Add the schemes to command line switches, so child processes can also + // register them. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( atom::switches::kStandardSchemes, base::JoinString(schemes, ",")); - - mate::Dictionary opts; - bool secure = false; - if (args->GetNext(&opts) && opts.Get("secure", &secure) && secure) { - // add switches to register as secure + if (secure) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( atom::switches::kSecureSchemes, base::JoinString(schemes, ",")); } diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 83d103a631f..3a2d5f44007 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -432,7 +432,8 @@ void DownloadIdCallback(content::DownloadManager* download_manager, last_modified, offset, length, std::string(), content::DownloadItem::INTERRUPTED, content::DownloadDangerType::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, - content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT, false); + content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT, false, + std::vector()); } } // namespace diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index b40deaee1c6..3aba1babef5 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -13,6 +13,8 @@ #include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" +#include "atom/browser/atom_javascript_dialog_manager.h" +#include "atom/browser/child_web_contents_tracker.h" #include "atom/browser/lib/bluetooth_chooser.h" #include "atom/browser/native_window.h" #include "atom/browser/net/atom_network_delegate.h" @@ -39,6 +41,7 @@ #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/options_switches.h" +#include "base/process/process_handle.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" @@ -49,6 +52,7 @@ #include "chrome/browser/ssl/security_state_tab_helper.h" #include "content/browser/frame_host/navigation_entry_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/view_messages.h" #include "content/public/browser/favicon_status.h" @@ -78,6 +82,7 @@ #include "third_party/WebKit/public/platform/WebInputEvent.h" #include "third_party/WebKit/public/web/WebFindOptions.h" #include "ui/display/screen.h" +#include "ui/events/base_event_utils.h" #if !defined(OS_MACOSX) #include "ui/aura/window.h" @@ -332,6 +337,9 @@ WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options) else if (options.Get("offscreen", &b) && b) type_ = OFF_SCREEN; + // Init embedder earlier + options.Get("embedder", &embedder_); + // Whether to enable DevTools. options.Get("devTools", &enable_devtools_); @@ -356,7 +364,18 @@ WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options) session->browser_context(), site_instance); guest_delegate_.reset(new WebViewGuestDelegate); params.guest_delegate = guest_delegate_.get(); - web_contents = content::WebContents::Create(params); + + if (embedder_ && embedder_->IsOffScreen()) { + auto* view = new OffScreenWebContentsView(false, + base::Bind(&WebContents::OnPaint, base::Unretained(this))); + params.view = view; + params.delegate_view = view; + + web_contents = content::WebContents::Create(params); + view->SetWebContents(web_contents); + } else { + web_contents = content::WebContents::Create(params); + } } else if (IsOffScreen()) { bool transparent = false; options.Get("transparent", &transparent); @@ -406,7 +425,7 @@ void WebContents::InitWithSessionAndOptions(v8::Isolate* isolate, guest_delegate_->Initialize(this); NativeWindow* owner_window = nullptr; - if (options.Get("embedder", &embedder_) && embedder_) { + if (embedder_) { // New WebContents's owner_window is the embedder's owner_window. auto relay = NativeWindowRelay::FromWebContents(embedder_->web_contents()); @@ -502,6 +521,7 @@ void WebContents::AddNewContents(content::WebContents* source, const gfx::Rect& initial_rect, bool user_gesture, bool* was_blocked) { + new ChildWebContentsTracker(new_contents); v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); auto api_web_contents = CreateFrom(isolate(), new_contents); @@ -585,8 +605,8 @@ bool WebContents::PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { - if (event.type == blink::WebInputEvent::Type::RawKeyDown - || event.type == blink::WebInputEvent::Type::KeyUp) + if (event.type() == blink::WebInputEvent::Type::RawKeyDown || + event.type() == blink::WebInputEvent::Type::KeyUp) return Emit("before-input-event", event); else return false; @@ -698,6 +718,15 @@ std::unique_ptr WebContents::RunBluetoothChooser( return std::move(bluetooth_chooser); } +content::JavaScriptDialogManager* +WebContents::GetJavaScriptDialogManager( + content::WebContents* source) { + if (!dialog_manager_) + dialog_manager_.reset(new AtomJavaScriptDialogManager(this)); + + return dialog_manager_.get(); +} + void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) { // Do nothing, we override this method just to avoid compilation error since // there are two virtual functions named BeforeUnloadFired. @@ -890,6 +919,15 @@ void WebContents::Observe(int type, } } +void WebContents::BeforeUnloadDialogCancelled() { + if (deferred_load_url_.id) { + auto& controller = web_contents()->GetController(); + if (!controller.GetPendingEntry()) { + deferred_load_url_.id = 0; + } + } +} + void WebContents::DevToolsReloadPage() { Emit("devtools-reload-page"); } @@ -906,7 +944,7 @@ void WebContents::DevToolsOpened() { devtools_web_contents_.Reset(isolate(), handle.ToV8()); // Set inspected tabID. - base::FundamentalValue tab_id(ID()); + base::Value tab_id(ID()); managed_web_contents()->CallClientFunction( "DevToolsAPI.setInspectedTabId", &tab_id, nullptr, nullptr); @@ -979,7 +1017,7 @@ void WebContents::NavigationEntryCommitted( int64_t WebContents::GetID() const { int64_t process_id = web_contents()->GetRenderProcessHost()->GetID(); - int64_t routing_id = web_contents()->GetRoutingID(); + int64_t routing_id = web_contents()->GetRenderViewHost()->GetRoutingID(); int64_t rv = (process_id << 32) + routing_id; return rv; } @@ -988,6 +1026,11 @@ int WebContents::GetProcessID() const { return web_contents()->GetRenderProcessHost()->GetID(); } +base::ProcessId WebContents::GetOSProcessID() const { + auto process_handle = web_contents()->GetRenderProcessHost()->GetHandle(); + return base::GetProcId(process_handle); +} + WebContents::Type WebContents::GetType() const { return type_; } @@ -1345,7 +1388,7 @@ void WebContents::SelectAll() { } void WebContents::Unselect() { - web_contents()->Unselect(); + web_contents()->CollapseSelection(); } void WebContents::Replace(const base::string16& word) { @@ -1420,30 +1463,31 @@ bool WebContents::SendIPCMessage(bool all_frames, void WebContents::SendInputEvent(v8::Isolate* isolate, v8::Local input_event) { - const auto view = web_contents()->GetRenderWidgetHostView(); + const auto view = static_cast( + web_contents()->GetRenderWidgetHostView()); if (!view) return; - const auto host = view->GetRenderWidgetHost(); - if (!host) - return; int type = mate::GetWebInputEventType(isolate, input_event); if (blink::WebInputEvent::isMouseEventType(type)) { blink::WebMouseEvent mouse_event; if (mate::ConvertFromV8(isolate, input_event, &mouse_event)) { - host->ForwardMouseEvent(mouse_event); + view->ProcessMouseEvent(mouse_event, ui::LatencyInfo()); return; } } else if (blink::WebInputEvent::isKeyboardEventType(type)) { - content::NativeWebKeyboardEvent keyboard_event; + content::NativeWebKeyboardEvent keyboard_event( + blink::WebKeyboardEvent::RawKeyDown, + blink::WebInputEvent::NoModifiers, + ui::EventTimeForNow()); if (mate::ConvertFromV8(isolate, input_event, &keyboard_event)) { - host->ForwardKeyboardEvent(keyboard_event); + view->ProcessKeyboardEvent(keyboard_event); return; } } else if (type == blink::WebInputEvent::MouseWheel) { blink::WebMouseWheelEvent mouse_wheel_event; if (mate::ConvertFromV8(isolate, input_event, &mouse_wheel_event)) { - host->ForwardWheelEvent(mouse_wheel_event); + view->ProcessMouseWheelEvent(mouse_wheel_event, ui::LatencyInfo()); return; } } @@ -1525,8 +1569,7 @@ void WebContents::CapturePage(mate::Arguments* args) { } const auto view = web_contents()->GetRenderWidgetHostView(); - const auto host = view ? view->GetRenderWidgetHost() : nullptr; - if (!view || !host) { + if (!view) { callback.Run(gfx::Image()); return; } @@ -1546,10 +1589,10 @@ void WebContents::CapturePage(mate::Arguments* args) { if (scale > 1.0f) bitmap_size = gfx::ScaleToCeiledSize(view_size, scale); - host->CopyFromBackingStore(gfx::Rect(rect.origin(), view_size), - bitmap_size, - base::Bind(&OnCapturePageDone, callback), - kBGRA_8888_SkColorType); + view->CopyFromSurface(gfx::Rect(rect.origin(), view_size), + bitmap_size, + base::Bind(&OnCapturePageDone, callback), + kBGRA_8888_SkColorType); } void WebContents::OnCursorChange(const content::WebCursor& cursor) { @@ -1581,9 +1624,7 @@ bool WebContents::IsOffScreen() const { } void WebContents::OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap) { - mate::Handle image = - NativeImage::Create(isolate(), gfx::Image::CreateFrom1xBitmap(bitmap)); - Emit("paint", dirty_rect, image); + Emit("paint", dirty_rect, gfx::Image::CreateFrom1xBitmap(bitmap)); } void WebContents::StartPainting() { @@ -1647,6 +1688,18 @@ void WebContents::Invalidate() { } } +gfx::Size WebContents::GetSizeForNewRenderView( + content::WebContents* wc) const { + if (IsOffScreen() && wc == web_contents()) { + auto relay = NativeWindowRelay::FromWebContents(web_contents()); + if (relay) { + return relay->window->GetSize(); + } + } + + return gfx::Size(); +} + void WebContents::SetZoomLevel(double level) { zoom_controller_->SetZoomLevel(level); } @@ -1747,6 +1800,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .MakeDestroyable() .SetMethod("getId", &WebContents::GetID) .SetMethod("getProcessId", &WebContents::GetProcessID) + .SetMethod("getOSProcessId", &WebContents::GetOSProcessID) .SetMethod("equal", &WebContents::Equal) .SetMethod("_loadURL", &WebContents::LoadURL) .SetMethod("downloadURL", &WebContents::DownloadURL) diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 2e5a89a2764..4bb3b2c0894 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -42,6 +42,7 @@ namespace atom { struct SetSizeParams; class AtomBrowserContext; +class AtomJavaScriptDialogManager; class WebContentsZoomController; class WebViewGuestDelegate; @@ -83,6 +84,7 @@ class WebContents : public mate::TrackableObject, int64_t GetID() const; int GetProcessID() const; + base::ProcessId GetOSProcessID() const; Type GetType() const; bool Equal(const WebContents* web_contents) const; void LoadURL(const GURL& url, const mate::Dictionary& options); @@ -185,6 +187,7 @@ class WebContents : public mate::TrackableObject, void SetFrameRate(int frame_rate); int GetFrameRate() const; void Invalidate(); + gfx::Size GetSizeForNewRenderView(content::WebContents*) const override; // Methods for zoom handling. void SetZoomLevel(double level); @@ -298,6 +301,8 @@ class WebContents : public mate::TrackableObject, std::unique_ptr RunBluetoothChooser( content::RenderFrameHost* frame, const content::BluetoothChooser::EventHandler& handler) override; + content::JavaScriptDialogManager* GetJavaScriptDialogManager( + content::WebContents* source) override; // content::WebContentsObserver: void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; @@ -342,6 +347,7 @@ class WebContents : public mate::TrackableObject, void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) override; + void BeforeUnloadDialogCancelled() override; // brightray::InspectableWebContentsDelegate: void DevToolsReloadPage() override; @@ -389,6 +395,7 @@ class WebContents : public mate::TrackableObject, v8::Global devtools_web_contents_; v8::Global debugger_; + std::unique_ptr dialog_manager_; std::unique_ptr guest_delegate_; // The host webcontents that may contain this webcontents. diff --git a/atom/browser/api/frame_subscriber.cc b/atom/browser/api/frame_subscriber.cc index b5009e15794..076ebde9826 100644 --- a/atom/browser/api/frame_subscriber.cc +++ b/atom/browser/api/frame_subscriber.cc @@ -24,6 +24,7 @@ FrameSubscriber::FrameSubscriber(v8::Isolate* isolate, view_(view), callback_(callback), only_dirty_(only_dirty), + source_id_for_copy_request_(base::UnguessableToken::Create()), weak_factory_(this) { } @@ -32,8 +33,7 @@ bool FrameSubscriber::ShouldCaptureFrame( base::TimeTicks present_time, scoped_refptr* storage, DeliverFrameCallback* callback) { - const auto host = view_ ? view_->GetRenderWidgetHost() : nullptr; - if (!view_ || !host) + if (!view_) return false; if (dirty_rect.IsEmpty()) @@ -54,7 +54,7 @@ bool FrameSubscriber::ShouldCaptureFrame( rect = gfx::Rect(rect.origin(), bitmap_size); - host->CopyFromBackingStore( + view_->CopyFromSurface( rect, rect.size(), base::Bind(&FrameSubscriber::OnFrameDelivered, @@ -64,6 +64,10 @@ bool FrameSubscriber::ShouldCaptureFrame( return false; } +const base::UnguessableToken& FrameSubscriber::GetSourceIdForCopyRequest() { + return source_id_for_copy_request_; +} + void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback, const gfx::Rect& damage_rect, const SkBitmap& bitmap, diff --git a/atom/browser/api/frame_subscriber.h b/atom/browser/api/frame_subscriber.h index f1c7f0af45e..3761f144d04 100644 --- a/atom/browser/api/frame_subscriber.h +++ b/atom/browser/api/frame_subscriber.h @@ -7,9 +7,9 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" +#include "content/browser/renderer_host/render_widget_host_view_frame_subscriber.h" #include "content/public/browser/readback_types.h" #include "content/public/browser/render_widget_host_view.h" -#include "content/public/browser/render_widget_host_view_frame_subscriber.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/size.h" #include "v8/include/v8.h" @@ -32,6 +32,7 @@ class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { base::TimeTicks present_time, scoped_refptr* storage, DeliverFrameCallback* callback) override; + const base::UnguessableToken& GetSourceIdForCopyRequest() override; private: void OnFrameDelivered(const FrameCaptureCallback& callback, @@ -44,6 +45,8 @@ class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { FrameCaptureCallback callback_; bool only_dirty_; + base::UnguessableToken source_id_for_copy_request_; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(FrameSubscriber); diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 77c3212e2e0..6cede9c0112 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -15,6 +15,7 @@ #include "atom/browser/atom_quota_permission_context.h" #include "atom/browser/atom_resource_dispatcher_host_delegate.h" #include "atom/browser/atom_speech_recognition_manager_delegate.h" +#include "atom/browser/child_web_contents_tracker.h" #include "atom/browser/native_window.h" #include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" @@ -87,17 +88,31 @@ content::WebContents* AtomBrowserClient::GetWebContentsFromProcessID( } bool AtomBrowserClient::ShouldCreateNewSiteInstance( + content::RenderFrameHost* render_frame_host, content::BrowserContext* browser_context, content::SiteInstance* current_instance, const GURL& url) { - if (url.SchemeIs(url::kJavaScriptScheme)) // "javacript:" scheme should always use same SiteInstance return false; - if (!IsRendererSandboxed(current_instance->GetProcess()->GetID())) - // non-sandboxed renderers should always create a new SiteInstance - return true; + int process_id = current_instance->GetProcess()->GetID(); + if (!IsRendererSandboxed(process_id)) { + if (!RendererUsesNativeWindowOpen(process_id)) { + // non-sandboxed renderers without native window.open should always create + // a new SiteInstance + return true; + } + auto web_contents = + content::WebContents::FromRenderFrameHost(render_frame_host); + if (!ChildWebContentsTracker::IsChildWebContents(web_contents)) { + // Root WebContents should always create new process to make sure + // native addons are loaded correctly after reload / navigation. + // (Non-root WebContents opened by window.open() should try to + // reuse process to allow synchronous cross-window scripting.) + return true; + } + } // Create new a SiteInstance if navigating to a different site. auto src_url = current_instance->GetSiteURL(); @@ -109,19 +124,27 @@ bool AtomBrowserClient::ShouldCreateNewSiteInstance( content::SiteInstance::GetSiteForURL(browser_context, url) != src_url; } -void AtomBrowserClient::AddSandboxedRendererId(int process_id) { - base::AutoLock auto_lock(sandboxed_renderers_lock_); - sandboxed_renderers_.insert(process_id); +void AtomBrowserClient::AddProcessPreferences( + int process_id, AtomBrowserClient::ProcessPreferences prefs) { + base::AutoLock auto_lock(process_preferences_lock_); + process_preferences_[process_id] = prefs; } -void AtomBrowserClient::RemoveSandboxedRendererId(int process_id) { - base::AutoLock auto_lock(sandboxed_renderers_lock_); - sandboxed_renderers_.erase(process_id); +void AtomBrowserClient::RemoveProcessPreferences(int process_id) { + base::AutoLock auto_lock(process_preferences_lock_); + process_preferences_.erase(process_id); } bool AtomBrowserClient::IsRendererSandboxed(int process_id) { - base::AutoLock auto_lock(sandboxed_renderers_lock_); - return sandboxed_renderers_.count(process_id); + base::AutoLock auto_lock(process_preferences_lock_); + auto it = process_preferences_.find(process_id); + return it != process_preferences_.end() && it->second.sandbox; +} + +bool AtomBrowserClient::RendererUsesNativeWindowOpen(int process_id) { + base::AutoLock auto_lock(process_preferences_lock_); + auto it = process_preferences_.find(process_id); + return it != process_preferences_.end() && it->second.native_window_open; } void AtomBrowserClient::RenderProcessWillLaunch( @@ -133,11 +156,13 @@ void AtomBrowserClient::RenderProcessWillLaunch( new WidevineCdmMessageFilter(process_id, host->GetBrowserContext())); content::WebContents* web_contents = GetWebContentsFromProcessID(process_id); - if (WebContentsPreferences::IsSandboxed(web_contents)) { - AddSandboxedRendererId(host->GetID()); - // ensure the sandboxed renderer id is removed later - host->AddObserver(this); - } + ProcessPreferences process_prefs; + process_prefs.sandbox = WebContentsPreferences::IsSandboxed(web_contents); + process_prefs.native_window_open + = WebContentsPreferences::UsesNativeWindowOpen(web_contents); + AddProcessPreferences(host->GetID(), process_prefs); + // ensure the ProcessPreferences is removed later + host->AddObserver(this); } content::SpeechRecognitionManagerDelegate* @@ -182,7 +207,8 @@ void AtomBrowserClient::OverrideSiteInstanceForNavigation( return; } - if (!ShouldCreateNewSiteInstance(browser_context, current_instance, url)) + if (!ShouldCreateNewSiteInstance(render_frame_host, browser_context, + current_instance, url)) return; scoped_refptr site_instance = @@ -296,27 +322,27 @@ void AtomBrowserClient::ResourceDispatcherHostCreated() { } bool AtomBrowserClient::CanCreateWindow( + int opener_render_process_id, + int opener_render_frame_id, const GURL& opener_url, const GURL& opener_top_level_frame_url, const GURL& source_origin, - WindowContainerType container_type, + content::mojom::WindowContainerType container_type, const GURL& target_url, const content::Referrer& referrer, const std::string& frame_name, WindowOpenDisposition disposition, - const blink::WebWindowFeatures& features, + const blink::mojom::WindowFeatures& features, const std::vector& additional_features, const scoped_refptr& body, bool user_gesture, bool opener_suppressed, content::ResourceContext* context, - int render_process_id, - int opener_render_view_id, - int opener_render_frame_id, bool* no_javascript_access) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (IsRendererSandboxed(render_process_id)) { + if (IsRendererSandboxed(opener_render_process_id) + || RendererUsesNativeWindowOpen(opener_render_process_id)) { *no_javascript_access = false; return true; } @@ -330,7 +356,7 @@ bool AtomBrowserClient::CanCreateWindow( disposition, additional_features, body, - render_process_id, + opener_render_process_id, opener_render_frame_id)); } @@ -381,7 +407,7 @@ void AtomBrowserClient::RenderProcessHostDestroyed( break; } } - RemoveSandboxedRendererId(process_id); + RemoveProcessPreferences(process_id); } } // namespace atom diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 70573d6eee3..c9a58981a2c 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -80,23 +80,22 @@ class AtomBrowserClient : public brightray::BrowserClient, std::unique_ptr delegate) override; void ResourceDispatcherHostCreated() override; bool CanCreateWindow( + int opener_render_process_id, + int opener_render_frame_id, const GURL& opener_url, const GURL& opener_top_level_frame_url, const GURL& source_origin, - WindowContainerType container_type, + content::mojom::WindowContainerType container_type, const GURL& target_url, const content::Referrer& referrer, const std::string& frame_name, WindowOpenDisposition disposition, - const blink::WebWindowFeatures& features, + const blink::mojom::WindowFeatures& features, const std::vector& additional_features, const scoped_refptr& body, bool user_gesture, bool opener_suppressed, content::ResourceContext* context, - int render_process_id, - int opener_render_view_id, - int opener_render_frame_id, bool* no_javascript_access) override; void GetAdditionalAllowedSchemesForFileSystem( std::vector* schemes) override; @@ -112,19 +111,24 @@ class AtomBrowserClient : public brightray::BrowserClient, void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; private: - bool ShouldCreateNewSiteInstance(content::BrowserContext* browser_context, + bool ShouldCreateNewSiteInstance(content::RenderFrameHost* render_frame_host, + content::BrowserContext* browser_context, content::SiteInstance* current_instance, const GURL& dest_url); - // Add/remove a process id to `sandboxed_renderers_`. - void AddSandboxedRendererId(int process_id); - void RemoveSandboxedRendererId(int process_id); + struct ProcessPreferences { + bool sandbox; + bool native_window_open; + }; + void AddProcessPreferences(int process_id, ProcessPreferences prefs); + void RemoveProcessPreferences(int process_id); bool IsRendererSandboxed(int process_id); + bool RendererUsesNativeWindowOpen(int process_id); // pending_render_process => current_render_process. std::map pending_processes_; - // Set that contains the process ids of all sandboxed renderers - std::set sandboxed_renderers_; - base::Lock sandboxed_renderers_lock_; + + std::map process_preferences_; + base::Lock process_preferences_lock_; std::unique_ptr resource_dispatcher_host_delegate_; diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index d3f8237d5ec..190898fdef9 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -129,17 +129,14 @@ void AtomBrowserMainParts::PostEarlyInitialization() { node_bindings_->Initialize(); - // Support the "--debug" switch. - node_debugger_.reset(new NodeDebugger(js_env_->isolate())); - // Create the global environment. node::Environment* env = node_bindings_->CreateEnvironment(js_env_->context()); node_env_.reset(new NodeEnvironment(env)); - // Make sure node can get correct environment when debugging. - if (node_debugger_->IsRunning()) - env->AssignToContext(v8::Debug::GetDebugContext(js_env_->isolate())); + // Enable support for v8 inspector + node_debugger_.reset(new NodeDebugger(env)); + node_debugger_->Start(); // Add Electron extended APIs. atom_bindings_->BindTo(js_env_->isolate(), env->process_object()); diff --git a/atom/browser/atom_javascript_dialog_manager.cc b/atom/browser/atom_javascript_dialog_manager.cc index f874a99db55..c593b2bfba1 100644 --- a/atom/browser/atom_javascript_dialog_manager.cc +++ b/atom/browser/atom_javascript_dialog_manager.cc @@ -7,33 +7,37 @@ #include #include +#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/native_window.h" #include "atom/browser/ui/message_box.h" #include "base/bind.h" #include "base/strings/utf_string_conversions.h" #include "ui/gfx/image/image_skia.h" -using content::JavaScriptMessageType; +using content::JavaScriptDialogType; namespace atom { +AtomJavaScriptDialogManager::AtomJavaScriptDialogManager( + api::WebContents* api_web_contents) + : api_web_contents_(api_web_contents) {} + void AtomJavaScriptDialogManager::RunJavaScriptDialog( content::WebContents* web_contents, const GURL& origin_url, - JavaScriptMessageType message_type, + JavaScriptDialogType dialog_type, const base::string16& message_text, const base::string16& default_prompt_text, const DialogClosedCallback& callback, bool* did_suppress_message) { - - if (message_type != JavaScriptMessageType::JAVASCRIPT_MESSAGE_TYPE_ALERT && - message_type != JavaScriptMessageType::JAVASCRIPT_MESSAGE_TYPE_CONFIRM) { + if (dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_ALERT && + dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_CONFIRM) { callback.Run(false, base::string16()); return; } std::vector buttons = {"OK"}; - if (message_type == JavaScriptMessageType::JAVASCRIPT_MESSAGE_TYPE_CONFIRM) { + if (dialog_type == JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_CONFIRM) { buttons.push_back("Cancel"); } @@ -49,13 +53,13 @@ void AtomJavaScriptDialogManager::RunBeforeUnloadDialog( content::WebContents* web_contents, bool is_reload, const DialogClosedCallback& callback) { - // FIXME(zcbenz): the |message_text| is removed, figure out what should we do. - callback.Run(false, base::ASCIIToUTF16("This should not be displayed")); + bool default_prevented = api_web_contents_->Emit("will-prevent-unload"); + callback.Run(default_prevented, base::string16()); + return; } void AtomJavaScriptDialogManager::CancelDialogs( content::WebContents* web_contents, - bool suppress_callbacks, bool reset_state) { } diff --git a/atom/browser/atom_javascript_dialog_manager.h b/atom/browser/atom_javascript_dialog_manager.h index 01cc76248c8..d5e6c543361 100644 --- a/atom/browser/atom_javascript_dialog_manager.h +++ b/atom/browser/atom_javascript_dialog_manager.h @@ -11,13 +11,19 @@ namespace atom { +namespace api { +class WebContents; +} + class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { public: + explicit AtomJavaScriptDialogManager(api::WebContents* api_web_contents); + // content::JavaScriptDialogManager implementations. void RunJavaScriptDialog( content::WebContents* web_contents, const GURL& origin_url, - content::JavaScriptMessageType javascript_message_type, + content::JavaScriptDialogType dialog_type, const base::string16& message_text, const base::string16& default_prompt_text, const DialogClosedCallback& callback, @@ -27,13 +33,13 @@ class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { bool is_reload, const DialogClosedCallback& callback) override; void CancelDialogs(content::WebContents* web_contents, - bool suppress_callbacks, bool reset_state) override; private: static void OnMessageBoxCallback(const DialogClosedCallback& callback, int code, bool checkbox_checked); + api::WebContents* api_web_contents_; }; } // namespace atom diff --git a/atom/browser/atom_permission_manager.cc b/atom/browser/atom_permission_manager.cc index b22f4253237..e890618be73 100644 --- a/atom/browser/atom_permission_manager.cc +++ b/atom/browser/atom_permission_manager.cc @@ -131,7 +131,7 @@ int AtomPermissionManager::RequestPermissions( auto web_contents = content::WebContents::FromRenderFrameHost(render_frame_host); - int request_id = pending_requests_.Add(new PendingRequest( + int request_id = pending_requests_.Add(base::MakeUnique( render_frame_host, permissions, response_callback)); for (size_t i = 0; i < permissions.size(); ++i) { @@ -187,12 +187,6 @@ blink::mojom::PermissionStatus AtomPermissionManager::GetPermissionStatus( return blink::mojom::PermissionStatus::GRANTED; } -void AtomPermissionManager::RegisterPermissionUsage( - content::PermissionType permission, - const GURL& requesting_origin, - const GURL& embedding_origin) { -} - int AtomPermissionManager::SubscribePermissionStatusChange( content::PermissionType permission, const GURL& requesting_origin, diff --git a/atom/browser/atom_permission_manager.h b/atom/browser/atom_permission_manager.h index 7b74fe3fba4..b8a768a0794 100644 --- a/atom/browser/atom_permission_manager.h +++ b/atom/browser/atom_permission_manager.h @@ -66,9 +66,6 @@ class AtomPermissionManager : public content::PermissionManager { content::PermissionType permission, const GURL& requesting_origin, const GURL& embedding_origin) override; - void RegisterPermissionUsage(content::PermissionType permission, - const GURL& requesting_origin, - const GURL& embedding_origin) override; int SubscribePermissionStatusChange( content::PermissionType permission, const GURL& requesting_origin, @@ -79,7 +76,7 @@ class AtomPermissionManager : public content::PermissionManager { private: class PendingRequest; - using PendingRequestsMap = IDMap; + using PendingRequestsMap = IDMap>; RequestHandler request_handler_; diff --git a/atom/browser/child_web_contents_tracker.cc b/atom/browser/child_web_contents_tracker.cc new file mode 100644 index 00000000000..3c9eb0d3f04 --- /dev/null +++ b/atom/browser/child_web_contents_tracker.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2017 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/child_web_contents_tracker.h" + +#include + +namespace atom { + +namespace { + +std::unordered_set g_child_web_contents; + +} // namespace + +ChildWebContentsTracker::ChildWebContentsTracker( + content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) { + g_child_web_contents.insert(web_contents); +} + +bool ChildWebContentsTracker::IsChildWebContents( + content::WebContents* web_contents) { + return g_child_web_contents.find(web_contents) != g_child_web_contents.end(); +} + +void ChildWebContentsTracker::WebContentsDestroyed() { + g_child_web_contents.erase(web_contents()); + delete this; +} + +} // namespace atom diff --git a/atom/browser/child_web_contents_tracker.h b/atom/browser/child_web_contents_tracker.h new file mode 100644 index 00000000000..785509e6ea7 --- /dev/null +++ b/atom/browser/child_web_contents_tracker.h @@ -0,0 +1,25 @@ +// Copyright (c) 2017 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_CHILD_WEB_CONTENTS_TRACKER_H_ +#define ATOM_BROWSER_CHILD_WEB_CONTENTS_TRACKER_H_ + +#include "content/public/browser/web_contents_observer.h" + +namespace atom { + +// ChildWebContentsTracker tracks child WebContents +// created by native `window.open()` +class ChildWebContentsTracker : public content::WebContentsObserver { + public: + explicit ChildWebContentsTracker(content::WebContents* web_contents); + static bool IsChildWebContents(content::WebContents* web_contents); + + protected: + void WebContentsDestroyed() override; +}; + +} // namespace atom + +#endif // ATOM_BROWSER_CHILD_WEB_CONTENTS_TRACKER_H_ diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index 282fa92de69..b0430dafeb8 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -9,7 +9,6 @@ #include #include "atom/browser/atom_browser_context.h" -#include "atom/browser/atom_javascript_dialog_manager.h" #include "atom/browser/native_window.h" #include "atom/browser/ui/file_dialog.h" #include "atom/browser/web_dialog_helper.h" @@ -230,15 +229,6 @@ bool CommonWebContentsDelegate::CanOverscrollContent() const { return false; } -content::JavaScriptDialogManager* -CommonWebContentsDelegate::GetJavaScriptDialogManager( - content::WebContents* source) { - if (!dialog_manager_) - dialog_manager_.reset(new AtomJavaScriptDialogManager); - - return dialog_manager_.get(); -} - content::ColorChooser* CommonWebContentsDelegate::OpenColorChooser( content::WebContents* web_contents, SkColor color, @@ -494,9 +484,9 @@ void CommonWebContentsDelegate::OnDevToolsIndexingWorkCalculated( int request_id, const std::string& file_system_path, int total_work) { - base::FundamentalValue request_id_value(request_id); + base::Value request_id_value(request_id); base::StringValue file_system_path_value(file_system_path); - base::FundamentalValue total_work_value(total_work); + base::Value total_work_value(total_work); web_contents_->CallClientFunction("DevToolsAPI.indexingTotalWorkCalculated", &request_id_value, &file_system_path_value, @@ -507,9 +497,9 @@ void CommonWebContentsDelegate::OnDevToolsIndexingWorked( int request_id, const std::string& file_system_path, int worked) { - base::FundamentalValue request_id_value(request_id); + base::Value request_id_value(request_id); base::StringValue file_system_path_value(file_system_path); - base::FundamentalValue worked_value(worked); + base::Value worked_value(worked); web_contents_->CallClientFunction("DevToolsAPI.indexingWorked", &request_id_value, &file_system_path_value, @@ -520,7 +510,7 @@ void CommonWebContentsDelegate::OnDevToolsIndexingDone( int request_id, const std::string& file_system_path) { devtools_indexing_jobs_.erase(request_id); - base::FundamentalValue request_id_value(request_id); + base::Value request_id_value(request_id); base::StringValue file_system_path_value(file_system_path); web_contents_->CallClientFunction("DevToolsAPI.indexingDone", &request_id_value, @@ -536,7 +526,7 @@ void CommonWebContentsDelegate::OnDevToolsSearchCompleted( for (const auto& file_path : file_paths) { file_paths_value.AppendString(file_path); } - base::FundamentalValue request_id_value(request_id); + base::Value request_id_value(request_id); base::StringValue file_system_path_value(file_system_path); web_contents_->CallClientFunction("DevToolsAPI.searchCompleted", &request_id_value, diff --git a/atom/browser/common_web_contents_delegate.h b/atom/browser/common_web_contents_delegate.h index d1d26314966..fd468afd563 100644 --- a/atom/browser/common_web_contents_delegate.h +++ b/atom/browser/common_web_contents_delegate.h @@ -20,7 +20,6 @@ using brightray::DevToolsFileSystemIndexer; namespace atom { class AtomBrowserContext; -class AtomJavaScriptDialogManager; class NativeWindow; class WebDialogHelper; @@ -62,8 +61,6 @@ class CommonWebContentsDelegate content::WebContents* source, const content::OpenURLParams& params) override; bool CanOverscrollContent() const override; - content::JavaScriptDialogManager* GetJavaScriptDialogManager( - content::WebContents* source) override; content::ColorChooser* OpenColorChooser( content::WebContents* web_contents, SkColor color, @@ -147,7 +144,6 @@ class CommonWebContentsDelegate bool native_fullscreen_; std::unique_ptr web_dialog_helper_; - std::unique_ptr dialog_manager_; scoped_refptr devtools_file_system_indexer_; // Make sure BrowserContext is alwasys destroyed after WebContents. diff --git a/atom/browser/common_web_contents_delegate_mac.mm b/atom/browser/common_web_contents_delegate_mac.mm index c7660613793..079d17624c9 100644 --- a/atom/browser/common_web_contents_delegate_mac.mm +++ b/atom/browser/common_web_contents_delegate_mac.mm @@ -20,7 +20,7 @@ void CommonWebContentsDelegate::HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { if (event.skip_in_browser || - event.type == content::NativeWebKeyboardEvent::Char) + event.type() == content::NativeWebKeyboardEvent::Char) return; // Escape exits tabbed fullscreen mode. diff --git a/atom/browser/javascript_environment.cc b/atom/browser/javascript_environment.cc index b3e01c1c300..d13b955972f 100644 --- a/atom/browser/javascript_environment.cc +++ b/atom/browser/javascript_environment.cc @@ -8,16 +8,45 @@ #include "base/command_line.h" #include "base/message_loop/message_loop.h" +#include "base/threading/thread_task_runner_handle.h" #include "content/public/common/content_switches.h" -#include "gin/array_buffer.h" #include "gin/v8_initializer.h" +#if defined(OS_WIN) +#include "atom/node/osfhandle.h" +#endif + #include "atom/common/node_includes.h" namespace atom { +void* ArrayBufferAllocator::Allocate(size_t length) { +#if defined(OS_WIN) + return node::ArrayBufferCalloc(length); +#else + return calloc(1, length); +#endif +} + +void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { +#if defined(OS_WIN) + return node::ArrayBufferMalloc(length); +#else + return malloc(length); +#endif +} + +void ArrayBufferAllocator::Free(void* data, size_t length) { +#if defined(OS_WIN) + node::ArrayBufferFree(data, length); +#else + free(data); +#endif +} + JavascriptEnvironment::JavascriptEnvironment() : initialized_(Initialize()), + isolate_holder_(base::ThreadTaskRunnerHandle::Get()), isolate_(isolate_holder_.isolate()), isolate_scope_(isolate_), locker_(isolate_), @@ -44,7 +73,7 @@ bool JavascriptEnvironment::Initialize() { gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode, gin::IsolateHolder::kStableV8Extras, - gin::ArrayBufferAllocator::SharedInstance()); + &allocator_); return true; } diff --git a/atom/browser/javascript_environment.h b/atom/browser/javascript_environment.h index 43a7295f902..75d1629f661 100644 --- a/atom/browser/javascript_environment.h +++ b/atom/browser/javascript_environment.h @@ -14,6 +14,14 @@ class Environment; namespace atom { +// ArrayBuffer's allocator, used on Chromium's side. +class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { + public: + void* Allocate(size_t length) override; + void* AllocateUninitialized(size_t length) override; + void Free(void* data, size_t length) override; +}; + // Manage the V8 isolate and context automatically. class JavascriptEnvironment { public: @@ -31,6 +39,7 @@ class JavascriptEnvironment { bool Initialize(); bool initialized_; + ArrayBufferAllocator allocator_; gin::IsolateHolder isolate_holder_; v8::Isolate* isolate_; v8::Isolate::Scope isolate_scope_; diff --git a/atom/browser/loader/layered_resource_handler.cc b/atom/browser/loader/layered_resource_handler.cc index 8b41b1ad638..c22ddd355de 100644 --- a/atom/browser/loader/layered_resource_handler.cc +++ b/atom/browser/loader/layered_resource_handler.cc @@ -15,12 +15,12 @@ LayeredResourceHandler::LayeredResourceHandler( LayeredResourceHandler::~LayeredResourceHandler() {} -bool LayeredResourceHandler::OnResponseStarted( +void LayeredResourceHandler::OnResponseStarted( content::ResourceResponse* response, - bool* defer) { + std::unique_ptr controller) { if (delegate_) delegate_->OnResponseStarted(response); - return next_handler_->OnResponseStarted(response, defer); + next_handler_->OnResponseStarted(response, std::move(controller)); } } // namespace atom diff --git a/atom/browser/loader/layered_resource_handler.h b/atom/browser/loader/layered_resource_handler.h index db3a974eea3..9aad269c7e6 100644 --- a/atom/browser/loader/layered_resource_handler.h +++ b/atom/browser/loader/layered_resource_handler.h @@ -26,8 +26,9 @@ class LayeredResourceHandler : public content::LayeredResourceHandler { ~LayeredResourceHandler() override; // content::LayeredResourceHandler: - bool OnResponseStarted(content::ResourceResponse* response, - bool* defer) override; + void OnResponseStarted( + content::ResourceResponse* response, + std::unique_ptr controller) override; private: Delegate* delegate_; diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index d3f18d8fb95..b0d25e03f23 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -147,6 +147,7 @@ class NativeWindow : public base::SupportsUserData, virtual void SetMenu(AtomMenuModel* menu); virtual void SetParentWindow(NativeWindow* parent); virtual void SetBrowserView(NativeBrowserView* browser_view) = 0; + virtual gfx::NativeView GetNativeView() = 0; virtual gfx::NativeWindow GetNativeWindow() = 0; virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0; diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index af0f157ecae..69bf3afef65 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -89,6 +89,7 @@ class NativeWindowMac : public NativeWindow, void SetContentProtection(bool enable) override; void SetBrowserView(NativeBrowserView* browser_view) override; void SetParentWindow(NativeWindow* parent) override; + gfx::NativeView GetNativeView() override; gfx::NativeWindow GetNativeWindow() override; gfx::AcceleratedWidget GetAcceleratedWidget() override; void SetProgressBar(double progress, const ProgressState state) override; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 9dc119e239d..755e46c4f32 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -1314,6 +1314,10 @@ void NativeWindowMac::SetParentWindow(NativeWindow* parent) { [parent->GetNativeWindow() addChildWindow:window_ ordered:NSWindowAbove]; } +gfx::NativeView NativeWindowMac::GetNativeView() { + return inspectable_web_contents()->GetView()->GetNativeView(); +} + gfx::NativeWindow NativeWindowMac::GetNativeWindow() { return window_; } @@ -1458,7 +1462,7 @@ void NativeWindowMac::SetEscapeTouchBarItem(const mate::PersistentDictionary& it } void NativeWindowMac::OnInputEvent(const blink::WebInputEvent& event) { - switch (event.type) { + switch (event.type()) { case blink::WebInputEvent::GestureScrollBegin: case blink::WebInputEvent::GestureScrollUpdate: case blink::WebInputEvent::GestureScrollEnd: diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 7e6f23947c2..00f0befd50f 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -86,7 +86,7 @@ bool IsAltKey(const content::NativeWebKeyboardEvent& event) { bool IsAltModifier(const content::NativeWebKeyboardEvent& event) { typedef content::NativeWebKeyboardEvent::Modifiers Modifiers; - int modifiers = event.modifiers; + int modifiers = event.modifiers(); modifiers &= ~Modifiers::NumLockOn; modifiers &= ~Modifiers::CapsLockOn; return (modifiers == Modifiers::AltKey) || @@ -767,13 +767,14 @@ void NativeWindowViews::SetBackgroundColor(const std::string& color_name) { } void NativeWindowViews::SetHasShadow(bool has_shadow) { - wm::SetShadowType( + wm::SetShadowElevation( GetNativeWindow(), - has_shadow ? wm::SHADOW_TYPE_RECTANGULAR : wm::SHADOW_TYPE_NONE); + has_shadow ? wm::ShadowElevation::MEDIUM : wm::ShadowElevation::NONE); } bool NativeWindowViews::HasShadow() { - return wm::GetShadowType(GetNativeWindow()) != wm::SHADOW_TYPE_NONE; + return GetNativeWindow()->GetProperty(wm::kShadowElevationKey) + != wm::ShadowElevation::NONE; } void NativeWindowViews::SetIgnoreMouseEvents(bool ignore) { @@ -923,6 +924,10 @@ void NativeWindowViews::SetParentWindow(NativeWindow* parent) { #endif } +gfx::NativeView NativeWindowViews::GetNativeView() { + return window_->GetNativeView(); +} + gfx::NativeWindow NativeWindowViews::GetNativeWindow() { return window_->GetNativeWindow(); } @@ -1234,10 +1239,10 @@ void NativeWindowViews::HandleKeyboardEvent( // Show accelerator when "Alt" is pressed. if (menu_bar_visible_ && IsAltKey(event)) menu_bar_->SetAcceleratorVisibility( - event.type == blink::WebInputEvent::RawKeyDown); + event.type() == blink::WebInputEvent::RawKeyDown); // Show the submenu when "Alt+Key" is pressed. - if (event.type == blink::WebInputEvent::RawKeyDown && !IsAltKey(event) && + if (event.type() == blink::WebInputEvent::RawKeyDown && !IsAltKey(event) && IsAltModifier(event)) { if (!menu_bar_visible_ && (menu_bar_->GetAcceleratorIndex(event.windowsKeyCode) != -1)) @@ -1250,10 +1255,10 @@ void NativeWindowViews::HandleKeyboardEvent( return; // Toggle the menu bar only when a single Alt is released. - if (event.type == blink::WebInputEvent::RawKeyDown && IsAltKey(event)) { + if (event.type() == blink::WebInputEvent::RawKeyDown && IsAltKey(event)) { // When a single Alt is pressed: menu_bar_alt_pressed_ = true; - } else if (event.type == blink::WebInputEvent::KeyUp && IsAltKey(event) && + } else if (event.type() == blink::WebInputEvent::KeyUp && IsAltKey(event) && menu_bar_alt_pressed_) { // When a single Alt is released right after a Alt is pressed: menu_bar_alt_pressed_ = false; diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 276cd4adde2..15bdbad1880 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -106,6 +106,7 @@ class NativeWindowViews : public NativeWindow, void SetMenu(AtomMenuModel* menu_model) override; void SetBrowserView(NativeBrowserView* browser_view) override; void SetParentWindow(NativeWindow* parent) override; + gfx::NativeView GetNativeView() override; gfx::NativeWindow GetNativeWindow() override; void SetOverlayIcon(const gfx::Image& overlay, const std::string& description) override; diff --git a/atom/browser/net/atom_url_request_job_factory.cc b/atom/browser/net/atom_url_request_job_factory.cc index 92c4eb40a97..d0488085f5f 100644 --- a/atom/browser/net/atom_url_request_job_factory.cc +++ b/atom/browser/net/atom_url_request_job_factory.cc @@ -50,15 +50,16 @@ bool AtomURLRequestJobFactory::InterceptProtocol( return false; ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme]; protocol_handler_map_[scheme] = protocol_handler.release(); - original_protocols_.set(scheme, base::WrapUnique(original_protocol_handler)); + original_protocols_[scheme].reset(original_protocol_handler); return true; } bool AtomURLRequestJobFactory::UninterceptProtocol(const std::string& scheme) { - if (!original_protocols_.contains(scheme)) + auto it = original_protocols_.find(scheme); + if (it == original_protocols_.end()) return false; - protocol_handler_map_[scheme] = - original_protocols_.take_and_erase(scheme).release(); + protocol_handler_map_[scheme] = it->second.release(); + original_protocols_.erase(it); return true; } @@ -78,7 +79,9 @@ bool AtomURLRequestJobFactory::HasProtocolHandler( } void AtomURLRequestJobFactory::Clear() { - base::STLDeleteValues(&protocol_handler_map_); + for (auto& it : protocol_handler_map_) + delete it.second; + protocol_handler_map_.clear(); } net::URLRequestJob* AtomURLRequestJobFactory::MaybeCreateJobWithProtocolHandler( diff --git a/atom/browser/net/atom_url_request_job_factory.h b/atom/browser/net/atom_url_request_job_factory.h index 5b439c2f9cd..16d1ed3b207 100644 --- a/atom/browser/net/atom_url_request_job_factory.h +++ b/atom/browser/net/atom_url_request_job_factory.h @@ -9,9 +9,9 @@ #include #include #include +#include #include -#include "base/containers/scoped_ptr_hash_map.h" #include "net/url_request/url_request_job_factory.h" namespace atom { @@ -64,7 +64,7 @@ class AtomURLRequestJobFactory : public net::URLRequestJobFactory { ProtocolHandlerMap protocol_handler_map_; // Map that stores the original protocols of schemes. - using OriginalProtocolsMap = base::ScopedPtrHashMap< + using OriginalProtocolsMap = std::unordered_map< std::string, std::unique_ptr>; // Can only be accessed in IO thread. OriginalProtocolsMap original_protocols_; diff --git a/atom/browser/net/js_asker.cc b/atom/browser/net/js_asker.cc index 67fb578650f..0df6bb5c37d 100644 --- a/atom/browser/net/js_asker.cc +++ b/atom/browser/net/js_asker.cc @@ -59,11 +59,11 @@ void AskForOptions(v8::Isolate* isolate, } bool IsErrorOptions(base::Value* value, int* error) { - if (value->IsType(base::Value::TYPE_DICTIONARY)) { + if (value->IsType(base::Value::Type::DICTIONARY)) { base::DictionaryValue* dict = static_cast(value); if (dict->GetInteger("error", error)) return true; - } else if (value->IsType(base::Value::TYPE_INTEGER)) { + } else if (value->IsType(base::Value::Type::INTEGER)) { if (value->GetAsInteger(error)) return true; } diff --git a/atom/browser/net/js_asker.h b/atom/browser/net/js_asker.h index df64a217504..f9859f115ad 100644 --- a/atom/browser/net/js_asker.h +++ b/atom/browser/net/js_asker.h @@ -13,6 +13,7 @@ #include "content/public/browser/browser_thread.h" #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" +#include "net/http/http_status_code.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_job.h" #include "v8/include/v8.h" @@ -82,6 +83,9 @@ class JsAsker : public RequestJob { base::Bind(&JsAsker::OnResponse, weak_factory_.GetWeakPtr()))); } + + int GetResponseCode() const override { return net::HTTP_OK; } + void GetResponseInfo(net::HttpResponseInfo* info) override { info->headers = new net::HttpResponseHeaders(""); } diff --git a/atom/browser/net/url_request_async_asar_job.cc b/atom/browser/net/url_request_async_asar_job.cc index 942f06233e3..86aa7c55067 100644 --- a/atom/browser/net/url_request_async_asar_job.cc +++ b/atom/browser/net/url_request_async_asar_job.cc @@ -7,6 +7,7 @@ #include #include "atom/common/atom_constants.h" +#include "base/threading/sequenced_worker_pool.h" namespace atom { @@ -18,10 +19,10 @@ URLRequestAsyncAsarJob::URLRequestAsyncAsarJob( void URLRequestAsyncAsarJob::StartAsync(std::unique_ptr options) { base::FilePath::StringType file_path; - if (options->IsType(base::Value::TYPE_DICTIONARY)) { + if (options->IsType(base::Value::Type::DICTIONARY)) { static_cast(options.get())->GetString( "path", &file_path); - } else if (options->IsType(base::Value::TYPE_STRING)) { + } else if (options->IsType(base::Value::Type::STRING)) { options->GetAsString(&file_path); } diff --git a/atom/browser/net/url_request_buffer_job.cc b/atom/browser/net/url_request_buffer_job.cc index 70f46030817..117ba96ff1a 100644 --- a/atom/browser/net/url_request_buffer_job.cc +++ b/atom/browser/net/url_request_buffer_job.cc @@ -34,13 +34,13 @@ URLRequestBufferJob::URLRequestBufferJob( void URLRequestBufferJob::StartAsync(std::unique_ptr options) { const base::BinaryValue* binary = nullptr; - if (options->IsType(base::Value::TYPE_DICTIONARY)) { + if (options->IsType(base::Value::Type::DICTIONARY)) { base::DictionaryValue* dict = static_cast(options.get()); dict->GetString("mimeType", &mime_type_); dict->GetString("charset", &charset_); dict->GetBinary("data", &binary); - } else if (options->IsType(base::Value::TYPE_BINARY)) { + } else if (options->IsType(base::Value::Type::BINARY)) { options->GetAsBinary(&binary); } @@ -67,7 +67,7 @@ void URLRequestBufferJob::StartAsync(std::unique_ptr options) { } void URLRequestBufferJob::GetResponseInfo(net::HttpResponseInfo* info) { - std::string status("HTTP/1.1 "); + std::string status("HTTP/1.1 200 OK"); status.append(base::IntToString(status_code_)); status.append(" "); status.append(net::GetHttpReasonPhrase(status_code_)); diff --git a/atom/browser/net/url_request_fetch_job.cc b/atom/browser/net/url_request_fetch_job.cc index a8c13483e6a..0fd598d2ec9 100644 --- a/atom/browser/net/url_request_fetch_job.cc +++ b/atom/browser/net/url_request_fetch_job.cc @@ -9,14 +9,18 @@ #include "atom/browser/api/atom_api_session.h" #include "atom/browser/atom_browser_context.h" +#include "base/guid.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" +#include "brightray/browser/url_request_context_getter.h" +#include "content/browser/streams/stream_context.h" #include "native_mate/dictionary.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.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 "url/url_constants.h" using content::BrowserThread; @@ -62,7 +66,8 @@ class ResponsePiper : public net::URLFetcherResponseWriter { job_->HeadersCompleted(); first_write_ = false; } - return job_->DataAvailable(buffer, num_bytes, callback); + job_->stream()->AddData(buffer->data(), num_bytes); + return num_bytes; } int Finish(int net_error, const net::CompletionCallback& callback) override { return net::OK; @@ -77,12 +82,11 @@ class ResponsePiper : public net::URLFetcherResponseWriter { } // namespace -URLRequestFetchJob::URLRequestFetchJob( - net::URLRequest* request, net::NetworkDelegate* network_delegate) +URLRequestFetchJob::URLRequestFetchJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate) : JsAsker(request, network_delegate), pending_buffer_size_(0), - write_num_bytes_(0) { -} + total_bytes_read_(0) {} void URLRequestFetchJob::BeforeStartInUI( v8::Isolate* isolate, v8::Local value) { @@ -112,7 +116,7 @@ void URLRequestFetchJob::BeforeStartInUI( } void URLRequestFetchJob::StartAsync(std::unique_ptr options) { - if (!options->IsType(base::Value::TYPE_DICTIONARY)) { + if (!options->IsType(base::Value::Type::DICTIONARY)) { NotifyStartError(net::URLRequestStatus( net::URLRequestStatus::FAILED, net::ERR_NOT_IMPLEMENTED)); return; @@ -169,7 +173,22 @@ void URLRequestFetchJob::StartAsync(std::unique_ptr options) { fetcher_->SetExtraRequestHeaders( request()->extra_request_headers().ToString()); - fetcher_->Start(); + // Create readable stream for URLFetcher response. + content::StreamContext* stream_context = + static_cast(request_context_getter()) + ->stream_context(); + + if (stream_context) { + GURL stream_url(std::string(url::kBlobScheme) + ":" + + formated_url.GetOrigin().spec() + base::GenerateGUID()); + stream_ = + new content::Stream(stream_context->registry(), nullptr, stream_url); + stream_->SetReadObserver(this); + fetcher_->Start(); + } else { + NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::CANCELED, + net::ERR_ABORTED)); + } } void URLRequestFetchJob::HeadersCompleted() { @@ -178,53 +197,55 @@ void URLRequestFetchJob::HeadersCompleted() { NotifyHeadersComplete(); } -int URLRequestFetchJob::DataAvailable(net::IOBuffer* buffer, - int num_bytes, - const net::CompletionCallback& callback) { - // When pending_buffer_ is empty, there's no ReadRawData() operation waiting - // for IO completion, we have to save the parameters until the request is - // ready to read data. - if (!pending_buffer_.get()) { - write_buffer_ = buffer; - write_num_bytes_ = num_bytes; - write_callback_ = callback; - return net::ERR_IO_PENDING; - } - - // Write data to the pending buffer and clear them after the writing. - int bytes_read = BufferCopy(buffer, num_bytes, - pending_buffer_.get(), pending_buffer_size_); - ClearPendingBuffer(); - ReadRawDataComplete(bytes_read); - return bytes_read; -} - void URLRequestFetchJob::Kill() { JsAsker::Kill(); + ClearStream(); fetcher_.reset(); } +void URLRequestFetchJob::OnDataAvailable(content::Stream* stream) { + if (!pending_buffer_.get()) + return; + + int result = 0; + auto state = stream_->ReadRawData(pending_buffer_.get(), pending_buffer_size_, + &result); + if (state == content::Stream::STREAM_ABORTED) + result = net::ERR_CONNECTION_RESET; + + // Clear the buffers before notifying the read is complete, so that it is + // safe for the observer to read. + pending_buffer_ = nullptr; + pending_buffer_size_ = 0; + + if (result > 0) + total_bytes_read_ += result; + ReadRawDataComplete(result); +} + int URLRequestFetchJob::ReadRawData(net::IOBuffer* dest, int dest_size) { if (GetResponseCode() == 204) { request()->set_received_response_content_length(prefilter_bytes_read()); return net::OK; } - // When write_buffer_ is empty, there is no data valable yet, we have to save - // the dest buffer util DataAvailable. - if (!write_buffer_.get()) { - pending_buffer_ = dest; - pending_buffer_size_ = dest_size; - return net::ERR_IO_PENDING; + int bytes_read = 0; + switch (stream_->ReadRawData(dest, dest_size, &bytes_read)) { + case content::Stream::STREAM_HAS_DATA: + total_bytes_read_ += bytes_read; + return bytes_read; + case content::Stream::STREAM_COMPLETE: + return stream_->GetStatus(); + case content::Stream::STREAM_EMPTY: + pending_buffer_ = dest; + pending_buffer_size_ = dest_size; + return net::ERR_IO_PENDING; + case content::Stream::STREAM_ABORTED: + // Handle this as connection reset. + return net::ERR_CONNECTION_RESET; } - - // Read from the write buffer and clear them after reading. - int bytes_read = BufferCopy(write_buffer_.get(), write_num_bytes_, - dest, dest_size); - net::CompletionCallback write_callback = write_callback_; - ClearWriteBuffer(); - write_callback.Run(bytes_read); - return bytes_read; + NOTREACHED(); + return net::ERR_FAILED; } bool URLRequestFetchJob::GetMimeType(std::string* mime_type) const { @@ -246,11 +267,18 @@ int URLRequestFetchJob::GetResponseCode() const { return response_info_->headers->response_code(); } -void URLRequestFetchJob::OnURLFetchComplete(const net::URLFetcher* source) { - ClearPendingBuffer(); - ClearWriteBuffer(); +int64_t URLRequestFetchJob::GetTotalReceivedBytes() const { + int64_t total_received_bytes = 0; + if (response_info_) + total_received_bytes = response_info_->headers->raw_headers().size(); + if (stream_.get()) + total_received_bytes += total_bytes_read_; + return total_received_bytes; +} - if (fetcher_->GetStatus().is_success()) { +void URLRequestFetchJob::OnURLFetchComplete(const net::URLFetcher* source) { + auto status = fetcher_->GetStatus(); + if (status.is_success()) { if (!response_info_) { // Since we notify header completion only after first write there will be // no response object constructed for http respones with no content 204. @@ -258,28 +286,16 @@ void URLRequestFetchJob::OnURLFetchComplete(const net::URLFetcher* source) { HeadersCompleted(); return; } - ReadRawDataComplete(0); + stream_->Finalize(0); } else { - NotifyStartError(fetcher_->GetStatus()); + stream_->Finalize(status.error()); + NotifyStartError(status); } } -int URLRequestFetchJob::BufferCopy(net::IOBuffer* source, int num_bytes, - net::IOBuffer* target, int target_size) { - int bytes_written = std::min(num_bytes, target_size); - memcpy(target->data(), source->data(), bytes_written); - return bytes_written; -} - -void URLRequestFetchJob::ClearPendingBuffer() { - pending_buffer_ = nullptr; - pending_buffer_size_ = 0; -} - -void URLRequestFetchJob::ClearWriteBuffer() { - write_buffer_ = nullptr; - write_num_bytes_ = 0; - write_callback_.Reset(); +void URLRequestFetchJob::ClearStream() { + stream_->RemoveReadObserver(this); + stream_ = nullptr; } } // namespace atom diff --git a/atom/browser/net/url_request_fetch_job.h b/atom/browser/net/url_request_fetch_job.h index 906ba68d396..75689c8908d 100644 --- a/atom/browser/net/url_request_fetch_job.h +++ b/atom/browser/net/url_request_fetch_job.h @@ -9,21 +9,23 @@ #include "atom/browser/net/js_asker.h" #include "browser/url_request_context_getter.h" +#include "content/browser/streams/stream.h" +#include "content/browser/streams/stream_read_observer.h" #include "net/url_request/url_fetcher_delegate.h" namespace atom { class URLRequestFetchJob : public JsAsker, public net::URLFetcherDelegate, - public brightray::URLRequestContextGetter::Delegate { + public brightray::URLRequestContextGetter::Delegate, + public content::StreamReadObserver { public: URLRequestFetchJob(net::URLRequest*, net::NetworkDelegate*); // Called by response writer. void HeadersCompleted(); - int DataAvailable(net::IOBuffer* buffer, - int num_bytes, - const net::CompletionCallback& callback); + + content::Stream* stream() const { return stream_.get(); } protected: // JsAsker: @@ -36,28 +38,26 @@ class URLRequestFetchJob : public JsAsker, bool GetMimeType(std::string* mime_type) const override; void GetResponseInfo(net::HttpResponseInfo* info) override; int GetResponseCode() const override; + int64_t GetTotalReceivedBytes() const override; // net::URLFetcherDelegate: void OnURLFetchComplete(const net::URLFetcher* source) override; + // content::StreamReadObserver: + void OnDataAvailable(content::Stream* stream) override; + private: - int BufferCopy(net::IOBuffer* source, int num_bytes, - net::IOBuffer* target, int target_size); - void ClearPendingBuffer(); - void ClearWriteBuffer(); + void ClearStream(); scoped_refptr url_request_context_getter_; std::unique_ptr fetcher_; std::unique_ptr response_info_; + scoped_refptr stream_; // Saved arguments passed to ReadRawData. scoped_refptr pending_buffer_; int pending_buffer_size_; - - // Saved arguments passed to DataAvailable. - scoped_refptr write_buffer_; - int write_num_bytes_; - net::CompletionCallback write_callback_; + int total_bytes_read_; DISALLOW_COPY_AND_ASSIGN(URLRequestFetchJob); }; diff --git a/atom/browser/net/url_request_string_job.cc b/atom/browser/net/url_request_string_job.cc index abec345e63d..60dc323d8ad 100644 --- a/atom/browser/net/url_request_string_job.cc +++ b/atom/browser/net/url_request_string_job.cc @@ -17,13 +17,13 @@ URLRequestStringJob::URLRequestStringJob( } void URLRequestStringJob::StartAsync(std::unique_ptr options) { - if (options->IsType(base::Value::TYPE_DICTIONARY)) { + if (options->IsType(base::Value::Type::DICTIONARY)) { base::DictionaryValue* dict = static_cast(options.get()); dict->GetString("mimeType", &mime_type_); dict->GetString("charset", &charset_); dict->GetString("data", &data_); - } else if (options->IsType(base::Value::TYPE_STRING)) { + } else if (options->IsType(base::Value::Type::STRING)) { options->GetAsString(&data_); } net::URLRequestSimpleJob::Start(); diff --git a/atom/browser/node_debugger.cc b/atom/browser/node_debugger.cc index 9fdeb6099e6..2f17d468011 100644 --- a/atom/browser/node_debugger.cc +++ b/atom/browser/node_debugger.cc @@ -4,202 +4,47 @@ #include "atom/browser/node_debugger.h" -#include - -#include "base/bind.h" #include "base/command_line.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "content/public/browser/browser_thread.h" -#include "net/test/embedded_test_server/tcp_listen_socket.h" - -#include "atom/common/node_includes.h" +#include "libplatform/libplatform.h" +#include "native_mate/dictionary.h" namespace atom { -namespace { - -// NodeDebugger is stored in Isolate's data, slots 0, 1, 3 have already been -// taken by gin, blink and node, using 2 is a safe option for now. -const int kIsolateSlot = 2; - -const char* kContentLength = "Content-Length"; - -} // namespace - -NodeDebugger::NodeDebugger(v8::Isolate* isolate) - : isolate_(isolate), - thread_("NodeDebugger"), - content_length_(-1), - weak_factory_(this) { - bool use_debug_agent = false; - int port = 5858; - - std::string port_str; - base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); - if (cmd->HasSwitch("debug")) { - use_debug_agent = true; - port_str = cmd->GetSwitchValueASCII("debug"); - } else if (cmd->HasSwitch("debug-brk")) { - use_debug_agent = true; - port_str = cmd->GetSwitchValueASCII("debug-brk"); - } - - if (use_debug_agent) { - if (!port_str.empty()) - base::StringToInt(port_str, &port); - - isolate_->SetData(kIsolateSlot, this); - v8::Debug::SetMessageHandler(isolate_, DebugMessageHandler); - - weak_up_ui_handle_.data = this; - uv_async_init(uv_default_loop(), &weak_up_ui_handle_, ProcessMessageInUI); - - // Start a new IO thread. - base::Thread::Options options; - options.message_loop_type = base::MessageLoop::TYPE_IO; - if (!thread_.StartWithOptions(options)) { - LOG(ERROR) << "Unable to start debugger thread"; - return; - } - - // Start the server in new IO thread. - thread_.task_runner()->PostTask( - FROM_HERE, - base::Bind(&NodeDebugger::StartServer, weak_factory_.GetWeakPtr(), - port)); - } +NodeDebugger::NodeDebugger(node::Environment* env) : env_(env) { } NodeDebugger::~NodeDebugger() { - thread_.Stop(); } -bool NodeDebugger::IsRunning() const { - return thread_.IsRunning(); -} - -void NodeDebugger::StartServer(int port) { - server_ = net::test_server::TCPListenSocket::CreateAndListen( - "127.0.0.1", port, this); - if (!server_) { - LOG(ERROR) << "Cannot start debugger server"; +void NodeDebugger::Start() { + auto inspector = env_->inspector_agent(); + if (inspector == nullptr) return; - } -} -void NodeDebugger::CloseSession() { - accepted_socket_.reset(); -} - -void NodeDebugger::OnMessage(const std::string& message) { - if (message.find("\"type\":\"request\",\"command\":\"disconnect\"}") != - std::string::npos) - CloseSession(); - - base::string16 message16 = base::UTF8ToUTF16(message); - v8::Debug::SendCommand( - isolate_, - reinterpret_cast(message16.data()), message16.size()); - - uv_async_send(&weak_up_ui_handle_); -} - -void NodeDebugger::SendMessage(const std::string& message) { - if (accepted_socket_) { - std::string header = base::StringPrintf( - "%s: %d\r\n\r\n", kContentLength, static_cast(message.size())); - accepted_socket_->Send(header); - accepted_socket_->Send(message); - } -} - -void NodeDebugger::SendConnectMessage() { - accepted_socket_->Send(base::StringPrintf( - "Type: connect\r\n" - "V8-Version: %s\r\n" - "Protocol-Version: 1\r\n" - "Embedding-Host: %s\r\n" - "%s: 0\r\n", - v8::V8::GetVersion(), ATOM_PRODUCT_NAME, kContentLength), true); -} - -// static -void NodeDebugger::ProcessMessageInUI(uv_async_t* handle) { - NodeDebugger* self = static_cast(handle->data); - v8::Debug::ProcessDebugMessages(self->isolate_); -} - -// static -void NodeDebugger::DebugMessageHandler(const v8::Debug::Message& message) { - NodeDebugger* self = static_cast( - message.GetIsolate()->GetData(kIsolateSlot)); - - if (self) { - std::string message8(*v8::String::Utf8Value(message.GetJSON())); - self->thread_.task_runner()->PostTask( - FROM_HERE, - base::Bind(&NodeDebugger::SendMessage, self->weak_factory_.GetWeakPtr(), - message8)); - } -} - -void NodeDebugger::DidAccept( - net::test_server::StreamListenSocket* server, - std::unique_ptr socket) { - // Only accept one session. - if (accepted_socket_) { - socket->Send(std::string("Remote debugging session already active"), true); - return; + node::DebugOptions options; + for (auto& arg : base::CommandLine::ForCurrentProcess()->argv()) { +#if defined(OS_WIN) + options.ParseOption(base::UTF16ToUTF8(arg)); +#else + options.ParseOption(arg); +#endif } - accepted_socket_ = std::move(socket); - SendConnectMessage(); -} + if (options.inspector_enabled()) { + // Use custom platform since the gin platform does not work correctly + // with node's inspector agent + platform_.reset(v8::platform::CreateDefaultPlatform()); -void NodeDebugger::DidRead(net::test_server::StreamListenSocket* socket, - const char* data, - int len) { - buffer_.append(data, len); - - do { - if (buffer_.empty()) - return; - - // Read the "Content-Length" header. - if (content_length_ < 0) { - size_t pos = buffer_.find("\r\n\r\n"); - if (pos == std::string::npos) - return; - - // We can be sure that the header is "Content-Length: xxx\r\n". - std::string content_length = buffer_.substr(16, pos - 16); - if (!base::StringToInt(content_length, &content_length_)) { - DidClose(accepted_socket_.get()); - return; - } - - // Strip header from buffer. - buffer_ = buffer_.substr(pos + 4); + // Set process._debugWaitConnect if --inspect-brk was specified to stop + // the debugger on the first line + if (options.wait_for_connect()) { + mate::Dictionary process(env_->isolate(), env_->process_object()); + process.Set("_debugWaitConnect", true); } - // Read the message. - if (buffer_.size() >= static_cast(content_length_)) { - std::string message = buffer_.substr(0, content_length_); - buffer_ = buffer_.substr(content_length_); - - OnMessage(message); - - // Get ready for next message. - content_length_ = -1; - } - } while (true); -} - -void NodeDebugger::DidClose(net::test_server::StreamListenSocket* socket) { - // If we lost the connection, then simulate a disconnect msg: - OnMessage("{\"seq\":1,\"type\":\"request\",\"command\":\"disconnect\"}"); + inspector->Start(platform_.get(), nullptr, options); + } } } // namespace atom diff --git a/atom/browser/node_debugger.h b/atom/browser/node_debugger.h index 118812a139a..a3553192b4e 100644 --- a/atom/browser/node_debugger.h +++ b/atom/browser/node_debugger.h @@ -6,56 +6,22 @@ #define ATOM_BROWSER_NODE_DEBUGGER_H_ #include -#include -#include "base/memory/weak_ptr.h" -#include "base/threading/thread.h" -#include "net/test/embedded_test_server/stream_listen_socket.h" -#include "v8/include/v8-debug.h" -#include "vendor/node/deps/uv/include/uv.h" +#include "atom/common/node_includes.h" namespace atom { -// Add support for node's "--debug" switch. -class NodeDebugger : public net::test_server::StreamListenSocket::Delegate { +// Add support for node's "--inspect" switch. +class NodeDebugger { public: - explicit NodeDebugger(v8::Isolate* isolate); - virtual ~NodeDebugger(); + explicit NodeDebugger(node::Environment* env); + ~NodeDebugger(); - bool IsRunning() const; + void Start(); private: - void StartServer(int port); - void CloseSession(); - void OnMessage(const std::string& message); - void SendMessage(const std::string& message); - void SendConnectMessage(); - - static void ProcessMessageInUI(uv_async_t* handle); - - static void DebugMessageHandler(const v8::Debug::Message& message); - - // net::test_server::StreamListenSocket::Delegate: - void DidAccept( - net::test_server::StreamListenSocket* server, - std::unique_ptr socket) override; - void DidRead(net::test_server::StreamListenSocket* socket, - const char* data, - int len) override; - void DidClose(net::test_server::StreamListenSocket* socket) override; - - v8::Isolate* isolate_; - - uv_async_t weak_up_ui_handle_; - - base::Thread thread_; - std::unique_ptr server_; - std::unique_ptr accepted_socket_; - - std::string buffer_; - int content_length_; - - base::WeakPtrFactory weak_factory_; + node::Environment* env_; + std::unique_ptr platform_; DISALLOW_COPY_AND_ASSIGN(NodeDebugger); }; diff --git a/atom/browser/osr/osr_output_device.cc b/atom/browser/osr/osr_output_device.cc index 5f605f668c9..c35e364236f 100644 --- a/atom/browser/osr/osr_output_device.cc +++ b/atom/browser/osr/osr_output_device.cc @@ -4,7 +4,9 @@ #include "atom/browser/osr/osr_output_device.h" -#include "third_party/skia/include/core/SkDevice.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/src/core/SkDevice.h" #include "ui/gfx/skia_util.h" namespace atom { @@ -36,8 +38,11 @@ void OffScreenOutputDevice::Resize( return; } - if (transparent_) - bitmap_->eraseARGB(0, 0, 0, 0); + if (transparent_) { + bitmap_->eraseColor(SK_ColorTRANSPARENT); + } else { + bitmap_->eraseColor(SK_ColorWHITE); + } canvas_.reset(new SkCanvas(*bitmap_)); } @@ -47,6 +52,17 @@ SkCanvas* OffScreenOutputDevice::BeginPaint(const gfx::Rect& damage_rect) { DCHECK(bitmap_.get()); damage_rect_ = damage_rect; + SkIRect damage = SkIRect::MakeXYWH( + damage_rect_.x(), + damage_rect_.y(), + damage_rect_.width(), + damage_rect_.height()); + + if (transparent_) { + bitmap_->erase(SK_ColorTRANSPARENT, damage); + } else { + bitmap_->erase(SK_ColorWHITE, damage); + } return canvas_.get(); } diff --git a/atom/browser/osr/osr_render_widget_host_view.cc b/atom/browser/osr/osr_render_widget_host_view.cc index 2003118f389..605b42ec1d1 100644 --- a/atom/browser/osr/osr_render_widget_host_view.cc +++ b/atom/browser/osr/osr_render_widget_host_view.cc @@ -16,16 +16,19 @@ #include "components/display_compositor/gl_helper.h" #include "content/browser/renderer_host/render_widget_host_delegate.h" #include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_view_frame_subscriber.h" +#include "content/browser/renderer_host/resize_lock.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/context_factory.h" -#include "content/public/browser/render_widget_host_view_frame_subscriber.h" +#include "media/base/video_frame.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_type.h" #include "ui/events/latency_info.h" #include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/skbitmap_operations.h" namespace atom { @@ -34,72 +37,87 @@ namespace { const float kDefaultScaleFactor = 1.0; const int kFrameRetryLimit = 2; +#if !defined(OS_MACOSX) + +const int kResizeLockTimeoutMs = 67; + +class AtomResizeLock : public content::ResizeLock { + public: + AtomResizeLock(OffScreenRenderWidgetHostView* host, + const gfx::Size new_size, + bool defer_compositor_lock) + : ResizeLock(new_size, defer_compositor_lock), + host_(host), + cancelled_(false), + weak_ptr_factory_(this) { + DCHECK(host_); + host_->HoldResize(); + + content::BrowserThread::PostDelayedTask(content::BrowserThread::UI, + FROM_HERE, base::Bind(&AtomResizeLock::CancelLock, + weak_ptr_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(kResizeLockTimeoutMs)); + } + + ~AtomResizeLock() override { + CancelLock(); + } + + bool GrabDeferredLock() override { + return ResizeLock::GrabDeferredLock(); + } + + void UnlockCompositor() override { + ResizeLock::UnlockCompositor(); + compositor_lock_ = NULL; + } + + protected: + void LockCompositor() override { + ResizeLock::LockCompositor(); + compositor_lock_ = host_->GetCompositor()->GetCompositorLock(); + } + + void CancelLock() { + if (cancelled_) + return; + cancelled_ = true; + UnlockCompositor(); + host_->ReleaseResize(); + } + + private: + OffScreenRenderWidgetHostView* host_; + scoped_refptr compositor_lock_; + bool cancelled_; + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(AtomResizeLock); +}; + +#endif // !defined(OS_MACOSX) + } // namespace class AtomCopyFrameGenerator { public: - AtomCopyFrameGenerator(int frame_rate_threshold_ms, - OffScreenRenderWidgetHostView* view) - : frame_rate_threshold_ms_(frame_rate_threshold_ms), - view_(view), - frame_pending_(false), - frame_in_progress_(false), + AtomCopyFrameGenerator(OffScreenRenderWidgetHostView* view, + int frame_rate_threshold_us) + : view_(view), frame_retry_count_(0), + next_frame_time_(base::TimeTicks::Now()), + frame_duration_(base::TimeDelta::FromMicroseconds( + frame_rate_threshold_us)), weak_ptr_factory_(this) { last_time_ = base::Time::Now(); } - void GenerateCopyFrame( - bool force_frame, - const gfx::Rect& damage_rect) { - if (force_frame && !frame_pending_) - frame_pending_ = true; - - if (!frame_pending_) - return; - - if (!damage_rect.IsEmpty()) - pending_damage_rect_.Union(damage_rect); - - if (frame_in_progress_) - return; - - frame_in_progress_ = true; - - const int64_t frame_rate_delta = - (base::TimeTicks::Now() - frame_start_time_).InMilliseconds(); - if (frame_rate_delta < frame_rate_threshold_ms_) { - content::BrowserThread::PostDelayedTask(content::BrowserThread::UI, - FROM_HERE, - base::Bind(&AtomCopyFrameGenerator::InternalGenerateCopyFrame, - weak_ptr_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds( - frame_rate_threshold_ms_ - frame_rate_delta)); - return; - } - - InternalGenerateCopyFrame(); - } - - bool frame_pending() const { return frame_pending_; } - - void set_frame_rate_threshold_ms(int frame_rate_threshold_ms) { - frame_rate_threshold_ms_ = frame_rate_threshold_ms; - } - - private: - void InternalGenerateCopyFrame() { - frame_pending_ = false; - frame_start_time_ = base::TimeTicks::Now(); - + void GenerateCopyFrame(const gfx::Rect& damage_rect) { if (!view_->render_widget_host()) return; - const gfx::Rect damage_rect = pending_damage_rect_; - pending_damage_rect_.SetRect(0, 0, 0, 0); - std::unique_ptr request = - cc::CopyOutputRequest::CreateRequest(base::Bind( + cc::CopyOutputRequest::CreateBitmapRequest(base::Bind( &AtomCopyFrameGenerator::CopyFromCompositingSurfaceHasResult, weak_ptr_factory_.GetWeakPtr(), damage_rect)); @@ -108,6 +126,12 @@ class AtomCopyFrameGenerator { view_->GetRootLayer()->RequestCopyOfOutput(std::move(request)); } + void set_frame_rate_threshold_us(int frame_rate_threshold_us) { + frame_duration_ = base::TimeDelta::FromMicroseconds( + frame_rate_threshold_us); + } + + private: void CopyFromCompositingSurfaceHasResult( const gfx::Rect& damage_rect, std::unique_ptr result) { @@ -117,180 +141,62 @@ class AtomCopyFrameGenerator { return; } - if (result->HasTexture()) { - PrepareTextureCopyOutputResult(damage_rect, std::move(result)); - return; - } - - DCHECK(result->HasBitmap()); - PrepareBitmapCopyOutputResult(damage_rect, std::move(result)); - } - - void PrepareTextureCopyOutputResult( - const gfx::Rect& damage_rect, - std::unique_ptr result) { - DCHECK(result->HasTexture()); - base::ScopedClosureRunner scoped_callback_runner( - base::Bind(&AtomCopyFrameGenerator::OnCopyFrameCaptureFailure, - weak_ptr_factory_.GetWeakPtr(), - damage_rect)); - - const gfx::Size& result_size = result->size(); - SkIRect bitmap_size; - if (bitmap_) - bitmap_->getBounds(&bitmap_size); - - if (!bitmap_ || - bitmap_size.width() != result_size.width() || - bitmap_size.height() != result_size.height()) { - bitmap_.reset(new SkBitmap); - bitmap_->allocN32Pixels(result_size.width(), - result_size.height(), - true); - if (bitmap_->drawsNothing()) - return; - } - - content::ImageTransportFactory* factory = - content::ImageTransportFactory::GetInstance(); - display_compositor::GLHelper* gl_helper = factory->GetGLHelper(); - if (!gl_helper) - return; - - std::unique_ptr bitmap_pixels_lock( - new SkAutoLockPixels(*bitmap_)); - uint8_t* pixels = static_cast(bitmap_->getPixels()); - - cc::TextureMailbox texture_mailbox; - std::unique_ptr release_callback; - result->TakeTexture(&texture_mailbox, &release_callback); - DCHECK(texture_mailbox.IsTexture()); - if (!texture_mailbox.IsTexture()) - return; - - ignore_result(scoped_callback_runner.Release()); - - gl_helper->CropScaleReadbackAndCleanMailbox( - texture_mailbox.mailbox(), - texture_mailbox.sync_token(), - result_size, - gfx::Rect(result_size), - result_size, - pixels, - kN32_SkColorType, - base::Bind( - &AtomCopyFrameGenerator::CopyFromCompositingSurfaceFinishedProxy, - weak_ptr_factory_.GetWeakPtr(), - base::Passed(&release_callback), - damage_rect, - base::Passed(&bitmap_), - base::Passed(&bitmap_pixels_lock)), - display_compositor::GLHelper::SCALER_QUALITY_FAST); - } - - static void CopyFromCompositingSurfaceFinishedProxy( - base::WeakPtr generator, - std::unique_ptr release_callback, - const gfx::Rect& damage_rect, - std::unique_ptr bitmap, - std::unique_ptr bitmap_pixels_lock, - bool result) { - gpu::SyncToken sync_token; - if (result) { - display_compositor::GLHelper* gl_helper = - content::ImageTransportFactory::GetInstance()->GetGLHelper(); - if (gl_helper) - gl_helper->GenerateSyncToken(&sync_token); - } - const bool lost_resource = !sync_token.HasData(); - release_callback->Run(sync_token, lost_resource); - - if (generator) { - generator->CopyFromCompositingSurfaceFinished( - damage_rect, std::move(bitmap), std::move(bitmap_pixels_lock), - result); - } else { - bitmap_pixels_lock.reset(); - bitmap.reset(); - } - } - - void CopyFromCompositingSurfaceFinished( - const gfx::Rect& damage_rect, - std::unique_ptr bitmap, - std::unique_ptr bitmap_pixels_lock, - bool result) { - DCHECK(!bitmap_); - bitmap_ = std::move(bitmap); - - if (result) { - OnCopyFrameCaptureSuccess(damage_rect, *bitmap_, - std::move(bitmap_pixels_lock)); - } else { - bitmap_pixels_lock.reset(); - OnCopyFrameCaptureFailure(damage_rect); - } - } - - void PrepareBitmapCopyOutputResult( - const gfx::Rect& damage_rect, - std::unique_ptr result) { DCHECK(result->HasBitmap()); std::unique_ptr source = result->TakeBitmap(); DCHECK(source); if (source) { - std::unique_ptr bitmap_pixels_lock( - new SkAutoLockPixels(*source)); - OnCopyFrameCaptureSuccess(damage_rect, *source, - std::move(bitmap_pixels_lock)); + base::AutoLock autolock(lock_); + std::shared_ptr bitmap(source.release()); + + base::TimeTicks now = base::TimeTicks::Now(); + base::TimeDelta next_frame_in = next_frame_time_ - now; + if (next_frame_in > frame_duration_ / 4) { + next_frame_time_ += frame_duration_; + content::BrowserThread::PostDelayedTask(content::BrowserThread::UI, + FROM_HERE, + base::Bind(&AtomCopyFrameGenerator::OnCopyFrameCaptureSuccess, + weak_ptr_factory_.GetWeakPtr(), + damage_rect, + bitmap), + next_frame_in); + } else { + next_frame_time_ = now + frame_duration_; + OnCopyFrameCaptureSuccess(damage_rect, bitmap); + } + + frame_retry_count_ = 0; } else { OnCopyFrameCaptureFailure(damage_rect); } } - void OnCopyFrameCaptureFailure( - const gfx::Rect& damage_rect) { - pending_damage_rect_.Union(damage_rect); - + void OnCopyFrameCaptureFailure(const gfx::Rect& damage_rect) { const bool force_frame = (++frame_retry_count_ <= kFrameRetryLimit); - OnCopyFrameCaptureCompletion(force_frame); + if (force_frame) { + // Retry with the same |damage_rect|. + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, + base::Bind(&AtomCopyFrameGenerator::GenerateCopyFrame, + weak_ptr_factory_.GetWeakPtr(), + damage_rect)); + } } void OnCopyFrameCaptureSuccess( const gfx::Rect& damage_rect, - const SkBitmap& bitmap, - std::unique_ptr bitmap_pixels_lock) { - view_->OnPaint(damage_rect, bitmap); - - if (frame_retry_count_ > 0) - frame_retry_count_ = 0; - - OnCopyFrameCaptureCompletion(false); + std::shared_ptr bitmap) { + base::AutoLock lock(onPaintLock_); + view_->OnPaint(damage_rect, *bitmap); } - void OnCopyFrameCaptureCompletion(bool force_frame) { - frame_in_progress_ = false; - - if (frame_pending_) { - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(&AtomCopyFrameGenerator::GenerateCopyFrame, - weak_ptr_factory_.GetWeakPtr(), - force_frame, - gfx::Rect())); - } - } - - int frame_rate_threshold_ms_; + base::Lock lock_; + base::Lock onPaintLock_; OffScreenRenderWidgetHostView* view_; base::Time last_time_; - base::TimeTicks frame_start_time_; - bool frame_pending_; - bool frame_in_progress_; int frame_retry_count_; - std::unique_ptr bitmap_; - gfx::Rect pending_damage_rect_; + base::TimeTicks next_frame_time_; + base::TimeDelta frame_duration_; base::WeakPtrFactory weak_ptr_factory_; @@ -299,12 +205,15 @@ class AtomCopyFrameGenerator { class AtomBeginFrameTimer : public cc::DelayBasedTimeSourceClient { public: - AtomBeginFrameTimer(int frame_rate_threshold_ms, + AtomBeginFrameTimer(int frame_rate_threshold_us, const base::Closure& callback) : callback_(callback) { time_source_.reset(new cc::DelayBasedTimeSource( content::BrowserThread::GetTaskRunnerForThread( content::BrowserThread::UI).get())); + time_source_->SetTimebaseAndInterval( + base::TimeTicks(), + base::TimeDelta::FromMicroseconds(frame_rate_threshold_us)); time_source_->SetClient(this); } @@ -316,10 +225,10 @@ class AtomBeginFrameTimer : public cc::DelayBasedTimeSourceClient { return time_source_->Active(); } - void SetFrameRateThresholdMs(int frame_rate_threshold_ms) { + void SetFrameRateThresholdUs(int frame_rate_threshold_us) { time_source_->SetTimebaseAndInterval( base::TimeTicks::Now(), - base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms)); + base::TimeDelta::FromMicroseconds(frame_rate_threshold_us)); } private: @@ -337,37 +246,49 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView( bool transparent, const OnPaintCallback& callback, content::RenderWidgetHost* host, + OffScreenRenderWidgetHostView* parent_host_view, NativeWindow* native_window) : render_widget_host_(content::RenderWidgetHostImpl::From(host)), + parent_host_view_(parent_host_view), + popup_host_view_(nullptr), + child_host_view_(nullptr), native_window_(native_window), software_output_device_(nullptr), transparent_(transparent), callback_(callback), + parent_callback_(nullptr), frame_rate_(60), - frame_rate_threshold_ms_(0), + frame_rate_threshold_us_(0), last_time_(base::Time::Now()), scale_factor_(kDefaultScaleFactor), - is_showing_(!render_widget_host_->is_hidden()), size_(native_window->GetSize()), painting_(true), + is_showing_(!render_widget_host_->is_hidden()), + is_destroyed_(false), + popup_position_(gfx::Rect()), + hold_resize_(false), + pending_resize_(false), weak_ptr_factory_(this) { DCHECK(render_widget_host_); - render_widget_host_->SetView(this); - + bool is_guest_view_hack = parent_host_view_ != nullptr; #if !defined(OS_MACOSX) - content::ImageTransportFactory* factory = - content::ImageTransportFactory::GetInstance(); delegated_frame_host_ = base::MakeUnique( - factory->GetContextFactory()->AllocateFrameSinkId(), this); + AllocateFrameSinkId(is_guest_view_hack), this); root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR)); #endif #if defined(OS_MACOSX) - CreatePlatformWidget(); + CreatePlatformWidget(is_guest_view_hack); #else + // On macOS the ui::Compositor is created/owned by the platform view. + content::ImageTransportFactory* factory = + content::ImageTransportFactory::GetInstance(); + ui::ContextFactoryPrivate* context_factory_private = + factory->GetContextFactoryPrivate(); compositor_.reset( - new ui::Compositor(content::GetContextFactory(), + new ui::Compositor(context_factory_private->AllocateFrameSinkId(), + content::GetContextFactory(), context_factory_private, base::ThreadTaskRunnerHandle::Get())); compositor_->SetAcceleratedWidget(native_window_->GetAcceleratedWidget()); compositor_->SetRootLayer(root_layer_.get()); @@ -377,6 +298,8 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView( native_window_->AddObserver(this); ResizeRootLayer(); + render_widget_host_->SetView(this); + InstallTransparency(); } OffScreenRenderWidgetHostView::~OffScreenRenderWidgetHostView() { @@ -394,15 +317,33 @@ OffScreenRenderWidgetHostView::~OffScreenRenderWidgetHostView() { delegated_frame_host_->ResetCompositor(); #endif + if (copy_frame_generator_.get()) + copy_frame_generator_.reset(NULL); + #if defined(OS_MACOSX) DestroyPlatformWidget(); +#else + delegated_frame_host_.reset(NULL); + compositor_.reset(NULL); + root_layer_.reset(NULL); #endif } +void OffScreenRenderWidgetHostView::OnWindowResize() { + // In offscreen mode call RenderWidgetHostView's SetSize explicitly + auto size = native_window_->GetSize(); + SetSize(size); +} + +void OffScreenRenderWidgetHostView::OnWindowClosed() { + native_window_->RemoveObserver(this); + native_window_ = nullptr; +} + void OffScreenRenderWidgetHostView::OnBeginFrameTimerTick() { const base::TimeTicks frame_time = base::TimeTicks::Now(); const base::TimeDelta vsync_period = - base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_); + base::TimeDelta::FromMicroseconds(frame_rate_threshold_us_); SendBeginFrame(frame_time, vsync_period); } @@ -416,10 +357,17 @@ void OffScreenRenderWidgetHostView::SendBeginFrame( base::TimeTicks deadline = display_time - estimated_browser_composite_time; + const cc::BeginFrameArgs& begin_frame_args = + cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, + begin_frame_source_.source_id(), + begin_frame_number_, frame_time, deadline, + vsync_period, cc::BeginFrameArgs::NORMAL); + DCHECK(begin_frame_args.IsValid()); + begin_frame_number_++; + render_widget_host_->Send(new ViewMsg_BeginFrame( render_widget_host_->GetRoutingID(), - cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, - vsync_period, cc::BeginFrameArgs::NORMAL))); + begin_frame_args)); } bool OffScreenRenderWidgetHostView::OnMessageReceived( @@ -437,6 +385,17 @@ bool OffScreenRenderWidgetHostView::OnMessageReceived( } void OffScreenRenderWidgetHostView::InitAsChild(gfx::NativeView) { + DCHECK(parent_host_view_); + + if (parent_host_view_->child_host_view_) { + parent_host_view_->child_host_view_->CancelWidget(); + } + + parent_host_view_->set_child_host_view(this); + parent_host_view_->Hide(); + + ResizeRootLayer(); + Show(); } content::RenderWidgetHost* OffScreenRenderWidgetHostView::GetRenderWidgetHost() @@ -446,14 +405,11 @@ content::RenderWidgetHost* OffScreenRenderWidgetHostView::GetRenderWidgetHost() void OffScreenRenderWidgetHostView::SetSize(const gfx::Size& size) { size_ = size; - - ResizeRootLayer(); - if (render_widget_host_) - render_widget_host_->WasResized(); - GetDelegatedFrameHost()->WasResized(); + WasResized(); } void OffScreenRenderWidgetHostView::SetBounds(const gfx::Rect& new_bounds) { + SetSize(new_bounds.size()); } gfx::Vector2dF OffScreenRenderWidgetHostView::GetLastScrollOffset() const { @@ -481,7 +437,7 @@ bool OffScreenRenderWidgetHostView::HasFocus() const { } bool OffScreenRenderWidgetHostView::IsSurfaceAvailableForCopy() const { - return GetDelegatedFrameHost()->CanCopyToBitmap(); + return GetDelegatedFrameHost()->CanCopyFromCompositingSurface(); } void OffScreenRenderWidgetHostView::Show() { @@ -523,6 +479,9 @@ bool OffScreenRenderWidgetHostView::IsShowing() { } gfx::Rect OffScreenRenderWidgetHostView::GetViewBounds() const { + if (IsPopupWidget()) + return popup_position_; + return gfx::Rect(size_); } @@ -561,47 +520,42 @@ void OffScreenRenderWidgetHostView::OnSwapCompositorFrame( last_scroll_offset_ = frame.metadata.root_scroll_offset; } - if (frame.delegated_frame_data) { + if (!frame.render_pass_list.empty()) { if (software_output_device_) { - if (!begin_frame_timer_.get()) { + if (!begin_frame_timer_.get() || IsPopupWidget()) { software_output_device_->SetActive(painting_); } // The compositor will draw directly to the SoftwareOutputDevice which // then calls OnPaint. -#if defined(OS_MACOSX) - browser_compositor_->SwapCompositorFrame(output_surface_id, - std::move(frame)); -#else - delegated_frame_host_->SwapDelegatedFrame(output_surface_id, - std::move(frame)); -#endif + // We would normally call BrowserCompositorMac::SwapCompositorFrame on + // macOS, however it contains compositor resize logic that we don't want. + // Consequently we instead call the SwapDelegatedFrame method directly. + GetDelegatedFrameHost()->SwapDelegatedFrame(output_surface_id, + std::move(frame)); } else { if (!copy_frame_generator_.get()) { copy_frame_generator_.reset( - new AtomCopyFrameGenerator(frame_rate_threshold_ms_, this)); + new AtomCopyFrameGenerator(this, frame_rate_threshold_us_)); } // Determine the damage rectangle for the current frame. This is the same // calculation that SwapDelegatedFrame uses. - cc::RenderPass* root_pass = - frame.delegated_frame_data->render_pass_list.back().get(); + cc::RenderPass* root_pass = frame.render_pass_list.back().get(); gfx::Size frame_size = root_pass->output_rect.size(); gfx::Rect damage_rect = gfx::ToEnclosingRect(gfx::RectF(root_pass->damage_rect)); damage_rect.Intersect(gfx::Rect(frame_size)); -#if defined(OS_MACOSX) - browser_compositor_->SwapCompositorFrame(output_surface_id, - std::move(frame)); -#else - delegated_frame_host_->SwapDelegatedFrame(output_surface_id, - std::move(frame)); -#endif + // We would normally call BrowserCompositorMac::SwapCompositorFrame on + // macOS, however it contains compositor resize logic that we don't want. + // Consequently we instead call the SwapDelegatedFrame method directly. + GetDelegatedFrameHost()->SwapDelegatedFrame(output_surface_id, + std::move(frame)); // Request a copy of the last compositor frame which will eventually call // OnPaint asynchronously. - copy_frame_generator_->GenerateCopyFrame(true, damage_rect); + copy_frame_generator_->GenerateCopyFrame(damage_rect); } } } @@ -612,10 +566,25 @@ void OffScreenRenderWidgetHostView::ClearCompositorFrame() { void OffScreenRenderWidgetHostView::InitAsPopup( content::RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) { + DCHECK_EQ(parent_host_view_, parent_host_view); + + if (parent_host_view_->popup_host_view_) { + parent_host_view_->popup_host_view_->CancelWidget(); + } + + parent_host_view_->set_popup_host_view(this); + parent_host_view_->popup_bitmap_.reset(new SkBitmap); + parent_callback_ = base::Bind(&OffScreenRenderWidgetHostView::OnPopupPaint, + parent_host_view_->weak_ptr_factory_.GetWeakPtr()); + + popup_position_ = pos; + + ResizeRootLayer(); + Show(); } void OffScreenRenderWidgetHostView::InitAsFullscreen( - content::RenderWidgetHostView *) { + content::RenderWidgetHostView *) { } void OffScreenRenderWidgetHostView::UpdateCursor(const content::WebCursor &) { @@ -637,6 +606,23 @@ void OffScreenRenderWidgetHostView::RenderProcessGone(base::TerminationStatus, } void OffScreenRenderWidgetHostView::Destroy() { + if (!is_destroyed_) { + is_destroyed_ = true; + + if (parent_host_view_ != NULL) { + CancelWidget(); + } else { + if (popup_host_view_) + popup_host_view_->CancelWidget(); + popup_bitmap_.reset(); + if (child_host_view_) + child_host_view_->CancelWidget(); + for (auto guest_host_view : guest_host_views_) + guest_host_view->CancelWidget(); + Hide(); + } + } + delete this; } @@ -647,7 +633,7 @@ void OffScreenRenderWidgetHostView::SelectionBoundsChanged( const ViewHostMsg_SelectionBounds_Params &) { } -void OffScreenRenderWidgetHostView::CopyFromCompositingSurface( +void OffScreenRenderWidgetHostView::CopyFromSurface( const gfx::Rect& src_subrect, const gfx::Size& dst_size, const content::ReadbackRequestCallback& callback, @@ -656,18 +642,14 @@ void OffScreenRenderWidgetHostView::CopyFromCompositingSurface( src_subrect, dst_size, callback, preferred_color_type); } -void OffScreenRenderWidgetHostView::CopyFromCompositingSurfaceToVideoFrame( +void OffScreenRenderWidgetHostView::CopyFromSurfaceToVideoFrame( const gfx::Rect& src_subrect, - const scoped_refptr& target, + scoped_refptr target, const base::Callback& callback) { GetDelegatedFrameHost()->CopyFromCompositingSurfaceToVideoFrame( src_subrect, target, callback); } -bool OffScreenRenderWidgetHostView::CanCopyToVideoFrame() const { - return GetDelegatedFrameHost()->CanCopyToVideoFrame(); -} - void OffScreenRenderWidgetHostView::BeginFrameSubscription( std::unique_ptr subscriber) { GetDelegatedFrameHost()->BeginFrameSubscription(std::move(subscriber)); @@ -677,6 +659,13 @@ void OffScreenRenderWidgetHostView::EndFrameSubscription() { GetDelegatedFrameHost()->EndFrameSubscription(); } +void OffScreenRenderWidgetHostView::InitAsGuest( + content::RenderWidgetHostView* parent_host_view, + content::RenderWidgetHostViewGuest* guest_view) { + parent_host_view_->AddGuestHostView(this); + parent_host_view_->RegisterGuestViewFrameSwappedCallback(guest_view); +} + bool OffScreenRenderWidgetHostView::HasAcceleratedSurface(const gfx::Size &) { return false; } @@ -685,22 +674,40 @@ gfx::Rect OffScreenRenderWidgetHostView::GetBoundsInRootWindow() { return gfx::Rect(size_); } -void OffScreenRenderWidgetHostView::LockCompositingSurface() { -} - -void OffScreenRenderWidgetHostView::UnlockCompositingSurface() { -} - void OffScreenRenderWidgetHostView::ImeCompositionRangeChanged( const gfx::Range &, const std::vector&) { } gfx::Size OffScreenRenderWidgetHostView::GetPhysicalBackingSize() const { - return size_; + return gfx::ConvertSizeToPixel(scale_factor_, GetRequestedRendererSize()); } gfx::Size OffScreenRenderWidgetHostView::GetRequestedRendererSize() const { - return size_; + return GetDelegatedFrameHost()->GetRequestedRendererSize(); +} + +content::RenderWidgetHostViewBase* + OffScreenRenderWidgetHostView::CreateViewForWidget( + content::RenderWidgetHost* render_widget_host, + content::RenderWidgetHost* embedder_render_widget_host, + content::WebContentsView* web_contents_view) { + if (render_widget_host->GetView()) { + return static_cast( + render_widget_host->GetView()); + } + + OffScreenRenderWidgetHostView* embedder_host_view = nullptr; + if (embedder_render_widget_host) { + embedder_host_view = static_cast( + embedder_render_widget_host->GetView()); + } + + return new OffScreenRenderWidgetHostView( + transparent_, + callback_, + render_widget_host, + embedder_host_view, + native_window_); } #if !defined(OS_MACOSX) @@ -714,22 +721,29 @@ bool OffScreenRenderWidgetHostView::DelegatedFrameHostIsVisible() const { SkColor OffScreenRenderWidgetHostView::DelegatedFrameHostGetGutterColor( SkColor color) const { + if (render_widget_host_->delegate() && + render_widget_host_->delegate()->IsFullscreenForCurrentTab()) { + return SK_ColorWHITE; + } return color; } gfx::Size OffScreenRenderWidgetHostView::DelegatedFrameHostDesiredSizeInDIP() const { - return size_; + return GetRootLayer()->bounds().size(); } bool OffScreenRenderWidgetHostView::DelegatedFrameCanCreateResizeLock() const { - return false; + return !render_widget_host_->auto_resize_enabled(); } std::unique_ptr OffScreenRenderWidgetHostView::DelegatedFrameHostCreateResizeLock( bool defer_compositor_lock) { - return nullptr; + return std::unique_ptr(new AtomResizeLock( + this, + DelegatedFrameHostDesiredSizeInDIP(), + defer_compositor_lock)); } void OffScreenRenderWidgetHostView::DelegatedFrameHostResizeLockWasReleased() { @@ -752,6 +766,91 @@ void OffScreenRenderWidgetHostView::SetBeginFrameSource( #endif // !defined(OS_MACOSX) +bool OffScreenRenderWidgetHostView::TransformPointToLocalCoordSpace( + const gfx::Point& point, + const cc::SurfaceId& original_surface, + gfx::Point* transformed_point) { + // Transformations use physical pixels rather than DIP, so conversion + // is necessary. + gfx::Point point_in_pixels = + gfx::ConvertPointToPixel(scale_factor_, point); + if (!GetDelegatedFrameHost()->TransformPointToLocalCoordSpace( + point_in_pixels, original_surface, transformed_point)) { + return false; + } + + *transformed_point = + gfx::ConvertPointToDIP(scale_factor_, *transformed_point); + return true; +} + +bool OffScreenRenderWidgetHostView::TransformPointToCoordSpaceForView( + const gfx::Point& point, + RenderWidgetHostViewBase* target_view, + gfx::Point* transformed_point) { + if (target_view == this) { + *transformed_point = point; + return true; + } + + // In TransformPointToLocalCoordSpace() there is a Point-to-Pixel conversion, + // but it is not necessary here because the final target view is responsible + // for converting before computing the final transform. + return GetDelegatedFrameHost()->TransformPointToCoordSpaceForView( + point, target_view, transformed_point); +} + +void OffScreenRenderWidgetHostView::CancelWidget() { + if (render_widget_host_) + render_widget_host_->LostCapture(); + Hide(); + + if (parent_host_view_) { + if (parent_host_view_->popup_host_view_ == this) { + parent_host_view_->set_popup_host_view(NULL); + parent_host_view_->popup_bitmap_.reset(); + } else if (parent_host_view_->child_host_view_ == this) { + parent_host_view_->set_child_host_view(NULL); + parent_host_view_->Show(); + } else { + parent_host_view_->RemoveGuestHostView(this); + } + parent_host_view_ = NULL; + } + + if (render_widget_host_ && !is_destroyed_) { + is_destroyed_ = true; + // Results in a call to Destroy(). + render_widget_host_->ShutdownAndDestroyWidget(true); + } +} + +void OffScreenRenderWidgetHostView::AddGuestHostView( + OffScreenRenderWidgetHostView* guest_host) { + guest_host_views_.insert(guest_host); +} + +void OffScreenRenderWidgetHostView::RemoveGuestHostView( + OffScreenRenderWidgetHostView* guest_host) { + guest_host_views_.erase(guest_host); +} + +void OffScreenRenderWidgetHostView::RegisterGuestViewFrameSwappedCallback( + content::RenderWidgetHostViewGuest* guest_host_view) { + guest_host_view->RegisterFrameSwappedCallback(base::MakeUnique( + base::Bind(&OffScreenRenderWidgetHostView::OnGuestViewFrameSwapped, + weak_ptr_factory_.GetWeakPtr(), + base::Unretained(guest_host_view)))); +} + +void OffScreenRenderWidgetHostView::OnGuestViewFrameSwapped( + content::RenderWidgetHostViewGuest* guest_host_view) { + InvalidateBounds( + gfx::ConvertRectToPixel(scale_factor_, guest_host_view->GetViewBounds())); + + RegisterGuestViewFrameSwappedCallback(guest_host_view); +} + std::unique_ptr OffScreenRenderWidgetHostView::CreateSoftwareOutputDevice( ui::Compositor* compositor) { @@ -759,6 +858,8 @@ std::unique_ptr DCHECK(!copy_frame_generator_); DCHECK(!software_output_device_); + ResizeRootLayer(); + software_output_device_ = new OffScreenOutputDevice( transparent_, base::Bind(&OffScreenRenderWidgetHostView::OnPaint, @@ -780,7 +881,7 @@ bool OffScreenRenderWidgetHostView::InstallTransparency() { } bool OffScreenRenderWidgetHostView::IsAutoResizeEnabled() const { - return false; + return render_widget_host_->auto_resize_enabled(); } void OffScreenRenderWidgetHostView::SetNeedsBeginFrames( @@ -794,10 +895,147 @@ void OffScreenRenderWidgetHostView::SetNeedsBeginFrames( } } +void CopyBitmapTo( + const SkBitmap& destination, + const SkBitmap& source, + const gfx::Rect& pos) { + SkAutoLockPixels source_pixels_lock(source); + SkAutoLockPixels destination_pixels_lock(destination); + + char* src = static_cast(source.getPixels()); + char* dest = static_cast(destination.getPixels()); + int pixelsize = source.bytesPerPixel(); + + if (pos.x() + pos.width() <= destination.width() && + pos.y() + pos.height() <= destination.height()) { + for (int i = 0; i < pos.height(); i++) { + memcpy(dest + ((pos.y() + i) * destination.width() + pos.x()) * pixelsize, + src + (i * source.width()) * pixelsize, + pos.width() * pixelsize); + } + } + + destination.notifyPixelsChanged(); +} + void OffScreenRenderWidgetHostView::OnPaint( const gfx::Rect& damage_rect, const SkBitmap& bitmap) { TRACE_EVENT0("electron", "OffScreenRenderWidgetHostView::OnPaint"); - callback_.Run(damage_rect, bitmap); + + HoldResize(); + + if (parent_callback_) { + parent_callback_.Run(damage_rect, bitmap); + } else if (popup_host_view_ && popup_bitmap_.get()) { + gfx::Rect pos = popup_host_view_->popup_position_; + gfx::Rect damage(damage_rect); + damage.Union(pos); + + SkBitmap copy = SkBitmapOperations::CreateTiledBitmap(bitmap, + pos.x(), pos.y(), pos.width(), pos.height()); + + CopyBitmapTo(bitmap, *popup_bitmap_, pos); + callback_.Run(damage, bitmap); + CopyBitmapTo(bitmap, copy, pos); + } else { + callback_.Run(damage_rect, bitmap); + } + + ReleaseResize(); +} + +void OffScreenRenderWidgetHostView::OnPopupPaint( + const gfx::Rect& damage_rect, const SkBitmap& bitmap) { + if (popup_host_view_ && popup_bitmap_.get()) + bitmap.deepCopyTo(popup_bitmap_.get()); + InvalidateBounds(popup_host_view_->popup_position_); +} + +void OffScreenRenderWidgetHostView::HoldResize() { + if (!hold_resize_) + hold_resize_ = true; +} + +void OffScreenRenderWidgetHostView::ReleaseResize() { + if (!hold_resize_) + return; + + hold_resize_ = false; + if (pending_resize_) { + pending_resize_ = false; + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, + base::Bind(&OffScreenRenderWidgetHostView::WasResized, + weak_ptr_factory_.GetWeakPtr())); + } +} + +void OffScreenRenderWidgetHostView::WasResized() { + if (hold_resize_) { + if (!pending_resize_) + pending_resize_ = true; + return; + } + + ResizeRootLayer(); + if (render_widget_host_) + render_widget_host_->WasResized(); + GetDelegatedFrameHost()->WasResized(); +} + +void OffScreenRenderWidgetHostView::ProcessKeyboardEvent( + const content::NativeWebKeyboardEvent& event) { + if (!render_widget_host_) + return; + render_widget_host_->ForwardKeyboardEvent(event); +} + +void OffScreenRenderWidgetHostView::ProcessMouseEvent( + const blink::WebMouseEvent& event, + const ui::LatencyInfo& latency) { + if (!IsPopupWidget()) { + if (popup_host_view_ && + popup_host_view_->popup_position_.Contains(event.x, event.y)) { + blink::WebMouseEvent popup_event(event); + popup_event.x -= popup_host_view_->popup_position_.x(); + popup_event.y -= popup_host_view_->popup_position_.y(); + popup_event.windowX = popup_event.x; + popup_event.windowY = popup_event.y; + + popup_host_view_->ProcessMouseEvent(popup_event, latency); + return; + } + } + if (!render_widget_host_) + return; + render_widget_host_->ForwardMouseEvent(event); +} + +void OffScreenRenderWidgetHostView::ProcessMouseWheelEvent( + const blink::WebMouseWheelEvent& event, + const ui::LatencyInfo& latency) { + if (!IsPopupWidget()) { + if (popup_host_view_) { + if (popup_host_view_->popup_position_.Contains(event.x, event.y)) { + blink::WebMouseWheelEvent popup_event(event); + popup_event.x -= popup_host_view_->popup_position_.x(); + popup_event.y -= popup_host_view_->popup_position_.y(); + popup_event.windowX = popup_event.x; + popup_event.windowY = popup_event.y; + popup_host_view_->ProcessMouseWheelEvent(popup_event, latency); + return; + } else { + // Scrolling outside of the popup widget so destroy it. + // Execute asynchronously to avoid deleting the widget from inside some + // other callback. + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, + base::Bind(&OffScreenRenderWidgetHostView::CancelWidget, + popup_host_view_->weak_ptr_factory_.GetWeakPtr())); + } + } + } + if (!render_widget_host_) + return; + render_widget_host_->ForwardWheelEvent(event); } void OffScreenRenderWidgetHostView::SetPainting(bool painting) { @@ -813,12 +1051,22 @@ bool OffScreenRenderWidgetHostView::IsPainting() const { } void OffScreenRenderWidgetHostView::SetFrameRate(int frame_rate) { - if (frame_rate <= 0) - frame_rate = 1; - if (frame_rate > 60) - frame_rate = 60; + if (parent_host_view_) { + if (parent_host_view_->GetFrameRate() == GetFrameRate()) + return; - frame_rate_ = frame_rate; + frame_rate_ = parent_host_view_->GetFrameRate(); + } else { + if (frame_rate <= 0) + frame_rate = 1; + if (frame_rate > 60) + frame_rate = 60; + + frame_rate_ = frame_rate; + } + + for (auto guest_host_view : guest_host_views_) + guest_host_view->SetFrameRate(frame_rate); SetupFrameRate(true); } @@ -843,46 +1091,52 @@ OffScreenRenderWidgetHostView::GetDelegatedFrameHost() const { #endif void OffScreenRenderWidgetHostView::SetupFrameRate(bool force) { - if (!force && frame_rate_threshold_ms_ != 0) + if (!force && frame_rate_threshold_us_ != 0) return; - frame_rate_threshold_ms_ = 1000 / frame_rate_; + frame_rate_threshold_us_ = 1000000 / frame_rate_; GetCompositor()->vsync_manager()->SetAuthoritativeVSyncInterval( - base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_)); + base::TimeDelta::FromMicroseconds(frame_rate_threshold_us_)); - if (copy_frame_generator_) { - copy_frame_generator_->set_frame_rate_threshold_ms( - frame_rate_threshold_ms_); + if (copy_frame_generator_.get()) { + copy_frame_generator_->set_frame_rate_threshold_us( + frame_rate_threshold_us_); } - if (begin_frame_timer_) { - begin_frame_timer_->SetFrameRateThresholdMs(frame_rate_threshold_ms_); + if (begin_frame_timer_.get()) { + begin_frame_timer_->SetFrameRateThresholdUs(frame_rate_threshold_us_); } else { begin_frame_timer_.reset(new AtomBeginFrameTimer( - frame_rate_threshold_ms_, + frame_rate_threshold_us_, base::Bind(&OffScreenRenderWidgetHostView::OnBeginFrameTimerTick, weak_ptr_factory_.GetWeakPtr()))); } } void OffScreenRenderWidgetHostView::Invalidate() { - const gfx::Rect& bounds_in_pixels = GetViewBounds(); + InvalidateBounds(GetViewBounds()); +} +void OffScreenRenderWidgetHostView::InvalidateBounds(const gfx::Rect& bounds) { if (software_output_device_) { - software_output_device_->OnPaint(bounds_in_pixels); + software_output_device_->OnPaint(bounds); } else if (copy_frame_generator_) { - copy_frame_generator_->GenerateCopyFrame(true, bounds_in_pixels); + copy_frame_generator_->GenerateCopyFrame(bounds); } } void OffScreenRenderWidgetHostView::ResizeRootLayer() { SetupFrameRate(false); - const float orgScaleFactor = scale_factor_; - const bool scaleFactorDidChange = (orgScaleFactor != scale_factor_); + const float compositorScaleFactor = GetCompositor()->device_scale_factor(); + const bool scaleFactorDidChange = (compositorScaleFactor != scale_factor_); - gfx::Size size = GetViewBounds().size(); + gfx::Size size; + if (!IsPopupWidget()) + size = GetViewBounds().size(); + else + size = popup_position_.size(); if (!scaleFactorDidChange && size == GetRootLayer()->bounds().size()) return; @@ -894,15 +1148,20 @@ void OffScreenRenderWidgetHostView::ResizeRootLayer() { GetCompositor()->SetScaleAndSize(scale_factor_, size_in_pixels); } -void OffScreenRenderWidgetHostView::OnWindowResize() { - // In offscreen mode call RenderWidgetHostView's SetSize explicitly - auto size = native_window_->GetSize(); - SetSize(size); -} - -void OffScreenRenderWidgetHostView::OnWindowClosed() { - native_window_->RemoveObserver(this); - native_window_ = nullptr; +cc::FrameSinkId OffScreenRenderWidgetHostView::AllocateFrameSinkId( + bool is_guest_view_hack) { + // GuestViews have two RenderWidgetHostViews and so we need to make sure + // we don't have FrameSinkId collisions. + // The FrameSinkId generated here must be unique with FrameSinkId allocated + // in ContextFactoryPrivate. + content::ImageTransportFactory* factory = + content::ImageTransportFactory::GetInstance(); + return is_guest_view_hack + ? factory->GetContextFactoryPrivate()->AllocateFrameSinkId() + : cc::FrameSinkId(base::checked_cast( + render_widget_host_->GetProcess()->GetID()), + base::checked_cast( + render_widget_host_->GetRoutingID())); } } // namespace atom diff --git a/atom/browser/osr/osr_render_widget_host_view.h b/atom/browser/osr/osr_render_widget_host_view.h index 7380476bee6..1f8558db1f9 100644 --- a/atom/browser/osr/osr_render_widget_host_view.h +++ b/atom/browser/osr/osr_render_widget_host_view.h @@ -5,6 +5,7 @@ #ifndef ATOM_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_ #define ATOM_BROWSER_OSR_OSR_RENDER_WIDGET_HOST_VIEW_H_ +#include #include #include @@ -20,10 +21,13 @@ #include "base/time/time.h" #include "cc/output/compositor_frame.h" #include "cc/scheduler/begin_frame_source.h" +#include "content/browser/frame_host/render_widget_host_view_guest.h" #include "content/browser/renderer_host/delegated_frame_host.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/browser/renderer_host/resize_lock.h" +#include "content/browser/web_contents/web_contents_view.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/WebKit/public/platform/WebVector.h" #include "ui/base/ime/text_input_client.h" #include "ui/compositor/compositor.h" @@ -69,6 +73,7 @@ class OffScreenRenderWidgetHostView OffScreenRenderWidgetHostView(bool transparent, const OnPaintCallback& callback, content::RenderWidgetHost* render_widget_host, + OffScreenRenderWidgetHostView* parent_host_view, NativeWindow* native_window); ~OffScreenRenderWidgetHostView() override; @@ -126,28 +131,33 @@ class OffScreenRenderWidgetHostView #endif void SelectionBoundsChanged(const ViewHostMsg_SelectionBounds_Params &) override; - void CopyFromCompositingSurface(const gfx::Rect &, - const gfx::Size &, - const content::ReadbackRequestCallback &, - const SkColorType) override; - void CopyFromCompositingSurfaceToVideoFrame( - const gfx::Rect &, - const scoped_refptr &, - const base::Callback &) override; - bool CanCopyToVideoFrame(void) const override; + void CopyFromSurface( + const gfx::Rect& src_subrect, + const gfx::Size& dst_size, + const content::ReadbackRequestCallback& callback, + const SkColorType color_type) override; + void CopyFromSurfaceToVideoFrame( + const gfx::Rect& src_subrect, + scoped_refptr target, + const base::Callback& callback) override; void BeginFrameSubscription( std::unique_ptr) override; void EndFrameSubscription() override; + void InitAsGuest( + content::RenderWidgetHostView*, + content::RenderWidgetHostViewGuest*) override; bool HasAcceleratedSurface(const gfx::Size &) override; gfx::Rect GetBoundsInRootWindow(void) override; - void LockCompositingSurface(void) override; - void UnlockCompositingSurface(void) override; void ImeCompositionRangeChanged( const gfx::Range &, const std::vector&) override; gfx::Size GetPhysicalBackingSize() const override; gfx::Size GetRequestedRendererSize() const override; + content::RenderWidgetHostViewBase* CreateViewForWidget( + content::RenderWidgetHost*, + content::RenderWidgetHost*, + content::WebContentsView*) override; + #if !defined(OS_MACOSX) // content::DelegatedFrameHostClient: int DelegatedFrameHostGetGpuMemoryBufferClientId(void) const; @@ -166,6 +176,15 @@ class OffScreenRenderWidgetHostView void SetBeginFrameSource(cc::BeginFrameSource* source) override; #endif // !defined(OS_MACOSX) + bool TransformPointToLocalCoordSpace( + const gfx::Point& point, + const cc::SurfaceId& original_surface, + gfx::Point* transformed_point) override; + bool TransformPointToCoordSpaceForView( + const gfx::Point& point, + RenderWidgetHostViewBase* target_view, + gfx::Point* transformed_point) override; + // ui::CompositorDelegate: std::unique_ptr CreateSoftwareOutputDevice( ui::Compositor* compositor) override; @@ -182,11 +201,36 @@ class OffScreenRenderWidgetHostView base::TimeDelta vsync_period); #if defined(OS_MACOSX) - void CreatePlatformWidget(); + void CreatePlatformWidget(bool is_guest_view_hack); void DestroyPlatformWidget(); #endif + void CancelWidget(); + void AddGuestHostView(OffScreenRenderWidgetHostView* guest_host); + void RemoveGuestHostView(OffScreenRenderWidgetHostView* guest_host); + + void RegisterGuestViewFrameSwappedCallback( + content::RenderWidgetHostViewGuest* guest_host_view); + void OnGuestViewFrameSwapped( + content::RenderWidgetHostViewGuest* guest_host_view); + void OnPaint(const gfx::Rect& damage_rect, const SkBitmap& bitmap); + void OnPopupPaint(const gfx::Rect& damage_rect, const SkBitmap& bitmap); + + bool IsPopupWidget() const { + return popup_type_ != blink::WebPopupTypeNone; + } + + void HoldResize(); + void ReleaseResize(); + void WasResized(); + + void ProcessKeyboardEvent( + const content::NativeWebKeyboardEvent& event) override; + void ProcessMouseEvent(const blink::WebMouseEvent& event, + const ui::LatencyInfo& latency) override; + void ProcessMouseWheelEvent(const blink::WebMouseWheelEvent& event, + const ui::LatencyInfo& latency) override; void SetPainting(bool painting); bool IsPainting() const; @@ -199,6 +243,7 @@ class OffScreenRenderWidgetHostView content::DelegatedFrameHost* GetDelegatedFrameHost() const; void Invalidate(); + void InvalidateBounds(const gfx::Rect&); content::RenderWidgetHostImpl* render_widget_host() const { return render_widget_host_; } @@ -206,29 +251,53 @@ class OffScreenRenderWidgetHostView gfx::Size size() const { return size_; } float scale_factor() const { return scale_factor_; } + void set_popup_host_view(OffScreenRenderWidgetHostView* popup_view) { + popup_host_view_ = popup_view; + } + + void set_child_host_view(OffScreenRenderWidgetHostView* child_view) { + child_host_view_ = child_view; + } + private: void SetupFrameRate(bool force); void ResizeRootLayer(); + cc::FrameSinkId AllocateFrameSinkId(bool is_guest_view_hack); + // Weak ptrs. content::RenderWidgetHostImpl* render_widget_host_; + + OffScreenRenderWidgetHostView* parent_host_view_; + OffScreenRenderWidgetHostView* popup_host_view_; + std::unique_ptr popup_bitmap_; + OffScreenRenderWidgetHostView* child_host_view_; + std::set guest_host_views_; + NativeWindow* native_window_; OffScreenOutputDevice* software_output_device_; const bool transparent_; OnPaintCallback callback_; + OnPaintCallback parent_callback_; int frame_rate_; - int frame_rate_threshold_ms_; + int frame_rate_threshold_us_; base::Time last_time_; float scale_factor_; - bool is_showing_; gfx::Vector2dF last_scroll_offset_; gfx::Size size_; bool painting_; + bool is_showing_; + bool is_destroyed_; + gfx::Rect popup_position_; + + bool hold_resize_; + bool pending_resize_; + std::unique_ptr root_layer_; std::unique_ptr compositor_; std::unique_ptr delegated_frame_host_; @@ -236,6 +305,10 @@ class OffScreenRenderWidgetHostView std::unique_ptr copy_frame_generator_; std::unique_ptr begin_frame_timer_; + // Provides |source_id| for BeginFrameArgs that we create. + cc::StubBeginFrameSource begin_frame_source_; + uint64_t begin_frame_number_ = cc::BeginFrameArgs::kStartingFrameNumber; + #if defined(OS_MACOSX) CALayer* background_layer_; std::unique_ptr browser_compositor_; diff --git a/atom/browser/osr/osr_render_widget_host_view_mac.mm b/atom/browser/osr/osr_render_widget_host_view_mac.mm index 7cf010ff8e2..8e13c2c577d 100644 --- a/atom/browser/osr/osr_render_widget_host_view_mac.mm +++ b/atom/browser/osr/osr_render_widget_host_view_mac.mm @@ -121,10 +121,12 @@ void OffScreenRenderWidgetHostView::SelectionChanged( RenderWidgetHostViewBase::SelectionChanged(text, offset, range); } -void OffScreenRenderWidgetHostView::CreatePlatformWidget() { +void OffScreenRenderWidgetHostView::CreatePlatformWidget( + bool is_guest_view_hack) { mac_helper_ = new MacHelper(this); browser_compositor_.reset(new content::BrowserCompositorMac( - mac_helper_, mac_helper_, render_widget_host_->is_hidden(), true)); + mac_helper_, mac_helper_, render_widget_host_->is_hidden(), true, + AllocateFrameSinkId(is_guest_view_hack))); } void OffScreenRenderWidgetHostView::DestroyPlatformWidget() { diff --git a/atom/browser/osr/osr_web_contents_view.cc b/atom/browser/osr/osr_web_contents_view.cc index 0d9c52743bc..0e10abf6a03 100644 --- a/atom/browser/osr/osr_web_contents_view.cc +++ b/atom/browser/osr/osr_web_contents_view.cc @@ -4,7 +4,11 @@ #include "atom/browser/osr/osr_web_contents_view.h" +#include "atom/common/api/api_messages.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/render_view_host.h" #include "third_party/WebKit/public/platform/WebScreenInfo.h" +#include "ui/display/screen.h" namespace atom { @@ -27,19 +31,33 @@ OffScreenWebContentsView::~OffScreenWebContentsView() { void OffScreenWebContentsView::SetWebContents( content::WebContents* web_contents) { web_contents_ = web_contents; + + RenderViewCreated(web_contents_->GetRenderViewHost()); } #if !defined(OS_MACOSX) gfx::NativeView OffScreenWebContentsView::GetNativeView() const { - return gfx::NativeView(); + if (!web_contents_) return gfx::NativeView(); + + auto relay = NativeWindowRelay::FromWebContents(web_contents_); + if (!relay) return gfx::NativeView(); + return relay->window->GetNativeView(); } gfx::NativeView OffScreenWebContentsView::GetContentNativeView() const { - return gfx::NativeView(); + if (!web_contents_) return gfx::NativeView(); + + auto relay = NativeWindowRelay::FromWebContents(web_contents_); + if (!relay) return gfx::NativeView(); + return relay->window->GetNativeView(); } gfx::NativeWindow OffScreenWebContentsView::GetTopLevelNativeWindow() const { - return gfx::NativeWindow(); + if (!web_contents_) return gfx::NativeWindow(); + + auto relay = NativeWindowRelay::FromWebContents(web_contents_); + if (!relay) return gfx::NativeWindow(); + return relay->window->GetNativeWindow(); } #endif @@ -67,7 +85,7 @@ content::DropData* OffScreenWebContentsView::GetDropData() const { } gfx::Rect OffScreenWebContentsView::GetViewBounds() const { - return view_ ? view_->GetViewBounds() : gfx::Rect(); + return GetView() ? GetView()->GetViewBounds() : gfx::Rect(); } void OffScreenWebContentsView::CreateView(const gfx::Size& initial_size, @@ -77,19 +95,40 @@ void OffScreenWebContentsView::CreateView(const gfx::Size& initial_size, content::RenderWidgetHostViewBase* OffScreenWebContentsView::CreateViewForWidget( content::RenderWidgetHost* render_widget_host, bool is_guest_view_hack) { + if (render_widget_host->GetView()) { + return static_cast( + render_widget_host->GetView()); + } + auto relay = NativeWindowRelay::FromWebContents(web_contents_); - view_ = new OffScreenRenderWidgetHostView( - transparent_, callback_, render_widget_host, relay->window.get()); - return view_; + return new OffScreenRenderWidgetHostView( + transparent_, + callback_, + render_widget_host, + nullptr, + relay->window.get()); } content::RenderWidgetHostViewBase* OffScreenWebContentsView::CreateViewForPopupWidget( content::RenderWidgetHost* render_widget_host) { auto relay = NativeWindowRelay::FromWebContents(web_contents_); - view_ = new OffScreenRenderWidgetHostView( - transparent_, callback_, render_widget_host, relay->window.get()); - return view_; + + content::WebContentsImpl *web_contents_impl = + static_cast(web_contents_); + + OffScreenRenderWidgetHostView *view = + static_cast( + web_contents_impl->GetOuterWebContents() + ? web_contents_impl->GetOuterWebContents()->GetRenderWidgetHostView() + : web_contents_impl->GetRenderWidgetHostView()); + + return new OffScreenRenderWidgetHostView( + transparent_, + callback_, + render_widget_host, + view, + relay->window.get()); } void OffScreenWebContentsView::SetPageTitle(const base::string16& title) { @@ -97,8 +136,12 @@ void OffScreenWebContentsView::SetPageTitle(const base::string16& title) { void OffScreenWebContentsView::RenderViewCreated( content::RenderViewHost* host) { - if (view_) - view_->InstallTransparency(); + if (GetView()) + GetView()->InstallTransparency(); + +#if defined(OS_MACOSX) + host->Send(new AtomViewMsg_Offscreen(host->GetRoutingID())); +#endif } void OffScreenWebContentsView::RenderViewSwappedIn( @@ -110,14 +153,22 @@ void OffScreenWebContentsView::SetOverscrollControllerEnabled(bool enabled) { void OffScreenWebContentsView::GetScreenInfo( content::ScreenInfo* screen_info) const { - screen_info->rect = gfx::Rect(view_->size()); - screen_info->available_rect = gfx::Rect(view_->size()); screen_info->depth = 24; screen_info->depth_per_component = 8; - screen_info->device_scale_factor = view_->scale_factor(); screen_info->orientation_angle = 0; + screen_info->device_scale_factor = 1.0; screen_info->orientation_type = content::SCREEN_ORIENTATION_VALUES_LANDSCAPE_PRIMARY; + + if (GetView()) { + screen_info->rect = gfx::Rect(GetView()->size()); + screen_info->available_rect = gfx::Rect(GetView()->size()); + } else { + const display::Display display = + display::Screen::GetScreen()->GetPrimaryDisplay(); + screen_info->rect = display.bounds(); + screen_info->available_rect = display.work_area(); + } } #if defined(OS_MACOSX) @@ -151,4 +202,12 @@ void OffScreenWebContentsView::UpdateDragCursor( blink::WebDragOperation operation) { } +OffScreenRenderWidgetHostView* OffScreenWebContentsView::GetView() const { + if (web_contents_) { + return static_cast( + web_contents_->GetRenderViewHost()->GetWidget()->GetView()); + } + return nullptr; +} + } // namespace atom diff --git a/atom/browser/osr/osr_web_contents_view.h b/atom/browser/osr/osr_web_contents_view.h index 8e4e2429947..ffb3b38619c 100644 --- a/atom/browser/osr/osr_web_contents_view.h +++ b/atom/browser/osr/osr_web_contents_view.h @@ -75,11 +75,12 @@ class OffScreenWebContentsView : public content::WebContentsView, void PlatformDestroy(); #endif + OffScreenRenderWidgetHostView* GetView() const; + const bool transparent_; OnPaintCallback callback_; // Weak refs. - OffScreenRenderWidgetHostView* view_; content::WebContents* web_contents_; #if defined(OS_MACOSX) diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 673bceb38f0..e19b6c8e164 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile electron.icns CFBundleVersion - 1.6.8 + 1.7.1 CFBundleShortVersionString - 1.6.8 + 1.7.1 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index bc9d66a5800..3ab0cf2bebf 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,6,8,0 - PRODUCTVERSION 1,6,8,0 + FILEVERSION 1,7,1,0 + PRODUCTVERSION 1,7,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "1.6.8" + VALUE "FileVersion", "1.7.1" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "1.6.8" + VALUE "ProductVersion", "1.7.1" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/ui/certificate_trust_mac.mm b/atom/browser/ui/certificate_trust_mac.mm index e0888dd3ea2..be6e6963f46 100644 --- a/atom/browser/ui/certificate_trust_mac.mm +++ b/atom/browser/ui/certificate_trust_mac.mm @@ -69,7 +69,7 @@ auto cert_db = net::CertDatabase::GetInstance(); // This forces Chromium to reload the certificate since it might be trusted // now. - cert_db->NotifyObserversCertDBChanged(cert_.get()); + cert_db->NotifyObserversCertDBChanged(); callback_.Run(); diff --git a/atom/browser/ui/certificate_trust_win.cc b/atom/browser/ui/certificate_trust_win.cc index 06c0ffd7f14..dd26291a2e4 100644 --- a/atom/browser/ui/certificate_trust_win.cc +++ b/atom/browser/ui/certificate_trust_win.cc @@ -38,7 +38,7 @@ BOOL AddToTrustedRootStore(const PCCERT_CONTEXT cert_context, if (result) { // force Chromium to reload it's database for this certificate auto cert_db = net::CertDatabase::GetInstance(); - cert_db->NotifyObserversCertDBChanged(cert.get()); + cert_db->NotifyObserversCertDBChanged(); } CertCloseStore(root_cert_store, CERT_CLOSE_STORE_FORCE_FLAG); diff --git a/atom/browser/ui/message_box_gtk.cc b/atom/browser/ui/message_box_gtk.cc index a7bbe51ecf1..ffaa0a76cd7 100644 --- a/atom/browser/ui/message_box_gtk.cc +++ b/atom/browser/ui/message_box_gtk.cc @@ -41,7 +41,7 @@ class GtkMessageBox : public NativeWindowObserver { const gfx::ImageSkia& icon) : cancel_id_(cancel_id), checkbox_checked_(false), - parent_(static_cast(parent_window)) { + parent_(static_cast(parent_window)) { // Create dialog. dialog_ = gtk_message_dialog_new( nullptr, // parent @@ -94,7 +94,7 @@ class GtkMessageBox : public NativeWindowObserver { // Parent window. if (parent_) { parent_->AddObserver(this); - parent_->SetEnabled(false); + static_cast(parent_)->SetEnabled(false); libgtkui::SetGtkTransientForAura(dialog_, parent_->GetNativeWindow()); gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); } @@ -104,7 +104,7 @@ class GtkMessageBox : public NativeWindowObserver { gtk_widget_destroy(dialog_); if (parent_) { parent_->RemoveObserver(this); - parent_->SetEnabled(true); + static_cast(parent_)->SetEnabled(true); } } @@ -179,7 +179,7 @@ class GtkMessageBox : public NativeWindowObserver { bool checkbox_checked_; - NativeWindowViews* parent_; + NativeWindow* parent_; GtkWidget* dialog_; MessageBoxCallback callback_; diff --git a/atom/browser/ui/views/global_menu_bar_x11.cc b/atom/browser/ui/views/global_menu_bar_x11.cc index 266d10b96db..d48d86961ba 100644 --- a/atom/browser/ui/views/global_menu_bar_x11.cc +++ b/atom/browser/ui/views/global_menu_bar_x11.cc @@ -9,8 +9,10 @@ // This conflicts with mate::Converter, #undef True #undef False -// and V8. +// and V8, #undef None +// and url_request_status.h, +#undef Status #include #include diff --git a/atom/browser/ui/views/submenu_button.cc b/atom/browser/ui/views/submenu_button.cc index 617dd3346da..aabed323d2b 100644 --- a/atom/browser/ui/views/submenu_button.cc +++ b/atom/browser/ui/views/submenu_button.cc @@ -36,7 +36,7 @@ SubmenuButton::SubmenuButton(const base::string16& title, if (GetUnderlinePosition(title, &accelerator_, &underline_start_, &underline_end_)) - gfx::Canvas::SizeStringInt(GetText(), GetFontList(), &text_width_, + gfx::Canvas::SizeStringInt(GetText(), gfx::FontList(), &text_width_, &text_height_, 0, 0); SetInkDropMode(InkDropMode::ON); @@ -107,8 +107,8 @@ bool SubmenuButton::GetUnderlinePosition(const base::string16& text, void SubmenuButton::GetCharacterPosition( const base::string16& text, int index, int* pos) { int height = 0; - gfx::Canvas::SizeStringInt(text.substr(0, index), GetFontList(), pos, &height, - 0, 0); + gfx::Canvas::SizeStringInt(text.substr(0, index), gfx::FontList(), pos, + &height, 0, 0); } } // namespace atom diff --git a/atom/browser/ui/webui/pdf_viewer_handler.cc b/atom/browser/ui/webui/pdf_viewer_handler.cc index be2f3373055..5829b59f6d3 100644 --- a/atom/browser/ui/webui/pdf_viewer_handler.cc +++ b/atom/browser/ui/webui/pdf_viewer_handler.cc @@ -33,9 +33,8 @@ void CreateResponseHeadersDictionary(const net::HttpResponseHeaders* headers, while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) { base::Value* existing_value = nullptr; if (result->Get(header_name, &existing_value)) { - base::StringValue* existing_string_value = - static_cast(existing_value); - existing_string_value->GetString()->append(", ").append(header_value); + std::string src = existing_value->GetString(); + result->SetString(header_name, src + ", " + header_value); } else { result->SetString(header_name, header_value); } @@ -131,7 +130,7 @@ void PdfViewerHandler::GetDefaultZoom(const base::ListValue* args) { double zoom_level = host_zoom_map->GetDefaultZoomLevel(); ResolveJavascriptCallback( *callback_id, - base::FundamentalValue(content::ZoomLevelToZoomFactor(zoom_level))); + base::Value(content::ZoomLevelToZoomFactor(zoom_level))); } void PdfViewerHandler::GetInitialZoom(const base::ListValue* args) { @@ -145,7 +144,7 @@ void PdfViewerHandler::GetInitialZoom(const base::ListValue* args) { content::HostZoomMap::GetZoomLevel(web_ui()->GetWebContents()); ResolveJavascriptCallback( *callback_id, - base::FundamentalValue(content::ZoomLevelToZoomFactor(zoom_level))); + base::Value(content::ZoomLevelToZoomFactor(zoom_level))); } void PdfViewerHandler::SetZoom(const base::ListValue* args) { @@ -159,7 +158,7 @@ void PdfViewerHandler::SetZoom(const base::ListValue* args) { content::HostZoomMap::SetZoomLevel(web_ui()->GetWebContents(), zoom_level); - ResolveJavascriptCallback(*callback_id, base::FundamentalValue(zoom_level)); + ResolveJavascriptCallback(*callback_id, base::Value(zoom_level)); } void PdfViewerHandler::GetStrings(const base::ListValue* args) { @@ -204,7 +203,7 @@ void PdfViewerHandler::OnZoomLevelChanged( if (change.host == kPdfViewerUIHost) { CallJavascriptFunction( "cr.webUIListenerCallback", base::StringValue("onZoomLevelChanged"), - base::FundamentalValue( + base::Value( content::ZoomLevelToZoomFactor(change.zoom_level))); } } diff --git a/atom/browser/ui/webui/pdf_viewer_ui.cc b/atom/browser/ui/webui/pdf_viewer_ui.cc index 580d1831e58..b6073f8409a 100644 --- a/atom/browser/ui/webui/pdf_viewer_ui.cc +++ b/atom/browser/ui/webui/pdf_viewer_ui.cc @@ -134,7 +134,8 @@ class PdfViewerUI::ResourceRequester content::ResourceDispatcherHostImpl::Get()->InitializeURLRequest( request.get(), content::Referrer(url, blink::WebReferrerPolicyDefault), false, // download. - render_process_id, render_view_id, render_frame_id, resource_context); + render_process_id, render_view_id, render_frame_id, + content::PREVIEWS_OFF, resource_context); content::ResourceRequestInfoImpl* info = content::ResourceRequestInfoImpl::ForRequest(request.get()); @@ -202,7 +203,8 @@ PdfViewerUI::PdfViewerUI(content::BrowserContext* browser_context, content::WebContentsObserver(web_ui->GetWebContents()), src_(src) { pdf_handler_ = new PdfViewerHandler(src); - web_ui->AddMessageHandler(pdf_handler_); + web_ui->AddMessageHandler( + std::unique_ptr(pdf_handler_)); content::URLDataSource::Add(browser_context, new BundledDataSource); } diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 7bb8605039a..adab0bf85ac 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -106,6 +106,8 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( // integration. if (IsSandboxed(web_contents)) command_line->AppendSwitch(switches::kEnableSandbox); + if (web_preferences.GetBoolean("nativeWindowOpen", &b) && b) + command_line->AppendSwitch(switches::kNativeWindowOpen); // The preload script. base::FilePath::StringType preload; @@ -212,6 +214,22 @@ bool WebContentsPreferences::IsSandboxed(content::WebContents* web_contents) { return sandboxed; } +bool WebContentsPreferences::UsesNativeWindowOpen( + content::WebContents* web_contents) { + WebContentsPreferences* self; + if (!web_contents) + return false; + + self = FromWebContents(web_contents); + if (!self) + return false; + + base::DictionaryValue& web_preferences = self->web_preferences_; + bool use = false; + web_preferences.GetBoolean("nativeWindowOpen", &use); + return use; +} + // static void WebContentsPreferences::OverrideWebkitPrefs( content::WebContents* web_contents, content::WebPreferences* prefs) { diff --git a/atom/browser/web_contents_preferences.h b/atom/browser/web_contents_preferences.h index f6e44c51b10..f046cdfc99a 100644 --- a/atom/browser/web_contents_preferences.h +++ b/atom/browser/web_contents_preferences.h @@ -38,6 +38,7 @@ class WebContentsPreferences content::WebContents* web_contents, base::CommandLine* command_line); static bool IsSandboxed(content::WebContents* web_contents); + static bool UsesNativeWindowOpen(content::WebContents* web_contents); // Modify the WebPreferences according to |web_contents|'s preferences. static void OverrideWebkitPrefs( diff --git a/atom/common/api/api_messages.h b/atom/common/api/api_messages.h index ef945d9eebe..e25089b869a 100644 --- a/atom/common/api/api_messages.h +++ b/atom/common/api/api_messages.h @@ -35,6 +35,8 @@ IPC_MESSAGE_ROUTED3(AtomViewMsg_Message, base::string16 /* channel */, base::ListValue /* arguments */) +IPC_MESSAGE_ROUTED0(AtomViewMsg_Offscreen) + // Sent by the renderer when the draggable regions are updated. IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions, std::vector /* regions */) diff --git a/atom/common/api/atom_api_asar.cc b/atom/common/api/atom_api_asar.cc index 3151da6f171..ef27850370f 100644 --- a/atom/common/api/atom_api_asar.cc +++ b/atom/common/api/atom_api_asar.cc @@ -6,17 +6,17 @@ #include -#include "atom_natives.h" // NOLINT: This file is generated with coffee2c. - #include "atom/common/asar/archive.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/file_path_converter.h" -#include "atom/common/node_includes.h" #include "native_mate/arguments.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" #include "native_mate/wrappable.h" +#include "atom/common/node_includes.h" +#include "atom_natives.h" // NOLINT: This file is generated with js2c. + namespace { class Archive : public mate::Wrappable { @@ -130,7 +130,7 @@ class Archive : public mate::Wrappable { void InitAsarSupport(v8::Isolate* isolate, v8::Local process, v8::Local require) { - // Evaluate asar_init.coffee. + // Evaluate asar_init.js. const char* asar_init_native = reinterpret_cast( static_cast(node::asar_init_data)); v8::Local asar_init = v8::Script::Compile(v8::String::NewFromUtf8( diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index 6811ab91930..c15e47407b6 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -202,6 +202,11 @@ void Noop(char*, void*) { NativeImage::NativeImage(v8::Isolate* isolate, const gfx::Image& image) : image_(image) { Init(isolate); + if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) { + isolate->AdjustAmountOfExternalAllocatedMemory( + image_.ToImageSkia()->bitmap()->computeSize64()); + } + MarkHighMemoryUsage(); } #if defined(OS_WIN) @@ -212,10 +217,20 @@ NativeImage::NativeImage(v8::Isolate* isolate, const base::FilePath& hicon_path) ReadImageSkiaFromICO(&image_skia, GetHICON(256)); image_ = gfx::Image(image_skia); Init(isolate); + if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) { + isolate->AdjustAmountOfExternalAllocatedMemory( + image_.ToImageSkia()->bitmap()->computeSize64()); + } + MarkHighMemoryUsage(); } #endif -NativeImage::~NativeImage() {} +NativeImage::~NativeImage() { + if (image_.HasRepresentation(gfx::Image::kImageRepSkia)) { + isolate()->AdjustAmountOfExternalAllocatedMemory( + - image_.ToImageSkia()->bitmap()->computeSize64()); + } +} #if defined(OS_WIN) HICON NativeImage::GetHICON(int size) { diff --git a/atom/common/asar/archive.cc b/atom/common/asar/archive.cc index a3a02996d40..8f75c8fbb57 100644 --- a/atom/common/asar/archive.cc +++ b/atom/common/asar/archive.cc @@ -181,7 +181,7 @@ bool Archive::Init() { std::string error; base::JSONReader reader; std::unique_ptr value(reader.ReadToValue(header)); - if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) { + if (!value || !value->IsType(base::Value::Type::DICTIONARY)) { LOG(ERROR) << "Failed to parse header: " << error; return false; } @@ -269,8 +269,9 @@ bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) { } bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) { - if (external_files_.contains(path)) { - *out = external_files_.get(path)->path(); + auto it = external_files_.find(path.value()); + if (it != external_files_.end()) { + *out = it->second->path(); return true; } @@ -296,7 +297,7 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) { #endif *out = temp_file->path(); - external_files_.set(path, std::move(temp_file)); + external_files_[path.value()] = std::move(temp_file); return true; } diff --git a/atom/common/asar/archive.h b/atom/common/asar/archive.h index 9e3abc3434d..4796d1014b7 100644 --- a/atom/common/asar/archive.h +++ b/atom/common/asar/archive.h @@ -6,9 +6,9 @@ #define ATOM_COMMON_ASAR_ARCHIVE_H_ #include +#include #include -#include "base/containers/scoped_ptr_hash_map.h" #include "base/files/file.h" #include "base/files/file_path.h" @@ -75,8 +75,8 @@ class Archive { std::unique_ptr header_; // Cached external temporary files. - base::ScopedPtrHashMap> - external_files_; + std::unordered_map> external_files_; DISALLOW_COPY_AND_ASSIGN(Archive); }; diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 5474abbc93b..572774358b1 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -6,8 +6,8 @@ #define ATOM_COMMON_ATOM_VERSION_H_ #define ATOM_MAJOR_VERSION 1 -#define ATOM_MINOR_VERSION 6 -#define ATOM_PATCH_VERSION 8 +#define ATOM_MINOR_VERSION 7 +#define ATOM_PATCH_VERSION 1 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/chrome_version.h b/atom/common/chrome_version.h index 4838875354a..e25591c3bf8 100644 --- a/atom/common/chrome_version.h +++ b/atom/common/chrome_version.h @@ -8,7 +8,7 @@ #ifndef ATOM_COMMON_CHROME_VERSION_H_ #define ATOM_COMMON_CHROME_VERSION_H_ -#define CHROME_VERSION_STRING "56.0.2924.87" +#define CHROME_VERSION_STRING "58.0.3029.110" #define CHROME_VERSION "v" CHROME_VERSION_STRING #endif // ATOM_COMMON_CHROME_VERSION_H_ diff --git a/atom/common/common_message_generator.cc b/atom/common/common_message_generator.cc index 854fc8778e9..6b14637cf3c 100644 --- a/atom/common/common_message_generator.cc +++ b/atom/common/common_message_generator.cc @@ -31,4 +31,3 @@ namespace IPC { namespace IPC { #include "atom/common/common_message_generator.h" } // namespace IPC - diff --git a/atom/common/common_message_generator.h b/atom/common/common_message_generator.h index a63c40b9623..8b41a17f29b 100644 --- a/atom/common/common_message_generator.h +++ b/atom/common/common_message_generator.h @@ -9,4 +9,5 @@ #include "chrome/common/tts_messages.h" #include "chrome/common/widevine_cdm_messages.h" #include "chrome/common/chrome_utility_messages.h" +#include "chrome/common/chrome_utility_printing_messages.h" #include "components/pdf/common/pdf_messages.h" diff --git a/atom/common/crash_reporter/crash_reporter_mac.mm b/atom/common/crash_reporter/crash_reporter_mac.mm index 4b59be5dfca..990e1b3b195 100644 --- a/atom/common/crash_reporter/crash_reporter_mac.mm +++ b/atom/common/crash_reporter/crash_reporter_mac.mm @@ -43,11 +43,16 @@ void CrashReporterMac::InitBreakpad(const std::string& product_name, base::FilePath handler_path = framework_bundle_path.Append("Resources").Append("crashpad_handler"); + std::vector args = { + "--no-rate-limit", + "--no-upload-gzip", // not all servers accept gzip + }; + crashpad::CrashpadClient crashpad_client; crashpad_client.StartHandler(handler_path, crashes_dir, crashes_dir, submit_url, StringMap(), - std::vector(), + args, true, false); } // @autoreleasepool diff --git a/atom/common/native_mate_converters/blink_converter.cc b/atom/common/native_mate_converters/blink_converter.cc index d4ee1e44891..5acfe545e49 100644 --- a/atom/common/native_mate_converters/blink_converter.cc +++ b/atom/common/native_mate_converters/blink_converter.cc @@ -14,7 +14,8 @@ #include "content/public/browser/native_web_keyboard_event.h" #include "native_mate/dictionary.h" #include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/web/WebCache.h" +#include "third_party/WebKit/public/platform/WebMouseEvent.h" +#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" #include "third_party/WebKit/public/web/WebDeviceEmulationParams.h" #include "third_party/WebKit/public/web/WebFindOptions.h" #include "ui/base/clipboard/clipboard.h" @@ -149,12 +150,14 @@ bool Converter::FromV8( mate::Dictionary dict; if (!ConvertFromV8(isolate, val, &dict)) return false; - if (!dict.Get("type", &out->type)) + blink::WebInputEvent::Type type; + if (!dict.Get("type", &type)) return false; + out->setType(type); std::vector modifiers; if (dict.Get("modifiers", &modifiers)) - out->modifiers = VectorToBitArray(modifiers); - out->timeStampSeconds = base::Time::Now().ToDoubleT(); + out->setModifiers(VectorToBitArray(modifiers)); + out->setTimeStampSeconds(base::Time::Now().ToDoubleT()); return true; } @@ -175,19 +178,19 @@ bool Converter::FromV8( ui::KeyboardCode keyCode = atom::KeyboardCodeFromStr(str, &shifted); out->windowsKeyCode = keyCode; if (shifted) - out->modifiers |= blink::WebInputEvent::ShiftKey; + out->setModifiers(out->modifiers() | blink::WebInputEvent::ShiftKey); ui::DomCode domCode = ui::UsLayoutKeyboardCodeToDomCode(keyCode); out->domCode = static_cast(domCode); ui::DomKey domKey; ui::KeyboardCode dummy_code; - int flags = atom::WebEventModifiersToEventFlags(out->modifiers); + int flags = atom::WebEventModifiersToEventFlags(out->modifiers()); if (ui::DomCodeToUsLayoutDomKey(domCode, flags, &domKey, &dummy_code)) out->domKey = static_cast(domKey); - if ((out->type == blink::WebInputEvent::Char || - out->type == blink::WebInputEvent::RawKeyDown)) { + if ((out->type() == blink::WebInputEvent::Char || + out->type() == blink::WebInputEvent::RawKeyDown)) { // Make sure to not read beyond the buffer in case some bad code doesn't // NULL-terminate it (this is called from plugins). size_t text_length_cap = blink::WebKeyboardEvent::textLengthCap; @@ -219,20 +222,20 @@ v8::Local Converter::ToV8( v8::Isolate* isolate, const content::NativeWebKeyboardEvent& in) { mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); - if (in.type == blink::WebInputEvent::Type::RawKeyDown) + if (in.type() == blink::WebInputEvent::Type::RawKeyDown) dict.Set("type", "keyDown"); - else if (in.type == blink::WebInputEvent::Type::KeyUp) + else if (in.type() == blink::WebInputEvent::Type::KeyUp) dict.Set("type", "keyUp"); dict.Set("key", ui::KeycodeConverter::DomKeyToKeyString(in.domKey)); dict.Set("code", ui::KeycodeConverter::DomCodeToCodeString( static_cast(in.domCode))); using Modifiers = blink::WebInputEvent::Modifiers; - dict.Set("isAutoRepeat", (in.modifiers & Modifiers::IsAutoRepeat) != 0); - dict.Set("shift", (in.modifiers & Modifiers::ShiftKey) != 0); - dict.Set("control", (in.modifiers & Modifiers::ControlKey) != 0); - dict.Set("alt", (in.modifiers & Modifiers::AltKey) != 0); - dict.Set("meta", (in.modifiers & Modifiers::MetaKey) != 0); + dict.Set("isAutoRepeat", (in.modifiers() & Modifiers::IsAutoRepeat) != 0); + dict.Set("shift", (in.modifiers() & Modifiers::ShiftKey) != 0); + dict.Set("control", (in.modifiers() & Modifiers::ControlKey) != 0); + dict.Set("alt", (in.modifiers() & Modifiers::AltKey) != 0); + dict.Set("meta", (in.modifiers() & Modifiers::MetaKey) != 0); return dict.GetHandle(); } @@ -277,7 +280,7 @@ bool Converter::FromV8( bool can_scroll = true; if (dict.Get("canScroll", &can_scroll) && !can_scroll) { out->hasPreciseScrollingDeltas = false; - out->modifiers &= ~blink::WebInputEvent::ControlKey; + out->setModifiers(out->modifiers() & ~blink::WebInputEvent::ControlKey); } #endif return true; @@ -444,7 +447,7 @@ v8::Local Converter::ToV8( mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); dict.Set("count", static_cast(stat.count)); dict.Set("size", static_cast(stat.size)); - dict.Set("liveSize", static_cast(stat.liveSize)); + dict.Set("liveSize", static_cast(stat.decodedSize)); return dict.GetHandle(); } diff --git a/atom/common/native_mate_converters/blink_converter.h b/atom/common/native_mate_converters/blink_converter.h index 34156f313e5..7b3a14e8307 100644 --- a/atom/common/native_mate_converters/blink_converter.h +++ b/atom/common/native_mate_converters/blink_converter.h @@ -6,7 +6,7 @@ #define ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_ #include "native_mate/converter.h" -#include "third_party/WebKit/public/web/WebCache.h" +#include "third_party/WebKit/public/platform/WebCache.h" #include "third_party/WebKit/public/web/WebContextMenuData.h" namespace blink { diff --git a/atom/common/native_mate_converters/v8_value_converter.cc b/atom/common/native_mate_converters/v8_value_converter.cc index 3ba30e14ff4..65620bdb8ba 100644 --- a/atom/common/native_mate_converters/v8_value_converter.cc +++ b/atom/common/native_mate_converters/v8_value_converter.cc @@ -167,42 +167,42 @@ base::Value* V8ValueConverter::FromV8Value( v8::Local V8ValueConverter::ToV8ValueImpl( v8::Isolate* isolate, const base::Value* value) const { switch (value->GetType()) { - case base::Value::TYPE_NULL: + case base::Value::Type::NONE: return v8::Null(isolate); - case base::Value::TYPE_BOOLEAN: { + case base::Value::Type::BOOLEAN: { bool val = false; value->GetAsBoolean(&val); return v8::Boolean::New(isolate, val); } - case base::Value::TYPE_INTEGER: { + case base::Value::Type::INTEGER: { int val = 0; value->GetAsInteger(&val); return v8::Integer::New(isolate, val); } - case base::Value::TYPE_DOUBLE: { + case base::Value::Type::DOUBLE: { double val = 0.0; value->GetAsDouble(&val); return v8::Number::New(isolate, val); } - case base::Value::TYPE_STRING: { + case base::Value::Type::STRING: { std::string val; value->GetAsString(&val); return v8::String::NewFromUtf8( isolate, val.c_str(), v8::String::kNormalString, val.length()); } - case base::Value::TYPE_LIST: + case base::Value::Type::LIST: return ToV8Array(isolate, static_cast(value)); - case base::Value::TYPE_DICTIONARY: + case base::Value::Type::DICTIONARY: return ToV8Object(isolate, static_cast(value)); - case base::Value::TYPE_BINARY: + case base::Value::Type::BINARY: return ToArrayBuffer(isolate, static_cast(value)); @@ -314,13 +314,13 @@ base::Value* V8ValueConverter::FromV8ValueImpl( return base::Value::CreateNullValue().release(); if (val->IsBoolean()) - return new base::FundamentalValue(val->ToBoolean()->Value()); + return new base::Value(val->ToBoolean()->Value()); if (val->IsInt32()) - return new base::FundamentalValue(val->ToInt32()->Value()); + return new base::Value(val->ToInt32()->Value()); if (val->IsNumber()) - return new base::FundamentalValue(val->ToNumber()->Value()); + return new base::Value(val->ToNumber()->Value()); if (val->IsString()) { v8::String::Utf8Value utf8(val->ToString()); @@ -490,7 +490,7 @@ base::Value* V8ValueConverter::FromV8Object( // there *is* a "windowId" property, but since it should be an int, code // on the browser which doesn't additionally check for null will fail. // We can avoid all bugs related to this by stripping null. - if (strip_null_from_objects_ && child->IsType(base::Value::TYPE_NULL)) + if (strip_null_from_objects_ && child->IsType(base::Value::Type::NONE)) continue; result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()), diff --git a/atom/common/native_mate_converters/v8_value_converter.h b/atom/common/native_mate_converters/v8_value_converter.h index 2b8dcf85964..ae6113cfeb0 100644 --- a/atom/common/native_mate_converters/v8_value_converter.h +++ b/atom/common/native_mate_converters/v8_value_converter.h @@ -10,10 +10,10 @@ #include "v8/include/v8.h" namespace base { -class BinaryValue; class DictionaryValue; class ListValue; class Value; +using BinaryValue = Value; } namespace atom { diff --git a/atom/common/native_mate_converters/value_converter.cc b/atom/common/native_mate_converters/value_converter.cc index c3c7ae0383c..3ed68136709 100644 --- a/atom/common/native_mate_converters/value_converter.cc +++ b/atom/common/native_mate_converters/value_converter.cc @@ -15,7 +15,7 @@ bool Converter::FromV8(v8::Isolate* isolate, std::unique_ptr converter(new atom::V8ValueConverter); std::unique_ptr value(converter->FromV8Value( val, isolate->GetCurrentContext())); - if (value && value->IsType(base::Value::TYPE_DICTIONARY)) { + if (value && value->IsType(base::Value::Type::DICTIONARY)) { out->Swap(static_cast(value.get())); return true; } else { @@ -36,7 +36,7 @@ bool Converter::FromV8(v8::Isolate* isolate, std::unique_ptr converter(new atom::V8ValueConverter); std::unique_ptr value(converter->FromV8Value( val, isolate->GetCurrentContext())); - if (value->IsType(base::Value::TYPE_LIST)) { + if (value->IsType(base::Value::Type::LIST)) { out->Swap(static_cast(value.get())); return true; } else { diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index ae757b1da88..2a7be576276 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -198,12 +198,6 @@ node::Environment* NodeBindings::CreateEnvironment( PathService::Get(content::CHILD_PROCESS_EXE, &helper_exec_path); process.Set("helperExecPath", helper_exec_path); - // Set process._debugWaitConnect if --debug-brk was specified to stop - // the debugger on the first line - if (browser_env_ == BROWSER && - base::CommandLine::ForCurrentProcess()->HasSwitch("debug-brk")) - process.Set("_debugWaitConnect", true); - return env; } diff --git a/atom/common/node_includes.h b/atom/common/node_includes.h index b6cbf36090c..5e9c3fdbec8 100644 --- a/atom/common/node_includes.h +++ b/atom/common/node_includes.h @@ -29,6 +29,7 @@ #include "vendor/node/src/env-inl.h" #include "vendor/node/src/node.h" #include "vendor/node/src/node_buffer.h" +#include "vendor/node/src/node_debug_options.h" #include "vendor/node/src/node_internals.h" #endif // ATOM_COMMON_NODE_INCLUDES_H_ diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 2f1c0368f35..657a36ac41c 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -172,6 +172,7 @@ const char kGuestInstanceID[] = "guest-instance-id"; const char kOpenerID[] = "opener-id"; const char kScrollBounce[] = "scroll-bounce"; const char kHiddenPage[] = "hidden-page"; +const char kNativeWindowOpen[] = "native-window-open"; // Command switch passed to renderer process to control nodeIntegration. const char kNodeIntegrationInWorker[] = "node-integration-in-worker"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 69e7af029e1..057aa26e080 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -92,6 +92,7 @@ extern const char kGuestInstanceID[]; extern const char kOpenerID[]; extern const char kScrollBounce[]; extern const char kHiddenPage[]; +extern const char kNativeWindowOpen[]; extern const char kNodeIntegrationInWorker[]; extern const char kWidevineCdmPath[]; diff --git a/atom/node/osfhandle.cc b/atom/node/osfhandle.cc index b0ac8c1ce3b..f21f55a792a 100644 --- a/atom/node/osfhandle.cc +++ b/atom/node/osfhandle.cc @@ -6,7 +6,11 @@ #include +#if !defined(DEBUG) #define U_I18N_IMPLEMENTATION +#define U_COMMON_IMPLEMENTATION +#define U_COMBINED_IMPLEMENTATION +#endif #include "third_party/icu/source/common/unicode/ubidi.h" #include "third_party/icu/source/common/unicode/uchar.h" @@ -15,6 +19,7 @@ #include "third_party/icu/source/common/unicode/unorm.h" #include "third_party/icu/source/common/unicode/urename.h" #include "third_party/icu/source/common/unicode/ustring.h" +#include "third_party/icu/source/i18n/unicode/dtitvfmt.h" #include "third_party/icu/source/i18n/unicode/measfmt.h" #include "third_party/icu/source/i18n/unicode/translit.h" #include "third_party/icu/source/i18n/unicode/ucsdet.h" @@ -22,6 +27,7 @@ #include "third_party/icu/source/i18n/unicode/uregex.h" #include "third_party/icu/source/i18n/unicode/uspoof.h" #include "third_party/icu/source/i18n/unicode/usearch.h" +#include "util-inl.h" #include "v8-profiler.h" #include "v8-inspector.h" @@ -35,6 +41,18 @@ int close(int fd) { return _close(fd); } +void* ArrayBufferCalloc(size_t length) { + return UncheckedCalloc(length); +} + +void* ArrayBufferMalloc(size_t length) { + return UncheckedMalloc(length); +} + +void ArrayBufferFree(void* data, size_t length) { + return ::free(data); +} + void ReferenceSymbols() { // Following symbols are used by electron.exe but got stripped by compiler, // by using the symbols we can force compiler to keep the objects in node.dll, @@ -60,6 +78,9 @@ void ReferenceSymbols() { UMeasureFormatWidth width = UMEASFMT_WIDTH_WIDE; UErrorCode status = U_ZERO_ERROR; icu::MeasureFormat format(icu::Locale::getRoot(), width, status); + icu::DateInterval internal(0, 0); + icu::DateIntervalFormat::createInstance(UnicodeString(), + icu::Locale::getRoot(), status); reinterpret_cast(nullptr)->clone(); } diff --git a/atom/node/osfhandle.h b/atom/node/osfhandle.h index 1427bb89511..58f87198ae2 100644 --- a/atom/node/osfhandle.h +++ b/atom/node/osfhandle.h @@ -21,6 +21,12 @@ namespace node { __declspec(dllexport) int open_osfhandle(intptr_t osfhandle, int flags); __declspec(dllexport) int close(int fd); +// Memory allocation functions from Node's module, used by ArrayBuffer allocator +// to make sure memories are allocated and freed with the same allocator. +__declspec(dllexport) void* ArrayBufferCalloc(size_t length); +__declspec(dllexport) void* ArrayBufferMalloc(size_t length); +__declspec(dllexport) void ArrayBufferFree(void* data, size_t length); + // A trick to force referencing symbols. __declspec(dllexport) void ReferenceSymbols(); diff --git a/atom/renderer/api/atom_api_spell_check_client.cc b/atom/renderer/api/atom_api_spell_check_client.cc index 9a07cbf6eaa..8d434f1aa0c 100644 --- a/atom/renderer/api/atom_api_spell_check_client.cc +++ b/atom/renderer/api/atom_api_spell_check_client.cc @@ -51,13 +51,13 @@ SpellCheckClient::SpellCheckClient(const std::string& language, SpellCheckClient::~SpellCheckClient() {} -void SpellCheckClient::spellCheck( +void SpellCheckClient::checkSpelling( const blink::WebString& text, int& misspelling_start, int& misspelling_len, blink::WebVector* optional_suggestions) { std::vector results; - SpellCheckText(base::string16(text), true, &results); + SpellCheckText(text.utf16(), true, &results); if (results.size() == 1) { misspelling_start = results[0].location; misspelling_len = results[0].length; @@ -66,10 +66,8 @@ void SpellCheckClient::spellCheck( void SpellCheckClient::requestCheckingOfText( const blink::WebString& textToCheck, - const blink::WebVector& markersInText, - const blink::WebVector& markerOffsets, blink::WebTextCheckingCompletion* completionCallback) { - base::string16 text(textToCheck); + base::string16 text(textToCheck.utf16()); if (text.empty() || !HasWordCharacters(text, 0)) { completionCallback->didCancelCheckingText(); return; diff --git a/atom/renderer/api/atom_api_spell_check_client.h b/atom/renderer/api/atom_api_spell_check_client.h index aa6abb0f56b..cb4a36aadf0 100644 --- a/atom/renderer/api/atom_api_spell_check_client.h +++ b/atom/renderer/api/atom_api_spell_check_client.h @@ -13,6 +13,10 @@ #include "native_mate/scoped_persistent.h" #include "third_party/WebKit/public/web/WebSpellCheckClient.h" +namespace blink { +struct WebTextCheckingResult; +} + namespace atom { namespace api { @@ -27,15 +31,13 @@ class SpellCheckClient : public blink::WebSpellCheckClient { private: // blink::WebSpellCheckClient: - void spellCheck( + void checkSpelling( const blink::WebString& text, int& misspelledOffset, int& misspelledLength, blink::WebVector* optionalSuggestions) override; void requestCheckingOfText( const blink::WebString& textToCheck, - const blink::WebVector& markersInText, - const blink::WebVector& markerOffsets, blink::WebTextCheckingCompletion* completionCallback) override; void showSpellingUI(bool show) override; bool isShowingSpellingUI() override; diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc index f103b89391d..ac12f974791 100644 --- a/atom/renderer/api/atom_api_web_frame.cc +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -16,15 +16,15 @@ #include "content/public/renderer/render_view.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" -#include "third_party/WebKit/public/web/WebCache.h" +#include "third_party/WebKit/public/platform/WebCache.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrameWidget.h" #include "third_party/WebKit/public/web/WebInputMethodController.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebScriptExecutionCallback.h" #include "third_party/WebKit/public/web/WebScriptSource.h" -#include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "third_party/WebKit/public/web/WebView.h" +#include "third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h" #include "atom/common/node_includes.h" @@ -110,7 +110,8 @@ void WebFrame::SetLayoutZoomLevelLimits(double min_level, double max_level) { v8::Local WebFrame::RegisterEmbedderCustomElement( const base::string16& name, v8::Local options) { blink::WebExceptionCode c = 0; - return web_frame_->document().registerEmbedderCustomElement(name, options, c); + return web_frame_->document().registerEmbedderCustomElement( + blink::WebString::fromUTF16(name), options, c); } void WebFrame::RegisterElementResizeCallback( @@ -145,15 +146,14 @@ void WebFrame::SetSpellCheckProvider(mate::Arguments* args, void WebFrame::RegisterURLSchemeAsSecure(const std::string& scheme) { // TODO(pfrazee): Remove 2.0 - // Register scheme to secure list (https, wss, data). - blink::WebSecurityPolicy::registerURLSchemeAsSecure( - blink::WebString::fromUTF8(scheme)); + blink::SchemeRegistry::registerURLSchemeAsSecure( + WTF::String::fromUTF8(scheme.data(), scheme.length())); } void WebFrame::RegisterURLSchemeAsBypassingCSP(const std::string& scheme) { // Register scheme to bypass pages's Content Security Policy. - blink::WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy( - blink::WebString::fromUTF8(scheme)); + blink::SchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy( + WTF::String::fromUTF8(scheme.data(), scheme.length())); } void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme, @@ -175,32 +175,36 @@ void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme, } } // Register scheme to privileged list (https, wss, data, chrome-extension) - blink::WebString privileged_scheme(blink::WebString::fromUTF8(scheme)); + WTF::String privileged_scheme( + WTF::String::fromUTF8(scheme.data(), scheme.length())); if (secure) { // TODO(pfrazee): Remove 2.0 - blink::WebSecurityPolicy::registerURLSchemeAsSecure(privileged_scheme); + blink::SchemeRegistry::registerURLSchemeAsSecure(privileged_scheme); } if (bypassCSP) { - blink::WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy( + blink::SchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy( privileged_scheme); } if (allowServiceWorkers) { - blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers( + blink::SchemeRegistry::registerURLSchemeAsAllowingServiceWorkers( privileged_scheme); } if (supportFetchAPI) { - blink::WebSecurityPolicy::registerURLSchemeAsSupportingFetchAPI( + blink::SchemeRegistry::registerURLSchemeAsSupportingFetchAPI( privileged_scheme); } if (corsEnabled) { - blink::WebSecurityPolicy::registerURLSchemeAsCORSEnabled(privileged_scheme); + blink::SchemeRegistry::registerURLSchemeAsCORSEnabled(privileged_scheme); } } void WebFrame::InsertText(const std::string& text) { web_frame_->frameWidget() ->getActiveWebInputMethodController() - ->commitText(blink::WebString::fromUTF8(text), 0); + ->commitText(blink::WebString::fromUTF8(text), + blink::WebVector(), + blink::WebRange(), + 0); } void WebFrame::InsertCSS(const std::string& css) { @@ -216,7 +220,7 @@ void WebFrame::ExecuteJavaScript(const base::string16& code, std::unique_ptr callback( new ScriptExecutionCallback(completion_callback)); web_frame_->requestExecuteScriptAndReturnValue( - blink::WebScriptSource(code), + blink::WebScriptSource(blink::WebString::fromUTF16(code)), has_user_gesture, callback.release()); } diff --git a/atom/renderer/api/atom_api_web_frame.h b/atom/renderer/api/atom_api_web_frame.h index a35b86cacfa..6c6d0b19f98 100644 --- a/atom/renderer/api/atom_api_web_frame.h +++ b/atom/renderer/api/atom_api_web_frame.h @@ -11,7 +11,7 @@ #include "atom/renderer/guest_view_container.h" #include "native_mate/handle.h" #include "native_mate/wrappable.h" -#include "third_party/WebKit/public/web/WebCache.h" +#include "third_party/WebKit/public/platform/WebCache.h" namespace blink { class WebLocalFrame; diff --git a/atom/renderer/atom_render_frame_observer.cc b/atom/renderer/atom_render_frame_observer.cc index d9a41a48d4a..3912a7bcc6d 100644 --- a/atom/renderer/atom_render_frame_observer.cc +++ b/atom/renderer/atom_render_frame_observer.cc @@ -24,7 +24,6 @@ void AtomRenderFrameObserver::DidClearWindowObject() { void AtomRenderFrameObserver::DidCreateScriptContext( v8::Handle context, - int extension_group, int world_id) { if (ShouldNotifyClient(world_id)) renderer_client_->DidCreateScriptContext(context, render_frame_); @@ -62,8 +61,7 @@ void AtomRenderFrameObserver::CreateIsolatedWorldContext() { // Create initial script context in isolated world blink::WebScriptSource source("void 0"); - frame->executeScriptInIsolatedWorld( - World::ISOLATED_WORLD, &source, 1, ExtensionGroup::MAIN_GROUP); + frame->executeScriptInIsolatedWorld(World::ISOLATED_WORLD, &source, 1); } bool AtomRenderFrameObserver::IsMainWorld(int world_id) { diff --git a/atom/renderer/atom_render_frame_observer.h b/atom/renderer/atom_render_frame_observer.h index 51cb21b3b0e..6eeb32aac03 100644 --- a/atom/renderer/atom_render_frame_observer.h +++ b/atom/renderer/atom_render_frame_observer.h @@ -17,10 +17,6 @@ enum World { ISOLATED_WORLD = 999 }; -enum ExtensionGroup { - MAIN_GROUP = 1 -}; - // Helper class to forward the messages to the client. class AtomRenderFrameObserver : public content::RenderFrameObserver { public: @@ -30,7 +26,6 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver { // content::RenderFrameObserver: void DidClearWindowObject() override; void DidCreateScriptContext(v8::Handle context, - int extension_group, int world_id) override; void WillReleaseScriptContext(v8::Local context, int world_id) override; diff --git a/atom/renderer/atom_render_view_observer.cc b/atom/renderer/atom_render_view_observer.cc index a68238ba4ce..6aa26cccaf0 100644 --- a/atom/renderer/atom_render_view_observer.cc +++ b/atom/renderer/atom_render_view_observer.cc @@ -136,6 +136,7 @@ bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(AtomRenderViewObserver, message) IPC_MESSAGE_HANDLER(AtomViewMsg_Message, OnBrowserMessage) + IPC_MESSAGE_HANDLER(AtomViewMsg_Offscreen, OnOffscreen) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -169,4 +170,8 @@ void AtomRenderViewObserver::OnBrowserMessage(bool send_to_all, } } +void AtomRenderViewObserver::OnOffscreen() { + blink::WebView::setUseExternalPopupMenus(false); +} + } // namespace atom diff --git a/atom/renderer/atom_render_view_observer.h b/atom/renderer/atom_render_view_observer.h index e642bbe9422..4751db2d8c6 100644 --- a/atom/renderer/atom_render_view_observer.h +++ b/atom/renderer/atom_render_view_observer.h @@ -40,6 +40,8 @@ class AtomRenderViewObserver : public content::RenderViewObserver { const base::string16& channel, const base::ListValue& args); + void OnOffscreen(); + AtomRendererClient* renderer_client_; // Whether the document object has been created. diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 5dafe084ce2..393b864031a 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -7,8 +7,6 @@ #include #include -#include "atom_natives.h" // NOLINT: This file is generated with js2c - #include "atom/common/api/atom_bindings.h" #include "atom/common/api/event_emitter_caller.h" #include "atom/common/asar/asar_util.h" @@ -27,6 +25,7 @@ #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "atom/common/node_includes.h" +#include "atom_natives.h" // NOLINT: This file is generated with js2c namespace atom { @@ -172,8 +171,7 @@ void AtomRendererClient::WillDestroyWorkerContextOnWorkerThread( v8::Local AtomRendererClient::GetContext( blink::WebFrame* frame, v8::Isolate* isolate) { if (isolated_world()) - return frame->worldScriptContext( - isolate, World::ISOLATED_WORLD, ExtensionGroup::MAIN_GROUP); + return frame->worldScriptContext(isolate, World::ISOLATED_WORLD); else return frame->mainWorldScriptContext(); } @@ -206,6 +204,8 @@ void AtomRendererClient::SetupMainWorldOverrides( dict.Set(options::kOpenerID, command_line->GetSwitchValueASCII(switches::kOpenerID)); dict.Set("hiddenPage", command_line->HasSwitch(switches::kHiddenPage)); + dict.Set("nativeWindowOpen", + command_line->HasSwitch(switches::kNativeWindowOpen)); v8::Local args[] = { binding }; ignore_result(func->Call(context, v8::Null(isolate), 1, args)); diff --git a/atom/renderer/atom_sandboxed_renderer_client.cc b/atom/renderer/atom_sandboxed_renderer_client.cc index 180d3b75d30..2768f072cd6 100644 --- a/atom/renderer/atom_sandboxed_renderer_client.cc +++ b/atom/renderer/atom_sandboxed_renderer_client.cc @@ -6,14 +6,11 @@ #include -#include "atom_natives.h" // NOLINT: This file is generated with js2c - #include "atom/common/api/api_messages.h" #include "atom/common/api/atom_bindings.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/native_mate_converters/v8_value_converter.h" #include "atom/common/native_mate_converters/value_converter.h" -#include "atom/common/node_includes.h" #include "atom/common/options_switches.h" #include "atom/renderer/api/atom_api_renderer_ipc.h" #include "atom/renderer/atom_render_view_observer.h" @@ -31,6 +28,9 @@ #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebView.h" +#include "atom/common/node_includes.h" +#include "atom_natives.h" // NOLINT: This file is generated with js2c + namespace atom { namespace { diff --git a/atom/renderer/renderer_client_base.cc b/atom/renderer/renderer_client_base.cc index 4d3675e2f6f..1219dfb5dcf 100644 --- a/atom/renderer/renderer_client_base.cc +++ b/atom/renderer/renderer_client_base.cc @@ -30,6 +30,7 @@ #include "third_party/WebKit/public/web/WebPluginParams.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" +#include "third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h" #if defined(OS_MACOSX) #include "base/mac/mac_util.h" @@ -85,6 +86,13 @@ void RendererClientBase::RenderThreadStarted() { blink::WebCustomElement::addEmbedderCustomElementName("webview"); blink::WebCustomElement::addEmbedderCustomElementName("browserplugin"); + // Parse --secure-schemes=scheme1,scheme2 + std::vector secure_schemes_list = + ParseSchemesCLISwitch(switches::kSecureSchemes); + for (const std::string& scheme : secure_schemes_list) + blink::SchemeRegistry::registerURLSchemeAsSecure( + WTF::String::fromUTF8(scheme.data(), scheme.length())); + preferences_manager_.reset(new PreferencesManager); #if defined(OS_WIN) @@ -126,13 +134,6 @@ void RendererClientBase::RenderFrameCreated( // Allow access to file scheme from pdf viewer. blink::WebSecurityPolicy::addOriginAccessWhitelistEntry( GURL(kPdfViewerUIOrigin), "file", "", true); - - // Parse --secure-schemes=scheme1,scheme2 - std::vector secure_schemes_list = - ParseSchemesCLISwitch(switches::kSecureSchemes); - for (const std::string& secure_scheme : secure_schemes_list) - blink::WebSecurityPolicy::registerURLSchemeAsSecure( - blink::WebString::fromUTF8(secure_scheme)); } void RendererClientBase::RenderViewCreated(content::RenderView* render_view) { diff --git a/brightray/CPPLINT.cfg b/brightray/CPPLINT.cfg new file mode 100644 index 00000000000..01e99482f2b --- /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 00000000000..fe24a3c65fb --- /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 00000000000..3d0f7d3edfd --- /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 00000000000..efa0f433ba8 --- /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 00000000000..574103b91ca --- /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 00000000000..88e8bd40848 --- /dev/null +++ b/brightray/browser/browser_context.cc @@ -0,0 +1,245 @@ +// 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/browser/streams/stream_context.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(); +} + +content::StreamContext* BrowserContext::GetStreamContext() { + return content::StreamContext::GetFor(this); +} + +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 00000000000..704cfb9f33c --- /dev/null +++ b/brightray/browser/browser_context.h @@ -0,0 +1,143 @@ +// 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; + content::StreamContext* GetStreamContext() 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 00000000000..8d46b4af4b4 --- /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 00000000000..ab9b086d786 --- /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 00000000000..1df09f20e79 --- /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 00000000000..30ee028669e --- /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 00000000000..c48272f7977 --- /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 00000000000..13c54c4c7ec --- /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 00000000000..b77a83fc07c --- /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 00000000000..e41830258fd --- /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 00000000000..bb1acefab52 --- /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 00000000000..7d4ae4d7d45 --- /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 00000000000..2e2e09f43f5 --- /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 00000000000..9764886d571 --- /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 00000000000..176de9b71ba --- /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 00000000000..74aeac67113 --- /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 00000000000..44c0c22ec64 --- /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 00000000000..a94bcac44d5 --- /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 00000000000..20ee3081558 --- /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 00000000000..25cffa957af --- /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 00000000000..2fabf5d2ba3 --- /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 00000000000..5906499da8c --- /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 00000000000..900857c7a10 --- /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 00000000000..e7ad57fbee8 --- /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 00000000000..94edc1c1a71 --- /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 00000000000..5510bc23cf3 --- /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 00000000000..a48f4794147 --- /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 00000000000..dad3acb4335 --- /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 00000000000..cb9384cb0e0 --- /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 00000000000..6df5ebaaec9 --- /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 00000000000..a90f31ea60e --- /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 00000000000..98594861496 --- /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 00000000000..aca8392bc35 --- /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 00000000000..9819f2d767e --- /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 00000000000..25e7083948b --- /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 00000000000..f6ec9e9da6b --- /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 00000000000..f4599487c84 --- /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 00000000000..27ed9e6bf2f --- /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 00000000000..08e8edebb0b --- /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 00000000000..6bee83d411b --- /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 00000000000..fd77e0685e4 --- /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 00000000000..514a27625ea --- /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 00000000000..fec4c1a0539 --- /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 00000000000..93ec53557d3 --- /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 00000000000..dbbd66d2580 --- /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 00000000000..612449b25de --- /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 00000000000..7941af8a563 --- /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 00000000000..c01749ab43d --- /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 00000000000..47aacfb66b6 --- /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 00000000000..0005c7f56c7 --- /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 00000000000..81aae86f8d4 --- /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 00000000000..8bf411ac1a4 --- /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 00000000000..c36d888df11 --- /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 00000000000..44b7b9c6c14 --- /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 00000000000..2d038e837f8 --- /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 00000000000..73e8538310d --- /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 00000000000..4a04876b6f1 --- /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 00000000000..58988577b43 --- /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 00000000000..9ec577814c3 --- /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 00000000000..ced70acc5e4 --- /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 00000000000..7f01d73cad1 --- /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 00000000000..7373c14ddef --- /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 00000000000..49286a04742 --- /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 00000000000..6687038b097 --- /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 00000000000..c3753e8286f --- /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 00000000000..6b41eb738c1 --- /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 00000000000..e62c335350c --- /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 00000000000..f7be1b028b6 --- /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 00000000000..b8a8b522536 --- /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 00000000000..ba9df5446f3 --- /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 00000000000..a330bc25d1b --- /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 00000000000..93512f71753 --- /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 00000000000..c0ad3d03f06 --- /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 00000000000..01f3284e2b8 --- /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 00000000000..30ef3b16b4b --- /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 00000000000..721ba92ace4 --- /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 00000000000..198e35d8c38 --- /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 00000000000..734529f06eb --- /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 00000000000..36c51515353 --- /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 00000000000..f9b09c64bdf --- /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 00000000000..2010dbd1a04 --- /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 00000000000..0c89db2d4c5 --- /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 00000000000..209260ace1c --- /dev/null +++ b/brightray/browser/url_request_context_getter.cc @@ -0,0 +1,377 @@ +// 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/strings/string_number_conversions.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) { + auto command_line = base::CommandLine::ForCurrentProcess(); + int max_size = 0; + base::StringToInt(command_line->GetSwitchValueASCII(switches::kDiskCacheSize), + &max_size); + + 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, + max_size, + 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 00000000000..dab7ae1e872 --- /dev/null +++ b/brightray/browser/url_request_context_getter.h @@ -0,0 +1,123 @@ +// 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 content { +class StreamContext; +} + +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; } + virtual content::StreamContext* GetStreamContext() { 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(); + } + content::StreamContext* stream_context() const { + return delegate_->GetStreamContext(); + } + + 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 00000000000..8f52ca56995 --- /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 00000000000..9094b9f35dc --- /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 00000000000..99797dea4ad --- /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 00000000000..d63eeb533d6 --- /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 00000000000..565c0258883 --- /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 00000000000..ca86de6baa6 --- /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 00000000000..930775c792b --- /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 00000000000..679490c1852 --- /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 00000000000..f002c476d5a --- /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 00000000000..c191439befc --- /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 00000000000..082ebe76227 --- /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 00000000000..879e62cd39b --- /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 00000000000..ef364ceb4cc --- /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 00000000000..68068a6de26 --- /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 00000000000..643a61f5331 --- /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 00000000000..1ab968c3ef0 --- /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 00000000000..bb9bd9fa4ae --- /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 00000000000..5b6093658aa --- /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 00000000000..9a418cd5037 --- /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 00000000000..0e0c9249c11 --- /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 00000000000..47c7d596a88 --- /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 00000000000..7148b18e18a --- /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 00000000000..aed065d7ec6 --- /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 00000000000..25b02c0d239 --- /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 00000000000..6898b6376de --- /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 00000000000..22635711cb4 --- /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 00000000000..ebe066be613 --- /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 00000000000..4d1d6238c95 --- /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 00000000000..3b7e6a66a95 --- /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 00000000000..78e6bc200b4 --- /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 00000000000..3e386b40dc5 --- /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 00000000000..4551ddfa0aa --- /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 00000000000..2f63a2fb1b1 --- /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 00000000000..29040840ce3 --- /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 00000000000..17ca0585773 --- /dev/null +++ b/brightray/common/switches.cc @@ -0,0 +1,61 @@ +// 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"; + +// Forces the maximum disk space to be used by the disk cache, in bytes. +const char kDiskCacheSize[] = "disk-cache-size"; + +} // namespace switches + +} // namespace brightray diff --git a/brightray/common/switches.h b/brightray/common/switches.h new file mode 100644 index 00000000000..286c4d633b9 --- /dev/null +++ b/brightray/common/switches.h @@ -0,0 +1,27 @@ +// 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[]; +extern const char kDiskCacheSize[]; + +} // 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 00000000000..e1ee46dddee --- /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 00000000000..b7d44013b84 --- /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/chromium_src/chrome/browser/certificate_manager_model.cc b/chromium_src/chrome/browser/certificate_manager_model.cc index 2274c09e341..5f073478cdc 100644 --- a/chromium_src/chrome/browser/certificate_manager_model.cc +++ b/chromium_src/chrome/browser/certificate_manager_model.cc @@ -92,12 +92,12 @@ CertificateManagerModel::CertificateManagerModel( CertificateManagerModel::~CertificateManagerModel() { } -int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module, +int CertificateManagerModel::ImportFromPKCS12(PK11SlotInfo* slot_info, const std::string& data, const base::string16& password, bool is_extractable, net::CertificateList* imported_certs) { - return cert_db_->ImportFromPKCS12(module, data, password, + return cert_db_->ImportFromPKCS12(slot_info, data, password, is_extractable, imported_certs); } diff --git a/chromium_src/chrome/browser/certificate_manager_model.h b/chromium_src/chrome/browser/certificate_manager_model.h index 7646da5b9b4..eb84b69f733 100644 --- a/chromium_src/chrome/browser/certificate_manager_model.h +++ b/chromium_src/chrome/browser/certificate_manager_model.h @@ -44,7 +44,7 @@ class CertificateManagerModel { // |data|, using the given |password|. If |is_extractable| is false, // mark the private key as unextractable from the module. // Returns a net error code on failure. - int ImportFromPKCS12(net::CryptoModule* module, + int ImportFromPKCS12(PK11SlotInfo* slot_info, const std::string& data, const base::string16& password, bool is_extractable, diff --git a/chromium_src/chrome/browser/media/native_desktop_media_list.cc b/chromium_src/chrome/browser/media/native_desktop_media_list.cc index 391bdd50a31..4a9c0e03c8b 100644 --- a/chromium_src/chrome/browser/media/native_desktop_media_list.cc +++ b/chromium_src/chrome/browser/media/native_desktop_media_list.cc @@ -8,6 +8,8 @@ #include #include +using base::PlatformThreadRef; + #include "base/hash.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" diff --git a/chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc b/chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc index b06c3a06409..a35e80cea23 100644 --- a/chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc +++ b/chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc @@ -18,10 +18,9 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/common/chrome_utility_messages.h" -#include "chrome/common/print_messages.h" +#include "chrome/common/chrome_utility_printing_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_data.h" #include "content/public/browser/utility_process_host.h" @@ -30,13 +29,13 @@ #include "printing/pdf_render_settings.h" #include "ui/base/l10n/l10n_util.h" +using content::BrowserThread; + namespace printing { namespace { -using content::BrowserThread; - -class PdfToEmfConverterImpl; +class PdfConverterImpl; // Allows to delete temporary directory after all temporary files created inside // are closed. Windows cannot delete directory with opened files. Directory is @@ -59,8 +58,8 @@ class RefCountedTempDir DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir); }; -typedef std::unique_ptr - ScopedTempFile; +using ScopedTempFile = + std::unique_ptr; // Wrapper for Emf to keep only file handle in memory, and load actual data only // on playback. Emf::InitFromFile() can play metafile directly from disk, but it @@ -74,21 +73,39 @@ class LazyEmf : public MetafilePlayer { } ~LazyEmf() override { Close(); } + protected: + // MetafilePlayer: bool SafePlayback(HDC hdc) const override; - bool GetDataAsVector(std::vector* buffer) const override; - bool SaveTo(base::File* file) const override; - private: void Close() const; bool LoadEmf(Emf* emf) const; + private: mutable scoped_refptr temp_dir_; mutable ScopedTempFile file_; // Mutable because of consts in base class. + bool GetDataAsVector(std::vector* buffer) const override; + bool SaveTo(base::File* file) const override; + DISALLOW_COPY_AND_ASSIGN(LazyEmf); }; -// Converts PDF into EMF. +// Postscript metafile subclass to override SafePlayback. +class PostScriptMetaFile : public LazyEmf { + public: + PostScriptMetaFile(const scoped_refptr& temp_dir, + ScopedTempFile file) + : LazyEmf(temp_dir, std::move(file)) {} + ~PostScriptMetaFile() override; + + protected: + // MetafilePlayer: + bool SafePlayback(HDC hdc) const override; + + DISALLOW_COPY_AND_ASSIGN(PostScriptMetaFile); +}; + +// Class for converting PDF to another format for printing (Emf, Postscript). // Class uses 3 threads: UI, IO and FILE. // Internal workflow is following: // 1. Create instance on the UI thread. (files_, settings_,) @@ -101,36 +118,33 @@ class LazyEmf : public MetafilePlayer { // // All these steps work sequentially, so no data should be accessed // simultaneously by several threads. -class PdfToEmfUtilityProcessHostClient +class PdfConverterUtilityProcessHostClient : public content::UtilityProcessHostClient { public: - PdfToEmfUtilityProcessHostClient( - base::WeakPtr converter, + PdfConverterUtilityProcessHostClient( + base::WeakPtr converter, const PdfRenderSettings& settings); void Start(const scoped_refptr& data, - bool print_text_with_gdi, - const PdfToEmfConverter::StartCallback& start_callback); + const PdfConverter::StartCallback& start_callback); void GetPage(int page_number, - const PdfToEmfConverter::GetPageCallback& get_page_callback); + const PdfConverter::GetPageCallback& get_page_callback); void Stop(); + // UtilityProcessHostClient implementation. + void OnProcessCrashed(int exit_code) override; + void OnProcessLaunchFailed(int exit_code) override; + // Needs to be public to handle ChromeUtilityHostMsg_PreCacheFontCharacters // sync message replies. bool Send(IPC::Message* msg); - // UtilityProcessHostClient implementation. - void OnProcessCrashed(int exit_code) override; - void OnProcessLaunchFailed(int exit_code) override; - bool OnMessageReceived(const IPC::Message& message) override; - - private: + protected: class GetPageCallbackData { public: - GetPageCallbackData(int page_number, - PdfToEmfConverter::GetPageCallback callback) + GetPageCallbackData(int page_number, PdfConverter::GetPageCallback callback) : page_number_(page_number), callback_(callback) {} GetPageCallbackData(GetPageCallbackData&& other) { @@ -140,45 +154,62 @@ class PdfToEmfUtilityProcessHostClient GetPageCallbackData& operator=(GetPageCallbackData&& rhs) { page_number_ = rhs.page_number_; callback_ = rhs.callback_; - emf_ = std::move(rhs.emf_); + file_ = std::move(rhs.file_); return *this; } int page_number() const { return page_number_; } - const PdfToEmfConverter::GetPageCallback& callback() const { - return callback_; - } - ScopedTempFile TakeEmf() { return std::move(emf_); } - void set_emf(ScopedTempFile emf) { emf_ = std::move(emf); } + const PdfConverter::GetPageCallback& callback() const { return callback_; } + ScopedTempFile TakeFile() { return std::move(file_); } + void set_file(ScopedTempFile file) { file_ = std::move(file); } private: int page_number_; - PdfToEmfConverter::GetPageCallback callback_; - ScopedTempFile emf_; + + PdfConverter::GetPageCallback callback_; + ScopedTempFile file_; DISALLOW_COPY_AND_ASSIGN(GetPageCallbackData); }; - ~PdfToEmfUtilityProcessHostClient() override; + ~PdfConverterUtilityProcessHostClient() override; - // Message handlers. + bool OnMessageReceived(const IPC::Message& message) override; + + // Helper functions: must be overridden by subclasses + // Set the process name + virtual base::string16 GetName() const; + // Create a metafileplayer subclass file from a temporary file. + virtual std::unique_ptr GetFileFromTemp( + std::unique_ptr + temp_file); + // Send the messages to Start, GetPage, and Stop. + virtual void SendStartMessage(IPC::PlatformFileForTransit transit); + virtual void SendGetPageMessage(int page_number, + IPC::PlatformFileForTransit transit); + virtual void SendStopMessage(); + + // Message handlers: void OnPageCount(int page_count); void OnPageDone(bool success, float scale_factor); + + void OnFailed(); + void OnTempPdfReady(ScopedTempFile pdf); + void OnTempFileReady(GetPageCallbackData* callback_data, + ScopedTempFile temp_file); + + // Additional message handler needed for Pdf to Emf void OnPreCacheFontCharacters(const LOGFONT& log_font, const base::string16& characters); - void OnFailed(); - void OnTempPdfReady(bool print_text_with_gdi, ScopedTempFile pdf); - void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf); - scoped_refptr temp_dir_; - // Used to suppress callbacks after PdfToEmfConverterImpl is deleted. - base::WeakPtr converter_; + // Used to suppress callbacks after PdfConverter is deleted. + base::WeakPtr converter_; PdfRenderSettings settings_; // Document loaded callback. - PdfToEmfConverter::StartCallback start_callback_; + PdfConverter::StartCallback start_callback_; // Process host for IPC. base::WeakPtr utility_process_host_; @@ -186,22 +217,37 @@ class PdfToEmfUtilityProcessHostClient // Queue of callbacks for GetPage() requests. Utility process should reply // with PageDone in the same order as requests were received. // Use containers that keeps element pointers valid after push() and pop(). - typedef std::queue GetPageCallbacks; + using GetPageCallbacks = std::queue; GetPageCallbacks get_page_callbacks_; - DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); + DISALLOW_COPY_AND_ASSIGN(PdfConverterUtilityProcessHostClient); }; -class PdfToEmfConverterImpl : public PdfToEmfConverter { - public: - PdfToEmfConverterImpl(); +std::unique_ptr +PdfConverterUtilityProcessHostClient::GetFileFromTemp( + std::unique_ptr + temp_file) { + if (settings_.mode == PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2 || + settings_.mode == PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3) { + return base::MakeUnique(temp_dir_, + std::move(temp_file)); + } + return base::MakeUnique(temp_dir_, std::move(temp_file)); +} - ~PdfToEmfConverterImpl() override; +class PdfConverterImpl : public PdfConverter { + public: + PdfConverterImpl(); + + ~PdfConverterImpl() override; + + base::WeakPtr GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } void Start(const scoped_refptr& data, const PdfRenderSettings& conversion_settings, - bool print_text_with_gdi, - const StartCallback& start_callback) override; + const StartCallback& start_callback); void GetPage(int page_number, const GetPageCallback& get_page_callback) override; @@ -209,11 +255,17 @@ class PdfToEmfConverterImpl : public PdfToEmfConverter { // Helps to cancel callbacks if this object is destroyed. void RunCallback(const base::Closure& callback); - private: - scoped_refptr utility_client_; - base::WeakPtrFactory weak_ptr_factory_; + void Start( + const scoped_refptr& utility_client, + const scoped_refptr& data, + const StartCallback& start_callback); - DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl); + private: + scoped_refptr utility_client_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PdfConverterImpl); }; ScopedTempFile CreateTempFile(scoped_refptr* temp_dir) { @@ -260,10 +312,10 @@ ScopedTempFile CreateTempPdfFile( bool LazyEmf::SafePlayback(HDC hdc) const { Emf emf; bool result = LoadEmf(&emf) && emf.SafePlayback(hdc); - // TODO(vitalybuka): Fix destruction of metafiles. For some reasons - // instances of Emf are not deleted. crbug.com/411683 + // TODO(thestig): Fix destruction of metafiles. For some reasons + // instances of Emf are not deleted. https://crbug.com/260806 // It's known that the Emf going to be played just once to a printer. So just - // release file here. + // release |file_| here. Close(); return result; } @@ -280,7 +332,7 @@ bool LazyEmf::SaveTo(base::File* file) const { void LazyEmf::Close() const { file_.reset(); - temp_dir_ = NULL; + temp_dir_ = nullptr; } bool LazyEmf::LoadEmf(Emf* emf) const { @@ -294,24 +346,55 @@ bool LazyEmf::LoadEmf(Emf* emf) const { return emf->InitFromData(data.data(), data.size()); } -PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( - base::WeakPtr converter, +PostScriptMetaFile::~PostScriptMetaFile() { +} + +bool PostScriptMetaFile::SafePlayback(HDC hdc) const { + // TODO(thestig): Fix destruction of metafiles. For some reasons + // instances of Emf are not deleted. https://crbug.com/260806 + // It's known that the Emf going to be played just once to a printer. So just + // release |file_| before returning. + Emf emf; + if (!LoadEmf(&emf)) { + Close(); + return false; + } + + { + // Ensure enumerator destruction before calling Close() below. + Emf::Enumerator emf_enum(emf, nullptr, nullptr); + for (const Emf::Record& record : emf_enum) { + auto* emf_record = record.record(); + if (emf_record->iType != EMR_GDICOMMENT) + continue; + + const EMRGDICOMMENT* comment = + reinterpret_cast(emf_record); + const char* data = reinterpret_cast(comment->Data); + const uint16_t* ptr = reinterpret_cast(data); + int ret = ExtEscape(hdc, PASSTHROUGH, 2 + *ptr, data, 0, nullptr); + DCHECK_EQ(*ptr, ret); + } + } + Close(); + return true; +} + +PdfConverterUtilityProcessHostClient::PdfConverterUtilityProcessHostClient( + base::WeakPtr converter, const PdfRenderSettings& settings) - : converter_(converter), settings_(settings) { -} + : converter_(converter), settings_(settings) {} -PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { -} +PdfConverterUtilityProcessHostClient::~PdfConverterUtilityProcessHostClient() {} -void PdfToEmfUtilityProcessHostClient::Start( +void PdfConverterUtilityProcessHostClient::Start( const scoped_refptr& data, - bool print_text_with_gdi, - const PdfToEmfConverter::StartCallback& start_callback) { + const PdfConverter::StartCallback& start_callback) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&PdfToEmfUtilityProcessHostClient::Start, this, data, - print_text_with_gdi, start_callback)); + base::Bind(&PdfConverterUtilityProcessHostClient::Start, this, data, + start_callback)); return; } @@ -324,50 +407,41 @@ void PdfToEmfUtilityProcessHostClient::Start( utility_process_host_ = content::UtilityProcessHost::Create( this, base::ThreadTaskRunnerHandle::Get()) ->AsWeakPtr(); - utility_process_host_->SetName(base::ASCIIToUTF16( - "IDS_UTILITY_PROCESS_EMF_CONVERTOR_NAME")); + utility_process_host_->SetName(GetName()); BrowserThread::PostTaskAndReplyWithResult( BrowserThread::FILE, FROM_HERE, base::Bind(&CreateTempPdfFile, data, &temp_dir_), - base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this, - print_text_with_gdi)); + base::Bind(&PdfConverterUtilityProcessHostClient::OnTempPdfReady, this)); } -void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(bool print_text_with_gdi, - ScopedTempFile pdf) { +void PdfConverterUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!utility_process_host_ || !pdf) return OnFailed(); // Should reply with OnPageCount(). - Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles( - IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false), settings_, - print_text_with_gdi)); + SendStartMessage( + IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false)); } -void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) { +void PdfConverterUtilityProcessHostClient::OnPageCount(int page_count) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (start_callback_.is_null()) return OnFailed(); - BrowserThread::PostTask(BrowserThread::UI, - FROM_HERE, - base::Bind(&PdfToEmfConverterImpl::RunCallback, - converter_, + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&PdfConverterImpl::RunCallback, converter_, base::Bind(start_callback_, page_count))); start_callback_.Reset(); } -void PdfToEmfUtilityProcessHostClient::GetPage( +void PdfConverterUtilityProcessHostClient::GetPage( int page_number, - const PdfToEmfConverter::GetPageCallback& get_page_callback) { + const PdfConverter::GetPageCallback& get_page_callback) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&PdfToEmfUtilityProcessHostClient::GetPage, - this, - page_number, - get_page_callback)); + BrowserThread::IO, FROM_HERE, + base::Bind(&PdfConverterUtilityProcessHostClient::GetPage, this, + page_number, get_page_callback)); return; } @@ -378,55 +452,84 @@ void PdfToEmfUtilityProcessHostClient::GetPage( return OnFailed(); BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&CreateTempFile, &temp_dir_), - base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempEmfReady, - this, + BrowserThread::FILE, FROM_HERE, base::Bind(&CreateTempFile, &temp_dir_), + base::Bind(&PdfConverterUtilityProcessHostClient::OnTempFileReady, this, &get_page_callbacks_.back())); } -void PdfToEmfUtilityProcessHostClient::OnTempEmfReady( +void PdfConverterUtilityProcessHostClient::OnTempFileReady( GetPageCallbackData* callback_data, - ScopedTempFile emf) { + ScopedTempFile temp_file) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!utility_process_host_ || !emf) + if (!utility_process_host_ || !temp_file) return OnFailed(); IPC::PlatformFileForTransit transit = - IPC::GetPlatformFileForTransit(emf->GetPlatformFile(), false); - callback_data->set_emf(std::move(emf)); + IPC::GetPlatformFileForTransit(temp_file->GetPlatformFile(), false); + callback_data->set_file(std::move(temp_file)); // Should reply with OnPageDone(). - Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage( - callback_data->page_number(), transit)); + SendGetPageMessage(callback_data->page_number(), transit); } -void PdfToEmfUtilityProcessHostClient::OnPageDone(bool success, - float scale_factor) { +void PdfConverterUtilityProcessHostClient::OnPageDone(bool success, + float scale_factor) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (get_page_callbacks_.empty()) return OnFailed(); GetPageCallbackData& data = get_page_callbacks_.front(); - std::unique_ptr emf; + std::unique_ptr file; if (success) { - ScopedTempFile temp_emf = data.TakeEmf(); - if (!temp_emf) // Unexpected message from utility process. + ScopedTempFile temp_file = data.TakeFile(); + if (!temp_file) // Unexpected message from utility process. return OnFailed(); - emf = base::MakeUnique(temp_dir_, std::move(temp_emf)); + file = GetFileFromTemp(std::move(temp_file)); } - BrowserThread::PostTask(BrowserThread::UI, - FROM_HERE, - base::Bind(&PdfToEmfConverterImpl::RunCallback, - converter_, - base::Bind(data.callback(), - data.page_number(), - scale_factor, - base::Passed(&emf)))); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PdfConverterImpl::RunCallback, converter_, + base::Bind(data.callback(), data.page_number(), scale_factor, + base::Passed(&file)))); get_page_callbacks_.pop(); } -void PdfToEmfUtilityProcessHostClient::OnPreCacheFontCharacters( +void PdfConverterUtilityProcessHostClient::Stop() { + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&PdfConverterUtilityProcessHostClient::Stop, this)); + return; + } + SendStopMessage(); +} + +void PdfConverterUtilityProcessHostClient::OnProcessCrashed(int exit_code) { + OnFailed(); +} + +void PdfConverterUtilityProcessHostClient::OnProcessLaunchFailed( + int exit_code) { + OnFailed(); +} + +bool PdfConverterUtilityProcessHostClient::Send(IPC::Message* msg) { + if (utility_process_host_) + return utility_process_host_->Send(msg); + delete msg; + return false; +} + +void PdfConverterUtilityProcessHostClient::OnFailed() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!start_callback_.is_null()) + OnPageCount(0); + while (!get_page_callbacks_.empty()) + OnPageDone(false, 0.0f); + utility_process_host_.reset(); +} + + +void PdfConverterUtilityProcessHostClient::OnPreCacheFontCharacters( const LOGFONT& font, const base::string16& str) { // TODO(scottmg): pdf/ppapi still require the renderer to be able to precache @@ -458,29 +561,10 @@ void PdfToEmfUtilityProcessHostClient::OnPreCacheFontCharacters( DeleteEnhMetaFile(metafile); } -void PdfToEmfUtilityProcessHostClient::Stop() { - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&PdfToEmfUtilityProcessHostClient::Stop, this)); - return; - } - Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop()); -} - -void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { - OnFailed(); -} - -void PdfToEmfUtilityProcessHostClient::OnProcessLaunchFailed(int exit_code) { - OnFailed(); -} - -bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( +bool PdfConverterUtilityProcessHostClient::OnMessageReceived( const IPC::Message& message) { bool handled = true; - IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) + IPC_BEGIN_MESSAGE_MAP(PdfConverterUtilityProcessHostClient, message) IPC_MESSAGE_HANDLER( ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount) IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, @@ -492,59 +576,69 @@ bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( return handled; } -bool PdfToEmfUtilityProcessHostClient::Send(IPC::Message* msg) { - if (utility_process_host_) - return utility_process_host_->Send(msg); - delete msg; - return false; +base::string16 PdfConverterUtilityProcessHostClient::GetName() const { + return L"ChromeUtilityProcessPDFConvertor"; } -void PdfToEmfUtilityProcessHostClient::OnFailed() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!start_callback_.is_null()) - OnPageCount(0); - while (!get_page_callbacks_.empty()) - OnPageDone(false, 0.0f); - utility_process_host_.reset(); +void PdfConverterUtilityProcessHostClient::SendGetPageMessage( + int page_number, + IPC::PlatformFileForTransit transit) { + Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage(page_number, + transit)); } -PdfToEmfConverterImpl::PdfToEmfConverterImpl() : weak_ptr_factory_(this) { +void PdfConverterUtilityProcessHostClient::SendStartMessage( + IPC::PlatformFileForTransit transit) { + Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles(transit, settings_)); } -PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { +void PdfConverterUtilityProcessHostClient::SendStopMessage() { + Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop()); +} + +// Pdf Converter Impl and subclasses +PdfConverterImpl::PdfConverterImpl() : weak_ptr_factory_(this) {} + +PdfConverterImpl::~PdfConverterImpl() { if (utility_client_.get()) utility_client_->Stop(); } -void PdfToEmfConverterImpl::Start( - const scoped_refptr& data, - const PdfRenderSettings& conversion_settings, - bool print_text_with_gdi, - const StartCallback& start_callback) { - DCHECK(!utility_client_.get()); - utility_client_ = new PdfToEmfUtilityProcessHostClient( - weak_ptr_factory_.GetWeakPtr(), conversion_settings); - utility_client_->Start(data, print_text_with_gdi, start_callback); +void PdfConverterImpl::Start( + const scoped_refptr& utility_client, + const scoped_refptr& data, + const StartCallback& start_callback) { + DCHECK(!utility_client_); + utility_client_ = utility_client; + utility_client_->Start(data, start_callback); } -void PdfToEmfConverterImpl::GetPage(int page_number, - const GetPageCallback& get_page_callback) { +void PdfConverterImpl::GetPage(int page_number, + const GetPageCallback& get_page_callback) { utility_client_->GetPage(page_number, get_page_callback); } -void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) { +void PdfConverterImpl::RunCallback(const base::Closure& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); callback.Run(); } } // namespace -PdfToEmfConverter::~PdfToEmfConverter() { -} +PdfConverter::~PdfConverter() {} // static -std::unique_ptr PdfToEmfConverter::CreateDefault() { - return std::unique_ptr(new PdfToEmfConverterImpl()); +std::unique_ptr PdfConverter::StartPdfConverter( + const scoped_refptr& data, + const PdfRenderSettings& conversion_settings, + const StartCallback& start_callback) { + std::unique_ptr converter = + base::MakeUnique(); + converter->Start( + new PdfConverterUtilityProcessHostClient(converter->GetWeakPtr(), + conversion_settings), + data, start_callback); + return std::move(converter); } } // namespace printing diff --git a/chromium_src/chrome/browser/printing/pdf_to_emf_converter.h b/chromium_src/chrome/browser/printing/pdf_to_emf_converter.h index 1f527152740..9990400a7bc 100644 --- a/chromium_src/chrome/browser/printing/pdf_to_emf_converter.h +++ b/chromium_src/chrome/browser/printing/pdf_to_emf_converter.h @@ -15,24 +15,21 @@ namespace printing { class MetafilePlayer; struct PdfRenderSettings; -class PdfToEmfConverter { +class PdfConverter { public: - typedef base::Callback StartCallback; - typedef base::Callback emf)> - GetPageCallback; - - virtual ~PdfToEmfConverter(); - - static std::unique_ptr CreateDefault(); + using StartCallback = base::Callback; + using GetPageCallback = + base::Callback file)>; + virtual ~PdfConverter(); // Starts conversion of PDF provided as |data|. Calls |start_callback| // with positive |page_count|. |page_count| is 0 if initialization failed. - virtual void Start(const scoped_refptr& data, - const PdfRenderSettings& conversion_settings, - bool print_text_with_gdi, - const StartCallback& start_callback) = 0; + static std::unique_ptr StartPdfConverter( + const scoped_refptr& data, + const PdfRenderSettings& conversion_settings, + const StartCallback& start_callback); // Requests conversion of the page. |page_number| is 0-base page number in // PDF provided in Start() call. @@ -41,7 +38,6 @@ class PdfToEmfConverter { virtual void GetPage(int page_number, const GetPageCallback& get_page_callback) = 0; }; - } // namespace printing #endif // CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_ diff --git a/chromium_src/chrome/browser/printing/print_job.cc b/chromium_src/chrome/browser/printing/print_job.cc index 70bdb900458..c5038f370de 100644 --- a/chromium_src/chrome/browser/printing/print_job.cc +++ b/chromium_src/chrome/browser/printing/print_job.cc @@ -14,6 +14,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/threading/worker_pool.h" @@ -222,27 +223,23 @@ PrintedDocument* PrintJob::document() const { } #if defined(OS_WIN) - -class PrintJob::PdfToEmfState { +class PrintJob::PdfConversionState { public: - PdfToEmfState(const gfx::Size& page_size, const gfx::Rect& content_area) + PdfConversionState(gfx::Size page_size, gfx::Rect content_area) : page_count_(0), current_page_(0), pages_in_progress_(0), page_size_(page_size), - content_area_(content_area), - converter_(PdfToEmfConverter::CreateDefault()) {} + content_area_(content_area) {} void Start(const scoped_refptr& data, const PdfRenderSettings& conversion_settings, - bool print_text_with_gdi, - const PdfToEmfConverter::StartCallback& start_callback) { - converter_->Start(data, conversion_settings, print_text_with_gdi, - start_callback); + const PdfConverter::StartCallback& start_callback) { + converter_ = PdfConverter::StartPdfConverter( + data, conversion_settings, start_callback); } - void GetMorePages( - const PdfToEmfConverter::GetPageCallback& get_page_callback) { + void GetMorePages(const PdfConverter::GetPageCallback& get_page_callback) { const int kMaxNumberOfTempFilesPerDocument = 3; while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument && current_page_ < page_count_) { @@ -251,8 +248,7 @@ class PrintJob::PdfToEmfState { } } - void OnPageProcessed( - const PdfToEmfConverter::GetPageCallback& get_page_callback) { + void OnPageProcessed(const PdfConverter::GetPageCallback& get_page_callback) { --pages_in_progress_; GetMorePages(get_page_callback); // Release converter if we don't need this any more. @@ -270,7 +266,7 @@ class PrintJob::PdfToEmfState { int pages_in_progress_; gfx::Size page_size_; gfx::Rect content_area_; - std::unique_ptr converter_; + std::unique_ptr converter_; }; void PrintJob::AppendPrintedPage(int page_number) { @@ -282,46 +278,67 @@ void PrintJob::StartPdfToEmfConversion( const gfx::Size& page_size, const gfx::Rect& content_area, bool print_text_with_gdi) { - DCHECK(!pdf_to_emf_state_); - pdf_to_emf_state_ = base::MakeUnique(page_size, content_area); + DCHECK(!pdf_conversion_state_); + pdf_conversion_state_ = + base::MakeUnique(page_size, content_area); const int kPrinterDpi = settings().dpi(); - pdf_to_emf_state_->Start( - bytes, PdfRenderSettings(content_area, kPrinterDpi, true), - print_text_with_gdi, base::Bind(&PrintJob::OnPdfToEmfStarted, this)); + PdfRenderSettings settings( + content_area, gfx::Point(0, 0), kPrinterDpi, /*autorotate=*/true, + print_text_with_gdi ? PdfRenderSettings::Mode::GDI_TEXT + : PdfRenderSettings::Mode::NORMAL); + pdf_conversion_state_->Start( + bytes, settings, base::Bind(&PrintJob::OnPdfConversionStarted, this)); } -void PrintJob::OnPdfToEmfStarted(int page_count) { +void PrintJob::OnPdfConversionStarted(int page_count) { if (page_count <= 0) { - pdf_to_emf_state_.reset(); + pdf_conversion_state_.reset(); Cancel(); return; } - pdf_to_emf_state_->set_page_count(page_count); - pdf_to_emf_state_->GetMorePages( - base::Bind(&PrintJob::OnPdfToEmfPageConverted, this)); + pdf_conversion_state_->set_page_count(page_count); + pdf_conversion_state_->GetMorePages( + base::Bind(&PrintJob::OnPdfPageConverted, this)); } -void PrintJob::OnPdfToEmfPageConverted(int page_number, - float scale_factor, - std::unique_ptr emf) { - DCHECK(pdf_to_emf_state_); - if (!document_.get() || !emf || page_number < 0 || +void PrintJob::OnPdfPageConverted(int page_number, + float scale_factor, + std::unique_ptr metafile) { + DCHECK(pdf_conversion_state_); + if (!document_.get() || !metafile || page_number < 0 || static_cast(page_number) >= pdf_page_mapping_.size()) { - pdf_to_emf_state_.reset(); + pdf_conversion_state_.reset(); Cancel(); return; } // Update the rendered document. It will send notifications to the listener. - document_->SetPage(pdf_page_mapping_[page_number], std::move(emf), - scale_factor, pdf_to_emf_state_->page_size(), - pdf_to_emf_state_->content_area()); + document_->SetPage(pdf_page_mapping_[page_number], std::move(metafile), + scale_factor, pdf_conversion_state_->page_size(), + pdf_conversion_state_->content_area()); - pdf_to_emf_state_->GetMorePages( - base::Bind(&PrintJob::OnPdfToEmfPageConverted, this)); + pdf_conversion_state_->GetMorePages( + base::Bind(&PrintJob::OnPdfPageConverted, this)); } -#endif // OS_WIN +void PrintJob::StartPdfToPostScriptConversion( + const scoped_refptr& bytes, + const gfx::Rect& content_area, + const gfx::Point& physical_offsets, + bool ps_level2) { + DCHECK(!pdf_conversion_state_); + pdf_conversion_state_ = base::MakeUnique( + gfx::Size(), gfx::Rect()); + const int kPrinterDpi = settings().dpi(); + PdfRenderSettings settings( + content_area, physical_offsets, kPrinterDpi, true /* autorotate? */, + ps_level2 ? PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2 + : PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3); + pdf_conversion_state_->Start( + bytes, settings, base::Bind(&PrintJob::OnPdfConversionStarted, this)); +} + +#endif // defined(OS_WIN) void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) { if (document_.get() == new_document) @@ -372,8 +389,10 @@ void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) { } case JobEventDetails::PAGE_DONE: #if defined(OS_WIN) - pdf_to_emf_state_->OnPageProcessed( - base::Bind(&PrintJob::OnPdfToEmfPageConverted, this)); + if (pdf_conversion_state_) { + pdf_conversion_state_->OnPageProcessed( + base::Bind(&PrintJob::OnPdfPageConverted, this)); + } #endif // defined(OS_WIN) break; default: { diff --git a/chromium_src/chrome/browser/printing/print_job.h b/chromium_src/chrome/browser/printing/print_job.h index e5c77aeb234..bca518f6fcd 100644 --- a/chromium_src/chrome/browser/printing/print_job.h +++ b/chromium_src/chrome/browser/printing/print_job.h @@ -100,6 +100,12 @@ class PrintJob : public PrintJobWorkerOwner, const gfx::Size& page_size, const gfx::Rect& content_area, bool print_text_with_gdi); + + void StartPdfToPostScriptConversion( + const scoped_refptr& bytes, + const gfx::Rect& content_area, + const gfx::Point& physical_offset, + bool ps_level2); #endif // defined(OS_WIN) protected: @@ -126,10 +132,10 @@ class PrintJob : public PrintJobWorkerOwner, void HoldUntilStopIsCalled(); #if defined(OS_WIN) - void OnPdfToEmfStarted(int page_count); - void OnPdfToEmfPageConverted(int page_number, - float scale_factor, - std::unique_ptr emf); + void OnPdfConversionStarted(int page_count); + void OnPdfPageConverted(int page_number, + float scale_factor, + std::unique_ptr emf); #endif // defined(OS_WIN) content::NotificationRegistrar registrar_; @@ -157,8 +163,8 @@ class PrintJob : public PrintJobWorkerOwner, bool is_canceling_; #if defined(OS_WIN) - class PdfToEmfState; - std::unique_ptr pdf_to_emf_state_; + class PdfConversionState; + std::unique_ptr pdf_conversion_state_; std::vector pdf_page_mapping_; #endif // defined(OS_WIN) diff --git a/chromium_src/chrome/browser/process_singleton_posix.cc b/chromium_src/chrome/browser/process_singleton_posix.cc index 7d5677031f4..e1e25cf91de 100644 --- a/chromium_src/chrome/browser/process_singleton_posix.cc +++ b/chromium_src/chrome/browser/process_singleton_posix.cc @@ -49,25 +49,34 @@ #include #include +#include #include #include +#include + #include "atom/common/atom_command_line.h" + #include "base/base_paths.h" #include "base/bind.h" #include "base/command_line.h" +#include "base/files/file_descriptor_watcher_posix.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/location.h" #include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" +#include "base/metrics/histogram_macros.h" #include "base/path_service.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/safe_strerror.h" #include "base/rand_util.h" #include "base/sequenced_task_runner_helpers.h" #include "base/single_thread_task_runner.h" -#include "base/stl_util.h" +#include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -78,6 +87,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/timer/timer.h" +#include "build/build_config.h" #include "content/public/browser/browser_thread.h" #include "net/base/network_interfaces.h" #include "ui/base/l10n/l10n_util.h" @@ -222,9 +232,8 @@ int SetupSocketOnly() { int sock = socket(PF_UNIX, SOCK_STREAM, 0); PCHECK(sock >= 0) << "socket() failed"; - int rv = base::SetNonBlocking(sock); - DCHECK_EQ(0, rv) << "Failed to make non-blocking socket."; - rv = SetCloseOnExec(sock); + DCHECK(base::SetNonBlocking(sock)) << "Failed to make non-blocking socket."; + int rv = SetCloseOnExec(sock); DCHECK_EQ(0, rv) << "Failed to set CLOEXEC on socket."; return sock; @@ -305,7 +314,6 @@ bool ParseLockPath(const base::FilePath& path, bool DisplayProfileInUseError(const base::FilePath& lock_path, const std::string& hostname, int pid) { - // TODO: yolo return true; } @@ -455,44 +463,38 @@ bool ReplaceOldSingletonLock(const base::FilePath& symlink_content, // This class sets up a listener on the singleton socket and handles parsing // messages that come in on the singleton socket. class ProcessSingleton::LinuxWatcher - : public base::MessageLoopForIO::Watcher, - public base::MessageLoop::DestructionObserver, - public base::RefCountedThreadSafe { public: // A helper class to read message from an established socket. - class SocketReader : public base::MessageLoopForIO::Watcher { + class SocketReader { public: SocketReader(ProcessSingleton::LinuxWatcher* parent, - base::MessageLoop* ui_message_loop, + scoped_refptr ui_task_runner, int fd) : parent_(parent), - ui_message_loop_(ui_message_loop), + ui_task_runner_(ui_task_runner), fd_(fd), bytes_read_(0) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // Wait for reads. - base::MessageLoopForIO::current()->WatchFileDescriptor( - fd, true, base::MessageLoopForIO::WATCH_READ, &fd_reader_, this); + fd_watch_controller_ = base::FileDescriptorWatcher::WatchReadable( + fd, base::Bind(&SocketReader::OnSocketCanReadWithoutBlocking, + base::Unretained(this))); // If we haven't completed in a reasonable amount of time, give up. timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeoutInSeconds), this, &SocketReader::CleanupAndDeleteSelf); } - ~SocketReader() override { CloseSocket(fd_); } - - // MessageLoopForIO::Watcher impl. - void OnFileCanReadWithoutBlocking(int fd) override; - void OnFileCanWriteWithoutBlocking(int fd) override { - // SocketReader only watches for accept (read) events. - NOTREACHED(); - } + ~SocketReader() { CloseSocket(fd_); } // Finish handling the incoming message by optionally sending back an ACK // message and removing this SocketReader. void FinishWithACK(const char *message, size_t length); private: + void OnSocketCanReadWithoutBlocking(); + void CleanupAndDeleteSelf() { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -500,13 +502,15 @@ class ProcessSingleton::LinuxWatcher // We're deleted beyond this point. } - base::MessageLoopForIO::FileDescriptorWatcher fd_reader_; + // Controls watching |fd_|. + std::unique_ptr + fd_watch_controller_; // The ProcessSingleton::LinuxWatcher that owns us. ProcessSingleton::LinuxWatcher* const parent_; - // A reference to the UI message loop. - base::MessageLoop* const ui_message_loop_; + // A reference to the UI task runner. + scoped_refptr ui_task_runner_; // The file descriptor we're reading. const int fd_; @@ -525,9 +529,7 @@ class ProcessSingleton::LinuxWatcher // We expect to only be constructed on the UI thread. explicit LinuxWatcher(ProcessSingleton* parent) - : ui_message_loop_(base::MessageLoop::current()), - parent_(parent) { - } + : ui_task_runner_(base::ThreadTaskRunnerHandle::Get()), parent_(parent) {} // Start listening for connections on the socket. This method should be // called from the IO thread. @@ -540,79 +542,63 @@ class ProcessSingleton::LinuxWatcher const std::vector& argv, SocketReader* reader); - // MessageLoopForIO::Watcher impl. These run on the IO thread. - void OnFileCanReadWithoutBlocking(int fd) override; - void OnFileCanWriteWithoutBlocking(int fd) override { - // ProcessSingleton only watches for accept (read) events. - NOTREACHED(); - } - - // MessageLoop::DestructionObserver - void WillDestroyCurrentMessageLoop() override { - fd_watcher_.StopWatchingFileDescriptor(); - } - private: friend struct BrowserThread::DeleteOnThread; friend class base::DeleteHelper; - ~LinuxWatcher() override { + ~LinuxWatcher() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - STLDeleteElements(&readers_); - - base::MessageLoopForIO* ml = base::MessageLoopForIO::current(); - ml->RemoveDestructionObserver(this); } + void OnSocketCanReadWithoutBlocking(int socket); + // Removes and deletes the SocketReader. void RemoveSocketReader(SocketReader* reader); - base::MessageLoopForIO::FileDescriptorWatcher fd_watcher_; + std::unique_ptr socket_watcher_; // A reference to the UI message loop (i.e., the message loop we were // constructed on). - base::MessageLoop* ui_message_loop_; + scoped_refptr ui_task_runner_; // The ProcessSingleton that owns us. ProcessSingleton* const parent_; - std::set readers_; + std::set> readers_; DISALLOW_COPY_AND_ASSIGN(LinuxWatcher); }; -void ProcessSingleton::LinuxWatcher::OnFileCanReadWithoutBlocking(int fd) { +void ProcessSingleton::LinuxWatcher::OnSocketCanReadWithoutBlocking( + int socket) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // Accepting incoming client. sockaddr_un from; socklen_t from_len = sizeof(from); - int connection_socket = HANDLE_EINTR(accept( - fd, reinterpret_cast(&from), &from_len)); + int connection_socket = HANDLE_EINTR( + accept(socket, reinterpret_cast(&from), &from_len)); if (-1 == connection_socket) { PLOG(ERROR) << "accept() failed"; return; } - int rv = base::SetNonBlocking(connection_socket); - DCHECK_EQ(0, rv) << "Failed to make non-blocking socket."; - SocketReader* reader = new SocketReader(this, - ui_message_loop_, - connection_socket); - readers_.insert(reader); + DCHECK(base::SetNonBlocking(connection_socket)) + << "Failed to make non-blocking socket."; + readers_.insert( + base::MakeUnique(this, ui_task_runner_, connection_socket)); } void ProcessSingleton::LinuxWatcher::StartListening(int socket) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // Watch for client connections on this socket. - base::MessageLoopForIO* ml = base::MessageLoopForIO::current(); - ml->AddDestructionObserver(this); - ml->WatchFileDescriptor(socket, true, base::MessageLoopForIO::WATCH_READ, - &fd_watcher_, this); + socket_watcher_ = base::FileDescriptorWatcher::WatchReadable( + socket, base::Bind(&LinuxWatcher::OnSocketCanReadWithoutBlocking, + base::Unretained(this), socket)); } void ProcessSingleton::LinuxWatcher::HandleMessage( const std::string& current_dir, const std::vector& argv, SocketReader* reader) { - DCHECK(ui_message_loop_ == base::MessageLoop::current()); + DCHECK(ui_task_runner_->BelongsToCurrentThread()); DCHECK(reader); if (parent_->notification_callback_.Run(argv, @@ -632,25 +618,27 @@ void ProcessSingleton::LinuxWatcher::HandleMessage( void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(reader); - readers_.erase(reader); - delete reader; + auto it = std::find_if(readers_.begin(), readers_.end(), + [reader](const std::unique_ptr& ptr) { + return ptr.get() == reader; + }); + readers_.erase(it); } /////////////////////////////////////////////////////////////////////////////// // ProcessSingleton::LinuxWatcher::SocketReader // -void ProcessSingleton::LinuxWatcher::SocketReader::OnFileCanReadWithoutBlocking( - int fd) { +void ProcessSingleton::LinuxWatcher::SocketReader:: + OnSocketCanReadWithoutBlocking() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK_EQ(fd, fd_); while (bytes_read_ < sizeof(buf_)) { - ssize_t rv = HANDLE_EINTR( - read(fd, buf_ + bytes_read_, sizeof(buf_) - bytes_read_)); + ssize_t rv = + HANDLE_EINTR(read(fd_, buf_ + bytes_read_, sizeof(buf_) - bytes_read_)); if (rv < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { PLOG(ERROR) << "read() failed"; - CloseSocket(fd); + CloseSocket(fd_); return; } else { // It would block, so we just return and continue to watch for the next @@ -696,10 +684,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::OnFileCanReadWithoutBlocking( tokens.erase(tokens.begin()); // Return to the UI thread to handle opening a new browser tab. - ui_message_loop_->task_runner()->PostTask( + ui_task_runner_->PostTask( FROM_HERE, base::Bind(&ProcessSingleton::LinuxWatcher::HandleMessage, parent_, current_dir, tokens, this)); - fd_reader_.StopWatchingFileDescriptor(); + fd_watch_controller_.reset(); // LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader // object by invoking SocketReader::FinishWithACK(). @@ -731,7 +719,8 @@ ProcessSingleton::ProcessSingleton( const base::FilePath& user_data_dir, const NotificationCallback& notification_callback) : notification_callback_(notification_callback), - current_pid_(base::GetCurrentProcId()) { + current_pid_(base::GetCurrentProcId()), + watcher_(new LinuxWatcher(this)) { // The user_data_dir may have not been created yet. base::CreateDirectoryAndGetError(user_data_dir, nullptr); @@ -897,12 +886,26 @@ ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate( const base::CommandLine& command_line, int retry_attempts, const base::TimeDelta& timeout) { + const base::TimeTicks begin_ticks = base::TimeTicks::Now(); NotifyResult result = NotifyOtherProcessWithTimeout( command_line, retry_attempts, timeout, true); - if (result != PROCESS_NONE) + if (result != PROCESS_NONE) { + if (result == PROCESS_NOTIFIED) { + UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToNotify", + base::TimeTicks::Now() - begin_ticks); + } else { + UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToFailure", + base::TimeTicks::Now() - begin_ticks); + } return result; - if (Create()) + } + + if (Create()) { + UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToCreate", + base::TimeTicks::Now() - begin_ticks); return PROCESS_NONE; + } + // If the Create() failed, try again to notify. (It could be that another // instance was starting at the same time and managed to grab the lock before // we did.) @@ -910,6 +913,15 @@ ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate( // aren't going to try to take over the lock ourselves. result = NotifyOtherProcessWithTimeout( command_line, retry_attempts, timeout, false); + + if (result == PROCESS_NOTIFIED) { + UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToNotify", + base::TimeTicks::Now() - begin_ticks); + } else { + UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToFailure", + base::TimeTicks::Now() - begin_ticks); + } + if (result != PROCESS_NONE) return result; @@ -1019,15 +1031,13 @@ bool ProcessSingleton::Create() { if (listen(sock, 5) < 0) NOTREACHED() << "listen failed: " << base::safe_strerror(errno); - // In Electron the ProcessSingleton is created earlier than the IO - // thread gets created, so we have to postpone the call until message - // loop is up an running. - scoped_refptr task_runner = - base::ThreadTaskRunnerHandle::Get(); - task_runner->PostTask( + DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, - base::Bind(&ProcessSingleton::StartListening, - base::Unretained(this), sock)); + base::Bind(&ProcessSingleton::LinuxWatcher::StartListening, + watcher_, + sock)); return true; } @@ -1038,17 +1048,6 @@ void ProcessSingleton::Cleanup() { UnlinkPath(lock_path_); } -void ProcessSingleton::StartListening(int sock) { - watcher_ = new LinuxWatcher(this); - DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO)); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&ProcessSingleton::LinuxWatcher::StartListening, - watcher_.get(), - sock)); -} - bool ProcessSingleton::IsSameChromeInstance(pid_t pid) { pid_t cur_pid = current_pid_; while (pid != cur_pid) { diff --git a/chromium_src/chrome/common/chrome_utility_printing_messages.h b/chromium_src/chrome/common/chrome_utility_printing_messages.h new file mode 100644 index 00000000000..fc71f0bd05f --- /dev/null +++ b/chromium_src/chrome/common/chrome_utility_printing_messages.h @@ -0,0 +1,111 @@ +// 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. + +// Multiply-included message file, so no include guard. + +#include +#include + +#include "base/strings/string16.h" +#include "build/build_config.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_param_traits.h" +#include "ipc/ipc_platform_file.h" +#include "printing/backend/print_backend.h" +#include "printing/features/features.h" +#include "printing/page_range.h" +#include "printing/pdf_render_settings.h" +#include "printing/pwg_raster_settings.h" + +#if defined(OS_WIN) +#include +#endif + +#define IPC_MESSAGE_START ChromeUtilityPrintingMsgStart + +IPC_ENUM_TRAITS_MAX_VALUE(printing::PdfRenderSettings::Mode, + printing::PdfRenderSettings::Mode::LAST) + +IPC_STRUCT_TRAITS_BEGIN(printing::PdfRenderSettings) + IPC_STRUCT_TRAITS_MEMBER(area) + IPC_STRUCT_TRAITS_MEMBER(offsets) + IPC_STRUCT_TRAITS_MEMBER(dpi) + IPC_STRUCT_TRAITS_MEMBER(autorotate) + IPC_STRUCT_TRAITS_MEMBER(mode) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(printing::PrinterCapsAndDefaults) + IPC_STRUCT_TRAITS_MEMBER(printer_capabilities) + IPC_STRUCT_TRAITS_MEMBER(caps_mime_type) + IPC_STRUCT_TRAITS_MEMBER(printer_defaults) + IPC_STRUCT_TRAITS_MEMBER(defaults_mime_type) +IPC_STRUCT_TRAITS_END() + +IPC_ENUM_TRAITS_MAX_VALUE(printing::ColorModel, printing::PROCESSCOLORMODEL_RGB) + +IPC_STRUCT_TRAITS_BEGIN(printing::PrinterSemanticCapsAndDefaults::Paper) + IPC_STRUCT_TRAITS_MEMBER(display_name) + IPC_STRUCT_TRAITS_MEMBER(vendor_id) + IPC_STRUCT_TRAITS_MEMBER(size_um) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(printing::PrinterSemanticCapsAndDefaults) + IPC_STRUCT_TRAITS_MEMBER(collate_capable) + IPC_STRUCT_TRAITS_MEMBER(collate_default) + IPC_STRUCT_TRAITS_MEMBER(copies_capable) + IPC_STRUCT_TRAITS_MEMBER(duplex_capable) + IPC_STRUCT_TRAITS_MEMBER(duplex_default) + IPC_STRUCT_TRAITS_MEMBER(color_changeable) + IPC_STRUCT_TRAITS_MEMBER(color_default) + IPC_STRUCT_TRAITS_MEMBER(color_model) + IPC_STRUCT_TRAITS_MEMBER(bw_model) + IPC_STRUCT_TRAITS_MEMBER(papers) + IPC_STRUCT_TRAITS_MEMBER(default_paper) + IPC_STRUCT_TRAITS_MEMBER(dpis) + IPC_STRUCT_TRAITS_MEMBER(default_dpi) +IPC_STRUCT_TRAITS_END() + +IPC_ENUM_TRAITS_MAX_VALUE(printing::PwgRasterTransformType, + printing::TRANSFORM_TYPE_LAST) + +IPC_STRUCT_TRAITS_BEGIN(printing::PwgRasterSettings) + IPC_STRUCT_TRAITS_MEMBER(odd_page_transform) + IPC_STRUCT_TRAITS_MEMBER(rotate_all_pages) + IPC_STRUCT_TRAITS_MEMBER(reverse_page_order) +IPC_STRUCT_TRAITS_END() + +#if defined(OS_WIN) +// Reply when the utility process loaded PDF. |page_count| is 0, if loading +// failed. +IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, + int /* page_count */) + +// Reply when the utility process rendered the PDF page. +IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, + bool /* success */, + float /* scale_factor */) + +// Request that the given font characters be loaded by the browser so it's +// cached by the OS. Please see +// PdfToEmfUtilityProcessHostClient::OnPreCacheFontCharacters for details. +IPC_SYNC_MESSAGE_CONTROL2_0(ChromeUtilityHostMsg_PreCacheFontCharacters, + LOGFONT /* font_data */, + base::string16 /* characters */) + +// Tell the utility process to start rendering the given PDF into a metafile. +// Utility process would be alive until +// ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop message. +IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_RenderPDFPagesToMetafiles, + IPC::PlatformFileForTransit /* input_file */, + printing::PdfRenderSettings /* settings */) + +// Requests conversion of the next page. +IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage, + int /* page_number */, + IPC::PlatformFileForTransit /* output_file */) + +// Requests utility process to stop conversion and exit. +IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop) + +#endif // OS_WIN diff --git a/chromium_src/chrome/common/print_messages.h b/chromium_src/chrome/common/print_messages.h index 219278d401f..2a88b0c3b76 100644 --- a/chromium_src/chrome/common/print_messages.h +++ b/chromium_src/chrome/common/print_messages.h @@ -75,6 +75,9 @@ struct PrintMsg_PrintPages_Params { IPC_ENUM_TRAITS_MAX_VALUE(printing::MarginType, printing::MARGIN_TYPE_LAST) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(printing::DuplexMode, + printing::UNKNOWN_DUPLEX_MODE, + printing::SHORT_EDGE) IPC_ENUM_TRAITS_MAX_VALUE(blink::WebPrintScalingOption, blink::WebPrintScalingOptionLast) @@ -317,39 +320,3 @@ IPC_MESSAGE_ROUTED1(PrintHostMsg_MetafileReadyForPrinting, IPC_MESSAGE_ROUTED2(PrintHostMsg_PrintPreviewFailed, int /* document cookie */, int /* request_id */); - -#if defined(OS_WIN) -// Tell the utility process to start rendering the given PDF into a metafile. -// Utility process would be alive until -// ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop message. -IPC_MESSAGE_CONTROL3(ChromeUtilityMsg_RenderPDFPagesToMetafiles, - IPC::PlatformFileForTransit /* input_file */, - printing::PdfRenderSettings /* settings */, - bool /* print_text_with_gdi */) - -// Requests conversion of the next page. -IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage, - int /* page_number */, - IPC::PlatformFileForTransit /* output_file */) - -// Requests utility process to stop conversion and exit. -IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop) - -// Reply when the utility process loaded PDF. |page_count| is 0, if loading -// failed. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, - int /* page_count */) - -// Reply when the utility process rendered the PDF page. -IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, - bool /* success */, - float /* scale_factor */) - -// Request that the given font characters be loaded by the browser so it's -// cached by the OS. Please see -// PdfToEmfUtilityProcessHostClient::OnPreCacheFontCharacters for details. -IPC_SYNC_MESSAGE_CONTROL2_0(ChromeUtilityHostMsg_PreCacheFontCharacters, - LOGFONT /* font_data */, - base::string16 /* characters */) - -#endif diff --git a/chromium_src/chrome/renderer/media/chrome_key_systems.cc b/chromium_src/chrome/renderer/media/chrome_key_systems.cc index 206ac400fba..87b5c43e7fb 100644 --- a/chromium_src/chrome/renderer/media/chrome_key_systems.cc +++ b/chromium_src/chrome/renderer/media/chrome_key_systems.cc @@ -66,11 +66,11 @@ class ExternalClearKeyProperties : public KeySystemProperties { return true; case media::EmeInitDataType::CENC: -#if defined(USE_PROPRIETARY_CODECS) +#if BUILDFLAG(USE_PROPRIETARY_CODECS) return true; #else return false; -#endif // defined(USE_PROPRIETARY_CODECS) +#endif // BUILDFLAG(USE_PROPRIETARY_CODECS) case media::EmeInitDataType::UNKNOWN: return false; @@ -80,7 +80,7 @@ class ExternalClearKeyProperties : public KeySystemProperties { } SupportedCodecs GetSupportedCodecs() const override { -#if defined(USE_PROPRIETARY_CODECS) +#if BUILDFLAG(USE_PROPRIETARY_CODECS) return media::EME_CODEC_MP4_ALL | media::EME_CODEC_WEBM_ALL; #else return media::EME_CODEC_WEBM_ALL; @@ -224,21 +224,21 @@ static void AddPepperBasedWidevine( // as those may offer a higher level of protection. supported_codecs |= media::EME_CODEC_WEBM_OPUS; supported_codecs |= media::EME_CODEC_WEBM_VORBIS; -#if defined(USE_PROPRIETARY_CODECS) +#if BUILDFLAG(USE_PROPRIETARY_CODECS) supported_codecs |= media::EME_CODEC_MP4_AAC; -#endif // defined(USE_PROPRIETARY_CODECS) +#endif // BUILDFLAG(USE_PROPRIETARY_CODECS) for (size_t i = 0; i < codecs.size(); ++i) { if (codecs[i] == kCdmSupportedCodecVp8) supported_codecs |= media::EME_CODEC_WEBM_VP8; if (codecs[i] == kCdmSupportedCodecVp9) supported_codecs |= media::EME_CODEC_WEBM_VP9; -#if defined(USE_PROPRIETARY_CODECS) +#if BUILDFLAG(USE_PROPRIETARY_CODECS) if (codecs[i] == kCdmSupportedCodecAvc1) supported_codecs |= media::EME_CODEC_MP4_AVC1; if (codecs[i] == kCdmSupportedCodecVp9) supported_codecs |= media::EME_CODEC_MP4_VP9; -#endif // defined(USE_PROPRIETARY_CODECS) +#endif // BUILDFLAG(USE_PROPRIETARY_CODECS) } using Robustness = cdm::WidevineKeySystemProperties::Robustness; diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc b/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc index c1097093335..450f9f89398 100644 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc +++ b/chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc @@ -15,7 +15,8 @@ #include "ppapi/proxy/serialized_structs.h" #if defined(OS_LINUX) || defined(OS_OPENBSD) -#include "content/public/common/child_process_sandbox_support_linux.h" +#include "content/public/child/child_process_sandbox_support_linux.h" +#include "content/public/common/common_sandbox_support_linux.h" #elif defined(OS_WIN) #include "third_party/skia/include/ports/SkFontMgr.h" #endif diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc b/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc index 5e41c5b8a65..09e5b46df6f 100644 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc +++ b/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc @@ -8,7 +8,8 @@ #include #include "base/lazy_instance.h" -#include "base/metrics/histogram.h" +#include "base/macros.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" #include "content/public/renderer/pepper_plugin_instance.h" #include "content/public/renderer/render_thread.h" diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper.cc index 088ba650da4..939dc13711d 100644 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper.cc +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper.cc @@ -12,7 +12,7 @@ #include "base/json/json_writer.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" -#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" #include "base/process/process_handle.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" @@ -32,6 +32,7 @@ #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebFrameClient.h" +#include "third_party/WebKit/public/web/WebFrameWidget.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebPlugin.h" #include "third_party/WebKit/public/web/WebPluginDocument.h" @@ -484,12 +485,9 @@ void PrepareFrameAndViewForPrint::ResizeForPrinting() { // Backup size and offset if it's a local frame. blink::WebView* web_view = frame_.view(); - // Backup size and offset. - if (blink::WebFrame* web_frame = web_view->mainFrame()) - prev_scroll_offset_ = web_frame->scrollOffset(); if (blink::WebFrame* web_frame = web_view->mainFrame()) { if (web_frame->isWebLocalFrame()) - prev_scroll_offset_ = web_frame->scrollOffset(); + prev_scroll_offset_ = web_frame->getScrollOffset(); } prev_view_size_ = web_view->size(); @@ -535,8 +533,10 @@ void PrepareFrameAndViewForPrint::CopySelection( blink::WebView::create(this, blink::WebPageVisibilityStateVisible); owns_web_view_ = true; content::RenderView::ApplyWebPreferences(prefs, web_view); - web_view->setMainFrame( - blink::WebLocalFrame::create(blink::WebTreeScopeType::Document, this)); + blink::WebLocalFrame* main_frame = blink::WebLocalFrame::create( + blink::WebTreeScopeType::Document, this, nullptr, nullptr); + web_view->setMainFrame(main_frame); + blink::WebFrameWidget::create(this, web_view, main_frame); frame_.Reset(web_view->mainFrame()->toWebLocalFrame()); node_to_print_.reset(); @@ -565,7 +565,8 @@ blink::WebLocalFrame* PrepareFrameAndViewForPrint::createChildFrame( const blink::WebString& unique_name, blink::WebSandboxFlags sandbox_flags, const blink::WebFrameOwnerProperties& frame_owner_properties) { - blink::WebLocalFrame* frame = blink::WebLocalFrame::create(scope, this); + blink::WebLocalFrame* frame = blink::WebLocalFrame::create( + scope, this, nullptr, nullptr); parent->appendChild(frame); return frame; } diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc index 197baedc2f1..4bc37f4da97 100644 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc @@ -126,12 +126,12 @@ void PrintWebViewHelper::PrintPageInternal( &content_area); gfx::Rect canvas_area = content_area; - SkCanvas* canvas = metafile->GetVectorCanvasForNewPage( - page_size, canvas_area, scale_factor); + cc::PaintCanvas* canvas = + metafile->GetVectorCanvasForNewPage(page_size, canvas_area, scale_factor); if (!canvas) return; - MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); + MetafileSkiaWrapper::SetMetafileOnCanvas(canvas, metafile); RenderPageContent(frame, params.page_number, canvas_area, content_area, scale_factor, canvas); diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm b/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm index 341b89717be..6332df49af5 100644 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm @@ -14,7 +14,6 @@ #include "printing/page_size_margins.h" #include "third_party/WebKit/public/platform/WebCanvas.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" -#include "third_party/skia/include/core/SkCanvas.h" namespace printing { @@ -112,13 +111,13 @@ void PrintWebViewHelper::RenderPage(const PrintMsg_Print_Params& params, gfx::Rect canvas_area = content_area; { - SkCanvas* canvas = metafile->GetVectorCanvasForNewPage( + cc::PaintCanvas* canvas = metafile->GetVectorCanvasForNewPage( *page_size, canvas_area, scale_factor); if (!canvas) return; - MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); - skia::SetIsPreviewMetafile(*canvas, is_preview); + MetafileSkiaWrapper::SetMetafileOnCanvas(canvas, metafile); + cc::SetIsPreviewMetafile(canvas, is_preview); RenderPageContent(frame, page_number, canvas_area, content_area, scale_factor, static_cast(canvas)); } diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc index e96a2b8339a..abac5543003 100644 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc @@ -14,7 +14,6 @@ #include "printing/page_size_margins.h" #include "printing/pdf_metafile_skia.h" #include "printing/units.h" -#include "skia/ext/platform_device.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" @@ -161,12 +160,12 @@ void PrintWebViewHelper::PrintPageInternal( frame->getPrintPageShrink(params.page_number); float scale_factor = css_scale_factor * webkit_page_shrink_factor; - SkCanvas* canvas = metafile->GetVectorCanvasForNewPage( - page_size, canvas_area, scale_factor); + cc::PaintCanvas* canvas = + metafile->GetVectorCanvasForNewPage(page_size, canvas_area, scale_factor); if (!canvas) return; - MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile); + MetafileSkiaWrapper::SetMetafileOnCanvas(canvas, metafile); #if 0 if (params.params.display_header_footer) { diff --git a/chromium_src/chrome/utility/printing_handler_win.cc b/chromium_src/chrome/utility/printing_handler_win.cc index 4265991272a..cd01a09601a 100644 --- a/chromium_src/chrome/utility/printing_handler_win.cc +++ b/chromium_src/chrome/utility/printing_handler_win.cc @@ -8,6 +8,7 @@ #include "base/lazy_instance.h" #include "base/path_service.h" #include "base/scoped_native_library.h" +#include "chrome/common/chrome_utility_printing_messages.h" #include "chrome/common/print_messages.h" #include "content/public/utility/utility_thread.h" #include "pdf/pdf.h" @@ -59,13 +60,25 @@ bool PrintingHandlerWin::OnMessageReceived(const IPC::Message& message) { void PrintingHandlerWin::OnRenderPDFPagesToMetafile( IPC::PlatformFileForTransit pdf_transit, - const PdfRenderSettings& settings, - bool print_text_with_gdi) { + const PdfRenderSettings& settings) { pdf_rendering_settings_ = settings; - chrome_pdf::SetPDFUseGDIPrinting(print_text_with_gdi); + chrome_pdf::SetPDFUseGDIPrinting(pdf_rendering_settings_.mode == + PdfRenderSettings::Mode::GDI_TEXT); + int postscript_level; + switch (pdf_rendering_settings_.mode) { + case PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2: + postscript_level = 2; + break; + case PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3: + postscript_level = 3; + break; + default: + postscript_level = 0; // Not using postscript. + } + chrome_pdf::SetPDFPostscriptPrintingLevel(postscript_level); + base::File pdf_file = IPC::PlatformFileForTransitToFile(pdf_transit); int page_count = LoadPDF(std::move(pdf_file)); - //int page_count = 1; Send( new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount(page_count)); } @@ -75,8 +88,12 @@ void PrintingHandlerWin::OnRenderPDFPagesToMetafileGetPage( IPC::PlatformFileForTransit output_file) { base::File emf_file = IPC::PlatformFileForTransitToFile(output_file); float scale_factor = 1.0f; - bool success = - RenderPdfPageToMetafile(page_number, std::move(emf_file), &scale_factor); + bool postscript = pdf_rendering_settings_.mode == + PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2 || + pdf_rendering_settings_.mode == + PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3; + bool success = RenderPdfPageToMetafile(page_number, std::move(emf_file), + &scale_factor, postscript); Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone( success, scale_factor)); } @@ -105,7 +122,8 @@ int PrintingHandlerWin::LoadPDF(base::File pdf_file) { bool PrintingHandlerWin::RenderPdfPageToMetafile(int page_number, base::File output_file, - float* scale_factor) { + float* scale_factor, + bool postscript) { Emf metafile; metafile.Init(); @@ -116,18 +134,30 @@ bool PrintingHandlerWin::RenderPdfPageToMetafile(int page_number, // original coordinates and we'll be able to print in full resolution. // Before playback we'll need to counter the scaling up that will happen // in the service (print_system_win.cc). - *scale_factor = gfx::CalculatePageScale( - metafile.context(), pdf_rendering_settings_.area.right(), - pdf_rendering_settings_.area.bottom()); - gfx::ScaleDC(metafile.context(), *scale_factor); + // + // The postscript driver does not use the metafile size since it outputs + // postscript rather than a metafile. Instead it uses the printable area + // sent to RenderPDFPageToDC to determine the area to render. Therefore, + // don't scale the DC to match the metafile, and send the printer physical + // offsets to the driver. + if (!postscript) { + *scale_factor = gfx::CalculatePageScale( + metafile.context(), pdf_rendering_settings_.area.right(), + pdf_rendering_settings_.area.bottom()); + gfx::ScaleDC(metafile.context(), *scale_factor); + } // The underlying metafile is of type Emf and ignores the arguments passed // to StartPage. metafile.StartPage(gfx::Size(), gfx::Rect(), 1); + int offset_x = postscript ? pdf_rendering_settings_.offsets.x() : 0; + int offset_y = postscript ? pdf_rendering_settings_.offsets.y() : 0; + if (!chrome_pdf::RenderPDFPageToDC( &pdf_data_.front(), pdf_data_.size(), page_number, metafile.context(), - pdf_rendering_settings_.dpi, pdf_rendering_settings_.area.x(), - pdf_rendering_settings_.area.y(), + pdf_rendering_settings_.dpi, + pdf_rendering_settings_.area.x() - offset_x, + pdf_rendering_settings_.area.y() - offset_y, pdf_rendering_settings_.area.width(), pdf_rendering_settings_.area.height(), true, false, true, true, pdf_rendering_settings_.autorotate)) { @@ -138,4 +168,4 @@ bool PrintingHandlerWin::RenderPdfPageToMetafile(int page_number, return metafile.SaveTo(&output_file); } -} // printing +} // namespace printing diff --git a/chromium_src/chrome/utility/printing_handler_win.h b/chromium_src/chrome/utility/printing_handler_win.h index b4a1902dc71..d058e7e5055 100644 --- a/chromium_src/chrome/utility/printing_handler_win.h +++ b/chromium_src/chrome/utility/printing_handler_win.h @@ -29,8 +29,7 @@ class PrintingHandlerWin : public UtilityMessageHandler { private: // IPC message handlers. void OnRenderPDFPagesToMetafile(IPC::PlatformFileForTransit pdf_transit, - const PdfRenderSettings& settings, - bool print_text_with_gdi); + const PdfRenderSettings& settings); void OnRenderPDFPagesToMetafileGetPage( int page_number, IPC::PlatformFileForTransit output_file); @@ -39,7 +38,8 @@ class PrintingHandlerWin : public UtilityMessageHandler { int LoadPDF(base::File pdf_file); bool RenderPdfPageToMetafile(int page_number, base::File output_file, - float* scale_factor); + float* scale_factor, + bool postscript); std::vector pdf_data_; PdfRenderSettings pdf_rendering_settings_; diff --git a/chromium_src/components/pdf/renderer/pepper_pdf_host.cc b/chromium_src/components/pdf/renderer/pepper_pdf_host.cc index 5f0e9afe548..28f046c13ce 100644 --- a/chromium_src/components/pdf/renderer/pepper_pdf_host.cc +++ b/chromium_src/components/pdf/renderer/pepper_pdf_host.cc @@ -47,7 +47,7 @@ int32_t PepperPDFHost::OnHostMsgDidStartLoading( if (!render_frame) return PP_ERROR_FAILED; - render_frame->DidStartLoading(); + render_frame->PluginDidStartLoading(); return PP_OK; } @@ -57,7 +57,7 @@ int32_t PepperPDFHost::OnHostMsgDidStopLoading( if (!render_frame) return PP_ERROR_FAILED; - render_frame->DidStopLoading(); + render_frame->PluginDidStopLoading(); return PP_OK; } diff --git a/chromium_src/net/test/embedded_test_server/stream_listen_socket.cc b/chromium_src/net/test/embedded_test_server/stream_listen_socket.cc deleted file mode 100644 index 0b16c1395eb..00000000000 --- a/chromium_src/net/test/embedded_test_server/stream_listen_socket.cc +++ /dev/null @@ -1,333 +0,0 @@ -// 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. - -#include "net/test/embedded_test_server/stream_listen_socket.h" - -#include - -#if defined(OS_WIN) -// winsock2.h must be included first in order to ensure it is included before -// windows.h. -#include -#elif defined(OS_POSIX) -#include -#include -#include -#include -#include -#include "net/base/net_errors.h" -#endif - -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/posix/eintr_wrapper.h" -#include "base/sys_byteorder.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" -#include "net/base/ip_endpoint.h" -#include "net/base/net_errors.h" -#include "net/base/network_interfaces.h" -#include "net/base/sockaddr_storage.h" -#include "net/socket/socket_descriptor.h" - -using std::string; - -#if defined(OS_WIN) -typedef int socklen_t; -#endif // defined(OS_WIN) - -namespace net { - -namespace test_server { - -namespace { - -const int kReadBufSize = 4096; - -} // namespace - -#if defined(OS_WIN) -const int StreamListenSocket::kSocketError = SOCKET_ERROR; -#elif defined(OS_POSIX) -const int StreamListenSocket::kSocketError = -1; -#endif - -StreamListenSocket::StreamListenSocket(SocketDescriptor s, - StreamListenSocket::Delegate* del) - : socket_delegate_(del), - socket_(s), - reads_paused_(false), - has_pending_reads_(false) { -#if defined(OS_WIN) - socket_event_ = WSACreateEvent(); - // TODO(ibrar): error handling in case of socket_event_ == WSA_INVALID_EVENT. - WatchSocket(NOT_WAITING); -#elif defined(OS_POSIX) - wait_state_ = NOT_WAITING; -#endif -} - -StreamListenSocket::~StreamListenSocket() { - CloseSocket(); -#if defined(OS_WIN) - if (socket_event_) { - WSACloseEvent(socket_event_); - socket_event_ = WSA_INVALID_EVENT; - } -#endif -} - -void StreamListenSocket::Send(const char* bytes, - int len, - bool append_linefeed) { - SendInternal(bytes, len); - if (append_linefeed) - SendInternal("\r\n", 2); -} - -void StreamListenSocket::Send(const string& str, bool append_linefeed) { - Send(str.data(), static_cast(str.length()), append_linefeed); -} - -int StreamListenSocket::GetLocalAddress(IPEndPoint* address) const { - SockaddrStorage storage; - if (getsockname(socket_, storage.addr, &storage.addr_len)) { -#if defined(OS_WIN) - int err = WSAGetLastError(); -#else - int err = errno; -#endif - return MapSystemError(err); - } - if (!address->FromSockAddr(storage.addr, storage.addr_len)) - return ERR_ADDRESS_INVALID; - return OK; -} - -int StreamListenSocket::GetPeerAddress(IPEndPoint* address) const { - SockaddrStorage storage; - if (getpeername(socket_, storage.addr, &storage.addr_len)) { -#if defined(OS_WIN) - int err = WSAGetLastError(); -#else - int err = errno; -#endif - return MapSystemError(err); - } - - if (!address->FromSockAddr(storage.addr, storage.addr_len)) - return ERR_ADDRESS_INVALID; - - return OK; -} - -SocketDescriptor StreamListenSocket::AcceptSocket() { - SocketDescriptor conn = HANDLE_EINTR(accept(socket_, NULL, NULL)); - if (conn == kInvalidSocket) - LOG(ERROR) << "Error accepting connection."; - else - base::SetNonBlocking(conn); - return conn; -} - -void StreamListenSocket::SendInternal(const char* bytes, int len) { - char* send_buf = const_cast(bytes); - int len_left = len; - while (true) { - int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0)); - if (sent == len_left) { // A shortcut to avoid extraneous checks. - break; - } - if (sent == kSocketError) { -#if defined(OS_WIN) - if (WSAGetLastError() != WSAEWOULDBLOCK) { - LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError(); -#elif defined(OS_POSIX) - if (errno != EWOULDBLOCK && errno != EAGAIN) { - LOG(ERROR) << "send failed: errno==" << errno; -#endif - break; - } - // Otherwise we would block, and now we have to wait for a retry. - // Fall through to PlatformThread::YieldCurrentThread() - } else { - // sent != len_left according to the shortcut above. - // Shift the buffer start and send the remainder after a short while. - send_buf += sent; - len_left -= sent; - } - base::PlatformThread::YieldCurrentThread(); - } -} - -void StreamListenSocket::Listen() { - int backlog = 10; // TODO(erikkay): maybe don't allow any backlog? - if (listen(socket_, backlog) == -1) { - // TODO(erikkay): error handling. - LOG(ERROR) << "Could not listen on socket."; - return; - } -#if defined(OS_POSIX) - WatchSocket(WAITING_ACCEPT); -#endif -} - -void StreamListenSocket::Read() { - char buf[kReadBufSize + 1]; // +1 for null termination. - int len; - do { - len = HANDLE_EINTR(recv(socket_, buf, kReadBufSize, 0)); - if (len == kSocketError) { -#if defined(OS_WIN) - int err = WSAGetLastError(); - if (err == WSAEWOULDBLOCK) { -#elif defined(OS_POSIX) - if (errno == EWOULDBLOCK || errno == EAGAIN) { -#endif - break; - } else { - // TODO(ibrar): some error handling required here. - break; - } - } else if (len == 0) { -#if defined(OS_POSIX) - // In Windows, Close() is called by OnObjectSignaled. In POSIX, we need - // to call it here. - Close(); -#endif - } else { - // TODO(ibrar): maybe change DidRead to take a length instead. - DCHECK_GT(len, 0); - DCHECK_LE(len, kReadBufSize); - buf[len] = 0; // Already create a buffer with +1 length. - socket_delegate_->DidRead(this, buf, len); - } - } while (len == kReadBufSize); -} - -void StreamListenSocket::Close() { -#if defined(OS_POSIX) - if (wait_state_ == NOT_WAITING) - return; - wait_state_ = NOT_WAITING; -#endif - UnwatchSocket(); - socket_delegate_->DidClose(this); -} - -void StreamListenSocket::CloseSocket() { - if (socket_ != kInvalidSocket) { - UnwatchSocket(); -#if defined(OS_WIN) - closesocket(socket_); -#elif defined(OS_POSIX) - close(socket_); -#endif - } -} - -void StreamListenSocket::WatchSocket(WaitState state) { -#if defined(OS_WIN) - WSAEventSelect(socket_, socket_event_, FD_ACCEPT | FD_CLOSE | FD_READ); - watcher_.StartWatchingOnce(socket_event_, this); -#elif defined(OS_POSIX) - // Implicitly calls StartWatchingFileDescriptor(). - base::MessageLoopForIO::current()->WatchFileDescriptor( - socket_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this); - wait_state_ = state; -#endif -} - -void StreamListenSocket::UnwatchSocket() { -#if defined(OS_WIN) - watcher_.StopWatching(); -#elif defined(OS_POSIX) - watcher_.StopWatchingFileDescriptor(); -#endif -} - -// TODO(ibrar): We can add these functions into OS dependent files. -#if defined(OS_WIN) -// MessageLoop watcher callback. -void StreamListenSocket::OnObjectSignaled(HANDLE object) { - WSANETWORKEVENTS ev; - if (kSocketError == WSAEnumNetworkEvents(socket_, socket_event_, &ev)) { - // TODO - return; - } - - // If both FD_CLOSE and FD_READ are set we only call Read(). - // This will cause OnObjectSignaled to be called immediately again - // unless this socket is destroyed in Read(). - if ((ev.lNetworkEvents & (FD_CLOSE | FD_READ)) == FD_CLOSE) { - Close(); - // Close might have deleted this object. We should return immediately. - return; - } - // The object was reset by WSAEnumNetworkEvents. Watch for the next signal. - watcher_.StartWatchingOnce(object, this); - - if (ev.lNetworkEvents == 0) { - // Occasionally the event is set even though there is no new data. - // The net seems to think that this is ignorable. - return; - } - if (ev.lNetworkEvents & FD_ACCEPT) { - Accept(); - } - if (ev.lNetworkEvents & FD_READ) { - if (reads_paused_) { - has_pending_reads_ = true; - } else { - Read(); - // Read might have deleted this object. We should return immediately. - } - } -} -#elif defined(OS_POSIX) -void StreamListenSocket::OnFileCanReadWithoutBlocking(int fd) { - switch (wait_state_) { - case WAITING_ACCEPT: - Accept(); - break; - case WAITING_READ: - if (reads_paused_) { - has_pending_reads_ = true; - } else { - Read(); - } - break; - default: - // Close() is called by Read() in the Linux case. - NOTREACHED(); - break; - } -} - -void StreamListenSocket::OnFileCanWriteWithoutBlocking(int fd) { - // MessagePumpLibevent callback, we don't listen for write events - // so we shouldn't ever reach here. - NOTREACHED(); -} - -#endif - -void StreamListenSocket::PauseReads() { - DCHECK(!reads_paused_); - reads_paused_ = true; -} - -void StreamListenSocket::ResumeReads() { - DCHECK(reads_paused_); - reads_paused_ = false; - if (has_pending_reads_) { - has_pending_reads_ = false; - Read(); - } -} - -} // namespace test_server - -} // namespace net diff --git a/chromium_src/net/test/embedded_test_server/stream_listen_socket.h b/chromium_src/net/test/embedded_test_server/stream_listen_socket.h deleted file mode 100644 index 00d4b58dcf3..00000000000 --- a/chromium_src/net/test/embedded_test_server/stream_listen_socket.h +++ /dev/null @@ -1,152 +0,0 @@ -// 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. - -// Stream-based listen socket implementation that handles reading and writing -// to the socket, but does not handle creating the socket nor connecting -// sockets, which are handled by subclasses on creation and in Accept, -// respectively. - -// StreamListenSocket handles IO asynchronously in the specified MessageLoop. -// This class is NOT thread safe. It uses WSAEVENT handles to monitor activity -// in a given MessageLoop. This means that callbacks will happen in that loop's -// thread always and that all other methods (including constructor and -// destructor) should also be called from the same thread. - -#ifndef NET_TEST_EMBEDDED_TEST_SERVER_STREAM_LISTEN_SOCKET_H_ -#define NET_TEST_EMBEDDED_TEST_SERVER_STREAM_LISTEN_SOCKET_H_ - -#include - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#endif -#include -#if defined(OS_WIN) -#include "base/win/object_watcher.h" -#elif defined(OS_POSIX) -#include "base/message_loop/message_loop.h" -#endif - -#include "base/macros.h" -#include "base/compiler_specific.h" -#include "net/base/net_export.h" -#include "net/socket/socket_descriptor.h" - -namespace net { - -class IPEndPoint; - -namespace test_server { - -class StreamListenSocket : -#if defined(OS_WIN) - public base::win::ObjectWatcher::Delegate { -#elif defined(OS_POSIX) - public base::MessageLoopForIO::Watcher { -#endif - - public: - ~StreamListenSocket() override; - - // TODO(erikkay): this delegate should really be split into two parts - // to split up the listener from the connected socket. Perhaps this class - // should be split up similarly. - class Delegate { - public: - // |server| is the original listening Socket, connection is the new - // Socket that was created. - virtual void DidAccept(StreamListenSocket* server, - std::unique_ptr connection) = 0; - virtual void DidRead(StreamListenSocket* connection, - const char* data, - int len) = 0; - virtual void DidClose(StreamListenSocket* sock) = 0; - - protected: - virtual ~Delegate() {} - }; - - // Send data to the socket. - void Send(const char* bytes, int len, bool append_linefeed = false); - void Send(const std::string& str, bool append_linefeed = false); - - // Copies the local address to |address|. Returns a network error code. - // This method is virtual to support unit testing. - virtual int GetLocalAddress(IPEndPoint* address) const; - // Copies the peer address to |address|. Returns a network error code. - // This method is virtual to support unit testing. - virtual int GetPeerAddress(IPEndPoint* address) const; - - static const int kSocketError; - - protected: - enum WaitState { NOT_WAITING = 0, WAITING_ACCEPT = 1, WAITING_READ = 2 }; - - StreamListenSocket(SocketDescriptor s, Delegate* del); - - SocketDescriptor AcceptSocket(); - virtual void Accept() = 0; - - void Listen(); - void Read(); - void Close(); - void CloseSocket(); - - // Pass any value in case of Windows, because in Windows - // we are not using state. - void WatchSocket(WaitState state); - void UnwatchSocket(); - - Delegate* const socket_delegate_; - - private: - friend class TransportClientSocketTest; - - void SendInternal(const char* bytes, int len); - -#if defined(OS_WIN) - // ObjectWatcher delegate. - void OnObjectSignaled(HANDLE object) override; - base::win::ObjectWatcher watcher_; - HANDLE socket_event_; -#elif defined(OS_POSIX) - // Called by MessagePumpLibevent when the socket is ready to do I/O. - void OnFileCanReadWithoutBlocking(int fd) override; - void OnFileCanWriteWithoutBlocking(int fd) override; - WaitState wait_state_; - // The socket's libevent wrapper. - base::MessageLoopForIO::FileDescriptorWatcher watcher_; -#endif - - // NOTE: This is for unit test use only! - // Pause/Resume calling Read(). Note that ResumeReads() will also call - // Read() if there is anything to read. - void PauseReads(); - void ResumeReads(); - - const SocketDescriptor socket_; - bool reads_paused_; - bool has_pending_reads_; - - DISALLOW_COPY_AND_ASSIGN(StreamListenSocket); -}; - -// Abstract factory that must be subclassed for each subclass of -// StreamListenSocket. -class StreamListenSocketFactory { - public: - virtual ~StreamListenSocketFactory() {} - - // Returns a new instance of StreamListenSocket or NULL if an error occurred. - virtual std::unique_ptr CreateAndListen( - StreamListenSocket::Delegate* delegate) const = 0; -}; - -} // namespace test_server - -} // namespace net - -#endif // NET_TEST_EMBEDDED_TEST_SERVER_STREAM_LISTEN_SOCKET_H_ diff --git a/chromium_src/net/test/embedded_test_server/tcp_listen_socket.cc b/chromium_src/net/test/embedded_test_server/tcp_listen_socket.cc deleted file mode 100644 index 50aac809052..00000000000 --- a/chromium_src/net/test/embedded_test_server/tcp_listen_socket.cc +++ /dev/null @@ -1,120 +0,0 @@ -// 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. - -#include "net/test/embedded_test_server/tcp_listen_socket.h" - -#if defined(OS_WIN) -// winsock2.h must be included first in order to ensure it is included before -// windows.h. -#include -#include -#elif defined(OS_POSIX) -#include -#include -#include -#include -#include -#include "net/base/net_errors.h" -#endif - -#include "base/logging.h" -#include "base/sys_byteorder.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" -#include "net/base/network_interfaces.h" -#include "net/base/winsock_init.h" -#include "net/socket/socket_descriptor.h" - -using std::string; - -namespace net { - -namespace test_server { - -// static -std::unique_ptr TCPListenSocket::CreateAndListen( - const string& ip, - uint16_t port, - StreamListenSocket::Delegate* del) { - SocketDescriptor s = CreateAndBind(ip, port); - if (s == kInvalidSocket) - return std::unique_ptr(); - std::unique_ptr sock(new TCPListenSocket(s, del)); - sock->Listen(); - return sock; -} - -TCPListenSocket::TCPListenSocket(SocketDescriptor s, - StreamListenSocket::Delegate* del) - : StreamListenSocket(s, del) { -} - -TCPListenSocket::~TCPListenSocket() { -} - -SocketDescriptor TCPListenSocket::CreateAndBind(const string& ip, - uint16_t port) { - SocketDescriptor s = CreatePlatformSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (s != kInvalidSocket) { -#if defined(OS_POSIX) - // Allow rapid reuse. - static const int kOn = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); -#endif - sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(ip.c_str()); - addr.sin_port = base::HostToNet16(port); - if (bind(s, reinterpret_cast(&addr), sizeof(addr))) { -#if defined(OS_WIN) - closesocket(s); -#elif defined(OS_POSIX) - close(s); -#endif - LOG(ERROR) << "Could not bind socket to " << ip << ":" << port; - s = kInvalidSocket; - } - } - return s; -} - -SocketDescriptor TCPListenSocket::CreateAndBindAnyPort(const string& ip, - uint16_t* port) { - SocketDescriptor s = CreateAndBind(ip, 0); - if (s == kInvalidSocket) - return kInvalidSocket; - sockaddr_in addr; - socklen_t addr_size = sizeof(addr); - bool failed = getsockname(s, reinterpret_cast(&addr), - &addr_size) != 0; - if (addr_size != sizeof(addr)) - failed = true; - if (failed) { - LOG(ERROR) << "Could not determine bound port, getsockname() failed"; -#if defined(OS_WIN) - closesocket(s); -#elif defined(OS_POSIX) - close(s); -#endif - return kInvalidSocket; - } - *port = base::NetToHost16(addr.sin_port); - return s; -} - -void TCPListenSocket::Accept() { - SocketDescriptor conn = AcceptSocket(); - if (conn == kInvalidSocket) - return; - std::unique_ptr sock(new TCPListenSocket(conn, socket_delegate_)); -#if defined(OS_POSIX) - sock->WatchSocket(WAITING_READ); -#endif - socket_delegate_->DidAccept(this, std::move(sock)); -} - -} // namespace test_server - -} // namespace net diff --git a/chromium_src/net/test/embedded_test_server/tcp_listen_socket.h b/chromium_src/net/test/embedded_test_server/tcp_listen_socket.h deleted file mode 100644 index db18fd0f0eb..00000000000 --- a/chromium_src/net/test/embedded_test_server/tcp_listen_socket.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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. - -#ifndef NET_TEST_EMBEDDED_TEST_SERVER_TCP_LISTEN_SOCKET_H_ -#define NET_TEST_EMBEDDED_TEST_SERVER_TCP_LISTEN_SOCKET_H_ - -#include - -#include "base/macros.h" -#include "net/base/net_export.h" -#include "net/socket/socket_descriptor.h" -#include "net/test/embedded_test_server/stream_listen_socket.h" - -namespace net { - -namespace test_server { - -// Implements a TCP socket. -class TCPListenSocket : public StreamListenSocket { - public: - ~TCPListenSocket() override; - - // Listen on port for the specified IP address. Use 127.0.0.1 to only - // accept local connections. - static std::unique_ptr CreateAndListen( - const std::string& ip, - uint16_t port, - StreamListenSocket::Delegate* del); - - protected: - TCPListenSocket(SocketDescriptor s, StreamListenSocket::Delegate* del); - - // Implements StreamListenSocket::Accept. - void Accept() override; - - private: - friend class EmbeddedTestServer; - friend class TCPListenSocketTester; - - // Get raw TCP socket descriptor bound to ip:port. - static SocketDescriptor CreateAndBind(const std::string& ip, uint16_t port); - - // Get raw TCP socket descriptor bound to ip and return port it is bound to. - static SocketDescriptor CreateAndBindAnyPort(const std::string& ip, - uint16_t* port); - - DISALLOW_COPY_AND_ASSIGN(TCPListenSocket); -}; - -} // namespace test_server - -} // namespace net - -#endif // NET_TEST_EMBEDDED_TEST_SERVER_TCP_LISTEN_SOCKET_H_ diff --git a/common.gypi b/common.gypi index 7c1bf366aa6..fff12d9b366 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. @@ -16,7 +16,7 @@ 'openssl_no_asm': 1, 'use_openssl_def': 0, 'OPENSSL_PRODUCT': 'libopenssl.a', - 'node_release_urlbase': 'https://atom.io/download/atom-shell', + 'node_release_urlbase': 'https://atom.io/download/electron', 'node_byteorder': ' { + win = new BrowserWindow({ + webPreferences: { + // `plugins` harus diaktifkan. + plugins: true + } + }) + win.show() +}) +``` + +## Verifikasi plugin + +Untuk memverifikasi jika plugin telah berhasil, anda dapat menggunakan cara berikut: + +* Buka *devtools* dan periksa apakah `navigator.plugins` menyertakan *Widevine +Plugin CDM* +* Buka https://shaka-player-demo.appspot.com/ dan muat manifes yang menggunakan +`Widevine`. +* Buka http://www.dash-player.com/demo/drm-test-area/, periksa apakah di halamannya +terdapat `bitdash uses Widevine in your browser`, lalu putar videonya. diff --git a/docs-translations/id/tutorial/windows-store-guide.md b/docs-translations/id/tutorial/windows-store-guide.md new file mode 100644 index 00000000000..3fa81d7c063 --- /dev/null +++ b/docs-translations/id/tutorial/windows-store-guide.md @@ -0,0 +1,170 @@ +# Panduan Windows Store + +Dengan Windows 8, eksekusi win32 yg lama mendapatkan saudara yang baru: *The +Universal Windows Platform*. Format `.appx` yang baru tidak hanya memungkinkan +sejumlah API yang baru dan hebat seperti *Cortana* atau *Push Notifications*, +tetapi juga melalui *Windows Store*, ini akan menyederhanakan instalasi dan update. + +Microsoft [telah mengembangkan sebuah alat yang mengkompilasi aplikasi Elektron sebagai paket `.appx`][electron-windows-store], memungkinkan *developer* untuk menggunakan beberapa +sarana yang dapat ditemukan di model aplikasi baru. Panduan ini menjelaskan cara +menggunakannya - dan kemampuan dan keterbatasan paket Electron AppX. + +## Latar Belakang dan Persyaratan + +Windows 10 "Anniversary Update" dapat menjalankan win32 `.exe` *binaries* dengan cara +meluncurkan mereka bersama-sama dengan *filesystem* virtual dan pendaftaran . Keduanya +dibuat saat kompilasi dengan menjalankan aplikasi and instalasi di dalam *Windows +Container*, memungkinkan *Windows* untuk mengidentifikasi secara tepat modifikasi +sistem operasi mana yang dilakukan saat instalasi. Memasangkan eksekusi +*filesystem* virtual dan pendaftaran virtual yang memungkinkan *Windows* untuk +menjalankan *one-click* instalasi and menghapus instalasi. + +Selain itu, exe diluncurkan di dalam bentuk appx - yang berarti bisa menggunakan +API banyak yang tersedia di *Windows Universal Platform*. Untuk mendapatkan +kemampuan yang lebih, aplikasi Electron dapat dipasangkan dengan *UWP background task* +tersembunyi yang dapat diluncurkan bersamaan dengan `exe` - seperti diluncurkan +sebagai dampingan untuk menjalankan *tasks* yang berjalan di *background*, +menerima *push-notification*, atau untuk berkomunikasi dengan aplikasi UWP lainnya. + +Untuk mengkompilasi aplikasi Elektron yang ada, pastikan anda memenuhi +persyaratan berikut: + +* Windows 10 with Anniversary Update (dikeluarkan August 2nd, 2016) +* The Windows 10 SDK, [unduh disini][windows-sdk] +* Setidaknya Node 4 (untuk mengecek, jalankan `node -v`) + + +Kemudian, instal `electron-windows-store` CLI: + +``` +npm install -g electron-windows-store +``` + +## Step 1: Kemas Aplikasi Elektron Anda + +Kemas aplikasi menggunakan [electron-packager][electron-packager] (atau alat serupa). +Pastikan untuk menghapus `node_modules` yang tidak anda perlukan dalam aplikasi akhir +anda, karena modul yang tidak anda butuhkan hanya akan meningkatkan ukuran aplikasi anda. + +Outputnya kira-kira akan terlihat seperti ini: +``` +├── Ghost.exe +├── LICENSE +├── content_resources_200_percent.pak +├── content_shell.pak +├── d3dcompiler_47.dll +├── ffmpeg.dll +├── icudtl.dat +├── libEGL.dll +├── libGLESv2.dll +├── locales +│   ├── am.pak +│   ├── ar.pak +│   ├── [...] +├── natives_blob.bin +├── node.dll +├── resources +│   ├── app +│   └── atom.asar +├── snapshot_blob.bin +├── squirrel.exe +└── ui_resources_200_percent.pak +``` + + +## 2: Menjalankan *electron-windows-store* + +Dari *PowerShell* (jalankan sebagai "Administrator"), jalankan +`Electron-windows-store` dengan parameter yang dibutuhkan, menggunakan kedua +direktori *input* dan *output*, nama dan versi aplikasi, dan konfirmasi +`Node_modules` harus di *flatten*. + +``` +electron-windows-store ` + --input-directory C:\myelectronapp ` + --output-directory C:\output\myelectronapp ` + --flatten true ` + --package-version 1.0.0.0 ` + --package-name myelectronapp +``` + +Setelah dijalankan, alat ini akan mulai bekerja: Ia akan menerima aplikasi Elektron +anda sebagai *input*, *flattening* `node_modules`. Kemudian, ia akan mengarsipkan +aplikasi anda sebagai `app.zip`. Dengan menggunakan *installer* dan *Windows Container* +, alat ini menciptakan paket AppX yang "diperluas" - termasuk *Windows Application +Manifest* (`AppXManifest.xml`) berserta dengan sistem *virtual file* dan pendaftaran +virtual di dalam map *output* anda. + + +Setelah *file* AppX yang diperluas telah dibuat, alat ini menggunakan +*Windows App Packager* (`MakeAppx.exe`) untuk menggabungkan paket AppX menjadi satu +*file* dari file-file yang ada di *disk*. Akhirnya, alat ini juga bisa digunakan +untuk membuat sertifikat terpercaya di komputer anda untuk menandatangani paket +AppX yang baru. Dengan paket AppX yang telah ditandatangani, CLI juga bisa +secara otomatis menginstal paket di mesin anda. + + +## 3: Menggunakan Paket AppX + +Untuk menjalankan paket, pengguna akan memerlukan Windows 10 dengan apa +yang disebutnya *"Anniversary Update"* - rincian tentang cara memperbarui Windows +dapat ditemukan [di sini][how-to-update]. + +Di sisi lain dari aplikasi-aplikasi UWP tradisional, aplikasi yang terpaket saat ini +perlu menjalani proses verifikasi manual, yang dapat anda terapkan +[disini][centennial-campaigns]. Sementara itu, semua pengguna bisa menginstal +paket anda dengan mengklik dua kali, oleh sebab itu, pengiriman submisi ke toko +tidak diperlukan jika anda hanya mencari metode instalasi yang mudah. Di lingkungan +yang dikelola (biasanya perusahaan), `Add-AppxPackage` [PowerShell Cmdlet dapat digunakan untuk menginstalnya secara otomatis][add-appxpackage]. + +Keterbatasan penting lainnya adalah paket AppX yang telah dikompilasi masih berisi +*Win32 executable* - dan karena itu tidak akan berjalan di *Xbox*, *HoloLens*, +atau Telepon. + + +## Opsional: Tambahkan Fitur UWP menggunakan *BackgroundTask* + +Anda dapat memasangkan aplikasi Elektron Anda dengan tugas *background* UWP yang +tersembunyi yang akan memanfaatkan sepenuhnya fitur Windows 10 - seperti *push-notification*, +integrasi Cortana, atau *live tiles*. + +Untuk mencari tahu bagaimana aplikasi Elektron yang menggunakan *background task* +untuk mengirim *toast notification* dan *live tiles*, [lihat contoh yang disediakan Microsoft][background-task]. + + +## Opsional: Mengkonversi menggunakan *Container Virtualization* + +Untuk menghasilkan paket AppX, `elektron-windows-store` CLI menggunakan *template* +yang seharusnya bekerja untuk sebagian besar aplikasi Electron. Namun, jika anda +menggunakan *custom installer*, atau jika anda mengalami masalah dengan paket +yang dihasilkan, anda dapat mencoba membuat paket menggunakan kompilasi dengan +bantuan Windows Container - di dalam mode itu, CLI akan menginstal dan menjalankan +aplikasi Anda di dalam *Windows Container* yang kosong untuk menentukan +modifikasi apa yang aplikasi Anda lakukan pada sistem operasi. + +Sebelum menjalankan CLI, anda harus mengatur *"Windows Desktop App Converter"*. +Ini akan memakan waktu beberapa menit, tapi jangan khawatir - anda hanya perlu +melakukan ini sekali saja. Unduh *Desktop App Converter* dari [di sini][app-converter]. +Anda akan menerima dua file: `DesktopAppConverter.zip` dan` BaseImage-14316.wim`. + +1. *Unzip* `DesktopAppConverter.zip`. Dari PowerShell (dibuka dengan + "jalankan sebagai Administrator", pastikan bahwa kebijakan eksekusi sistem +anda mengizinkan untuk menjalankan semua yang ingin dijalankan dengan menggunakan `Set-ExecutionPolicy bypass`. +2. Kemudian, jalankan instalasi *Desktop App Converter*, dengan menggunakan lokasi +*Windows Base Image* (di unduh sebagai `BaseImage-14316.wim`), dengan cara memanggil +perintah `. \ DesktopAppConverter.ps1 -Setup -BaseImage. \ BaseImage-14316.wim`. +3. Jika menjalankan perintah tersebut menyebabkan *reboot*, silakan *restart* +mesin anda dan mejalankan perintah yang telah disebutkan diatas setelah berhasil +*restart*. + +Setelah instalasi telah berhasil, anda dapat melajutkan untuk mengkompilasi +aplikasi Electron anda. + +[windows-sdk]: https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk +[app-converter]: https://www.microsoft.com/en-us/download/details.aspx?id=51691 +[add-appxpackage]: https://technet.microsoft.com/en-us/library/hh856048.aspx +[electron-packager]: https://github.com/electron-userland/electron-packager +[electron-windows-store]: https://github.com/catalystcode/electron-windows-store +[background-task]: https://github.com/felixrieseberg/electron-uwp-background +[centennial-campaigns]: https://developer.microsoft.com/en-us/windows/projects/campaigns/desktop-bridge +[how-to-update]: https://blogs.windows.com/windowsexperience/2016/08/02/how-to-get-the-windows-10-anniversary-update 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 09a8621a24c..aa5e2d45ef0 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 97ed0cbf741..3dc71bf7a87 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 26b4380d43c..fd74ac57c12 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 ce2cb98e01d..df42f99fdad 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 31e814f3d9e..b80e4d99a19 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 8d592ddc742..050bc73793d 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/ru-RU/README.md b/docs-translations/ru-RU/README.md index 96df31da25a..dd674c1c105 100644 --- a/docs-translations/ru-RU/README.md +++ b/docs-translations/ru-RU/README.md @@ -1,10 +1,10 @@ -Пожалуйста, убедитесь, что Вы используете документацию, которая соответствует вашей версии Electron. -Номер версии должен быть частью адреса страницы. Если это не так, Вы -возможно, используете документацию ветки разработки, которая может содержать изменения api, +Пожалуйста, убедитесь, что вы используете документацию, которая соответствует вашей версии Electron. +Номер версии должен быть частью адреса страницы. Если это не так, вы, +возможно, используете документацию ветки разработки, которая может содержать изменения API, которые не совместимы с вашей версией Electron. Если это так, -Вы можете переключиться на другую версию документации в списке +вы можете переключиться на другую версию документации в списке [доступные версии](https://electron.atom.io/docs/) на [atom.io](atom.io), или -если Вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и +если вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и выберите тег, который соответствует вашей версии. ## Руководства diff --git a/docs-translations/zh-CN/README.md b/docs-translations/zh-CN/README.md index 706738d60d2..c6ff17cda90 100644 --- a/docs-translations/zh-CN/README.md +++ b/docs-translations/zh-CN/README.md @@ -93,7 +93,7 @@ * [构建步骤(macOS)](development/build-instructions-osx.md) * [构建步骤(Windows)](development/build-instructions-windows.md) * [构建步骤(Linux)](development/build-instructions-linux.md) -* [调试步骤 (macOS)](development/debug-instructions-macos.md) +* [调试步骤 (macOS)](development/debugging-instructions-macos.md) * [调试步骤 (Windows)](development/debug-instructions-windows.md) * [在调试中使用 Symbol Server](development/setting-up-symbol-server.md) * [文档风格指南](styleguide.md) 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 68fddb11d23..c97fbae768d 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 6bd1452b816..329dc74bd1f 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 0afa03463c5..fd9f6069134 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 0a7929e57e0..bad622438ff 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/api/app.md b/docs/api/app.md index cf537474af2..614989dc99c 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -667,6 +667,8 @@ app.setJumpList([ * `argv` String[] - An array of the second instance's command line arguments * `workingDirectory` String - The second instance's working directory +Returns `Boolean`. + This method makes your application a Single Instance Application - instead of allowing multiple instances of your app to run, this will ensure that only a single instance of your app is running, and other instances signal this diff --git a/docs/api/browser-view.md b/docs/api/browser-view.md index 3b7e6f9b9bf..2779beebfab 100644 --- a/docs/api/browser-view.md +++ b/docs/api/browser-view.md @@ -44,31 +44,31 @@ Objects created with `new BrowserView` have the following properties: #### `view.webContents` _Experimental_ -A [`webContents`](web-contents.md) object owned by this view. +A [`WebContents`](web-contents.md) object owned by this view. -#### `win.id` _Experimental_ +#### `view.id` _Experimental_ A `Integer` representing the unique ID of the view. ### Instance Methods -Objects created with `new BrowserWindow` have the following instance methods: +Objects created with `new BrowserView` have the following instance methods: -#### `win.setAutoResize(options)` _Experimental_ +#### `view.setAutoResize(options)` _Experimental_ * `options` Object - * `width`: If `true`, the view's width will grow and shrink together with - the window. `false` by default. - * `height`: If `true`, the view's height will grow and shrink together with - the window. `false` by default. + * `width` Boolean - If `true`, the view's width will grow and shrink together + with the window. `false` by default. + * `height` Boolean - If `true`, the view's height will grow and shrink + together with the window. `false` by default. -#### `win.setBounds(bounds)` _Experimental_ +#### `view.setBounds(bounds)` _Experimental_ * `bounds` [Rectangle](structures/rectangle.md) Resizes and moves the view to the supplied bounds relative to the window. -#### `win.setBackgroundColor(color)` _Experimental_ +#### `view.setBackgroundColor(color)` _Experimental_ * `color` String - Color in `#aarrggbb` or `#argb` form. The alpha channel is optional. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 8fcc3f8213d..4bb07cbb55d 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -307,6 +307,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. 'Electron Isolated Context' entry in the combo box at the top of the Console tab. **Note:** This option is currently experimental and may change or be removed in future Electron releases. + * `nativeWindowOpen` Boolean (optional) - Whether to use native `window.open()`. Defaults to `false`. When setting minimum or maximum window size with `minWidth`/`maxWidth`/ `minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from @@ -1027,7 +1028,7 @@ Same as `webContents.capturePage([rect, ]callback)`. * `httpReferrer` String (optional) - A HTTP Referrer url. * `userAgent` String (optional) - A user agent originating the request. * `extraHeaders` String (optional) - Extra headers separated by "\n" - * `postData` ([UploadRawData](structures/upload-raw-data.md) | [UploadFile](structures/upload-file.md) | [UploadFileSystem](structures/upload-file-system.md) | [UploadBlob](structures/upload-blob.md))[] - (optional) + * `postData` ([UploadRawData[]](structures/upload-raw-data.md) | [UploadFile[]](structures/upload-file.md) | [UploadFileSystem[]](structures/upload-file-system.md) | [UploadBlob[]](structures/upload-blob.md)) - (optional) * `baseURLForDataURL` String (optional) - Base url (with trailing path separator) for files to be loaded by the data url. This is needed only if the specified `url` is a data url and needs to load other files. Same as `webContents.loadURL(url[, options])`. diff --git a/docs/api/chrome-command-line-switches.md b/docs/api/chrome-command-line-switches.md index 5bc1a99e952..4aa809b02c1 100644 --- a/docs/api/chrome-command-line-switches.md +++ b/docs/api/chrome-command-line-switches.md @@ -28,7 +28,7 @@ Disables the disk cache for HTTP requests. Disable HTTP/2 and SPDY/3.1 protocols. -## --debug=`port` and --debug-brk=`port` +## --inspect=`port` and --inspect-brk=`port` Debug-related flags, see the [Debugging the Main Process][debugging-main-process] guide for details. @@ -36,6 +36,10 @@ Debug-related flags, see the [Debugging the Main Process][debugging-main-process Enables remote debugging over HTTP on the specified `port`. +## --disk-cache-size=`size` + +Forces the maximum disk space to be used by the disk cache, in bytes. + ## --js-flags=`flags` Specifies the flags passed to the Node JS engine. It has to be passed when starting diff --git a/docs/api/desktop-capturer.md b/docs/api/desktop-capturer.md index 00542f402f4..5f11d94561f 100644 --- a/docs/api/desktop-capturer.md +++ b/docs/api/desktop-capturer.md @@ -1,7 +1,7 @@ # desktopCapturer > Access information about media sources that can be used to capture audio and -> video from the desktop using the [`navigator.webkitGetUserMedia`] API. +> video from the desktop using the [`navigator.mediaDevices.getUserMedia`] API. Process: [Renderer](../glossary.md#renderer-process) @@ -16,7 +16,7 @@ desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => { if (error) throw error for (let i = 0; i < sources.length; ++i) { if (sources[i].name === 'Electron') { - navigator.webkitGetUserMedia({ + navigator.mediaDevices.getUserMedia({ audio: false, video: { mandatory: { @@ -44,12 +44,27 @@ function handleError (e) { ``` To capture video from a source provided by `desktopCapturer` the constraints -passed to [`navigator.webkitGetUserMedia`] must include +passed to [`navigator.mediaDevices.getUserMedia`] must include `chromeMediaSource: 'desktop'`, and `audio: false`. To capture both audio and video from the entire desktop the constraints passed -to [`navigator.webkitGetUserMedia`] must include `chromeMediaSource: 'screen'`, -and `audio: true`, but should not include a `chromeMediaSourceId` constraint. +to [`navigator.mediaDevices.getUserMedia`] must include `chromeMediaSource: 'desktop'`, +for both `audio` and `video`, but should not include a `chromeMediaSourceId` constraint. + +```javascript +const constraints = { + audio: { + mandatory: { + chromeMediaSource: 'desktop' + } + }, + video: { + mandatory: { + chromeMediaSource: 'desktop' + } + } +} +``` ## Methods @@ -60,7 +75,7 @@ The `desktopCapturer` module has the following methods: * `options` Object * `types` String[] - An array of Strings that lists the types of desktop sources to be captured, available types are `screen` and `window`. - * `thumbnailSize` [Size](structures/size.md) (optional) - The size that the media source thumbnail + * `thumbnailSize` [Size](structures/size.md) (optional) - The size that the media source thumbnail should be scaled to. Default is `150` x `150`. * `callback` Function * `error` Error @@ -73,4 +88,4 @@ and calls `callback(error, sources)` when finished. objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured. -[`navigator.webkitGetUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/Navigator/getUserMedia +[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia diff --git a/docs/api/dialog.md b/docs/api/dialog.md index 68cfa7fcea9..79a4ea781ac 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -161,8 +161,8 @@ It returns the index of the clicked button. The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal. -If a `callback` is passed, the API call will be asynchronous and the result -will be passed via `callback(response)`. +If a `callback` is passed, the dialog will not block the process. The API call +will be asynchronous and the result will be passed via `callback(response)`. ### `dialog.showErrorBox(title, content)` diff --git a/docs/api/native-image.md b/docs/api/native-image.md index da496436b32..8163c233962 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -153,6 +153,8 @@ Creates a new `NativeImage` instance from `buffer`. * `dataURL` String +Returns `NativeImage` + Creates a new `NativeImage` instance from `dataURL`. ## Class: NativeImage diff --git a/docs/api/structures/memory-info.md b/docs/api/structures/memory-info.md index 69c67f16cc8..f6b9d17aaa1 100644 --- a/docs/api/structures/memory-info.md +++ b/docs/api/structures/memory-info.md @@ -1,6 +1,6 @@ # MemoryInfo Object -* `workingSetSize` Integer - Process id of the process. +* `pid` Integer - Process id of the process. * `workingSetSize` Integer - The amount of memory currently pinned to actual physical RAM. * `peakWorkingSetSize` Integer - The maximum amount of memory that has ever been pinned to actual physical RAM. @@ -9,4 +9,4 @@ * `sharedBytes` Integer - The amount of memory shared between processes, typically memory consumed by the Electron code itself -Note that all statistics are reported in Kilobytes. \ No newline at end of file +Note that all statistics are reported in Kilobytes. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 5304ec58159..15f0ebe6646 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -218,6 +218,36 @@ When in-page navigation happens, the page URL changes but does not cause navigation outside of the page. Examples of this occurring are when anchor links are clicked or when the DOM `hashchange` event is triggered. +#### Event: 'will-prevent-unload' + +Returns: + +* `event` Event + +Emitted when a `beforeunload` event handler is attempting to cancel a page unload. + +Calling `event.preventDefault()` will ignore the `beforeunload` event handler +and allow the page to be unloaded. + +```javascript +const {BrowserWindow, dialog} = require('electron') +const win = new BrowserWindow({width: 800, height: 600}) +win.webContents.on('will-prevent-unload', (event) => { + const choice = dialog.showMessageBox(win, { + type: 'question', + buttons: ['Leave', 'Stay'], + title: 'Do you want to leave this site?', + message: 'Changes you made may not be saved.', + defaultId: 0, + cancelId: 1 + }) + const leave = (choice === 0) + if (leave) { + event.preventDefault() + } +}) +``` + #### Event: 'crashed' Returns: @@ -537,7 +567,7 @@ that can't be set via `` attributes. * `httpReferrer` String (optional) - A HTTP Referrer url. * `userAgent` String (optional) - A user agent originating the request. * `extraHeaders` String (optional) - Extra headers separated by "\n" - * `postData` ([UploadRawData](structures/upload-raw-data.md) | [UploadFile](structures/upload-file.md) | [UploadFileSystem](structures/upload-file-system.md) | [UploadBlob](structures/upload-blob.md))[] - (optional) + * `postData` ([UploadRawData[]](structures/upload-raw-data.md) | [UploadFile[]](structures/upload-file.md) | [UploadFileSystem[]](structures/upload-file-system.md) | [UploadBlob[]](structures/upload-blob.md)) - (optional) * `baseURLForDataURL` String (optional) - Base url (with trailing path separator) for files to be loaded by the data url. This is needed only if the specified `url` is a data url and needs to load other files. Loads the `url` in the window. The `url` must contain the protocol prefix, @@ -1295,6 +1325,10 @@ Setting the WebRTC IP handling policy allows you to control which IPs are exposed via WebRTC. See [BrowserLeaks](https://browserleaks.com/webrtc) for more details. +#### `contents.getOSProcessId()` + +Returns `Integer` - The `pid` of the associated renderer process. + ### Instance Properties #### `contents.id` diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index 1da5679c18a..8c5ce3ddd15 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -312,8 +312,8 @@ webview.addEventListener('dom-ready', () => { * `httpReferrer` String (optional) - A HTTP Referrer url. * `userAgent` String (optional) - A user agent originating the request. * `extraHeaders` String (optional) - Extra headers separated by "\n" - * `postData` ([UploadRawData](structures/upload-raw-data.md) | [UploadFile](structures/upload-file.md) | [UploadFileSystem](structures/upload-file-system.md) | [UploadBlob](structures/upload-blob.md))[] - (optional) - * `baseURLForDataURL` String (optional) - Base url (with trailing path separator) for files to be loaded by the data url. This is needed only if the specified `url` is a data url and needs to load other files. + * `postData` ([UploadRawData[]](structures/upload-raw-data.md) | [UploadFile[]](structures/upload-file.md) | [UploadFileSystem[]](structures/upload-file-system.md) | [UploadBlob[]](structures/upload-blob.md)) - (optional) + * `baseURLForDataURL` String (optional) - Base url (with trailing path separator) for files to be loaded by the data url. This is needed only if the specified `url` is a data url and needs to load other files. Loads the `url` in the webview, the `url` must contain the protocol prefix, e.g. the `http://` or `file://`. @@ -511,14 +511,14 @@ Inserts `text` to the focused element. * `text` String - Content to be searched, must not be empty. * `options` Object (optional) - * `forward` Boolean - Whether to search forward or backward, defaults to `true`. - * `findNext` Boolean - Whether the operation is first request or a follow up, + * `forward` Boolean - (optional) Whether to search forward or backward, defaults to `true`. + * `findNext` Boolean - (optional) Whether the operation is first request or a follow up, defaults to `false`. - * `matchCase` Boolean - Whether search should be case-sensitive, + * `matchCase` Boolean - (optional) Whether search should be case-sensitive, defaults to `false`. - * `wordStart` Boolean - Whether to look only at the start of words. + * `wordStart` Boolean - (optional) Whether to look only at the start of words. defaults to `false`. - * `medialCapitalAsWordStart` Boolean - When combined with `wordStart`, + * `medialCapitalAsWordStart` Boolean - (optional) When combined with `wordStart`, accepts a match in the middle of a word if the match begins with an uppercase letter followed by a lowercase or non-letter. Accepts several other intra-word matches, defaults to `false`. diff --git a/docs/api/window-open.md b/docs/api/window-open.md index 41332aa6cfd..30ea8511945 100644 --- a/docs/api/window-open.md +++ b/docs/api/window-open.md @@ -45,3 +45,40 @@ has to be a field of `BrowserWindow`'s options. Sends a message to the parent window with the specified origin or `*` for no origin preference. + +### Use Native `window.open()` + +If you want to use native `window.open()` implementation, pass `useNativeWindowOpen: true` in `webPreferences` option. +Native `window.open()` allows synchronous access to opened windows so it is convenient choice if you need to open a dialog or a preferences window. + +The creation of the `BrowserWindow` is customizable in `WebContents`'s `new-window` event. + +```javascript +// main process +const mainWindow = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + nativeWindowOpen: true + } +}) +mainWindow.webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures) => { + if (frameName === 'modal') { + // open window as modal + event.preventDefault() + Object.assign(options, { + modal: true, + parent: mainWindow, + width: 100, + height: 100 + }) + event.newGuest = new BrowserWindow(options) + } +}) +``` + +```javascript +// renderer process (mainWindow) +let modal = window.open('', 'modal') +modal.document.write('

Hello

') +``` diff --git a/docs/development/atom-shell-vs-node-webkit.md b/docs/development/atom-shell-vs-node-webkit.md index 0912a8d66e3..4c8cd4ef5fe 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 df12df6ae97..083f52d0047 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/clang-format.md b/docs/development/clang-format.md index 7ad261a2def..34f469c1fbe 100644 --- a/docs/development/clang-format.md +++ b/docs/development/clang-format.md @@ -32,3 +32,4 @@ For further guidance on setting up editor integration, see these pages: * [Atom](https://atom.io/packages/clang-format) * [Vim & Emacs](http://clang.llvm.org/docs/ClangFormat.html#vim-integration) + * [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) diff --git a/docs/development/debug-instructions-windows.md b/docs/development/debug-instructions-windows.md index b2d33473934..440cb33e414 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 e119db466a1..054909773f6 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 cc74fe69427..4af519120ee 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 6e47a8b4f52..1004e6d3645 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 b923037f9f0..b5397182387 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/docs/tutorial/debugging-main-process-node-inspector.md b/docs/tutorial/debugging-main-process-node-inspector.md deleted file mode 100644 index 2c42ef3139a..00000000000 --- a/docs/tutorial/debugging-main-process-node-inspector.md +++ /dev/null @@ -1,131 +0,0 @@ -# Debugging the Main Process in node-inspector - -[`node-inspector`][node-inspector] provides a familiar DevTools GUI that can -be used in Chrome to debug Electron's main process, however, because -`node-inspector` relies on some native Node modules they must be rebuilt to -target the version of Electron you wish to debug. You can either rebuild -the `node-inspector` dependencies yourself, or let -[`electron-inspector`][electron-inspector] do it for you, both approaches are -covered in this document. - -**Note**: At the time of writing the latest release of `node-inspector` -(0.12.8) can't be rebuilt to target Electron 1.3.0 or later without patching -one of its dependencies. If you use `electron-inspector` it will take care of -this for you. - - -## Use `electron-inspector` for Debugging - -### 1. Install the [node-gyp required tools][node-gyp-required-tools] - -### 2. Install [`electron-rebuild`][electron-rebuild], if you haven't done so already. - -```shell -npm install electron-rebuild --save-dev -``` - -### 3. Install [`electron-inspector`][electron-inspector] - -```shell -npm install electron-inspector --save-dev -``` - -### 4. Start Electron - -Launch Electron with the `--debug` switch: - -```shell -electron --debug=5858 your/app -``` - -or, to pause execution on the first line of JavaScript: - -```shell -electron --debug-brk=5858 your/app -``` - -### 5. Start electron-inspector - -On macOS / Linux: - -```shell -node_modules/.bin/electron-inspector -``` - -On Windows: - -```shell -node_modules\\.bin\\electron-inspector -``` - -`electron-inspector` will need to rebuild `node-inspector` dependencies on the -first run, and any time you change your Electron version. The rebuild process -may require an internet connection to download Node headers and libs, and may -take a few minutes. - -### 6. Load the debugger UI - -Open http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 in the Chrome -browser. You may have to click pause if starting with `--debug-brk` to force -the UI to update. - - -## Use `node-inspector` for Debugging - -### 1. Install the [node-gyp required tools][node-gyp-required-tools] - -### 2. Install [`node-inspector`][node-inspector] - -```bash -$ npm install node-inspector -``` - -### 3. Install [`node-pre-gyp`][node-pre-gyp] - -```bash -$ npm install node-pre-gyp -``` - -### 4. Recompile the `node-inspector` `v8` modules for Electron - -**Note:** Update the target argument to be your Electron version number - -```bash -$ node_modules/.bin/node-pre-gyp --target=1.2.5 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall -$ node_modules/.bin/node-pre-gyp --target=1.2.5 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall -``` - -See also [How to install native modules][how-to-install-native-modules]. - -### 5. Enable debug mode for Electron - -You can either start Electron with a debug flag like: - -```bash -$ electron --debug=5858 your/app -``` - -or, to pause your script on the first line: - -```bash -$ electron --debug-brk=5858 your/app -``` - -### 6. Start the [`node-inspector`][node-inspector] server using Electron - -```bash -$ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin/inspector.js -``` - -### 7. Load the debugger UI - -Open http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 in the Chrome -browser. You may have to click pause if starting with `--debug-brk` to see the -entry line. - -[electron-inspector]: https://github.com/enlight/electron-inspector -[electron-rebuild]: https://github.com/electron/electron-rebuild -[node-inspector]: https://github.com/node-inspector/node-inspector -[node-pre-gyp]: https://github.com/mapbox/node-pre-gyp -[node-gyp-required-tools]: https://github.com/nodejs/node-gyp#installation -[how-to-install-native-modules]: using-native-node-modules.md#how-to-install-native-modules diff --git a/docs/tutorial/debugging-main-process.md b/docs/tutorial/debugging-main-process.md index fb5759e4995..a30760671d0 100644 --- a/docs/tutorial/debugging-main-process.md +++ b/docs/tutorial/debugging-main-process.md @@ -3,31 +3,31 @@ The DevTools in an Electron browser window can only debug JavaScript that's executed in that window (i.e. the web pages). To debug JavaScript that's executed in the main process you will need to use an external debugger and -launch Electron with the `--debug` or `--debug-brk` switch. +launch Electron with the `--inspector` or `--inspector-brk` switch. ## Command Line Switches Use one of the following command line switches to enable debugging of the main process: -### `--debug=[port]` +### `--inspect=[port]` -Electron will listen for V8 debugger protocol messages on the specified `port`, +Electron will listen for V8 inspector protocol messages on the specified `port`, an external debugger will need to connect on this port. The default `port` is `5858`. ```shell -electron --debug=5858 your/app +electron --inspect=5858 your/app ``` -### `--debug-brk=[port]` +### `--inspect-brk=[port]` -Like `--debug` but pauses execution on the first line of JavaScript. +Like `--inspector` but pauses execution on the first line of JavaScript. ## External Debuggers -You will need to use a debugger that supports the V8 debugger protocol, -the following guides should help you to get started: +You will need to use a debugger that supports the V8 inspector protocol. +- Connect Chrome by visiting `chrome://inspect` and selecting to inspect the + launched Electron app present there. - [Debugging the Main Process in VSCode](debugging-main-process-vscode.md) -- [Debugging the Main Process in node-inspector](debugging-main-process-node-inspector.md) diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index f490b738891..9a13715fad6 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -192,10 +192,10 @@ $ .\node_modules\.bin\electron . If you downloaded Electron manually, you can also use the included binary to execute your app directly. -#### Windows +#### macOS ```bash -$ .\electron\electron.exe your-app\ +$ ./Electron.app/Contents/MacOS/Electron your-app/ ``` #### Linux @@ -204,10 +204,10 @@ $ .\electron\electron.exe your-app\ $ ./electron/electron your-app/ ``` -#### macOS +#### Windows ```bash -$ ./Electron.app/Contents/MacOS/Electron your-app/ +$ .\electron\electron.exe your-app\ ``` `Electron.app` here is part of the Electron's release package, you can download diff --git a/docs/tutorial/windows-store-guide.md b/docs/tutorial/windows-store-guide.md index abf96431810..6bcefe40402 100644 --- a/docs/tutorial/windows-store-guide.md +++ b/docs/tutorial/windows-store-guide.md @@ -109,7 +109,7 @@ In order to run your package, your users will need Windows 10 with the so-called In opposition to traditional UWP apps, packaged apps currently need to undergo a manual verification process, for which you can apply [here][centennial-campaigns]. In the meantime, all users will be able to just install your package by double-clicking it, -so a submission to the store might not be necessary if you're simply looking for an +so a submission to the store might not be necessary if you're simply looking for an easier installation method. In managed environments (usually enterprises), the `Add-AppxPackage` [PowerShell Cmdlet can be used to install it in an automated fashion][add-appxpackage]. diff --git a/electron.gyp b/electron.gyp index f960fd99f1a..e0c91c151fd 100644 --- a/electron.gyp +++ b/electron.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '1.6.8', + 'version%': '1.7.1', 'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c', }, 'includes': [ @@ -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,13 +220,14 @@ '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': [ # We need to access internal implementations of Node. 'NODE_WANT_INTERNALS=1', 'NODE_SHARED_MODE', + 'HAVE_INSPECTOR=1', # This is defined in skia/skia_common.gypi. 'SK_SUPPORT_LEGACY_GETTOPDEVICE', # Disable warnings for g_settings_list_schemas. @@ -237,9 +238,6 @@ 'USING_V8_SHARED', 'USING_V8_PLATFORM_SHARED', 'USING_V8_BASE_SHARED', - # Remove this after enable_plugins becomes a feature flag. - 'ENABLE_PLUGINS', - 'USE_PROPRIETARY_CODECS', ], 'sources': [ '<@(lib_sources)', @@ -247,7 +245,7 @@ 'include_dirs': [ '.', 'chromium_src', - 'vendor/brightray', + 'brightray', 'vendor/native_mate', # Include atom_natives.h. '<(SHARED_INTERMEDIATE_DIR)', @@ -274,7 +272,7 @@ ], }, 'export_dependent_settings': [ - 'vendor/brightray/brightray.gyp:brightray', + 'brightray/brightray.gyp:brightray', ], 'conditions': [ ['libchromiumcontent_component', { diff --git a/filenames.gypi b/filenames.gypi index 8408b273e66..570d20ab10c 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -202,6 +202,8 @@ 'atom/browser/browser_mac.mm', 'atom/browser/browser_win.cc', 'atom/browser/browser_observer.h', + 'atom/browser/child_web_contents_tracker.cc', + 'atom/browser/child_web_contents_tracker.h', 'atom/browser/common_web_contents_delegate_mac.mm', 'atom/browser/common_web_contents_delegate_views.cc', 'atom/browser/common_web_contents_delegate.cc', @@ -632,10 +634,6 @@ 'chromium_src/extensions/common/url_pattern.h', 'chromium_src/library_loaders/libspeechd_loader.cc', 'chromium_src/library_loaders/libspeechd.h', - 'chromium_src/net/test/embedded_test_server/stream_listen_socket.cc', - 'chromium_src/net/test/embedded_test_server/stream_listen_socket.h', - 'chromium_src/net/test/embedded_test_server/tcp_listen_socket.cc', - 'chromium_src/net/test/embedded_test_server/tcp_listen_socket.h', '<@(native_mate_files)', '<(SHARED_INTERMEDIATE_DIR)/atom_natives.h', '<(SHARED_INTERMEDIATE_DIR)/grit/pdf_viewer_resources_map.cc', diff --git a/lib/browser/guest-window-manager.js b/lib/browser/guest-window-manager.js index ecf4093dcf0..305da92f9e6 100644 --- a/lib/browser/guest-window-manager.js +++ b/lib/browser/guest-window-manager.js @@ -255,6 +255,11 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event const newGuest = event.newGuest if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) { if (newGuest !== undefined && newGuest !== null) { + if (options.webContents === newGuest.webContents) { + // the webContents is not changed, so set defaultPrevented to false to + // stop the callers of this event from destroying the webContents. + event.defaultPrevented = false + } event.returnValue = setupGuest(event.sender, frameName, newGuest, options) } else { event.returnValue = null diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index 8d543f2d7d3..815298d3d9d 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -394,6 +394,11 @@ ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, id) { objectsRegistry.remove(event.sender.getId(), id) }) +ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e) => { + objectsRegistry.clear(e.sender.getId()) + e.returnValue = null +}) + ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstanceId) { try { let guestViewManager = require('./guest-view-manager') diff --git a/lib/isolated_renderer/init.js b/lib/isolated_renderer/init.js index 943713d985b..1536ab10638 100644 --- a/lib/isolated_renderer/init.js +++ b/lib/isolated_renderer/init.js @@ -19,8 +19,8 @@ const ipcRenderer = { once () {} } -let {guestInstanceId, hiddenPage, openerId} = binding +let {guestInstanceId, hiddenPage, openerId, nativeWindowOpen} = binding if (guestInstanceId != null) guestInstanceId = parseInt(guestInstanceId) if (openerId != null) openerId = parseInt(openerId) -require('../renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage) +require('../renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, nativeWindowOpen) diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index 5e790133d38..fdc95cdb5cd 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -304,6 +304,10 @@ ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', function (event, id) { callbacksRegistry.remove(id) }) +process.on('exit', () => { + ipcRenderer.sendSync('ELECTRON_BROWSER_CONTEXT_RELEASE') +}) + // Get remote module. exports.require = function (module) { return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module)) diff --git a/lib/renderer/override.js b/lib/renderer/override.js index f31e9c0e8cd..fdeb6605a5c 100644 --- a/lib/renderer/override.js +++ b/lib/renderer/override.js @@ -4,5 +4,6 @@ const {ipcRenderer} = require('electron') const {guestInstanceId, openerId} = process const hiddenPage = process.argv.includes('--hidden-page') +const usesNativeWindowOpen = process.argv.includes('--native-window-open') -require('./window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage) +require('./window-setup')(ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen) diff --git a/lib/renderer/window-setup.js b/lib/renderer/window-setup.js index 99f7aef5423..b1187e96f18 100644 --- a/lib/renderer/window-setup.js +++ b/lib/renderer/window-setup.js @@ -106,7 +106,7 @@ const getHistoryOperation = function (ipcRenderer, ...args) { return ipcRenderer.sendSync('ELECTRON_SYNC_NAVIGATION_CONTROLLER', ...args) } -module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage) => { +module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen) => { if (guestInstanceId == null) { // Override default window.close. window.close = function () { @@ -114,16 +114,18 @@ module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage) => { } } - // Make the browser window or guest view emit "new-window" event. - window.open = function (url, frameName, features) { - if (url != null && url !== '') { - url = resolveURL(url) - } - const guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features)) - if (guestId != null) { - return getOrCreateProxy(ipcRenderer, guestId) - } else { - return null + if (!usesNativeWindowOpen) { + // Make the browser window or guest view emit "new-window" event. + window.open = function (url, frameName, features) { + if (url != null && url !== '') { + url = resolveURL(url) + } + const guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features)) + if (guestId != null) { + return getOrCreateProxy(ipcRenderer, guestId) + } else { + return null + } } } diff --git a/package.json b/package.json index af80571da52..e9e2dde9a7f 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "electron", - "version": "1.6.8", + "version": "1.7.1", "devDependencies": { "asar": "^0.11.0", "browserify": "^13.1.0", "electabul": "~0.0.4", - "electron-docs-linter": "^2.1.0", - "electron-typescript-definitions": "^1.0.1", + "electron-docs-linter": "^2.3.3", + "electron-typescript-definitions": "^1.2.5", "request": "*", "standard": "^8.4.0", "standard-markdown": "^2.1.1" @@ -40,6 +40,8 @@ "lint-py": "python ./script/pylint.py", "lint-api-docs-js": "standard-markdown docs && standard-markdown docs-translations", "lint-api-docs": "electron-docs-linter", + "create-api-json": "electron-docs-linter docs --outfile=out/electron-api.json --version=$npm_package_version", + "create-typescript-definitions": "npm run create-api-json && electron-typescript-definitions --in=out/electron-api.json --out=out/electron.d.ts", "preinstall": "node -e 'process.exit(0)'", "release": "./script/upload.py -p", "repl": "python ./script/start.py --interactive", diff --git a/script/bootstrap.py b/script/bootstrap.py index 1fd3fd83906..0feee9b5002 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) @@ -63,6 +64,7 @@ def main(): create_chrome_version_h() touch_config_gypi() run_update(defines, args.msvs) + create_node_headers() update_electron_modules('spec', args.target_arch) @@ -134,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): @@ -176,17 +182,21 @@ def update_node_modules(dirname, env=None): if os.environ.has_key('CI'): try: execute_stdout(args, env) + execute_stdout([NPM, 'rebuild'], env) except subprocess.CalledProcessError: pass else: execute_stdout(args, env) + execute_stdout([NPM, 'rebuild'], env) def update_electron_modules(dirname, target_arch): env = os.environ.copy() + version = get_electron_version() env['npm_config_arch'] = target_arch - env['npm_config_target'] = get_electron_version() - env['npm_config_disturl'] = 'https://atom.io/download/electron' + env['npm_config_target'] = version + env['npm_config_nodedir'] = os.path.join(SOURCE_ROOT, 'dist', + 'node-{0}'.format(version)) update_node_modules(dirname, env) @@ -220,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') @@ -260,5 +269,19 @@ def run_update(defines, msvs): execute_stdout(args) +def create_node_headers(): + execute_stdout([sys.executable, + os.path.join(SOURCE_ROOT, 'script', 'create-node-headers.py'), + '--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 e0a95f69a46..63d7514de7b 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 c63bdbbadee..d1ccaa252db 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 6c715522f8c..0c91dc06aea 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 9188ad8dca5..3c61f06ac06 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%'] @@ -150,11 +150,8 @@ def create_typescript_definitions(): env['PATH'] = os.path.pathsep.join([node_bin_dir, env['PATH']]) infile = os.path.relpath(os.path.join(DIST_DIR, 'electron-api.json')) outfile = os.path.relpath(os.path.join(DIST_DIR, 'electron.d.ts')) - tslintconfig = os.path.relpath(os.path.join(DIST_DIR, - '../node_modules/electron-typescript-definitions/tslint.json')) execute(['electron-typescript-definitions', '--in={0}'.format(infile), '--out={0}'.format(outfile)], env=env) - execute(['tslint', '--config', tslintconfig, outfile], env=env) def strip_binaries(): for binary in TARGET_BINARIES[PLATFORM]: diff --git a/script/create-node-headers.py b/script/create-node-headers.py new file mode 100755 index 00000000000..992cd165751 --- /dev/null +++ b/script/create-node-headers.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +import argparse +import os +import shutil +import sys +import tarfile + +from lib.util import safe_mkdir, scoped_cwd + + +SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +DIST_DIR = os.path.join(SOURCE_ROOT, 'dist') +NODE_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'node') +OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'R') + +HEADERS_SUFFIX = [ + '.h', + '.gypi', +] +HEADERS_DIRS = [ + 'src', + 'deps/http_parser', + 'deps/zlib', + 'deps/uv', + 'deps/npm', + 'deps/mdb_v8', +] +HEADERS_FILES = [ + 'common.gypi', + 'config.gypi', +] + + +def main(): + safe_mkdir(DIST_DIR) + + args = parse_args() + node_headers_dir = os.path.join(DIST_DIR, 'node-{0}'.format(args.version)) + iojs_headers_dir = os.path.join(DIST_DIR, 'iojs-{0}'.format(args.version)) + iojs2_headers_dir = os.path.join(DIST_DIR, + 'iojs-{0}-headers'.format(args.version)) + + copy_headers(node_headers_dir) + create_header_tarball(node_headers_dir) + copy_headers(iojs_headers_dir) + create_header_tarball(iojs_headers_dir) + copy_headers(iojs2_headers_dir) + create_header_tarball(iojs2_headers_dir) + + +def parse_args(): + parser = argparse.ArgumentParser(description='create node header tarballs') + parser.add_argument('-v', '--version', help='Specify the version', + required=True) + return parser.parse_args() + + +def copy_headers(dist_headers_dir): + safe_mkdir(dist_headers_dir) + + # Copy standard node headers from node. repository. + for include_path in HEADERS_DIRS: + abs_path = os.path.join(NODE_DIR, include_path) + for dirpath, _, filenames in os.walk(abs_path): + for filename in filenames: + extension = os.path.splitext(filename)[1] + if extension not in HEADERS_SUFFIX: + continue + copy_source_file(os.path.join(dirpath, filename), NODE_DIR, + dist_headers_dir) + for other_file in HEADERS_FILES: + copy_source_file(os.path.join(NODE_DIR, other_file), NODE_DIR, + dist_headers_dir) + + # Copy V8 headers from chromium's repository. + 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] + if extension not in HEADERS_SUFFIX: + continue + copy_source_file(os.path.join(dirpath, filename), src, + os.path.join(dist_headers_dir, 'deps')) + + +def create_header_tarball(dist_headers_dir): + target = dist_headers_dir + '.tar.gz' + with scoped_cwd(DIST_DIR): + tarball = tarfile.open(name=target, mode='w:gz') + tarball.add(os.path.relpath(dist_headers_dir)) + tarball.close() + + +def copy_source_file(source, start, destination): + relative = os.path.relpath(source, start=start) + final_destination = os.path.join(destination, relative) + safe_mkdir(os.path.dirname(final_destination)) + shutil.copy2(source, final_destination) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/script/dump-symbols.py b/script/dump-symbols.py index 76949e95aa9..fdc90b613f6 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 5818571089f..4a55bf1fb06 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -9,7 +9,7 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ 'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent' LIBCHROMIUMCONTENT_COMMIT = os.getenv('LIBCHROMIUMCONTENT_COMMIT') or \ - '4a0e32606e52c12c50c2e3a0973d015d8cdff494' + 'ecc5298428a02f8acb9af285c75e1491715ae4dd' PLATFORM = { 'cygwin': 'win32', @@ -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 35331eb9362..b4f08bc28f2 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-node-headers.py b/script/upload-node-headers.py index 230b08ed5ac..6c5163f7629 100755 --- a/script/upload-node-headers.py +++ b/script/upload-node-headers.py @@ -5,50 +5,17 @@ import glob import os import shutil import sys -import tarfile from lib.config import PLATFORM, get_target_arch, s3_config -from lib.util import execute, safe_mkdir, scoped_cwd, s3put +from lib.util import safe_mkdir, scoped_cwd, s3put SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) DIST_DIR = os.path.join(SOURCE_ROOT, 'dist') -NODE_DIR = os.path.join(SOURCE_ROOT, 'vendor', 'node') OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'R') -HEADERS_SUFFIX = [ - '.h', - '.gypi', -] -HEADERS_DIRS = [ - 'src', - 'deps/http_parser', - 'deps/zlib', - 'deps/uv', - 'deps/npm', - 'deps/mdb_v8', -] -HEADERS_FILES = [ - 'common.gypi', - 'config.gypi', -] - - def main(): - safe_mkdir(DIST_DIR) - args = parse_args() - node_headers_dir = os.path.join(DIST_DIR, 'node-{0}'.format(args.version)) - iojs_headers_dir = os.path.join(DIST_DIR, 'iojs-{0}'.format(args.version)) - iojs2_headers_dir = os.path.join(DIST_DIR, - 'iojs-{0}-headers'.format(args.version)) - - copy_headers(node_headers_dir) - create_header_tarball(node_headers_dir) - copy_headers(iojs_headers_dir) - create_header_tarball(iojs_headers_dir) - copy_headers(iojs2_headers_dir) - create_header_tarball(iojs2_headers_dir) # Upload node's headers to S3. bucket, access_key, secret_key = s3_config() @@ -62,50 +29,6 @@ def parse_args(): return parser.parse_args() -def copy_headers(dist_headers_dir): - safe_mkdir(dist_headers_dir) - - # Copy standard node headers from node. repository. - for include_path in HEADERS_DIRS: - abs_path = os.path.join(NODE_DIR, include_path) - for dirpath, _, filenames in os.walk(abs_path): - for filename in filenames: - extension = os.path.splitext(filename)[1] - if extension not in HEADERS_SUFFIX: - continue - copy_source_file(os.path.join(dirpath, filename), NODE_DIR, - dist_headers_dir) - for other_file in HEADERS_FILES: - copy_source_file(os.path.join(NODE_DIR, other_file), NODE_DIR, - dist_headers_dir) - - # Copy V8 headers from chromium's repository. - src = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', '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] - if extension not in HEADERS_SUFFIX: - continue - copy_source_file(os.path.join(dirpath, filename), src, - os.path.join(dist_headers_dir, 'deps')) - - -def create_header_tarball(dist_headers_dir): - target = dist_headers_dir + '.tar.gz' - with scoped_cwd(DIST_DIR): - tarball = tarfile.open(name=target, mode='w:gz') - tarball.add(os.path.relpath(dist_headers_dir)) - tarball.close() - - -def copy_source_file(source, start, destination): - relative = os.path.relpath(source, start=start) - final_destination = os.path.join(destination, relative) - safe_mkdir(os.path.dirname(final_destination)) - shutil.copy2(source, final_destination) - - def upload_node(bucket, access_key, secret_key, version): with scoped_cwd(DIST_DIR): s3put(bucket, access_key, secret_key, DIST_DIR, diff --git a/script/upload-windows-pdb.py b/script/upload-windows-pdb.py index 6e8030337f3..40c38b30739 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/upload.py b/script/upload.py index 7d689f6cae8..ea5e8eca3c2 100755 --- a/script/upload.py +++ b/script/upload.py @@ -102,6 +102,7 @@ def main(): run_python_script('upload-windows-pdb.py') # Upload node headers. + run_python_script('create-node-headers.py', '-v', args.version) run_python_script('upload-node-headers.py', '-v', args.version) diff --git a/script/verify-ffmpeg.py b/script/verify-ffmpeg.py index 9d9dba06703..1d78515a4ad 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/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 059aa908940..70ed7ae3aa8 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1178,7 +1178,6 @@ describe('BrowserWindow module', function () { } }) w.loadURL('file://' + path.join(fixtures, 'api', 'sandbox.html?allocate-memory')) - w.webContents.openDevTools({mode: 'detach'}) ipcMain.once('answer', function (event, {bytesBeforeOpen, bytesAfterOpen, bytesAfterClose}) { const memoryIncreaseByOpen = bytesAfterOpen - bytesBeforeOpen const memoryDecreaseByClose = bytesAfterOpen - bytesAfterClose @@ -1190,6 +1189,137 @@ describe('BrowserWindow module', function () { done() }) }) + + // see #9387 + it('properly manages remote object references after page reload', (done) => { + w.destroy() + w = new BrowserWindow({ + show: false, + webPreferences: { + preload: preload, + sandbox: true + } + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'sandbox.html?reload-remote')) + + ipcMain.on('get-remote-module-path', (event) => { + event.returnValue = path.join(fixtures, 'module', 'hello.js') + }) + + let reload = false + ipcMain.on('reloaded', (event) => { + event.returnValue = reload + reload = !reload + }) + + ipcMain.once('reload', (event) => { + event.sender.reload() + }) + + ipcMain.once('answer', (event, arg) => { + ipcMain.removeAllListeners('reloaded') + ipcMain.removeAllListeners('get-remote-module-path') + assert.equal(arg, 'hi') + done() + }) + }) + + it('properly manages remote object references after page reload in child window', (done) => { + w.destroy() + w = new BrowserWindow({ + show: false, + webPreferences: { + preload: preload, + sandbox: true + } + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'sandbox.html?reload-remote-child')) + + ipcMain.on('get-remote-module-path', (event) => { + event.returnValue = path.join(fixtures, 'module', 'hello-child.js') + }) + + let reload = false + ipcMain.on('reloaded', (event) => { + event.returnValue = reload + reload = !reload + }) + + ipcMain.once('reload', (event) => { + event.sender.reload() + }) + + ipcMain.once('answer', (event, arg) => { + ipcMain.removeAllListeners('reloaded') + ipcMain.removeAllListeners('get-remote-module-path') + assert.equal(arg, 'hi child window') + done() + }) + }) + }) + + describe('nativeWindowOpen option', () => { + beforeEach(() => { + w.destroy() + w = new BrowserWindow({ + show: false, + webPreferences: { + nativeWindowOpen: true + } + }) + }) + + it('opens window of about:blank with cross-scripting enabled', (done) => { + ipcMain.once('answer', (event, content) => { + assert.equal(content, 'Hello') + done() + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'native-window-open-blank.html')) + }) + + it('opens window of same domain with cross-scripting enabled', (done) => { + ipcMain.once('answer', (event, content) => { + assert.equal(content, 'Hello') + done() + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'native-window-open-file.html')) + }) + + if (process.platform !== 'win32' || process.execPath.toLowerCase().indexOf('\\out\\d\\') === -1) { + it('loads native addons correctly after reload', (done) => { + ipcMain.once('answer', (event, content) => { + assert.equal(content, 'function') + ipcMain.once('answer', (event, content) => { + assert.equal(content, 'function') + done() + }) + w.reload() + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'native-window-open-native-addon.html')) + }) + } + }) + }) + + describe('nativeWindowOpen + contextIsolation options', () => { + beforeEach(() => { + w.destroy() + w = new BrowserWindow({ + show: false, + webPreferences: { + nativeWindowOpen: true, + contextIsolation: true, + preload: path.join(fixtures, 'api', 'native-window-open-isolated-preload.js') + } + }) + }) + + it('opens window with cross-scripting enabled from isolated context', (done) => { + ipcMain.once('answer', (event, content) => { + assert.equal(content, 'Hello') + done() + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'native-window-open-isolated.html')) }) }) @@ -1214,6 +1344,60 @@ describe('BrowserWindow module', function () { }) w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-empty-string.html')) }) + + it('emits for each close attempt', function (done) { + var beforeUnloadCount = 0 + w.on('onbeforeunload', function () { + beforeUnloadCount++ + if (beforeUnloadCount < 3) { + w.close() + } else if (beforeUnloadCount === 3) { + done() + } + }) + w.webContents.once('did-finish-load', function () { + w.close() + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false-prevent3.html')) + }) + + it('emits for each reload attempt', function (done) { + var beforeUnloadCount = 0 + w.on('onbeforeunload', function () { + beforeUnloadCount++ + if (beforeUnloadCount < 3) { + w.reload() + } else if (beforeUnloadCount === 3) { + done() + } + }) + w.webContents.once('did-finish-load', function () { + w.webContents.once('did-finish-load', function () { + assert.fail('Reload was not prevented') + }) + w.reload() + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false-prevent3.html')) + }) + + it('emits for each navigation attempt', function (done) { + var beforeUnloadCount = 0 + w.on('onbeforeunload', function () { + beforeUnloadCount++ + if (beforeUnloadCount < 3) { + w.loadURL('about:blank') + } else if (beforeUnloadCount === 3) { + done() + } + }) + w.webContents.once('did-finish-load', function () { + w.webContents.once('did-finish-load', function () { + assert.fail('Navigation was not prevented') + }) + w.loadURL('about:blank') + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false-prevent3.html')) + }) }) describe('new-window event', function () { @@ -2171,6 +2355,8 @@ describe('BrowserWindow module', function () { beforeEach(function () { if (w != null) w.destroy() w = new BrowserWindow({ + width: 100, + height: 100, show: false, webPreferences: { backgroundThrottling: false, @@ -2179,9 +2365,12 @@ describe('BrowserWindow module', function () { }) }) - it('creates offscreen window', function (done) { - w.webContents.once('paint', function (event, rect, data, size) { + it('creates offscreen window with correct size', function (done) { + w.webContents.once('paint', function (event, rect, data) { assert.notEqual(data.length, 0) + let size = data.getSize() + assertWithinDelta(size.width, 100, 2, 'width') + assertWithinDelta(size.height, 100, 2, 'height') done() }) w.loadURL('file://' + fixtures + '/api/offscreen-rendering.html') @@ -2202,7 +2391,7 @@ describe('BrowserWindow module', function () { describe('window.webContents.isPainting()', function () { it('returns whether is currently painting', function (done) { - w.webContents.once('paint', function (event, rect, data, size) { + w.webContents.once('paint', function (event, rect, data) { assert.equal(w.webContents.isPainting(), true) done() }) @@ -2226,7 +2415,7 @@ describe('BrowserWindow module', function () { w.webContents.on('dom-ready', function () { w.webContents.stopPainting() w.webContents.startPainting() - w.webContents.once('paint', function (event, rect, data, size) { + w.webContents.once('paint', function (event, rect, data) { assert.equal(w.webContents.isPainting(), true) done() }) @@ -2237,7 +2426,7 @@ describe('BrowserWindow module', function () { describe('window.webContents.getFrameRate()', function () { it('has default frame rate', function (done) { - w.webContents.once('paint', function (event, rect, data, size) { + w.webContents.once('paint', function (event, rect, data) { assert.equal(w.webContents.getFrameRate(), 60) done() }) @@ -2249,7 +2438,7 @@ describe('BrowserWindow module', function () { it('sets custom frame rate', function (done) { w.webContents.on('dom-ready', function () { w.webContents.setFrameRate(30) - w.webContents.once('paint', function (event, rect, data, size) { + w.webContents.once('paint', function (event, rect, data) { assert.equal(w.webContents.getFrameRate(), 30) done() }) diff --git a/spec/api-crash-reporter-spec.js b/spec/api-crash-reporter-spec.js index 92020dbea62..a08cbaaa09d 100644 --- a/spec/api-crash-reporter-spec.js +++ b/spec/api-crash-reporter-spec.js @@ -9,6 +9,7 @@ const url = require('url') const {closeWindow} = require('./window-helpers') const {remote} = require('electron') +const isCI = remote.getGlobal('isCi') const {app, BrowserWindow, crashReporter} = remote.require('electron') describe('crashReporter module', function () { @@ -16,6 +17,12 @@ describe('crashReporter module', function () { return } + // FIXME internal Linux CI is failing when it detects a process crashes + // which is a false positive here since crashes are explicitly triggered + if (isCI && process.platform === 'linux') { + return + } + var originalTempDirectory = null var tempDirectory = null diff --git a/spec/api-web-contents-spec.js b/spec/api-web-contents-spec.js index 7eac9d3e0e9..9bbeefc4d7a 100644 --- a/spec/api-web-contents-spec.js +++ b/spec/api-web-contents-spec.js @@ -324,6 +324,20 @@ describe('webContents module', function () { }) }) + describe('getOSProcessId()', function () { + it('returns a valid procress id', function (done) { + assert.strictEqual(w.webContents.getOSProcessId(), 0) + + w.webContents.once('did-finish-load', () => { + const pid = w.webContents.getOSProcessId() + assert.equal(typeof pid, 'number') + assert(pid > 0, `pid ${pid} is not greater than 0`) + done() + }) + w.loadURL('about:blank') + }) + }) + describe('zoom api', () => { const zoomScheme = remote.getGlobal('zoomScheme') const hostZoomMap = { @@ -543,6 +557,31 @@ describe('webContents module', function () { }) }) + describe('will-prevent-unload event', function () { + it('does not emit if beforeunload returns undefined', function (done) { + w.once('closed', function () { + done() + }) + w.webContents.on('will-prevent-unload', function (e) { + assert.fail('should not have fired') + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-undefined.html')) + }) + + it('emits if beforeunload returns false', (done) => { + w.webContents.on('will-prevent-unload', () => { + done() + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')) + }) + + it('supports calling preventDefault on will-prevent-unload events', function (done) { + ipcRenderer.send('prevent-next-will-prevent-unload', w.webContents.id) + w.once('closed', () => done()) + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')) + }) + }) + describe('destroy()', () => { let server diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 1302fbbc433..8274caf0213 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -149,8 +149,6 @@ describe('chromium feature', function () { }) describe('navigator.serviceWorker', function () { - var url = 'file://' + fixtures + '/pages/service-worker/index.html' - it('should register for file scheme', function (done) { w = new BrowserWindow({ show: false @@ -169,7 +167,49 @@ describe('chromium feature', function () { }) } }) - w.loadURL(url) + w.loadURL(`file://${fixtures}/pages/service-worker/index.html`) + }) + + it('should register for intercepted file scheme', function (done) { + const customSession = session.fromPartition('intercept-file') + customSession.protocol.interceptBufferProtocol('file', function (request, callback) { + let file = url.parse(request.url).pathname + // Remove leading slash before drive letter on Windows + if (file[0] === '/' && process.platform === 'win32') { + file = file.slice(1) + } + const content = fs.readFileSync(path.normalize(file)) + const ext = path.extname(file) + let type = 'text/html' + if (ext === '.js') { + type = 'application/javascript' + } + callback({data: content, mimeType: type}) + }, function (error) { + if (error) done(error) + }) + + w = new BrowserWindow({ + show: false, + webPreferences: { + session: customSession + } + }) + w.webContents.on('ipc-message', function (event, args) { + if (args[0] === 'reload') { + w.webContents.reload() + } else if (args[0] === 'error') { + done('unexpected error : ' + args[1]) + } else if (args[0] === 'response') { + assert.equal(args[1], 'Hello from serviceWorker!') + customSession.clearStorageData({ + storages: ['serviceworkers'] + }, function () { + customSession.protocol.uninterceptProtocol('file', (error) => done(error)) + }) + } + }) + w.loadURL(`file://${fixtures}/pages/service-worker/index.html`) }) }) diff --git a/spec/fixtures/api/beforeunload-false-prevent3.html b/spec/fixtures/api/beforeunload-false-prevent3.html new file mode 100644 index 00000000000..6ed2a7d1aa4 --- /dev/null +++ b/spec/fixtures/api/beforeunload-false-prevent3.html @@ -0,0 +1,17 @@ + + + + + diff --git a/spec/fixtures/api/native-window-open-blank.html b/spec/fixtures/api/native-window-open-blank.html new file mode 100644 index 00000000000..7174594e244 --- /dev/null +++ b/spec/fixtures/api/native-window-open-blank.html @@ -0,0 +1,11 @@ + + + + + diff --git a/spec/fixtures/api/native-window-open-child.html b/spec/fixtures/api/native-window-open-child.html new file mode 100644 index 00000000000..986a4a1a25b --- /dev/null +++ b/spec/fixtures/api/native-window-open-child.html @@ -0,0 +1 @@ +

Hello

diff --git a/spec/fixtures/api/native-window-open-file.html b/spec/fixtures/api/native-window-open-file.html new file mode 100644 index 00000000000..30f2c61c9e0 --- /dev/null +++ b/spec/fixtures/api/native-window-open-file.html @@ -0,0 +1,12 @@ + + + + + diff --git a/spec/fixtures/api/native-window-open-isolated-preload.js b/spec/fixtures/api/native-window-open-isolated-preload.js new file mode 100644 index 00000000000..aa214166021 --- /dev/null +++ b/spec/fixtures/api/native-window-open-isolated-preload.js @@ -0,0 +1,5 @@ +const {ipcRenderer} = require('electron') + +window.addEventListener('message', (event) => { + ipcRenderer.send('answer', event.data) +}) diff --git a/spec/fixtures/api/native-window-open-isolated.html b/spec/fixtures/api/native-window-open-isolated.html new file mode 100644 index 00000000000..706209f1908 --- /dev/null +++ b/spec/fixtures/api/native-window-open-isolated.html @@ -0,0 +1,10 @@ + + + + + diff --git a/spec/fixtures/api/native-window-open-native-addon.html b/spec/fixtures/api/native-window-open-native-addon.html new file mode 100644 index 00000000000..c1f75872ef9 --- /dev/null +++ b/spec/fixtures/api/native-window-open-native-addon.html @@ -0,0 +1,9 @@ + + + + + diff --git a/spec/fixtures/api/sandbox.html b/spec/fixtures/api/sandbox.html index f8d7aa7921d..5353d8815a7 100644 --- a/spec/fixtures/api/sandbox.html +++ b/spec/fixtures/api/sandbox.html @@ -13,13 +13,28 @@ await timeout(100) } } - if (window.opener) { + + const [,test] = window.location.href.split('?') + if (window.opener && test !== 'reload-remote') { window.callback = () => { opener.require('electron').ipcRenderer.send('answer', document.body.innerHTML) } } else { - const {ipcRenderer} = require('electron') + const {ipcRenderer, remote} = require('electron') const tests = { + 'reload-remote-child': () => { + open(`${location.protocol}//${location.pathname}?reload-remote`) + }, + 'reload-remote': async () => { + const p = ipcRenderer.sendSync('get-remote-module-path') + const Hello = remote.require(p) + if (!ipcRenderer.sendSync('reloaded')) { + ipcRenderer.send('reload') + return + } + await invokeGc() + ipcRenderer.send('answer', new Hello().say()) + }, 'allocate-memory': async () => { await invokeGc() const {privateBytes: bytesBeforeOpen} = process.getProcessMemoryInfo() @@ -95,7 +110,6 @@ popup.close() }, false) - let [,test] = window.location.href.split('?') if (tests.hasOwnProperty(test)) tests[test]() } diff --git a/spec/fixtures/module/hello-child.js b/spec/fixtures/module/hello-child.js new file mode 100644 index 00000000000..09ac18900e1 --- /dev/null +++ b/spec/fixtures/module/hello-child.js @@ -0,0 +1,6 @@ +class Hello { + say () { + return 'hi child window' + } +} +module.exports = Hello diff --git a/spec/fixtures/module/hello.js b/spec/fixtures/module/hello.js new file mode 100644 index 00000000000..9debd60618e --- /dev/null +++ b/spec/fixtures/module/hello.js @@ -0,0 +1,6 @@ +class Hello { + say () { + return 'hi' + } +} +module.exports = Hello diff --git a/spec/fixtures/module/preload-sandbox.js b/spec/fixtures/module/preload-sandbox.js index 15d6e06a862..f8f88339852 100644 --- a/spec/fixtures/module/preload-sandbox.js +++ b/spec/fixtures/module/preload-sandbox.js @@ -3,9 +3,9 @@ const {ipcRenderer} = require('electron') window.ipcRenderer = ipcRenderer window.setImmediate = setImmediate + window.require = require if (location.protocol === 'file:') { window.test = 'preload' - window.require = require window.process = process } else if (location.href !== 'about:blank') { addEventListener('DOMContentLoaded', () => { diff --git a/spec/static/main.js b/spec/static/main.js index ba31b61ae07..fcfd08b1aea 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -266,6 +266,10 @@ ipcMain.on('prevent-next-will-attach-webview', (event) => { event.sender.once('will-attach-webview', event => event.preventDefault()) }) +ipcMain.on('prevent-next-will-prevent-unload', (event, id) => { + webContents.fromId(id).once('will-prevent-unload', event => event.preventDefault()) +}) + ipcMain.on('disable-node-on-next-will-attach-webview', (event, id) => { event.sender.once('will-attach-webview', (event, webPreferences, params) => { params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}` diff --git a/tools/mac/change_mach_o_flags.py b/tools/mac/change_mach_o_flags.py new file mode 100755 index 00000000000..4547cb75f7a --- /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 00000000000..ee4f21105b4 --- /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 909c4926549..00000000000 --- a/vendor/brightray +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 909c49265493bd095c27cefd999567be2107899a diff --git a/vendor/crashpad b/vendor/crashpad index eeac857dfb5..2871326543e 160000 --- a/vendor/crashpad +++ b/vendor/crashpad @@ -1 +1 @@ -Subproject commit eeac857dfb5b255c899c8763d62654863b4c8890 +Subproject commit 2871326543ea0e01488f034bb660bf5a39f55c0c diff --git a/vendor/gyp b/vendor/gyp new file mode 160000 index 00000000000..549e55a2276 --- /dev/null +++ b/vendor/gyp @@ -0,0 +1 @@ +Subproject commit 549e55a22765ccf99e46b419b2dff6338bcac64a diff --git a/vendor/libchromiumcontent b/vendor/libchromiumcontent new file mode 160000 index 00000000000..ecc5298428a --- /dev/null +++ b/vendor/libchromiumcontent @@ -0,0 +1 @@ +Subproject commit ecc5298428a02f8acb9af285c75e1491715ae4dd diff --git a/vendor/native_mate b/vendor/native_mate index fd0e7dc4ab7..7d9c1a80f02 160000 --- a/vendor/native_mate +++ b/vendor/native_mate @@ -1 +1 @@ -Subproject commit fd0e7dc4ab778f0d1ccda6c9640464ea06ee771e +Subproject commit 7d9c1a80f025f4c46f7da8ea73246fe0f1968579 diff --git a/vendor/node b/vendor/node index 3fe90cfcf54..3cdf4532cf3 160000 --- a/vendor/node +++ b/vendor/node @@ -1 +1 @@ -Subproject commit 3fe90cfcf54dd946980e59daf550a7cdb2317c8f +Subproject commit 3cdf4532cf3da20af37e57cbbd59eb860be233aa