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 |