Fix possible memory corruptions in policy decision dispatching logic
authorTomasz Czekala <t.czekala@partner.samsung.com>
Sun, 25 Jan 2015 11:03:41 +0000 (12:03 +0100)
committerYoungsoo Choi <kenshin.choi@samsung.com>
Tue, 10 Jul 2018 06:57:09 +0000 (06:57 +0000)
In some cases suspended policy decisions could be deleted by new policy
decision events. This patch fixes that also providing some feedback for
policy decisions that can't be suspended at all.
This patch is a port of this patch:
http://165.213.202.130:8080/#/c/70024/
by Kamil Klimek

Bug: http://107.108.218.239/bugzilla/show_bug.cgi?id=10629
Reviewed by: Piotr Tworek, arno renevier

Change-Id: I4431c4c8e9ec0deb4f7c76ea494ef47bec6de0d5
Signed-off-by: Tomasz Czekala <t.czekala@partner.samsung.com>
tizen_src/ewk/efl_integration/public/ewk_policy_decision.cc
tizen_src/ewk/unittest/utc_blink_ewk_policy_decision_suspend_func.cpp
tizen_src/impl/eweb_view.cc
tizen_src/impl/eweb_view.h
tizen_src/impl/tizen_webview/public/tw_policy_decision.cc
tizen_src/impl/tizen_webview/public/tw_policy_decision.h
tizen_src/impl/web_contents_delegate_efl.cc
tizen_src/impl/web_contents_delegate_efl.h

index 6d3c986..a3cabf3 100644 (file)
@@ -81,9 +81,7 @@ int ewk_policy_decision_response_status_code_get(const Ewk_Policy_Decision* poli
 Eina_Bool ewk_policy_decision_suspend(Ewk_Policy_Decision* policyDecision)
 {
   EINA_SAFETY_ON_NULL_RETURN_VAL(policyDecision, EINA_FALSE);
-  policyDecision->Suspend();
-  // It is already suspended. We don't need to take any action here.
-  return EINA_TRUE;
+  return policyDecision->Suspend();
 }
 
 Eina_Bool ewk_policy_decision_use(Ewk_Policy_Decision* policyDecision)
index 8acb6ef..84edac3 100755 (executable)
 
 class utc_blink_ewk_policy_decision_suspend : public utc_blink_ewk_base {
 protected:
-
-  void PostSetUp()
-  {
-    evas_object_smart_callback_add(GetEwkWebView(), "policy,navigation,decide", policy_navigation_decide, this);
-  }
-
-  void PreTearDown()
+  utc_blink_ewk_policy_decision_suspend()
+    : policy_decision(NULL)
   {
-    evas_object_smart_callback_del(GetEwkWebView(), "policy,navigation,decide", policy_navigation_decide);
   }
 
-  void LoadFinished(Evas_Object* webview) {
 
-    EventLoopStop(utc_blink_ewk_base::Success); // will noop if EventLoopStop was alraedy called
+  void LoadFinished(Evas_Object* webview)
+  {
+    EventLoopStop(utc_blink_ewk_base::Failure); // will noop if EventLoopStop was already called
   }
 
-  static void policy_navigation_decide(void* data, Evas_Object* webview, void* event_info)
+  static void policy_decision_suspend(utc_blink_ewk_policy_decision_suspend* owner, Evas_Object* webview, Ewk_Policy_Decision* policy_decision)
   {
-    utc_message("[policy_navigation_decide] :: \n");
-    utc_blink_ewk_policy_decision_suspend *owner = static_cast<utc_blink_ewk_policy_decision_suspend*>(data);
-    Ewk_Policy_Decision* policy_decision = (Ewk_Policy_Decision*)event_info;
+    utc_message("[policy_decision_suspend] :: \n");
+    ASSERT_TRUE(owner);
+    EXPECT_TRUE(policy_decision);
 
-    if (policy_decision && ewk_policy_decision_suspend(policy_decision)) {
+    if (!owner->policy_decision) {
+      // We need to handle suspend here, as if suspend fails then policy_decision is treated as not decided and deleted
+      if (EINA_TRUE == ewk_policy_decision_suspend(policy_decision)) {
+        owner->policy_decision = policy_decision;
+      }
 
       owner->EventLoopStop(utc_blink_ewk_base::Success);
     }
   }
+
+  static void fail_event_loop(utc_blink_ewk_policy_decision_suspend* owner, Evas_Object* webview, void*)
+  {
+    ASSERT_TRUE(owner);
+    owner->policy_decision = NULL;
+    owner->EventLoopStop(Failure);
+  }
+
+protected:
+  Ewk_Policy_Decision *policy_decision;
 };
 
 /**
  * @brief Tests if suspend operation for policy decision is set properly
  */
-TEST_F(utc_blink_ewk_policy_decision_suspend, POS_TEST)
+TEST_F(utc_blink_ewk_policy_decision_suspend, SuspendNavigation)
+{
+  evas_object_smart_callback_auto sc(GetEwkWebView(), "policy,navigation,decide", ToSmartCallback(policy_decision_suspend), this);
+
+  ASSERT_EQ(EINA_TRUE, ewk_view_url_set(GetEwkWebView(), "http://www.google.com"));
+  // Wait for policy decision smart callback
+  ASSERT_EQ(Success, EventLoopStart());
+
+  ASSERT_FALSE(policy_decision);
+  // TODO: enable path below if we manage to support suspend for navigation
+  /*
+  ASSERT_TRUE(policy_decision) << "suspend failed - suspend for navigation may be not supported";
+
+  // we've suspended navigation - try waiting for load finished. It should timeout
+  ASSERT_EQ(Timeout, EventLoopStart(5.0));
+
+  // now resume it
+  ASSERT_EQ(EINA_TRUE, ewk_policy_decision_use(policy_decision));
+
+  // and wait till request finished - LoadFinished sets error to Failure
+  ASSERT_EQ(Failure, EventLoopStart());
+  */
+}
+
+TEST_F(utc_blink_ewk_policy_decision_suspend, SuspendNewWindow)
+{
+  evas_object_smart_callback_auto sc(GetEwkWebView(), "policy,newwindow,decide", ToSmartCallback(policy_decision_suspend), this);
+  evas_object_smart_callback_auto popup_blocked(GetEwkWebView(), "popup,blocked", ToSmartCallback(fail_event_loop), this);
+  evas_object_smart_callback_auto create_window(GetEwkWebView(), "create,window", ToSmartCallback(fail_event_loop), this);
+
+  ASSERT_EQ(EINA_TRUE, ewk_view_html_string_load(GetEwkWebView(), "<html><body>Page</body></html>", NULL, NULL));
+
+  // Wait for page to load
+  ASSERT_EQ(Failure, EventLoopStart());
+
+  // execute window.open
+  ewk_view_script_execute(GetEwkWebView(), "window.open('www.google.com');", NULL, NULL);
+  // Wait for policy,newwindow,decide callback
+  ASSERT_EQ(Success, EventLoopStart());
+
+  ASSERT_TRUE(policy_decision) << "suspend failed - suspend for newwindow may be not supported";
+
+  // we've suspended decision - we should not get create,window or popup,blocked smart callback
+  ASSERT_EQ(Timeout, EventLoopStart(5.0));
+
+  // Allow new window
+  ASSERT_EQ(EINA_TRUE, ewk_policy_decision_use(policy_decision));
+
+  // window,create will NULLify policy_decision to was called - it will be called synchronously from ewk_policy_decision_use
+  ASSERT_FALSE(policy_decision);
+}
+
+TEST_F(utc_blink_ewk_policy_decision_suspend, SuspendResponse)
 {
-  Eina_Bool result = ewk_view_url_set(GetEwkWebView(), "http://www.google.com");
-  if (!result)
-    FAIL();
+  evas_object_smart_callback_auto sc(GetEwkWebView(), "policy,response,decide", ToSmartCallback(policy_decision_suspend), this);
+
+  ASSERT_EQ(EINA_TRUE, ewk_view_url_set(GetEwkWebView(), "http://www.google.com"));
+  // Wait for policy decision smart callback
+  ASSERT_EQ(Success, EventLoopStart());
+
+  ASSERT_TRUE(policy_decision) << "suspend failed - suspend for response may be not supported";
+
+  // we've suspended navigation - try waiting for load finished. It should timeout
+  ASSERT_EQ(Timeout, EventLoopStart(5.0));
+
+  // now resume it
+  ASSERT_EQ(EINA_TRUE, ewk_policy_decision_use(policy_decision));
 
-  utc_blink_ewk_base::MainLoopResult main_result = EventLoopStart();
-  if (main_result != utc_blink_ewk_base::Success)
-    FAIL();
+  // and wait till request finished - LoadFinished sets error to Failure
+  ASSERT_EQ(Failure, EventLoopStart());
 }
 
 /**
  * @brief Tests if function works properly in case of NULL of a webview
  */
-TEST_F(utc_blink_ewk_policy_decision_suspend, NEG_TEST)
+TEST_F(utc_blink_ewk_policy_decision_suspend, InvalidArgs)
 {
-  utc_check_ne(ewk_policy_decision_suspend(0), EINA_TRUE);
+  ASSERT_EQ(EINA_FALSE, ewk_policy_decision_suspend(NULL));
 }
index dd475b1..bef4a97 100644 (file)
@@ -671,12 +671,15 @@ void EWebView::InvokeAuthCallback(LoginDelegateEfl* login_delegate,
 }
 
 void EWebView::InvokePolicyResponseCallback(tizen_webview::PolicyDecision* policy_decision) {
-  set_policy_decision(policy_decision);
-  SmartCallback<EWebViewCallbacks::PolicyResponseDecide>().call(policy_decision_.get());
+  SmartCallback<EWebViewCallbacks::PolicyResponseDecide>().call(policy_decision);
 
-  // if app has not decided nor suspended, we act as if it was accepted.
-  if (!policy_decision_->isDecided() && !policy_decision_->isSuspended())
-    policy_decision_->Use();
+  if (policy_decision->isSuspended())
+    return;
+
+  if (!policy_decision->isDecided())
+    policy_decision->Use();
+
+  delete policy_decision;
 }
 
 void EWebView::InvokePolicyNavigationCallback(RenderViewHost* rvh,
@@ -685,15 +688,20 @@ void EWebView::InvokePolicyNavigationCallback(RenderViewHost* rvh,
 
   SmartCallback<EWebViewCallbacks::SaveSessionData>().call();
 
-  policy_decision_.reset(new tizen_webview::PolicyDecision(params, rvh));
+  scoped_ptr<PolicyDecision> policy_decision(new PolicyDecision(params, rvh));
+
+  SmartCallback<EWebViewCallbacks::NavigationPolicyDecision>().call(policy_decision.get());
 
-  SmartCallback<EWebViewCallbacks::NavigationPolicyDecision>().call(policy_decision_.get());
+  CHECK(!policy_decision->isSuspended());
 
-  // if app has not decided nor suspended, we act as if it was accepted.
-  if (!policy_decision_->isDecided() && !policy_decision_->isSuspended())
-    policy_decision_->Use();
+  // TODO: Navigation can't be suspended
+  // this aproach is synchronous and requires immediate response
+  // Maybe there is different approach (like resource throttle response mechanism) that allows us to
+  // suspend navigation
+  if (!policy_decision->isDecided())
+    policy_decision->Use();
 
-  *handled = policy_decision_->GetImpl()->GetNavigationPolicyHandler()->GetDecision() == NavigationPolicyHandlerEfl::Handled;
+  *handled = policy_decision->GetImpl()->GetNavigationPolicyHandler()->GetDecision() == NavigationPolicyHandlerEfl::Handled;
 }
 
 void EWebView::HandleTouchEvents(tizen_webview::Touch_Event_Type type, const Eina_List *points, const Evas_Modifier *modifiers)
index 6ea879b..f02acd0 100644 (file)
@@ -354,8 +354,7 @@ class EWebView {
    * @note ownership of snapshot is passed to caller
   */
   Evas_Object* GetSnapshot(Eina_Rectangle rect);
-  void set_policy_decision(tizen_webview::PolicyDecision* pr) { policy_decision_.reset(pr); }
-  tizen_webview::PolicyDecision* get_policy_decision() const { return policy_decision_.get(); }
+
   bool GetSnapshotAsync(Eina_Rectangle rect, Evas* canvas, tizen_webview::Web_App_Screenshot_Captured_Callback callback, void* user_data);
   void InvokePolicyResponseCallback(tizen_webview::PolicyDecision* policy_decision);
   void InvokePolicyNavigationCallback(content::RenderViewHost* rvh,
@@ -515,7 +514,7 @@ class EWebView {
   double text_zoom_factor_;
   mutable std::string selected_text_;
   scoped_ptr<_Ewk_Auth_Challenge> auth_challenge_;
-  scoped_ptr<tizen_webview::PolicyDecision> policy_decision_;
+
 #if defined(OS_TIZEN)
   Eina_List* popupMenuItems_;
   Popup_Picker* popupPicker_;
index 9615703..736b010 100644 (file)
@@ -5,9 +5,14 @@
 
 #include "tw_policy_decision.h"
 
+#include "tw_webview.h"
 #include "API/ewk_policy_decision_private.h"
 #include "tw_webview.h"
 
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
 namespace tizen_webview {
 
 
@@ -37,18 +42,30 @@ PolicyDecision::~PolicyDecision() {
 
 void PolicyDecision::Use() {
   impl_->Use();
+
+  if (isSuspended()) {
+    BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
+  }
 }
 
 void PolicyDecision::Ignore() {
   impl_->Ignore();
+
+  if (isSuspended()) {
+    BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
+  }
 }
 
 void PolicyDecision::Download() {
   impl_->Download();
+
+  if (isSuspended()) {
+    BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
+  }
 }
 
-void PolicyDecision::Suspend() {
-  impl_->Suspend();
+bool PolicyDecision::Suspend() {
+  return impl_->Suspend();
 }
 
 bool PolicyDecision::isDecided() const {
index a4d733d..3fb9cc1 100644 (file)
@@ -14,7 +14,6 @@
 
 // TODO: break below dependency
 namespace content {
-class WebContentsDelegateEfl;
 class RenderViewHost;
 }
 
@@ -70,7 +69,7 @@ class PolicyDecision {
   void Use();
   void Ignore();
   void Download();
-  void Suspend();
+  bool Suspend();
 
   bool isDecided() const;
   bool isSuspended() const;
index 66d0449..a54d8b8 100644 (file)
@@ -67,7 +67,6 @@ WebContentsDelegateEfl::WebContentsDelegateEfl(EWebView* view)
     , is_fullscreen_(false)
     , web_contents_(view->web_contents())
     , document_created_(false)
-    , should_open_new_window_(true)
     , dialog_manager_(NULL)
     , forward_backward_list_count_(0)
     , WebContentsObserver(&view->web_contents())
index fa48dd0..a7f7a69 100644 (file)
@@ -142,8 +142,6 @@ class WebContentsDelegateEfl
   void UpdateFormNavigation(int formElementCount, int currentNodeIndex,
       bool prevState, bool nextState);
 
-  void set_new_window_policy(bool policy) { should_open_new_window_ = policy; }
-  bool get_new_window_policy() const { return should_open_new_window_; }
   JavaScriptDialogManager* GetJavaScriptDialogManager() override;
 
   void DidFirstVisuallyNonEmptyPaint() override;
@@ -201,7 +199,6 @@ class WebContentsDelegateEfl
 
   scoped_ptr<ContentSecurityPolicy> pending_content_security_policy_;
   bool document_created_;
-  bool should_open_new_window_;
   JavaScriptDialogManagerEfl* dialog_manager_;
   int forward_backward_list_count_;
   scoped_ptr<FaviconDownloader> favicon_downloader_;