app_bindings.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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#include "chrome/renderer/extensions/app_bindings.h" 6 7#include "base/command_line.h" 8#include "base/strings/string16.h" 9#include "base/strings/string_util.h" 10#include "base/strings/utf_string_conversions.h" 11#include "base/values.h" 12#include "chrome/common/chrome_switches.h" 13#include "chrome/common/extensions/extension_messages.h" 14#include "chrome/common/extensions/extension_set.h" 15#include "chrome/common/extensions/manifest.h" 16#include "chrome/renderer/extensions/chrome_v8_context.h" 17#include "chrome/renderer/extensions/console.h" 18#include "chrome/renderer/extensions/dispatcher.h" 19#include "chrome/renderer/extensions/extension_helper.h" 20#include "content/public/renderer/render_view.h" 21#include "content/public/renderer/v8_value_converter.h" 22#include "grit/renderer_resources.h" 23#include "third_party/WebKit/public/web/WebDocument.h" 24#include "third_party/WebKit/public/web/WebFrame.h" 25#include "v8/include/v8.h" 26 27using WebKit::WebFrame; 28using content::V8ValueConverter; 29 30namespace extensions { 31 32namespace { 33 34bool IsCheckoutURL(const std::string& url_spec) { 35 std::string checkout_url_prefix = 36 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 37 switches::kAppsCheckoutURL); 38 if (checkout_url_prefix.empty()) 39 checkout_url_prefix = "https://checkout.google.com/"; 40 41 return StartsWithASCII(url_spec, checkout_url_prefix, false); 42} 43 44bool CheckAccessToAppDetails(WebFrame* frame) { 45 if (!IsCheckoutURL(frame->document().url().spec())) { 46 std::string error("Access denied for URL: "); 47 error += frame->document().url().spec(); 48 v8::ThrowException(v8::String::New(error.c_str())); 49 return false; 50 } 51 52 return true; 53} 54 55const char* kInvalidCallbackIdError = "Invalid callbackId"; 56 57} // namespace 58 59AppBindings::AppBindings(Dispatcher* dispatcher, ChromeV8Context* context) 60 : ChromeV8Extension(dispatcher, context), 61 ChromeV8ExtensionHandler(context) { 62 RouteFunction("GetIsInstalled", 63 base::Bind(&AppBindings::GetIsInstalled, base::Unretained(this))); 64 RouteFunction("GetDetails", 65 base::Bind(&AppBindings::GetDetails, base::Unretained(this))); 66 RouteFunction("GetDetailsForFrame", 67 base::Bind(&AppBindings::GetDetailsForFrame, base::Unretained(this))); 68 RouteFunction("GetInstallState", 69 base::Bind(&AppBindings::GetInstallState, base::Unretained(this))); 70 RouteFunction("GetRunningState", 71 base::Bind(&AppBindings::GetRunningState, base::Unretained(this))); 72} 73 74void AppBindings::GetIsInstalled( 75 const v8::FunctionCallbackInfo<v8::Value>& args) { 76 const Extension* extension = context()->extension(); 77 78 // TODO(aa): Why only hosted app? 79 bool result = extension && extension->is_hosted_app() && 80 dispatcher_->IsExtensionActive(extension->id()); 81 args.GetReturnValue().Set(result); 82} 83 84void AppBindings::GetDetails( 85 const v8::FunctionCallbackInfo<v8::Value>& args) { 86 CHECK(context()->web_frame()); 87 args.GetReturnValue().Set(GetDetailsForFrameImpl(context()->web_frame())); 88} 89 90void AppBindings::GetDetailsForFrame( 91 const v8::FunctionCallbackInfo<v8::Value>& args) { 92 CHECK(context()->web_frame()); 93 if (!CheckAccessToAppDetails(context()->web_frame())) 94 return; 95 96 if (args.Length() < 0) { 97 v8::ThrowException(v8::String::New("Not enough arguments.")); 98 return; 99 } 100 101 if (!args[0]->IsObject()) { 102 v8::ThrowException(v8::String::New("Argument 0 must be an object.")); 103 return; 104 } 105 106 v8::Local<v8::Context> context = 107 v8::Local<v8::Object>::Cast(args[0])->CreationContext(); 108 CHECK(!context.IsEmpty()); 109 110 WebFrame* target_frame = WebFrame::frameForContext(context); 111 if (!target_frame) { 112 console::Error(v8::Context::GetCalling(), 113 "Could not find frame for specified object."); 114 return; 115 } 116 117 args.GetReturnValue().Set(GetDetailsForFrameImpl(target_frame)); 118} 119 120v8::Handle<v8::Value> AppBindings::GetDetailsForFrameImpl( 121 WebFrame* frame) { 122 if (frame->document().securityOrigin().isUnique()) 123 return v8::Null(); 124 125 const Extension* extension = 126 dispatcher_->extensions()->GetExtensionOrAppByURL( 127 frame->document().url()); 128 129 if (!extension) 130 return v8::Null(); 131 132 scoped_ptr<base::DictionaryValue> manifest_copy( 133 extension->manifest()->value()->DeepCopy()); 134 manifest_copy->SetString("id", extension->id()); 135 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); 136 return converter->ToV8Value(manifest_copy.get(), 137 frame->mainWorldScriptContext()); 138} 139 140void AppBindings::GetInstallState( 141 const v8::FunctionCallbackInfo<v8::Value>& args) { 142 // Get the callbackId. 143 int callback_id = 0; 144 if (args.Length() == 1) { 145 if (!args[0]->IsInt32()) { 146 v8::ThrowException(v8::String::New(kInvalidCallbackIdError)); 147 return; 148 } 149 callback_id = args[0]->Int32Value(); 150 } 151 152 content::RenderView* render_view = context()->GetRenderView(); 153 CHECK(render_view); 154 155 Send(new ExtensionHostMsg_GetAppInstallState( 156 render_view->GetRoutingID(), context()->web_frame()->document().url(), 157 GetRoutingID(), callback_id)); 158} 159 160void AppBindings::GetRunningState( 161 const v8::FunctionCallbackInfo<v8::Value>& args) { 162 // To distinguish between ready_to_run and cannot_run states, we need the top 163 // level frame. 164 const WebFrame* parent_frame = context()->web_frame(); 165 while (parent_frame->parent()) 166 parent_frame = parent_frame->parent(); 167 168 const ExtensionSet* extensions = dispatcher_->extensions(); 169 170 // The app associated with the top level frame. 171 const Extension* parent_app = extensions->GetHostedAppByURL( 172 parent_frame->document().url()); 173 174 // The app associated with this frame. 175 const Extension* this_app = extensions->GetHostedAppByURL( 176 context()->web_frame()->document().url()); 177 178 if (!this_app || !parent_app) { 179 args.GetReturnValue().Set( 180 v8::String::New(extension_misc::kAppStateCannotRun)); 181 return; 182 } 183 184 const char* state = NULL; 185 if (dispatcher_->IsExtensionActive(parent_app->id())) { 186 if (parent_app == this_app) 187 state = extension_misc::kAppStateRunning; 188 else 189 state = extension_misc::kAppStateCannotRun; 190 } else if (parent_app == this_app) { 191 state = extension_misc::kAppStateReadyToRun; 192 } else { 193 state = extension_misc::kAppStateCannotRun; 194 } 195 196 args.GetReturnValue().Set(v8::String::New(state)); 197} 198 199bool AppBindings::OnMessageReceived(const IPC::Message& message) { 200 IPC_BEGIN_MESSAGE_MAP(AppBindings, message) 201 IPC_MESSAGE_HANDLER(ExtensionMsg_GetAppInstallStateResponse, 202 OnAppInstallStateResponse) 203 IPC_MESSAGE_UNHANDLED(CHECK(false) << "Unhandled IPC message") 204 IPC_END_MESSAGE_MAP() 205 return true; 206} 207 208void AppBindings::OnAppInstallStateResponse( 209 const std::string& state, int callback_id) { 210 v8::HandleScope handle_scope; 211 v8::Context::Scope context_scope(context()->v8_context()); 212 v8::Handle<v8::Value> argv[] = { 213 v8::String::New(state.c_str()), 214 v8::Integer::New(callback_id) 215 }; 216 context()->module_system()->CallModuleMethod( 217 "app", "onInstallStateResponse", arraysize(argv), argv); 218} 219 220} // namespace extensions 221