| OLD | NEW |
| (Empty) |
| 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_task_runner->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_task_runner->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_task_runner->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_task_runner->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_task_runner->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_task_runner->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_task_runner->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_task_runner->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_task_runner->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_task_runner->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()->QuitWhenIdle(); | |
| 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 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); | |
| 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::MessageLoopForUI msg_loop; | |
| 823 | |
| 824 // Announce our message loop task runner to the world. | |
| 825 globals.appview_task_runner = msg_loop.task_runner(); | |
| 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_task_runner = 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 base::string16 current_title(current_tab_info.title); | |
| 1092 base::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 } | |
| OLD | NEW |