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/string16.h"
9 #include "base/string_util.h"
10 #include "base/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/v8_value_converter.h"
21 #include "content/public/renderer/render_view.h"
22 #include "grit/renderer_resources.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
25 #include "v8/include/v8.h"
27 using WebKit::WebFrame
;
28 using content::V8ValueConverter
;
30 namespace extensions
{
34 bool 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/";
41 return StartsWithASCII(url_spec
, checkout_url_prefix
, false);
44 bool 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()));
55 const char* kInvalidCallbackIdError
= "Invalid callbackId";
59 AppBindings::AppBindings(Dispatcher
* dispatcher
, ChromeV8Context
* context
)
60 : ChromeV8Extension(dispatcher
, context
->v8_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)));
74 v8::Handle
<v8::Value
> AppBindings::GetIsInstalled(
75 const v8::Arguments
& args
) {
76 const Extension
* extension
= context_
->extension();
78 // TODO(aa): Why only hosted app?
79 bool result
= extension
&& extension
->is_hosted_app() &&
80 dispatcher_
->IsExtensionActive(extension
->id());
81 return v8::Boolean::New(result
);
84 v8::Handle
<v8::Value
> AppBindings::GetDetails(
85 const v8::Arguments
& args
) {
86 CHECK(context_
->web_frame());
87 return GetDetailsForFrameImpl(context_
->web_frame());
90 v8::Handle
<v8::Value
> AppBindings::GetDetailsForFrame(
91 const v8::Arguments
& args
) {
92 CHECK(context_
->web_frame());
93 if (!CheckAccessToAppDetails(context_
->web_frame()))
94 return v8::Undefined();
96 if (args
.Length() < 0)
97 return v8::ThrowException(v8::String::New("Not enough arguments."));
99 if (!args
[0]->IsObject()) {
100 return v8::ThrowException(
101 v8::String::New("Argument 0 must be an object."));
104 v8::Local
<v8::Context
> context
=
105 v8::Local
<v8::Object
>::Cast(args
[0])->CreationContext();
106 CHECK(!context
.IsEmpty());
108 WebFrame
* target_frame
= WebFrame::frameForContext(context
);
110 console::Error(v8::Context::GetCalling(),
111 "Could not find frame for specified object.");
112 return v8::Undefined();
115 return GetDetailsForFrameImpl(target_frame
);
118 v8::Handle
<v8::Value
> AppBindings::GetDetailsForFrameImpl(
120 const Extension
* extension
=
121 dispatcher_
->extensions()->GetExtensionOrAppByURL(
122 ExtensionURLInfo(frame
->document().securityOrigin(),
123 frame
->document().url()));
127 scoped_ptr
<DictionaryValue
> manifest_copy(
128 extension
->manifest()->value()->DeepCopy());
129 manifest_copy
->SetString("id", extension
->id());
130 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
131 return converter
->ToV8Value(manifest_copy
.get(),
132 frame
->mainWorldScriptContext());
135 v8::Handle
<v8::Value
> AppBindings::GetInstallState(const v8::Arguments
& args
) {
136 // Get the callbackId.
138 if (args
.Length() == 1) {
139 if (!args
[0]->IsInt32()) {
140 v8::ThrowException(v8::String::New(kInvalidCallbackIdError
));
141 return v8::Undefined();
143 callback_id
= args
[0]->Int32Value();
146 content::RenderView
* render_view
= context_
->GetRenderView();
149 Send(new ExtensionHostMsg_GetAppInstallState(
150 render_view
->GetRoutingID(), context_
->web_frame()->document().url(),
151 GetRoutingID(), callback_id
));
152 return v8::Undefined();
155 v8::Handle
<v8::Value
> AppBindings::GetRunningState(const v8::Arguments
& args
) {
156 // To distinguish between ready_to_run and cannot_run states, we need the top
158 const WebFrame
* parent_frame
= context_
->web_frame();
159 while (parent_frame
->parent())
160 parent_frame
= parent_frame
->parent();
162 const ExtensionSet
* extensions
= dispatcher_
->extensions();
164 // The app associated with the top level frame.
165 const Extension
* parent_app
= extensions
->GetHostedAppByURL(
166 ExtensionURLInfo(parent_frame
->document().url()));
168 // The app associated with this frame.
169 const Extension
* this_app
= extensions
->GetHostedAppByURL(
170 ExtensionURLInfo(context_
->web_frame()->document().url()));
172 if (!this_app
|| !parent_app
)
173 return v8::String::New(extension_misc::kAppStateCannotRun
);
175 const char* state
= NULL
;
176 if (dispatcher_
->IsExtensionActive(parent_app
->id())) {
177 if (parent_app
== this_app
)
178 state
= extension_misc::kAppStateRunning
;
180 state
= extension_misc::kAppStateCannotRun
;
181 } else if (parent_app
== this_app
) {
182 state
= extension_misc::kAppStateReadyToRun
;
184 state
= extension_misc::kAppStateCannotRun
;
187 return v8::String::New(state
);
190 bool AppBindings::OnMessageReceived(const IPC::Message
& message
) {
191 IPC_BEGIN_MESSAGE_MAP(AppBindings
, message
)
192 IPC_MESSAGE_HANDLER(ExtensionMsg_GetAppInstallStateResponse
,
193 OnAppInstallStateResponse
)
194 IPC_MESSAGE_UNHANDLED(CHECK(false) << "Unhandled IPC message")
195 IPC_END_MESSAGE_MAP()
199 void AppBindings::OnAppInstallStateResponse(
200 const std::string
& state
, int callback_id
) {
201 v8::HandleScope handle_scope
;
202 v8::Context::Scope
context_scope(context_
->v8_context());
203 v8::Handle
<v8::Value
> argv
[2];
204 argv
[0] = v8::String::New(state
.c_str());
205 argv
[1] = v8::Integer::New(callback_id
);
206 CHECK(context_
->CallChromeHiddenMethod("app.onInstallStateResponse",
207 arraysize(argv
), argv
, NULL
));
210 } // namespace extensions