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;
47 using xwalk::application_manifest_keys::kDisplay;
53 const void* kXWalkContentUserDataKey = &kXWalkContentUserDataKey;
55 class XWalkContentUserData : public base::SupportsUserData::Data {
57 explicit XWalkContentUserData(XWalkContent* ptr) : content_(ptr) {}
59 static XWalkContent* GetContents(content::WebContents* web_contents) {
62 XWalkContentUserData* data = reinterpret_cast<XWalkContentUserData*>(
63 web_contents->GetUserData(kXWalkContentUserDataKey));
64 return data ? data->content_ : NULL;
68 XWalkContent* content_;
73 XWalkContent::XWalkContent(JNIEnv* env,
75 jobject web_contents_delegate,
76 jobject contents_client_bridge)
77 : java_ref_(env, obj),
78 web_contents_delegate_(
79 new XWalkWebContentsDelegate(env, web_contents_delegate)),
80 contents_client_bridge_(
81 new XWalkContentsClientBridge(env, contents_client_bridge)) {
84 XWalkContent::~XWalkContent() {
88 XWalkContent* XWalkContent::FromID(int render_process_id,
90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
91 const content::RenderViewHost* rvh =
92 content::RenderViewHost::FromID(render_process_id, render_view_id);
93 if (!rvh) return NULL;
94 content::WebContents* web_contents =
95 content::WebContents::FromRenderViewHost(rvh);
96 if (!web_contents) return NULL;
97 return FromWebContents(web_contents);
101 XWalkContent* XWalkContent::FromWebContents(
102 content::WebContents* web_contents) {
103 return XWalkContentUserData::GetContents(web_contents);
106 jint XWalkContent::GetWebContents(
107 JNIEnv* env, jobject obj, jobject io_thread_client,
108 jobject intercept_navigation_delegate) {
109 if (!web_contents_) {
110 web_contents_.reset(CreateWebContents(env, io_thread_client,
111 intercept_navigation_delegate));
113 render_view_host_ext_.reset(
114 new XWalkRenderViewHostExt(web_contents_.get()));
116 return reinterpret_cast<jint>(web_contents_.get());
119 content::WebContents* XWalkContent::CreateWebContents(
120 JNIEnv* env, jobject io_thread_client,
121 jobject intercept_navigation_delegate) {
123 RuntimeContext* runtime_context =
124 XWalkRunner::GetInstance()->runtime_context();
125 CHECK(runtime_context);
127 content::WebContents* web_contents = content::WebContents::Create(
128 content::WebContents::CreateParams(runtime_context));
130 web_contents->SetUserData(kXWalkContentUserDataKey,
131 new XWalkContentUserData(this));
133 XWalkContentsIoThreadClientImpl::RegisterPendingContents(web_contents);
135 // XWalk does not use disambiguation popup for multiple targets.
136 content::RendererPreferences* prefs =
137 web_contents->GetMutableRendererPrefs();
138 prefs->tap_multiple_targets_strategy =
139 content::TAP_MULTIPLE_TARGETS_STRATEGY_NONE;
141 XWalkContentsClientBridgeBase::Associate(web_contents,
142 contents_client_bridge_.get());
143 XWalkContentsIoThreadClientImpl::Associate(web_contents,
144 ScopedJavaLocalRef<jobject>(env, io_thread_client));
145 int child_id = web_contents->GetRenderProcessHost()->GetID();
146 int route_id = web_contents->GetRoutingID();
147 RuntimeResourceDispatcherHostDelegateAndroid::OnIoThreadClientReady(
149 InterceptNavigationDelegate::Associate(web_contents,
150 make_scoped_ptr(new InterceptNavigationDelegate(
151 env, intercept_navigation_delegate)));
152 web_contents->SetDelegate(web_contents_delegate_.get());
156 void XWalkContent::ClearCache(
159 jboolean include_disk_files) {
160 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
161 render_view_host_ext_->ClearCache();
163 if (include_disk_files) {
164 RemoveHttpDiskCache(web_contents_->GetBrowserContext(),
165 web_contents_->GetRoutingID());
169 ScopedJavaLocalRef<jstring> XWalkContent::DevToolsAgentId(JNIEnv* env,
171 content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
172 scoped_refptr<content::DevToolsAgentHost> agent_host(
173 content::DevToolsAgentHost::GetOrCreateFor(rvh));
174 return base::android::ConvertUTF8ToJavaString(env, agent_host->GetId());
177 void XWalkContent::Destroy(JNIEnv* env, jobject obj) {
181 ScopedJavaLocalRef<jstring> XWalkContent::GetVersion(JNIEnv* env,
183 return base::android::ConvertUTF8ToJavaString(env, XWALK_VERSION);
186 void XWalkContent::SetJsOnlineProperty(JNIEnv* env,
188 jboolean network_up) {
189 render_view_host_ext_->SetJsOnlineProperty(network_up);
192 jboolean XWalkContent::SetManifest(JNIEnv* env,
195 jstring manifest_string) {
196 std::string path_str = base::android::ConvertJavaStringToUTF8(env, path);
197 std::string json_input =
198 base::android::ConvertJavaStringToUTF8(env, manifest_string);
200 base::Value* manifest_value = base::JSONReader::Read(json_input);
201 if (!manifest_value) return false;
203 base::DictionaryValue* manifest_dictionary;
204 manifest_value->GetAsDictionary(&manifest_dictionary);
205 if (!manifest_dictionary) return false;
207 scoped_ptr<base::DictionaryValue>
208 manifest_dictionary_ptr(manifest_dictionary);
210 xwalk::application::Manifest manifest(
211 xwalk::application::Manifest::INVALID_TYPE,
212 manifest_dictionary_ptr.Pass());
215 if (manifest.GetString(
216 xwalk::application_manifest_keys::kLaunchLocalPathKey, &url)) {
217 // According to original proposal for "app:launch:local_path", the "http"
218 // and "https" schemes are supported. So |url| should do nothing when it
219 // already has "http" or "https" scheme.
220 std::string scheme = GURL(url).scheme();
221 if (scheme != content::kHttpScheme && scheme != content::kHttpsScheme)
222 url = path_str + url;
225 xwalk::application_manifest_keys::kLaunchWebURLKey, &url);
230 xwalk::application_manifest_keys::kCSPKey, &csp);
231 RuntimeContext* runtime_context =
232 XWalkRunner::GetInstance()->runtime_context();
233 CHECK(runtime_context);
234 runtime_context->SetCSPString(csp);
236 ScopedJavaLocalRef<jstring> url_buffer =
237 base::android::ConvertUTF8ToJavaString(env, url);
239 if (manifest.HasPath(kDisplay)) {
240 std::string display_string;
241 manifest.GetString(kDisplay, &display_string);
242 // TODO(David): update the handling process of the display strings
243 // including fullscreen etc.
244 bool display_as_fullscreen = (
245 display_string.find("fullscreen") != std::string::npos);
246 Java_XWalkContent_onGetFullscreenFlagFromManifest(
247 env, obj, display_as_fullscreen ? JNI_TRUE : JNI_FALSE);
250 // Check whether need to display launch screen. (Read from manifest.json)
251 if (manifest.HasPath(
252 xwalk::application_manifest_keys::kLaunchScreen)) {
253 std::string ready_when;
254 // Get the value of 'ready_when' from manifest.json and callback
257 xwalk::application_manifest_keys::kLaunchScreenReadyWhen, &ready_when);
258 ScopedJavaLocalRef<jstring> ready_when_buffer =
259 base::android::ConvertUTF8ToJavaString(env, ready_when);
260 Java_XWalkContent_onGetUrlAndLaunchScreenFromManifest(
261 env, obj, url_buffer.obj(), ready_when_buffer.obj());
263 // No need to display launch screen, load the url directly.
264 Java_XWalkContent_onGetUrlFromManifest(env, obj, url_buffer.obj());
269 jint XWalkContent::GetRoutingID(JNIEnv* env, jobject obj) {
270 DCHECK(web_contents_.get());
271 return web_contents_->GetRoutingID();
274 base::android::ScopedJavaLocalRef<jbyteArray> XWalkContent::GetState(
277 if (!web_contents_->GetController().GetEntryCount())
278 return ScopedJavaLocalRef<jbyteArray>();
281 if (!WriteToPickle(*web_contents_, &pickle)) {
282 return ScopedJavaLocalRef<jbyteArray>();
284 return base::android::ToJavaByteArray(
286 reinterpret_cast<const uint8*>(pickle.data()),
291 jboolean XWalkContent::SetState(JNIEnv* env, jobject obj, jbyteArray state) {
292 std::vector<uint8> state_vector;
293 base::android::JavaByteArrayToByteVector(env, state, &state_vector);
295 Pickle pickle(reinterpret_cast<const char*>(state_vector.begin()),
296 state_vector.size());
297 PickleIterator iterator(pickle);
299 return RestoreFromPickle(&iterator, web_contents_.get());
302 static jint Init(JNIEnv* env, jobject obj, jobject web_contents_delegate,
303 jobject contents_client_bridge) {
304 XWalkContent* xwalk_core_content =
305 new XWalkContent(env, obj, web_contents_delegate, contents_client_bridge);
306 return reinterpret_cast<jint>(xwalk_core_content);
309 bool RegisterXWalkContent(JNIEnv* env) {
310 return RegisterNativesImpl(env) >= 0;
315 void ShowGeolocationPromptHelperTask(
316 const JavaObjectWeakGlobalRef& java_ref,
317 const GURL& origin) {
318 JNIEnv* env = AttachCurrentThread();
319 ScopedJavaLocalRef<jobject> j_ref = java_ref.get(env);
321 ScopedJavaLocalRef<jstring> j_origin(
322 ConvertUTF8ToJavaString(env, origin.spec()));
323 Java_XWalkContent_onGeolocationPermissionsShowPrompt(env,
329 void ShowGeolocationPromptHelper(const JavaObjectWeakGlobalRef& java_ref,
330 const GURL& origin) {
331 JNIEnv* env = AttachCurrentThread();
332 if (java_ref.get(env).obj()) {
333 content::BrowserThread::PostTask(
334 content::BrowserThread::UI,
336 base::Bind(&ShowGeolocationPromptHelperTask,
341 } // anonymous namespace
343 void XWalkContent::ShowGeolocationPrompt(
344 const GURL& requesting_frame,
345 const base::Callback<void(bool)>& callback) {
346 GURL origin = requesting_frame.GetOrigin();
347 bool show_prompt = pending_geolocation_prompts_.empty();
348 pending_geolocation_prompts_.push_back(OriginCallback(origin, callback));
350 ShowGeolocationPromptHelper(java_ref_, origin);
355 void XWalkContent::InvokeGeolocationCallback(JNIEnv* env,
359 GURL callback_origin(base::android::ConvertJavaStringToUTF16(env, origin));
360 if (callback_origin.GetOrigin() ==
361 pending_geolocation_prompts_.front().first) {
362 pending_geolocation_prompts_.front().second.Run(value);
363 pending_geolocation_prompts_.pop_front();
364 if (!pending_geolocation_prompts_.empty()) {
365 ShowGeolocationPromptHelper(java_ref_,
366 pending_geolocation_prompts_.front().first);
371 void XWalkContent::HideGeolocationPrompt(const GURL& origin) {
372 bool removed_current_outstanding_callback = false;
373 std::list<OriginCallback>::iterator it = pending_geolocation_prompts_.begin();
374 while (it != pending_geolocation_prompts_.end()) {
375 if ((*it).first == origin.GetOrigin()) {
376 if (it == pending_geolocation_prompts_.begin()) {
377 removed_current_outstanding_callback = true;
379 it = pending_geolocation_prompts_.erase(it);
385 if (removed_current_outstanding_callback) {
386 JNIEnv* env = AttachCurrentThread();
387 ScopedJavaLocalRef<jobject> j_ref = java_ref_.get(env);
389 Java_XWalkContent_onGeolocationPermissionsHidePrompt(env, j_ref.obj());
391 if (!pending_geolocation_prompts_.empty()) {
392 ShowGeolocationPromptHelper(java_ref_,
393 pending_geolocation_prompts_.front().first);