diff options
author | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-07 19:17:08 +0000 |
---|---|---|
committer | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-07 19:17:08 +0000 |
commit | 5b963d0f04a1e4baf5bf9a371890bbb698520f7d (patch) | |
tree | 9c020c289fa550d28b6b67789ee199a77ef759fb /win8/metro_driver/chrome_app_view.cc | |
parent | d13928d51c251bdcc76b961c1ad8bb9339da9917 (diff) | |
download | chromium_src-5b963d0f04a1e4baf5bf9a371890bbb698520f7d.zip chromium_src-5b963d0f04a1e4baf5bf9a371890bbb698520f7d.tar.gz chromium_src-5b963d0f04a1e4baf5bf9a371890bbb698520f7d.tar.bz2 |
Integrate the Windows 8 code into the Chromium tree (take 2).
This time, also: fix invalid path in metro_driver.gyp. Also, set executable bit on check_sdk_patch.py and post_build.bat.
This reverts "Revert 155429 - Integrate the Windows 8 code into the Chromium tree."
Original CL: https://chromiumcodereview.appspot.com/10875008
BUG=127799
TEST=A Chromium build can run as the immersive browser on Windows 8.
TBR=ben
Review URL: https://chromiumcodereview.appspot.com/10912152
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@155444 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'win8/metro_driver/chrome_app_view.cc')
-rw-r--r-- | win8/metro_driver/chrome_app_view.cc | 1059 |
1 files changed, 1059 insertions, 0 deletions
diff --git a/win8/metro_driver/chrome_app_view.cc b/win8/metro_driver/chrome_app_view.cc new file mode 100644 index 0000000..7d5580e --- /dev/null +++ b/win8/metro_driver/chrome_app_view.cc @@ -0,0 +1,1059 @@ +// 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 "win8/metro_driver/stdafx.h" +#include "win8/metro_driver/chrome_app_view.h" + +#include <algorithm> +#include <windows.applicationModel.datatransfer.h> +#include <windows.foundation.h> + +#include "base/bind.h" +#include "base/message_loop.h" +#include "base/win/metro.h" + +// This include allows to send WM_SYSCOMMANDs to chrome. +#include "chrome/app/chrome_command_ids.h" +#include "win8/metro_driver/winrt_utils.h" +#include "ui/base/ui_base_switches.h" + +typedef winfoundtn::ITypedEventHandler< + winapp::Core::CoreApplicationView*, + winapp::Activation::IActivatedEventArgs*> ActivatedHandler; + +typedef winfoundtn::ITypedEventHandler< + winui::Core::CoreWindow*, + winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler; + +typedef winfoundtn::ITypedEventHandler< + winui::Input::EdgeGesture*, + winui::Input::EdgeGestureEventArgs*> EdgeEventHandler; + +typedef winfoundtn::ITypedEventHandler< + winapp::DataTransfer::DataTransferManager*, + winapp::DataTransfer::DataRequestedEventArgs*> ShareDataRequestedHandler; + +typedef winfoundtn::ITypedEventHandler< + winui::ViewManagement::InputPane*, + winui::ViewManagement::InputPaneVisibilityEventArgs*> + InputPaneEventHandler; + +struct Globals globals; + +// TODO(ananta) +// Remove this once we consolidate metro driver with chrome. +const wchar_t kMetroGetCurrentTabInfoMessage[] = + L"CHROME_METRO_GET_CURRENT_TAB_INFO"; + +static const int kFlipWindowsHotKeyId = 0x0000baba; + +static const int kAnimateWindowTimeoutMs = 200; + +static const int kCheckOSKDelayMs = 300; + +const wchar_t kOSKClassName[] = L"IPTip_Main_Window"; + +static const int kOSKAdjustmentOffset = 20; + +namespace { + +void AdjustToFitWindow(HWND hwnd, int flags) { + RECT rect = {0}; + ::GetWindowRect(globals.core_window, &rect); + int cx = rect.right - rect.left; + int cy = rect.bottom - rect.top; + + ::SetWindowPos(hwnd, HWND_TOP, + rect.left, rect.top, cx, cy, + SWP_NOZORDER | flags); +} + +void AdjustFrameWindowStyleForMetro(HWND hwnd) { + DVLOG(1) << __FUNCTION__; + // Ajust the frame so the live preview works and the frame buttons dissapear. + ::SetWindowLong(hwnd, GWL_STYLE, + WS_POPUP | (::GetWindowLong(hwnd, GWL_STYLE) & + ~(WS_MAXIMIZE | WS_CAPTION | WS_THICKFRAME | WS_SYSMENU))); + ::SetWindowLong(hwnd, GWL_EXSTYLE, + ::GetWindowLong(hwnd, GWL_EXSTYLE) & ~(WS_EX_DLGMODALFRAME | + WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); + AdjustToFitWindow(hwnd, SWP_FRAMECHANGED | SWP_NOACTIVATE); +} + +void SetFrameWindowInternal(HWND hwnd) { + DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd); + + HWND current_top_frame = + !globals.host_windows.empty() ? globals.host_windows.front().first : NULL; + if (hwnd != current_top_frame && IsWindow(current_top_frame)) { + DVLOG(1) << "Hiding current top window, hwnd=" + << LONG_PTR(current_top_frame); + ::ShowWindow(current_top_frame, SW_HIDE); + } + + // If chrome opens a url in a foreground tab, it may call SetFrameWindow + // again. Ensure that we don't have dups. + globals.host_windows.remove_if([hwnd](std::pair<HWND, bool>& item) { + return (item.first == hwnd); + }); + + globals.host_windows.push_front(std::make_pair(hwnd, false)); + + AdjustFrameWindowStyleForMetro(hwnd); +} + +void CloseFrameWindowInternal(HWND hwnd) { + DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd); + + globals.host_windows.remove_if([hwnd](std::pair<HWND, bool>& item) { + return (item.first == hwnd); + }); + + if (globals.host_windows.size() > 0) { + DVLOG(1) << "Making new top frame window visible:" + << reinterpret_cast<int>(globals.host_windows.front().first); + AdjustToFitWindow(globals.host_windows.front().first, SWP_SHOWWINDOW); + } else { + // time to quit + DVLOG(1) << "Last host window closed. Calling Exit()."; + globals.app_exit->Exit(); + } +} + +void FlipFrameWindowsInternal() { + DVLOG(1) << __FUNCTION__; + // Get the first window in the frame windows queue and push it to the end. + // Metroize the next window in the queue. + if (globals.host_windows.size() > 1) { + std::pair<HWND, bool> current_top_window = globals.host_windows.front(); + globals.host_windows.pop_front(); + + DVLOG(1) << "Making new top frame window visible:" + << reinterpret_cast<int>(globals.host_windows.front().first); + + AdjustToFitWindow(globals.host_windows.front().first, SWP_SHOWWINDOW); + + DVLOG(1) << "Hiding current top window:" + << reinterpret_cast<int>(current_top_window.first); + AnimateWindow(current_top_window.first, kAnimateWindowTimeoutMs, + AW_HIDE | AW_HOR_POSITIVE | AW_SLIDE); + + globals.host_windows.push_back(current_top_window); + } +} + +} // namespace + +HRESULT ChromeAppView::TileRequestCreateDone( + winfoundtn::IAsyncOperation<bool>* async, + AsyncStatus status) { + if (status == Completed) { + unsigned char result; + CheckHR(async->GetResults(&result)); + DVLOG(1) << __FUNCTION__ << " result " << static_cast<int>(result); + } else { + LOG(ERROR) << __FUNCTION__ << " Unexpected async status " << status; + } + + return S_OK; +} + +void ChromeAppView::DisplayNotification( + const ToastNotificationHandler::DesktopNotification& notification) { + DVLOG(1) << __FUNCTION__; + + if (IsValidNotification(notification.id)) { + NOTREACHED() << "Duplicate notification id passed in."; + return; + } + + base::AutoLock lock(notification_lock_); + + ToastNotificationHandler* notification_handler = + new ToastNotificationHandler; + + notification_map_[notification.id].reset(notification_handler); + notification_handler->DisplayNotification(notification); +} + +void ChromeAppView::CancelNotification(const std::string& notification) { + DVLOG(1) << __FUNCTION__; + + base::AutoLock lock(notification_lock_); + + NotificationMap::iterator index = notification_map_.find(notification); + if (index == notification_map_.end()) { + NOTREACHED() << "Invalid notification:" << notification.c_str(); + return; + } + + scoped_ptr<ToastNotificationHandler> notification_handler( + index->second.release()); + + notification_map_.erase(index); + + notification_handler->CancelNotification(); +} + +// Returns true if the notification passed in is valid. +bool ChromeAppView::IsValidNotification(const std::string& notification) { + DVLOG(1) << __FUNCTION__; + + base::AutoLock lock(notification_lock_); + return notification_map_.find(notification) != notification_map_.end(); +} + +void ChromeAppView::ShowDialogBox( + const MetroDialogBox::DialogBoxInfo& dialog_box_info) { + VLOG(1) << __FUNCTION__; + dialog_box_.Show(dialog_box_info); +} + +void ChromeAppView::DismissDialogBox() { + VLOG(1) << __FUNCTION__; + dialog_box_.Dismiss(); +} + +// static +HRESULT ChromeAppView::Unsnap() { + mswr::ComPtr<winui::ViewManagement::IApplicationViewStatics> view_statics; + HRESULT hr = winrt_utils::CreateActivationFactory( + RuntimeClass_Windows_UI_ViewManagement_ApplicationView, + view_statics.GetAddressOf()); + CheckHR(hr); + + winui::ViewManagement::ApplicationViewState state = + winui::ViewManagement::ApplicationViewState_FullScreenLandscape; + hr = view_statics->get_Value(&state); + CheckHR(hr); + + if (state == winui::ViewManagement::ApplicationViewState_Snapped) { + boolean success = FALSE; + hr = view_statics->TryUnsnap(&success); + + if (FAILED(hr) || !success) { + LOG(ERROR) << "Failed to unsnap. Error 0x" << hr; + if (SUCCEEDED(hr)) + hr = E_UNEXPECTED; + } + } + return hr; +} + +void ChromeAppView::SetFullscreen(bool fullscreen) { + VLOG(1) << __FUNCTION__; + + if (osk_offset_adjustment_) { + VLOG(1) << "Scrolling the window down by: " + << osk_offset_adjustment_; + + ::ScrollWindowEx(globals.host_windows.front().first, + 0, + osk_offset_adjustment_, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE | SW_SCROLLCHILDREN); + osk_offset_adjustment_ = 0; + } +} + +void UnsnapHelper() { + ChromeAppView::Unsnap(); +} + +extern "C" __declspec(dllexport) +void MetroUnsnap() { + DVLOG(1) << __FUNCTION__; + globals.appview_msg_loop->PostTask( + FROM_HERE, base::Bind(&UnsnapHelper)); +} + +extern "C" __declspec(dllexport) +HWND GetRootWindow() { + DVLOG(1) << __FUNCTION__; + return globals.core_window; +} + +extern "C" __declspec(dllexport) +void SetFrameWindow(HWND hwnd) { + DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd); + globals.appview_msg_loop->PostTask( + FROM_HERE, base::Bind(&SetFrameWindowInternal, hwnd)); +} + +// TODO(ananta) +// Handle frame window close by deleting it from the window list and making the +// next guy visible. +extern "C" __declspec(dllexport) + void CloseFrameWindow(HWND hwnd) { + DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd); + + // This is a hack to ensure that the BrowserViewLayout code layout happens + // just at the right time to hide the switcher button if it is visible. + globals.appview_msg_loop->PostDelayedTask( + FROM_HERE, base::Bind(&CloseFrameWindowInternal, hwnd), + base::TimeDelta::FromMilliseconds(50)); +} + +// Returns the initial url. This returns a valid url only if we were launched +// into metro via a url navigation. +extern "C" __declspec(dllexport) +const wchar_t* GetInitialUrl() { + DVLOG(1) << __FUNCTION__; + bool was_initial_activation = globals.is_initial_activation; + globals.is_initial_activation = false; + if (!was_initial_activation || globals.navigation_url.empty()) + return L""; + + const wchar_t* initial_url = globals.navigation_url.c_str(); + DVLOG(1) << initial_url; + return initial_url; +} + +// Returns the initial search string. This returns a valid url only if we were +// launched into metro via the search charm +extern "C" __declspec(dllexport) +const wchar_t* GetInitialSearchString() { + DVLOG(1) << __FUNCTION__; + bool was_initial_activation = globals.is_initial_activation; + globals.is_initial_activation = false; + if (!was_initial_activation || globals.search_string.empty()) + return L""; + + const wchar_t* initial_search_string = globals.search_string.c_str(); + DVLOG(1) << initial_search_string; + return initial_search_string; +} + +// Returns the launch type. +extern "C" __declspec(dllexport) +base::win::MetroLaunchType GetLaunchType( + base::win::MetroPreviousExecutionState* previous_state) { + if (previous_state) { + *previous_state = static_cast<base::win::MetroPreviousExecutionState>( + globals.previous_state); + } + return static_cast<base::win::MetroLaunchType>( + globals.initial_activation_kind); +} + +extern "C" __declspec(dllexport) +void FlipFrameWindows() { + DVLOG(1) << __FUNCTION__; + globals.appview_msg_loop->PostTask( + FROM_HERE, base::Bind(&FlipFrameWindowsInternal)); +} + +extern "C" __declspec(dllexport) +void DisplayNotification(const char* origin_url, const char* icon_url, + const wchar_t* title, const wchar_t* body, + const wchar_t* display_source, + const char* notification_id) { + // TODO(ananta) + // Needs implementation. + DVLOG(1) << __FUNCTION__; + + ToastNotificationHandler::DesktopNotification notification(origin_url, + icon_url, + title, + body, + display_source, + notification_id); + globals.appview_msg_loop->PostTask( + FROM_HERE, base::Bind(&ChromeAppView::DisplayNotification, + globals.view, notification)); +} + +extern "C" __declspec(dllexport) +bool CancelNotification(const char* notification_id) { + // TODO(ananta) + // Needs implementation. + DVLOG(1) << __FUNCTION__; + + if (!globals.view->IsValidNotification(notification_id)) { + NOTREACHED() << "Invalid notification id :" << notification_id; + return false; + } + + globals.appview_msg_loop->PostTask( + FROM_HERE, base::Bind(&ChromeAppView::CancelNotification, + globals.view, std::string(notification_id))); + return true; +} + +// Returns command line switches if any to be used by metro chrome. +extern "C" __declspec(dllexport) +const wchar_t* GetMetroCommandLineSwitches() { + DVLOG(1) << __FUNCTION__; + // The metro_command_line_switches field should be filled up once. + // ideally in ChromeAppView::Activate. + return globals.metro_command_line_switches.c_str(); +} + +// Provides functionality to display a metro style dialog box with two buttons. +// Only one dialog box can be displayed at any given time. +extern "C" __declspec(dllexport) +void ShowDialogBox( + const wchar_t* title, + const wchar_t* content, + const wchar_t* button1_label, + const wchar_t* button2_label, + base::win::MetroDialogButtonPressedHandler button1_handler, + base::win::MetroDialogButtonPressedHandler button2_handler) { + VLOG(1) << __FUNCTION__; + + DCHECK(title); + DCHECK(content); + DCHECK(button1_label); + DCHECK(button2_label); + DCHECK(button1_handler); + DCHECK(button2_handler); + + MetroDialogBox::DialogBoxInfo dialog_box_info; + dialog_box_info.title = title; + dialog_box_info.content = content; + dialog_box_info.button1_label = button1_label; + dialog_box_info.button2_label = button2_label; + dialog_box_info.button1_handler = button1_handler; + dialog_box_info.button2_handler = button2_handler; + + globals.appview_msg_loop->PostTask( + FROM_HERE, base::Bind( + &ChromeAppView::ShowDialogBox, globals.view, dialog_box_info)); +} + +// Provides functionality to dismiss the previously displayed metro style +// dialog box. +extern "C" __declspec(dllexport) +void DismissDialogBox() { + VLOG(1) << __FUNCTION__; + + globals.appview_msg_loop->PostTask( + FROM_HERE, base::Bind( + &ChromeAppView::DismissDialogBox, + globals.view)); +} + +extern "C" __declspec(dllexport) +void SetFullscreen(bool fullscreen) { + VLOG(1) << __FUNCTION__; + + globals.appview_msg_loop->PostTask( + FROM_HERE, base::Bind( + &ChromeAppView::SetFullscreen, + globals.view, fullscreen)); +} + +BOOL CALLBACK CoreWindowFinder(HWND hwnd, LPARAM) { + char classname[128]; + if (::GetClassNameA(hwnd, classname, ARRAYSIZE(classname))) { + if (lstrcmpiA("Windows.UI.Core.CoreWindow", classname) == 0) { + globals.core_window = hwnd; + return FALSE; + } + } + return TRUE; +} + +template <typename ContainerT> +void CloseSecondaryWindows(ContainerT& windows) { + DVLOG(1) << "Closing secondary windows", windows.size(); + std::for_each(windows.begin(), windows.end(), [](HWND hwnd) { + ::PostMessageW(hwnd, WM_CLOSE, 0, 0); + }); + windows.clear(); +} + +void EndChromeSession() { + DVLOG(1) << "Sending chrome WM_ENDSESSION window message."; + ::SendMessage(globals.host_windows.front().first, WM_ENDSESSION, FALSE, + ENDSESSION_CLOSEAPP); +} + +DWORD WINAPI HostMainThreadProc(void*) { + // Test feature - devs have requested the ability to easily add metro-chrome + // command line arguments. This is hard since shortcut arguments are ignored, + // by Metro, so we instead read them directly from the pinned taskbar + // shortcut. This may call Coinitialize and there is tell of badness + // occurring if CoInitialize is called on a metro thread. + globals.metro_command_line_switches = + winrt_utils::ReadArgumentsFromPinnedTaskbarShortcut(); + + globals.g_core_proc = reinterpret_cast<WNDPROC>( + ::SetWindowLong(globals.core_window, GWL_WNDPROC, + reinterpret_cast<long>(ChromeAppView::CoreWindowProc))); + DWORD exit_code = globals.host_main(globals.host_context); + DVLOG(1) << "host thread done, exit_code=" << exit_code; + globals.app_exit->Exit(); + return exit_code; +} + +ChromeAppView::ChromeAppView() + : osk_visible_notification_received_(false), + osk_offset_adjustment_(0) { + globals.previous_state = + winapp::Activation::ApplicationExecutionState_NotRunning; +} + +ChromeAppView::~ChromeAppView() { + DVLOG(1) << __FUNCTION__; +} + +IFACEMETHODIMP +ChromeAppView::Initialize(winapp::Core::ICoreApplicationView* view) { + view_ = view; + DVLOG(1) << __FUNCTION__; + globals.main_thread_id = ::GetCurrentThreadId(); + + HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>( + this, &ChromeAppView::OnActivate).Get(), + &activated_token_); + CheckHR(hr); + return hr; +} + +IFACEMETHODIMP +ChromeAppView::SetWindow(winui::Core::ICoreWindow* window) { + window_ = window; + DVLOG(1) << __FUNCTION__; + + HRESULT hr = url_launch_handler_.Initialize(); + CheckHR(hr, "Failed to initialize url launch handler."); + + // Register for size notifications. + hr = window_->add_SizeChanged(mswr::Callback<SizeChangedHandler>( + this, &ChromeAppView::OnSizeChanged).Get(), + &sizechange_token_); + CheckHR(hr); + + // Register for edge gesture notifications. + mswr::ComPtr<winui::Input::IEdgeGestureStatics> edge_gesture_statics; + hr = winrt_utils::CreateActivationFactory( + RuntimeClass_Windows_UI_Input_EdgeGesture, + edge_gesture_statics.GetAddressOf()); + CheckHR(hr, "Failed to activate IEdgeGestureStatics."); + + mswr::ComPtr<winui::Input::IEdgeGesture> edge_gesture; + hr = edge_gesture_statics->GetForCurrentView(&edge_gesture); + CheckHR(hr); + + hr = edge_gesture->add_Completed(mswr::Callback<EdgeEventHandler>( + this, &ChromeAppView::OnEdgeGestureCompleted).Get(), + &edgeevent_token_); + CheckHR(hr); + + // Register for share notifications. + mswr::ComPtr<winapp::DataTransfer::IDataTransferManagerStatics> + data_mgr_statics; + hr = winrt_utils::CreateActivationFactory( + RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager, + data_mgr_statics.GetAddressOf()); + CheckHR(hr, "Failed to activate IDataTransferManagerStatics."); + + mswr::ComPtr<winapp::DataTransfer::IDataTransferManager> data_transfer_mgr; + hr = data_mgr_statics->GetForCurrentView(&data_transfer_mgr); + CheckHR(hr, "Failed to get IDataTransferManager for current view."); + + hr = data_transfer_mgr->add_DataRequested( + mswr::Callback<ShareDataRequestedHandler>( + this, &ChromeAppView::OnShareDataRequested).Get(), + &share_data_requested_token_); + CheckHR(hr); + + // TODO(ananta) + // The documented InputPane notifications don't fire on Windows 8 in metro + // chrome. Uncomment this once we figure out why they don't fire. + // RegisterInputPaneNotifications(); + + hr = winrt_utils::CreateActivationFactory( + RuntimeClass_Windows_UI_ViewManagement_ApplicationView, + app_view_.GetAddressOf()); + CheckHR(hr); + + DVLOG(1) << "Created appview instance."; + + hr = devices_handler_.Initialize(window); + // Don't check or return the failure here, we need to let the app + // initialization succeed. Even if we won't be able to access devices + // we still want to allow the app to start. + LOG_IF(ERROR, FAILED(hr)) << "Failed to initialize devices handler."; + return S_OK; +} + +IFACEMETHODIMP +ChromeAppView::Load(HSTRING entryPoint) { + DVLOG(1) << __FUNCTION__; + return S_OK; +} + +void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) { + // We're entering a nested message loop, let's allow dispatching + // tasks while we're in there. + MessageLoop::current()->SetNestableTasksAllowed(true); + + // Enter main core message loop. There are several ways to exit it + // Nicely: + // 1 - User action like ALT-F4. + // 2 - Calling ICoreApplicationExit::Exit(). + // 3- Posting WM_CLOSE to the core window. + HRESULT hr = dispatcher->ProcessEvents( + winui::Core::CoreProcessEventsOption + ::CoreProcessEventsOption_ProcessUntilQuit); + + // Wind down the thread's chrome message loop. + MessageLoop::current()->Quit(); +} + +void ChromeAppView::CheckForOSKActivation() { + // Hack for checking if the OSK was displayed while we are in the foreground. + // The input pane notifications which are supposed to fire when the OSK is + // shown and hidden don't seem to be firing in Windows 8 metro for us. + // The current hack is supposed to workaround that issue till we figure it + // out. Logic is to find the OSK window and see if we are the foreground + // process. If yes then fire the notification once for when the OSK is shown + // and once for when it is hidden. + // TODO(ananta) + // Take this out when the documented input pane notifcation issues are + // addressed. + HWND osk = ::FindWindow(kOSKClassName, NULL); + if (::IsWindow(osk)) { + HWND foreground_window = ::GetForegroundWindow(); + if (globals.host_windows.size() > 0 && + foreground_window == globals.host_windows.front().first) { + RECT osk_rect = {0}; + ::GetWindowRect(osk, &osk_rect); + + if (::IsWindowVisible(osk) && ::IsWindowEnabled(osk)) { + if (!globals.view->osk_visible_notification_received()) { + DVLOG(1) << "Found KB window while we are in the forground."; + HandleInputPaneVisible(osk_rect); + } + } else if (osk_visible_notification_received()) { + DVLOG(1) << "KB window hidden while we are in the foreground."; + HandleInputPaneHidden(osk_rect); + } + } + } + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&ChromeAppView::CheckForOSKActivation, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); +} + +IFACEMETHODIMP +ChromeAppView::Run() { + DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(window_.Get()); + mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher; + HRESULT hr = window_->get_Dispatcher(&dispatcher); + CheckHR(hr, "Dispatcher failed."); + + hr = window_->Activate(); + if (SUCCEEDED(hr)) { + // TODO(cpu): Draw something here. + } else { + DVLOG(1) << "Activate failed, hr=" << hr; + } + + // Create a message loop to allow message passing into this thread. + MessageLoop msg_loop(MessageLoop::TYPE_UI); + + // Announce our message loop to the world. + globals.appview_msg_loop = msg_loop.message_loop_proxy(); + + // And post the task that'll do the inner Metro message pumping to it. + msg_loop.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get())); + + // Post the recurring task which checks for OSK activation in metro chrome. + // Please refer to the comments in the CheckForOSKActivation function for why + // this is needed. + // TODO(ananta) + // Take this out when the documented OSK notifications start working. + msg_loop.PostDelayedTask( + FROM_HERE, + base::Bind(&ChromeAppView::CheckForOSKActivation, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); + + msg_loop.Run(); + + globals.appview_msg_loop = NULL; + + DVLOG(0) << "ProcessEvents done, hr=" << hr; + + // We join here with chrome's main thread so that the chrome is not killed + // while a critical operation is still in progress. Now, if there are host + // windows active it is possible we end up stuck on the wait below therefore + // we tell chrome to close its windows. + if (!globals.host_windows.empty()) { + DVLOG(1) << "Chrome still has windows open!"; + EndChromeSession(); + } + DWORD wr = ::WaitForSingleObject(globals.host_thread, INFINITE); + if (wr != WAIT_OBJECT_0) { + DVLOG(1) << "Waiting for host thread failed : " << wr; + } + ::CloseHandle(globals.host_thread); + globals.host_thread = NULL; + + return hr; +} + +IFACEMETHODIMP +ChromeAppView::Uninitialize() { + DVLOG(1) << __FUNCTION__; + window_ = nullptr; + view_ = nullptr; + base::AutoLock lock(notification_lock_); + notification_map_.clear(); + return S_OK; +} + +HRESULT ChromeAppView::RegisterInputPaneNotifications() { + DVLOG(1) << __FUNCTION__; + + mswr::ComPtr<winui::ViewManagement::IInputPaneStatics> + input_pane_statics; + HRESULT hr = winrt_utils::CreateActivationFactory( + RuntimeClass_Windows_UI_ViewManagement_InputPane, + input_pane_statics.GetAddressOf()); + CheckHR(hr); + + hr = input_pane_statics->GetForCurrentView(&input_pane_); + CheckHR(hr); + DVLOG(1) << "Got input pane."; + + hr = input_pane_->add_Showing( + mswr::Callback<InputPaneEventHandler>( + this, &ChromeAppView::OnInputPaneVisible).Get(), + &input_pane_visible_token_); + CheckHR(hr); + + DVLOG(1) << "Added showing event handler for input pane", + input_pane_visible_token_.value; + + hr = input_pane_->add_Hiding( + mswr::Callback<InputPaneEventHandler>( + this, &ChromeAppView::OnInputPaneHiding).Get(), + &input_pane_hiding_token_); + CheckHR(hr); + + DVLOG(1) << "Added hiding event handler for input pane, value=" + << input_pane_hiding_token_.value; + return hr; +} + +HRESULT ChromeAppView::OnActivate(winapp::Core::ICoreApplicationView*, + winapp::Activation::IActivatedEventArgs* args) { + DVLOG(1) << __FUNCTION__; + + args->get_PreviousExecutionState(&globals.previous_state); + DVLOG(1) << "Previous Execution State: " << globals.previous_state; + + window_->Activate(); + url_launch_handler_.Activate(args); + + if (globals.previous_state == + winapp::Activation::ApplicationExecutionState_Running && + globals.host_thread) { + DVLOG(1) << "Already running. Skipping rest of OnActivate."; + return S_OK; + } + + do { + ::Sleep(10); + ::EnumThreadWindows(globals.main_thread_id, &CoreWindowFinder, 0); + } while (globals.core_window == NULL); + + DVLOG(1) << "CoreWindow found: " << std::hex << globals.core_window; + + if (!globals.host_thread) { + globals.host_thread = + ::CreateThread(NULL, 0, HostMainThreadProc, NULL, 0, NULL); + + if (!globals.host_thread) { + NOTREACHED() << "thread creation failed."; + return E_UNEXPECTED; + } + } + + if (RegisterHotKey(globals.core_window, kFlipWindowsHotKeyId, + MOD_CONTROL, VK_F12)) { + DVLOG(1) << "Registered flip window hotkey."; + } else { + VPLOG(1) << "Failed to register flip window hotkey."; + } + HRESULT hr = settings_handler_.Initialize(); + CheckHR(hr,"Failed to initialize settings handler."); + return hr; +} + +// We subclass the core window for moving the associated chrome window when the +// core window is moved around, typically in the snap view operation. The +// size changes are handled in the documented size changed event. +LRESULT CALLBACK ChromeAppView::CoreWindowProc( + HWND window, UINT message, WPARAM wp, LPARAM lp) { + + static const UINT kBrowserClosingMessage = + ::RegisterWindowMessage(L"DefaultBrowserClosing"); + + if (message == WM_WINDOWPOSCHANGED) { + WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lp); + if (!(pos->flags & SWP_NOMOVE)) { + DVLOG(1) << "WM_WINDOWPOSCHANGED. Moving the chrome window."; + globals.view->OnPositionChanged(pos->x, pos->y); + } + } else if (message == WM_HOTKEY && wp == kFlipWindowsHotKeyId) { + FlipFrameWindows(); + } else if (message == kBrowserClosingMessage) { + DVLOG(1) << "Received DefaultBrowserClosing window message."; + // Ensure that the view is uninitialized. The kBrowserClosingMessage + // means that the app is going to be terminated, i.e. the proper + // uninitialization sequence does not occur. + globals.view->Uninitialize(); + if (!globals.host_windows.empty()) { + EndChromeSession(); + } + } + return CallWindowProc(globals.g_core_proc, window, message, wp, lp); +} + +HRESULT ChromeAppView::OnSizeChanged(winui::Core::ICoreWindow* sender, + winui::Core::IWindowSizeChangedEventArgs* args) { + if (!globals.host_windows.size()) { + return S_OK; + } + + winfoundtn::Size size; + args->get_Size(&size); + + int cx = static_cast<int>(size.Width); + int cy = static_cast<int>(size.Height); + + if (!::SetWindowPos(globals.host_windows.front().first, NULL, 0, 0, cx, cy, + SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED)) { + DVLOG(1) << "SetWindowPos failed."; + } + DVLOG(1) << "size changed cx=" << cx; + DVLOG(1) << "size changed cy=" << cy; + + winui::ViewManagement::ApplicationViewState view_state = + winui::ViewManagement::ApplicationViewState_FullScreenLandscape; + app_view_->get_Value(&view_state); + + HWND top_level_frame = globals.host_windows.front().first; + if (view_state == winui::ViewManagement::ApplicationViewState_Snapped) { + DVLOG(1) << "Enabling metro snap mode."; + ::PostMessageW(top_level_frame, WM_SYSCOMMAND, IDC_METRO_SNAP_ENABLE, 0); + } else { + ::PostMessageW(top_level_frame, WM_SYSCOMMAND, IDC_METRO_SNAP_DISABLE, 0); + } + return S_OK; +} + +HRESULT ChromeAppView::OnPositionChanged(int x, int y) { + DVLOG(1) << __FUNCTION__; + + ::SetWindowPos(globals.host_windows.front().first, NULL, x, y, 0, 0, + SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE); + return S_OK; +} + +HRESULT ChromeAppView::OnEdgeGestureCompleted( + winui::Input::IEdgeGesture* gesture, + winui::Input::IEdgeGestureEventArgs* args) { + DVLOG(1) << "edge gesture completed."; + + winui::ViewManagement::ApplicationViewState view_state = + winui::ViewManagement::ApplicationViewState_FullScreenLandscape; + app_view_->get_Value(&view_state); + // We don't want fullscreen chrome unless we are fullscreen metro. + if ((view_state == winui::ViewManagement::ApplicationViewState_Filled) || + (view_state == winui::ViewManagement::ApplicationViewState_Snapped)) { + DVLOG(1) << "No full screen in snapped view state:" << view_state; + return S_OK; + } + + // Deactivate anything pending, e.g., the wrench or a context menu. + BOOL success = ::ReleaseCapture(); + DCHECK(success) << "Couldn't ReleaseCapture() before going full screen"; + + DVLOG(1) << "Going full screen."; + ::PostMessageW(globals.host_windows.front().first, WM_SYSCOMMAND, + IDC_FULLSCREEN, 0); + return S_OK; +} + +HRESULT ChromeAppView::OnShareDataRequested( + winapp::DataTransfer::IDataTransferManager* data_transfer_mgr, + winapp::DataTransfer::IDataRequestedEventArgs* event_args) { + + DVLOG(1) << "Share data requested."; + + // The current tab info is retrieved from Chrome via a registered window + // message. + + static const UINT get_current_tab_info = + RegisterWindowMessage(kMetroGetCurrentTabInfoMessage); + + static const int kGetTabInfoTimeoutMs = 1000; + + mswr::ComPtr<winapp::DataTransfer::IDataRequest> data_request; + HRESULT hr = event_args->get_Request(&data_request); + CheckHR(hr); + + mswr::ComPtr<winapp::DataTransfer::IDataPackage> data_package; + hr = data_request->get_Data(&data_package); + CheckHR(hr); + + base::win::CurrentTabInfo current_tab_info; + current_tab_info.title = NULL; + current_tab_info.url = NULL; + + DWORD_PTR result = 0; + + if (!SendMessageTimeout(globals.host_windows.front().first, + get_current_tab_info, + reinterpret_cast<WPARAM>(¤t_tab_info), + 0, + SMTO_ABORTIFHUNG, + kGetTabInfoTimeoutMs, + &result)) { + VPLOG(1) << "Failed to retrieve tab info from chrome."; + return E_FAIL; + } + + if (!current_tab_info.title || !current_tab_info.url) { + DVLOG(1) << "Failed to retrieve tab info from chrome."; + return E_FAIL; + } + + string16 current_title(current_tab_info.title); + string16 current_url(current_tab_info.url); + + LocalFree(current_tab_info.title); + LocalFree(current_tab_info.url); + + mswr::ComPtr<winapp::DataTransfer::IDataPackagePropertySet> data_properties; + hr = data_package->get_Properties(&data_properties); + + mswrw::HString title; + title.Attach(MakeHString(current_title)); + data_properties->put_Title(title.Get()); + + mswr::ComPtr<winfoundtn::IUriRuntimeClassFactory> uri_factory; + hr = winrt_utils::CreateActivationFactory( + RuntimeClass_Windows_Foundation_Uri, + uri_factory.GetAddressOf()); + CheckHR(hr); + + mswrw::HString url; + url.Attach(MakeHString(current_url)); + mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri; + hr = uri_factory->CreateUri(url.Get(), &uri); + CheckHR(hr); + + hr = data_package->SetUri(uri.Get()); + CheckHR(hr); + + return S_OK; +} + +void ChromeAppView::HandleInputPaneVisible(const RECT& osk_rect) { + DCHECK(!osk_visible_notification_received_); + + DVLOG(1) << __FUNCTION__; + DVLOG(1) << "OSK width:" << osk_rect.right - osk_rect.left; + DVLOG(1) << "OSK height:" << osk_rect.bottom - osk_rect.top; + + globals.host_windows.front().second = false; + + POINT cursor_pos = {0}; + GetCursorPos(&cursor_pos); + + osk_offset_adjustment_ = 0; + + if (::PtInRect(&osk_rect, cursor_pos)) { + DVLOG(1) << "OSK covering focus point."; + int osk_height = osk_rect.bottom - osk_rect.top; + + osk_offset_adjustment_ = osk_height + kOSKAdjustmentOffset; + + DVLOG(1) << "Scrolling window by offset: " << osk_offset_adjustment_; + ::ScrollWindowEx(globals.host_windows.front().first, + 0, + -osk_offset_adjustment_, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE | SW_SCROLLCHILDREN); + + globals.host_windows.front().second = true; + } + osk_visible_notification_received_ = true; +} + +void ChromeAppView::HandleInputPaneHidden(const RECT& osk_rect) { + DCHECK(osk_visible_notification_received_); + DVLOG(1) << __FUNCTION__; + DVLOG(1) << "OSK width:" << osk_rect.right - osk_rect.left; + DVLOG(1) << "OSK height:" << osk_rect.bottom - osk_rect.top; + osk_visible_notification_received_ = false; + if (globals.host_windows.front().second == true) { + + if (osk_offset_adjustment_) { + DVLOG(1) << "Restoring scrolled window offset: " + << osk_offset_adjustment_; + + ::ScrollWindowEx(globals.host_windows.front().first, + 0, + osk_offset_adjustment_, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE | SW_SCROLLCHILDREN); + } + + globals.host_windows.front().second = false; + } +} + +HRESULT ChromeAppView::OnInputPaneVisible( + winui::ViewManagement::IInputPane* input_pane, + winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args) { + DVLOG(1) << __FUNCTION__; + return S_OK; +} + +HRESULT ChromeAppView::OnInputPaneHiding( + winui::ViewManagement::IInputPane* input_pane, + winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args) { + DVLOG(1) << __FUNCTION__; + return S_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +ChromeAppViewFactory::ChromeAppViewFactory( + winapp::Core::ICoreApplication* icore_app, + LPTHREAD_START_ROUTINE host_main, + void* host_context) { + globals.host_main = host_main; + globals.host_context = host_context; + mswr::ComPtr<winapp::Core::ICoreApplication> core_app(icore_app); + mswr::ComPtr<winapp::Core::ICoreApplicationExit> app_exit; + CheckHR(core_app.As(&app_exit)); + globals.app_exit = app_exit.Detach(); +} + +IFACEMETHODIMP +ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView** view) { + globals.view = mswr::Make<ChromeAppView>().Detach(); + *view = globals.view; + return (*view) ? S_OK : E_OUTOFMEMORY; +} |