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>(¤t_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 } 1217