From b1f98f2be2a9b791754eaebbefe4ffa599aa8c0b Mon Sep 17 00:00:00 2001 From: ananta Date: Fri, 26 Sep 2014 15:32:12 -0700 Subject: The metro_driver should not block the UI thread while waiting for the Chrome browser IPC channel to be initialized. This causes the taskbar to become unresponsive for a bit on Windows 7. Windows 8 also has the same problem. However it is not as visible as on Windows 7. Fix is to do the IPC channel dance in a timer. BUG=417100 R=cpu Review URL: https://codereview.chromium.org/593353003 Cr-Commit-Position: refs/heads/master@{#297058} --- win8/metro_driver/chrome_app_view_ash.cc | 172 +++++++++++++++++++++---------- win8/metro_driver/chrome_app_view_ash.h | 17 ++- 2 files changed, 134 insertions(+), 55 deletions(-) diff --git a/win8/metro_driver/chrome_app_view_ash.cc b/win8/metro_driver/chrome_app_view_ash.cc index db2d9f3..8c4df51 100644 --- a/win8/metro_driver/chrome_app_view_ash.cc +++ b/win8/metro_driver/chrome_app_view_ash.cc @@ -14,7 +14,6 @@ #include "base/files/file_path.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" -#include "base/threading/thread.h" #include "base/win/metro.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" @@ -87,6 +86,8 @@ enum KeyModifier { ALT = 4 }; +const int kChromeChannelPollTimerMs = 100; + // Helper function to send keystrokes via the SendInput function. // mnemonic_char: The keystroke to be sent. // modifiers: Combination with Alt, Ctrl, Shift, etc. @@ -273,16 +274,6 @@ class ChromeChannelListener : public IPC::Listener { ChromeAppViewAsh* app_view_; }; -bool WaitForChromeIPCConnection(const std::string& channel_name) { - int ms_elapsed = 0; - while (!IPC::Channel::IsNamedServerInitialized(channel_name) && - ms_elapsed < 10000) { - ms_elapsed += 100; - Sleep(100); - } - return IPC::Channel::IsNamedServerInitialized(channel_name); -} - void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) { // We're entering a nested message loop, let's allow dispatching // tasks while we're in there. @@ -531,7 +522,8 @@ ChromeAppViewAsh::ChromeAppViewAsh() core_window_hwnd_(NULL), metro_dpi_scale_(0), win32_dpi_scale_(0), - last_cursor_(NULL) { + last_cursor_(NULL), + channel_listener_(NULL) { DVLOG(1) << __FUNCTION__; globals.previous_state = winapp::Activation::ApplicationExecutionState_NotRunning; @@ -678,52 +670,29 @@ ChromeAppViewAsh::Run() { CheckHR(hr, "Dispatcher failed."); // Create the IPC channel IO thread. It needs to out-live the ChannelProxy. - base::Thread io_thread("metro_IO_thread"); + io_thread_.reset(new base::Thread("metro_IO_thread")); base::Thread::Options options; options.message_loop_type = base::MessageLoop::TYPE_IO; - io_thread.StartWithOptions(options); - - // Start up Chrome and wait for the desired IPC server connection to exist. - WaitForChromeIPCConnection(win8::kMetroViewerIPCChannelName); + io_thread_->StartWithOptions(options); - // In Aura mode we create an IPC channel to the browser, then ask it to - // connect to us. ChromeChannelListener ui_channel_listener(&ui_loop_, this); - scoped_ptr channel = - IPC::ChannelProxy::Create(win8::kMetroViewerIPCChannelName, - IPC::Channel::MODE_NAMED_CLIENT, - &ui_channel_listener, - io_thread.message_loop_proxy()); - ui_channel_ = channel.get(); + channel_listener_ = &ui_channel_listener; - // Upon receipt of the MetroViewerHostMsg_SetTargetSurface message the - // browser will use D3D from the browser process to present to our Window. - ui_channel_->Send(new MetroViewerHostMsg_SetTargetSurface( - gfx::NativeViewId(core_window_hwnd_), win32_dpi_scale_)); - DVLOG(1) << "ICoreWindow sent " << core_window_hwnd_; + // We can't do anything until the Chrome browser IPC channel is initialized. + // Lazy initialization in a timer. + ui_loop_.PostDelayedTask(FROM_HERE, + base::Bind(base::IgnoreResult(&ChromeAppViewAsh::StartChromeOSMode), + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(kChromeChannelPollTimerMs)); - // Send an initial size message so that the Ash root window host gets sized - // correctly. - RECT rect = {0}; - ::GetWindowRect(core_window_hwnd_, &rect); - ui_channel_->Send( - new MetroViewerHostMsg_WindowSizeChanged(rect.right - rect.left, - rect.bottom - rect.top)); - - input_source_ = metro_driver::InputSource::Create(); - if (input_source_) { - input_source_->AddObserver(this); - // Send an initial input source. - OnInputSourceChanged(); - } - - // Start receiving IME popup window notifications. - metro_driver::AddImePopupObserver(this); - - // And post the task that'll do the inner Metro message pumping to it. + // Post the task that'll do the inner Metro message pumping to it. ui_loop_.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get())); ui_loop_.Run(); + io_thread_.reset(NULL); + ui_channel_.reset(NULL); + channel_listener_ = NULL; + DVLOG(0) << "ProcessEvents done, hr=" << hr; return hr; } @@ -976,6 +945,8 @@ void ChromeAppViewAsh::OnInputSourceChanged() { if (!input_source_) return; + DCHECK(ui_channel_); + LANGID langid = 0; bool is_ime = false; if (!input_source_->GetActiveSource(&langid, &is_ime)) { @@ -1006,6 +977,8 @@ void ChromeAppViewAsh::SendMouseButton(int x, uint32 flags, ui::EventFlags changed_button, bool is_horizontal_wheel) { + if (!ui_channel_) + return; MetroViewerHostMsg_MouseButtonParams params; params.x = static_cast(x); params.y = static_cast(y); @@ -1075,6 +1048,9 @@ HRESULT ChromeAppViewAsh::OnActivate( HRESULT ChromeAppViewAsh::OnPointerMoved(winui::Core::ICoreWindow* sender, winui::Core::IPointerEventArgs* args) { + if (!ui_channel_) + return S_OK; + PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_); HRESULT hr = pointer.Init(args); if (FAILED(hr)) @@ -1110,6 +1086,9 @@ HRESULT ChromeAppViewAsh::OnPointerMoved(winui::Core::ICoreWindow* sender, HRESULT ChromeAppViewAsh::OnPointerPressed( winui::Core::ICoreWindow* sender, winui::Core::IPointerEventArgs* args) { + if (!ui_channel_) + return S_OK; + PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_); HRESULT hr = pointer.Init(args); if (FAILED(hr)) @@ -1133,6 +1112,9 @@ HRESULT ChromeAppViewAsh::OnPointerPressed( HRESULT ChromeAppViewAsh::OnPointerReleased( winui::Core::ICoreWindow* sender, winui::Core::IPointerEventArgs* args) { + if (!ui_channel_) + return S_OK; + PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_); HRESULT hr = pointer.Init(args); if (FAILED(hr)) @@ -1158,6 +1140,9 @@ HRESULT ChromeAppViewAsh::OnPointerReleased( HRESULT ChromeAppViewAsh::OnWheel( winui::Core::ICoreWindow* sender, winui::Core::IPointerEventArgs* args) { + if (!ui_channel_) + return S_OK; + PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_); HRESULT hr = pointer.Init(args); if (FAILED(hr)) @@ -1172,6 +1157,9 @@ HRESULT ChromeAppViewAsh::OnWheel( HRESULT ChromeAppViewAsh::OnKeyDown( winui::Core::ICoreWindow* sender, winui::Core::IKeyEventArgs* args) { + if (!ui_channel_) + return S_OK; + winsys::VirtualKey virtual_key; HRESULT hr = args->get_VirtualKey(&virtual_key); if (FAILED(hr)) @@ -1191,6 +1179,9 @@ HRESULT ChromeAppViewAsh::OnKeyDown( HRESULT ChromeAppViewAsh::OnKeyUp( winui::Core::ICoreWindow* sender, winui::Core::IKeyEventArgs* args) { + if (!ui_channel_) + return S_OK; + winsys::VirtualKey virtual_key; HRESULT hr = args->get_VirtualKey(&virtual_key); if (FAILED(hr)) @@ -1210,6 +1201,9 @@ HRESULT ChromeAppViewAsh::OnKeyUp( HRESULT ChromeAppViewAsh::OnAcceleratorKeyDown( winui::Core::ICoreDispatcher* sender, winui::Core::IAcceleratorKeyEventArgs* args) { + if (!ui_channel_) + return S_OK; + winsys::VirtualKey virtual_key; HRESULT hr = args->get_VirtualKey(&virtual_key); if (FAILED(hr)) @@ -1264,6 +1258,9 @@ HRESULT ChromeAppViewAsh::OnAcceleratorKeyDown( HRESULT ChromeAppViewAsh::OnCharacterReceived( winui::Core::ICoreWindow* sender, winui::Core::ICharacterReceivedEventArgs* args) { + if (!ui_channel_) + return S_OK; + unsigned int char_code = 0; HRESULT hr = args->get_KeyCode(&char_code); if (FAILED(hr)) @@ -1284,6 +1281,9 @@ HRESULT ChromeAppViewAsh::OnCharacterReceived( HRESULT ChromeAppViewAsh::OnWindowActivated( winui::Core::ICoreWindow* sender, winui::Core::IWindowActivatedEventArgs* args) { + if (!ui_channel_) + return S_OK; + if (args) { winui::Core::CoreWindowActivationState state; HRESULT hr = args->get_WindowActivationState(&state); @@ -1358,18 +1358,19 @@ HRESULT ChromeAppViewAsh::HandleProtocolRequest( HRESULT ChromeAppViewAsh::OnEdgeGestureCompleted( winui::Input::IEdgeGesture* gesture, winui::Input::IEdgeGestureEventArgs* args) { - ui_channel_->Send(new MetroViewerHostMsg_EdgeGesture()); + if (ui_channel_) + ui_channel_->Send(new MetroViewerHostMsg_EdgeGesture()); return S_OK; } void ChromeAppViewAsh::OnSearchRequest(const base::string16& search_string) { - DCHECK(ui_channel_); - ui_channel_->Send(new MetroViewerHostMsg_SearchRequest(search_string)); + if (ui_channel_) + ui_channel_->Send(new MetroViewerHostMsg_SearchRequest(search_string)); } void ChromeAppViewAsh::OnNavigateToUrl(const base::string16& url) { - DCHECK(ui_channel_); - ui_channel_->Send(new MetroViewerHostMsg_OpenURL(url)); + if (ui_channel_) + ui_channel_->Send(new MetroViewerHostMsg_OpenURL(url)); } HRESULT ChromeAppViewAsh::OnSizeChanged(winui::Core::ICoreWindow* sender, @@ -1392,6 +1393,69 @@ HRESULT ChromeAppViewAsh::OnSizeChanged(winui::Core::ICoreWindow* sender, return S_OK; } +void ChromeAppViewAsh::StartChromeOSMode() { + static int ms_elapsed = 0; + + if (!IPC::Channel::IsNamedServerInitialized( + win8::kMetroViewerIPCChannelName) && ms_elapsed < 10000) { + ms_elapsed += 100; + ui_loop_.PostDelayedTask(FROM_HERE, + base::Bind(base::IgnoreResult(&ChromeAppViewAsh::StartChromeOSMode), + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(kChromeChannelPollTimerMs)); + return; + } + + if (!IPC::Channel::IsNamedServerInitialized( + win8::kMetroViewerIPCChannelName)) { + DVLOG(1) << "Failed to connect to chrome channel : " + << win8::kMetroViewerIPCChannelName; + DVLOG(1) << "Exiting. Elapsed time :" << ms_elapsed; + PostMessage(core_window_hwnd_, WM_CLOSE, 0, 0); + return; + } + + DVLOG(1) << "Found channel : " << win8::kMetroViewerIPCChannelName; + + DCHECK(channel_listener_); + + // In Aura mode we create an IPC channel to the browser, then ask it to + // connect to us. + ui_channel_ = + IPC::ChannelProxy::Create(win8::kMetroViewerIPCChannelName, + IPC::Channel::MODE_NAMED_CLIENT, + channel_listener_, + io_thread_->message_loop_proxy()); + DVLOG(1) << "Created channel proxy"; + + // Upon receipt of the MetroViewerHostMsg_SetTargetSurface message the + // browser will use D3D from the browser process to present to our Window. + ui_channel_->Send(new MetroViewerHostMsg_SetTargetSurface( + gfx::NativeViewId(core_window_hwnd_), + win32_dpi_scale_)); + DVLOG(1) << "ICoreWindow sent " << core_window_hwnd_; + + // Send an initial size message so that the Ash root window host gets sized + // correctly. + RECT rect = {0}; + ::GetWindowRect(core_window_hwnd_, &rect); + ui_channel_->Send( + new MetroViewerHostMsg_WindowSizeChanged(rect.right - rect.left, + rect.bottom - rect.top)); + + input_source_ = metro_driver::InputSource::Create(); + if (input_source_) { + input_source_->AddObserver(this); + // Send an initial input source. + OnInputSourceChanged(); + } + + // Start receiving IME popup window notifications. + metro_driver::AddImePopupObserver(this); + + DVLOG(1) << "Channel setup complete"; +} + /////////////////////////////////////////////////////////////////////////////// ChromeAppViewFactory::ChromeAppViewFactory( diff --git a/win8/metro_driver/chrome_app_view_ash.h b/win8/metro_driver/chrome_app_view_ash.h index ca0dad5..0a88fad 100644 --- a/win8/metro_driver/chrome_app_view_ash.h +++ b/win8/metro_driver/chrome_app_view_ash.h @@ -13,6 +13,8 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/string16.h" +#include "base/threading/thread.h" +#include "ipc/ipc_listener.h" #include "ui/events/event_constants.h" #include "win8/metro_driver/direct3d_helper.h" #include "win8/metro_driver/ime/ime_popup_observer.h" @@ -196,6 +198,12 @@ class ChromeAppViewAsh HRESULT OnSizeChanged(winui::Core::ICoreWindow* sender, winui::Core::IWindowSizeChangedEventArgs* args); + // This function checks if the Chrome browser channel is initialized. If yes + // then it goes ahead and starts up the viewer in Chrome OS mode. If not it + // posts a delayed task and checks again. It does this for a duration of 10 + // seconds and then bails. + void StartChromeOSMode(); + mswr::ComPtr window_; mswr::ComPtr view_; EventRegistrationToken activated_token_; @@ -220,8 +228,11 @@ class ChromeAppViewAsh // Set the D3D swap chain and nothing else. metro_driver::Direct3DHelper direct3d_helper_; + // The IPC channel IO thread. + scoped_ptr io_thread_; + // The channel to Chrome, in particular to the MetroViewerProcessHost. - IPC::ChannelProxy* ui_channel_; + scoped_ptr ui_channel_; // The actual window behind the view surface. HWND core_window_hwnd_; @@ -241,6 +252,10 @@ class ChromeAppViewAsh // The cursor set by the chroem browser process. HCURSOR last_cursor_; + + // Pointer to the channel listener for the channel between the viewer and + // the browser. + IPC::Listener* channel_listener_; }; #endif // WIN8_METRO_DRIVER_CHROME_APP_VIEW_ASH_H_ -- cgit v1.1