OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "chrome/browser/chromeos/app_launcher.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "app/gfx/canvas.h" | |
11 #include "app/resource_bundle.h" | |
12 #include "base/command_line.h" | |
13 #include "base/message_loop.h" | |
14 #include "base/string_util.h" | |
15 #include "chrome/browser/autocomplete/autocomplete_edit.h" | |
16 #include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h" | |
17 #include "chrome/browser/browser.h" | |
18 #include "chrome/browser/browser_list.h" | |
19 #include "chrome/browser/browser_window.h" | |
20 #include "chrome/browser/bubble_positioner.h" | |
21 #include "chrome/browser/chromeos/frame/browser_view.h" | |
22 #include "chrome/browser/chromeos/status/status_area_view.h" | |
23 #include "chrome/browser/chromeos/wm_ipc.h" | |
24 #include "chrome/browser/in_process_webkit/dom_storage_context.h" | |
25 #include "chrome/browser/in_process_webkit/webkit_context.h" | |
26 #include "chrome/browser/profile.h" | |
27 #include "chrome/browser/renderer_host/render_view_host.h" | |
28 #include "chrome/browser/renderer_host/render_view_host_factory.h" | |
29 #include "chrome/browser/renderer_host/render_widget_host_view.h" | |
30 #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" | |
31 #include "chrome/browser/renderer_host/site_instance.h" | |
32 #include "chrome/browser/renderer_preferences_util.h" | |
33 #include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" | |
34 #include "chrome/browser/tab_contents/tab_contents.h" | |
35 #include "chrome/browser/views/bubble_border.h" | |
36 #include "gfx/insets.h" | |
37 #include "gfx/point.h" | |
38 #include "grit/app_resources.h" | |
39 #include "grit/generated_resources.h" | |
40 #include "grit/theme_resources.h" | |
41 #include "third_party/skia/include/core/SkBitmap.h" | |
42 #include "views/background.h" | |
43 #include "views/controls/native/native_view_host.h" | |
44 #include "views/painter.h" | |
45 #include "views/screen.h" | |
46 #include "views/widget/root_view.h" | |
47 #include "views/widget/widget_gtk.h" | |
48 | |
49 namespace { | |
50 | |
51 // Padding & margins for the navigation entry. | |
52 const int kNavigationEntryPadding = 2; | |
53 const int kNavigationEntryXMargin = 3; | |
54 const int kNavigationEntryYMargin = 1; | |
55 | |
56 // NavigationBar size. | |
57 const int kNavigationBarHeight = 25; | |
58 | |
59 // Padding for the bubble info window. | |
60 const int kBubbleWindowXPadding = 6; | |
61 const int kBubbleWindowYPadding = 16; | |
62 | |
63 // Command line switch for specifying url of the page. | |
64 const wchar_t kURLSwitch[] = L"main-menu-url"; | |
65 | |
66 // Command line switch for specifying the size of the main menu. The default is | |
67 // full screen. | |
68 const wchar_t kMenuSizeSwitch[] = L"main-menu-size"; | |
69 | |
70 // URL of the page to load. This is ignored if kURLSwitch is specified. | |
71 const char kMenuURL[] = "http://goto.ext.google.com/crux-home"; | |
72 | |
73 // Returns the URL of the menu. | |
74 static GURL GetMenuURL() { | |
75 std::wstring url_string = | |
76 CommandLine::ForCurrentProcess()->GetSwitchValue(kURLSwitch); | |
77 if (!url_string.empty()) | |
78 return GURL(WideToUTF8(url_string)); | |
79 return GURL(kMenuURL); | |
80 } | |
81 | |
82 // RenderWidgetHostViewGtk propagates the mouse press events (see | |
83 // render_widget_host_view_gtk.cc). We subclass to stop the propagation here, | |
84 // as if the click event was propagated it would reach the TopContainer view and | |
85 // it would close the popup. | |
86 class RWHVNativeViewHost : public views::NativeViewHost { | |
87 public: | |
88 RWHVNativeViewHost() {} | |
89 | |
90 virtual bool OnMousePressed(const views::MouseEvent& event) { return true; } | |
91 | |
92 private: | |
93 DISALLOW_COPY_AND_ASSIGN(RWHVNativeViewHost); | |
94 }; | |
95 | |
96 } // namspace | |
97 | |
98 namespace chromeos { | |
99 | |
100 //////////////////////////////////////////////////////////////////////////////// | |
101 // NavigationBar | |
102 // | |
103 // A navigation bar that is shown in the app launcher in compact navigation bar | |
104 // mode. | |
105 | |
106 class NavigationBar : public views::View, | |
107 public AutocompleteEditController, | |
108 public BubblePositioner { | |
109 public: | |
110 explicit NavigationBar(AppLauncher* app_launcher) | |
111 : views::View(), | |
112 app_launcher_(app_launcher), | |
113 location_entry_view_(NULL) { | |
114 SetFocusable(true); | |
115 location_entry_view_ = new views::NativeViewHost; | |
116 AddChildView(location_entry_view_); | |
117 } | |
118 | |
119 virtual ~NavigationBar() { | |
120 if (location_entry_view_->native_view()) | |
121 location_entry_view_->Detach(); | |
122 } | |
123 | |
124 // views::View overrides. | |
125 virtual void Focus() { | |
126 location_entry_->SetFocus(); | |
127 location_entry_->SelectAll(true); | |
128 } | |
129 | |
130 virtual void Layout() { | |
131 gfx::Rect bounds = GetLocalBounds(false); | |
132 | |
133 const int vertical_margin = | |
134 kNavigationEntryPadding + kNavigationEntryYMargin; | |
135 | |
136 location_entry_view_->SetBounds( | |
137 bounds.x() + kNavigationEntryXMargin + kNavigationEntryPadding, | |
138 bounds.y() + vertical_margin, | |
139 bounds.width() - 2 * (kNavigationEntryPadding + | |
140 kNavigationEntryXMargin), | |
141 bounds.height() - vertical_margin * 2); | |
142 } | |
143 | |
144 virtual void Paint(gfx::Canvas* canvas) { | |
145 const int padding = kNavigationEntryPadding; | |
146 canvas->FillRectInt(SK_ColorWHITE, 0, 0, width(), height()); | |
147 // Draw border around the entry. | |
148 gfx::Rect bounds = location_entry_view_->GetBounds( | |
149 views::View::APPLY_MIRRORING_TRANSFORMATION); | |
150 canvas->DrawRectInt(SK_ColorGRAY, | |
151 bounds.x() - padding, | |
152 bounds.y() - padding, | |
153 bounds.width() + padding * 2, | |
154 bounds.height() + padding * 2); | |
155 } | |
156 | |
157 // BubblePositioner implementation. | |
158 virtual gfx::Rect GetLocationStackBounds() const { | |
159 gfx::Rect bounds = location_entry_view_->GetBounds( | |
160 views::View::APPLY_MIRRORING_TRANSFORMATION); | |
161 gfx::Point origin(bounds.x(), bounds.bottom() + kNavigationEntryPadding); | |
162 views::View::ConvertPointToScreen(this, &origin); | |
163 gfx::Rect rect = gfx::Rect(origin, gfx::Size(500, 0)); | |
164 if (UILayoutIsRightToLeft()) { | |
165 // Align the window to the right side of the entry view when | |
166 // UI is RTL mode. | |
167 rect.set_x(rect.x() - (rect.width() - location_entry_view_->width())); | |
168 } | |
169 return rect; | |
170 } | |
171 | |
172 // AutocompleteController implementation. | |
173 virtual void OnAutocompleteAccept(const GURL& url, | |
174 WindowOpenDisposition disposition, | |
175 PageTransition::Type transition, | |
176 const GURL& alternate_nav_url) { | |
177 app_launcher_->AddTabWithURL(url, transition); | |
178 app_launcher_->Hide(); | |
179 } | |
180 virtual void OnChanged() {} | |
181 virtual void OnInputInProgress(bool in_progress) {} | |
182 virtual void OnKillFocus() {} | |
183 virtual void OnSetFocus() { | |
184 views::FocusManager* focus_manager = GetFocusManager(); | |
185 if (!focus_manager) { | |
186 NOTREACHED(); | |
187 return; | |
188 } | |
189 focus_manager->SetFocusedView(this); | |
190 } | |
191 virtual SkBitmap GetFavIcon() const { | |
192 return SkBitmap(); | |
193 } | |
194 virtual std::wstring GetTitle() const { | |
195 return std::wstring(); | |
196 } | |
197 | |
198 // AutocompleteEditView depends on the browser instance. | |
199 // Create new one when the browser instance changes. | |
200 void Update(Browser* browser) { | |
201 // Detach the native view if any. | |
202 if (location_entry_view_ && location_entry_view_->native_view()) | |
203 location_entry_view_->Detach(); | |
204 | |
205 location_entry_.reset(new AutocompleteEditViewGtk( | |
206 this, browser->toolbar_model(), browser->profile(), | |
207 browser->command_updater(), false, this)); | |
208 location_entry_->Init(); | |
209 gtk_widget_show_all(location_entry_->widget()); | |
210 gtk_widget_hide(location_entry_->widget()); | |
211 | |
212 location_entry_view_->set_focus_view(this); | |
213 location_entry_view_->Attach(location_entry_->widget()); | |
214 } | |
215 | |
216 private: | |
217 AppLauncher* app_launcher_; | |
218 views::NativeViewHost* location_entry_view_; | |
219 scoped_ptr<AutocompleteEditViewGtk> location_entry_; | |
220 | |
221 DISALLOW_COPY_AND_ASSIGN(NavigationBar); | |
222 }; | |
223 | |
224 //////////////////////////////////////////////////////////////////////////////// | |
225 // TopContainer | |
226 // | |
227 // A view that grays-out the browser and contains the navigation bar and | |
228 // renderer view. | |
229 | |
230 AppLauncher::TopContainer::TopContainer(AppLauncher* app_launcher) | |
231 : app_launcher_(app_launcher) { | |
232 // Use a transparent black background so the browser appears grayed-out. | |
233 set_background(views::Background::CreateSolidBackground(0, 0, 0, 100)); | |
234 } | |
235 | |
236 void AppLauncher::TopContainer::Layout() { | |
237 if (bounds().IsEmpty()) | |
238 return; | |
239 | |
240 // We only expect to contain the BubbleContents. | |
241 DCHECK(GetChildViewCount() == 1); | |
242 GetChildViewAt(0)->SetBounds(kBubbleWindowXPadding, kBubbleWindowYPadding, | |
243 width() * 2 / 3, height() * 4 / 5); | |
244 } | |
245 | |
246 bool AppLauncher::TopContainer::OnMousePressed(const views::MouseEvent& event) { | |
247 // Clicking outside the bubble closes the bubble. | |
248 app_launcher_->Hide(); | |
249 return false; | |
250 } | |
251 | |
252 //////////////////////////////////////////////////////////////////////////////// | |
253 // BubbleContainer | |
254 // | |
255 // The view that contains the navigation bar and render view. It has a bubble | |
256 // border. | |
257 | |
258 AppLauncher::BubbleContainer::BubbleContainer(AppLauncher* app_launcher) | |
259 : app_launcher_(app_launcher) { | |
260 BubbleBorder* bubble_border = new BubbleBorder(); | |
261 bubble_border->set_arrow_location(BubbleBorder::TOP_LEFT); | |
262 set_border(bubble_border); | |
263 set_background(new BubbleBackground(bubble_border)); | |
264 } | |
265 | |
266 void AppLauncher::BubbleContainer::Layout() { | |
267 if (bounds().IsEmpty() || GetChildViewCount() == 0) | |
268 return; | |
269 | |
270 gfx::Rect bounds = GetLocalBounds(false); | |
271 // TODO(jcampan): figure-out why we need to inset for the contained view not | |
272 // to paint over the bubble border. | |
273 bounds.Inset(2, 2); | |
274 | |
275 app_launcher_->navigation_bar_->SetBounds(bounds.x(), bounds.y(), | |
276 bounds.width(), | |
277 kNavigationBarHeight); | |
278 int render_y = app_launcher_->navigation_bar_->bounds().bottom(); | |
279 gfx::Size rwhv_size = | |
280 gfx::Size(bounds.width(), | |
281 std::max(0, bounds.height() - render_y + bounds.y())); | |
282 app_launcher_->render_view_container_->SetBounds(bounds.x(), render_y, | |
283 rwhv_size.width(), | |
284 rwhv_size.height()); | |
285 app_launcher_->rwhv_->SetSize(rwhv_size); | |
286 } | |
287 | |
288 //////////////////////////////////////////////////////////////////////////////// | |
289 // AppLauncher | |
290 | |
291 AppLauncher::AppLauncher(Browser* browser) | |
292 : browser_(browser), | |
293 popup_(NULL), | |
294 site_instance_(NULL), | |
295 contents_rvh_(NULL), | |
296 rwhv_(NULL), | |
297 ALLOW_THIS_IN_INITIALIZER_LIST(tab_contents_delegate_(this)), | |
298 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), | |
299 top_container_(NULL), | |
300 bubble_container_(NULL), | |
301 navigation_bar_(NULL), | |
302 render_view_container_(NULL), | |
303 has_shown_(false) { | |
304 popup_ = new views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW); | |
305 // The background image has transparency, so we make the window transparent. | |
306 popup_->MakeTransparent(); | |
307 popup_->Init(NULL, gfx::Rect()); | |
308 WmIpc::instance()->SetWindowType( | |
309 popup_->GetNativeView(), | |
310 WmIpc::WINDOW_TYPE_CHROME_INFO_BUBBLE, | |
311 NULL); | |
312 | |
313 // Register Esc as an accelerator for closing the app launcher. | |
314 views::FocusManager* focus_manager = popup_->GetFocusManager(); | |
315 focus_manager->RegisterAccelerator(views::Accelerator(base::VKEY_ESCAPE, | |
316 false, false, false), | |
317 this); | |
318 | |
319 top_container_ = new TopContainer(this); | |
320 popup_->SetContentsView(top_container_); | |
321 | |
322 bubble_container_ = new BubbleContainer(this); | |
323 top_container_->AddChildView(bubble_container_); | |
324 navigation_bar_ = new NavigationBar(this); | |
325 bubble_container_->AddChildView(navigation_bar_); | |
326 | |
327 GURL menu_url(GetMenuURL()); | |
328 DCHECK(BrowserList::begin() != BrowserList::end()); | |
329 // TODO(sky): this shouldn't pick a random profile to use. | |
330 Profile* profile = (*BrowserList::begin())->profile(); | |
331 int64 session_storage_namespace_id = profile->GetWebKitContext()-> | |
332 dom_storage_context()->AllocateSessionStorageNamespaceId(); | |
333 site_instance_ = SiteInstance::CreateSiteInstanceForURL(profile, menu_url); | |
334 contents_rvh_ = new RenderViewHost(site_instance_, this, MSG_ROUTING_NONE, | |
335 session_storage_namespace_id); | |
336 | |
337 rwhv_ = new RenderWidgetHostViewGtk(contents_rvh_); | |
338 rwhv_->InitAsChild(); | |
339 contents_rvh_->CreateRenderView(profile->GetRequestContext()); | |
340 | |
341 render_view_container_ = new RWHVNativeViewHost; | |
342 bubble_container_->AddChildView(render_view_container_); | |
343 render_view_container_->Attach(rwhv_->GetNativeView()); | |
344 contents_rvh_->NavigateToURL(menu_url); | |
345 | |
346 navigation_bar_->Update(browser); | |
347 // Set the transient window so that ChromeOS WM treat this | |
348 // as if a popup window. | |
349 gtk_window_set_transient_for( | |
350 GTK_WINDOW(popup_->GetNativeView()), | |
351 GTK_WINDOW(browser_->window()->GetNativeHandle())); | |
352 | |
353 ActiveWindowWatcherX::AddObserver(this); | |
354 } | |
355 | |
356 AppLauncher::~AppLauncher() { | |
357 contents_rvh_->Shutdown(); | |
358 popup_->CloseNow(); | |
359 ActiveWindowWatcherX::RemoveObserver(this); | |
360 } | |
361 | |
362 void AppLauncher::Update() { | |
363 popup_->SetBounds(browser_->window()->GetRestoredBounds()); | |
364 top_container_->Layout(); | |
365 } | |
366 | |
367 void AppLauncher::Show() { | |
368 Cleanup(); | |
369 | |
370 Update(); | |
371 popup_->Show(); | |
372 | |
373 GtkWidget* rwhv_widget = rwhv_->GetNativeView(); | |
374 if (!has_shown_) { | |
375 has_shown_ = true; | |
376 gtk_widget_realize(rwhv_widget); | |
377 } | |
378 } | |
379 | |
380 void AppLauncher::ActiveWindowChanged(GdkWindow* active_window) { | |
381 if (!popup_->IsActive()) | |
382 Hide(); | |
383 else | |
384 navigation_bar_->RequestFocus(); | |
385 } | |
386 | |
387 bool AppLauncher::AcceleratorPressed(const views::Accelerator& accelerator) { | |
388 DCHECK(accelerator.GetKeyCode() == base::VKEY_ESCAPE); | |
389 popup_->Hide(); | |
390 return true; | |
391 } | |
392 | |
393 void AppLauncher::Hide() { | |
394 popup_->Hide(); | |
395 // The stack may have pending_contents_ on it. Delay deleting the | |
396 // pending_contents_ as TabContents doesn't deal well with being deleted | |
397 // while on the stack. | |
398 MessageLoop::current()->PostTask(FROM_HERE, | |
399 method_factory_.NewRunnableMethod(&AppLauncher::Cleanup)); | |
400 } | |
401 | |
402 void AppLauncher::Cleanup() { | |
403 pending_contents_.reset(NULL); | |
404 method_factory_.RevokeAll(); | |
405 } | |
406 | |
407 void AppLauncher::RequestMove(const gfx::Rect& new_bounds) { | |
408 // Invoking PositionChild results in a gtk signal that triggers attempting to | |
409 // to resize the window. We need to set the size request so that it resizes | |
410 // correctly when this happens. | |
411 gtk_widget_set_size_request(popup_->GetNativeView(), | |
412 new_bounds.width(), new_bounds.height()); | |
413 popup_->SetBounds(new_bounds); | |
414 } | |
415 | |
416 RendererPreferences AppLauncher::GetRendererPrefs(Profile* profile) const { | |
417 RendererPreferences preferences; | |
418 renderer_preferences_util::UpdateFromSystemSettings(&preferences, profile); | |
419 return preferences; | |
420 } | |
421 | |
422 void AppLauncher::AddTabWithURL(const GURL& url, | |
423 PageTransition::Type transition) { | |
424 switch (StatusAreaView::GetOpenTabsMode()) { | |
425 case StatusAreaView::OPEN_TABS_ON_LEFT: { | |
426 // Add the new tab at the first non-pinned location. | |
427 int index = browser_->tabstrip_model()->IndexOfFirstNonMiniTab(); | |
428 browser_->AddTabWithURL(url, GURL(), transition, | |
429 true, index, true, NULL); | |
430 break; | |
431 } | |
432 case StatusAreaView::OPEN_TABS_CLOBBER: { | |
433 browser_->GetSelectedTabContents()->controller().LoadURL( | |
434 url, GURL(), transition); | |
435 break; | |
436 } | |
437 case StatusAreaView::OPEN_TABS_ON_RIGHT: { | |
438 browser_->AddTabWithURL(url, GURL(), transition, true, -1, true, NULL); | |
439 break; | |
440 } | |
441 } | |
442 } | |
443 | |
444 void AppLauncher::CreateNewWindow(int route_id) { | |
445 if (pending_contents_.get()) { | |
446 NOTREACHED(); | |
447 return; | |
448 } | |
449 | |
450 helper_.CreateNewWindow(route_id, browser_->profile(), site_instance_, | |
451 DOMUIFactory::GetDOMUIType(GURL(GetMenuURL())), | |
452 NULL); | |
453 pending_contents_.reset(helper_.GetCreatedWindow(route_id)); | |
454 pending_contents_->set_delegate(&tab_contents_delegate_); | |
455 } | |
456 | |
457 void AppLauncher::ShowCreatedWindow(int route_id, | |
458 WindowOpenDisposition disposition, | |
459 const gfx::Rect& initial_pos, | |
460 bool user_gesture) { | |
461 if (disposition == NEW_POPUP) { | |
462 pending_contents_->set_delegate(NULL); | |
463 browser_->GetSelectedTabContents()->AddNewContents( | |
464 pending_contents_.release(), disposition, initial_pos, user_gesture); | |
465 Hide(); | |
466 } | |
467 } | |
468 | |
469 void AppLauncher::StartDragging(const WebDropData& drop_data, | |
470 WebKit::WebDragOperationsMask allowed_ops, | |
471 const SkBitmap& image, | |
472 const gfx::Point& image_offset) { | |
473 // We're not going to do any drag & drop, but we have to tell the renderer the | |
474 // drag & drop ended, othewise the renderer thinks the drag operation is | |
475 // underway and mouse events won't work. | |
476 contents_rvh_->DragSourceSystemDragEnded(); | |
477 } | |
478 | |
479 AppLauncher::TabContentsDelegateImpl::TabContentsDelegateImpl( | |
480 AppLauncher* app_launcher) | |
481 : app_launcher_(app_launcher) { | |
482 } | |
483 | |
484 void AppLauncher::TabContentsDelegateImpl::OpenURLFromTab( | |
485 TabContents* source, | |
486 const GURL& url, | |
487 const GURL& referrer, | |
488 WindowOpenDisposition disposition, | |
489 PageTransition::Type transition) { | |
490 app_launcher_->browser_->OpenURL(url, referrer, NEW_FOREGROUND_TAB, | |
491 PageTransition::LINK); | |
492 app_launcher_->Hide(); | |
493 } | |
494 | |
495 } // namespace chromeos | |
OLD | NEW |