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.
5 #include "chrome/renderer/extensions/app_bindings.h"
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_constants.h"
14 #include "content/public/renderer/render_view.h"
15 #include "content/public/renderer/v8_value_converter.h"
16 #include "extensions/common/extension_messages.h"
17 #include "extensions/common/extension_set.h"
18 #include "extensions/common/manifest.h"
19 #include "extensions/renderer/console.h"
20 #include "extensions/renderer/dispatcher.h"
21 #include "extensions/renderer/extension_helper.h"
22 #include "extensions/renderer/script_context.h"
23 #include "third_party/WebKit/public/web/WebDocument.h"
24 #include "third_party/WebKit/public/web/WebLocalFrame.h"
25 #include "v8/include/v8.h"
27 using blink::WebFrame
;
28 using blink::WebLocalFrame
;
29 using content::V8ValueConverter
;
31 namespace extensions
{
35 bool IsCheckoutURL(const std::string
& url_spec
) {
36 std::string checkout_url_prefix
=
37 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
38 switches::kAppsCheckoutURL
);
39 if (checkout_url_prefix
.empty())
40 checkout_url_prefix
= "https://checkout.google.com/";
42 return StartsWithASCII(url_spec
, checkout_url_prefix
, false);
45 bool CheckAccessToAppDetails(WebFrame
* frame
, v8::Isolate
* isolate
) {
46 if (!IsCheckoutURL(frame
->document().url().spec())) {
47 std::string
error("Access denied for URL: ");
48 error
+= frame
->document().url().spec();
49 isolate
->ThrowException(v8::String::NewFromUtf8(isolate
, error
.c_str()));
56 const char kInvalidCallbackIdError
[] = "Invalid callbackId";
60 AppBindings::AppBindings(Dispatcher
* dispatcher
, ScriptContext
* context
)
61 : ObjectBackedNativeHandler(context
),
62 ChromeV8ExtensionHandler(context
),
63 dispatcher_(dispatcher
) {
64 RouteFunction("GetIsInstalled",
65 base::Bind(&AppBindings::GetIsInstalled
, base::Unretained(this)));
66 RouteFunction("GetDetails",
67 base::Bind(&AppBindings::GetDetails
, base::Unretained(this)));
68 RouteFunction("GetDetailsForFrame",
69 base::Bind(&AppBindings::GetDetailsForFrame
, base::Unretained(this)));
70 RouteFunction("GetInstallState",
71 base::Bind(&AppBindings::GetInstallState
, base::Unretained(this)));
72 RouteFunction("GetRunningState",
73 base::Bind(&AppBindings::GetRunningState
, base::Unretained(this)));
76 void AppBindings::GetIsInstalled(
77 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
78 const Extension
* extension
= context()->extension();
80 // TODO(aa): Why only hosted app?
81 bool result
= extension
&& extension
->is_hosted_app() &&
82 dispatcher_
->IsExtensionActive(extension
->id());
83 args
.GetReturnValue().Set(result
);
86 void AppBindings::GetDetails(
87 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
88 CHECK(context()->web_frame());
89 args
.GetReturnValue().Set(GetDetailsForFrameImpl(context()->web_frame()));
92 void AppBindings::GetDetailsForFrame(
93 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
94 CHECK(context()->web_frame());
95 if (!CheckAccessToAppDetails(context()->web_frame(), context()->isolate()))
98 if (args
.Length() < 0) {
99 context()->isolate()->ThrowException(
100 v8::String::NewFromUtf8(context()->isolate(), "Not enough arguments."));
104 if (!args
[0]->IsObject()) {
105 context()->isolate()->ThrowException(v8::String::NewFromUtf8(
106 context()->isolate(), "Argument 0 must be an object."));
110 v8::Local
<v8::Context
> context
=
111 v8::Local
<v8::Object
>::Cast(args
[0])->CreationContext();
112 CHECK(!context
.IsEmpty());
114 WebLocalFrame
* target_frame
= WebLocalFrame::frameForContext(context
);
116 console::Error(args
.GetIsolate()->GetCallingContext(),
117 "Could not find frame for specified object.");
121 args
.GetReturnValue().Set(GetDetailsForFrameImpl(target_frame
));
124 v8::Handle
<v8::Value
> AppBindings::GetDetailsForFrameImpl(
126 v8::Isolate
* isolate
= frame
->mainWorldScriptContext()->GetIsolate();
127 if (frame
->document().securityOrigin().isUnique())
128 return v8::Null(isolate
);
130 const Extension
* extension
=
131 dispatcher_
->extensions()->GetExtensionOrAppByURL(
132 frame
->document().url());
135 return v8::Null(isolate
);
137 scoped_ptr
<base::DictionaryValue
> manifest_copy(
138 extension
->manifest()->value()->DeepCopy());
139 manifest_copy
->SetString("id", extension
->id());
140 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
141 return converter
->ToV8Value(manifest_copy
.get(),
142 frame
->mainWorldScriptContext());
145 void AppBindings::GetInstallState(
146 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
147 // Get the callbackId.
149 if (args
.Length() == 1) {
150 if (!args
[0]->IsInt32()) {
151 context()->isolate()->ThrowException(v8::String::NewFromUtf8(
152 context()->isolate(), kInvalidCallbackIdError
));
155 callback_id
= args
[0]->Int32Value();
158 content::RenderView
* render_view
= context()->GetRenderView();
161 Send(new ExtensionHostMsg_GetAppInstallState(
162 render_view
->GetRoutingID(), context()->web_frame()->document().url(),
163 GetRoutingID(), callback_id
));
166 void AppBindings::GetRunningState(
167 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
168 // To distinguish between ready_to_run and cannot_run states, we need the top
170 const WebFrame
* parent_frame
= context()->web_frame();
171 while (parent_frame
->parent())
172 parent_frame
= parent_frame
->parent();
174 const ExtensionSet
* extensions
= dispatcher_
->extensions();
176 // The app associated with the top level frame.
177 const Extension
* parent_app
= extensions
->GetHostedAppByURL(
178 parent_frame
->document().url());
180 // The app associated with this frame.
181 const Extension
* this_app
= extensions
->GetHostedAppByURL(
182 context()->web_frame()->document().url());
184 if (!this_app
|| !parent_app
) {
185 args
.GetReturnValue().Set(v8::String::NewFromUtf8(
186 context()->isolate(), extension_misc::kAppStateCannotRun
));
190 const char* state
= NULL
;
191 if (dispatcher_
->IsExtensionActive(parent_app
->id())) {
192 if (parent_app
== this_app
)
193 state
= extension_misc::kAppStateRunning
;
195 state
= extension_misc::kAppStateCannotRun
;
196 } else if (parent_app
== this_app
) {
197 state
= extension_misc::kAppStateReadyToRun
;
199 state
= extension_misc::kAppStateCannotRun
;
202 args
.GetReturnValue()
203 .Set(v8::String::NewFromUtf8(context()->isolate(), state
));
206 bool AppBindings::OnMessageReceived(const IPC::Message
& message
) {
207 IPC_BEGIN_MESSAGE_MAP(AppBindings
, message
)
208 IPC_MESSAGE_HANDLER(ExtensionMsg_GetAppInstallStateResponse
,
209 OnAppInstallStateResponse
)
210 IPC_MESSAGE_UNHANDLED(CHECK(false) << "Unhandled IPC message")
211 IPC_END_MESSAGE_MAP()
215 void AppBindings::OnAppInstallStateResponse(
216 const std::string
& state
, int callback_id
) {
217 v8::Isolate
* isolate
= context()->isolate();
218 v8::HandleScope
handle_scope(isolate
);
219 v8::Context::Scope
context_scope(context()->v8_context());
220 v8::Handle
<v8::Value
> argv
[] = {
221 v8::String::NewFromUtf8(isolate
, state
.c_str()),
222 v8::Integer::New(isolate
, callback_id
)
224 context()->module_system()->CallModuleMethod(
225 "app", "onInstallStateResponse", arraysize(argv
), argv
);
228 } // namespace extensions