1// Copyright 2013 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/guestview/guestview.h"
6
7#include "base/lazy_instance.h"
8#include "chrome/browser/extensions/event_router.h"
9#include "chrome/browser/guestview/guestview_constants.h"
10#include "chrome/browser/profiles/profile.h"
11#include "content/public/browser/render_process_host.h"
12#include "content/public/browser/web_contents.h"
13
14using content::WebContents;
15
16namespace {
17
18// <embedder_process_id, guest_instance_id> => GuestView*
19typedef std::map<std::pair<int, int>, GuestView*> EmbedderGuestViewMap;
20static base::LazyInstance<EmbedderGuestViewMap> embedder_guestview_map =
21    LAZY_INSTANCE_INITIALIZER;
22
23typedef std::map<WebContents*, GuestView*> WebContentsGuestViewMap;
24static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map =
25    LAZY_INSTANCE_INITIALIZER;
26
27}  // namespace
28
29GuestView::Event::Event(const std::string& event_name,
30                        scoped_ptr<DictionaryValue> args)
31    : event_name_(event_name),
32      args_(args.Pass()) {
33}
34
35GuestView::Event::~Event() {
36}
37
38scoped_ptr<DictionaryValue> GuestView::Event::GetArguments() {
39  return args_.Pass();
40}
41
42GuestView::GuestView(WebContents* guest_web_contents)
43    : guest_web_contents_(guest_web_contents),
44      embedder_web_contents_(NULL),
45      embedder_render_process_id_(0),
46      browser_context_(guest_web_contents->GetBrowserContext()),
47      guest_instance_id_(guest_web_contents->GetEmbeddedInstanceID()),
48      view_instance_id_(guestview::kInstanceIDNone) {
49  webcontents_guestview_map.Get().insert(
50      std::make_pair(guest_web_contents, this));
51}
52
53// static
54GuestView* GuestView::FromWebContents(WebContents* web_contents) {
55  WebContentsGuestViewMap* guest_map = webcontents_guestview_map.Pointer();
56  WebContentsGuestViewMap::iterator it = guest_map->find(web_contents);
57  return it == guest_map->end() ? NULL : it->second;
58}
59
60// static
61GuestView* GuestView::From(int embedder_process_id, int guest_instance_id) {
62  EmbedderGuestViewMap* guest_map = embedder_guestview_map.Pointer();
63  EmbedderGuestViewMap::iterator it = guest_map->find(
64      std::make_pair(embedder_process_id, guest_instance_id));
65  return it == guest_map->end() ? NULL : it->second;
66}
67
68void GuestView::Attach(content::WebContents* embedder_web_contents,
69                       const std::string& extension_id,
70                       const base::DictionaryValue& args) {
71  embedder_web_contents_ = embedder_web_contents;
72  embedder_render_process_id_ =
73      embedder_web_contents->GetRenderProcessHost()->GetID();
74  extension_id_ = extension_id;
75  args.GetInteger(guestview::kParameterInstanceId, &view_instance_id_);
76
77  std::pair<int, int> key(embedder_render_process_id_, guest_instance_id_);
78  embedder_guestview_map.Get().insert(std::make_pair(key, this));
79
80  // GuestView::Attach is called prior to initialization (and initial
81  // navigation) of the guest in the content layer in order to permit mapping
82  // the necessary associations between the <*view> element and its guest. This
83  // is needed by the <webview> WebRequest API to allow intercepting resource
84  // requests during navigation. However, queued events should be fired after
85  // content layer initialization in order to ensure that load events (such as
86  // 'loadstop') fire in embedder after the contentWindow is available.
87  base::MessageLoop::current()->PostTask(
88      FROM_HERE,
89      base::Bind(&GuestView::SendQueuedEvents,
90                  base::Unretained(this)));
91}
92
93GuestView::Type GuestView::GetViewType() const {
94  return GuestView::UNKNOWN;
95}
96
97WebViewGuest* GuestView::AsWebView() {
98  return NULL;
99}
100
101AdViewGuest* GuestView::AsAdView() {
102  return NULL;
103}
104
105GuestView::~GuestView() {
106  std::pair<int, int> key(embedder_render_process_id_, guest_instance_id_);
107  embedder_guestview_map.Get().erase(key);
108
109  webcontents_guestview_map.Get().erase(guest_web_contents());
110
111  while (!pending_events_.empty()) {
112    delete pending_events_.front();
113    pending_events_.pop();
114  }
115}
116
117void GuestView::DispatchEvent(Event* event) {
118  if (!attached()) {
119    pending_events_.push(event);
120    return;
121  }
122
123  Profile* profile = Profile::FromBrowserContext(browser_context_);
124
125  extensions::EventFilteringInfo info;
126  info.SetURL(GURL());
127  info.SetInstanceID(guest_instance_id_);
128  scoped_ptr<ListValue> args(new ListValue());
129  args->Append(event->GetArguments().release());
130
131  extensions::EventRouter::DispatchEvent(
132      embedder_web_contents_, profile, extension_id_,
133      event->event_name(), args.Pass(),
134      extensions::EventRouter::USER_GESTURE_UNKNOWN, info);
135
136  delete event;
137}
138
139void GuestView::SendQueuedEvents() {
140  if (!attached())
141    return;
142
143  while (!pending_events_.empty()) {
144    Event* event = pending_events_.front();
145    pending_events_.pop();
146    DispatchEvent(event);
147  }
148}
149