1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2013 Intel Corporation. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
6 #include "xwalk/runtime/browser/android/xwalk_content.h"
13 #include "base/android/jni_array.h"
14 #include "base/android/jni_string.h"
15 #include "base/base_paths_android.h"
16 #include "base/json/json_reader.h"
17 #include "base/path_service.h"
18 #include "base/pickle.h"
19 #include "content/public/browser/browser_context.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/devtools_agent_host.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/common/renderer_preferences.h"
26 #include "content/public/common/url_constants.h"
27 #include "components/navigation_interception/intercept_navigation_delegate.h"
28 #include "xwalk/application/common/application_manifest_constants.h"
29 #include "xwalk/application/common/manifest.h"
30 #include "xwalk/runtime/browser/android/net_disk_cache_remover.h"
31 #include "xwalk/runtime/browser/android/state_serializer.h"
32 #include "xwalk/runtime/browser/android/xwalk_contents_client_bridge.h"
33 #include "xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.h"
34 #include "xwalk/runtime/browser/android/xwalk_contents_io_thread_client_impl.h"
35 #include "xwalk/runtime/browser/android/xwalk_web_contents_delegate.h"
36 #include "xwalk/runtime/browser/runtime_context.h"
37 #include "xwalk/runtime/browser/runtime_resource_dispatcher_host_delegate_android.h"
38 #include "xwalk/runtime/browser/xwalk_runner.h"
39 #include "jni/XWalkContent_jni.h"
41 using base::android::AttachCurrentThread;
42 using base::android::ConvertUTF8ToJavaString;
43 using base::android::ScopedJavaLocalRef;
44 using content::BrowserThread;
45 using content::WebContents;
46 using navigation_interception::InterceptNavigationDelegate;
52 const void* kXWalkContentUserDataKey = &kXWalkContentUserDataKey;
54 class XWalkContentUserData : public base::SupportsUserData::Data {
56 explicit XWalkContentUserData(XWalkContent* ptr) : content_(ptr) {}
58 static XWalkContent* GetContents(content::WebContents* web_contents) {
61 XWalkContentUserData* data = reinterpret_cast<XWalkContentUserData*>(
62 web_contents->GetUserData(kXWalkContentUserDataKey));
63 return data ? data->content_ : NULL;
67 XWalkContent* content_;
72 XWalkContent::XWalkContent(JNIEnv* env,
74 jobject web_contents_delegate,
75 jobject contents_client_bridge)
76 : java_ref_(env, obj),
77 web_contents_delegate_(
78 new XWalkWebContentsDelegate(env, web_contents_delegate)),
79 contents_client_bridge_(
80 new XWalkContentsClientBridge(env, contents_client_bridge)) {
83 XWalkContent::~XWalkContent() {
87 XWalkContent* XWalkContent::FromID(int render_process_id,
89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90 const content::RenderViewHost* rvh =
91 content::RenderViewHost::FromID(render_process_id, render_view_id);
92 if (!rvh) return NULL;
93 content::WebContents* web_contents =
94 content::WebContents::FromRenderViewHost(rvh);
95 if (!web_contents) return NULL;
96 return FromWebContents(web_contents);
100 XWalkContent* XWalkContent::FromWebContents(
101 content::WebContents* web_contents) {
102 return XWalkContentUserData::GetContents(web_contents);
105 jint XWalkContent::GetWebContents(
106 JNIEnv* env, jobject obj, jobject io_thread_client,
107 jobject intercept_navigation_delegate) {
108 if (!web_contents_) {
109 web_contents_.reset(CreateWebContents(env, io_thread_client,
110 intercept_navigation_delegate));
112 render_view_host_ext_.reset(
113 new XWalkRenderViewHostExt(web_contents_.get()));
115 return reinterpret_cast<jint>(web_contents_.get());
118 content::WebContents* XWalkContent::CreateWebContents(
119 JNIEnv* env, jobject io_thread_client,
120 jobject intercept_navigation_delegate) {
122 RuntimeContext* runtime_context =
123 XWalkRunner::GetInstance()->runtime_context();
124 CHECK(runtime_context);
126 content::WebContents* web_contents = content::WebContents::Create(
127 content::WebContents::CreateParams(runtime_context));
129 web_contents->SetUserData(kXWalkContentUserDataKey,
130 new XWalkContentUserData(this));
132 XWalkContentsIoThreadClientImpl::RegisterPendingContents(web_contents);
134 // XWalk does not use disambiguation popup for multiple targets.
135 content::RendererPreferences* prefs =
136 web_contents->GetMutableRendererPrefs();
137 prefs->tap_multiple_targets_strategy =
138 content::TAP_MULTIPLE_TARGETS_STRATEGY_NONE;
140 XWalkContentsClientBridgeBase::Associate(web_contents,
141 contents_client_bridge_.get());
142 XWalkContentsIoThreadClientImpl::Associate(web_contents,
143 ScopedJavaLocalRef<jobject>(env, io_thread_client));
144 int child_id = web_contents->GetRenderProcessHost()->GetID();
145 int route_id = web_contents->GetRoutingID();
146 RuntimeResourceDispatcherHostDelegateAndroid::OnIoThreadClientReady(
148 InterceptNavigationDelegate::Associate(web_contents,
149 make_scoped_ptr(new InterceptNavigationDelegate(
150 env, intercept_navigation_delegate)));
151 web_contents->SetDelegate(web_contents_delegate_.get());
155 void XWalkContent::ClearCache(
158 jboolean include_disk_files) {
159 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
160 render_view_host_ext_->ClearCache();
162 if (include_disk_files) {
163 RemoveHttpDiskCache(web_contents_->GetBrowserContext(),
164 web_contents_->GetRoutingID());
168 ScopedJavaLocalRef<jstring> XWalkContent::DevToolsAgentId(JNIEnv* env,
170 content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
171 scoped_refptr<content::DevToolsAgentHost> agent_host(
172 content::DevToolsAgentHost::GetOrCreateFor(rvh));
173 return base::android::ConvertUTF8ToJavaString(env, agent_host->GetId());
176 void XWalkContent::Destroy(JNIEnv* env, jobject obj) {
180 ScopedJavaLocalRef<jstring> XWalkContent::GetVersion(JNIEnv* env,
182 return base::android::ConvertUTF8ToJavaString(env, XWALK_VERSION);
185 void XWalkContent::SetJsOnlineProperty(JNIEnv* env,
187 jboolean network_up) {
188 render_view_host_ext_->SetJsOnlineProperty(network_up);
191 jboolean XWalkContent::SetManifest(JNIEnv* env,
194 jstring manifest_string) {
195 std::string path_str = base::android::ConvertJavaStringToUTF8(env, path);
196 std::string json_input =
197 base::android::ConvertJavaStringToUTF8(env, manifest_string);
199 base::Value* manifest_value = base::JSONReader::Read(json_input);
200 if (!manifest_value) return false;
202 base::DictionaryValue* manifest_dictionary;
203 manifest_value->GetAsDictionary(&manifest_dictionary);
204 if (!manifest_dictionary) return false;
206 scoped_ptr<base::DictionaryValue>
207 manifest_dictionary_ptr(manifest_dictionary);
209 xwalk::application::Manifest manifest(
210 xwalk::application::Manifest::INVALID_TYPE,
211 manifest_dictionary_ptr.Pass());
214 if (manifest.GetString(
215 xwalk::application_manifest_keys::kLaunchLocalPathKey, &url)) {
216 // According to original proposal for "app:launch:local_path", the "http"
217 // and "https" schemes are supported. So |url| should do nothing when it
218 // already has "http" or "https" scheme.
219 std::string lower_url = url;
220 std::transform(lower_url.begin(), lower_url.end(),
221 lower_url.begin(), std::tolower);
222 if (lower_url.find(content::kHttpScheme) == std::string::npos &&
223 lower_url.find(content::kHttpsScheme) == std::string::npos) {
224 url = path_str + url;
228 xwalk::application_manifest_keys::kLaunchWebURLKey, &url);
233 xwalk::application_manifest_keys::kCSPKey, &csp);
234 RuntimeContext* runtime_context =
235 XWalkRunner::GetInstance()->runtime_context();
236 CHECK(runtime_context);
237 runtime_context->SetCSPString(csp);
239 ScopedJavaLocalRef<jstring> url_buffer =
240 base::android::ConvertUTF8ToJavaString(env, url);
242 // Check whether need to display launch screen. (Read from manifest.json)
243 if (manifest.HasPath(
244 xwalk::application_manifest_keys::kLaunchScreen)) {
245 std::string ready_when;
246 // Get the value of 'ready_when' from manifest.json and callback
249 xwalk::application_manifest_keys::kLaunchScreenReadyWhen, &ready_when);
250 ScopedJavaLocalRef<jstring> ready_when_buffer =
251 base::android::ConvertUTF8ToJavaString(env, ready_when);
252 Java_XWalkContent_onGetUrlAndLaunchScreenFromManifest(
253 env, obj, url_buffer.obj(), ready_when_buffer.obj());
255 // No need to display launch screen, load the url directly.
256 Java_XWalkContent_onGetUrlFromManifest(env, obj, url_buffer.obj());
261 jint XWalkContent::GetRoutingID(JNIEnv* env, jobject obj) {
262 DCHECK(web_contents_.get());
263 return web_contents_->GetRoutingID();
266 base::android::ScopedJavaLocalRef<jbyteArray> XWalkContent::GetState(
269 if (!web_contents_->GetController().GetEntryCount())
270 return ScopedJavaLocalRef<jbyteArray>();
273 if (!WriteToPickle(*web_contents_, &pickle)) {
274 return ScopedJavaLocalRef<jbyteArray>();
276 return base::android::ToJavaByteArray(
278 reinterpret_cast<const uint8*>(pickle.data()),
283 jboolean XWalkContent::SetState(JNIEnv* env, jobject obj, jbyteArray state) {
284 std::vector<uint8> state_vector;
285 base::android::JavaByteArrayToByteVector(env, state, &state_vector);
287 Pickle pickle(reinterpret_cast<const char*>(state_vector.begin()),
288 state_vector.size());
289 PickleIterator iterator(pickle);
291 return RestoreFromPickle(&iterator, web_contents_.get());
294 static jint Init(JNIEnv* env, jobject obj, jobject web_contents_delegate,
295 jobject contents_client_bridge) {
296 XWalkContent* xwalk_core_content =
297 new XWalkContent(env, obj, web_contents_delegate, contents_client_bridge);
298 return reinterpret_cast<jint>(xwalk_core_content);
301 bool RegisterXWalkContent(JNIEnv* env) {
302 return RegisterNativesImpl(env) >= 0;
307 void ShowGeolocationPromptHelperTask(
308 const JavaObjectWeakGlobalRef& java_ref,
309 const GURL& origin) {
310 JNIEnv* env = AttachCurrentThread();
311 ScopedJavaLocalRef<jobject> j_ref = java_ref.get(env);
313 ScopedJavaLocalRef<jstring> j_origin(
314 ConvertUTF8ToJavaString(env, origin.spec()));
315 Java_XWalkContent_onGeolocationPermissionsShowPrompt(env,
321 void ShowGeolocationPromptHelper(const JavaObjectWeakGlobalRef& java_ref,
322 const GURL& origin) {
323 JNIEnv* env = AttachCurrentThread();
324 if (java_ref.get(env).obj()) {
325 content::BrowserThread::PostTask(
326 content::BrowserThread::UI,
328 base::Bind(&ShowGeolocationPromptHelperTask,
333 } // anonymous namespace
335 void XWalkContent::ShowGeolocationPrompt(
336 const GURL& requesting_frame,
337 const base::Callback<void(bool)>& callback) {
338 GURL origin = requesting_frame.GetOrigin();
339 bool show_prompt = pending_geolocation_prompts_.empty();
340 pending_geolocation_prompts_.push_back(OriginCallback(origin, callback));
342 ShowGeolocationPromptHelper(java_ref_, origin);
347 void XWalkContent::InvokeGeolocationCallback(JNIEnv* env,
351 GURL callback_origin(base::android::ConvertJavaStringToUTF16(env, origin));
352 if (callback_origin.GetOrigin() ==
353 pending_geolocation_prompts_.front().first) {
354 pending_geolocation_prompts_.front().second.Run(value);
355 pending_geolocation_prompts_.pop_front();
356 if (!pending_geolocation_prompts_.empty()) {
357 ShowGeolocationPromptHelper(java_ref_,
358 pending_geolocation_prompts_.front().first);
363 void XWalkContent::HideGeolocationPrompt(const GURL& origin) {
364 bool removed_current_outstanding_callback = false;
365 std::list<OriginCallback>::iterator it = pending_geolocation_prompts_.begin();
366 while (it != pending_geolocation_prompts_.end()) {
367 if ((*it).first == origin.GetOrigin()) {
368 if (it == pending_geolocation_prompts_.begin()) {
369 removed_current_outstanding_callback = true;
371 it = pending_geolocation_prompts_.erase(it);
377 if (removed_current_outstanding_callback) {
378 JNIEnv* env = AttachCurrentThread();
379 ScopedJavaLocalRef<jobject> j_ref = java_ref_.get(env);
381 Java_XWalkContent_onGeolocationPermissionsHidePrompt(env, j_ref.obj());
383 if (!pending_geolocation_prompts_.empty()) {
384 ShowGeolocationPromptHelper(java_ref_,
385 pending_geolocation_prompts_.front().first);