Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / automation / automation_provider.cc
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 "chrome/browser/automation/automation_provider.h"
6
7 #include <set>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/command_line.h"
12 #include "base/debug/trace_event.h"
13 #include "base/files/file_path.h"
14 #include "base/json/json_reader.h"
15 #include "base/json/json_string_value_serializer.h"
16 #include "base/json/json_writer.h"
17 #include "base/json/string_escape.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/path_service.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/synchronization/waitable_event.h"
26 #include "base/threading/thread.h"
27 #include "base/values.h"
28 #include "chrome/app/chrome_command_ids.h"
29 #include "chrome/browser/automation/automation_browser_tracker.h"
30 #include "chrome/browser/automation/automation_provider_list.h"
31 #include "chrome/browser/automation/automation_provider_observers.h"
32 #include "chrome/browser/automation/automation_tab_tracker.h"
33 #include "chrome/browser/automation/automation_window_tracker.h"
34 #include "chrome/browser/browser_process.h"
35 #include "chrome/browser/browsing_data/browsing_data_helper.h"
36 #include "chrome/browser/browsing_data/browsing_data_remover.h"
37 #include "chrome/browser/character_encoding.h"
38 #include "chrome/browser/content_settings/host_content_settings_map.h"
39 #include "chrome/browser/net/url_request_mock_util.h"
40 #include "chrome/browser/printing/print_job.h"
41 #include "chrome/browser/profiles/profile_manager.h"
42 #include "chrome/browser/ssl/ssl_blocking_page.h"
43 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
44 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
45 #include "chrome/browser/ui/browser_commands.h"
46 #include "chrome/browser/ui/browser_finder.h"
47 #include "chrome/browser/ui/browser_tabstrip.h"
48 #include "chrome/browser/ui/browser_window.h"
49 #include "chrome/browser/ui/find_bar/find_bar.h"
50 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
51 #include "chrome/browser/ui/find_bar/find_notification_details.h"
52 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
53 #include "chrome/browser/ui/login/login_prompt.h"
54 #include "chrome/browser/ui/omnibox/location_bar.h"
55 #include "chrome/common/automation_constants.h"
56 #include "chrome/common/automation_messages.h"
57 #include "chrome/common/chrome_constants.h"
58 #include "chrome/common/chrome_paths.h"
59 #include "chrome/common/chrome_switches.h"
60 #include "chrome/common/chrome_version_info.h"
61 #include "chrome/common/pref_names.h"
62 #include "chrome/common/render_messages.h"
63 #include "chrome/common/url_constants.h"
64 #include "content/public/browser/browser_thread.h"
65 #include "content/public/browser/download_item.h"
66 #include "content/public/browser/native_web_keyboard_event.h"
67 #include "content/public/browser/render_view_host.h"
68 #include "content/public/browser/tracing_controller.h"
69 #include "content/public/browser/web_contents.h"
70 #include "content/public/browser/web_contents_view.h"
71 #include "ipc/ipc_channel_proxy.h"
72 #include "net/proxy/proxy_config_service_fixed.h"
73 #include "net/proxy/proxy_service.h"
74 #include "net/url_request/url_request_context.h"
75 #include "net/url_request/url_request_context_getter.h"
76 #include "third_party/WebKit/public/web/WebFindOptions.h"
77
78 #if defined(OS_CHROMEOS)
79 #include "chromeos/chromeos_switches.h"
80 #include "chromeos/login/login_state.h"
81 #endif  // defined(OS_CHROMEOS)
82
83 using blink::WebFindOptions;
84 using base::Time;
85 using content::BrowserThread;
86 using content::DownloadItem;
87 using content::NavigationController;
88 using content::RenderViewHost;
89 using content::TracingController;
90 using content::WebContents;
91
92 AutomationProvider::AutomationProvider(Profile* profile)
93     : profile_(profile),
94       reply_message_(NULL),
95       reinitialize_on_channel_error_(
96           CommandLine::ForCurrentProcess()->HasSwitch(
97               switches::kAutomationReinitializeOnChannelError)),
98       use_initial_load_observers_(true),
99       is_connected_(false),
100       initial_tab_loads_complete_(false),
101       login_webui_ready_(true) {
102   TRACE_EVENT_BEGIN_ETW("AutomationProvider::AutomationProvider", 0, "");
103
104   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
105
106   browser_tracker_.reset(new AutomationBrowserTracker(this));
107   tab_tracker_.reset(new AutomationTabTracker(this));
108   window_tracker_.reset(new AutomationWindowTracker(this));
109   metric_event_duration_observer_.reset(new MetricEventDurationObserver());
110
111   TRACE_EVENT_END_ETW("AutomationProvider::AutomationProvider", 0, "");
112 }
113
114 AutomationProvider::~AutomationProvider() {
115   if (channel_.get())
116     channel_->Close();
117 }
118
119 void AutomationProvider::set_profile(Profile* profile) {
120   profile_ = profile;
121 }
122
123 bool AutomationProvider::InitializeChannel(const std::string& channel_id) {
124   TRACE_EVENT_BEGIN_ETW("AutomationProvider::InitializeChannel", 0, "");
125
126   channel_id_ = channel_id;
127   std::string effective_channel_id = channel_id;
128
129   // If the channel_id starts with kNamedInterfacePrefix, create a named IPC
130   // server and listen on it, else connect as client to an existing IPC server
131   bool use_named_interface =
132       channel_id.find(automation::kNamedInterfacePrefix) == 0;
133   if (use_named_interface) {
134     effective_channel_id = channel_id.substr(
135         strlen(automation::kNamedInterfacePrefix));
136     if (effective_channel_id.length() <= 0)
137       return false;
138
139     reinitialize_on_channel_error_ = true;
140   }
141
142   channel_.reset(new IPC::ChannelProxy(
143       effective_channel_id,
144       GetChannelMode(use_named_interface),
145       this,
146       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()));
147
148 #if defined(OS_CHROMEOS)
149   if (use_initial_load_observers_) {
150     // Wait for webui login to be ready.
151     // Observer will delete itself.
152     if (CommandLine::ForCurrentProcess()->HasSwitch(
153             chromeos::switches::kLoginManager) &&
154         !chromeos::LoginState::Get()->IsUserLoggedIn()) {
155       login_webui_ready_ = false;
156       new OOBEWebuiReadyObserver(this);
157     }
158   }
159 #endif
160
161   TRACE_EVENT_END_ETW("AutomationProvider::InitializeChannel", 0, "");
162
163   return true;
164 }
165
166 IPC::Channel::Mode AutomationProvider::GetChannelMode(
167     bool use_named_interface) {
168   if (use_named_interface)
169     return IPC::Channel::MODE_NAMED_SERVER;
170   else
171     return IPC::Channel::MODE_CLIENT;
172 }
173
174 std::string AutomationProvider::GetProtocolVersion() {
175   chrome::VersionInfo version_info;
176   return version_info.Version().c_str();
177 }
178
179 void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) {
180   VLOG(2) << "SetExpectedTabCount:" << expected_tabs;
181   if (expected_tabs == 0)
182     OnInitialTabLoadsComplete();
183   else
184     initial_load_observer_.reset(new InitialLoadObserver(expected_tabs, this));
185 }
186
187 void AutomationProvider::OnInitialTabLoadsComplete() {
188   initial_tab_loads_complete_ = true;
189   VLOG(2) << "OnInitialTabLoadsComplete";
190   SendInitialLoadMessage();
191 }
192
193 void AutomationProvider::OnOOBEWebuiReady() {
194   login_webui_ready_ = true;
195   VLOG(2) << "OnOOBEWebuiReady";
196   SendInitialLoadMessage();
197 }
198
199 void AutomationProvider::SendInitialLoadMessage() {
200   if (is_connected_ && initial_tab_loads_complete_ && login_webui_ready_) {
201     VLOG(2) << "Initial loads complete; sending initial loads message.";
202     Send(new AutomationMsg_InitialLoadsComplete());
203   }
204 }
205
206 void AutomationProvider::DisableInitialLoadObservers() {
207   use_initial_load_observers_ = false;
208   OnInitialTabLoadsComplete();
209   OnOOBEWebuiReady();
210 }
211
212 int AutomationProvider::GetIndexForNavigationController(
213     const NavigationController* controller, const Browser* parent) const {
214   DCHECK(parent);
215   return parent->tab_strip_model()->GetIndexOfWebContents(
216       controller->GetWebContents());
217 }
218
219 // TODO(phajdan.jr): move to TestingAutomationProvider.
220 base::DictionaryValue* AutomationProvider::GetDictionaryFromDownloadItem(
221     const DownloadItem* download, bool incognito) {
222   const char *download_state_string = NULL;
223   switch (download->GetState()) {
224     case DownloadItem::IN_PROGRESS:
225       download_state_string = "IN_PROGRESS";
226       break;
227     case DownloadItem::CANCELLED:
228       download_state_string = "CANCELLED";
229       break;
230     case DownloadItem::INTERRUPTED:
231       download_state_string = "INTERRUPTED";
232       break;
233     case DownloadItem::COMPLETE:
234       download_state_string = "COMPLETE";
235       break;
236     case DownloadItem::MAX_DOWNLOAD_STATE:
237       NOTREACHED();
238       download_state_string = "UNKNOWN";
239       break;
240   }
241   DCHECK(download_state_string);
242   if (!download_state_string)
243     download_state_string = "UNKNOWN";
244
245   const char* download_danger_type_string = NULL;
246   switch (download->GetDangerType()) {
247     case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
248       download_danger_type_string = "NOT_DANGEROUS";
249       break;
250     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
251       download_danger_type_string = "DANGEROUS_FILE";
252       break;
253     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
254       download_danger_type_string = "DANGEROUS_URL";
255       break;
256     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
257       download_danger_type_string = "DANGEROUS_CONTENT";
258       break;
259     case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
260       download_danger_type_string = "MAYBE_DANGEROUS_CONTENT";
261       break;
262     case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
263       download_danger_type_string = "UNCOMMON_CONTENT";
264       break;
265     case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
266       download_danger_type_string = "USER_VALIDATED";
267       break;
268     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
269       download_danger_type_string = "DANGEROUS_HOST";
270       break;
271     case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
272       download_danger_type_string = "POTENTIALLY_UNWANTED";
273       break;
274     case content::DOWNLOAD_DANGER_TYPE_MAX:
275       NOTREACHED();
276       download_danger_type_string = "UNKNOWN";
277       break;
278   }
279   DCHECK(download_danger_type_string);
280   if (!download_danger_type_string)
281     download_danger_type_string = "UNKNOWN";
282
283   base::DictionaryValue* dl_item_value = new base::DictionaryValue;
284   dl_item_value->SetInteger("id", static_cast<int>(download->GetId()));
285   dl_item_value->SetString("url", download->GetURL().spec());
286   dl_item_value->SetString("referrer_url", download->GetReferrerUrl().spec());
287   dl_item_value->SetString("file_name",
288                            download->GetFileNameToReportUser().value());
289   dl_item_value->SetString("full_path",
290                            download->GetTargetFilePath().value());
291   dl_item_value->SetBoolean("is_paused", download->IsPaused());
292   dl_item_value->SetBoolean("open_when_complete",
293                             download->GetOpenWhenComplete());
294   dl_item_value->SetBoolean("is_temporary", download->IsTemporary());
295   dl_item_value->SetBoolean("is_otr", incognito);
296   dl_item_value->SetString("state", download_state_string);
297   dl_item_value->SetString("danger_type", download_danger_type_string);
298   dl_item_value->SetInteger("PercentComplete", download->PercentComplete());
299
300   return dl_item_value;
301 }
302
303 void AutomationProvider::OnChannelConnected(int pid) {
304   is_connected_ = true;
305
306   // Send a hello message with our current automation protocol version.
307   VLOG(2) << "Testing channel connected, sending hello message";
308   channel_->Send(new AutomationMsg_Hello(GetProtocolVersion()));
309
310   SendInitialLoadMessage();
311 }
312
313 bool AutomationProvider::OnMessageReceived(const IPC::Message& message) {
314   bool handled = true;
315   bool deserialize_success = true;
316   IPC_BEGIN_MESSAGE_MAP_EX(AutomationProvider, message, deserialize_success)
317     IPC_MESSAGE_HANDLER(AutomationMsg_HandleUnused, HandleUnused)
318     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Find, HandleFindRequest)
319     IPC_MESSAGE_HANDLER(AutomationMsg_OverrideEncoding, OverrideEncoding)
320     IPC_MESSAGE_HANDLER(AutomationMsg_ReloadAsync, ReloadAsync)
321     IPC_MESSAGE_HANDLER(AutomationMsg_StopAsync, StopAsync)
322     IPC_MESSAGE_HANDLER(AutomationMsg_SetPageFontSize, OnSetPageFontSize)
323     IPC_MESSAGE_HANDLER(AutomationMsg_JavaScriptStressTestControl,
324                         JavaScriptStressTestControl)
325     IPC_MESSAGE_HANDLER(AutomationMsg_BeginTracing, BeginTracing)
326     IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_EndTracing, EndTracing)
327     IPC_MESSAGE_UNHANDLED(handled = false)
328   IPC_END_MESSAGE_MAP_EX()
329   if (!handled)
330     OnUnhandledMessage(message);
331   if (!deserialize_success)
332     OnMessageDeserializationFailure();
333   return handled;
334 }
335
336 void AutomationProvider::OnUnhandledMessage(const IPC::Message& message) {
337   // We should not hang here. Print a message to indicate what's going on,
338   // and disconnect the channel to notify the caller about the error
339   // in a way it can't ignore, and make any further attempts to send
340   // messages fail fast.
341   LOG(ERROR) << "AutomationProvider received a message it can't handle. "
342              << "Message type: " << message.type()
343              << ", routing ID: " << message.routing_id() << ". "
344              << "Please make sure that you use switches::kTestingChannelID "
345              << "for test code (TestingAutomationProvider), and "
346              << "switches::kAutomationClientChannelID for everything else "
347              << "(like ChromeFrame). Closing the automation channel.";
348   channel_->Close();
349 }
350
351 void AutomationProvider::OnMessageDeserializationFailure() {
352   LOG(ERROR) << "Failed to deserialize IPC message. "
353              << "Closing the automation channel.";
354   channel_->Close();
355 }
356
357 void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) {
358   if (window_tracker_->ContainsHandle(handle)) {
359     window_tracker_->Remove(window_tracker_->GetResource(handle));
360   }
361 }
362
363 bool AutomationProvider::ReinitializeChannel() {
364   base::ThreadRestrictions::ScopedAllowIO allow_io;
365
366   // Make sure any old channels are cleaned up before starting up a new one.
367   channel_.reset();
368   return InitializeChannel(channel_id_);
369 }
370
371 void AutomationProvider::OnChannelError() {
372   if (reinitialize_on_channel_error_) {
373     VLOG(1) << "AutomationProxy disconnected, resetting AutomationProvider.";
374     if (ReinitializeChannel())
375       return;
376     VLOG(1) << "Error reinitializing AutomationProvider channel.";
377   }
378   VLOG(1) << "AutomationProxy went away, shutting down app.";
379   g_browser_process->GetAutomationProviderList()->RemoveProvider(this);
380 }
381
382 bool AutomationProvider::Send(IPC::Message* msg) {
383   DCHECK(channel_.get());
384   return channel_->Send(msg);
385 }
386
387 Browser* AutomationProvider::FindAndActivateTab(
388     NavigationController* controller) {
389   content::WebContentsDelegate* d = controller->GetWebContents()->GetDelegate();
390   if (d)
391     d->ActivateContents(controller->GetWebContents());
392   return chrome::FindBrowserWithWebContents(controller->GetWebContents());
393 }
394
395 void AutomationProvider::HandleFindRequest(
396     int handle,
397     const AutomationMsg_Find_Params& params,
398     IPC::Message* reply_message) {
399   if (!tab_tracker_->ContainsHandle(handle)) {
400     AutomationMsg_Find::WriteReplyParams(reply_message, -1, -1);
401     Send(reply_message);
402     return;
403   }
404
405   NavigationController* nav = tab_tracker_->GetResource(handle);
406   WebContents* web_contents = nav->GetWebContents();
407
408   SendFindRequest(web_contents,
409                   false,
410                   params.search_string,
411                   params.forward,
412                   params.match_case,
413                   params.find_next,
414                   reply_message);
415 }
416
417 void AutomationProvider::SendFindRequest(
418     WebContents* web_contents,
419     bool with_json,
420     const base::string16& search_string,
421     bool forward,
422     bool match_case,
423     bool find_next,
424     IPC::Message* reply_message) {
425   int request_id = FindInPageNotificationObserver::kFindInPageRequestId;
426   FindInPageNotificationObserver* observer =
427       new FindInPageNotificationObserver(this,
428                                          web_contents,
429                                          with_json,
430                                          reply_message);
431   if (!with_json) {
432     find_in_page_observer_.reset(observer);
433   }
434   FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(web_contents);
435   if (find_tab_helper)
436     find_tab_helper->set_current_find_request_id(request_id);
437
438   WebFindOptions options;
439   options.forward = forward;
440   options.matchCase = match_case;
441   options.findNext = find_next;
442   web_contents->Find(
443       FindInPageNotificationObserver::kFindInPageRequestId, search_string,
444       options);
445 }
446
447 WebContents* AutomationProvider::GetWebContentsForHandle(
448     int handle, NavigationController** tab) {
449   if (tab_tracker_->ContainsHandle(handle)) {
450     NavigationController* nav_controller = tab_tracker_->GetResource(handle);
451     if (tab)
452       *tab = nav_controller;
453     return nav_controller->GetWebContents();
454   }
455   return NULL;
456 }
457
458 // Gets the current used encoding name of the page in the specified tab.
459 void AutomationProvider::OverrideEncoding(int tab_handle,
460                                           const std::string& encoding_name,
461                                           bool* success) {
462   *success = false;
463   if (tab_tracker_->ContainsHandle(tab_handle)) {
464     NavigationController* nav = tab_tracker_->GetResource(tab_handle);
465     if (!nav)
466       return;
467     Browser* browser = FindAndActivateTab(nav);
468
469     // If the browser has UI, simulate what a user would do.
470     // Activate the tab and then click the encoding menu.
471     if (browser && chrome::IsCommandEnabled(browser, IDC_ENCODING_MENU)) {
472       int selected_encoding_id =
473           CharacterEncoding::GetCommandIdByCanonicalEncodingName(encoding_name);
474       if (selected_encoding_id) {
475         browser->OverrideEncoding(selected_encoding_id);
476         *success = true;
477       }
478     } else {
479       // There is no UI, Chrome probably runs as Chrome-Frame mode.
480       // Try to get WebContents and call its SetOverrideEncoding method.
481       WebContents* contents = nav->GetWebContents();
482       if (!contents)
483         return;
484       const std::string selected_encoding =
485           CharacterEncoding::GetCanonicalEncodingNameByAliasName(encoding_name);
486       if (selected_encoding.empty())
487         return;
488       contents->SetOverrideEncoding(selected_encoding);
489     }
490   }
491 }
492
493 void AutomationProvider::ReloadAsync(int tab_handle) {
494   if (tab_tracker_->ContainsHandle(tab_handle)) {
495     NavigationController* tab = tab_tracker_->GetResource(tab_handle);
496     if (!tab) {
497       NOTREACHED();
498       return;
499     }
500
501     const bool check_for_repost = true;
502     tab->Reload(check_for_repost);
503   }
504 }
505
506 void AutomationProvider::StopAsync(int tab_handle) {
507   RenderViewHost* view = GetViewForTab(tab_handle);
508   if (!view) {
509     // We tolerate StopAsync being called even before a view has been created.
510     // So just log a warning instead of a NOTREACHED().
511     DLOG(WARNING) << "StopAsync: no view for handle " << tab_handle;
512     return;
513   }
514
515   view->Stop();
516 }
517
518 void AutomationProvider::OnSetPageFontSize(int tab_handle,
519                                            int font_size) {
520   AutomationPageFontSize automation_font_size =
521       static_cast<AutomationPageFontSize>(font_size);
522
523   if (automation_font_size < SMALLEST_FONT ||
524       automation_font_size > LARGEST_FONT) {
525       DLOG(ERROR) << "Invalid font size specified : "
526                   << font_size;
527       return;
528   }
529
530   if (tab_tracker_->ContainsHandle(tab_handle)) {
531     NavigationController* tab = tab_tracker_->GetResource(tab_handle);
532     DCHECK(tab != NULL);
533     if (tab && tab->GetWebContents()) {
534       DCHECK(tab->GetWebContents()->GetBrowserContext() != NULL);
535       Profile* profile = Profile::FromBrowserContext(
536           tab->GetWebContents()->GetBrowserContext());
537       profile->GetPrefs()->SetInteger(prefs::kWebKitDefaultFontSize, font_size);
538     }
539   }
540 }
541
542 void AutomationProvider::JavaScriptStressTestControl(int tab_handle,
543                                                      int cmd,
544                                                      int param) {
545   RenderViewHost* view = GetViewForTab(tab_handle);
546   if (!view) {
547     NOTREACHED();
548     return;
549   }
550
551   view->Send(new ChromeViewMsg_JavaScriptStressTestControl(
552       view->GetRoutingID(), cmd, param));
553 }
554
555 void AutomationProvider::BeginTracing(const std::string& category_patterns,
556                                       bool* success) {
557   *success = TracingController::GetInstance()->EnableRecording(
558       category_patterns, TracingController::DEFAULT_OPTIONS,
559       TracingController::EnableRecordingDoneCallback());
560 }
561
562 void AutomationProvider::EndTracing(IPC::Message* reply_message) {
563   base::FilePath path;
564   if (!TracingController::GetInstance()->DisableRecording(
565       path, base::Bind(&AutomationProvider::OnTraceDataCollected, this,
566                        reply_message))) {
567     // If failed to call EndTracingAsync, need to reply with failure now.
568     AutomationMsg_EndTracing::WriteReplyParams(reply_message, path, false);
569     Send(reply_message);
570   }
571   // Otherwise defer EndTracing reply until TraceController calls us back.
572 }
573
574 void AutomationProvider::OnTraceDataCollected(IPC::Message* reply_message,
575                                               const base::FilePath& path) {
576   if (reply_message) {
577     AutomationMsg_EndTracing::WriteReplyParams(reply_message, path, true);
578     Send(reply_message);
579   }
580 }
581
582 RenderViewHost* AutomationProvider::GetViewForTab(int tab_handle) {
583   if (tab_tracker_->ContainsHandle(tab_handle)) {
584     NavigationController* tab = tab_tracker_->GetResource(tab_handle);
585     if (!tab) {
586       NOTREACHED();
587       return NULL;
588     }
589
590     WebContents* web_contents = tab->GetWebContents();
591     if (!web_contents) {
592       NOTREACHED();
593       return NULL;
594     }
595
596     RenderViewHost* view_host = web_contents->GetRenderViewHost();
597     return view_host;
598   }
599
600   return NULL;
601 }