web_navigation_api.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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// Implements the Chrome Extensions WebNavigation API.
6
7#include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h"
8
9#include "base/lazy_instance.h"
10#include "chrome/browser/extensions/api/web_navigation/web_navigation_api_constants.h"
11#include "chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h"
12#include "chrome/browser/extensions/event_router.h"
13#include "chrome/browser/extensions/extension_tab_util.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/tab_contents/retargeting_details.h"
16#include "chrome/browser/ui/browser.h"
17#include "chrome/browser/ui/browser_list.h"
18#include "chrome/browser/ui/tab_contents/tab_contents.h"
19#include "chrome/browser/view_type_utils.h"
20#include "chrome/common/chrome_notification_types.h"
21#include "chrome/common/extensions/api/web_navigation.h"
22#include "content/public/browser/resource_request_details.h"
23#include "content/public/browser/navigation_details.h"
24#include "content/public/browser/notification_service.h"
25#include "content/public/browser/notification_types.h"
26#include "content/public/browser/render_process_host.h"
27#include "content/public/browser/render_view_host.h"
28#include "content/public/browser/web_contents.h"
29#include "net/base/net_errors.h"
30
31namespace GetFrame = extensions::api::web_navigation::GetFrame;
32namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames;
33
34DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver)
35
36namespace extensions {
37
38namespace helpers = web_navigation_api_helpers;
39namespace keys = web_navigation_api_constants;
40
41namespace {
42
43typedef std::map<content::WebContents*, WebNavigationTabObserver*>
44    TabObserverMap;
45static base::LazyInstance<TabObserverMap> g_tab_observer =
46    LAZY_INSTANCE_INITIALIZER;
47
48}  // namespace
49
50// WebNavigtionEventRouter -------------------------------------------
51
52WebNavigationEventRouter::PendingWebContents::PendingWebContents()
53    : source_web_contents(NULL),
54      source_frame_id(0),
55      source_frame_is_main_frame(false),
56      target_web_contents(NULL),
57      target_url() {
58}
59
60WebNavigationEventRouter::PendingWebContents::PendingWebContents(
61    content::WebContents* source_web_contents,
62    int64 source_frame_id,
63    bool source_frame_is_main_frame,
64    content::WebContents* target_web_contents,
65    const GURL& target_url)
66    : source_web_contents(source_web_contents),
67      source_frame_id(source_frame_id),
68      source_frame_is_main_frame(source_frame_is_main_frame),
69      target_web_contents(target_web_contents),
70      target_url(target_url) {
71}
72
73WebNavigationEventRouter::PendingWebContents::~PendingWebContents() {}
74
75WebNavigationEventRouter::WebNavigationEventRouter(Profile* profile)
76    : profile_(profile) {
77  CHECK(registrar_.IsEmpty());
78  registrar_.Add(this,
79                 chrome::NOTIFICATION_RETARGETING,
80                 content::NotificationService::AllSources());
81  registrar_.Add(this,
82                 chrome::NOTIFICATION_TAB_ADDED,
83                 content::NotificationService::AllSources());
84  registrar_.Add(this,
85                 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
86                 content::NotificationService::AllSources());
87
88  BrowserList::AddObserver(this);
89  for (BrowserList::const_iterator iter = BrowserList::begin();
90       iter != BrowserList::end(); ++iter) {
91    OnBrowserAdded(*iter);
92  }
93}
94
95WebNavigationEventRouter::~WebNavigationEventRouter() {
96  BrowserList::RemoveObserver(this);
97}
98
99void WebNavigationEventRouter::OnBrowserAdded(Browser* browser) {
100  if (!profile_->IsSameProfile(browser->profile()))
101    return;
102  browser->tab_strip_model()->AddObserver(this);
103}
104
105void WebNavigationEventRouter::OnBrowserRemoved(Browser* browser) {
106  if (!profile_->IsSameProfile(browser->profile()))
107    return;
108  browser->tab_strip_model()->RemoveObserver(this);
109}
110
111void WebNavigationEventRouter::TabReplacedAt(
112    TabStripModel* tab_strip_model,
113    TabContents* old_contents,
114    TabContents* new_contents,
115    int index) {
116  WebNavigationTabObserver* tab_observer =
117      WebNavigationTabObserver::Get(old_contents->web_contents());
118  if (!tab_observer) {
119    // If you hit this DCHECK(), please add reproduction steps to
120    // http://crbug.com/109464.
121    DCHECK(chrome::GetViewType(old_contents->web_contents()) !=
122           chrome::VIEW_TYPE_TAB_CONTENTS);
123    return;
124  }
125  const FrameNavigationState& frame_navigation_state =
126      tab_observer->frame_navigation_state();
127
128  if (!frame_navigation_state.IsValidUrl(
129          old_contents->web_contents()->GetURL()) ||
130      !frame_navigation_state.IsValidUrl(
131          new_contents->web_contents()->GetURL()))
132    return;
133
134  helpers::DispatchOnTabReplaced(
135      old_contents->web_contents(),
136      profile_,
137      new_contents->web_contents());
138}
139
140void WebNavigationEventRouter::Observe(
141    int type,
142    const content::NotificationSource& source,
143    const content::NotificationDetails& details) {
144  switch (type) {
145    case chrome::NOTIFICATION_RETARGETING: {
146      Profile* profile = content::Source<Profile>(source).ptr();
147      if (profile->GetOriginalProfile() == profile_) {
148        Retargeting(
149            content::Details<const RetargetingDetails>(details).ptr());
150      }
151      break;
152    }
153
154    case chrome::NOTIFICATION_TAB_ADDED:
155      TabAdded(content::Details<content::WebContents>(details).ptr());
156      break;
157
158    case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
159      TabDestroyed(content::Source<content::WebContents>(source).ptr());
160      break;
161
162    default:
163      NOTREACHED();
164  }
165}
166
167void WebNavigationEventRouter::Retargeting(const RetargetingDetails* details) {
168  if (details->source_frame_id == 0)
169    return;
170  WebNavigationTabObserver* tab_observer =
171      WebNavigationTabObserver::Get(details->source_web_contents);
172  if (!tab_observer) {
173    // If you hit this DCHECK(), please add reproduction steps to
174    // http://crbug.com/109464.
175    DCHECK(chrome::GetViewType(details->source_web_contents) !=
176           chrome::VIEW_TYPE_TAB_CONTENTS);
177    return;
178  }
179  const FrameNavigationState& frame_navigation_state =
180      tab_observer->frame_navigation_state();
181
182  FrameNavigationState::FrameID frame_id(
183      details->source_frame_id,
184      details->source_web_contents->GetRenderViewHost());
185  if (!frame_navigation_state.CanSendEvents(frame_id))
186    return;
187
188  // If the WebContents was created as a response to an IPC from a renderer
189  // (and therefore doesn't yet have a TabContents), or if it isn't yet inserted
190  // into a tab strip, we need to delay the extension event until the
191  // WebContents is fully initialized.
192  if (TabContents::FromWebContents(details->target_web_contents) == NULL ||
193      details->not_yet_in_tabstrip) {
194    pending_web_contents_[details->target_web_contents] =
195        PendingWebContents(
196            details->source_web_contents,
197            details->source_frame_id,
198            frame_navigation_state.IsMainFrame(frame_id),
199            details->target_web_contents,
200            details->target_url);
201  } else {
202    helpers::DispatchOnCreatedNavigationTarget(
203        details->source_web_contents,
204        details->target_web_contents->GetBrowserContext(),
205        details->source_frame_id,
206        frame_navigation_state.IsMainFrame(frame_id),
207        details->target_web_contents,
208        details->target_url);
209  }
210}
211
212void WebNavigationEventRouter::TabAdded(content::WebContents* tab) {
213  std::map<content::WebContents*, PendingWebContents>::iterator iter =
214      pending_web_contents_.find(tab);
215  if (iter == pending_web_contents_.end())
216    return;
217
218  WebNavigationTabObserver* tab_observer =
219      WebNavigationTabObserver::Get(iter->second.source_web_contents);
220  if (!tab_observer) {
221    NOTREACHED();
222    return;
223  }
224  const FrameNavigationState& frame_navigation_state =
225      tab_observer->frame_navigation_state();
226
227  FrameNavigationState::FrameID frame_id(
228      iter->second.source_frame_id,
229      iter->second.source_web_contents->GetRenderViewHost());
230  if (frame_navigation_state.CanSendEvents(frame_id)) {
231    helpers::DispatchOnCreatedNavigationTarget(
232        iter->second.source_web_contents,
233        iter->second.target_web_contents->GetBrowserContext(),
234        iter->second.source_frame_id,
235        iter->second.source_frame_is_main_frame,
236        iter->second.target_web_contents,
237        iter->second.target_url);
238  }
239  pending_web_contents_.erase(iter);
240}
241
242void WebNavigationEventRouter::TabDestroyed(content::WebContents* tab) {
243  pending_web_contents_.erase(tab);
244  for (std::map<content::WebContents*, PendingWebContents>::iterator i =
245           pending_web_contents_.begin(); i != pending_web_contents_.end(); ) {
246    if (i->second.source_web_contents == tab)
247      pending_web_contents_.erase(i++);
248    else
249      ++i;
250  }
251}
252
253// WebNavigationTabObserver ------------------------------------------
254
255WebNavigationTabObserver::WebNavigationTabObserver(
256    content::WebContents* web_contents)
257    : WebContentsObserver(web_contents),
258      render_view_host_(NULL),
259      pending_render_view_host_(NULL) {
260  g_tab_observer.Get().insert(TabObserverMap::value_type(web_contents, this));
261  registrar_.Add(this,
262                 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
263                 content::Source<content::WebContents>(web_contents));
264  registrar_.Add(this,
265                 content::NOTIFICATION_RENDER_VIEW_HOST_DELETED,
266                 content::NotificationService::AllSources());
267}
268
269WebNavigationTabObserver::~WebNavigationTabObserver() {}
270
271// static
272WebNavigationTabObserver* WebNavigationTabObserver::Get(
273    content::WebContents* web_contents) {
274  TabObserverMap::iterator i = g_tab_observer.Get().find(web_contents);
275  return i == g_tab_observer.Get().end() ? NULL : i->second;
276}
277
278content::RenderViewHost* WebNavigationTabObserver::GetRenderViewHostInProcess(
279    int process_id) const {
280  if (render_view_host_ &&
281      render_view_host_->GetProcess()->GetID() == process_id) {
282    return render_view_host_;
283  }
284  if (pending_render_view_host_ &&
285      pending_render_view_host_->GetProcess()->GetID() == process_id) {
286    return pending_render_view_host_;
287  }
288  return NULL;
289}
290
291void WebNavigationTabObserver::Observe(
292    int type,
293    const content::NotificationSource& source,
294    const content::NotificationDetails& details) {
295  switch (type) {
296    case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
297      content::ResourceRedirectDetails* resource_redirect_details =
298          content::Details<content::ResourceRedirectDetails>(details).ptr();
299      ResourceType::Type resource_type =
300          resource_redirect_details->resource_type;
301      if (resource_type == ResourceType::MAIN_FRAME ||
302          resource_type == ResourceType::SUB_FRAME) {
303        content::RenderViewHost* render_view_host = NULL;
304        if (render_view_host_ &&
305            resource_redirect_details->origin_child_id ==
306                render_view_host_->GetProcess()->GetID() &&
307            resource_redirect_details->origin_route_id ==
308                render_view_host_->GetRoutingID()) {
309          render_view_host = render_view_host_;
310        } else if (pending_render_view_host_ &&
311                   resource_redirect_details->origin_child_id ==
312                       pending_render_view_host_->GetProcess()->GetID() &&
313                   resource_redirect_details->origin_route_id ==
314                       pending_render_view_host_->GetRoutingID()) {
315          render_view_host = pending_render_view_host_;
316        }
317        if (!render_view_host)
318          return;
319        FrameNavigationState::FrameID frame_id(
320            resource_redirect_details->frame_id, render_view_host);
321        navigation_state_.SetIsServerRedirected(frame_id);
322      }
323      break;
324    }
325
326    case content::NOTIFICATION_RENDER_VIEW_HOST_DELETED: {
327      content::RenderViewHost* render_view_host =
328          content::Source<content::RenderViewHost>(source).ptr();
329      if (render_view_host == render_view_host_) {
330        render_view_host_ = NULL;
331        if (pending_render_view_host_) {
332          SendErrorEvents(web_contents(),
333                          pending_render_view_host_,
334                          FrameNavigationState::FrameID());
335          pending_render_view_host_ = NULL;
336        }
337      } else if (render_view_host == pending_render_view_host_) {
338        pending_render_view_host_ = NULL;
339      } else {
340        return;
341      }
342      SendErrorEvents(
343          web_contents(), render_view_host, FrameNavigationState::FrameID());
344      break;
345    }
346
347    default:
348      NOTREACHED();
349  }
350}
351
352void WebNavigationTabObserver::AboutToNavigateRenderView(
353    content::RenderViewHost* render_view_host) {
354  if (!render_view_host_) {
355    render_view_host_ = render_view_host;
356  } else if (render_view_host != render_view_host_) {
357    if (pending_render_view_host_) {
358      SendErrorEvents(web_contents(),
359                      pending_render_view_host_,
360                      FrameNavigationState::FrameID());
361    }
362    pending_render_view_host_ = render_view_host;
363  }
364}
365
366void WebNavigationTabObserver::DidStartProvisionalLoadForFrame(
367    int64 frame_num,
368    int64 parent_frame_num,
369    bool is_main_frame,
370    const GURL& validated_url,
371    bool is_error_page,
372    content::RenderViewHost* render_view_host) {
373  if (!render_view_host_)
374    render_view_host_ = render_view_host;
375  if (render_view_host != render_view_host_ &&
376      render_view_host != pending_render_view_host_)
377    return;
378
379  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
380  FrameNavigationState::FrameID parent_frame_id(
381      parent_frame_num, render_view_host);
382
383  navigation_state_.TrackFrame(frame_id,
384                               parent_frame_id,
385                               validated_url,
386                               is_main_frame,
387                               is_error_page);
388  if (!navigation_state_.CanSendEvents(frame_id))
389    return;
390
391  helpers::DispatchOnBeforeNavigate(
392      web_contents(),
393      render_view_host->GetProcess()->GetID(),
394      frame_num,
395      is_main_frame,
396      parent_frame_num,
397      navigation_state_.IsMainFrame(parent_frame_id),
398      validated_url);
399}
400
401void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame(
402    int64 frame_num,
403    bool is_main_frame,
404    const GURL& url,
405    content::PageTransition transition_type,
406    content::RenderViewHost* render_view_host) {
407  if (render_view_host != render_view_host_ &&
408      render_view_host != pending_render_view_host_)
409    return;
410  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
411
412  bool is_reference_fragment_navigation =
413      IsReferenceFragmentNavigation(frame_id, url);
414  bool is_history_state_modification =
415      navigation_state_.GetNavigationCommitted(frame_id);
416
417  if (is_main_frame && render_view_host_ == render_view_host) {
418    // Changing the reference fragment or the history state using
419    // history.pushState or history.replaceState does not cancel on-going
420    // iframe navigations.
421    if (!is_reference_fragment_navigation && !is_history_state_modification)
422      SendErrorEvents(web_contents(), render_view_host_, frame_id);
423    if (pending_render_view_host_) {
424      SendErrorEvents(web_contents(),
425                      pending_render_view_host_,
426                      FrameNavigationState::FrameID());
427      pending_render_view_host_ = NULL;
428    }
429  } else if (pending_render_view_host_ == render_view_host) {
430    SendErrorEvents(
431        web_contents(), render_view_host_, FrameNavigationState::FrameID());
432    render_view_host_ = pending_render_view_host_;
433    pending_render_view_host_ = NULL;
434  }
435
436  // Update the URL as it might have changed.
437  navigation_state_.UpdateFrame(frame_id, url);
438  navigation_state_.SetNavigationCommitted(frame_id);
439
440  if (is_reference_fragment_navigation || is_history_state_modification)
441    navigation_state_.SetNavigationCompleted(frame_id);
442
443  if (!navigation_state_.CanSendEvents(frame_id))
444    return;
445
446  if (is_reference_fragment_navigation) {
447    helpers::DispatchOnCommitted(
448        keys::kOnReferenceFragmentUpdated,
449        web_contents(),
450        frame_num,
451        is_main_frame,
452        url,
453        transition_type);
454  } else if (is_history_state_modification) {
455    helpers::DispatchOnCommitted(
456        keys::kOnHistoryStateUpdated,
457        web_contents(),
458        frame_num,
459        is_main_frame,
460        url,
461        transition_type);
462  } else {
463    if (navigation_state_.GetIsServerRedirected(frame_id)) {
464      transition_type = static_cast<content::PageTransition>(
465          transition_type | content::PAGE_TRANSITION_SERVER_REDIRECT);
466    }
467    helpers::DispatchOnCommitted(
468        keys::kOnCommitted,
469        web_contents(),
470        frame_num,
471        is_main_frame,
472        url,
473        transition_type);
474  }
475}
476
477void WebNavigationTabObserver::DidFailProvisionalLoad(
478    int64 frame_num,
479    bool is_main_frame,
480    const GURL& validated_url,
481    int error_code,
482    const string16& error_description,
483    content::RenderViewHost* render_view_host) {
484  if (render_view_host != render_view_host_ &&
485      render_view_host != pending_render_view_host_)
486    return;
487  bool stop_tracking_frames = false;
488  if (render_view_host == pending_render_view_host_) {
489    pending_render_view_host_ = NULL;
490    stop_tracking_frames = true;
491  }
492
493  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
494  if (navigation_state_.CanSendEvents(frame_id)) {
495    helpers::DispatchOnErrorOccurred(
496        web_contents(), render_view_host->GetProcess()->GetID(), validated_url,
497        frame_num, is_main_frame, error_code);
498  }
499  navigation_state_.SetErrorOccurredInFrame(frame_id);
500  if (stop_tracking_frames) {
501    navigation_state_.StopTrackingFramesInRVH(render_view_host,
502                                              FrameNavigationState::FrameID());
503  }
504}
505
506void WebNavigationTabObserver::DocumentLoadedInFrame(
507    int64 frame_num,
508    content::RenderViewHost* render_view_host) {
509  if (render_view_host != render_view_host_)
510    return;
511  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
512  if (!navigation_state_.CanSendEvents(frame_id))
513    return;
514  helpers::DispatchOnDOMContentLoaded(web_contents(),
515                                      navigation_state_.GetUrl(frame_id),
516                                      navigation_state_.IsMainFrame(frame_id),
517                                      frame_num);
518}
519
520void WebNavigationTabObserver::DidFinishLoad(
521    int64 frame_num,
522    const GURL& validated_url,
523    bool is_main_frame,
524    content::RenderViewHost* render_view_host) {
525  if (render_view_host != render_view_host_)
526    return;
527  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
528  // When showing replacement content, we might get load signals for frames
529  // that weren't reguarly loaded.
530  if (!navigation_state_.IsValidFrame(frame_id))
531    return;
532  navigation_state_.SetNavigationCompleted(frame_id);
533  if (!navigation_state_.CanSendEvents(frame_id))
534    return;
535  DCHECK_EQ(navigation_state_.GetUrl(frame_id), validated_url);
536  DCHECK_EQ(navigation_state_.IsMainFrame(frame_id), is_main_frame);
537  helpers::DispatchOnCompleted(web_contents(),
538                               validated_url,
539                               is_main_frame,
540                               frame_num);
541}
542
543void WebNavigationTabObserver::DidFailLoad(
544    int64 frame_num,
545    const GURL& validated_url,
546    bool is_main_frame,
547    int error_code,
548    const string16& error_description,
549    content::RenderViewHost* render_view_host) {
550  if (render_view_host != render_view_host_)
551    return;
552  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
553  // When showing replacement content, we might get load signals for frames
554  // that weren't reguarly loaded.
555  if (!navigation_state_.IsValidFrame(frame_id))
556    return;
557  if (navigation_state_.CanSendEvents(frame_id)) {
558    helpers::DispatchOnErrorOccurred(
559        web_contents(), render_view_host->GetProcess()->GetID(), validated_url,
560        frame_num, is_main_frame, error_code);
561  }
562  navigation_state_.SetErrorOccurredInFrame(frame_id);
563}
564
565void WebNavigationTabObserver::DidOpenRequestedURL(
566    content::WebContents* new_contents,
567    const GURL& url,
568    const content::Referrer& referrer,
569    WindowOpenDisposition disposition,
570    content::PageTransition transition,
571    int64 source_frame_num) {
572  FrameNavigationState::FrameID frame_id(source_frame_num, render_view_host_);
573  if (!navigation_state_.CanSendEvents(frame_id))
574    return;
575
576  // We only send the onCreatedNavigationTarget if we end up creating a new
577  // window.
578  if (disposition != SINGLETON_TAB &&
579      disposition != NEW_FOREGROUND_TAB &&
580      disposition != NEW_BACKGROUND_TAB &&
581      disposition != NEW_POPUP &&
582      disposition != NEW_WINDOW &&
583      disposition != OFF_THE_RECORD)
584    return;
585
586  helpers::DispatchOnCreatedNavigationTarget(
587      web_contents(),
588      new_contents->GetBrowserContext(),
589      source_frame_num,
590      navigation_state_.IsMainFrame(frame_id),
591      new_contents,
592      url);
593}
594
595void WebNavigationTabObserver::WebContentsDestroyed(content::WebContents* tab) {
596  g_tab_observer.Get().erase(tab);
597  registrar_.RemoveAll();
598  SendErrorEvents(tab, NULL, FrameNavigationState::FrameID());
599}
600
601void WebNavigationTabObserver::SendErrorEvents(
602    content::WebContents* web_contents,
603    content::RenderViewHost* render_view_host,
604    FrameNavigationState::FrameID id_to_skip) {
605  for (FrameNavigationState::const_iterator frame = navigation_state_.begin();
606       frame != navigation_state_.end(); ++frame) {
607    if (!navigation_state_.GetNavigationCompleted(*frame) &&
608        navigation_state_.CanSendEvents(*frame) &&
609        *frame != id_to_skip &&
610        (!render_view_host || frame->render_view_host == render_view_host)) {
611      navigation_state_.SetErrorOccurredInFrame(*frame);
612      helpers::DispatchOnErrorOccurred(
613          web_contents,
614          frame->render_view_host->GetProcess()->GetID(),
615          navigation_state_.GetUrl(*frame),
616          frame->frame_num,
617          navigation_state_.IsMainFrame(*frame),
618          net::ERR_ABORTED);
619    }
620  }
621  if (render_view_host)
622    navigation_state_.StopTrackingFramesInRVH(render_view_host, id_to_skip);
623}
624
625// See also NavigationController::IsURLInPageNavigation.
626bool WebNavigationTabObserver::IsReferenceFragmentNavigation(
627    FrameNavigationState::FrameID frame_id,
628    const GURL& url) {
629  GURL existing_url = navigation_state_.GetUrl(frame_id);
630  if (existing_url == url)
631    return false;
632
633  url_canon::Replacements<char> replacements;
634  replacements.ClearRef();
635  return existing_url.ReplaceComponents(replacements) ==
636      url.ReplaceComponents(replacements);
637}
638
639bool GetFrameFunction::RunImpl() {
640  scoped_ptr<GetFrame::Params> params(GetFrame::Params::Create(*args_));
641  EXTENSION_FUNCTION_VALIDATE(params.get());
642  int tab_id = params->details.tab_id;
643  int frame_id = params->details.frame_id;
644  int process_id = params->details.process_id;
645
646  SetResult(Value::CreateNullValue());
647
648  content::WebContents* web_contents;
649  if (!ExtensionTabUtil::GetTabById(tab_id,
650                                    profile(),
651                                    include_incognito(),
652                                    NULL, NULL,
653                                    &web_contents,
654                                    NULL) ||
655      !web_contents) {
656    return true;
657  }
658
659  WebNavigationTabObserver* observer =
660      WebNavigationTabObserver::Get(web_contents);
661  DCHECK(observer);
662
663  const FrameNavigationState& frame_navigation_state =
664      observer->frame_navigation_state();
665
666  if (frame_id == 0)
667    frame_id = frame_navigation_state.GetMainFrameID().frame_num;
668
669  content::RenderViewHost* render_view_host =
670      observer->GetRenderViewHostInProcess(process_id);
671  if (!render_view_host)
672    return true;
673
674  FrameNavigationState::FrameID internal_frame_id(frame_id, render_view_host);
675  if (!frame_navigation_state.IsValidFrame(internal_frame_id))
676    return true;
677
678  GURL frame_url = frame_navigation_state.GetUrl(internal_frame_id);
679  if (!frame_navigation_state.IsValidUrl(frame_url))
680    return true;
681
682  GetFrame::Results::Details frame_details;
683  frame_details.url = frame_url.spec();
684  frame_details.error_occurred =
685      frame_navigation_state.GetErrorOccurredInFrame(internal_frame_id);
686  FrameNavigationState::FrameID parent_frame_id =
687      frame_navigation_state.GetParentFrameID(internal_frame_id);
688  frame_details.parent_frame_id = helpers::GetFrameId(
689      frame_navigation_state.IsMainFrame(parent_frame_id),
690      parent_frame_id.frame_num);
691  results_ = GetFrame::Results::Create(frame_details);
692  return true;
693}
694
695bool GetAllFramesFunction::RunImpl() {
696  scoped_ptr<GetAllFrames::Params> params(GetAllFrames::Params::Create(*args_));
697  EXTENSION_FUNCTION_VALIDATE(params.get());
698  int tab_id = params->details.tab_id;
699
700  SetResult(Value::CreateNullValue());
701
702  content::WebContents* web_contents;
703  if (!ExtensionTabUtil::GetTabById(tab_id,
704                                    profile(),
705                                    include_incognito(),
706                                    NULL, NULL,
707                                    &web_contents,
708                                    NULL) ||
709      !web_contents) {
710    return true;
711  }
712
713  WebNavigationTabObserver* observer =
714      WebNavigationTabObserver::Get(web_contents);
715  DCHECK(observer);
716
717  const FrameNavigationState& navigation_state =
718      observer->frame_navigation_state();
719
720  std::vector<linked_ptr<GetAllFrames::Results::DetailsElement> > result_list;
721  for (FrameNavigationState::const_iterator it = navigation_state.begin();
722       it != navigation_state.end(); ++it) {
723    FrameNavigationState::FrameID frame_id = *it;
724    FrameNavigationState::FrameID parent_frame_id =
725        navigation_state.GetParentFrameID(frame_id);
726    GURL frame_url = navigation_state.GetUrl(frame_id);
727    if (!navigation_state.IsValidUrl(frame_url))
728      continue;
729    linked_ptr<GetAllFrames::Results::DetailsElement> frame(
730        new GetAllFrames::Results::DetailsElement());
731    frame->url = frame_url.spec();
732    frame->frame_id = helpers::GetFrameId(
733        navigation_state.IsMainFrame(frame_id), frame_id.frame_num);
734    frame->parent_frame_id = helpers::GetFrameId(
735        navigation_state.IsMainFrame(parent_frame_id),
736        parent_frame_id.frame_num);
737    frame->process_id = frame_id.render_view_host->GetProcess()->GetID();
738    frame->error_occurred = navigation_state.GetErrorOccurredInFrame(frame_id);
739    result_list.push_back(frame);
740  }
741  results_ = GetAllFrames::Results::Create(result_list);
742  return true;
743}
744
745}  // namespace extensions
746