Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / browser / android / xwalk_content.cc
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.
5
6 #include "xwalk/runtime/browser/android/xwalk_content.h"
7
8 #include <algorithm>
9 #include <cctype>
10 #include <string>
11 #include <vector>
12
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"
40
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
48 namespace xwalk {
49
50 namespace {
51
52 const void* kXWalkContentUserDataKey = &kXWalkContentUserDataKey;
53
54 class XWalkContentUserData : public base::SupportsUserData::Data {
55  public:
56   explicit XWalkContentUserData(XWalkContent* ptr) : content_(ptr) {}
57
58   static XWalkContent* GetContents(content::WebContents* web_contents) {
59     if (!web_contents)
60       return NULL;
61     XWalkContentUserData* data = reinterpret_cast<XWalkContentUserData*>(
62         web_contents->GetUserData(kXWalkContentUserDataKey));
63     return data ? data->content_ : NULL;
64   }
65
66  private:
67   XWalkContent* content_;
68 };
69
70 }  // namespace
71
72 XWalkContent::XWalkContent(JNIEnv* env,
73                            jobject obj,
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)) {
81 }
82
83 XWalkContent::~XWalkContent() {
84 }
85
86 // static
87 XWalkContent* XWalkContent::FromID(int render_process_id,
88                                    int render_view_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);
97 }
98
99 // static
100 XWalkContent* XWalkContent::FromWebContents(
101     content::WebContents* web_contents) {
102   return XWalkContentUserData::GetContents(web_contents);
103 }
104
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));
111
112     render_view_host_ext_.reset(
113         new XWalkRenderViewHostExt(web_contents_.get()));
114   }
115   return reinterpret_cast<jint>(web_contents_.get());
116 }
117
118 content::WebContents* XWalkContent::CreateWebContents(
119     JNIEnv* env, jobject io_thread_client,
120     jobject intercept_navigation_delegate) {
121
122   RuntimeContext* runtime_context =
123       XWalkRunner::GetInstance()->runtime_context();
124   CHECK(runtime_context);
125
126   content::WebContents* web_contents = content::WebContents::Create(
127       content::WebContents::CreateParams(runtime_context));
128
129   web_contents->SetUserData(kXWalkContentUserDataKey,
130                             new XWalkContentUserData(this));
131
132   XWalkContentsIoThreadClientImpl::RegisterPendingContents(web_contents);
133
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;
139
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(
147       child_id, route_id);
148   InterceptNavigationDelegate::Associate(web_contents,
149       make_scoped_ptr(new InterceptNavigationDelegate(
150           env, intercept_navigation_delegate)));
151   web_contents->SetDelegate(web_contents_delegate_.get());
152   return web_contents;
153 }
154
155 void XWalkContent::ClearCache(
156     JNIEnv* env,
157     jobject obj,
158     jboolean include_disk_files) {
159   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
160   render_view_host_ext_->ClearCache();
161
162   if (include_disk_files) {
163     RemoveHttpDiskCache(web_contents_->GetBrowserContext(),
164                         web_contents_->GetRoutingID());
165   }
166 }
167
168 ScopedJavaLocalRef<jstring> XWalkContent::DevToolsAgentId(JNIEnv* env,
169                                                           jobject obj) {
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());
174 }
175
176 void XWalkContent::Destroy(JNIEnv* env, jobject obj) {
177   delete this;
178 }
179
180 ScopedJavaLocalRef<jstring> XWalkContent::GetVersion(JNIEnv* env,
181                                                      jobject obj) {
182   return base::android::ConvertUTF8ToJavaString(env, XWALK_VERSION);
183 }
184
185 void XWalkContent::SetJsOnlineProperty(JNIEnv* env,
186                                        jobject obj,
187                                        jboolean network_up) {
188   render_view_host_ext_->SetJsOnlineProperty(network_up);
189 }
190
191 jboolean XWalkContent::SetManifest(JNIEnv* env,
192                                    jobject obj,
193                                    jstring path,
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);
198
199   base::Value* manifest_value = base::JSONReader::Read(json_input);
200   if (!manifest_value) return false;
201
202   base::DictionaryValue* manifest_dictionary;
203   manifest_value->GetAsDictionary(&manifest_dictionary);
204   if (!manifest_dictionary) return false;
205
206   scoped_ptr<base::DictionaryValue>
207       manifest_dictionary_ptr(manifest_dictionary);
208
209   xwalk::application::Manifest manifest(
210       xwalk::application::Manifest::INVALID_TYPE,
211       manifest_dictionary_ptr.Pass());
212
213   std::string url;
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;
225     }
226   } else {
227     manifest.GetString(
228         xwalk::application_manifest_keys::kLaunchWebURLKey, &url);
229   }
230
231   std::string csp;
232   manifest.GetString(
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);
238
239   ScopedJavaLocalRef<jstring> url_buffer =
240       base::android::ConvertUTF8ToJavaString(env, url);
241
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
247     // to Java side.
248     manifest.GetString(
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());
254   } else {
255     // No need to display launch screen, load the url directly.
256     Java_XWalkContent_onGetUrlFromManifest(env, obj, url_buffer.obj());
257   }
258   return true;
259 }
260
261 jint XWalkContent::GetRoutingID(JNIEnv* env, jobject obj) {
262   DCHECK(web_contents_.get());
263   return web_contents_->GetRoutingID();
264 }
265
266 base::android::ScopedJavaLocalRef<jbyteArray> XWalkContent::GetState(
267     JNIEnv* env,
268     jobject obj) {
269   if (!web_contents_->GetController().GetEntryCount())
270     return ScopedJavaLocalRef<jbyteArray>();
271
272   Pickle pickle;
273   if (!WriteToPickle(*web_contents_, &pickle)) {
274     return ScopedJavaLocalRef<jbyteArray>();
275   } else {
276     return base::android::ToJavaByteArray(
277         env,
278         reinterpret_cast<const uint8*>(pickle.data()),
279         pickle.size());
280   }
281 }
282
283 jboolean XWalkContent::SetState(JNIEnv* env, jobject obj, jbyteArray state) {
284   std::vector<uint8> state_vector;
285   base::android::JavaByteArrayToByteVector(env, state, &state_vector);
286
287   Pickle pickle(reinterpret_cast<const char*>(state_vector.begin()),
288                 state_vector.size());
289   PickleIterator iterator(pickle);
290
291   return RestoreFromPickle(&iterator, web_contents_.get());
292 }
293
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);
299 }
300
301 bool RegisterXWalkContent(JNIEnv* env) {
302   return RegisterNativesImpl(env) >= 0;
303 }
304
305 namespace {
306
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);
312   if (j_ref.obj()) {
313     ScopedJavaLocalRef<jstring> j_origin(
314         ConvertUTF8ToJavaString(env, origin.spec()));
315     Java_XWalkContent_onGeolocationPermissionsShowPrompt(env,
316                                                          j_ref.obj(),
317                                                          j_origin.obj());
318   }
319 }
320
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,
327         FROM_HERE,
328         base::Bind(&ShowGeolocationPromptHelperTask,
329                    java_ref,
330                    origin));
331   }
332 }
333 }  // anonymous namespace
334
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));
341   if (show_prompt) {
342     ShowGeolocationPromptHelper(java_ref_, origin);
343   }
344 }
345
346 // Called by Java.
347 void XWalkContent::InvokeGeolocationCallback(JNIEnv* env,
348                                              jobject obj,
349                                              jboolean value,
350                                              jstring origin) {
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);
359     }
360   }
361 }
362
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;
370       }
371       it = pending_geolocation_prompts_.erase(it);
372     } else {
373       ++it;
374     }
375   }
376
377   if (removed_current_outstanding_callback) {
378     JNIEnv* env = AttachCurrentThread();
379     ScopedJavaLocalRef<jobject> j_ref = java_ref_.get(env);
380     if (j_ref.obj()) {
381       Java_XWalkContent_onGeolocationPermissionsHidePrompt(env, j_ref.obj());
382     }
383     if (!pending_geolocation_prompts_.empty()) {
384       ShowGeolocationPromptHelper(java_ref_,
385                             pending_geolocation_prompts_.front().first);
386     }
387   }
388 }
389 }  // namespace xwalk