Suppression for crbug/241044.
[chromium-blink-merge.git] / chrome / renderer / extensions / app_bindings.cc
blobb036bb230662d19054190de98ed067c8dfa9bb02
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 {
32 namespace {
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()));
49 return false;
52 return true;
55 const char* kInvalidCallbackIdError = "Invalid callbackId";
57 } // namespace
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);
109 if (!target_frame) {
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(
119 WebFrame* frame) {
120 const Extension* extension =
121 dispatcher_->extensions()->GetExtensionOrAppByURL(
122 ExtensionURLInfo(frame->document().securityOrigin(),
123 frame->document().url()));
124 if (!extension)
125 return v8::Null();
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.
137 int callback_id = 0;
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();
147 CHECK(render_view);
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
157 // level frame.
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;
179 else
180 state = extension_misc::kAppStateCannotRun;
181 } else if (parent_app == this_app) {
182 state = extension_misc::kAppStateReadyToRun;
183 } else {
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()
196 return true;
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