Bug 666604 - Allow untrusted events to trigger a link, r=bz, a=asa
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Tue, 19 Jul 2011 20:02:09 +0300
changeset 70539 daef8d0f83b01bd99ec9108ca74a79c81ebb5801
parent 70538 4137513fe9324bc0e77e4e8a7dde9843150e13de
child 70540 084847ea02b429c8a580b880b36fee773a1411ce
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
reviewersbz, asa
bugs666604
milestone6.0
Bug 666604 - Allow untrusted events to trigger a link, r=bz, a=asa
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/src/nsGenericElement.cpp
content/base/test/test_bug666604.html
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsILinkHandler.h
layout/forms/nsIsIndexFrame.cpp
layout/generic/nsImageFrame.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1341,20 +1341,23 @@ public:
    * @param aPresContext the pres context, must be non-null.
    * @param aLinkURI the URI of the link, must be non-null.
    * @param aTargetSpec the target (like target=, may be empty).
    * @param aClick whether this was a click or not (if false, this method
    *               assumes you just hovered over the link).
    * @param aIsUserTriggered whether the user triggered the link. This would be
    *                         false for loads from auto XLinks or from the
    *                         click() method if we ever implement it.
+   * @param aIsTrusted If PR_FALSE, JS Context will be pushed to stack
+   *                   when the link is triggered.
    */
   static void TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
                           nsIURI *aLinkURI, const nsString& aTargetSpec,
-                          PRBool aClick, PRBool aIsUserTriggered);
+                          PRBool aClick, PRBool aIsUserTriggered,
+                          PRBool aIsTrusted);
 
   /**
    * Return top-level widget in the parent chain.
    */
   static nsIWidget* GetTopLevelWidget(nsIWidget* aWidget);
 
   /**
    * Return the localized ellipsis for UI.
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4445,26 +4445,27 @@ nsContentUtils::IsSystemPrincipal(nsIPri
   nsresult rv = sSecurityManager->IsSystemPrincipal(aPrincipal, &isSystem);
   return NS_SUCCEEDED(rv) && isSystem;
 }
 
 /* static */
 void
 nsContentUtils::TriggerLink(nsIContent *aContent, nsPresContext *aPresContext,
                             nsIURI *aLinkURI, const nsString &aTargetSpec,
-                            PRBool aClick, PRBool aIsUserTriggered)
+                            PRBool aClick, PRBool aIsUserTriggered,
+                            PRBool aIsTrusted)
 {
   NS_ASSERTION(aPresContext, "Need a nsPresContext");
   NS_PRECONDITION(aLinkURI, "No link URI");
 
   if (aContent->IsEditable()) {
     return;
   }
 
-  nsILinkHandler *handler = aPresContext->GetLinkHandler();
+  nsCOMPtr<nsILinkHandler_5_0> handler = do_QueryInterface(aPresContext->GetLinkHandler());
   if (!handler) {
     return;
   }
 
   if (!aClick) {
     handler->OnOverLink(aContent, aLinkURI, aTargetSpec.get());
 
     return;
@@ -4480,17 +4481,18 @@ nsContentUtils::TriggerLink(nsIContent *
       (PRUint32)nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT;
     proceed =
       sSecurityManager->CheckLoadURIWithPrincipal(aContent->NodePrincipal(),
                                                   aLinkURI, flag);
   }
 
   // Only pass off the click event if the script security manager says it's ok.
   if (NS_SUCCEEDED(proceed)) {
-    handler->OnLinkClick(aContent, aLinkURI, aTargetSpec.get());
+    handler->OnLinkClick(aContent, aLinkURI, aTargetSpec.get(), nsnull, nsnull,
+                         aIsTrusted);
   }
 }
 
 /* static */
 nsIWidget*
 nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget)
 {
   if (!aWidget)
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -5252,17 +5252,20 @@ nsGenericElement::CreateSlots()
   return new nsDOMSlots();
 }
 
 PRBool
 nsGenericElement::CheckHandleEventForLinksPrecondition(nsEventChainVisitor& aVisitor,
                                                        nsIURI** aURI) const
 {
   if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
-      !NS_IS_TRUSTED_EVENT(aVisitor.mEvent) ||
+      (!NS_IS_TRUSTED_EVENT(aVisitor.mEvent) &&
+       (aVisitor.mEvent->message != NS_MOUSE_CLICK) &&
+       (aVisitor.mEvent->message != NS_KEY_PRESS) &&
+       (aVisitor.mEvent->message != NS_UI_ACTIVATE)) ||
       !aVisitor.mPresContext ||
       (aVisitor.mEvent->flags & NS_EVENT_FLAG_PREVENT_ANCHOR_ACTIONS)) {
     return PR_FALSE;
   }
 
   // Make sure we actually are a link
   return IsLink(aURI);
 }
@@ -5298,17 +5301,17 @@ nsGenericElement::PreHandleEventForLinks
     aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
     // FALL THROUGH
   case NS_FOCUS_CONTENT:
     if (aVisitor.mEvent->eventStructType != NS_FOCUS_EVENT ||
         !static_cast<nsFocusEvent*>(aVisitor.mEvent)->isRefocus) {
       nsAutoString target;
       GetLinkTarget(target);
       nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
-                                  PR_FALSE, PR_TRUE);
+                                  PR_FALSE, PR_TRUE, PR_TRUE);
       // Make sure any ancestor links don't also TriggerLink
       aVisitor.mEvent->flags |= NS_EVENT_FLAG_PREVENT_ANCHOR_ACTIONS;
     }
     break;
 
   case NS_MOUSE_EXIT_SYNTH:
     aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
     // FALL THROUGH
@@ -5401,17 +5404,17 @@ nsGenericElement::PostHandleEventForLink
     break;
 
   case NS_UI_ACTIVATE:
     {
       if (aVisitor.mEvent->originalTarget == this) {
         nsAutoString target;
         GetLinkTarget(target);
         nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
-                                    PR_TRUE, PR_TRUE);
+                                    PR_TRUE, PR_TRUE, NS_IS_TRUSTED_EVENT(aVisitor.mEvent));
         aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
       }
     }
     break;
 
   case NS_KEY_PRESS:
     {
       if (aVisitor.mEvent->eventStructType == NS_KEY_EVENT) {
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug666604.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666604
+-->
+<head>
+  <title>Test for Bug 666604</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666604">Mozilla Bug 666604</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<a href="javascript:activationListener()" id="testlink">test</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 666604 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var activationListener;
+
+function dispatchClick(target, ctrl) {
+  var e = document.createEvent("MouseEvent");
+  e.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, 
+                   ctrl, false, false, false, 0, null);
+  target.dispatchEvent(e);
+}
+
+function dispatchReturn(target, ctrl) {
+  var e = document.createEvent("KeyboardEvent");
+  e.initKeyEvent("keypress", true, true, window, ctrl, false,
+                 false, false, 13, 0);
+  target.dispatchEvent(e);                
+}
+
+function dispatchDOMActivate(target) {
+  var e = document.createEvent("UIEvent");
+  e.initUIEvent("DOMActivate", true, true, window, 0);
+  target.dispatchEvent(e);
+}
+
+var testlink = document.getElementById("testlink");
+function test1() {
+  activationListener =
+    function() {
+      ok(true, "Untrusted click should activate a link");
+      test2();
+    }
+  dispatchClick(testlink, false);
+}
+
+function test2() {
+  activationListener =
+    function() {
+      ok(true, "Untrusted return keypress should activate a link");
+      test3();
+    }
+  dispatchReturn(testlink, false);
+}
+
+function test3() {
+  activationListener =
+  function() {
+    ok(false, "Untrusted click+ctrl should not activate a link");
+    test4();
+  }
+  dispatchClick(testlink, true);
+  setTimeout(test4, 1000);
+}
+
+function test4() {
+  activationListener =
+    function() {
+      ok(false, "Untrusted return keypress+ctrl should not activate a link");
+      test5();
+    }
+  dispatchReturn(testlink, true);
+  setTimeout(test5, 1000);
+}
+
+function test5() {
+  activationListener =
+    function() {
+      ok(true, "click() should activate a link");
+      test6();
+    }
+  testlink.click();
+}
+
+function test6() {
+  activationListener =
+    function() {
+      ok(true, "Untrusted DOMActivate should activate a link");
+      test7();
+    }
+  dispatchDOMActivate(testlink);
+}
+
+function test7() {
+  testlink.href = "javascript:opener.activationListener(); window.close();";
+  testlink.target = "_blank";
+  activationListener =
+    function() {
+      ok(true, "Click() should activate a link");
+      setTimeout(test9, 0);
+    }
+  testlink.click();
+}
+
+function test9() {
+  SimpleTest.finish();
+}
+
+addLoadEvent(test1);
+
+</script>
+</pre>
+</body>
+</html>
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -857,16 +857,17 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
     NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
     NS_INTERFACE_MAP_ENTRY(nsIEditorDocShell)
     NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
     NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
     NS_INTERFACE_MAP_ENTRY(nsIObserver)
     NS_INTERFACE_MAP_ENTRY(nsILoadContext)
     NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
     NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
+    NS_INTERFACE_MAP_ENTRY(nsILinkHandler_5_0)
     NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
 
 ///*****************************************************************************
 // nsDocShell::nsIInterfaceRequestor
 //*****************************************************************************   
 NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
 {
@@ -11372,67 +11373,86 @@ nsDocShell::SelectNone(void)
 
 // link handling
 
 class OnLinkClickEvent : public nsRunnable {
 public:
   OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
                    nsIURI* aURI,
                    const PRUnichar* aTargetSpec,
-                   nsIInputStream* aPostDataStream = 0, 
-                   nsIInputStream* aHeadersDataStream = 0);
+                   nsIInputStream* aPostDataStream, 
+                   nsIInputStream* aHeadersDataStream,
+                   PRBool aIsTrusted);
 
   NS_IMETHOD Run() {
     nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
     nsAutoPopupStatePusher popupStatePusher(window, mPopupState);
 
-    mHandler->OnLinkClickSync(mContent, mURI,
-                              mTargetSpec.get(), mPostDataStream,
-                              mHeadersDataStream,
-                              nsnull, nsnull);
+    nsCxPusher pusher;
+    if (mIsTrusted || pusher.Push(mContent)) {
+      mHandler->OnLinkClickSync(mContent, mURI,
+                                mTargetSpec.get(), mPostDataStream,
+                                mHeadersDataStream,
+                                nsnull, nsnull);
+    }
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsDocShell>     mHandler;
   nsCOMPtr<nsIURI>         mURI;
   nsString                 mTargetSpec;
   nsCOMPtr<nsIInputStream> mPostDataStream;
   nsCOMPtr<nsIInputStream> mHeadersDataStream;
   nsCOMPtr<nsIContent>     mContent;
   PopupControlState        mPopupState;
+  PRBool                   mIsTrusted;
 };
 
 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
                                    nsIContent *aContent,
                                    nsIURI* aURI,
                                    const PRUnichar* aTargetSpec,
                                    nsIInputStream* aPostDataStream,
-                                   nsIInputStream* aHeadersDataStream)
+                                   nsIInputStream* aHeadersDataStream,
+                                   PRBool aIsTrusted)
   : mHandler(aHandler)
   , mURI(aURI)
   , mTargetSpec(aTargetSpec)
   , mPostDataStream(aPostDataStream)
   , mHeadersDataStream(aHeadersDataStream)
   , mContent(aContent)
+  , mIsTrusted(aIsTrusted)
 {
   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
 
   mPopupState = window->GetPopupControlState();
 }
 
 //----------------------------------------
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClick(nsIContent* aContent,
                         nsIURI* aURI,
                         const PRUnichar* aTargetSpec,
                         nsIInputStream* aPostDataStream,
                         nsIInputStream* aHeadersDataStream)
 {
+  return OnLinkClick(aContent, aURI, aTargetSpec,
+                     aPostDataStream, aHeadersDataStream, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsDocShell::OnLinkClick(nsIContent* aContent,
+                        nsIURI* aURI,
+                        const PRUnichar* aTargetSpec,
+                        nsIInputStream* aPostDataStream,
+                        nsIInputStream* aHeadersDataStream,
+                        PRBool aIsTrusted)
+{
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
   if (!IsOKToLoadURI(aURI)) {
     return NS_OK;
   }
 
   if (aContent->IsEditable()) {
     return NS_OK;
@@ -11449,17 +11469,17 @@ nsDocShell::OnLinkClick(nsIContent* aCon
                                                linkNode, mIsAppTab, target);
   }
   
   if (NS_FAILED(rv))
     target = aTargetSpec;  
 
   nsCOMPtr<nsIRunnable> ev =
       new OnLinkClickEvent(this, aContent, aURI, target.get(),
-                           aPostDataStream, aHeadersDataStream);
+                           aPostDataStream, aHeadersDataStream, aIsTrusted);
   return NS_DispatchToCurrentThread(ev);
 }
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClickSync(nsIContent *aContent,
                             nsIURI* aURI,
                             const PRUnichar* aTargetSpec,
                             nsIInputStream* aPostDataStream,
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -179,17 +179,17 @@ class nsDocShell : public nsDocLoader,
                    public nsIRefreshURI,
                    public nsIWebProgressListener,
                    public nsIEditorDocShell,
                    public nsIWebPageDescriptor,
                    public nsIAuthPromptProvider,
                    public nsIObserver,
                    public nsILoadContext,
                    public nsIWebShellServices,
-                   public nsILinkHandler,
+                   public nsILinkHandler_5_0,
                    public nsIClipboardCommands
 {
     friend class nsDSURIContentListener;
 
 public:
     // Object Management
     nsDocShell();
 
@@ -242,16 +242,23 @@ public:
         nsIInputStream* aPostDataStream = 0,
         nsIInputStream* aHeadersDataStream = 0,
         nsIDocShell** aDocShell = 0,
         nsIRequest** aRequest = 0);
     NS_IMETHOD OnOverLink(nsIContent* aContent,
         nsIURI* aURI,
         const PRUnichar* aTargetSpec);
     NS_IMETHOD OnLeaveLink();
+    // nsILinkHandler_5_0
+    NS_IMETHOD OnLinkClick(nsIContent* aContent,
+        nsIURI* aURI,
+        const PRUnichar* aTargetSpec,
+        nsIInputStream* aPostDataStream,
+        nsIInputStream* aHeadersDataStream,
+        PRBool aIsTrusted);
 
     nsDocShellInfoLoadType ConvertLoadTypeToDocShellLoadInfo(PRUint32 aLoadType);
     PRUint32 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType);
 
     // nsIScriptGlobalObjectOwner methods
     virtual nsIScriptGlobalObject* GetScriptGlobalObject();
 
     // Restores a cached presentation from history (mLSHE).
--- a/docshell/base/nsILinkHandler.h
+++ b/docshell/base/nsILinkHandler.h
@@ -111,9 +111,39 @@ public:
   /**
    * Process the mouse leaving a link.
    */
   NS_IMETHOD OnLeaveLink() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsILinkHandler, NS_ILINKHANDLER_IID)
 
+
+// Interface ID for nsILinkHandler_5_0
+#define NS_ILINKHANDLER_5_0_IID \
+{ 0xd7e08edf, 0x52f0, 0x49f2, \
+  { 0x86, 0xbc, 0x4d, 0x3e, 0x9f, 0x20, 0x92, 0x38 } }
+class nsILinkHandler_5_0 : public nsILinkHandler
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILINKHANDLER_5_0_IID)
+  /**
+   * Process a click on a link.
+   *
+   * @param aContent the content for the frame that generated the trigger
+   * @param aURI a URI object that defines the destination for the link
+   * @param aTargetSpec indicates where the link is targeted (may be an empty
+   *        string)
+   * @param aPostDataStream the POST data to send
+   * @param aHeadersDataStream ???
+   * @param aIsTrusted PR_FALSE if the triggerer is an untrusted DOM event.
+   */
+  NS_IMETHOD OnLinkClick(nsIContent* aContent, 
+                         nsIURI* aURI,
+                         const PRUnichar* aTargetSpec,
+                         nsIInputStream* aPostDataStream,
+                         nsIInputStream* aHeadersDataStream,
+                         PRBool aIsTrusted) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsILinkHandler_5_0, NS_ILINKHANDLER_5_0_IID)
+
 #endif /* nsILinkHandler_h___ */
--- a/layout/forms/nsIsIndexFrame.cpp
+++ b/layout/forms/nsIsIndexFrame.cpp
@@ -424,17 +424,17 @@ nsIsIndexFrame::OnSubmit(nsPresContext* 
   }
   nsCOMPtr<nsIURI> uri;
   result = NS_NewURI(getter_AddRefs(uri), href,
                      flatDocCharset.get(), baseURI);
   if (NS_FAILED(result)) return result;
 
   // Now pretend we're triggering a link
   nsContentUtils::TriggerLink(mContent, aPresContext, uri,
-                              EmptyString(), PR_TRUE, PR_TRUE);
+                              EmptyString(), PR_TRUE, PR_TRUE, PR_TRUE);
   return result;
 }
 
 void nsIsIndexFrame::GetSubmitCharset(nsCString& oCharset)
 {
   oCharset.AssignLiteral("UTF-8"); // default to utf-8
   // XXX
   // We may want to get it from the HTML 4 Accept-Charset attribute first
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1621,17 +1621,17 @@ nsImageFrame::HandleEvent(nsPresContext*
           uri->SetSpec(spec);                
           
           PRBool clicked = PR_FALSE;
           if (aEvent->message == NS_MOUSE_BUTTON_UP) {
             *aEventStatus = nsEventStatus_eConsumeDoDefault; 
             clicked = PR_TRUE;
           }
           nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
-                                      clicked, PR_TRUE);
+                                      clicked, PR_TRUE, PR_TRUE);
         }
       }
     }
   }
 
   return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
 }