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/child/v8_value_converter.h"
15 #include "content/public/renderer/render_view.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 dispatcher_(dispatcher
) {
63 RouteFunction("GetIsInstalled",
64 base::Bind(&AppBindings::GetIsInstalled
, base::Unretained(this)));
65 RouteFunction("GetDetails",
66 base::Bind(&AppBindings::GetDetails
, base::Unretained(this)));
67 RouteFunction("GetDetailsForFrame",
68 base::Bind(&AppBindings::GetDetailsForFrame
, base::Unretained(this)));
69 RouteFunction("GetInstallState",
70 base::Bind(&AppBindings::GetInstallState
, base::Unretained(this)));
71 RouteFunction("GetRunningState",
72 base::Bind(&AppBindings::GetRunningState
, base::Unretained(this)));
75 void AppBindings::GetIsInstalled(
76 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
77 const Extension
* extension
= context()->extension();
79 // TODO(aa): Why only hosted app?
80 bool result
= extension
&& extension
->is_hosted_app() &&
81 dispatcher_
->IsExtensionActive(extension
->id());
82 args
.GetReturnValue().Set(result
);
85 void AppBindings::GetDetails(
86 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
87 CHECK(context()->web_frame());
88 args
.GetReturnValue().Set(GetDetailsForFrameImpl(context()->web_frame()));
91 void AppBindings::GetDetailsForFrame(
92 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
93 CHECK(context()->web_frame());
94 if (!CheckAccessToAppDetails(context()->web_frame(), context()->isolate()))
97 if (args
.Length() < 0) {
98 context()->isolate()->ThrowException(
99 v8::String::NewFromUtf8(context()->isolate(), "Not enough arguments."));
103 if (!args
[0]->IsObject()) {
104 context()->isolate()->ThrowException(v8::String::NewFromUtf8(
105 context()->isolate(), "Argument 0 must be an object."));
109 v8::Local
<v8::Context
> context
=
110 v8::Local
<v8::Object
>::Cast(args
[0])->CreationContext();
111 CHECK(!context
.IsEmpty());
113 WebLocalFrame
* target_frame
= WebLocalFrame::frameForContext(context
);
115 console::Error(args
.GetIsolate()->GetCallingContext(),
116 "Could not find frame for specified object.");
120 args
.GetReturnValue().Set(GetDetailsForFrameImpl(target_frame
));
123 v8::Handle
<v8::Value
> AppBindings::GetDetailsForFrameImpl(
125 v8::Isolate
* isolate
= frame
->mainWorldScriptContext()->GetIsolate();
126 if (frame
->document().securityOrigin().isUnique())
127 return v8::Null(isolate
);
129 const Extension
* extension
=
130 dispatcher_
->extensions()->GetExtensionOrAppByURL(
131 frame
->document().url());
134 return v8::Null(isolate
);
136 scoped_ptr
<base::DictionaryValue
> manifest_copy(
137 extension
->manifest()->value()->DeepCopy());
138 manifest_copy
->SetString("id", extension
->id());
139 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
140 return converter
->ToV8Value(manifest_copy
.get(),
141 frame
->mainWorldScriptContext());
144 void AppBindings::GetInstallState(
145 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
146 // Get the callbackId.
148 if (args
.Length() == 1) {
149 if (!args
[0]->IsInt32()) {
150 context()->isolate()->ThrowException(v8::String::NewFromUtf8(
151 context()->isolate(), kInvalidCallbackIdError
));
154 callback_id
= args
[0]->Int32Value();
157 content::RenderView
* render_view
= context()->GetRenderView();
160 Send(new ExtensionHostMsg_GetAppInstallState(
161 render_view
->GetRoutingID(), context()->web_frame()->document().url(),
162 GetRoutingID(), callback_id
));
165 void AppBindings::GetRunningState(
166 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
167 // To distinguish between ready_to_run and cannot_run states, we need the top
169 const WebFrame
* parent_frame
= context()->web_frame();
170 while (parent_frame
->parent())
171 parent_frame
= parent_frame
->parent();
173 const ExtensionSet
* extensions
= dispatcher_
->extensions();
175 // The app associated with the top level frame.
176 const Extension
* parent_app
= extensions
->GetHostedAppByURL(
177 parent_frame
->document().url());
179 // The app associated with this frame.
180 const Extension
* this_app
= extensions
->GetHostedAppByURL(
181 context()->web_frame()->document().url());
183 if (!this_app
|| !parent_app
) {
184 args
.GetReturnValue().Set(v8::String::NewFromUtf8(
185 context()->isolate(), extension_misc::kAppStateCannotRun
));
189 const char* state
= NULL
;
190 if (dispatcher_
->IsExtensionActive(parent_app
->id())) {
191 if (parent_app
== this_app
)
192 state
= extension_misc::kAppStateRunning
;
194 state
= extension_misc::kAppStateCannotRun
;
195 } else if (parent_app
== this_app
) {
196 state
= extension_misc::kAppStateReadyToRun
;
198 state
= extension_misc::kAppStateCannotRun
;
201 args
.GetReturnValue()
202 .Set(v8::String::NewFromUtf8(context()->isolate(), state
));
205 bool AppBindings::OnMessageReceived(const IPC::Message
& message
) {
206 IPC_BEGIN_MESSAGE_MAP(AppBindings
, message
)
207 IPC_MESSAGE_HANDLER(ExtensionMsg_GetAppInstallStateResponse
,
208 OnAppInstallStateResponse
)
209 IPC_MESSAGE_UNHANDLED(CHECK(false) << "Unhandled IPC message")
210 IPC_END_MESSAGE_MAP()
214 void AppBindings::OnAppInstallStateResponse(
215 const std::string
& state
, int callback_id
) {
216 v8::Isolate
* isolate
= context()->isolate();
217 v8::HandleScope
handle_scope(isolate
);
218 v8::Context::Scope
context_scope(context()->v8_context());
219 v8::Handle
<v8::Value
> argv
[] = {
220 v8::String::NewFromUtf8(isolate
, state
.c_str()),
221 v8::Integer::New(isolate
, callback_id
)
223 context()->module_system()->CallModuleMethod(
224 "app", "onInstallStateResponse", arraysize(argv
), argv
);
227 } // namespace extensions