1// Copyright 2014 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 "extensions/browser/guest_view/extension_options/extension_options_guest.h" 6 7#include "base/values.h" 8#include "components/crx_file/id_util.h" 9#include "content/public/browser/render_process_host.h" 10#include "content/public/browser/site_instance.h" 11#include "content/public/browser/web_contents.h" 12#include "extensions/browser/api/extensions_api_client.h" 13#include "extensions/browser/extension_function_dispatcher.h" 14#include "extensions/browser/extension_registry.h" 15#include "extensions/browser/extension_web_contents_observer.h" 16#include "extensions/browser/guest_view/extension_options/extension_options_constants.h" 17#include "extensions/browser/guest_view/extension_options/extension_options_guest_delegate.h" 18#include "extensions/browser/guest_view/guest_view_manager.h" 19#include "extensions/common/api/extension_options_internal.h" 20#include "extensions/common/constants.h" 21#include "extensions/common/extension.h" 22#include "extensions/common/extension_messages.h" 23#include "extensions/common/feature_switch.h" 24#include "extensions/common/manifest_handlers/options_page_info.h" 25#include "extensions/common/permissions/permissions_data.h" 26#include "extensions/strings/grit/extensions_strings.h" 27#include "ipc/ipc_message_macros.h" 28 29using content::WebContents; 30using namespace extensions::core_api; 31 32namespace extensions { 33 34// static 35const char ExtensionOptionsGuest::Type[] = "extensionoptions"; 36 37ExtensionOptionsGuest::ExtensionOptionsGuest( 38 content::BrowserContext* browser_context, 39 int guest_instance_id) 40 : GuestView<ExtensionOptionsGuest>(browser_context, guest_instance_id), 41 extension_options_guest_delegate_( 42 extensions::ExtensionsAPIClient::Get() 43 ->CreateExtensionOptionsGuestDelegate(this)) { 44} 45 46ExtensionOptionsGuest::~ExtensionOptionsGuest() { 47} 48 49// static 50extensions::GuestViewBase* ExtensionOptionsGuest::Create( 51 content::BrowserContext* browser_context, 52 int guest_instance_id) { 53 if (!extensions::FeatureSwitch::embedded_extension_options()->IsEnabled()) { 54 return NULL; 55 } 56 return new ExtensionOptionsGuest(browser_context, guest_instance_id); 57} 58 59void ExtensionOptionsGuest::CreateWebContents( 60 const std::string& embedder_extension_id, 61 int embedder_render_process_id, 62 const GURL& embedder_site_url, 63 const base::DictionaryValue& create_params, 64 const WebContentsCreatedCallback& callback) { 65 // Get the extension's base URL. 66 std::string extension_id; 67 create_params.GetString(extensionoptions::kExtensionId, &extension_id); 68 69 if (!crx_file::id_util::IdIsValid(extension_id)) { 70 callback.Run(NULL); 71 return; 72 } 73 74 if (crx_file::id_util::IdIsValid(embedder_extension_id) && 75 extension_id != embedder_extension_id) { 76 // Extensions cannot embed other extensions' options pages. 77 callback.Run(NULL); 78 return; 79 } 80 81 GURL extension_url = 82 extensions::Extension::GetBaseURLFromExtensionId(extension_id); 83 if (!extension_url.is_valid()) { 84 callback.Run(NULL); 85 return; 86 } 87 88 // Get the options page URL for later use. 89 extensions::ExtensionRegistry* registry = 90 extensions::ExtensionRegistry::Get(browser_context()); 91 const extensions::Extension* extension = 92 registry->enabled_extensions().GetByID(extension_id); 93 options_page_ = extensions::OptionsPageInfo::GetOptionsPage(extension); 94 if (!options_page_.is_valid()) { 95 callback.Run(NULL); 96 return; 97 } 98 99 // Create a WebContents using the extension URL. The options page's 100 // WebContents should live in the same process as its parent extension's 101 // WebContents, so we can use |extension_url| for creating the SiteInstance. 102 content::SiteInstance* options_site_instance = 103 content::SiteInstance::CreateForURL(browser_context(), extension_url); 104 WebContents::CreateParams params(browser_context(), options_site_instance); 105 params.guest_delegate = this; 106 callback.Run(WebContents::Create(params)); 107} 108 109void ExtensionOptionsGuest::DidAttachToEmbedder() { 110 SetUpAutoSize(); 111 web_contents()->GetController().LoadURL(options_page_, 112 content::Referrer(), 113 ui::PAGE_TRANSITION_LINK, 114 std::string()); 115} 116 117void ExtensionOptionsGuest::DidInitialize() { 118 extension_function_dispatcher_.reset( 119 new extensions::ExtensionFunctionDispatcher(browser_context(), this)); 120 if (extension_options_guest_delegate_) { 121 extension_options_guest_delegate_->DidInitialize(); 122 } 123} 124 125void ExtensionOptionsGuest::DidStopLoading() { 126 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); 127 DispatchEventToEmbedder(new extensions::GuestViewBase::Event( 128 extension_options_internal::OnLoad::kEventName, args.Pass())); 129} 130 131const char* ExtensionOptionsGuest::GetAPINamespace() const { 132 return extensionoptions::kAPINamespace; 133} 134 135int ExtensionOptionsGuest::GetTaskPrefix() const { 136 return IDS_EXTENSION_TASK_MANAGER_EXTENSIONOPTIONS_TAG_PREFIX; 137} 138 139void ExtensionOptionsGuest::GuestSizeChangedDueToAutoSize( 140 const gfx::Size& old_size, 141 const gfx::Size& new_size) { 142 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); 143 args->SetInteger(extensionoptions::kNewWidth, new_size.width()); 144 args->SetInteger(extensionoptions::kNewHeight, new_size.height()); 145 args->SetInteger(extensionoptions::kOldWidth, old_size.width()); 146 args->SetInteger(extensionoptions::kOldHeight, old_size.height()); 147 DispatchEventToEmbedder(new extensions::GuestViewBase::Event( 148 extension_options_internal::OnSizeChanged::kEventName, args.Pass())); 149} 150 151bool ExtensionOptionsGuest::IsAutoSizeSupported() const { 152 return true; 153} 154 155content::WebContents* ExtensionOptionsGuest::GetAssociatedWebContents() const { 156 return web_contents(); 157} 158 159content::WebContents* ExtensionOptionsGuest::OpenURLFromTab( 160 content::WebContents* source, 161 const content::OpenURLParams& params) { 162 if (!extension_options_guest_delegate_) 163 return NULL; 164 165 // Don't allow external URLs with the CURRENT_TAB disposition be opened in 166 // this guest view, change the disposition to NEW_FOREGROUND_TAB. 167 if ((!params.url.SchemeIs(extensions::kExtensionScheme) || 168 params.url.host() != options_page_.host()) && 169 params.disposition == CURRENT_TAB) { 170 return extension_options_guest_delegate_->OpenURLInNewTab( 171 content::OpenURLParams(params.url, 172 params.referrer, 173 params.frame_tree_node_id, 174 NEW_FOREGROUND_TAB, 175 params.transition, 176 params.is_renderer_initiated)); 177 } 178 return extension_options_guest_delegate_->OpenURLInNewTab(params); 179} 180 181void ExtensionOptionsGuest::CloseContents(content::WebContents* source) { 182 DispatchEventToEmbedder(new extensions::GuestViewBase::Event( 183 extension_options_internal::OnClose::kEventName, 184 make_scoped_ptr(new base::DictionaryValue()))); 185} 186 187bool ExtensionOptionsGuest::HandleContextMenu( 188 const content::ContextMenuParams& params) { 189 if (!extension_options_guest_delegate_) 190 return false; 191 192 return extension_options_guest_delegate_->HandleContextMenu(params); 193} 194 195bool ExtensionOptionsGuest::ShouldCreateWebContents( 196 content::WebContents* web_contents, 197 int route_id, 198 WindowContainerType window_container_type, 199 const base::string16& frame_name, 200 const GURL& target_url, 201 const std::string& partition_id, 202 content::SessionStorageNamespace* session_storage_namespace) { 203 // This method handles opening links from within the guest. Since this guest 204 // view is used for displaying embedded extension options, we want any 205 // external links to be opened in a new tab, not in a new guest view. 206 // Therefore we just open the URL in a new tab, and since we aren't handling 207 // the new web contents, we return false. 208 // TODO(ericzeng): Open the tab in the background if the click was a 209 // ctrl-click or middle mouse button click 210 if (extension_options_guest_delegate_) { 211 extension_options_guest_delegate_->OpenURLInNewTab( 212 content::OpenURLParams(target_url, 213 content::Referrer(), 214 NEW_FOREGROUND_TAB, 215 ui::PAGE_TRANSITION_LINK, 216 false)); 217 } 218 return false; 219} 220 221bool ExtensionOptionsGuest::OnMessageReceived(const IPC::Message& message) { 222 bool handled = true; 223 IPC_BEGIN_MESSAGE_MAP(ExtensionOptionsGuest, message) 224 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest) 225 IPC_MESSAGE_UNHANDLED(handled = false) 226 IPC_END_MESSAGE_MAP() 227 return handled; 228} 229 230void ExtensionOptionsGuest::OnRequest( 231 const ExtensionHostMsg_Request_Params& params) { 232 extension_function_dispatcher_->Dispatch(params, 233 web_contents()->GetRenderViewHost()); 234} 235 236void ExtensionOptionsGuest::SetUpAutoSize() { 237 // Read the autosize parameters passed in from the embedder. 238 bool auto_size_enabled = false; 239 attach_params()->GetBoolean(extensionoptions::kAttributeAutoSize, 240 &auto_size_enabled); 241 242 int max_height = 0; 243 int max_width = 0; 244 attach_params()->GetInteger(extensionoptions::kAttributeMaxHeight, 245 &max_height); 246 attach_params()->GetInteger(extensionoptions::kAttributeMaxWidth, &max_width); 247 248 int min_height = 0; 249 int min_width = 0; 250 attach_params()->GetInteger(extensionoptions::kAttributeMinHeight, 251 &min_height); 252 attach_params()->GetInteger(extensionoptions::kAttributeMinWidth, &min_width); 253 254 // Call SetAutoSize to apply all the appropriate validation and clipping of 255 // values. 256 SetAutoSize(auto_size_enabled, 257 gfx::Size(min_width, min_height), 258 gfx::Size(max_width, max_height)); 259} 260 261} // namespace extensions 262