Upstream version 6.34.113.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 using xwalk::application_manifest_keys::kDisplay;
48
49 namespace xwalk {
50
51 namespace {
52
53 const void* kXWalkContentUserDataKey = &kXWalkContentUserDataKey;
54
55 class XWalkContentUserData : public base::SupportsUserData::Data {
56  public:
57   explicit XWalkContentUserData(XWalkContent* ptr) : content_(ptr) {}
58
59   static XWalkContent* GetContents(content::WebContents* web_contents) {
60     if (!web_contents)
61       return NULL;
62     XWalkContentUserData* data = reinterpret_cast<XWalkContentUserData*>(
63         web_contents->GetUserData(kXWalkContentUserDataKey));
64     return data ? data->content_ : NULL;
65   }
66
67  private:
68   XWalkContent* content_;
69 };
70
71 }  // namespace
72
73 XWalkContent::XWalkContent(JNIEnv* env,
74                            jobject obj,
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)) {
82 }
83
84 XWalkContent::~XWalkContent() {
85 }
86
87 // static
88 XWalkContent* XWalkContent::FromID(int render_process_id,
89                                    int render_view_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);
98 }
99
100 // static
101 XWalkContent* XWalkContent::FromWebContents(
102     content::WebContents* web_contents) {
103   return XWalkContentUserData::GetContents(web_contents);
104 }
105
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));
112
113     render_view_host_ext_.reset(
114         new XWalkRenderViewHostExt(web_contents_.get()));
115   }
116   return reinterpret_cast<jint>(web_contents_.get());
117 }
118
119 content::WebContents* XWalkContent::CreateWebContents(
120     JNIEnv* env, jobject io_thread_client,
121     jobject intercept_navigation_delegate) {
122
123   RuntimeContext* runtime_context =
124       XWalkRunner::GetInstance()->runtime_context();
125   CHECK(runtime_context);
126
127   content::WebContents* web_contents = content::WebContents::Create(
128       content::WebContents::CreateParams(runtime_context));
129
130   web_contents->SetUserData(kXWalkContentUserDataKey,
131                             new XWalkContentUserData(this));
132
133   XWalkContentsIoThreadClientImpl::RegisterPendingContents(web_contents);
134
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;
140
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(
148       child_id, route_id);
149   InterceptNavigationDelegate::Associate(web_contents,
150       make_scoped_ptr(new InterceptNavigationDelegate(
151           env, intercept_navigation_delegate)));
152   web_contents->SetDelegate(web_contents_delegate_.get());
153   return web_contents;
154 }
155
156 void XWalkContent::ClearCache(
157     JNIEnv* env,
158     jobject obj,
159     jboolean include_disk_files) {
160   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
161   render_view_host_ext_->ClearCache();
162
163   if (include_disk_files) {
164     RemoveHttpDiskCache(web_contents_->GetBrowserContext(),
165                         web_contents_->GetRoutingID());
166   }
167 }
168
169 ScopedJavaLocalRef<jstring> XWalkContent::DevToolsAgentId(JNIEnv* env,
170                                                           jobject obj) {
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());
175 }
176
177 void XWalkContent::Destroy(JNIEnv* env, jobject obj) {
178   delete this;
179 }
180
181 ScopedJavaLocalRef<jstring> XWalkContent::GetVersion(JNIEnv* env,
182                                                      jobject obj) {
183   return base::android::ConvertUTF8ToJavaString(env, XWALK_VERSION);
184 }
185
186 void XWalkContent::SetJsOnlineProperty(JNIEnv* env,
187                                        jobject obj,
188                                        jboolean network_up) {
189   render_view_host_ext_->SetJsOnlineProperty(network_up);
190 }
191
192 jboolean XWalkContent::SetManifest(JNIEnv* env,
193                                    jobject obj,
194                                    jstring path,
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);
199
200   base::Value* manifest_value = base::JSONReader::Read(json_input);
201   if (!manifest_value) return false;
202
203   base::DictionaryValue* manifest_dictionary;
204   manifest_value->GetAsDictionary(&manifest_dictionary);
205   if (!manifest_dictionary) return false;
206
207   scoped_ptr<base::DictionaryValue>
208       manifest_dictionary_ptr(manifest_dictionary);
209
210   xwalk::application::Manifest manifest(
211       xwalk::application::Manifest::INVALID_TYPE,
212       manifest_dictionary_ptr.Pass());
213
214   std::string url;
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;
223   } else {
224     manifest.GetString(
225         xwalk::application_manifest_keys::kLaunchWebURLKey, &url);
226   }
227
228   std::string csp;
229   manifest.GetString(
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);
235
236   ScopedJavaLocalRef<jstring> url_buffer =
237       base::android::ConvertUTF8ToJavaString(env, url);
238
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);
248   }
249
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
255     // to Java side.
256     manifest.GetString(
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());
262   } else {
263     // No need to display launch screen, load the url directly.
264     Java_XWalkContent_onGetUrlFromManifest(env, obj, url_buffer.obj());
265   }
266   return true;
267 }
268
269 jint XWalkContent::GetRoutingID(JNIEnv* env, jobject obj) {
270   DCHECK(web_contents_.get());
271   return web_contents_->GetRoutingID();
272 }
273
274 base::android::ScopedJavaLocalRef<jbyteArray> XWalkContent::GetState(
275     JNIEnv* env,
276     jobject obj) {
277   if (!web_contents_->GetController().GetEntryCount())
278     return ScopedJavaLocalRef<jbyteArray>();
279
280   Pickle pickle;
281   if (!WriteToPickle(*web_contents_, &pickle)) {
282     return ScopedJavaLocalRef<jbyteArray>();
283   } else {
284     return base::android::ToJavaByteArray(
285         env,
286         reinterpret_cast<const uint8*>(pickle.data()),
287         pickle.size());
288   }
289 }
290
291 jboolean XWalkContent::SetState(JNIEnv* env, jobject obj, jbyteArray state) {
292   std::vector<uint8> state_vector;
293   base::android::JavaByteArrayToByteVector(env, state, &state_vector);
294
295   Pickle pickle(reinterpret_cast<const char*>(state_vector.begin()),
296                 state_vector.size());
297   PickleIterator iterator(pickle);
298
299   return RestoreFromPickle(&iterator, web_contents_.get());
300 }
301
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);
307 }
308
309 bool RegisterXWalkContent(JNIEnv* env) {
310   return RegisterNativesImpl(env) >= 0;
311 }
312
313 namespace {
314
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);
320   if (j_ref.obj()) {
321     ScopedJavaLocalRef<jstring> j_origin(
322         ConvertUTF8ToJavaString(env, origin.spec()));
323     Java_XWalkContent_onGeolocationPermissionsShowPrompt(env,
324                                                          j_ref.obj(),
325                                                          j_origin.obj());
326   }
327 }
328
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,
335         FROM_HERE,
336         base::Bind(&ShowGeolocationPromptHelperTask,
337                    java_ref,
338                    origin));
339   }
340 }
341 }  // anonymous namespace
342
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));
349   if (show_prompt) {
350     ShowGeolocationPromptHelper(java_ref_, origin);
351   }
352 }
353
354 // Called by Java.
355 void XWalkContent::InvokeGeolocationCallback(JNIEnv* env,
356                                              jobject obj,
357                                              jboolean value,
358                                              jstring origin) {
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);
367     }
368   }
369 }
370
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;
378       }
379       it = pending_geolocation_prompts_.erase(it);
380     } else {
381       ++it;
382     }
383   }
384
385   if (removed_current_outstanding_callback) {
386     JNIEnv* env = AttachCurrentThread();
387     ScopedJavaLocalRef<jobject> j_ref = java_ref_.get(env);
388     if (j_ref.obj()) {
389       Java_XWalkContent_onGeolocationPermissionsHidePrompt(env, j_ref.obj());
390     }
391     if (!pending_geolocation_prompts_.empty()) {
392       ShowGeolocationPromptHelper(java_ref_,
393                             pending_geolocation_prompts_.front().first);
394     }
395   }
396 }
397 }  // namespace xwalk