Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / win8 / metro_driver / chrome_app_view.cc
blob4c5a8be96176b132edad45702b2bb8e9c2130129
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>
12 #include <algorithm>
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;
56 // TODO(ananta)
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";
73 namespace {
75 enum Modifier {
76 NONE,
77 SHIFT = 1,
78 CONTROL = 2,
79 ALT = 4
82 // Helper function to send keystrokes via the SendInput function.
83 // Params:
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,
89 bool unicode) {
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);
97 key_count++;
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);
104 key_count++;
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);
111 key_count++;
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);
118 if (extended)
119 keys[key_count].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
120 if (unicode)
121 keys[key_count].ki.dwFlags |= KEYEVENTF_UNICODE;
122 key_count++;
124 bool should_sleep = key_count > 1;
126 // Send key downs
127 for (int i = 0; i < key_count; i++) {
128 SendInput(1, &keys[ i ], sizeof(keys[0]));
129 keys[i].ki.dwFlags |= KEYEVENTF_KEYUP;
130 if (should_sleep) {
131 Sleep(10);
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]));
138 if (should_sleep) {
139 Sleep(10);
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_task_runner->PostDelayedTask(
161 FROM_HERE,
162 base::Bind(&MetroExit, false),
163 base::TimeDelta::FromMilliseconds(100));
165 } else {
166 globals.app_exit->Exit();
170 void AdjustToFitWindow(HWND hwnd, int flags) {
171 RECT rect = {0};
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,
182 UINT message,
183 WPARAM wp,
184 LPARAM lp) {
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)) {
189 case HTBOTTOM:
190 case HTBOTTOMLEFT:
191 case HTBOTTOMRIGHT:
192 case HTLEFT:
193 case HTRIGHT:
194 case HTTOP:
195 case HTTOPLEFT:
196 case HTTOPRIGHT:
197 lp = MAKELPARAM(HTCLIENT, HIWORD(lp));
198 break;
199 default:
200 break;
204 WNDPROC old_proc = reinterpret_cast<WNDPROC>(
205 ::GetProp(window, kChromeSubclassWindowProp));
206 DCHECK(old_proc);
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());
254 if (!new_window) {
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));
261 if (new_window) {
262 AdjustFrameWindowStyleForMetro(hwnd);
263 } else {
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);
285 } else {
286 // time to quit
287 DVLOG(1) << "Last host window closed. Calling Exit().";
288 MetroExit(true);
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);
314 } // namespace
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.";
322 return;
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();
342 return;
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();
372 // static
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());
378 CheckHR(hr);
380 winui::ViewManagement::ApplicationViewState state =
381 winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
382 hr = view_statics->get_Value(&state);
383 CheckHR(hr);
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;
391 if (SUCCEEDED(hr))
392 hr = E_UNEXPECTED;
395 return 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_,
408 NULL,
409 NULL,
410 NULL,
411 NULL,
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);
421 return view_state;
424 void UnsnapHelper() {
425 ChromeAppView::Unsnap();
428 extern "C" __declspec(dllexport)
429 void MetroUnsnap() {
430 DVLOG(1) << __FUNCTION__;
431 globals.appview_task_runner->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_task_runner->PostTask(
445 FROM_HERE, base::Bind(&SetFrameWindowInternal, hwnd));
448 // TODO(ananta)
449 // Handle frame window close by deleting it from the window list and making the
450 // next guy visible.
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_task_runner->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())
470 return L"";
472 const wchar_t* initial_url = globals.navigation_url.c_str();
473 DVLOG(1) << initial_url;
474 return 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())
485 return L"";
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_task_runner->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) {
518 // TODO(ananta)
519 // Needs implementation.
520 DVLOG(1) << __FUNCTION__;
522 ToastNotificationHandler::DesktopNotification notification(origin_url,
523 icon_url,
524 title,
525 body,
526 display_source,
527 notification_id,
528 handler,
529 handler_context);
530 globals.appview_task_runner->PostTask(
531 FROM_HERE, base::Bind(&ChromeAppView::DisplayNotification,
532 globals.view, notification));
535 extern "C" __declspec(dllexport)
536 bool CancelNotification(const char* notification_id) {
537 // TODO(ananta)
538 // Needs implementation.
539 DVLOG(1) << __FUNCTION__;
541 if (!globals.view->IsValidNotification(notification_id)) {
542 NOTREACHED() << "Invalid notification id :" << notification_id;
543 return false;
546 globals.appview_task_runner->PostTask(
547 FROM_HERE, base::Bind(&ChromeAppView::CancelNotification,
548 globals.view, std::string(notification_id)));
549 return true;
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)
564 void ShowDialogBox(
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__;
573 DCHECK(title);
574 DCHECK(content);
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_task_runner->PostTask(
589 FROM_HERE, base::Bind(
590 &ChromeAppView::ShowDialogBox, globals.view, dialog_box_info));
593 // Provides functionality to dismiss the previously displayed metro style
594 // dialog box.
595 extern "C" __declspec(dllexport)
596 void DismissDialogBox() {
597 VLOG(1) << __FUNCTION__;
599 globals.appview_task_runner->PostTask(
600 FROM_HERE, base::Bind(
601 &ChromeAppView::DismissDialogBox,
602 globals.view));
605 extern "C" __declspec(dllexport)
606 void SetFullscreen(bool fullscreen) {
607 VLOG(1) << __FUNCTION__;
609 globals.appview_task_runner->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);
621 windows.clear();
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;
646 MetroExit(true);
647 return 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__;
662 IFACEMETHODIMP
663 ChromeAppView::Initialize(winapp::Core::ICoreApplicationView* view) {
664 view_ = view;
665 DVLOG(1) << __FUNCTION__;
667 HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>(
668 this, &ChromeAppView::OnActivate).Get(),
669 &activated_token_);
670 CheckHR(hr);
671 return hr;
674 IFACEMETHODIMP
675 ChromeAppView::SetWindow(winui::Core::ICoreWindow* window) {
676 window_ = 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());
682 CheckHR(hr);
683 hr = interop->get_WindowHandle(&core_window_hwnd_);
684 CheckHR(hr);
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(),
691 &sizechange_token_);
692 CheckHR(hr);
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);
703 CheckHR(hr);
705 hr = edge_gesture->add_Completed(mswr::Callback<EdgeEventHandler>(
706 this, &ChromeAppView::OnEdgeGestureCompleted).Get(),
707 &edgeevent_token_);
708 CheckHR(hr);
710 // Register for share notifications.
711 mswr::ComPtr<winapp::DataTransfer::IDataTransferManagerStatics>
712 data_mgr_statics;
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_);
726 CheckHR(hr);
728 // TODO(ananta)
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());
735 CheckHR(hr);
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.";
744 return S_OK;
747 IFACEMETHODIMP
748 ChromeAppView::Load(HSTRING entryPoint) {
749 DVLOG(1) << __FUNCTION__;
750 return S_OK;
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
759 // Nicely:
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.
779 // TODO(ananta)
780 // Take this out when the documented input pane notification issues are
781 // addressed.
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) {
787 RECT osk_rect = {0};
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(
802 FROM_HERE,
803 base::Bind(&ChromeAppView::CheckForOSKActivation, base::Unretained(this)),
804 base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
807 IFACEMETHODIMP
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();
815 if (SUCCEEDED(hr)) {
816 // TODO(cpu): Draw something here.
817 } else {
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 task runner to the world.
825 globals.appview_task_runner = msg_loop.task_runner();
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
832 // this is needed.
833 // TODO(ananta)
834 // Take this out when the documented OSK notifications start working.
835 msg_loop.PostDelayedTask(
836 FROM_HERE,
837 base::Bind(&ChromeAppView::CheckForOSKActivation,
838 base::Unretained(this)),
839 base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
841 msg_loop.Run();
843 globals.appview_task_runner = 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!";
853 EndChromeSession();
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;
864 return hr;
867 IFACEMETHODIMP
868 ChromeAppView::Uninitialize() {
869 DVLOG(1) << __FUNCTION__;
870 window_ = nullptr;
871 view_ = nullptr;
872 base::AutoLock lock(notification_lock_);
873 notification_map_.clear();
874 return S_OK;
877 HRESULT ChromeAppView::RegisterInputPaneNotifications() {
878 DVLOG(1) << __FUNCTION__;
880 mswr::ComPtr<winui::ViewManagement::IInputPaneStatics>
881 input_pane_statics;
882 HRESULT hr = winrt_utils::CreateActivationFactory(
883 RuntimeClass_Windows_UI_ViewManagement_InputPane,
884 input_pane_statics.GetAddressOf());
885 CheckHR(hr);
887 hr = input_pane_statics->GetForCurrentView(&input_pane_);
888 CheckHR(hr);
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_);
895 CheckHR(hr);
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_);
904 CheckHR(hr);
906 DVLOG(1) << "Added hiding event handler for input pane, value="
907 << input_pane_hiding_token_.value;
908 return hr;
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;
918 window_->Activate();
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.";
925 return S_OK;
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.";
936 return E_UNEXPECTED;
940 if (RegisterHotKey(core_window_hwnd_, kFlipWindowsHotKeyId,
941 MOD_CONTROL, VK_F12)) {
942 DVLOG(1) << "Registered flip window hotkey.";
943 } else {
944 VPLOG(1) << "Failed to register flip window hotkey.";
946 HRESULT hr = settings_handler_.Initialize();
947 CheckHR(hr,"Failed to initialize settings handler.");
948 return hr;
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) {
967 FlipFrameWindows();
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()) {
975 EndChromeSession();
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()) {
984 return S_OK;
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);
1008 } else {
1009 ::PostMessageW(top_level_frame, WM_SYSCOMMAND, IDC_METRO_SNAP_DISABLE, 0);
1011 return S_OK;
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);
1019 return S_OK;
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;
1034 return S_OK;
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,
1043 IDC_FULLSCREEN, 0);
1044 return S_OK;
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
1054 // message.
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);
1063 CheckHR(hr);
1065 mswr::ComPtr<winapp::DataTransfer::IDataPackage> data_package;
1066 hr = data_request->get_Data(&data_package);
1067 CheckHR(hr);
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>(&current_tab_info),
1079 SMTO_ABORTIFHUNG,
1080 kGetTabInfoTimeoutMs,
1081 &result)) {
1082 VPLOG(1) << "Failed to retrieve tab info from chrome.";
1083 return E_FAIL;
1086 if (!current_tab_info.title || !current_tab_info.url) {
1087 DVLOG(1) << "Failed to retrieve tab info from chrome.";
1088 return E_FAIL;
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());
1108 CheckHR(hr);
1110 mswrw::HString url;
1111 url.Attach(MakeHString(current_url));
1112 mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
1113 hr = uri_factory->CreateUri(url.Get(), &uri);
1114 CheckHR(hr);
1116 hr = data_package->SetUri(uri.Get());
1117 CheckHR(hr);
1119 return S_OK;
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_,
1146 NULL,
1147 NULL,
1148 NULL,
1149 NULL,
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_,
1172 NULL,
1173 NULL,
1174 NULL,
1175 NULL,
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__;
1187 return S_OK;
1190 HRESULT ChromeAppView::OnInputPaneHiding(
1191 winui::ViewManagement::IInputPane* input_pane,
1192 winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args) {
1193 DVLOG(1) << __FUNCTION__;
1194 return S_OK;
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();
1211 IFACEMETHODIMP
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;