Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(554)

Side by Side Diff: chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc

Issue 2917183002: Renaming SearchAnswerWebContentsDelegate. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/ui/app_list/search_answer_web_contents_delegate.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 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/ui/app_list/search_answer_web_contents_delegate.h"
6
7 #include "base/command_line.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/metrics/user_metrics.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser_navigator.h"
13 #include "chrome/browser/ui/browser_navigator_params.h"
14 #include "content/public/browser/navigation_handle.h"
15 #include "content/public/browser/render_view_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_contents_delegate.h"
18 #include "content/public/common/renderer_preferences.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/http/http_status_code.h"
21 #include "ui/app_list/app_list_features.h"
22 #include "ui/app_list/app_list_model.h"
23 #include "ui/app_list/search_box_model.h"
24 #include "ui/app_list/search_result.h"
25 #include "ui/views/controls/webview/web_contents_set_background_color.h"
26 #include "ui/views/controls/webview/webview.h"
27 #include "ui/views/widget/widget.h"
28
29 namespace app_list {
30
31 namespace {
32
33 enum class SearchAnswerRequestResult {
34 REQUEST_RESULT_ANOTHER_REQUEST_STARTED = 0,
35 REQUEST_RESULT_REQUEST_FAILED = 1,
36 REQUEST_RESULT_NO_ANSWER = 2,
37 REQUEST_RESULT_RECEIVED_ANSWER = 3,
38 REQUEST_RESULT_RECEIVED_ANSWER_TOO_LARGE = 4,
39 REQUEST_RESULT_MAX = 5
40 };
41
42 void RecordRequestResult(SearchAnswerRequestResult request_result) {
43 UMA_HISTOGRAM_ENUMERATION("SearchAnswer.RequestResult", request_result,
44 SearchAnswerRequestResult::REQUEST_RESULT_MAX);
45 }
46
47 class SearchAnswerWebView : public views::WebView {
48 public:
49 explicit SearchAnswerWebView(content::BrowserContext* browser_context)
50 : WebView(browser_context) {}
51
52 // views::WebView overrides:
53 void VisibilityChanged(View* starting_from, bool is_visible) override {
54 WebView::VisibilityChanged(starting_from, is_visible);
55
56 if (GetWidget() && GetWidget()->IsVisible() && IsDrawn()) {
57 if (shown_time_.is_null())
58 shown_time_ = base::TimeTicks::Now();
59 } else {
60 if (!shown_time_.is_null()) {
61 UMA_HISTOGRAM_MEDIUM_TIMES("SearchAnswer.AnswerVisibleTime",
62 base::TimeTicks::Now() - shown_time_);
63 shown_time_ = base::TimeTicks();
64 }
65 }
66 }
67
68 const char* GetClassName() const override { return "SearchAnswerWebView"; }
69
70 private:
71 // Time when the answer became visible to the user.
72 base::TimeTicks shown_time_;
73
74 DISALLOW_COPY_AND_ASSIGN(SearchAnswerWebView);
75 };
76
77 class SearchAnswerResult : public SearchResult {
78 public:
79 SearchAnswerResult(Profile* profile,
80 const std::string& result_url,
81 views::View* web_view)
82 : profile_(profile) {
83 set_display_type(DISPLAY_CARD);
84 set_id(result_url);
85 set_relevance(1);
86 set_view(web_view);
87 }
88
89 // SearchResult overrides:
90 std::unique_ptr<SearchResult> Duplicate() const override {
91 return base::MakeUnique<SearchAnswerResult>(profile_, id(), view());
92 }
93
94 void Open(int event_flags) override {
95 chrome::NavigateParams params(profile_, GURL(id()),
96 ui::PAGE_TRANSITION_GENERATED);
97 params.disposition = ui::DispositionFromEventFlags(event_flags);
98 chrome::Navigate(&params);
99 }
100
101 private:
102 Profile* const profile_;
103 };
104
105 } // namespace
106
107 SearchAnswerWebContentsDelegate::SearchAnswerWebContentsDelegate(
108 Profile* profile,
109 app_list::AppListModel* model)
110 : profile_(profile),
111 model_(model),
112 web_view_(base::MakeUnique<SearchAnswerWebView>(profile)),
113 web_contents_(
114 content::WebContents::Create(content::WebContents::CreateParams(
115 profile,
116 content::SiteInstance::Create(profile)))),
117 answer_server_url_(features::AnswerServerUrl()) {
118 content::RendererPreferences* renderer_prefs =
119 web_contents_->GetMutableRendererPrefs();
120 renderer_prefs->can_accept_load_drops = false;
121 // We need the OpenURLFromTab() to get called.
122 renderer_prefs->browser_handles_all_top_level_requests = true;
123 web_contents_->GetRenderViewHost()->SyncRendererPrefs();
124
125 Observe(web_contents_.get());
126 web_contents_->SetDelegate(this);
127 web_view_->set_owned_by_client();
128 web_view_->SetWebContents(web_contents_.get());
129
130 // Make the webview transparent since it's going to be shown on top of a
131 // highlightable button.
132 views::WebContentsSetBackgroundColor::CreateForWebContentsWithColor(
133 web_contents_.get(), SK_ColorTRANSPARENT);
134 }
135
136 SearchAnswerWebContentsDelegate::~SearchAnswerWebContentsDelegate() {
137 RecordReceivedAnswerFinalResult();
138 }
139
140 views::View* SearchAnswerWebContentsDelegate::web_view() {
141 return web_view_.get();
142 }
143
144 void SearchAnswerWebContentsDelegate::Start(bool is_voice_query,
145 const base::string16& query) {
146 RecordReceivedAnswerFinalResult();
147 // Reset the state.
148 received_answer_ = false;
149 OnResultAvailable(false);
150 current_request_url_ = GURL();
151 server_request_start_time_ = answer_loaded_time_ = base::TimeTicks();
152
153 if (is_voice_query) {
154 // No need to send a server request and show a card because launcher
155 // automatically closes upon voice queries.
156 return;
157 }
158
159 if (!model_->search_engine_is_google())
160 return;
161
162 if (query.empty())
163 return;
164
165 // Start a request to the answer server.
166
167 // Lifetime of |prefixed_query| should be longer than the one of
168 // |replacements|.
169 base::string16 prefixed_query(base::UTF8ToUTF16("q=") + query);
170 GURL::ReplacementsW replacements;
171 replacements.SetQueryStr(prefixed_query);
172 current_request_url_ = answer_server_url_.ReplaceComponents(replacements);
173
174 content::NavigationController::LoadURLParams load_params(
175 current_request_url_);
176 load_params.transition_type = ui::PAGE_TRANSITION_AUTO_TOPLEVEL;
177 load_params.should_clear_history_list = true;
178 web_contents_->GetController().LoadURLWithParams(load_params);
179 server_request_start_time_ = base::TimeTicks::Now();
180
181 // We are going to call WebContents::GetPreferredSize().
182 web_contents_->GetRenderViewHost()->EnablePreferredSizeMode();
183 }
184
185 void SearchAnswerWebContentsDelegate::UpdatePreferredSize(
186 content::WebContents* web_contents,
187 const gfx::Size& pref_size) {
188 OnResultAvailable(received_answer_ && IsCardSizeOk() &&
189 !web_contents_->IsLoading());
190 web_view_->SetPreferredSize(pref_size);
191 if (!answer_loaded_time_.is_null()) {
192 UMA_HISTOGRAM_TIMES("SearchAnswer.ResizeAfterLoadTime",
193 base::TimeTicks::Now() - answer_loaded_time_);
194 }
195 }
196
197 content::WebContents* SearchAnswerWebContentsDelegate::OpenURLFromTab(
198 content::WebContents* source,
199 const content::OpenURLParams& params) {
200 if (!params.user_gesture)
201 return WebContentsDelegate::OpenURLFromTab(source, params);
202
203 // Open the user-clicked link in the browser taking into account the requested
204 // disposition.
205 chrome::NavigateParams new_tab_params(profile_, params.url,
206 params.transition);
207
208 new_tab_params.disposition = params.disposition;
209
210 if (params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
211 // When the user asks to open a link as a background tab, we show an
212 // activated window with the new activated tab after the user closes the
213 // launcher. So it's "background" relative to the launcher itself.
214 new_tab_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
215 new_tab_params.window_action = chrome::NavigateParams::SHOW_WINDOW_INACTIVE;
216 }
217
218 chrome::Navigate(&new_tab_params);
219
220 base::RecordAction(base::UserMetricsAction("SearchAnswer_OpenedUrl"));
221
222 return new_tab_params.target_contents;
223 }
224
225 bool SearchAnswerWebContentsDelegate::HandleContextMenu(
226 const content::ContextMenuParams& params) {
227 // Disable showing the menu.
228 return true;
229 }
230
231 void SearchAnswerWebContentsDelegate::DidFinishNavigation(
232 content::NavigationHandle* navigation_handle) {
233 if (navigation_handle->GetURL() != current_request_url_) {
234 RecordRequestResult(
235 SearchAnswerRequestResult::REQUEST_RESULT_ANOTHER_REQUEST_STARTED);
236 return;
237 }
238
239 if (!navigation_handle->HasCommitted() || navigation_handle->IsErrorPage() ||
240 !navigation_handle->IsInMainFrame()) {
241 RecordRequestResult(
242 SearchAnswerRequestResult::REQUEST_RESULT_REQUEST_FAILED);
243 return;
244 }
245
246 if (!features::IsAnswerCardDarkRunEnabled()) {
247 if (!ParseResponseHeaders(navigation_handle->GetResponseHeaders())) {
248 RecordRequestResult(SearchAnswerRequestResult::REQUEST_RESULT_NO_ANSWER);
249 return;
250 }
251 } else {
252 // In the dark run mode, every other "server response" contains a card.
253 dark_run_received_answer_ = !dark_run_received_answer_;
254 if (!dark_run_received_answer_)
255 return;
256 // SearchResult requires a non-empty id. This "url" will never be opened.
257 result_url_ = "some string";
258 }
259
260 received_answer_ = true;
261 UMA_HISTOGRAM_TIMES("SearchAnswer.NavigationTime",
262 base::TimeTicks::Now() - server_request_start_time_);
263 }
264
265 void SearchAnswerWebContentsDelegate::DidStopLoading() {
266 if (!received_answer_)
267 return;
268
269 if (IsCardSizeOk())
270 OnResultAvailable(true);
271 answer_loaded_time_ = base::TimeTicks::Now();
272 UMA_HISTOGRAM_TIMES("SearchAnswer.LoadingTime",
273 answer_loaded_time_ - server_request_start_time_);
274 base::RecordAction(base::UserMetricsAction("SearchAnswer_StoppedLoading"));
275 }
276
277 void SearchAnswerWebContentsDelegate::DidGetUserInteraction(
278 const blink::WebInputEvent::Type type) {
279 base::RecordAction(base::UserMetricsAction("SearchAnswer_UserInteraction"));
280 }
281
282 bool SearchAnswerWebContentsDelegate::IsCardSizeOk() const {
283 if (features::IsAnswerCardDarkRunEnabled())
284 return true;
285
286 const gfx::Size size = web_contents_->GetPreferredSize();
287 return size.width() <= features::AnswerCardMaxWidth() &&
288 size.height() <= features::AnswerCardMaxHeight();
289 }
290
291 void SearchAnswerWebContentsDelegate::RecordReceivedAnswerFinalResult() {
292 // Recording whether a server response with an answer contains a card of a
293 // fitting size, or a too large one. Cannot do this in DidStopLoading() or
294 // UpdatePreferredSize() because this may be followed by a resizing with
295 // different dimensions, so this method gets called when card's life ends.
296 if (!received_answer_)
297 return;
298
299 RecordRequestResult(
300 IsCardSizeOk() ? SearchAnswerRequestResult::REQUEST_RESULT_RECEIVED_ANSWER
301 : SearchAnswerRequestResult::
302 REQUEST_RESULT_RECEIVED_ANSWER_TOO_LARGE);
303 }
304
305 void SearchAnswerWebContentsDelegate::OnResultAvailable(bool is_available) {
306 SearchProvider::Results results;
307 if (is_available) {
308 results.reserve(1);
309 results.emplace_back(base::MakeUnique<SearchAnswerResult>(
310 profile_, result_url_, web_view_.get()));
311 }
312 SwapResults(&results);
313 }
314
315 bool SearchAnswerWebContentsDelegate::ParseResponseHeaders(
316 const net::HttpResponseHeaders* headers) {
317 if (!headers || headers->response_code() != net::HTTP_OK)
318 return false;
319 if (!headers->HasHeaderValue("SearchAnswer-HasResult", "true"))
320 return false;
321 if (!headers->GetNormalizedHeader("SearchAnswer-OpenResultUrl", &result_url_))
322 return false;
323 return true;
324 }
325
326 } // namespace app_list
OLDNEW
« no previous file with comments | « chrome/browser/ui/app_list/search_answer_web_contents_delegate.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698