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/guestview/adview/adview_guest.h" 9 #include "chrome/browser/guestview/guestview_constants.h" 10 #include "chrome/browser/guestview/webview/webview_guest.h" 11 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/common/content_settings.h" 13 #include "content/public/browser/render_process_host.h" 14 #include "content/public/browser/web_contents.h" 15 #include "content/public/common/url_constants.h" 16 #include "extensions/browser/event_router.h" 17 #include "net/base/escape.h" 18 19 using content::WebContents; 20 21 namespace { 22 23 // <embedder_process_id, guest_instance_id> => GuestView* 24 typedef std::map<std::pair<int, int>, GuestView*> EmbedderGuestViewMap; 25 static base::LazyInstance<EmbedderGuestViewMap> embedder_guestview_map = 26 LAZY_INSTANCE_INITIALIZER; 27 28 typedef std::map<WebContents*, GuestView*> WebContentsGuestViewMap; 29 static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map = 30 LAZY_INSTANCE_INITIALIZER; 31 32 } // namespace 33 34 GuestView::Event::Event(const std::string& name, 35 scoped_ptr<DictionaryValue> args) 36 : name_(name), 37 args_(args.Pass()) { 38 } 39 40 GuestView::Event::~Event() { 41 } 42 43 scoped_ptr<DictionaryValue> GuestView::Event::GetArguments() { 44 return args_.Pass(); 45 } 46 47 GuestView::GuestView(WebContents* guest_web_contents, 48 const std::string& extension_id) 49 : guest_web_contents_(guest_web_contents), 50 embedder_web_contents_(NULL), 51 extension_id_(extension_id), 52 embedder_render_process_id_(0), 53 browser_context_(guest_web_contents->GetBrowserContext()), 54 guest_instance_id_(guest_web_contents->GetEmbeddedInstanceID()), 55 view_instance_id_(guestview::kInstanceIDNone) { 56 webcontents_guestview_map.Get().insert( 57 std::make_pair(guest_web_contents, this)); 58 } 59 60 // static 61 GuestView::Type GuestView::GetViewTypeFromString(const std::string& api_type) { 62 if (api_type == "adview") { 63 return GuestView::ADVIEW; 64 } else if (api_type == "webview") { 65 return GuestView::WEBVIEW; 66 } 67 return GuestView::UNKNOWN; 68 } 69 70 // static 71 GuestView* GuestView::Create(WebContents* guest_web_contents, 72 const std::string& extension_id, 73 GuestView::Type view_type) { 74 switch (view_type) { 75 case GuestView::WEBVIEW: 76 return new WebViewGuest(guest_web_contents, extension_id); 77 case GuestView::ADVIEW: 78 return new AdViewGuest(guest_web_contents, extension_id); 79 default: 80 NOTREACHED(); 81 return NULL; 82 } 83 } 84 85 // static 86 GuestView* GuestView::FromWebContents(WebContents* web_contents) { 87 WebContentsGuestViewMap* guest_map = webcontents_guestview_map.Pointer(); 88 WebContentsGuestViewMap::iterator it = guest_map->find(web_contents); 89 return it == guest_map->end() ? NULL : it->second; 90 } 91 92 // static 93 GuestView* GuestView::From(int embedder_process_id, int guest_instance_id) { 94 EmbedderGuestViewMap* guest_map = embedder_guestview_map.Pointer(); 95 EmbedderGuestViewMap::iterator it = guest_map->find( 96 std::make_pair(embedder_process_id, guest_instance_id)); 97 return it == guest_map->end() ? NULL : it->second; 98 } 99 100 // static 101 bool GuestView::GetGuestPartitionConfigForSite(const GURL& site, 102 std::string* partition_domain, 103 std::string* partition_name, 104 bool* in_memory) { 105 if (!site.SchemeIs(content::kGuestScheme)) 106 return false; 107 108 // Since guest URLs are only used for packaged apps, there must be an app 109 // id in the URL. 110 CHECK(site.has_host()); 111 *partition_domain = site.host(); 112 // Since persistence is optional, the path must either be empty or the 113 // literal string. 114 *in_memory = (site.path() != "/persist"); 115 // The partition name is user supplied value, which we have encoded when the 116 // URL was created, so it needs to be decoded. 117 *partition_name = net::UnescapeURLComponent(site.query(), 118 net::UnescapeRule::NORMAL); 119 return true; 120 } 121 122 // static 123 void GuestView::GetDefaultContentSettingRules( 124 RendererContentSettingRules* rules, bool incognito) { 125 rules->image_rules.push_back(ContentSettingPatternSource( 126 ContentSettingsPattern::Wildcard(), 127 ContentSettingsPattern::Wildcard(), 128 CONTENT_SETTING_ALLOW, 129 std::string(), 130 incognito)); 131 132 rules->script_rules.push_back(ContentSettingPatternSource( 133 ContentSettingsPattern::Wildcard(), 134 ContentSettingsPattern::Wildcard(), 135 CONTENT_SETTING_ALLOW, 136 std::string(), 137 incognito)); 138 } 139 140 void GuestView::Attach(content::WebContents* embedder_web_contents, 141 const base::DictionaryValue& args) { 142 embedder_web_contents_ = embedder_web_contents; 143 embedder_render_process_id_ = 144 embedder_web_contents->GetRenderProcessHost()->GetID(); 145 args.GetInteger(guestview::kParameterInstanceId, &view_instance_id_); 146 147 std::pair<int, int> key(embedder_render_process_id_, guest_instance_id_); 148 embedder_guestview_map.Get().insert(std::make_pair(key, this)); 149 150 // GuestView::Attach is called prior to initialization (and initial 151 // navigation) of the guest in the content layer in order to permit mapping 152 // the necessary associations between the <*view> element and its guest. This 153 // is needed by the <webview> WebRequest API to allow intercepting resource 154 // requests during navigation. However, queued events should be fired after 155 // content layer initialization in order to ensure that load events (such as 156 // 'loadstop') fire in embedder after the contentWindow is available. 157 base::MessageLoop::current()->PostTask( 158 FROM_HERE, 159 base::Bind(&GuestView::SendQueuedEvents, 160 base::Unretained(this))); 161 } 162 163 GuestView::Type GuestView::GetViewType() const { 164 return GuestView::UNKNOWN; 165 } 166 167 WebViewGuest* GuestView::AsWebView() { 168 return NULL; 169 } 170 171 AdViewGuest* GuestView::AsAdView() { 172 return NULL; 173 } 174 175 GuestView::~GuestView() { 176 std::pair<int, int> key(embedder_render_process_id_, guest_instance_id_); 177 embedder_guestview_map.Get().erase(key); 178 179 webcontents_guestview_map.Get().erase(guest_web_contents()); 180 181 while (!pending_events_.empty()) { 182 delete pending_events_.front(); 183 pending_events_.pop(); 184 } 185 } 186 187 void GuestView::DispatchEvent(Event* event) { 188 if (!attached()) { 189 pending_events_.push(event); 190 return; 191 } 192 193 Profile* profile = Profile::FromBrowserContext(browser_context_); 194 195 extensions::EventFilteringInfo info; 196 info.SetURL(GURL()); 197 info.SetInstanceID(guest_instance_id_); 198 scoped_ptr<ListValue> args(new ListValue()); 199 args->Append(event->GetArguments().release()); 200 201 extensions::EventRouter::DispatchEvent( 202 embedder_web_contents_, profile, extension_id_, 203 event->name(), args.Pass(), 204 extensions::EventRouter::USER_GESTURE_UNKNOWN, info); 205 206 delete event; 207 } 208 209 void GuestView::SendQueuedEvents() { 210 if (!attached()) 211 return; 212 213 while (!pending_events_.empty()) { 214 Event* event = pending_events_.front(); 215 pending_events_.pop(); 216 DispatchEvent(event); 217 } 218 } 219