// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/guest_view/guest_view_base.h" #include "base/command_line.h" #include "base/lazy_instance.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/guest_view/app_view/app_view_guest.h" #include "chrome/browser/guest_view/guest_view_constants.h" #include "chrome/browser/guest_view/guest_view_manager.h" #include "chrome/browser/guest_view/web_view/web_view_guest.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/content_settings.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" #include "extensions/browser/event_router.h" #include "third_party/WebKit/public/web/WebInputEvent.h" using content::WebContents; namespace { typedef std::map<WebContents*, GuestViewBase*> WebContentsGuestViewMap; static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map = LAZY_INSTANCE_INITIALIZER; } // namespace GuestViewBase::Event::Event(const std::string& name, scoped_ptr<base::DictionaryValue> args) : name_(name), args_(args.Pass()) { } GuestViewBase::Event::~Event() { } scoped_ptr<base::DictionaryValue> GuestViewBase::Event::GetArguments() { return args_.Pass(); } // This observer ensures that the GuestViewBase destroys itself when its // embedder goes away. class GuestViewBase::EmbedderWebContentsObserver : public WebContentsObserver { public: explicit EmbedderWebContentsObserver(GuestViewBase* guest) : WebContentsObserver(guest->embedder_web_contents()), guest_(guest) { } virtual ~EmbedderWebContentsObserver() { } // WebContentsObserver implementation. virtual void WebContentsDestroyed() OVERRIDE { guest_->embedder_web_contents_ = NULL; guest_->EmbedderDestroyed(); guest_->Destroy(); } private: GuestViewBase* guest_; DISALLOW_COPY_AND_ASSIGN(EmbedderWebContentsObserver); }; GuestViewBase::GuestViewBase(content::BrowserContext* browser_context, int guest_instance_id) : embedder_web_contents_(NULL), embedder_render_process_id_(0), browser_context_(browser_context), guest_instance_id_(guest_instance_id), view_instance_id_(guestview::kInstanceIDNone), initialized_(false), weak_ptr_factory_(this) { } void GuestViewBase::Init( const std::string& embedder_extension_id, int embedder_render_process_id, const base::DictionaryValue& create_params, const WebContentsCreatedCallback& callback) { if (initialized_) return; initialized_ = true; CreateWebContents(embedder_extension_id, embedder_render_process_id, create_params, base::Bind(&GuestViewBase::CompleteCreateWebContents, AsWeakPtr(), embedder_extension_id, embedder_render_process_id, callback)); } void GuestViewBase::InitWithWebContents( const std::string& embedder_extension_id, int embedder_render_process_id, content::WebContents* guest_web_contents) { DCHECK(guest_web_contents); content::RenderProcessHost* embedder_render_process_host = content::RenderProcessHost::FromID(embedder_render_process_id); embedder_extension_id_ = embedder_extension_id; embedder_render_process_id_ = embedder_render_process_host->GetID(); embedder_render_process_host->AddObserver(this); WebContentsObserver::Observe(guest_web_contents); guest_web_contents->SetDelegate(this); webcontents_guestview_map.Get().insert( std::make_pair(guest_web_contents, this)); GuestViewManager::FromBrowserContext(browser_context_)-> AddGuest(guest_instance_id_, guest_web_contents); // Give the derived class an opportunity to perform additional initialization. DidInitialize(); } // static GuestViewBase* GuestViewBase::Create( content::BrowserContext* browser_context, int guest_instance_id, const std::string& view_type) { if (view_type == WebViewGuest::Type) { return new WebViewGuest(browser_context, guest_instance_id); } else if (view_type == AppViewGuest::Type) { if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableAppView)) { return NULL; } return new AppViewGuest(browser_context, guest_instance_id); } NOTREACHED(); return NULL; } // static GuestViewBase* GuestViewBase::FromWebContents(WebContents* web_contents) { WebContentsGuestViewMap* guest_map = webcontents_guestview_map.Pointer(); WebContentsGuestViewMap::iterator it = guest_map->find(web_contents); return it == guest_map->end() ? NULL : it->second; } // static GuestViewBase* GuestViewBase::From(int embedder_process_id, int guest_instance_id) { content::RenderProcessHost* host = content::RenderProcessHost::FromID(embedder_process_id); if (!host) return NULL; content::WebContents* guest_web_contents = GuestViewManager::FromBrowserContext(host->GetBrowserContext())-> GetGuestByInstanceIDSafely(guest_instance_id, embedder_process_id); if (!guest_web_contents) return NULL; return GuestViewBase::FromWebContents(guest_web_contents); } // static bool GuestViewBase::IsGuest(WebContents* web_contents) { return !!GuestViewBase::FromWebContents(web_contents); } // static void GuestViewBase::GetDefaultContentSettingRules( RendererContentSettingRules* rules, bool incognito) { rules->image_rules.push_back( ContentSettingPatternSource(ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(), CONTENT_SETTING_ALLOW, std::string(), incognito)); rules->script_rules.push_back( ContentSettingPatternSource(ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(), CONTENT_SETTING_ALLOW, std::string(), incognito)); } base::WeakPtr<GuestViewBase> GuestViewBase::AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } bool GuestViewBase::IsDragAndDropEnabled() const { return false; } void GuestViewBase::RenderProcessExited(content::RenderProcessHost* host, base::ProcessHandle handle, base::TerminationStatus status, int exit_code) { // GuestViewBase tracks the lifetime of its embedder render process until it // is attached to a particular embedder WebContents. At that point, its // lifetime is restricted in scope to the lifetime of its embedder // WebContents. CHECK(!attached()); CHECK_EQ(host->GetID(), embedder_render_process_id()); // This code path may be reached if the embedder WebContents is killed for // whatever reason immediately after a called to GuestViewInternal.createGuest // and before attaching the new guest to a frame. Destroy(); } void GuestViewBase::Destroy() { content::RenderProcessHost* host = content::RenderProcessHost::FromID(embedder_render_process_id()); if (host) host->RemoveObserver(this); WillDestroy(); if (!destruction_callback_.is_null()) destruction_callback_.Run(); delete guest_web_contents(); } void GuestViewBase::DidAttach() { // Give the derived class an opportunity to perform some actions. DidAttachToEmbedder(); SendQueuedEvents(); } int GuestViewBase::GetGuestInstanceID() const { return guest_instance_id_; } void GuestViewBase::SetOpener(GuestViewBase* guest) { if (guest && guest->IsViewType(GetViewType())) { opener_ = guest->AsWeakPtr(); return; } opener_ = base::WeakPtr<GuestViewBase>(); } void GuestViewBase::RegisterDestructionCallback( const DestructionCallback& callback) { destruction_callback_ = callback; } void GuestViewBase::WillAttach(content::WebContents* embedder_web_contents, const base::DictionaryValue& extra_params) { // After attachment, this GuestViewBase's lifetime is restricted to the // lifetime of its embedder WebContents. Observing the RenderProcessHost // of the embedder is no longer necessary. embedder_web_contents->GetRenderProcessHost()->RemoveObserver(this); embedder_web_contents_ = embedder_web_contents; embedder_web_contents_observer_.reset( new EmbedderWebContentsObserver(this)); extra_params.GetInteger(guestview::kParameterInstanceId, &view_instance_id_); extra_params_.reset(extra_params.DeepCopy()); WillAttachToEmbedder(); } void GuestViewBase::DidStopLoading(content::RenderViewHost* render_view_host) { if (!IsDragAndDropEnabled()) { const char script[] = "window.addEventListener('dragstart', function() { " " window.event.preventDefault(); " "});"; render_view_host->GetMainFrame()->ExecuteJavaScript( base::ASCIIToUTF16(script)); } DidStopLoading(); } void GuestViewBase::WebContentsDestroyed() { GuestDestroyed(); delete this; } bool GuestViewBase::ShouldFocusPageAfterCrash() { // Focus is managed elsewhere. return false; } bool GuestViewBase::PreHandleGestureEvent(content::WebContents* source, const blink::WebGestureEvent& event) { return event.type == blink::WebGestureEvent::GesturePinchBegin || event.type == blink::WebGestureEvent::GesturePinchUpdate || event.type == blink::WebGestureEvent::GesturePinchEnd; } GuestViewBase::~GuestViewBase() { std::pair<int, int> key(embedder_render_process_id_, guest_instance_id_); webcontents_guestview_map.Get().erase(guest_web_contents()); GuestViewManager::FromBrowserContext(browser_context_)-> RemoveGuest(guest_instance_id_); pending_events_.clear(); } void GuestViewBase::DispatchEventToEmbedder(Event* event) { scoped_ptr<Event> event_ptr(event); if (!in_extension()) { NOTREACHED(); return; } if (!attached()) { pending_events_.push_back(linked_ptr<Event>(event_ptr.release())); return; } Profile* profile = Profile::FromBrowserContext(browser_context_); extensions::EventFilteringInfo info; info.SetInstanceID(view_instance_id_); scoped_ptr<base::ListValue> args(new base::ListValue()); args->Append(event->GetArguments().release()); extensions::EventRouter::DispatchEvent( embedder_web_contents_, profile, embedder_extension_id_, event->name(), args.Pass(), extensions::EventRouter::USER_GESTURE_UNKNOWN, info); } void GuestViewBase::SendQueuedEvents() { if (!attached()) return; while (!pending_events_.empty()) { linked_ptr<Event> event_ptr = pending_events_.front(); pending_events_.pop_front(); DispatchEventToEmbedder(event_ptr.release()); } } void GuestViewBase::CompleteCreateWebContents( const std::string& embedder_extension_id, int embedder_render_process_id, const WebContentsCreatedCallback& callback, content::WebContents* guest_web_contents) { if (!guest_web_contents) { callback.Run(NULL); return; } InitWithWebContents(embedder_extension_id, embedder_render_process_id, guest_web_contents); callback.Run(guest_web_contents); }