1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "win8/metro_driver/stdafx.h"
6 #include "win8/metro_driver/chrome_app_view.h"
8 #include <corewindow.h>
9 #include <windows.applicationModel.datatransfer.h>
10 #include <windows.foundation.h>
14 #include "base/bind.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/win/metro.h"
17 // This include allows to send WM_SYSCOMMANDs to chrome.
18 #include "chrome/app/chrome_command_ids.h"
19 #include "ui/base/ui_base_switches.h"
20 #include "ui/gfx/native_widget_types.h"
21 #include "ui/metro_viewer/metro_viewer_messages.h"
22 #include "win8/metro_driver/metro_driver.h"
23 #include "win8/metro_driver/winrt_utils.h"
25 typedef winfoundtn::ITypedEventHandler
<
26 winapp::Core::CoreApplicationView
*,
27 winapp::Activation::IActivatedEventArgs
*> ActivatedHandler
;
29 typedef winfoundtn::ITypedEventHandler
<
30 winui::Core::CoreWindow
*,
31 winui::Core::WindowSizeChangedEventArgs
*> SizeChangedHandler
;
33 typedef winfoundtn::ITypedEventHandler
<
34 winui::Input::EdgeGesture
*,
35 winui::Input::EdgeGestureEventArgs
*> EdgeEventHandler
;
37 typedef winfoundtn::ITypedEventHandler
<
38 winapp::DataTransfer::DataTransferManager
*,
39 winapp::DataTransfer::DataRequestedEventArgs
*> ShareDataRequestedHandler
;
41 typedef winfoundtn::ITypedEventHandler
<
42 winui::ViewManagement::InputPane
*,
43 winui::ViewManagement::InputPaneVisibilityEventArgs
*>
44 InputPaneEventHandler
;
46 typedef winfoundtn::ITypedEventHandler
<
47 winui::Core::CoreWindow
*,
48 winui::Core::PointerEventArgs
*> PointerEventHandler
;
50 typedef winfoundtn::ITypedEventHandler
<
51 winui::Core::CoreWindow
*,
52 winui::Core::KeyEventArgs
*> KeyEventHandler
;
54 struct Globals globals
;
57 // Remove this once we consolidate metro driver with chrome.
58 const wchar_t kMetroGetCurrentTabInfoMessage
[] =
59 L
"CHROME_METRO_GET_CURRENT_TAB_INFO";
61 static const int kFlipWindowsHotKeyId
= 0x0000baba;
63 static const int kAnimateWindowTimeoutMs
= 200;
65 static const int kCheckOSKDelayMs
= 300;
67 const wchar_t kOSKClassName
[] = L
"IPTip_Main_Window";
69 static const int kOSKAdjustmentOffset
= 20;
71 const wchar_t kChromeSubclassWindowProp
[] = L
"MetroChromeWindowProc";
82 // Helper function to send keystrokes via the SendInput function.
84 // mnemonic_char: The keystroke to be sent.
85 // modifiers: Combination with Alt, Ctrl, Shift, etc.
86 // extended: whether this is an extended key.
87 // unicode: whether this is a unicode key.
88 void SendMnemonic(WORD mnemonic_char
, Modifier modifiers
, bool extended
,
90 INPUT keys
[4] = {0}; // Keyboard events
91 int key_count
= 0; // Number of generated events
93 if (modifiers
& SHIFT
) {
94 keys
[key_count
].type
= INPUT_KEYBOARD
;
95 keys
[key_count
].ki
.wVk
= VK_SHIFT
;
96 keys
[key_count
].ki
.wScan
= MapVirtualKey(VK_SHIFT
, 0);
100 if (modifiers
& CONTROL
) {
101 keys
[key_count
].type
= INPUT_KEYBOARD
;
102 keys
[key_count
].ki
.wVk
= VK_CONTROL
;
103 keys
[key_count
].ki
.wScan
= MapVirtualKey(VK_CONTROL
, 0);
107 if (modifiers
& ALT
) {
108 keys
[key_count
].type
= INPUT_KEYBOARD
;
109 keys
[key_count
].ki
.wVk
= VK_MENU
;
110 keys
[key_count
].ki
.wScan
= MapVirtualKey(VK_MENU
, 0);
114 keys
[key_count
].type
= INPUT_KEYBOARD
;
115 keys
[key_count
].ki
.wVk
= mnemonic_char
;
116 keys
[key_count
].ki
.wScan
= MapVirtualKey(mnemonic_char
, 0);
119 keys
[key_count
].ki
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
121 keys
[key_count
].ki
.dwFlags
|= KEYEVENTF_UNICODE
;
124 bool should_sleep
= key_count
> 1;
127 for (int i
= 0; i
< key_count
; i
++) {
128 SendInput(1, &keys
[ i
], sizeof(keys
[0]));
129 keys
[i
].ki
.dwFlags
|= KEYEVENTF_KEYUP
;
135 // Now send key ups in reverse order
136 for (int i
= key_count
; i
; i
--) {
137 SendInput(1, &keys
[ i
- 1 ], sizeof(keys
[0]));
144 // Helper function to Exit metro chrome cleanly. If we are in the foreground
145 // then we try and exit by sending an Alt+F4 key combination to the core
146 // window which ensures that the chrome application tile does not show up in
147 // the running metro apps list on the top left corner. We have seen cases
148 // where this does work. To workaround that we invoke the
149 // ICoreApplicationExit::Exit function in a background delayed task which
150 // ensures that chrome exits.
151 void MetroExit(bool send_alt_f4_mnemonic
) {
152 if (send_alt_f4_mnemonic
&& globals
.view
&&
153 globals
.view
->core_window_hwnd() == ::GetForegroundWindow()) {
154 DVLOG(1) << "We are in the foreground. Exiting via Alt F4";
155 SendMnemonic(VK_F4
, ALT
, false, false);
156 DWORD core_window_process_id
= 0;
157 DWORD core_window_thread_id
= GetWindowThreadProcessId(
158 globals
.view
->core_window_hwnd(), &core_window_process_id
);
159 if (core_window_thread_id
!= ::GetCurrentThreadId()) {
160 globals
.appview_msg_loop
->PostDelayedTask(
162 base::Bind(&MetroExit
, false),
163 base::TimeDelta::FromMilliseconds(100));
166 globals
.app_exit
->Exit();
170 void AdjustToFitWindow(HWND hwnd
, int flags
) {
172 ::GetWindowRect(globals
.view
->core_window_hwnd() , &rect
);
173 int cx
= rect
.right
- rect
.left
;
174 int cy
= rect
.bottom
- rect
.top
;
176 ::SetWindowPos(hwnd
, HWND_TOP
,
177 rect
.left
, rect
.top
, cx
, cy
,
178 SWP_NOZORDER
| flags
);
181 LRESULT CALLBACK
ChromeWindowProc(HWND window
,
185 if (message
== WM_SETCURSOR
) {
186 // Override the WM_SETCURSOR message to avoid showing the resize cursor
187 // as the mouse moves to the edge of the screen.
188 switch (LOWORD(lp
)) {
197 lp
= MAKELPARAM(HTCLIENT
, HIWORD(lp
));
204 WNDPROC old_proc
= reinterpret_cast<WNDPROC
>(
205 ::GetProp(window
, kChromeSubclassWindowProp
));
207 return CallWindowProc(old_proc
, window
, message
, wp
, lp
);
210 void AdjustFrameWindowStyleForMetro(HWND hwnd
) {
211 DVLOG(1) << __FUNCTION__
;
212 // Ajust the frame so the live preview works and the frame buttons dissapear.
213 ::SetWindowLong(hwnd
, GWL_STYLE
,
214 WS_POPUP
| (::GetWindowLong(hwnd
, GWL_STYLE
) &
215 ~(WS_MAXIMIZE
| WS_CAPTION
| WS_THICKFRAME
| WS_SYSMENU
)));
216 ::SetWindowLong(hwnd
, GWL_EXSTYLE
,
217 ::GetWindowLong(hwnd
, GWL_EXSTYLE
) & ~(WS_EX_DLGMODALFRAME
|
218 WS_EX_WINDOWEDGE
| WS_EX_CLIENTEDGE
| WS_EX_STATICEDGE
));
220 // Subclass the wndproc of the frame window, if it's not already there.
221 if (::GetProp(hwnd
, kChromeSubclassWindowProp
) == NULL
) {
222 WNDPROC old_chrome_proc
=
223 reinterpret_cast<WNDPROC
>(::SetWindowLongPtr(
224 hwnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(ChromeWindowProc
)));
225 ::SetProp(hwnd
, kChromeSubclassWindowProp
, old_chrome_proc
);
227 AdjustToFitWindow(hwnd
, SWP_FRAMECHANGED
| SWP_NOACTIVATE
);
230 void SetFrameWindowInternal(HWND hwnd
) {
231 DVLOG(1) << __FUNCTION__
<< ", hwnd=" << LONG_PTR(hwnd
);
233 HWND current_top_frame
=
234 !globals
.host_windows
.empty() ? globals
.host_windows
.front().first
: NULL
;
235 if (hwnd
!= current_top_frame
&& IsWindow(current_top_frame
)) {
236 DVLOG(1) << "Hiding current top window, hwnd="
237 << LONG_PTR(current_top_frame
);
238 ::ShowWindow(current_top_frame
, SW_HIDE
);
241 // Visible frame windows always need to be at the head of the list.
242 // Check if the window being shown already exists in our global list.
243 // If no then add it at the head of the list.
244 // If yes, retrieve the osk window scrolled state, remove the window from the
245 // list and readd it at the head.
246 std::list
<std::pair
<HWND
, bool> >::iterator index
=
247 std::find_if(globals
.host_windows
.begin(), globals
.host_windows
.end(),
248 [hwnd
](std::pair
<HWND
, bool>& item
) {
249 return (item
.first
== hwnd
);
252 bool window_scrolled_state
= false;
253 bool new_window
= (index
== globals
.host_windows
.end());
255 window_scrolled_state
= index
->second
;
256 globals
.host_windows
.erase(index
);
259 globals
.host_windows
.push_front(std::make_pair(hwnd
, window_scrolled_state
));
262 AdjustFrameWindowStyleForMetro(hwnd
);
264 DVLOG(1) << "Adjusting new top window to core window size";
265 AdjustToFitWindow(hwnd
, 0);
267 if (globals
.view
->GetViewState() ==
268 winui::ViewManagement::ApplicationViewState_Snapped
) {
269 DVLOG(1) << "Enabling Metro snap state on new window: " << hwnd
;
270 ::PostMessageW(hwnd
, WM_SYSCOMMAND
, IDC_METRO_SNAP_ENABLE
, 0);
274 void CloseFrameWindowInternal(HWND hwnd
) {
275 DVLOG(1) << __FUNCTION__
<< ", hwnd=" << LONG_PTR(hwnd
);
277 globals
.host_windows
.remove_if([hwnd
](std::pair
<HWND
, bool>& item
) {
278 return (item
.first
== hwnd
);
281 if (globals
.host_windows
.size() > 0) {
282 DVLOG(1) << "Making new top frame window visible:"
283 << reinterpret_cast<int>(globals
.host_windows
.front().first
);
284 AdjustToFitWindow(globals
.host_windows
.front().first
, SWP_SHOWWINDOW
);
287 DVLOG(1) << "Last host window closed. Calling Exit().";
292 void FlipFrameWindowsInternal() {
293 DVLOG(1) << __FUNCTION__
;
294 // Get the first window in the frame windows queue and push it to the end.
295 // Metroize the next window in the queue.
296 if (globals
.host_windows
.size() > 1) {
297 std::pair
<HWND
, bool> current_top_window
= globals
.host_windows
.front();
298 globals
.host_windows
.pop_front();
300 DVLOG(1) << "Making new top frame window visible:"
301 << reinterpret_cast<int>(globals
.host_windows
.front().first
);
303 AdjustToFitWindow(globals
.host_windows
.front().first
, SWP_SHOWWINDOW
);
305 DVLOG(1) << "Hiding current top window:"
306 << reinterpret_cast<int>(current_top_window
.first
);
307 AnimateWindow(current_top_window
.first
, kAnimateWindowTimeoutMs
,
308 AW_HIDE
| AW_HOR_POSITIVE
| AW_SLIDE
);
310 globals
.host_windows
.push_back(current_top_window
);
316 void ChromeAppView::DisplayNotification(
317 const ToastNotificationHandler::DesktopNotification
& notification
) {
318 DVLOG(1) << __FUNCTION__
;
320 if (IsValidNotification(notification
.id
)) {
321 NOTREACHED() << "Duplicate notification id passed in.";
325 base::AutoLock
lock(notification_lock_
);
327 ToastNotificationHandler
* notification_handler
=
328 new ToastNotificationHandler
;
330 notification_map_
[notification
.id
].reset(notification_handler
);
331 notification_handler
->DisplayNotification(notification
);
334 void ChromeAppView::CancelNotification(const std::string
& notification
) {
335 DVLOG(1) << __FUNCTION__
;
337 base::AutoLock
lock(notification_lock_
);
339 NotificationMap::iterator index
= notification_map_
.find(notification
);
340 if (index
== notification_map_
.end()) {
341 NOTREACHED() << "Invalid notification:" << notification
.c_str();
345 scoped_ptr
<ToastNotificationHandler
> notification_handler(
346 index
->second
.release());
348 notification_map_
.erase(index
);
350 notification_handler
->CancelNotification();
353 // Returns true if the notification passed in is valid.
354 bool ChromeAppView::IsValidNotification(const std::string
& notification
) {
355 DVLOG(1) << __FUNCTION__
;
357 base::AutoLock
lock(notification_lock_
);
358 return notification_map_
.find(notification
) != notification_map_
.end();
361 void ChromeAppView::ShowDialogBox(
362 const MetroDialogBox::DialogBoxInfo
& dialog_box_info
) {
363 VLOG(1) << __FUNCTION__
;
364 dialog_box_
.Show(dialog_box_info
);
367 void ChromeAppView::DismissDialogBox() {
368 VLOG(1) << __FUNCTION__
;
369 dialog_box_
.Dismiss();
373 HRESULT
ChromeAppView::Unsnap() {
374 mswr::ComPtr
<winui::ViewManagement::IApplicationViewStatics
> view_statics
;
375 HRESULT hr
= winrt_utils::CreateActivationFactory(
376 RuntimeClass_Windows_UI_ViewManagement_ApplicationView
,
377 view_statics
.GetAddressOf());
380 winui::ViewManagement::ApplicationViewState state
=
381 winui::ViewManagement::ApplicationViewState_FullScreenLandscape
;
382 hr
= view_statics
->get_Value(&state
);
385 if (state
== winui::ViewManagement::ApplicationViewState_Snapped
) {
386 boolean success
= FALSE
;
387 hr
= view_statics
->TryUnsnap(&success
);
389 if (FAILED(hr
) || !success
) {
390 LOG(ERROR
) << "Failed to unsnap. Error 0x" << hr
;
398 void ChromeAppView::SetFullscreen(bool fullscreen
) {
399 VLOG(1) << __FUNCTION__
;
401 if (osk_offset_adjustment_
) {
402 VLOG(1) << "Scrolling the window down by: "
403 << osk_offset_adjustment_
;
405 ::ScrollWindowEx(globals
.host_windows
.front().first
,
407 osk_offset_adjustment_
,
412 SW_INVALIDATE
| SW_SCROLLCHILDREN
);
413 osk_offset_adjustment_
= 0;
417 winui::ViewManagement::ApplicationViewState
ChromeAppView::GetViewState() {
418 winui::ViewManagement::ApplicationViewState view_state
=
419 winui::ViewManagement::ApplicationViewState_FullScreenLandscape
;
420 app_view_
->get_Value(&view_state
);
424 void UnsnapHelper() {
425 ChromeAppView::Unsnap();
428 extern "C" __declspec(dllexport
)
430 DVLOG(1) << __FUNCTION__
;
431 globals
.appview_msg_loop
->PostTask(
432 FROM_HERE
, base::Bind(&UnsnapHelper
));
435 extern "C" __declspec(dllexport
)
436 HWND
GetRootWindow() {
437 DVLOG(1) << __FUNCTION__
;
438 return globals
.view
->core_window_hwnd();
441 extern "C" __declspec(dllexport
)
442 void SetFrameWindow(HWND hwnd
) {
443 DVLOG(1) << __FUNCTION__
<< ", hwnd=" << LONG_PTR(hwnd
);
444 globals
.appview_msg_loop
->PostTask(
445 FROM_HERE
, base::Bind(&SetFrameWindowInternal
, hwnd
));
449 // Handle frame window close by deleting it from the window list and making the
451 extern "C" __declspec(dllexport
)
452 void CloseFrameWindow(HWND hwnd
) {
453 DVLOG(1) << __FUNCTION__
<< ", hwnd=" << LONG_PTR(hwnd
);
455 // This is a hack to ensure that the BrowserViewLayout code layout happens
456 // just at the right time to hide the switcher button if it is visible.
457 globals
.appview_msg_loop
->PostDelayedTask(
458 FROM_HERE
, base::Bind(&CloseFrameWindowInternal
, hwnd
),
459 base::TimeDelta::FromMilliseconds(50));
462 // Returns the initial url. This returns a valid url only if we were launched
463 // into metro via a url navigation.
464 extern "C" __declspec(dllexport
)
465 const wchar_t* GetInitialUrl() {
466 DVLOG(1) << __FUNCTION__
;
467 bool was_initial_activation
= globals
.is_initial_activation
;
468 globals
.is_initial_activation
= false;
469 if (!was_initial_activation
|| globals
.navigation_url
.empty())
472 const wchar_t* initial_url
= globals
.navigation_url
.c_str();
473 DVLOG(1) << initial_url
;
477 // Returns the initial search string. This returns a valid url only if we were
478 // launched into metro via the search charm
479 extern "C" __declspec(dllexport
)
480 const wchar_t* GetInitialSearchString() {
481 DVLOG(1) << __FUNCTION__
;
482 bool was_initial_activation
= globals
.is_initial_activation
;
483 globals
.is_initial_activation
= false;
484 if (!was_initial_activation
|| globals
.search_string
.empty())
487 const wchar_t* initial_search_string
= globals
.search_string
.c_str();
488 DVLOG(1) << initial_search_string
;
489 return initial_search_string
;
492 // Returns the launch type.
493 extern "C" __declspec(dllexport
)
494 base::win::MetroLaunchType
GetLaunchType(
495 base::win::MetroPreviousExecutionState
* previous_state
) {
496 if (previous_state
) {
497 *previous_state
= static_cast<base::win::MetroPreviousExecutionState
>(
498 globals
.previous_state
);
500 return static_cast<base::win::MetroLaunchType
>(
501 globals
.initial_activation_kind
);
504 extern "C" __declspec(dllexport
)
505 void FlipFrameWindows() {
506 DVLOG(1) << __FUNCTION__
;
507 globals
.appview_msg_loop
->PostTask(
508 FROM_HERE
, base::Bind(&FlipFrameWindowsInternal
));
511 extern "C" __declspec(dllexport
)
512 void DisplayNotification(const char* origin_url
, const char* icon_url
,
513 const wchar_t* title
, const wchar_t* body
,
514 const wchar_t* display_source
,
515 const char* notification_id
,
516 base::win::MetroNotificationClickedHandler handler
,
517 const wchar_t* handler_context
) {
519 // Needs implementation.
520 DVLOG(1) << __FUNCTION__
;
522 ToastNotificationHandler::DesktopNotification
notification(origin_url
,
530 globals
.appview_msg_loop
->PostTask(
531 FROM_HERE
, base::Bind(&ChromeAppView::DisplayNotification
,
532 globals
.view
, notification
));
535 extern "C" __declspec(dllexport
)
536 bool CancelNotification(const char* notification_id
) {
538 // Needs implementation.
539 DVLOG(1) << __FUNCTION__
;
541 if (!globals
.view
->IsValidNotification(notification_id
)) {
542 NOTREACHED() << "Invalid notification id :" << notification_id
;
546 globals
.appview_msg_loop
->PostTask(
547 FROM_HERE
, base::Bind(&ChromeAppView::CancelNotification
,
548 globals
.view
, std::string(notification_id
)));
552 // Returns command line switches if any to be used by metro chrome.
553 extern "C" __declspec(dllexport
)
554 const wchar_t* GetMetroCommandLineSwitches() {
555 DVLOG(1) << __FUNCTION__
;
556 // The metro_command_line_switches field should be filled up once.
557 // ideally in ChromeAppView::Activate.
558 return globals
.metro_command_line_switches
.c_str();
561 // Provides functionality to display a metro style dialog box with two buttons.
562 // Only one dialog box can be displayed at any given time.
563 extern "C" __declspec(dllexport
)
565 const wchar_t* title
,
566 const wchar_t* content
,
567 const wchar_t* button1_label
,
568 const wchar_t* button2_label
,
569 base::win::MetroDialogButtonPressedHandler button1_handler
,
570 base::win::MetroDialogButtonPressedHandler button2_handler
) {
571 VLOG(1) << __FUNCTION__
;
575 DCHECK(button1_label
);
576 DCHECK(button2_label
);
577 DCHECK(button1_handler
);
578 DCHECK(button2_handler
);
580 MetroDialogBox::DialogBoxInfo dialog_box_info
;
581 dialog_box_info
.title
= title
;
582 dialog_box_info
.content
= content
;
583 dialog_box_info
.button1_label
= button1_label
;
584 dialog_box_info
.button2_label
= button2_label
;
585 dialog_box_info
.button1_handler
= button1_handler
;
586 dialog_box_info
.button2_handler
= button2_handler
;
588 globals
.appview_msg_loop
->PostTask(
589 FROM_HERE
, base::Bind(
590 &ChromeAppView::ShowDialogBox
, globals
.view
, dialog_box_info
));
593 // Provides functionality to dismiss the previously displayed metro style
595 extern "C" __declspec(dllexport
)
596 void DismissDialogBox() {
597 VLOG(1) << __FUNCTION__
;
599 globals
.appview_msg_loop
->PostTask(
600 FROM_HERE
, base::Bind(
601 &ChromeAppView::DismissDialogBox
,
605 extern "C" __declspec(dllexport
)
606 void SetFullscreen(bool fullscreen
) {
607 VLOG(1) << __FUNCTION__
;
609 globals
.appview_msg_loop
->PostTask(
610 FROM_HERE
, base::Bind(
611 &ChromeAppView::SetFullscreen
,
612 globals
.view
, fullscreen
));
615 template <typename ContainerT
>
616 void CloseSecondaryWindows(ContainerT
& windows
) {
617 DVLOG(1) << "Closing secondary windows", windows
.size();
618 std::for_each(windows
.begin(), windows
.end(), [](HWND hwnd
) {
619 ::PostMessageW(hwnd
, WM_CLOSE
, 0, 0);
624 void EndChromeSession() {
625 DVLOG(1) << "Sending chrome WM_ENDSESSION window message.";
626 ::SendMessage(globals
.host_windows
.front().first
, WM_ENDSESSION
, FALSE
,
627 ENDSESSION_CLOSEAPP
);
630 DWORD WINAPI
HostMainThreadProc(void*) {
631 // Test feature - devs have requested the ability to easily add metro-chrome
632 // command line arguments. This is hard since shortcut arguments are ignored,
633 // by Metro, so we instead read them directly from the pinned taskbar
634 // shortcut. This may call Coinitialize and there is tell of badness
635 // occurring if CoInitialize is called on a metro thread.
636 globals
.metro_command_line_switches
=
637 winrt_utils::ReadArgumentsFromPinnedTaskbarShortcut();
639 globals
.g_core_proc
=
640 reinterpret_cast<WNDPROC
>(::SetWindowLongPtr(
641 globals
.view
->core_window_hwnd(), GWLP_WNDPROC
,
642 reinterpret_cast<LONG_PTR
>(ChromeAppView::CoreWindowProc
)));
643 DWORD exit_code
= globals
.host_main(globals
.host_context
);
645 DVLOG(1) << "host thread done, exit_code=" << exit_code
;
650 ChromeAppView::ChromeAppView()
651 : osk_visible_notification_received_(false),
652 osk_offset_adjustment_(0),
653 core_window_hwnd_(NULL
) {
654 globals
.previous_state
=
655 winapp::Activation::ApplicationExecutionState_NotRunning
;
658 ChromeAppView::~ChromeAppView() {
659 DVLOG(1) << __FUNCTION__
;
663 ChromeAppView::Initialize(winapp::Core::ICoreApplicationView
* view
) {
665 DVLOG(1) << __FUNCTION__
;
667 HRESULT hr
= view_
->add_Activated(mswr::Callback
<ActivatedHandler
>(
668 this, &ChromeAppView::OnActivate
).Get(),
675 ChromeAppView::SetWindow(winui::Core::ICoreWindow
* window
) {
677 DVLOG(1) << __FUNCTION__
;
679 // Retrieve the native window handle via the interop layer.
680 mswr::ComPtr
<ICoreWindowInterop
> interop
;
681 HRESULT hr
= window
->QueryInterface(interop
.GetAddressOf());
683 hr
= interop
->get_WindowHandle(&core_window_hwnd_
);
686 hr
= url_launch_handler_
.Initialize();
687 CheckHR(hr
, "Failed to initialize url launch handler.");
688 // Register for size notifications.
689 hr
= window_
->add_SizeChanged(mswr::Callback
<SizeChangedHandler
>(
690 this, &ChromeAppView::OnSizeChanged
).Get(),
694 // Register for edge gesture notifications.
695 mswr::ComPtr
<winui::Input::IEdgeGestureStatics
> edge_gesture_statics
;
696 hr
= winrt_utils::CreateActivationFactory(
697 RuntimeClass_Windows_UI_Input_EdgeGesture
,
698 edge_gesture_statics
.GetAddressOf());
699 CheckHR(hr
, "Failed to activate IEdgeGestureStatics.");
701 mswr::ComPtr
<winui::Input::IEdgeGesture
> edge_gesture
;
702 hr
= edge_gesture_statics
->GetForCurrentView(&edge_gesture
);
705 hr
= edge_gesture
->add_Completed(mswr::Callback
<EdgeEventHandler
>(
706 this, &ChromeAppView::OnEdgeGestureCompleted
).Get(),
710 // Register for share notifications.
711 mswr::ComPtr
<winapp::DataTransfer::IDataTransferManagerStatics
>
713 hr
= winrt_utils::CreateActivationFactory(
714 RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager
,
715 data_mgr_statics
.GetAddressOf());
716 CheckHR(hr
, "Failed to activate IDataTransferManagerStatics.");
718 mswr::ComPtr
<winapp::DataTransfer::IDataTransferManager
> data_transfer_mgr
;
719 hr
= data_mgr_statics
->GetForCurrentView(&data_transfer_mgr
);
720 CheckHR(hr
, "Failed to get IDataTransferManager for current view.");
722 hr
= data_transfer_mgr
->add_DataRequested(
723 mswr::Callback
<ShareDataRequestedHandler
>(
724 this, &ChromeAppView::OnShareDataRequested
).Get(),
725 &share_data_requested_token_
);
729 // The documented InputPane notifications don't fire on Windows 8 in metro
730 // chrome. Uncomment this once we figure out why they don't fire.
731 // RegisterInputPaneNotifications();
732 hr
= winrt_utils::CreateActivationFactory(
733 RuntimeClass_Windows_UI_ViewManagement_ApplicationView
,
734 app_view_
.GetAddressOf());
737 DVLOG(1) << "Created appview instance.";
739 hr
= devices_handler_
.Initialize(window
);
740 // Don't check or return the failure here, we need to let the app
741 // initialization succeed. Even if we won't be able to access devices
742 // we still want to allow the app to start.
743 LOG_IF(ERROR
, FAILED(hr
)) << "Failed to initialize devices handler.";
748 ChromeAppView::Load(HSTRING entryPoint
) {
749 DVLOG(1) << __FUNCTION__
;
753 void RunMessageLoop(winui::Core::ICoreDispatcher
* dispatcher
) {
754 // We're entering a nested message loop, let's allow dispatching
755 // tasks while we're in there.
756 base::MessageLoop::current()->SetNestableTasksAllowed(true);
758 // Enter main core message loop. There are several ways to exit it
760 // 1 - User action like ALT-F4.
761 // 2 - Calling ICoreApplicationExit::Exit().
762 // 3- Posting WM_CLOSE to the core window.
763 HRESULT hr
= dispatcher
->ProcessEvents(
764 winui::Core::CoreProcessEventsOption
765 ::CoreProcessEventsOption_ProcessUntilQuit
);
767 // Wind down the thread's chrome message loop.
768 base::MessageLoop::current()->Quit();
771 void ChromeAppView::CheckForOSKActivation() {
772 // Hack for checking if the OSK was displayed while we are in the foreground.
773 // The input pane notifications which are supposed to fire when the OSK is
774 // shown and hidden don't seem to be firing in Windows 8 metro for us.
775 // The current hack is supposed to workaround that issue till we figure it
776 // out. Logic is to find the OSK window and see if we are the foreground
777 // process. If yes then fire the notification once for when the OSK is shown
778 // and once for when it is hidden.
780 // Take this out when the documented input pane notification issues are
782 HWND osk
= ::FindWindow(kOSKClassName
, NULL
);
783 if (::IsWindow(osk
)) {
784 HWND foreground_window
= ::GetForegroundWindow();
785 if (globals
.host_windows
.size() > 0 &&
786 foreground_window
== globals
.host_windows
.front().first
) {
788 ::GetWindowRect(osk
, &osk_rect
);
790 if (::IsWindowVisible(osk
) && ::IsWindowEnabled(osk
)) {
791 if (!globals
.view
->osk_visible_notification_received()) {
792 DVLOG(1) << "Found KB window while we are in the forground.";
793 HandleInputPaneVisible(osk_rect
);
795 } else if (osk_visible_notification_received()) {
796 DVLOG(1) << "KB window hidden while we are in the foreground.";
797 HandleInputPaneHidden(osk_rect
);
801 base::MessageLoop::current()->PostDelayedTask(
803 base::Bind(&ChromeAppView::CheckForOSKActivation
, base::Unretained(this)),
804 base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs
));
808 ChromeAppView::Run() {
809 DVLOG(1) << __FUNCTION__
;
810 mswr::ComPtr
<winui::Core::ICoreDispatcher
> dispatcher
;
811 HRESULT hr
= window_
->get_Dispatcher(&dispatcher
);
812 CheckHR(hr
, "Dispatcher failed.");
814 hr
= window_
->Activate();
816 // TODO(cpu): Draw something here.
818 DVLOG(1) << "Activate failed, hr=" << hr
;
821 // Create a message loop to allow message passing into this thread.
822 base::MessageLoopForUI msg_loop
;
824 // Announce our message loop to the world.
825 globals
.appview_msg_loop
= msg_loop
.message_loop_proxy();
827 // And post the task that'll do the inner Metro message pumping to it.
828 msg_loop
.PostTask(FROM_HERE
, base::Bind(&RunMessageLoop
, dispatcher
.Get()));
830 // Post the recurring task which checks for OSK activation in metro chrome.
831 // Please refer to the comments in the CheckForOSKActivation function for why
834 // Take this out when the documented OSK notifications start working.
835 msg_loop
.PostDelayedTask(
837 base::Bind(&ChromeAppView::CheckForOSKActivation
,
838 base::Unretained(this)),
839 base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs
));
843 globals
.appview_msg_loop
= NULL
;
845 DVLOG(0) << "ProcessEvents done, hr=" << hr
;
847 // We join here with chrome's main thread so that the chrome is not killed
848 // while a critical operation is still in progress. Now, if there are host
849 // windows active it is possible we end up stuck on the wait below therefore
850 // we tell chrome to close its windows.
851 if (!globals
.host_windows
.empty()) {
852 DVLOG(1) << "Chrome still has windows open!";
855 DWORD wr
= ::WaitForSingleObject(globals
.host_thread
, INFINITE
);
856 if (wr
!= WAIT_OBJECT_0
) {
857 DVLOG(1) << "Waiting for host thread failed : " << wr
;
860 DVLOG(1) << "Host thread exited";
862 ::CloseHandle(globals
.host_thread
);
863 globals
.host_thread
= NULL
;
868 ChromeAppView::Uninitialize() {
869 DVLOG(1) << __FUNCTION__
;
872 base::AutoLock
lock(notification_lock_
);
873 notification_map_
.clear();
877 HRESULT
ChromeAppView::RegisterInputPaneNotifications() {
878 DVLOG(1) << __FUNCTION__
;
880 mswr::ComPtr
<winui::ViewManagement::IInputPaneStatics
>
882 HRESULT hr
= winrt_utils::CreateActivationFactory(
883 RuntimeClass_Windows_UI_ViewManagement_InputPane
,
884 input_pane_statics
.GetAddressOf());
887 hr
= input_pane_statics
->GetForCurrentView(&input_pane_
);
889 DVLOG(1) << "Got input pane.";
891 hr
= input_pane_
->add_Showing(
892 mswr::Callback
<InputPaneEventHandler
>(
893 this, &ChromeAppView::OnInputPaneVisible
).Get(),
894 &input_pane_visible_token_
);
897 DVLOG(1) << "Added showing event handler for input pane",
898 input_pane_visible_token_
.value
;
900 hr
= input_pane_
->add_Hiding(
901 mswr::Callback
<InputPaneEventHandler
>(
902 this, &ChromeAppView::OnInputPaneHiding
).Get(),
903 &input_pane_hiding_token_
);
906 DVLOG(1) << "Added hiding event handler for input pane, value="
907 << input_pane_hiding_token_
.value
;
911 HRESULT
ChromeAppView::OnActivate(winapp::Core::ICoreApplicationView
*,
912 winapp::Activation::IActivatedEventArgs
* args
) {
913 DVLOG(1) << __FUNCTION__
;
915 args
->get_PreviousExecutionState(&globals
.previous_state
);
916 DVLOG(1) << "Previous Execution State: " << globals
.previous_state
;
919 url_launch_handler_
.Activate(args
);
921 if (globals
.previous_state
==
922 winapp::Activation::ApplicationExecutionState_Running
&&
923 globals
.host_thread
) {
924 DVLOG(1) << "Already running. Skipping rest of OnActivate.";
928 if (!globals
.host_thread
) {
929 DWORD chrome_ui_thread_id
= 0;
930 globals
.host_thread
=
931 ::CreateThread(NULL
, 0, HostMainThreadProc
, NULL
, 0,
932 &chrome_ui_thread_id
);
934 if (!globals
.host_thread
) {
935 NOTREACHED() << "thread creation failed.";
940 if (RegisterHotKey(core_window_hwnd_
, kFlipWindowsHotKeyId
,
941 MOD_CONTROL
, VK_F12
)) {
942 DVLOG(1) << "Registered flip window hotkey.";
944 VPLOG(1) << "Failed to register flip window hotkey.";
946 HRESULT hr
= settings_handler_
.Initialize();
947 CheckHR(hr
,"Failed to initialize settings handler.");
951 // We subclass the core window for moving the associated chrome window when the
952 // core window is moved around, typically in the snap view operation. The
953 // size changes are handled in the documented size changed event.
954 LRESULT CALLBACK
ChromeAppView::CoreWindowProc(
955 HWND window
, UINT message
, WPARAM wp
, LPARAM lp
) {
957 static const UINT kBrowserClosingMessage
=
958 ::RegisterWindowMessage(L
"DefaultBrowserClosing");
960 if (message
== WM_WINDOWPOSCHANGED
) {
961 WINDOWPOS
* pos
= reinterpret_cast<WINDOWPOS
*>(lp
);
962 if (!(pos
->flags
& SWP_NOMOVE
)) {
963 DVLOG(1) << "WM_WINDOWPOSCHANGED. Moving the chrome window.";
964 globals
.view
->OnPositionChanged(pos
->x
, pos
->y
);
966 } else if (message
== WM_HOTKEY
&& wp
== kFlipWindowsHotKeyId
) {
968 } else if (message
== kBrowserClosingMessage
) {
969 DVLOG(1) << "Received DefaultBrowserClosing window message.";
970 // Ensure that the view is uninitialized. The kBrowserClosingMessage
971 // means that the app is going to be terminated, i.e. the proper
972 // uninitialization sequence does not occur.
973 globals
.view
->Uninitialize();
974 if (!globals
.host_windows
.empty()) {
978 return CallWindowProc(globals
.g_core_proc
, window
, message
, wp
, lp
);
981 HRESULT
ChromeAppView::OnSizeChanged(winui::Core::ICoreWindow
* sender
,
982 winui::Core::IWindowSizeChangedEventArgs
* args
) {
983 if (!globals
.host_windows
.size()) {
987 winfoundtn::Size size
;
988 args
->get_Size(&size
);
990 int cx
= static_cast<int>(size
.Width
);
991 int cy
= static_cast<int>(size
.Height
);
993 if (!::SetWindowPos(globals
.host_windows
.front().first
, NULL
, 0, 0, cx
, cy
,
994 SWP_NOMOVE
| SWP_NOZORDER
| SWP_FRAMECHANGED
)) {
995 DVLOG(1) << "SetWindowPos failed.";
997 DVLOG(1) << "size changed cx=" << cx
;
998 DVLOG(1) << "size changed cy=" << cy
;
1000 winui::ViewManagement::ApplicationViewState view_state
=
1001 winui::ViewManagement::ApplicationViewState_FullScreenLandscape
;
1002 app_view_
->get_Value(&view_state
);
1004 HWND top_level_frame
= globals
.host_windows
.front().first
;
1005 if (view_state
== winui::ViewManagement::ApplicationViewState_Snapped
) {
1006 DVLOG(1) << "Enabling metro snap mode.";
1007 ::PostMessageW(top_level_frame
, WM_SYSCOMMAND
, IDC_METRO_SNAP_ENABLE
, 0);
1009 ::PostMessageW(top_level_frame
, WM_SYSCOMMAND
, IDC_METRO_SNAP_DISABLE
, 0);
1014 HRESULT
ChromeAppView::OnPositionChanged(int x
, int y
) {
1015 DVLOG(1) << __FUNCTION__
;
1017 ::SetWindowPos(globals
.host_windows
.front().first
, NULL
, x
, y
, 0, 0,
1018 SWP_NOZORDER
| SWP_FRAMECHANGED
| SWP_NOSIZE
);
1022 HRESULT
ChromeAppView::OnEdgeGestureCompleted(
1023 winui::Input::IEdgeGesture
* gesture
,
1024 winui::Input::IEdgeGestureEventArgs
* args
) {
1025 DVLOG(1) << "edge gesture completed.";
1027 winui::ViewManagement::ApplicationViewState view_state
=
1028 winui::ViewManagement::ApplicationViewState_FullScreenLandscape
;
1029 app_view_
->get_Value(&view_state
);
1030 // We don't want fullscreen chrome unless we are fullscreen metro.
1031 if ((view_state
== winui::ViewManagement::ApplicationViewState_Filled
) ||
1032 (view_state
== winui::ViewManagement::ApplicationViewState_Snapped
)) {
1033 DVLOG(1) << "No full screen in snapped view state:" << view_state
;
1037 // Deactivate anything pending, e.g., the wrench or a context menu.
1038 BOOL success
= ::ReleaseCapture();
1039 DCHECK(success
) << "Couldn't ReleaseCapture() before going full screen";
1041 DVLOG(1) << "Going full screen.";
1042 ::PostMessageW(globals
.host_windows
.front().first
, WM_SYSCOMMAND
,
1047 HRESULT
ChromeAppView::OnShareDataRequested(
1048 winapp::DataTransfer::IDataTransferManager
* data_transfer_mgr
,
1049 winapp::DataTransfer::IDataRequestedEventArgs
* event_args
) {
1051 DVLOG(1) << "Share data requested.";
1053 // The current tab info is retrieved from Chrome via a registered window
1056 static const UINT get_current_tab_info
=
1057 RegisterWindowMessage(kMetroGetCurrentTabInfoMessage
);
1059 static const int kGetTabInfoTimeoutMs
= 1000;
1061 mswr::ComPtr
<winapp::DataTransfer::IDataRequest
> data_request
;
1062 HRESULT hr
= event_args
->get_Request(&data_request
);
1065 mswr::ComPtr
<winapp::DataTransfer::IDataPackage
> data_package
;
1066 hr
= data_request
->get_Data(&data_package
);
1069 base::win::CurrentTabInfo current_tab_info
;
1070 current_tab_info
.title
= NULL
;
1071 current_tab_info
.url
= NULL
;
1073 DWORD_PTR result
= 0;
1075 if (!SendMessageTimeout(globals
.host_windows
.front().first
,
1076 get_current_tab_info
,
1077 reinterpret_cast<WPARAM
>(¤t_tab_info
),
1080 kGetTabInfoTimeoutMs
,
1082 VPLOG(1) << "Failed to retrieve tab info from chrome.";
1086 if (!current_tab_info
.title
|| !current_tab_info
.url
) {
1087 DVLOG(1) << "Failed to retrieve tab info from chrome.";
1091 base::string16
current_title(current_tab_info
.title
);
1092 base::string16
current_url(current_tab_info
.url
);
1094 LocalFree(current_tab_info
.title
);
1095 LocalFree(current_tab_info
.url
);
1097 mswr::ComPtr
<winapp::DataTransfer::IDataPackagePropertySet
> data_properties
;
1098 hr
= data_package
->get_Properties(&data_properties
);
1100 mswrw::HString title
;
1101 title
.Attach(MakeHString(current_title
));
1102 data_properties
->put_Title(title
.Get());
1104 mswr::ComPtr
<winfoundtn::IUriRuntimeClassFactory
> uri_factory
;
1105 hr
= winrt_utils::CreateActivationFactory(
1106 RuntimeClass_Windows_Foundation_Uri
,
1107 uri_factory
.GetAddressOf());
1111 url
.Attach(MakeHString(current_url
));
1112 mswr::ComPtr
<winfoundtn::IUriRuntimeClass
> uri
;
1113 hr
= uri_factory
->CreateUri(url
.Get(), &uri
);
1116 hr
= data_package
->SetUri(uri
.Get());
1122 void ChromeAppView::HandleInputPaneVisible(const RECT
& osk_rect
) {
1123 DCHECK(!osk_visible_notification_received_
);
1125 DVLOG(1) << __FUNCTION__
;
1126 DVLOG(1) << "OSK width:" << osk_rect
.right
- osk_rect
.left
;
1127 DVLOG(1) << "OSK height:" << osk_rect
.bottom
- osk_rect
.top
;
1129 globals
.host_windows
.front().second
= false;
1131 POINT cursor_pos
= {0};
1132 GetCursorPos(&cursor_pos
);
1134 osk_offset_adjustment_
= 0;
1136 if (::PtInRect(&osk_rect
, cursor_pos
)) {
1137 DVLOG(1) << "OSK covering focus point.";
1138 int osk_height
= osk_rect
.bottom
- osk_rect
.top
;
1140 osk_offset_adjustment_
= osk_height
+ kOSKAdjustmentOffset
;
1142 DVLOG(1) << "Scrolling window by offset: " << osk_offset_adjustment_
;
1143 ::ScrollWindowEx(globals
.host_windows
.front().first
,
1145 -osk_offset_adjustment_
,
1150 SW_INVALIDATE
| SW_SCROLLCHILDREN
);
1152 globals
.host_windows
.front().second
= true;
1154 osk_visible_notification_received_
= true;
1157 void ChromeAppView::HandleInputPaneHidden(const RECT
& osk_rect
) {
1158 DCHECK(osk_visible_notification_received_
);
1159 DVLOG(1) << __FUNCTION__
;
1160 DVLOG(1) << "OSK width:" << osk_rect
.right
- osk_rect
.left
;
1161 DVLOG(1) << "OSK height:" << osk_rect
.bottom
- osk_rect
.top
;
1162 osk_visible_notification_received_
= false;
1163 if (globals
.host_windows
.front().second
== true) {
1165 if (osk_offset_adjustment_
) {
1166 DVLOG(1) << "Restoring scrolled window offset: "
1167 << osk_offset_adjustment_
;
1169 ::ScrollWindowEx(globals
.host_windows
.front().first
,
1171 osk_offset_adjustment_
,
1176 SW_INVALIDATE
| SW_SCROLLCHILDREN
);
1179 globals
.host_windows
.front().second
= false;
1183 HRESULT
ChromeAppView::OnInputPaneVisible(
1184 winui::ViewManagement::IInputPane
* input_pane
,
1185 winui::ViewManagement::IInputPaneVisibilityEventArgs
* event_args
) {
1186 DVLOG(1) << __FUNCTION__
;
1190 HRESULT
ChromeAppView::OnInputPaneHiding(
1191 winui::ViewManagement::IInputPane
* input_pane
,
1192 winui::ViewManagement::IInputPaneVisibilityEventArgs
* event_args
) {
1193 DVLOG(1) << __FUNCTION__
;
1197 ///////////////////////////////////////////////////////////////////////////////
1199 ChromeAppViewFactory::ChromeAppViewFactory(
1200 winapp::Core::ICoreApplication
* icore_app
,
1201 LPTHREAD_START_ROUTINE host_main
,
1202 void* host_context
) {
1203 globals
.host_main
= host_main
;
1204 globals
.host_context
= host_context
;
1205 mswr::ComPtr
<winapp::Core::ICoreApplication
> core_app(icore_app
);
1206 mswr::ComPtr
<winapp::Core::ICoreApplicationExit
> app_exit
;
1207 CheckHR(core_app
.As(&app_exit
));
1208 globals
.app_exit
= app_exit
.Detach();
1212 ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView
** view
) {
1213 globals
.view
= mswr::Make
<ChromeAppView
>().Detach();
1214 *view
= globals
.view
;
1215 return (*view
) ? S_OK
: E_OUTOFMEMORY
;