| Index: chrome/browser/ui/metro_driver/chrome_app_view.cc
|
| diff --git a/chrome/browser/ui/metro_driver/chrome_app_view.cc b/chrome/browser/ui/metro_driver/chrome_app_view.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ee6f2892c6c8f530e063ff824ca28a4b4a1815c9
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/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 "chrome/browser/ui/metro_driver/stdafx.h"
|
| +#include "chrome/browser/ui/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 "chrome/browser/ui/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;
|
| +}
|
|
|