Upstream version 8.37.186.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/json/json_writer.h"
18 #include "base/path_service.h"
19 #include "base/pickle.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/devtools_agent_host.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/common/renderer_preferences.h"
27 #include "content/public/common/url_constants.h"
28 #include "components/navigation_interception/intercept_navigation_delegate.h"
29 #include "xwalk/application/common/application_manifest_constants.h"
30 #include "xwalk/application/common/manifest.h"
31 #include "xwalk/runtime/browser/android/net_disk_cache_remover.h"
32 #include "xwalk/runtime/browser/android/state_serializer.h"
33 #include "xwalk/runtime/browser/android/xwalk_contents_client_bridge.h"
34 #include "xwalk/runtime/browser/android/xwalk_contents_client_bridge_base.h"
35 #include "xwalk/runtime/browser/android/xwalk_contents_io_thread_client_impl.h"
36 #include "xwalk/runtime/browser/android/xwalk_web_contents_delegate.h"
37 #include "xwalk/runtime/browser/runtime_context.h"
38 #include "xwalk/runtime/browser/runtime_resource_dispatcher_host_delegate_android.h"
39 #include "xwalk/runtime/browser/xwalk_runner.h"
40 #include "jni/XWalkContent_jni.h"
41
42 using base::android::AttachCurrentThread;
43 using base::android::ConvertUTF8ToJavaString;
44 using base::android::ScopedJavaLocalRef;
45 using content::BrowserThread;
46 using content::WebContents;
47 using navigation_interception::InterceptNavigationDelegate;
48 using xwalk::application_manifest_keys::kDisplay;
49
50 namespace keys = xwalk::application_manifest_keys;
51
52 namespace xwalk {
53
54 namespace {
55
56 const void* kXWalkContentUserDataKey = &kXWalkContentUserDataKey;
57
58 class XWalkContentUserData : public base::SupportsUserData::Data {
59  public:
60   explicit XWalkContentUserData(XWalkContent* ptr) : content_(ptr) {}
61
62   static XWalkContent* GetContents(content::WebContents* web_contents) {
63     if (!web_contents)
64       return NULL;
65     XWalkContentUserData* data = reinterpret_cast<XWalkContentUserData*>(
66         web_contents->GetUserData(kXWalkContentUserDataKey));
67     return data ? data->content_ : NULL;
68   }
69
70  private:
71   XWalkContent* content_;
72 };
73
74 // FIXME(wang16): Remove following methods after deprecated fields
75 // are not supported any more.
76 void PrintManifestDeprecationWarning(std::string field) {
77   LOG(WARNING) << "\"" << field << "\" is deprecated for Crosswalk. "
78       << "Please follow "
79       << "https://www.crosswalk-project.org/#documentation/manifest.";
80 }
81
82 bool ManifestHasPath(const xwalk::application::Manifest& manifest,
83                      const std::string& path,
84                      const std::string& deprecated_path) {
85   if (manifest.HasPath(path))
86     return true;
87   if (manifest.HasPath(deprecated_path)) {
88     PrintManifestDeprecationWarning(deprecated_path);
89     return true;
90   }
91   return false;
92 }
93
94 bool ManifestGetString(const xwalk::application::Manifest& manifest,
95                        const std::string& path,
96                        const std::string& deprecated_path,
97                        std::string* out_value) {
98   if (manifest.GetString(path, out_value))
99     return true;
100   if (manifest.GetString(deprecated_path, out_value)) {
101     PrintManifestDeprecationWarning(deprecated_path);
102     return true;
103   }
104   return false;
105 }
106
107 }  // namespace
108
109 XWalkContent::XWalkContent(JNIEnv* env,
110                            jobject obj,
111                            jobject web_contents_delegate,
112                            jobject contents_client_bridge)
113     : java_ref_(env, obj),
114       web_contents_delegate_(
115           new XWalkWebContentsDelegate(env, web_contents_delegate)),
116       contents_client_bridge_(
117           new XWalkContentsClientBridge(env, contents_client_bridge)) {
118 }
119
120 XWalkContent::~XWalkContent() {
121 }
122
123 // static
124 XWalkContent* XWalkContent::FromID(int render_process_id,
125                                    int render_view_id) {
126   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
127   const content::RenderViewHost* rvh =
128       content::RenderViewHost::FromID(render_process_id, render_view_id);
129   if (!rvh) return NULL;
130   content::WebContents* web_contents =
131       content::WebContents::FromRenderViewHost(rvh);
132   if (!web_contents) return NULL;
133   return FromWebContents(web_contents);
134 }
135
136 // static
137 XWalkContent* XWalkContent::FromWebContents(
138     content::WebContents* web_contents) {
139   return XWalkContentUserData::GetContents(web_contents);
140 }
141
142 jlong XWalkContent::GetWebContents(
143     JNIEnv* env, jobject obj, jobject io_thread_client,
144     jobject intercept_navigation_delegate) {
145   if (!web_contents_) {
146     web_contents_.reset(CreateWebContents(env, io_thread_client,
147                                           intercept_navigation_delegate));
148
149     render_view_host_ext_.reset(
150         new XWalkRenderViewHostExt(web_contents_.get()));
151   }
152   return reinterpret_cast<intptr_t>(web_contents_.get());
153 }
154
155 content::WebContents* XWalkContent::CreateWebContents(
156     JNIEnv* env, jobject io_thread_client,
157     jobject intercept_navigation_delegate) {
158
159   RuntimeContext* runtime_context =
160       XWalkRunner::GetInstance()->runtime_context();
161   CHECK(runtime_context);
162
163   content::WebContents* web_contents = content::WebContents::Create(
164       content::WebContents::CreateParams(runtime_context));
165
166   web_contents->SetUserData(kXWalkContentUserDataKey,
167                             new XWalkContentUserData(this));
168
169   XWalkContentsIoThreadClientImpl::RegisterPendingContents(web_contents);
170
171   // XWalk does not use disambiguation popup for multiple targets.
172   content::RendererPreferences* prefs =
173       web_contents->GetMutableRendererPrefs();
174   prefs->tap_multiple_targets_strategy =
175       content::TAP_MULTIPLE_TARGETS_STRATEGY_NONE;
176
177   XWalkContentsClientBridgeBase::Associate(web_contents,
178       contents_client_bridge_.get());
179   XWalkContentsIoThreadClientImpl::Associate(web_contents,
180       ScopedJavaLocalRef<jobject>(env, io_thread_client));
181   int render_process_id = web_contents->GetRenderProcessHost()->GetID();
182   int render_frame_id = web_contents->GetRoutingID();
183   RuntimeResourceDispatcherHostDelegateAndroid::OnIoThreadClientReady(
184       render_process_id, render_frame_id);
185   InterceptNavigationDelegate::Associate(web_contents,
186       make_scoped_ptr(new InterceptNavigationDelegate(
187           env, intercept_navigation_delegate)));
188   web_contents->SetDelegate(web_contents_delegate_.get());
189   return web_contents;
190 }
191
192 void XWalkContent::ClearCache(
193     JNIEnv* env,
194     jobject obj,
195     jboolean include_disk_files) {
196   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
197   render_view_host_ext_->ClearCache();
198
199   if (include_disk_files) {
200     RemoveHttpDiskCache(web_contents_->GetBrowserContext(),
201                         web_contents_->GetRoutingID());
202   }
203 }
204
205 ScopedJavaLocalRef<jstring> XWalkContent::DevToolsAgentId(JNIEnv* env,
206                                                           jobject obj) {
207   content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
208   scoped_refptr<content::DevToolsAgentHost> agent_host(
209       content::DevToolsAgentHost::GetOrCreateFor(rvh));
210   return base::android::ConvertUTF8ToJavaString(env, agent_host->GetId());
211 }
212
213 void XWalkContent::Destroy(JNIEnv* env, jobject obj) {
214   delete this;
215 }
216
217 ScopedJavaLocalRef<jstring> XWalkContent::GetVersion(JNIEnv* env,
218                                                      jobject obj) {
219   return base::android::ConvertUTF8ToJavaString(env, XWALK_VERSION);
220 }
221
222 void XWalkContent::SetJsOnlineProperty(JNIEnv* env,
223                                        jobject obj,
224                                        jboolean network_up) {
225   render_view_host_ext_->SetJsOnlineProperty(network_up);
226 }
227
228 jboolean XWalkContent::SetManifest(JNIEnv* env,
229                                    jobject obj,
230                                    jstring path,
231                                    jstring manifest_string) {
232   std::string path_str = base::android::ConvertJavaStringToUTF8(env, path);
233   std::string json_input =
234       base::android::ConvertJavaStringToUTF8(env, manifest_string);
235
236   base::Value* manifest_value = base::JSONReader::Read(json_input);
237   if (!manifest_value) return false;
238
239   base::DictionaryValue* manifest_dictionary;
240   manifest_value->GetAsDictionary(&manifest_dictionary);
241   if (!manifest_dictionary) return false;
242
243   scoped_ptr<base::DictionaryValue>
244       manifest_dictionary_ptr(manifest_dictionary);
245
246   xwalk::application::Manifest manifest(
247       xwalk::application::Manifest::INVALID_TYPE,
248       manifest_dictionary_ptr.Pass());
249
250   std::string url;
251   if (manifest.GetString(keys::kStartURLKey, &url)) {
252     std::string scheme = GURL(url).scheme();
253     if (scheme.empty())
254       url = path_str + url;
255   } else if (manifest.GetString(keys::kLaunchLocalPathKey, &url)) {
256     PrintManifestDeprecationWarning(keys::kLaunchLocalPathKey);
257     // According to original proposal for "app:launch:local_path", the "http"
258     // and "https" schemes are supported. So |url| should do nothing when it
259     // already has "http" or "https" scheme.
260     std::string scheme = GURL(url).scheme();
261     if (scheme != url::kHttpScheme && scheme != url::kHttpsScheme)
262       url = path_str + url;
263   } else if (manifest.GetString(keys::kLaunchWebURLKey, &url)) {
264     PrintManifestDeprecationWarning(keys::kLaunchWebURLKey);
265   } else {
266     NOTIMPLEMENTED();
267   }
268
269   std::string match_patterns;
270   const base::ListValue* xwalk_hosts = NULL;
271   if (manifest.GetList(
272           xwalk::application_manifest_keys::kXWalkHostsKey, &xwalk_hosts)) {
273       base::JSONWriter::Write(xwalk_hosts, &match_patterns);
274   }
275   render_view_host_ext_->SetOriginAccessWhitelist(url, match_patterns);
276
277   std::string csp;
278   ManifestGetString(manifest, keys::kCSPKey, keys::kDeprecatedCSPKey, &csp);
279   RuntimeContext* runtime_context =
280       XWalkRunner::GetInstance()->runtime_context();
281   CHECK(runtime_context);
282   runtime_context->SetCSPString(csp);
283
284   ScopedJavaLocalRef<jstring> url_buffer =
285       base::android::ConvertUTF8ToJavaString(env, url);
286
287   if (manifest.HasPath(kDisplay)) {
288     std::string display_string;
289     manifest.GetString(kDisplay, &display_string);
290     // TODO(David): update the handling process of the display strings
291     // including fullscreen etc.
292     bool display_as_fullscreen = (
293         display_string.find("fullscreen") != std::string::npos);
294     Java_XWalkContent_onGetFullscreenFlagFromManifest(
295         env, obj, display_as_fullscreen ? JNI_TRUE : JNI_FALSE);
296   }
297
298   // Check whether need to display launch screen. (Read from manifest.json)
299   if (ManifestHasPath(manifest,
300                       keys::kXWalkLaunchScreen,
301                       keys::kLaunchScreen)) {
302     std::string ready_when;
303     // Get the value of 'ready_when' from manifest.json
304     ManifestGetString(manifest,
305                       keys::kXWalkLaunchScreenReadyWhen,
306                       keys::kLaunchScreenReadyWhen,
307                       &ready_when);
308     ScopedJavaLocalRef<jstring> ready_when_buffer =
309         base::android::ConvertUTF8ToJavaString(env, ready_when);
310
311     // Get the value of 'image_border'
312     // 1. When 'launch_screen.[orientation]' was defined, but no 'image_border'
313     //    The value of 'image_border' will be set as 'empty'.
314     // 2. Otherwise, there is no 'launch_screen.[orientation]' defined,
315     //    The value of 'image_border' will be empty.
316     const char empty[] = "empty";
317     std::string image_border_default;
318     ManifestGetString(manifest,
319                       keys::kXWalkLaunchScreenImageBorderDefault,
320                       keys::kLaunchScreenImageBorderDefault,
321                       &image_border_default);
322     if (image_border_default.empty() &&
323         ManifestHasPath(manifest,
324                         keys::kXWalkLaunchScreenDefault,
325                         keys::kLaunchScreenDefault)) {
326       image_border_default = empty;
327     }
328
329     std::string image_border_landscape;
330     ManifestGetString(manifest,
331                       keys::kXWalkLaunchScreenImageBorderLandscape,
332                       keys::kLaunchScreenImageBorderLandscape,
333                       &image_border_landscape);
334     if (image_border_landscape.empty() &&
335         ManifestHasPath(manifest,
336                         keys::kXWalkLaunchScreenLandscape,
337                         keys::kLaunchScreenLandscape)) {
338       image_border_landscape = empty;
339     }
340
341     std::string image_border_portrait;
342     ManifestGetString(manifest,
343                       keys::kXWalkLaunchScreenImageBorderPortrait,
344                       keys::kLaunchScreenImageBorderPortrait,
345                       &image_border_portrait);
346     if (image_border_portrait.empty() &&
347         ManifestHasPath(manifest,
348                         keys::kXWalkLaunchScreenPortrait,
349                         keys::kLaunchScreenPortrait)) {
350       image_border_portrait = empty;
351     }
352
353     std::string image_border = image_border_default + ';' +
354         image_border_landscape  + ';' + image_border_portrait;
355     ScopedJavaLocalRef<jstring> image_border_buffer =
356         base::android::ConvertUTF8ToJavaString(env, image_border);
357
358     Java_XWalkContent_onGetUrlAndLaunchScreenFromManifest(
359         env, obj, url_buffer.obj(), ready_when_buffer.obj(),
360         image_border_buffer.obj());
361   } else {
362     // No need to display launch screen, load the url directly.
363     Java_XWalkContent_onGetUrlFromManifest(env, obj, url_buffer.obj());
364   }
365   return true;
366 }
367
368 jint XWalkContent::GetRoutingID(JNIEnv* env, jobject obj) {
369   DCHECK(web_contents_.get());
370   return web_contents_->GetRoutingID();
371 }
372
373 base::android::ScopedJavaLocalRef<jbyteArray> XWalkContent::GetState(
374     JNIEnv* env,
375     jobject obj) {
376   if (!web_contents_->GetController().GetEntryCount())
377     return ScopedJavaLocalRef<jbyteArray>();
378
379   Pickle pickle;
380   if (!WriteToPickle(*web_contents_, &pickle)) {
381     return ScopedJavaLocalRef<jbyteArray>();
382   } else {
383     return base::android::ToJavaByteArray(
384         env,
385         reinterpret_cast<const uint8*>(pickle.data()),
386         pickle.size());
387   }
388 }
389
390 jboolean XWalkContent::SetState(JNIEnv* env, jobject obj, jbyteArray state) {
391   std::vector<uint8> state_vector;
392   base::android::JavaByteArrayToByteVector(env, state, &state_vector);
393
394   Pickle pickle(reinterpret_cast<const char*>(state_vector.begin()),
395                 state_vector.size());
396   PickleIterator iterator(pickle);
397
398   return RestoreFromPickle(&iterator, web_contents_.get());
399 }
400
401 static jlong Init(JNIEnv* env, jobject obj, jobject web_contents_delegate,
402     jobject contents_client_bridge) {
403   XWalkContent* xwalk_core_content =
404     new XWalkContent(env, obj, web_contents_delegate, contents_client_bridge);
405   return reinterpret_cast<intptr_t>(xwalk_core_content);
406 }
407
408 bool RegisterXWalkContent(JNIEnv* env) {
409   return RegisterNativesImpl(env) >= 0;
410 }
411
412 namespace {
413
414 void ShowGeolocationPromptHelperTask(
415     const JavaObjectWeakGlobalRef& java_ref,
416     const GURL& origin) {
417   JNIEnv* env = AttachCurrentThread();
418   ScopedJavaLocalRef<jobject> j_ref = java_ref.get(env);
419   if (j_ref.obj()) {
420     ScopedJavaLocalRef<jstring> j_origin(
421         ConvertUTF8ToJavaString(env, origin.spec()));
422     Java_XWalkContent_onGeolocationPermissionsShowPrompt(env,
423                                                          j_ref.obj(),
424                                                          j_origin.obj());
425   }
426 }
427
428 void ShowGeolocationPromptHelper(const JavaObjectWeakGlobalRef& java_ref,
429                                  const GURL& origin) {
430   JNIEnv* env = AttachCurrentThread();
431   if (java_ref.get(env).obj()) {
432     content::BrowserThread::PostTask(
433         content::BrowserThread::UI,
434         FROM_HERE,
435         base::Bind(&ShowGeolocationPromptHelperTask,
436                    java_ref,
437                    origin));
438   }
439 }
440 }  // anonymous namespace
441
442 void XWalkContent::ShowGeolocationPrompt(
443     const GURL& requesting_frame,
444     const base::Callback<void(bool)>& callback) { // NOLINT
445   GURL origin = requesting_frame.GetOrigin();
446   bool show_prompt = pending_geolocation_prompts_.empty();
447   pending_geolocation_prompts_.push_back(OriginCallback(origin, callback));
448   if (show_prompt) {
449     ShowGeolocationPromptHelper(java_ref_, origin);
450   }
451 }
452
453 // Called by Java.
454 void XWalkContent::InvokeGeolocationCallback(JNIEnv* env,
455                                              jobject obj,
456                                              jboolean value,
457                                              jstring origin) {
458   GURL callback_origin(base::android::ConvertJavaStringToUTF16(env, origin));
459   if (callback_origin.GetOrigin() ==
460       pending_geolocation_prompts_.front().first) {
461     pending_geolocation_prompts_.front().second.Run(value);
462     pending_geolocation_prompts_.pop_front();
463     if (!pending_geolocation_prompts_.empty()) {
464       ShowGeolocationPromptHelper(java_ref_,
465                                   pending_geolocation_prompts_.front().first);
466     }
467   }
468 }
469
470 void XWalkContent::HideGeolocationPrompt(const GURL& origin) {
471   bool removed_current_outstanding_callback = false;
472   std::list<OriginCallback>::iterator it = pending_geolocation_prompts_.begin();
473   while (it != pending_geolocation_prompts_.end()) {
474     if ((*it).first == origin.GetOrigin()) {
475       if (it == pending_geolocation_prompts_.begin()) {
476         removed_current_outstanding_callback = true;
477       }
478       it = pending_geolocation_prompts_.erase(it);
479     } else {
480       ++it;
481     }
482   }
483
484   if (removed_current_outstanding_callback) {
485     JNIEnv* env = AttachCurrentThread();
486     ScopedJavaLocalRef<jobject> j_ref = java_ref_.get(env);
487     if (j_ref.obj()) {
488       Java_XWalkContent_onGeolocationPermissionsHidePrompt(env, j_ref.obj());
489     }
490     if (!pending_geolocation_prompts_.empty()) {
491       ShowGeolocationPromptHelper(java_ref_,
492                             pending_geolocation_prompts_.front().first);
493     }
494   }
495 }
496 }  // namespace xwalk