location_bar_view_mac.mm revision 3f50c38dc070f4bb515c1b64450dae14f316474e
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#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
6
7#include "app/l10n_util_mac.h"
8#include "app/resource_bundle.h"
9#include "base/stl_util-inl.h"
10#include "base/string_util.h"
11#include "base/sys_string_conversions.h"
12#include "base/utf_string_conversions.h"
13#include "chrome/app/chrome_command_ids.h"
14#include "chrome/browser/alternate_nav_url_fetcher.h"
15#import "chrome/browser/app_controller_mac.h"
16#import "chrome/browser/autocomplete/autocomplete_edit_view_mac.h"
17#import "chrome/browser/autocomplete/autocomplete_popup_model.h"
18#include "chrome/browser/browser_list.h"
19#include "chrome/browser/command_updater.h"
20#include "chrome/browser/content_setting_image_model.h"
21#include "chrome/browser/content_setting_bubble_model.h"
22#include "chrome/browser/defaults.h"
23#include "chrome/browser/extensions/extension_browser_event_router.h"
24#include "chrome/browser/extensions/extension_service.h"
25#include "chrome/browser/extensions/extension_tabs_module.h"
26#include "chrome/browser/instant/instant_controller.h"
27#include "chrome/browser/profiles/profile.h"
28#include "chrome/browser/search_engines/template_url.h"
29#include "chrome/browser/search_engines/template_url_model.h"
30#include "chrome/browser/tab_contents/navigation_entry.h"
31#include "chrome/browser/tab_contents/tab_contents.h"
32#import "chrome/browser/ui/cocoa/content_setting_bubble_cocoa.h"
33#include "chrome/browser/ui/cocoa/event_utils.h"
34#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h"
35#import "chrome/browser/ui/cocoa/extensions/extension_popup_controller.h"
36#import "chrome/browser/ui/cocoa/first_run_bubble_controller.h"
37#import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
38#import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h"
39#import "chrome/browser/ui/cocoa/location_bar/content_setting_decoration.h"
40#import "chrome/browser/ui/cocoa/location_bar/ev_bubble_decoration.h"
41#import "chrome/browser/ui/cocoa/location_bar/keyword_hint_decoration.h"
42#import "chrome/browser/ui/cocoa/location_bar/location_icon_decoration.h"
43#import "chrome/browser/ui/cocoa/location_bar/page_action_decoration.h"
44#import "chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.h"
45#import "chrome/browser/ui/cocoa/location_bar/star_decoration.h"
46#include "chrome/browser/ui/omnibox/location_bar_util.h"
47#include "chrome/common/extensions/extension.h"
48#include "chrome/common/extensions/extension_action.h"
49#include "chrome/common/extensions/extension_resource.h"
50#include "chrome/common/notification_service.h"
51#include "chrome/common/pref_names.h"
52#include "net/base/net_util.h"
53#include "grit/generated_resources.h"
54#include "grit/theme_resources.h"
55#include "skia/ext/skia_utils_mac.h"
56#include "third_party/skia/include/core/SkBitmap.h"
57
58namespace {
59
60// Vertical space between the bottom edge of the location_bar and the first run
61// bubble arrow point.
62const static int kFirstRunBubbleYOffset = 1;
63
64}
65
66// TODO(shess): This code is mostly copied from the gtk
67// implementation.  Make sure it's all appropriate and flesh it out.
68
69LocationBarViewMac::LocationBarViewMac(
70    AutocompleteTextField* field,
71    CommandUpdater* command_updater,
72    ToolbarModel* toolbar_model,
73    Profile* profile,
74    Browser* browser)
75    : edit_view_(new AutocompleteEditViewMac(this, toolbar_model, profile,
76                                             command_updater, field)),
77      command_updater_(command_updater),
78      field_(field),
79      disposition_(CURRENT_TAB),
80      location_icon_decoration_(new LocationIconDecoration(this)),
81      selected_keyword_decoration_(
82          new SelectedKeywordDecoration(
83              AutocompleteEditViewMac::GetFieldFont())),
84      ev_bubble_decoration_(
85          new EVBubbleDecoration(location_icon_decoration_.get(),
86                                 AutocompleteEditViewMac::GetFieldFont())),
87      star_decoration_(new StarDecoration(command_updater)),
88      keyword_hint_decoration_(
89          new KeywordHintDecoration(AutocompleteEditViewMac::GetFieldFont())),
90      profile_(profile),
91      browser_(browser),
92      toolbar_model_(toolbar_model),
93      update_instant_(true),
94      transition_(PageTransition::TYPED),
95      first_run_bubble_(this) {
96  for (size_t i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
97    DCHECK_EQ(i, content_setting_decorations_.size());
98    ContentSettingsType type = static_cast<ContentSettingsType>(i);
99    content_setting_decorations_.push_back(
100        new ContentSettingDecoration(type, this, profile_));
101  }
102
103  registrar_.Add(this,
104      NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
105      NotificationService::AllSources());
106}
107
108LocationBarViewMac::~LocationBarViewMac() {
109  // Disconnect from cell in case it outlives us.
110  [[field_ cell] clearDecorations];
111}
112
113void LocationBarViewMac::ShowFirstRunBubble(FirstRun::BubbleType bubble_type) {
114  // We need the browser window to be shown before we can show the bubble, but
115  // we get called before that's happened.
116  Task* task = first_run_bubble_.NewRunnableMethod(
117      &LocationBarViewMac::ShowFirstRunBubbleInternal, bubble_type);
118  MessageLoop::current()->PostTask(FROM_HERE, task);
119}
120
121void LocationBarViewMac::ShowFirstRunBubbleInternal(
122    FirstRun::BubbleType bubble_type) {
123  if (!field_ || ![field_ window])
124    return;
125
126  // The first run bubble's left edge should line up with the left edge of the
127  // omnibox. This is different from other bubbles, which line up at a point
128  // set by their top arrow. Because the BaseBubbleController adjusts the
129  // window origin left to account for the arrow spacing, the first run bubble
130  // moves the window origin right by this spacing, so that the
131  // BaseBubbleController will move it back to the correct position.
132  const NSPoint kOffset = NSMakePoint(
133      info_bubble::kBubbleArrowXOffset + info_bubble::kBubbleArrowWidth/2.0,
134      kFirstRunBubbleYOffset);
135  [FirstRunBubbleController showForView:field_ offset:kOffset profile:profile_];
136}
137
138std::wstring LocationBarViewMac::GetInputString() const {
139  return location_input_;
140}
141
142void LocationBarViewMac::SetSuggestedText(const string16& text) {
143  edit_view_->SetSuggestText(
144      edit_view_->model()->UseVerbatimInstant() ? string16() : text);
145}
146
147WindowOpenDisposition LocationBarViewMac::GetWindowOpenDisposition() const {
148  return disposition_;
149}
150
151PageTransition::Type LocationBarViewMac::GetPageTransition() const {
152  return transition_;
153}
154
155void LocationBarViewMac::AcceptInput() {
156  WindowOpenDisposition disposition =
157      event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]);
158  edit_view_->model()->AcceptInput(disposition, false);
159}
160
161void LocationBarViewMac::FocusLocation(bool select_all) {
162  edit_view_->FocusLocation(select_all);
163}
164
165void LocationBarViewMac::FocusSearch() {
166  edit_view_->SetForcedQuery();
167}
168
169void LocationBarViewMac::UpdateContentSettingsIcons() {
170  if (RefreshContentSettingsDecorations()) {
171    [field_ updateCursorAndToolTipRects];
172    [field_ setNeedsDisplay:YES];
173  }
174}
175
176void LocationBarViewMac::UpdatePageActions() {
177  size_t count_before = page_action_decorations_.size();
178  RefreshPageActionDecorations();
179  Layout();
180  if (page_action_decorations_.size() != count_before) {
181    NotificationService::current()->Notify(
182        NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED,
183        Source<LocationBar>(this),
184        NotificationService::NoDetails());
185  }
186}
187
188void LocationBarViewMac::InvalidatePageActions() {
189  size_t count_before = page_action_decorations_.size();
190  DeletePageActionDecorations();
191  Layout();
192  if (page_action_decorations_.size() != count_before) {
193    NotificationService::current()->Notify(
194        NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED,
195        Source<LocationBar>(this),
196        NotificationService::NoDetails());
197  }
198}
199
200void LocationBarViewMac::SaveStateToContents(TabContents* contents) {
201  // TODO(shess): Why SaveStateToContents vs SaveStateToTab?
202  edit_view_->SaveStateToTab(contents);
203}
204
205void LocationBarViewMac::Update(const TabContents* contents,
206                                bool should_restore_state) {
207  bool star_enabled = browser_defaults::bookmarks_enabled &&
208      [field_ isEditable] && !toolbar_model_->input_in_progress();
209  command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled);
210  star_decoration_->SetVisible(star_enabled);
211  RefreshPageActionDecorations();
212  RefreshContentSettingsDecorations();
213  // AutocompleteEditView restores state if the tab is non-NULL.
214  edit_view_->Update(should_restore_state ? contents : NULL);
215  OnChanged();
216}
217
218void LocationBarViewMac::OnAutocompleteWillClosePopup() {
219  if (!update_instant_)
220    return;
221
222  InstantController* controller = browser_->instant();
223  if (controller && !controller->commit_on_mouse_up())
224    controller->DestroyPreviewContents();
225  SetSuggestedText(string16());
226}
227
228void LocationBarViewMac::OnAutocompleteLosingFocus(gfx::NativeView unused) {
229  SetSuggestedText(string16());
230
231  InstantController* instant = browser_->instant();
232  if (!instant)
233    return;
234
235  // If |IsMouseDownFromActivate()| returns false, the RenderWidgetHostView did
236  // not receive a mouseDown event.  Therefore, we should destroy the preview.
237  // Otherwise, the RWHV was clicked, so we commit the preview.
238  if (!instant->is_displayable() || !instant->GetPreviewContents() ||
239      !instant->IsMouseDownFromActivate()) {
240    instant->DestroyPreviewContents();
241  } else if (instant->IsShowingInstant()) {
242    instant->SetCommitOnMouseUp();
243  } else {
244    instant->CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
245  }
246}
247
248void LocationBarViewMac::OnAutocompleteWillAccept() {
249  update_instant_ = false;
250}
251
252bool LocationBarViewMac::OnCommitSuggestedText(const std::wstring& typed_text) {
253  return edit_view_->CommitSuggestText();
254}
255
256bool LocationBarViewMac::AcceptCurrentInstantPreview() {
257  return InstantController::CommitIfCurrent(browser_->instant());
258}
259
260void LocationBarViewMac::OnSetSuggestedSearchText(
261    const string16& suggested_text) {
262  SetSuggestedText(suggested_text);
263}
264
265void LocationBarViewMac::OnPopupBoundsChanged(const gfx::Rect& bounds) {
266  InstantController* instant = browser_->instant();
267  if (instant)
268    instant->SetOmniboxBounds(bounds);
269}
270
271void LocationBarViewMac::OnAutocompleteAccept(const GURL& url,
272                                              WindowOpenDisposition disposition,
273                                              PageTransition::Type transition,
274                                              const GURL& alternate_nav_url) {
275  // WARNING: don't add an early return here. The calls after the if must
276  // happen.
277  if (url.is_valid()) {
278    location_input_ = UTF8ToWide(url.spec());
279    disposition_ = disposition;
280    transition_ = transition;
281
282    if (command_updater_) {
283      if (!alternate_nav_url.is_valid()) {
284        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
285      } else {
286        AlternateNavURLFetcher* fetcher =
287            new AlternateNavURLFetcher(alternate_nav_url);
288        // The AlternateNavURLFetcher will listen for the pending navigation
289        // notification that will be issued as a result of the "open URL." It
290        // will automatically install itself into that navigation controller.
291        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
292        if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) {
293          // I'm not sure this should be reachable, but I'm not also sure enough
294          // that it shouldn't to stick in a NOTREACHED().  In any case, this is
295          // harmless.
296          delete fetcher;
297        } else {
298          // The navigation controller will delete the fetcher.
299        }
300      }
301    }
302  }
303
304  if (browser_->instant() && !edit_view_->model()->popup_model()->IsOpen())
305    browser_->instant()->DestroyPreviewContents();
306
307  update_instant_ = true;
308}
309
310void LocationBarViewMac::OnChanged() {
311  // Update the location-bar icon.
312  const int resource_id = edit_view_->GetIcon();
313  NSImage* image = AutocompleteEditViewMac::ImageForResource(resource_id);
314  location_icon_decoration_->SetImage(image);
315  ev_bubble_decoration_->SetImage(image);
316  Layout();
317
318  InstantController* instant = browser_->instant();
319  string16 suggested_text;
320  if (update_instant_ && instant && GetTabContents()) {
321    if (edit_view_->model()->user_input_in_progress() &&
322        edit_view_->model()->popup_model()->IsOpen()) {
323      instant->Update
324          (browser_->GetSelectedTabContentsWrapper(),
325           edit_view_->model()->CurrentMatch(),
326           WideToUTF16(edit_view_->GetText()),
327           edit_view_->model()->UseVerbatimInstant(),
328           &suggested_text);
329      if (!instant->MightSupportInstant()) {
330        edit_view_->model()->FinalizeInstantQuery(std::wstring(),
331                                                  std::wstring());
332      }
333    } else {
334      instant->DestroyPreviewContents();
335      edit_view_->model()->FinalizeInstantQuery(std::wstring(),
336                                                std::wstring());
337    }
338  }
339
340  SetSuggestedText(suggested_text);
341}
342
343void LocationBarViewMac::OnSelectionBoundsChanged() {
344  NOTIMPLEMENTED();
345}
346
347void LocationBarViewMac::OnInputInProgress(bool in_progress) {
348  toolbar_model_->set_input_in_progress(in_progress);
349  Update(NULL, false);
350}
351
352void LocationBarViewMac::OnSetFocus() {
353  // Update the keyword and search hint states.
354  OnChanged();
355}
356
357void LocationBarViewMac::OnKillFocus() {
358  // Do nothing.
359}
360
361SkBitmap LocationBarViewMac::GetFavIcon() const {
362  NOTIMPLEMENTED();
363  return SkBitmap();
364}
365
366std::wstring LocationBarViewMac::GetTitle() const {
367  NOTIMPLEMENTED();
368  return std::wstring();
369}
370
371void LocationBarViewMac::Revert() {
372  edit_view_->RevertAll();
373}
374
375// TODO(pamg): Change all these, here and for other platforms, to size_t.
376int LocationBarViewMac::PageActionCount() {
377  return static_cast<int>(page_action_decorations_.size());
378}
379
380int LocationBarViewMac::PageActionVisibleCount() {
381  int result = 0;
382  for (size_t i = 0; i < page_action_decorations_.size(); ++i) {
383    if (page_action_decorations_[i]->IsVisible())
384      ++result;
385  }
386  return result;
387}
388
389TabContents* LocationBarViewMac::GetTabContents() const {
390  return browser_->GetSelectedTabContents();
391}
392
393PageActionDecoration* LocationBarViewMac::GetPageActionDecoration(
394    ExtensionAction* page_action) {
395  DCHECK(page_action);
396  for (size_t i = 0; i < page_action_decorations_.size(); ++i) {
397    if (page_action_decorations_[i]->page_action() == page_action)
398      return page_action_decorations_[i];
399  }
400  // If |page_action| is the browser action of an extension, no element in
401  // |page_action_decorations_| will match.
402  NOTREACHED();
403  return NULL;
404}
405
406void LocationBarViewMac::SetPreviewEnabledPageAction(
407    ExtensionAction* page_action, bool preview_enabled) {
408  DCHECK(page_action);
409  TabContents* contents = GetTabContents();
410  if (!contents)
411    return;
412  RefreshPageActionDecorations();
413  Layout();
414
415  PageActionDecoration* decoration = GetPageActionDecoration(page_action);
416  DCHECK(decoration);
417  if (!decoration)
418    return;
419
420  decoration->set_preview_enabled(preview_enabled);
421  decoration->UpdateVisibility(contents,
422      GURL(WideToUTF8(toolbar_model_->GetText())));
423}
424
425NSPoint LocationBarViewMac::GetPageActionBubblePoint(
426    ExtensionAction* page_action) {
427  PageActionDecoration* decoration = GetPageActionDecoration(page_action);
428  if (!decoration)
429    return NSZeroPoint;
430
431  AutocompleteTextFieldCell* cell = [field_ cell];
432  NSRect frame = [cell frameForDecoration:decoration inFrame:[field_ bounds]];
433  DCHECK(!NSIsEmptyRect(frame));
434  if (NSIsEmptyRect(frame))
435    return NSZeroPoint;
436
437  NSPoint bubble_point = decoration->GetBubblePointInFrame(frame);
438  return [field_ convertPoint:bubble_point toView:nil];
439}
440
441NSRect LocationBarViewMac::GetBlockedPopupRect() const {
442  const size_t kPopupIndex = CONTENT_SETTINGS_TYPE_POPUPS;
443  const LocationBarDecoration* decoration =
444      content_setting_decorations_[kPopupIndex];
445  if (!decoration || !decoration->IsVisible())
446    return NSZeroRect;
447
448  AutocompleteTextFieldCell* cell = [field_ cell];
449  const NSRect frame = [cell frameForDecoration:decoration
450                                        inFrame:[field_ bounds]];
451  return [field_ convertRect:frame toView:nil];
452}
453
454ExtensionAction* LocationBarViewMac::GetPageAction(size_t index) {
455  if (index < page_action_decorations_.size())
456    return page_action_decorations_[index]->page_action();
457  NOTREACHED();
458  return NULL;
459}
460
461ExtensionAction* LocationBarViewMac::GetVisiblePageAction(size_t index) {
462  size_t current = 0;
463  for (size_t i = 0; i < page_action_decorations_.size(); ++i) {
464    if (page_action_decorations_[i]->IsVisible()) {
465      if (current == index)
466        return page_action_decorations_[i]->page_action();
467
468      ++current;
469    }
470  }
471
472  NOTREACHED();
473  return NULL;
474}
475
476void LocationBarViewMac::TestPageActionPressed(size_t index) {
477  DCHECK_LT(index, page_action_decorations_.size());
478  if (index < page_action_decorations_.size())
479    page_action_decorations_[index]->OnMousePressed(NSZeroRect);
480}
481
482void LocationBarViewMac::SetEditable(bool editable) {
483  [field_ setEditable:editable ? YES : NO];
484  star_decoration_->SetVisible(browser_defaults::bookmarks_enabled &&
485      editable && !toolbar_model_->input_in_progress());
486  UpdatePageActions();
487  Layout();
488}
489
490bool LocationBarViewMac::IsEditable() {
491  return [field_ isEditable] ? true : false;
492}
493
494void LocationBarViewMac::SetStarred(bool starred) {
495  star_decoration_->SetStarred(starred);
496
497  // TODO(shess): The field-editor frame and cursor rects should not
498  // change, here.
499  [field_ updateCursorAndToolTipRects];
500  [field_ resetFieldEditorFrameIfNeeded];
501  [field_ setNeedsDisplay:YES];
502}
503
504NSPoint LocationBarViewMac::GetBookmarkBubblePoint() const {
505  AutocompleteTextFieldCell* cell = [field_ cell];
506  const NSRect frame = [cell frameForDecoration:star_decoration_.get()
507                                        inFrame:[field_ bounds]];
508  const NSPoint point = star_decoration_->GetBubblePointInFrame(frame);
509  return [field_ convertPoint:point toView:nil];
510}
511
512NSPoint LocationBarViewMac::GetPageInfoBubblePoint() const {
513  AutocompleteTextFieldCell* cell = [field_ cell];
514  if (ev_bubble_decoration_->IsVisible()) {
515    const NSRect frame = [cell frameForDecoration:ev_bubble_decoration_.get()
516                                          inFrame:[field_ bounds]];
517    const NSPoint point = ev_bubble_decoration_->GetBubblePointInFrame(frame);
518    return [field_ convertPoint:point toView:nil];
519  } else {
520    const NSRect frame =
521        [cell frameForDecoration:location_icon_decoration_.get()
522                         inFrame:[field_ bounds]];
523    const NSPoint point =
524        location_icon_decoration_->GetBubblePointInFrame(frame);
525    return [field_ convertPoint:point toView:nil];
526  }
527}
528
529NSImage* LocationBarViewMac::GetKeywordImage(const std::wstring& keyword) {
530  const TemplateURL* template_url =
531      profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword);
532  if (template_url && template_url->IsExtensionKeyword()) {
533    const SkBitmap& bitmap = profile_->GetExtensionService()->
534        GetOmniboxIcon(template_url->GetExtensionId());
535    return gfx::SkBitmapToNSImage(bitmap);
536  }
537
538  return AutocompleteEditViewMac::ImageForResource(IDR_OMNIBOX_SEARCH);
539}
540
541void LocationBarViewMac::Observe(NotificationType type,
542                                 const NotificationSource& source,
543                                 const NotificationDetails& details) {
544  switch (type.value) {
545    case NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: {
546      TabContents* contents = GetTabContents();
547      if (Details<TabContents>(contents) != details)
548        return;
549
550      [field_ updateCursorAndToolTipRects];
551      [field_ setNeedsDisplay:YES];
552      break;
553    }
554    default:
555      NOTREACHED() << "Unexpected notification";
556      break;
557  }
558}
559
560void LocationBarViewMac::PostNotification(NSString* notification) {
561  [[NSNotificationCenter defaultCenter] postNotificationName:notification
562                                        object:[NSValue valueWithPointer:this]];
563}
564
565bool LocationBarViewMac::RefreshContentSettingsDecorations() {
566  const bool input_in_progress = toolbar_model_->input_in_progress();
567  TabContents* tab_contents =
568      input_in_progress ? NULL : browser_->GetSelectedTabContents();
569  bool icons_updated = false;
570  for (size_t i = 0; i < content_setting_decorations_.size(); ++i) {
571    icons_updated |=
572        content_setting_decorations_[i]->UpdateFromTabContents(tab_contents);
573  }
574  return icons_updated;
575}
576
577void LocationBarViewMac::DeletePageActionDecorations() {
578  // TODO(shess): Deleting these decorations could result in the cell
579  // refering to them before things are laid out again.  Meanwhile, at
580  // least fail safe.
581  [[field_ cell] clearDecorations];
582
583  page_action_decorations_.reset();
584}
585
586void LocationBarViewMac::RefreshPageActionDecorations() {
587  if (!IsEditable()) {
588    DeletePageActionDecorations();
589    return;
590  }
591
592  ExtensionService* service = profile_->GetExtensionService();
593  if (!service)
594    return;
595
596  std::vector<ExtensionAction*> page_actions;
597  for (size_t i = 0; i < service->extensions()->size(); ++i) {
598    if (service->extensions()->at(i)->page_action())
599      page_actions.push_back(service->extensions()->at(i)->page_action());
600  }
601
602  // On startup we sometimes haven't loaded any extensions. This makes sure
603  // we catch up when the extensions (and any Page Actions) load.
604  if (page_actions.size() != page_action_decorations_.size()) {
605    DeletePageActionDecorations();  // Delete the old views (if any).
606
607    for (size_t i = 0; i < page_actions.size(); ++i) {
608      page_action_decorations_.push_back(
609          new PageActionDecoration(this, profile_, page_actions[i]));
610    }
611  }
612
613  if (page_action_decorations_.empty())
614    return;
615
616  TabContents* contents = GetTabContents();
617  if (!contents)
618    return;
619
620  GURL url = GURL(WideToUTF8(toolbar_model_->GetText()));
621  for (size_t i = 0; i < page_action_decorations_.size(); ++i) {
622    page_action_decorations_[i]->UpdateVisibility(
623        toolbar_model_->input_in_progress() ? NULL : contents, url);
624  }
625}
626
627// TODO(shess): This function should over time grow to closely match
628// the views Layout() function.
629void LocationBarViewMac::Layout() {
630  AutocompleteTextFieldCell* cell = [field_ cell];
631
632  // Reset the left-hand decorations.
633  // TODO(shess): Shortly, this code will live somewhere else, like in
634  // the constructor.  I am still wrestling with how best to deal with
635  // right-hand decorations, which are not a static set.
636  [cell clearDecorations];
637  [cell addLeftDecoration:location_icon_decoration_.get()];
638  [cell addLeftDecoration:selected_keyword_decoration_.get()];
639  [cell addLeftDecoration:ev_bubble_decoration_.get()];
640  [cell addRightDecoration:star_decoration_.get()];
641
642  // Note that display order is right to left.
643  for (size_t i = 0; i < page_action_decorations_.size(); ++i) {
644    [cell addRightDecoration:page_action_decorations_[i]];
645  }
646  for (size_t i = 0; i < content_setting_decorations_.size(); ++i) {
647    [cell addRightDecoration:content_setting_decorations_[i]];
648  }
649
650  [cell addRightDecoration:keyword_hint_decoration_.get()];
651
652  // By default only the location icon is visible.
653  location_icon_decoration_->SetVisible(true);
654  selected_keyword_decoration_->SetVisible(false);
655  ev_bubble_decoration_->SetVisible(false);
656  keyword_hint_decoration_->SetVisible(false);
657
658  // Get the keyword to use for keyword-search and hinting.
659  const std::wstring keyword(edit_view_->model()->keyword());
660  std::wstring short_name;
661  bool is_extension_keyword = false;
662  if (!keyword.empty()) {
663    short_name = profile_->GetTemplateURLModel()->
664        GetKeywordShortName(keyword, &is_extension_keyword);
665  }
666
667  const bool is_keyword_hint = edit_view_->model()->is_keyword_hint();
668
669  if (!keyword.empty() && !is_keyword_hint) {
670    // Switch from location icon to keyword mode.
671    location_icon_decoration_->SetVisible(false);
672    selected_keyword_decoration_->SetVisible(true);
673    selected_keyword_decoration_->SetKeyword(short_name, is_extension_keyword);
674    selected_keyword_decoration_->SetImage(GetKeywordImage(keyword));
675  } else if (toolbar_model_->GetSecurityLevel() == ToolbarModel::EV_SECURE) {
676    // Switch from location icon to show the EV bubble instead.
677    location_icon_decoration_->SetVisible(false);
678    ev_bubble_decoration_->SetVisible(true);
679
680    std::wstring label(toolbar_model_->GetEVCertName());
681    ev_bubble_decoration_->SetFullLabel(base::SysWideToNSString(label));
682  } else if (!keyword.empty() && is_keyword_hint) {
683    keyword_hint_decoration_->SetKeyword(WideToUTF16Hack(short_name),
684                                         is_extension_keyword);
685    keyword_hint_decoration_->SetVisible(true);
686  }
687
688  // These need to change anytime the layout changes.
689  // TODO(shess): Anytime the field editor might have changed, the
690  // cursor rects almost certainly should have changed.  The tooltips
691  // might change even when the rects don't change.
692  [field_ resetFieldEditorFrameIfNeeded];
693  [field_ updateCursorAndToolTipRects];
694
695  [field_ setNeedsDisplay:YES];
696}
697