- add sources.
[platform/framework/web/crosswalk.git] / src / win8 / metro_driver / chrome_app_view.cc
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.
4
5 #include "win8/metro_driver/stdafx.h"
6 #include "win8/metro_driver/chrome_app_view.h"
7
8 #include <corewindow.h>
9 #include <windows.applicationModel.datatransfer.h>
10 #include <windows.foundation.h>
11
12 #include <algorithm>
13
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"
24
25 typedef winfoundtn::ITypedEventHandler<
26     winapp::Core::CoreApplicationView*,
27     winapp::Activation::IActivatedEventArgs*> ActivatedHandler;
28
29 typedef winfoundtn::ITypedEventHandler<
30     winui::Core::CoreWindow*,
31     winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler;
32
33 typedef winfoundtn::ITypedEventHandler<
34     winui::Input::EdgeGesture*,
35     winui::Input::EdgeGestureEventArgs*> EdgeEventHandler;
36
37 typedef winfoundtn::ITypedEventHandler<
38     winapp::DataTransfer::DataTransferManager*,
39     winapp::DataTransfer::DataRequestedEventArgs*> ShareDataRequestedHandler;
40
41 typedef winfoundtn::ITypedEventHandler<
42     winui::ViewManagement::InputPane*,
43     winui::ViewManagement::InputPaneVisibilityEventArgs*>
44     InputPaneEventHandler;
45
46 typedef winfoundtn::ITypedEventHandler<
47     winui::Core::CoreWindow*,
48     winui::Core::PointerEventArgs*> PointerEventHandler;
49
50 typedef winfoundtn::ITypedEventHandler<
51     winui::Core::CoreWindow*,
52     winui::Core::KeyEventArgs*> KeyEventHandler;
53
54 struct Globals globals;
55
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";
60
61 static const int kFlipWindowsHotKeyId = 0x0000baba;
62
63 static const int kAnimateWindowTimeoutMs = 200;
64
65 static const int kCheckOSKDelayMs = 300;
66
67 const wchar_t kOSKClassName[] = L"IPTip_Main_Window";
68
69 static const int kOSKAdjustmentOffset = 20;
70
71 const wchar_t kChromeSubclassWindowProp[] = L"MetroChromeWindowProc";
72
73 namespace {
74
75 enum Modifier {
76   NONE,
77   SHIFT = 1,
78   CONTROL = 2,
79   ALT = 4
80 };
81
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
92
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++;
98   }
99
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++;
105   }
106
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++;
112   }
113
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);
117
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++;
123
124   bool should_sleep = key_count > 1;
125
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);
132     }
133   }
134
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);
140     }
141   }
142 }
143
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(
161         FROM_HERE,
162         base::Bind(&MetroExit, false),
163         base::TimeDelta::FromMilliseconds(100));
164     }
165   } else {
166     globals.app_exit->Exit();
167   }
168 }
169
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;
175
176   ::SetWindowPos(hwnd, HWND_TOP,
177                  rect.left, rect.top, cx, cy,
178                  SWP_NOZORDER | flags);
179 }
180
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;
201     }
202   }
203
204   WNDPROC old_proc = reinterpret_cast<WNDPROC>(
205       ::GetProp(window, kChromeSubclassWindowProp));
206   DCHECK(old_proc);
207   return CallWindowProc(old_proc, window, message, wp, lp);
208 }
209
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));
219
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);
226   }
227   AdjustToFitWindow(hwnd, SWP_FRAMECHANGED | SWP_NOACTIVATE);
228 }
229
230 void SetFrameWindowInternal(HWND hwnd) {
231   DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd);
232
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);
239   }
240
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);
250   });
251
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);
257   }
258
259   globals.host_windows.push_front(std::make_pair(hwnd, window_scrolled_state));
260
261   if (new_window) {
262     AdjustFrameWindowStyleForMetro(hwnd);
263   } else {
264     DVLOG(1) << "Adjusting new top window to core window size";
265     AdjustToFitWindow(hwnd, 0);
266   }
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);
271   }
272 }
273
274 void CloseFrameWindowInternal(HWND hwnd) {
275   DVLOG(1) << __FUNCTION__ << ", hwnd=" << LONG_PTR(hwnd);
276
277   globals.host_windows.remove_if([hwnd](std::pair<HWND, bool>& item) {
278     return (item.first == hwnd);
279   });
280
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);
289   }
290 }
291
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();
299
300     DVLOG(1) << "Making new top frame window visible:"
301             <<  reinterpret_cast<int>(globals.host_windows.front().first);
302
303     AdjustToFitWindow(globals.host_windows.front().first, SWP_SHOWWINDOW);
304
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);
309
310     globals.host_windows.push_back(current_top_window);
311   }
312 }
313
314 }  // namespace
315
316 void ChromeAppView::DisplayNotification(
317     const ToastNotificationHandler::DesktopNotification& notification) {
318   DVLOG(1) << __FUNCTION__;
319
320   if (IsValidNotification(notification.id)) {
321     NOTREACHED() << "Duplicate notification id passed in.";
322     return;
323   }
324
325   base::AutoLock lock(notification_lock_);
326
327   ToastNotificationHandler* notification_handler =
328       new ToastNotificationHandler;
329
330   notification_map_[notification.id].reset(notification_handler);
331   notification_handler->DisplayNotification(notification);
332 }
333
334 void ChromeAppView::CancelNotification(const std::string& notification) {
335   DVLOG(1) << __FUNCTION__;
336
337   base::AutoLock lock(notification_lock_);
338
339   NotificationMap::iterator index = notification_map_.find(notification);
340   if (index == notification_map_.end()) {
341     NOTREACHED() << "Invalid notification:" << notification.c_str();
342     return;
343   }
344
345   scoped_ptr<ToastNotificationHandler> notification_handler(
346       index->second.release());
347
348   notification_map_.erase(index);
349
350   notification_handler->CancelNotification();
351 }
352
353 // Returns true if the notification passed in is valid.
354 bool ChromeAppView::IsValidNotification(const std::string& notification) {
355   DVLOG(1) << __FUNCTION__;
356
357   base::AutoLock lock(notification_lock_);
358   return notification_map_.find(notification) != notification_map_.end();
359 }
360
361 void ChromeAppView::ShowDialogBox(
362     const MetroDialogBox::DialogBoxInfo& dialog_box_info) {
363   VLOG(1) << __FUNCTION__;
364   dialog_box_.Show(dialog_box_info);
365 }
366
367 void ChromeAppView::DismissDialogBox() {
368   VLOG(1) << __FUNCTION__;
369   dialog_box_.Dismiss();
370 }
371
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);
379
380   winui::ViewManagement::ApplicationViewState state =
381       winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
382   hr = view_statics->get_Value(&state);
383   CheckHR(hr);
384
385   if (state == winui::ViewManagement::ApplicationViewState_Snapped) {
386     boolean success = FALSE;
387     hr = view_statics->TryUnsnap(&success);
388
389     if (FAILED(hr) || !success) {
390       LOG(ERROR) << "Failed to unsnap. Error 0x" << hr;
391       if (SUCCEEDED(hr))
392         hr = E_UNEXPECTED;
393     }
394   }
395   return hr;
396 }
397
398 void ChromeAppView::SetFullscreen(bool fullscreen) {
399   VLOG(1) << __FUNCTION__;
400
401   if (osk_offset_adjustment_) {
402     VLOG(1) << "Scrolling the window down by: "
403             << osk_offset_adjustment_;
404
405     ::ScrollWindowEx(globals.host_windows.front().first,
406                      0,
407                      osk_offset_adjustment_,
408                      NULL,
409                      NULL,
410                      NULL,
411                      NULL,
412                      SW_INVALIDATE | SW_SCROLLCHILDREN);
413     osk_offset_adjustment_ = 0;
414   }
415 }
416
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;
422 }
423
424 void UnsnapHelper() {
425   ChromeAppView::Unsnap();
426 }
427
428 extern "C" __declspec(dllexport)
429 void MetroUnsnap() {
430   DVLOG(1) << __FUNCTION__;
431   globals.appview_msg_loop->PostTask(
432       FROM_HERE, base::Bind(&UnsnapHelper));
433 }
434
435 extern "C" __declspec(dllexport)
436 HWND GetRootWindow() {
437   DVLOG(1) << __FUNCTION__;
438   return globals.view->core_window_hwnd();
439 }
440
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));
446 }
447
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);
454
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));
460 }
461
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"";
471
472   const wchar_t* initial_url = globals.navigation_url.c_str();
473   DVLOG(1) << initial_url;
474   return initial_url;
475 }
476
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"";
486
487   const wchar_t* initial_search_string = globals.search_string.c_str();
488   DVLOG(1) << initial_search_string;
489   return initial_search_string;
490 }
491
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);
499   }
500   return static_cast<base::win::MetroLaunchType>(
501       globals.initial_activation_kind);
502 }
503
504 extern "C" __declspec(dllexport)
505 void FlipFrameWindows() {
506   DVLOG(1) << __FUNCTION__;
507   globals.appview_msg_loop->PostTask(
508       FROM_HERE, base::Bind(&FlipFrameWindowsInternal));
509 }
510
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__;
521
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_msg_loop->PostTask(
531       FROM_HERE, base::Bind(&ChromeAppView::DisplayNotification,
532                             globals.view, notification));
533 }
534
535 extern "C" __declspec(dllexport)
536 bool CancelNotification(const char* notification_id) {
537   // TODO(ananta)
538   // Needs implementation.
539   DVLOG(1) << __FUNCTION__;
540
541   if (!globals.view->IsValidNotification(notification_id)) {
542     NOTREACHED() << "Invalid notification id :" << notification_id;
543     return false;
544   }
545
546   globals.appview_msg_loop->PostTask(
547       FROM_HERE, base::Bind(&ChromeAppView::CancelNotification,
548                             globals.view, std::string(notification_id)));
549   return true;
550 }
551
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();
559 }
560
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__;
572
573   DCHECK(title);
574   DCHECK(content);
575   DCHECK(button1_label);
576   DCHECK(button2_label);
577   DCHECK(button1_handler);
578   DCHECK(button2_handler);
579
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;
587
588   globals.appview_msg_loop->PostTask(
589       FROM_HERE, base::Bind(
590           &ChromeAppView::ShowDialogBox, globals.view, dialog_box_info));
591 }
592
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__;
598
599   globals.appview_msg_loop->PostTask(
600       FROM_HERE, base::Bind(
601           &ChromeAppView::DismissDialogBox,
602           globals.view));
603 }
604
605 extern "C" __declspec(dllexport)
606 void SetFullscreen(bool fullscreen) {
607   VLOG(1) << __FUNCTION__;
608
609   globals.appview_msg_loop->PostTask(
610       FROM_HERE, base::Bind(
611           &ChromeAppView::SetFullscreen,
612           globals.view, fullscreen));
613 }
614
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);
620   });
621   windows.clear();
622 }
623
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);
628 }
629
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();
638
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);
644
645   DVLOG(1) << "host thread done, exit_code=" << exit_code;
646   MetroExit(true);
647   return exit_code;
648 }
649
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;
656 }
657
658 ChromeAppView::~ChromeAppView() {
659   DVLOG(1) << __FUNCTION__;
660 }
661
662 IFACEMETHODIMP
663 ChromeAppView::Initialize(winapp::Core::ICoreApplicationView* view) {
664   view_ = view;
665   DVLOG(1) << __FUNCTION__;
666
667   HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>(
668       this, &ChromeAppView::OnActivate).Get(),
669       &activated_token_);
670   CheckHR(hr);
671   return hr;
672 }
673
674 IFACEMETHODIMP
675 ChromeAppView::SetWindow(winui::Core::ICoreWindow* window) {
676   window_ = window;
677   DVLOG(1) << __FUNCTION__;
678
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);
685
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);
693
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.");
700
701   mswr::ComPtr<winui::Input::IEdgeGesture> edge_gesture;
702   hr = edge_gesture_statics->GetForCurrentView(&edge_gesture);
703   CheckHR(hr);
704
705   hr = edge_gesture->add_Completed(mswr::Callback<EdgeEventHandler>(
706       this, &ChromeAppView::OnEdgeGestureCompleted).Get(),
707       &edgeevent_token_);
708   CheckHR(hr);
709
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.");
717
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.");
721
722   hr = data_transfer_mgr->add_DataRequested(
723       mswr::Callback<ShareDataRequestedHandler>(
724           this, &ChromeAppView::OnShareDataRequested).Get(),
725       &share_data_requested_token_);
726   CheckHR(hr);
727
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);
736
737   DVLOG(1) << "Created appview instance.";
738
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;
745 }
746
747 IFACEMETHODIMP
748 ChromeAppView::Load(HSTRING entryPoint) {
749   DVLOG(1) << __FUNCTION__;
750   return S_OK;
751 }
752
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);
757
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);
766
767   // Wind down the thread's chrome message loop.
768   base::MessageLoop::current()->Quit();
769 }
770
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 notifcation 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);
789
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);
794         }
795       } else if (osk_visible_notification_received()) {
796         DVLOG(1) << "KB window hidden while we are in the foreground.";
797         HandleInputPaneHidden(osk_rect);
798       }
799     }
800   }
801   base::MessageLoop::current()->PostDelayedTask(
802       FROM_HERE,
803       base::Bind(&ChromeAppView::CheckForOSKActivation, base::Unretained(this)),
804       base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
805 }
806
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.");
813
814   hr = window_->Activate();
815   if (SUCCEEDED(hr)) {
816     // TODO(cpu): Draw something here.
817   } else {
818     DVLOG(1) << "Activate failed, hr=" << hr;
819   }
820
821   // Create a message loop to allow message passing into this thread.
822   base::MessageLoop msg_loop(base::MessageLoop::TYPE_UI);
823
824   // Announce our message loop to the world.
825   globals.appview_msg_loop = msg_loop.message_loop_proxy();
826
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()));
829
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));
840
841   msg_loop.Run();
842
843   globals.appview_msg_loop = NULL;
844
845   DVLOG(0) << "ProcessEvents done, hr=" << hr;
846
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();
854   }
855   DWORD wr = ::WaitForSingleObject(globals.host_thread, INFINITE);
856   if (wr != WAIT_OBJECT_0) {
857     DVLOG(1) << "Waiting for host thread failed : " << wr;
858   }
859
860   DVLOG(1) << "Host thread exited";
861
862   ::CloseHandle(globals.host_thread);
863   globals.host_thread = NULL;
864   return hr;
865 }
866
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;
875 }
876
877 HRESULT ChromeAppView::RegisterInputPaneNotifications() {
878   DVLOG(1) << __FUNCTION__;
879
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);
886
887   hr = input_pane_statics->GetForCurrentView(&input_pane_);
888   CheckHR(hr);
889   DVLOG(1) << "Got input pane.";
890
891   hr = input_pane_->add_Showing(
892       mswr::Callback<InputPaneEventHandler>(
893           this, &ChromeAppView::OnInputPaneVisible).Get(),
894       &input_pane_visible_token_);
895   CheckHR(hr);
896
897   DVLOG(1) << "Added showing event handler for input pane",
898       input_pane_visible_token_.value;
899
900   hr = input_pane_->add_Hiding(
901       mswr::Callback<InputPaneEventHandler>(
902           this, &ChromeAppView::OnInputPaneHiding).Get(),
903       &input_pane_hiding_token_);
904   CheckHR(hr);
905
906   DVLOG(1) << "Added hiding event handler for input pane, value="
907           << input_pane_hiding_token_.value;
908   return hr;
909 }
910
911 HRESULT ChromeAppView::OnActivate(winapp::Core::ICoreApplicationView*,
912     winapp::Activation::IActivatedEventArgs* args) {
913   DVLOG(1) << __FUNCTION__;
914
915   args->get_PreviousExecutionState(&globals.previous_state);
916   DVLOG(1) << "Previous Execution State: " << globals.previous_state;
917
918   window_->Activate();
919   url_launch_handler_.Activate(args);
920
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;
926   }
927
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);
933
934     if (!globals.host_thread) {
935       NOTREACHED() << "thread creation failed.";
936       return E_UNEXPECTED;
937     }
938   }
939
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.";
945   }
946   HRESULT hr = settings_handler_.Initialize();
947   CheckHR(hr,"Failed to initialize settings handler.");
948   return hr;
949 }
950
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) {
956
957   static const UINT kBrowserClosingMessage =
958       ::RegisterWindowMessage(L"DefaultBrowserClosing");
959
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);
965     }
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();
976     }
977   }
978   return CallWindowProc(globals.g_core_proc, window, message, wp, lp);
979 }
980
981 HRESULT ChromeAppView::OnSizeChanged(winui::Core::ICoreWindow* sender,
982     winui::Core::IWindowSizeChangedEventArgs* args) {
983   if (!globals.host_windows.size()) {
984     return S_OK;
985   }
986
987   winfoundtn::Size size;
988   args->get_Size(&size);
989
990   int cx = static_cast<int>(size.Width);
991   int cy = static_cast<int>(size.Height);
992
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.";
996   }
997   DVLOG(1) << "size changed cx=" << cx;
998   DVLOG(1) << "size changed cy=" << cy;
999
1000   winui::ViewManagement::ApplicationViewState view_state =
1001       winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
1002   app_view_->get_Value(&view_state);
1003
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);
1010   }
1011   return S_OK;
1012 }
1013
1014 HRESULT ChromeAppView::OnPositionChanged(int x, int y) {
1015   DVLOG(1) << __FUNCTION__;
1016
1017   ::SetWindowPos(globals.host_windows.front().first, NULL, x, y, 0, 0,
1018                  SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE);
1019   return S_OK;
1020 }
1021
1022 HRESULT ChromeAppView::OnEdgeGestureCompleted(
1023     winui::Input::IEdgeGesture* gesture,
1024     winui::Input::IEdgeGestureEventArgs* args) {
1025   DVLOG(1) << "edge gesture completed.";
1026
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;
1035   }
1036
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";
1040
1041   DVLOG(1) << "Going full screen.";
1042   ::PostMessageW(globals.host_windows.front().first, WM_SYSCOMMAND,
1043                  IDC_FULLSCREEN, 0);
1044   return S_OK;
1045 }
1046
1047 HRESULT ChromeAppView::OnShareDataRequested(
1048   winapp::DataTransfer::IDataTransferManager* data_transfer_mgr,
1049   winapp::DataTransfer::IDataRequestedEventArgs* event_args) {
1050
1051   DVLOG(1) << "Share data requested.";
1052
1053   // The current tab info is retrieved from Chrome via a registered window
1054   // message.
1055
1056   static const UINT get_current_tab_info =
1057       RegisterWindowMessage(kMetroGetCurrentTabInfoMessage);
1058
1059   static const int kGetTabInfoTimeoutMs = 1000;
1060
1061   mswr::ComPtr<winapp::DataTransfer::IDataRequest> data_request;
1062   HRESULT hr = event_args->get_Request(&data_request);
1063   CheckHR(hr);
1064
1065   mswr::ComPtr<winapp::DataTransfer::IDataPackage> data_package;
1066   hr = data_request->get_Data(&data_package);
1067   CheckHR(hr);
1068
1069   base::win::CurrentTabInfo current_tab_info;
1070   current_tab_info.title = NULL;
1071   current_tab_info.url = NULL;
1072
1073   DWORD_PTR result = 0;
1074
1075   if (!SendMessageTimeout(globals.host_windows.front().first,
1076                           get_current_tab_info,
1077                           reinterpret_cast<WPARAM>(&current_tab_info),
1078                           0,
1079                           SMTO_ABORTIFHUNG,
1080                           kGetTabInfoTimeoutMs,
1081                           &result)) {
1082     VPLOG(1) << "Failed to retrieve tab info from chrome.";
1083     return E_FAIL;
1084   }
1085
1086   if (!current_tab_info.title || !current_tab_info.url) {
1087     DVLOG(1) << "Failed to retrieve tab info from chrome.";
1088     return E_FAIL;
1089   }
1090
1091   string16 current_title(current_tab_info.title);
1092   string16 current_url(current_tab_info.url);
1093
1094   LocalFree(current_tab_info.title);
1095   LocalFree(current_tab_info.url);
1096
1097   mswr::ComPtr<winapp::DataTransfer::IDataPackagePropertySet> data_properties;
1098   hr = data_package->get_Properties(&data_properties);
1099
1100   mswrw::HString title;
1101   title.Attach(MakeHString(current_title));
1102   data_properties->put_Title(title.Get());
1103
1104   mswr::ComPtr<winfoundtn::IUriRuntimeClassFactory> uri_factory;
1105   hr = winrt_utils::CreateActivationFactory(
1106       RuntimeClass_Windows_Foundation_Uri,
1107       uri_factory.GetAddressOf());
1108   CheckHR(hr);
1109
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);
1115
1116   hr = data_package->SetUri(uri.Get());
1117   CheckHR(hr);
1118
1119   return S_OK;
1120 }
1121
1122 void ChromeAppView::HandleInputPaneVisible(const RECT& osk_rect) {
1123   DCHECK(!osk_visible_notification_received_);
1124
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;
1128
1129   globals.host_windows.front().second = false;
1130
1131   POINT cursor_pos = {0};
1132   GetCursorPos(&cursor_pos);
1133
1134   osk_offset_adjustment_ = 0;
1135
1136   if (::PtInRect(&osk_rect, cursor_pos)) {
1137     DVLOG(1) << "OSK covering focus point.";
1138     int osk_height = osk_rect.bottom - osk_rect.top;
1139
1140     osk_offset_adjustment_ = osk_height + kOSKAdjustmentOffset;
1141
1142     DVLOG(1) << "Scrolling window by offset: " << osk_offset_adjustment_;
1143     ::ScrollWindowEx(globals.host_windows.front().first,
1144                      0,
1145                      -osk_offset_adjustment_,
1146                      NULL,
1147                      NULL,
1148                      NULL,
1149                      NULL,
1150                      SW_INVALIDATE | SW_SCROLLCHILDREN);
1151
1152     globals.host_windows.front().second  = true;
1153   }
1154   osk_visible_notification_received_ = true;
1155 }
1156
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) {
1164
1165     if (osk_offset_adjustment_) {
1166       DVLOG(1) << "Restoring scrolled window offset: "
1167                << osk_offset_adjustment_;
1168
1169       ::ScrollWindowEx(globals.host_windows.front().first,
1170                        0,
1171                        osk_offset_adjustment_,
1172                        NULL,
1173                        NULL,
1174                        NULL,
1175                        NULL,
1176                        SW_INVALIDATE | SW_SCROLLCHILDREN);
1177     }
1178
1179     globals.host_windows.front().second = false;
1180   }
1181 }
1182
1183 HRESULT ChromeAppView::OnInputPaneVisible(
1184     winui::ViewManagement::IInputPane* input_pane,
1185     winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args) {
1186   DVLOG(1) << __FUNCTION__;
1187   return S_OK;
1188 }
1189
1190 HRESULT ChromeAppView::OnInputPaneHiding(
1191     winui::ViewManagement::IInputPane* input_pane,
1192     winui::ViewManagement::IInputPaneVisibilityEventArgs* event_args) {
1193   DVLOG(1) << __FUNCTION__;
1194   return S_OK;
1195 }
1196
1197 ///////////////////////////////////////////////////////////////////////////////
1198
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();
1209 }
1210
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;
1216 }