OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/cocoa/web_intent_picker_cocoa.h" | 5 #include "chrome/browser/ui/cocoa/web_intent_picker_cocoa.h" |
6 | 6 |
7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
11 #include "chrome/browser/profiles/profile.h" | 11 #include "chrome/browser/profiles/profile.h" |
12 #include "chrome/browser/ui/browser.h" | 12 #include "chrome/browser/ui/browser.h" |
13 #include "chrome/browser/ui/browser_window.h" | 13 #include "chrome/browser/ui/browser_window.h" |
14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
15 #include "chrome/browser/ui/cocoa/constrained_window_mac.h" | |
15 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" | 16 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" |
16 #import "chrome/browser/ui/cocoa/web_intent_bubble_controller.h" | 17 #import "chrome/browser/ui/cocoa/web_intent_bubble_controller.h" |
17 #include "chrome/browser/ui/intents/web_intent_inline_disposition_delegate.h" | 18 #include "chrome/browser/ui/intents/web_intent_inline_disposition_delegate.h" |
18 #include "chrome/browser/ui/intents/web_intent_picker.h" | 19 #include "chrome/browser/ui/intents/web_intent_picker.h" |
19 #include "chrome/browser/ui/intents/web_intent_picker_delegate.h" | 20 #include "chrome/browser/ui/intents/web_intent_picker_delegate.h" |
20 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 21 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
21 #include "content/public/browser/web_contents.h" | 22 #include "content/public/browser/web_contents.h" |
22 #include "skia/ext/skia_utils_mac.h" | 23 #include "skia/ext/skia_utils_mac.h" |
23 #include "ui/gfx/image/image.h" | 24 #include "ui/gfx/image/image.h" |
24 | 25 |
25 using content::WebContents; | 26 using content::WebContents; |
26 | 27 |
28 namespace { | |
29 | |
30 // Since any delegates for constrained windows are tasked with deleting | |
31 // themselves, and the WebIntentPicker needs to actually live longer than the | |
Nico
2012/03/03 06:13:49
s/actually//
groby-ooo-7-16
2012/03/03 23:41:49
Done.
| |
32 // constrained window, we need this forwarding class | |
Nico
2012/03/03 06:13:49
.
groby-ooo-7-16
2012/03/03 23:41:49
Done.
| |
33 class ConstrainedPickerSheetDelegate : | |
34 public ConstrainedWindowMacDelegateCustomSheet { | |
35 public: | |
36 ConstrainedPickerSheetDelegate(WebIntentPickerCocoa* picker, | |
37 WebIntentBubbleController* sheet_controller); | |
Nico
2012/03/03 06:13:49
indent 1 more
groby-ooo-7-16
2012/03/03 23:41:49
Done.
| |
38 virtual ~ConstrainedPickerSheetDelegate() {} | |
39 | |
40 // ConstrainedWindowMacDelegateCustomSheet interface | |
41 virtual void DeleteDelegate() OVERRIDE; | |
42 | |
43 private: | |
44 WebIntentPickerCocoa* picker_; // Weak reference to picker | |
45 | |
46 DISALLOW_COPY_AND_ASSIGN(ConstrainedPickerSheetDelegate); | |
47 }; | |
48 | |
49 ConstrainedPickerSheetDelegate::ConstrainedPickerSheetDelegate( | |
50 WebIntentPickerCocoa* picker, WebIntentBubbleController* sheet_controller) | |
51 : picker_(picker) { | |
52 init([sheet_controller window], sheet_controller, | |
53 @selector(sheetDidEnd:returnCode:contextInfo:)); | |
54 set_sheet([sheet_controller window]); | |
55 } | |
56 | |
57 void ConstrainedPickerSheetDelegate::DeleteDelegate() { | |
58 if (is_sheet_open()) | |
59 [NSApp endSheet:sheet()]; | |
60 | |
61 if (picker_) | |
62 picker_->OnCancelled(); | |
63 | |
64 delete this; | |
65 } | |
66 | |
67 } // namespace | |
68 | |
27 // static | 69 // static |
28 WebIntentPicker* WebIntentPicker::Create(Browser* browser, | 70 WebIntentPicker* WebIntentPicker::Create(Browser* browser, |
29 TabContentsWrapper* wrapper, | 71 TabContentsWrapper* wrapper, |
30 WebIntentPickerDelegate* delegate, | 72 WebIntentPickerDelegate* delegate, |
31 WebIntentPickerModel* model) { | 73 WebIntentPickerModel* model) { |
32 return new WebIntentPickerCocoa(browser, wrapper, delegate, model); | 74 return new WebIntentPickerCocoa(browser, wrapper, delegate, model); |
33 } | 75 } |
34 | 76 |
35 WebIntentPickerCocoa::WebIntentPickerCocoa() | 77 WebIntentPickerCocoa::WebIntentPickerCocoa() |
36 : delegate_(NULL), | 78 : delegate_(NULL), |
37 model_(NULL), | 79 model_(NULL), |
38 browser_(NULL), | 80 browser_(NULL), |
39 controller_(nil), | 81 sheet_controller_(nil), |
40 weak_ptr_factory_(this), | |
41 service_invoked(false) { | 82 service_invoked(false) { |
42 } | 83 } |
43 | 84 |
44 | |
45 WebIntentPickerCocoa::WebIntentPickerCocoa(Browser* browser, | 85 WebIntentPickerCocoa::WebIntentPickerCocoa(Browser* browser, |
46 TabContentsWrapper* wrapper, | 86 TabContentsWrapper* wrapper, |
47 WebIntentPickerDelegate* delegate, | 87 WebIntentPickerDelegate* delegate, |
48 WebIntentPickerModel* model) | 88 WebIntentPickerModel* model) |
49 : delegate_(delegate), | 89 : delegate_(delegate), |
50 model_(model), | 90 model_(model), |
51 browser_(browser), | 91 browser_(browser), |
52 controller_(nil), | 92 sheet_controller_(nil), |
53 weak_ptr_factory_(this), | 93 service_invoked(false) { |
54 service_invoked(false) { | |
55 model_->set_observer(this); | 94 model_->set_observer(this); |
56 | 95 |
57 DCHECK(browser); | 96 DCHECK(browser); |
58 DCHECK(delegate); | 97 DCHECK(delegate); |
59 NSWindow* parentWindow = browser->window()->GetNativeHandle(); | 98 DCHECK(wrapper); |
60 | 99 |
61 // Compute the anchor point, relative to location bar. | 100 sheet_controller_ = [[WebIntentBubbleController alloc] initWithPicker:this]; |
62 BrowserWindowController* controller = [parentWindow windowController]; | |
63 LocationBarViewMac* locationBar = [controller locationBarBridge]; | |
64 NSPoint anchor = locationBar->GetPageInfoBubblePoint(); | |
65 anchor = [browser->window()->GetNativeHandle() convertBaseToScreen:anchor]; | |
66 | 101 |
67 // The controller is deallocated when the window is closed, so no need to | 102 // Deleted when ConstrainedPickerSheetDelegate::DeleteDelegate() runs. |
68 // worry about it here. | 103 ConstrainedPickerSheetDelegate* constrained_delegate = |
69 [[WebIntentBubbleController alloc] initWithPicker:this | 104 new ConstrainedPickerSheetDelegate(this, sheet_controller_); |
70 parentWindow:parentWindow | 105 |
71 anchoredAt:anchor]; | 106 window_ = new ConstrainedWindowMac(wrapper, constrained_delegate); |
72 } | 107 } |
73 | 108 |
74 WebIntentPickerCocoa::~WebIntentPickerCocoa() { | 109 WebIntentPickerCocoa::~WebIntentPickerCocoa() { |
75 if (model_ != NULL) | 110 if (model_ != NULL) |
76 model_->set_observer(NULL); | 111 model_->set_observer(NULL); |
77 } | 112 } |
78 | 113 |
114 void WebIntentPickerCocoa::OnSheetDidEnd(NSWindow* sheet) { | |
115 [sheet orderOut:sheet_controller_]; | |
116 if (window_) | |
117 window_->CloseConstrainedWindow(); | |
118 } | |
119 | |
79 void WebIntentPickerCocoa::Close() { | 120 void WebIntentPickerCocoa::Close() { |
80 DCHECK(controller_); | 121 DCHECK(sheet_controller_); |
81 [controller_ close]; | 122 [sheet_controller_ closeSheet]; |
123 | |
82 if (inline_disposition_tab_contents_.get()) | 124 if (inline_disposition_tab_contents_.get()) |
83 inline_disposition_tab_contents_->web_contents()->OnCloseStarted(); | 125 inline_disposition_tab_contents_->web_contents()->OnCloseStarted(); |
84 } | 126 } |
85 | 127 |
86 void WebIntentPickerCocoa::PerformDelayedLayout() { | |
87 // Check to see if a layout has already been scheduled. | |
88 if (weak_ptr_factory_.HasWeakPtrs()) | |
89 return; | |
90 | |
91 // Delay performing layout by a second so that all the animations from | |
92 // InfoBubbleWindow and origin updates from BaseBubbleController finish, so | |
93 // that we don't all race trying to change the frame's origin. | |
94 // | |
95 // Using MessageLoop is superior here to |-performSelector:| because it will | |
96 // not retain its target; if the child outlives its parent, zombies get left | |
97 // behind (http://crbug.com/59619). This will cancel the scheduled task if | |
98 // the controller get destroyed before the message | |
99 // can be delivered. | |
100 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
101 base::Bind(&WebIntentPickerCocoa::PerformLayout, | |
102 weak_ptr_factory_.GetWeakPtr()), | |
103 100 /* milliseconds */); | |
104 } | |
105 | |
106 void WebIntentPickerCocoa::PerformLayout() { | 128 void WebIntentPickerCocoa::PerformLayout() { |
107 DCHECK(controller_); | 129 DCHECK(sheet_controller_); |
108 // If the window is animating closed when this is called, the | 130 // If the window is animating closed when this is called, the |
109 // animation could be holding the last reference to |controller_| | 131 // animation could be holding the last reference to |controller_| |
110 // (and thus |this|). Pin it until the task is completed. | 132 // (and thus |this|). Pin it until the task is completed. |
111 scoped_nsobject<WebIntentBubbleController> keep_alive([controller_ retain]); | 133 scoped_nsobject<WebIntentBubbleController> |
112 [controller_ performLayoutWithModel:model_]; | 134 keep_alive([sheet_controller_ retain]); |
135 [sheet_controller_ performLayoutWithModel:model_]; | |
113 } | 136 } |
114 | 137 |
115 void WebIntentPickerCocoa::OnModelChanged(WebIntentPickerModel* model) { | 138 void WebIntentPickerCocoa::OnModelChanged(WebIntentPickerModel* model) { |
116 PerformDelayedLayout(); | 139 PerformLayout(); |
117 } | 140 } |
118 | 141 |
119 void WebIntentPickerCocoa::OnFaviconChanged(WebIntentPickerModel* model, | 142 void WebIntentPickerCocoa::OnFaviconChanged(WebIntentPickerModel* model, |
120 size_t index) { | 143 size_t index) { |
121 // We don't handle individual icon changes - just redo the whole model. | 144 // We don't handle individual icon changes - just redo the whole model. |
122 PerformDelayedLayout(); | 145 PerformLayout(); |
123 } | 146 } |
124 | 147 |
125 void WebIntentPickerCocoa::OnExtensionIconChanged( | 148 void WebIntentPickerCocoa::OnExtensionIconChanged( |
126 WebIntentPickerModel* model, | 149 WebIntentPickerModel* model, |
127 const string16& extension_id) { | 150 const string16& extension_id) { |
128 // TODO(binji): implement. | 151 // TODO(binji): implement. |
129 } | 152 } |
130 | 153 |
131 void WebIntentPickerCocoa::OnInlineDisposition(WebIntentPickerModel* model) { | 154 void WebIntentPickerCocoa::OnInlineDisposition(WebIntentPickerModel* model) { |
132 const WebIntentPickerModel::InstalledService& installed_service = | 155 const WebIntentPickerModel::InstalledService& installed_service = |
133 model->GetInstalledServiceAt(model->inline_disposition_index()); | 156 model->GetInstalledServiceAt(model->inline_disposition_index()); |
134 | 157 |
135 content::WebContents* web_contents = content::WebContents::Create( | 158 content::WebContents* web_contents = content::WebContents::Create( |
136 browser_->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL); | 159 browser_->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL); |
137 inline_disposition_tab_contents_.reset(new TabContentsWrapper(web_contents)); | 160 inline_disposition_tab_contents_.reset(new TabContentsWrapper(web_contents)); |
138 inline_disposition_delegate_.reset(new WebIntentInlineDispositionDelegate); | 161 inline_disposition_delegate_.reset(new WebIntentInlineDispositionDelegate); |
139 web_contents->SetDelegate(inline_disposition_delegate_.get()); | 162 web_contents->SetDelegate(inline_disposition_delegate_.get()); |
140 | 163 |
141 // Must call this immediately after WebContents creation to avoid race | 164 // Must call this immediately after WebContents creation to avoid race |
142 // with load. | 165 // with load. |
143 delegate_->OnInlineDispositionWebContentsCreated(web_contents); | 166 delegate_->OnInlineDispositionWebContentsCreated(web_contents); |
144 | 167 |
145 inline_disposition_tab_contents_->web_contents()->GetController().LoadURL( | 168 inline_disposition_tab_contents_->web_contents()->GetController().LoadURL( |
146 installed_service.url, | 169 installed_service.url, |
147 content::Referrer(), | 170 content::Referrer(), |
148 content::PAGE_TRANSITION_START_PAGE, | 171 content::PAGE_TRANSITION_START_PAGE, |
149 std::string()); | 172 std::string()); |
150 | 173 |
151 [controller_ setInlineDispositionTabContents: | 174 [sheet_controller_ setInlineDispositionTabContents: |
152 inline_disposition_tab_contents_.get()]; | 175 inline_disposition_tab_contents_.get()]; |
153 PerformDelayedLayout(); | 176 PerformLayout(); |
154 } | 177 } |
155 | 178 |
156 void WebIntentPickerCocoa::OnCancelled() { | 179 void WebIntentPickerCocoa::OnCancelled() { |
157 DCHECK(delegate_); | 180 DCHECK(delegate_); |
158 if (!service_invoked) | 181 if (!service_invoked) |
159 delegate_->OnCancelled(); | 182 delegate_->OnCancelled(); |
160 delegate_->OnClosing(); | 183 delegate_->OnClosing(); |
161 MessageLoop::current()->DeleteSoon(FROM_HERE, this); | 184 MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
162 } | 185 } |
163 | 186 |
164 void WebIntentPickerCocoa::OnServiceChosen(size_t index) { | 187 void WebIntentPickerCocoa::OnServiceChosen(size_t index) { |
165 DCHECK(delegate_); | 188 DCHECK(delegate_); |
166 const WebIntentPickerModel::InstalledService& installed_service = | 189 const WebIntentPickerModel::InstalledService& installed_service = |
167 model_->GetInstalledServiceAt(index); | 190 model_->GetInstalledServiceAt(index); |
168 service_invoked = true; | 191 service_invoked = true; |
169 delegate_->OnServiceChosen(index, installed_service.disposition); | 192 delegate_->OnServiceChosen(index, installed_service.disposition); |
170 } | 193 } |
171 | 194 |
172 void WebIntentPickerCocoa::set_controller( | |
173 WebIntentBubbleController* controller) { | |
174 controller_ = controller; | |
175 } | |
OLD | NEW |