Bug 583288 (2/2) - Let forms using DELETE and PUT HTTP method when submitting. r=jst,sicking a2.0=sicking
authorMounir Lamouri <mounir.lamouri@gmail.com>
Fri, 10 Sep 2010 07:00:10 +0200
changeset 52359 d2b1c289404dc0d59ae59b0fc93711cbdcaffbbf
parent 52358 64ef0bd8c70d1b3984554b301a86b2139c532330
child 52360 5380df03991bd6c027ade28479f2dc24897bc46e
child 54823 198346fcc178bc40188ae10d9e06bae685f1b505
push idunknown
push userunknown
push dateunknown
reviewersjst, sicking
bugs583288
milestone2.0b6pre
Bug 583288 (2/2) - Let forms using DELETE and PUT HTTP method when submitting. r=jst,sicking a2.0=sicking
content/html/content/public/nsIForm.h
content/html/content/src/nsFormSubmissionConstants.h
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLFormElement.cpp
content/html/content/test/583288_redirect_server.sjs
content/html/content/test/583288_submit_server.sjs
content/html/content/test/Makefile.in
content/html/content/test/test_bug583288-1.html
content/html/content/test/test_bug583288-2.html
content/html/content/test/test_bug583288-3.html
content/html/content/test/test_bug585508.html
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
uriloader/base/nsDocLoader.cpp
uriloader/base/nsDocLoader.h
webshell/public/nsILinkHandler.h
--- a/content/html/content/public/nsIForm.h
+++ b/content/html/content/public/nsIForm.h
@@ -39,18 +39,20 @@
 
 #include "nsISupports.h"
 #include "nsAString.h"
 
 class nsIFormControl;
 class nsISimpleEnumerator;
 class nsIURI;
 
-#define NS_FORM_METHOD_GET  0
-#define NS_FORM_METHOD_POST 1
+#define NS_FORM_METHOD_GET     0
+#define NS_FORM_METHOD_POST    1
+#define NS_FORM_METHOD_PUT     2
+#define NS_FORM_METHOD_DELETE  3
 #define NS_FORM_ENCTYPE_URLENCODED 0
 #define NS_FORM_ENCTYPE_MULTIPART  1
 #define NS_FORM_ENCTYPE_TEXTPLAIN  2
 
 // IID for the nsIForm interface
 #define NS_IFORM_IID    \
 { 0x27f1ff6c, 0xeb78, 0x405b, \
  { 0xa6, 0xeb, 0xf0, 0xce, 0xa8, 0x30, 0x85, 0x58 } }
--- a/content/html/content/src/nsFormSubmissionConstants.h
+++ b/content/html/content/src/nsFormSubmissionConstants.h
@@ -34,18 +34,20 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsFormSubmissionConstants_h__
 #define nsFormSubmissionConstants_h__
 
 static const nsAttrValue::EnumTable kFormMethodTable[] = {
-  { "get", NS_FORM_METHOD_GET },
-  { "post", NS_FORM_METHOD_POST },
+  { "get",    NS_FORM_METHOD_GET },
+  { "post",   NS_FORM_METHOD_POST },
+  { "put",    NS_FORM_METHOD_PUT },
+  { "delete", NS_FORM_METHOD_DELETE },
   { 0 }
 };
 // Default method is 'get'.
 static const nsAttrValue::EnumTable* kFormDefaultMethod = &kFormMethodTable[0];
 
 static const nsAttrValue::EnumTable kFormEnctypeTable[] = {
   { "multipart/form-data", NS_FORM_ENCTYPE_MULTIPART },
   { "application/x-www-form-urlencoded", NS_FORM_ENCTYPE_URLENCODED },
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -503,16 +503,29 @@ public:
    * works for attributes in null namespace.
    *
    * @param aAttr      name of attribute.
    * @param aBaseAttr  name of base attribute.
    * @param aResult    result value [out]
    */
   NS_HIDDEN_(nsresult) GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, nsAString& aResult);
 
+  /**
+   * Helper method for NS_IMPL_ENUM_ATTR_DEFAULT_VALUE.
+   * Gets the enum value string of an attribute and using a default value if
+   * the attribute is missing or the string is an invalid enum value.
+   *
+   * @param aType     the name of the attribute.
+   * @param aDefault  the default value if the attribute is missing or invalid.
+   * @param aResult   string corresponding to the value [out].
+   */
+  NS_HIDDEN_(nsresult) GetEnumAttr(nsIAtom* aAttr,
+                                   const char* aDefault,
+                                   nsAString& aResult);
+
 protected:
   /**
    * Add/remove this element to the documents name cache
    */
   void AddToNameTable(nsIAtom* aName) {
     NS_ASSERTION(HasFlag(NODE_HAS_NAME), "Node lacking NODE_HAS_NAME flag");
     nsIDocument* doc = GetCurrentDoc();
     if (doc && !IsInAnonymousSubtree()) {
@@ -690,29 +703,16 @@ protected:
    * attributes in null namespace.
    *
    * @param aAttr    name of attribute.
    * @param aResult  result value [out]
    */
   NS_HIDDEN_(nsresult) GetURIListAttr(nsIAtom* aAttr, nsAString& aResult);
 
   /**
-   * Helper method for NS_IMPL_ENUM_ATTR_DEFAULT_VALUE.
-   * Gets the enum value string of an attribute and using a default value if
-   * the attribute is missing or the string is an invalid enum value.
-   *
-   * @param aType     the name of the attribute.
-   * @param aDefault  the default value if the attribute is missing or invalid.
-   * @param aResult   string corresponding to the value [out].
-   */
-  NS_HIDDEN_(nsresult) GetEnumAttr(nsIAtom* aAttr,
-                                   const char* aDefault,
-                                   nsAString& aResult);
-
-  /**
    * Locates the nsIEditor associated with this node.  In general this is
    * equivalent to GetEditorInternal(), but for designmode or contenteditable,
    * this may need to get an editor that's not actually on this element's
    * associated TextControlFrame.  This is used by the spellchecking routines
    * to get the editor affected by changing the spellcheck attribute on this
    * node.
    */
   virtual already_AddRefed<nsIEditor> GetAssociatedEditor();
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -873,21 +873,35 @@ nsHTMLFormElement::SubmitSubmission(nsFo
 
     nsAutoHandlingUserInputStatePusher userInpStatePusher(mSubmitInitiatedFromUserInput, PR_FALSE);
 
     nsCOMPtr<nsIInputStream> postDataStream;
     rv = aFormSubmission->GetEncodedSubmission(actionURI,
                                                getter_AddRefs(postDataStream));
     NS_ENSURE_SUBMIT_SUCCESS(rv);
 
+    nsAutoString method;
+    if (originatingElement &&
+        originatingElement->HasAttr(kNameSpaceID_None,
+                                    nsGkAtoms::formmethod)) {
+      if (!originatingElement->IsHTML()) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      static_cast<nsGenericHTMLElement*>(originatingElement)->
+        GetEnumAttr(nsGkAtoms::formmethod, kFormDefaultMethod->tag, method);
+    } else {
+      GetEnumAttr(nsGkAtoms::method, kFormDefaultMethod->tag, method);
+    }
+
     rv = linkHandler->OnLinkClickSync(this, actionURI,
                                       target.get(),
                                       postDataStream, nsnull,
                                       getter_AddRefs(docShell),
-                                      getter_AddRefs(mSubmittingRequest));
+                                      getter_AddRefs(mSubmittingRequest),
+                                      NS_LossyConvertUTF16toASCII(method).get());
     NS_ENSURE_SUBMIT_SUCCESS(rv);
   }
 
   // Even if the submit succeeds, it's possible for there to be no docshell
   // or request; for example, if it's to a named anchor within the same page
   // the submit will not really do anything.
   if (docShell) {
     // If the channel is pending, we have to listen for web progress.
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/583288_redirect_server.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response)
+{
+  // Redirect to another domain.
+  // Using 307 to keep the method.
+  response.setStatusLine(null, 307, "Temp");
+  response.setHeader("Location",
+                     "http://example.org:80/tests/content/html/content/test/583288_submit_server.sjs");
+}
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/583288_submit_server.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+  // Send the HTTP method.
+  response.write(request.method);
+}
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -207,12 +207,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug561634.html \
 		test_bug588683-1.html \
 		test_bug588683-2.html \
 		test_bug588683-3.html \
 		test_bug588683-4.html \
 		test_bug590353-1.html \
 		test_bug590353-2.html \
 		test_bug593689.html \
+		test_bug583288-1.html \
+		test_bug583288-2.html \
+		test_bug583288-3.html \
+		583288_submit_server.sjs \
+		583288_redirect_server.sjs \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug583288-1.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=583288
+-->
+<head>
+  <title>Test for Bug 583288</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload='runTests();'>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=583288">Mozilla Bug 583288</a>
+<p id="display"></p>
+<style>
+  iframe { width: 70px; height: 40px;}
+</style>
+<!--<div id="content" style="display: none">-->
+<div id="content">
+  <iframe name='get' id='get'></iframe>
+  <iframe name='post' id='post'></iframe>
+  <iframe name='put' id='put'></iframe>
+  <iframe name='del' id='del'></iframe>
+  <iframe name='get2' id='get2'></iframe>
+  <iframe name='post2' id='post2'></iframe>
+  <iframe name='put2' id='put2'></iframe>
+  <iframe name='del2' id='del2'></iframe>
+
+  <form action="583288_submit_server.sjs" target='get' method='get'>
+  </form>
+  <form action="583288_submit_server.sjs" target='post' method='post'>
+  </form>
+  <form action="583288_submit_server.sjs" target='put' method='put'>
+  </form>
+  <form action="583288_submit_server.sjs" target='del' method='delete'>
+  </form>
+
+  <form action="583288_submit_server.sjs">
+    <input type='submit' id='iget' value='get' formtarget='get2' formmethod='get'>
+  </form>
+  <form action="583288_submit_server.sjs">
+    <input type='submit' id='ipost' value='post' formtarget='post2' formmethod='post'>
+  </form>
+  <form action="583288_submit_server.sjs">
+    <input type='submit' id='iput' value='put' formtarget='put2' formmethod='put'>
+  </form>
+  <form action="583288_submit_server.sjs">
+    <input type='submit' id='idel' value='delete' formtarget='del2' formmethod='delete'>
+  </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 583288 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var gTestResults = {
+  get:   "GET",
+  get2:  "GET",
+  post:  "POST",
+  post2: "POST",
+  put:   "PUT",
+  put2:  "PUT",
+  del:   "DELETE",
+  del2:  "DELETE",
+};
+
+var gPendingLoad = 0; // Has to be set after depending on the frames number.
+
+function runTests()
+{
+  // We add a load event for the frames which will be called when the forms
+  // will be submitted.
+  var frames = [ document.getElementById('get'),
+                 document.getElementById('get2'),
+                 document.getElementById('post'),
+                 document.getElementById('post2'),
+                 document.getElementById('put'),
+                 document.getElementById('put2'),
+                 document.getElementById('del'),
+                 document.getElementById('del2'),
+               ];
+  gPendingLoad = frames.length;
+
+  for (var i=0; i<frames.length; ++i) {
+    frames[i].setAttribute('onload', "frameLoaded(this);");
+  }
+
+  // The four first forms can be submitted with .submit().
+  for (var i=0; i<4; ++i) {
+    document.forms[i].submit();
+  }
+
+  /**
+   * We are going to focus each element before interacting with them so we are
+   * sure they will be visible in the iframe.
+   * Considering we are using synthesizeKey that may be not required.
+   *
+   * Focusing the first element (id='iget') is launching the tests.
+   */
+  document.getElementById('iget').addEventListener('focus', function(aEvent) {
+    aEvent.target.removeEventListener('focus', arguments.callee, false);
+    synthesizeKey("VK_RETURN", {});
+    document.getElementById('ipost').focus();
+  }, false);
+
+  document.getElementById('ipost').addEventListener('focus', function(aEvent) {
+    aEvent.target.removeEventListener('focus', arguments.callee, false);
+    synthesizeKey("VK_RETURN", {});
+    document.getElementById('iput').focus();
+  }, false);
+
+  document.getElementById('iput').addEventListener('focus', function(aEvent) {
+    aEvent.target.removeEventListener('focus', arguments.callee, false);
+    synthesizeKey("VK_RETURN", {});
+    document.getElementById('idel').focus();
+  }, false);
+
+  document.getElementById('idel').addEventListener('focus', function(aEvent) {
+    aEvent.target.removeEventListener('focus', arguments.callee, false);
+    synthesizeKey("VK_RETURN", {});
+  }, false);
+
+  document.getElementById('iget').focus();
+}
+
+function frameLoaded(aFrame) {
+  // Check if formaction/action has the correct behavior.
+  is(aFrame.contentDocument.documentElement.textContent, gTestResults[aFrame.name],
+     "the method used during the form submission should be " +
+     gTestResults[aFrame.name]);
+
+  if (--gPendingLoad == 0) {
+    SimpleTest.finish();
+  }
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug583288-2.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=583288
+-->
+<head>
+  <title>Test for Bug 583288</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload='runTests();'>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=583288">Mozilla Bug 583288</a>
+<p id="display"></p>
+<style>
+  iframe { width: 70px; height: 40px;}
+</style>
+<!--<div id="content" style="display: none">-->
+<div id="content">
+  <iframe name='get' id='get'></iframe>
+  <iframe name='post' id='post'></iframe>
+  <iframe name='put' id='put'></iframe>
+  <iframe name='del' id='del'></iframe>
+
+  <form action="http://example.org:80/tests/content/html/content/test/583288_submit_server.sjs" target='get' method='get'>
+  </form>
+  <form action="http://example.org:80/tests/content/html/content/test/583288_submit_server.sjs" target='post' method='post'>
+  </form>
+  <form action="http://example.org:80/tests/content/html/content/test/583288_submit_server.sjs" target='put' method='put'>
+  </form>
+  <form action="http://example.org:80/tests/content/html/content/test/583288_submit_server.sjs" target='del' method='delete'>
+  </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 583288 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var gLoaded = 0;
+
+function runTests()
+{
+  // We add a load event for the frames which will be called when the forms
+  // will be submitted.
+  var frames = [ document.getElementById('get'),
+                 document.getElementById('post'),
+                 document.getElementById('put'),
+                 document.getElementById('del'),
+               ];
+
+  for (var i=0; i<2; ++i) {
+    frames[i].setAttribute('onload', "frameShouldLoad();");
+  }
+  for (var i=2; i<4; ++i) {
+    frames[i].setAttribute('onload', "frameShouldNotLoad();");
+  }
+
+  // The four forms can be submitted with .submit().
+  // Submitting those which should be blocked so we can considering if the the
+  // last ones are submitted, that means the firsts were blocked.
+  for (var i=3; i>=0; --i) {
+    document.forms[i].submit();
+  }
+
+  // After the two first succefull submissions, we are going to stop the test.
+  // The expected successfull submissions have been requested at the end so we
+  // expect them at the end. So if the two firsts ones didn't show up, we are
+  // assuming they have been blocked.
+  // Unfortunately, it doesn't sound like there is a better way to test that.
+  // The worst thing that can happen here is to have a random green (if the
+  // first two submissions were not blocked but came after the last two).
+}
+
+function frameShouldLoad() {
+  ok(true, "The form submission should succeed.");
+  if (++gLoaded == 2) {
+    finished();
+  }
+}
+
+function frameShouldNotLoad() {
+  ok(false, "The form submission should have been blocked");
+  if (++gLoaded == 2) {
+    finished();
+  }
+}
+
+function finished()
+{
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug583288-3.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=583288
+-->
+<head>
+  <title>Test for Bug 583288</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload='runTests();'>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=583288">Mozilla Bug 583288</a>
+<p id="display"></p>
+<style>
+  iframe { width: 70px; height: 40px;}
+</style>
+<!--<div id="content" style="display: none">-->
+<div id="content">
+  <iframe name='get' id='get'></iframe>
+  <iframe name='post' id='post'></iframe>
+  <iframe name='put' id='put'></iframe>
+  <iframe name='del' id='del'></iframe>
+
+  <form action="583288_redirect_server.sjs" target='get' method='get'>
+  </form>
+  <form action="583288_redirect_server.sjs" target='post' method='post'>
+  </form>
+  <form action="583288_redirect_server.sjs" target='put' method='put'>
+  </form>
+  <form action="583288_redirect_server.sjs" target='del' method='delete'>
+  </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 583288 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var gTestResults = {
+  get:   "GET",
+  post:  "POST",
+  put:   "",
+  del:   "",
+};
+
+var gPendingLoad = 0; // Has to be set after depending on the frames number.
+
+function runTests()
+{
+  // We add a load event for the frames which will be called when the forms
+  // will be submitted.
+  var frames = [ document.getElementById('get'),
+                 document.getElementById('post'),
+                 document.getElementById('put'),
+                 document.getElementById('del'),
+               ];
+  gPendingLoad = frames.length;
+
+  for (var i=0; i<gPendingLoad; ++i) {
+    frames[i].setAttribute('onload', "frameLoaded(this);");
+  }
+
+  // The four forms can be submitted with .submit().
+  for (var i=0; i<4; ++i) {
+    document.forms[i].submit();
+  }
+}
+
+function frameLoaded(aFrame) {
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+  is(aFrame.contentDocument.documentElement.textContent, gTestResults[aFrame.name],
+     "cross-origin submission with redirection should work");
+
+  if (--gPendingLoad == 0) {
+    SimpleTest.finish();
+  }
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/content/test/test_bug585508.html
+++ b/content/html/content/test/test_bug585508.html
@@ -25,21 +25,19 @@ var enctypeTestData = [
   // Invalid values.
   [ "", " ", "foo", "multipart/foo" ]
 ];
 
 var methodTestData = [
   // Default value.
   [ "get" ],
   // Valid values.
-  [ "get", "post" ],
+  [ "get", "post", "put", "delete" ],
   // Invalid values.
   [ "", " ", "foo" ],
-  // TODO values, see bug 583289 and bug 583288.
-  [ "delete", "put" ],
 ];
 
 function checkAttribute(form, attrName, idlName, data)
 {
   is(form.getAttribute(attrName), null,
      "By default " + attrName + " content attribute should be null");
   is(form[idlName], data[0][0],
      "By default " + idlName + " IDL attribute should be equal to " +
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1407,17 +1407,18 @@ nsDocShell::LoadURI(nsIURI * aURI,
                         target.get(),
                         nsnull,         // No type hint
                         postStream,
                         headersStream,
                         loadType,
                         nsnull,         // No SHEntry
                         aFirstParty,
                         nsnull,         // No nsIDocShell
-                        nsnull);        // No nsIRequest
+                        nsnull,         // No nsIRequest
+                        nsnull);        // Use default HTTP method
 }
 
 NS_IMETHODIMP
 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
                        const nsACString &aContentType,
                        const nsACString &aContentCharset,
                        nsIDocShellLoadInfo * aLoadInfo)
 {
@@ -4090,17 +4091,17 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, 
 
     nsCOMPtr<nsIURI> errorPageURI;
     nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return InternalLoad(errorPageURI, nsnull, nsnull,
                         INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nsnull, nsnull,
                         nsnull, nsnull, LOAD_ERROR_PAGE,
-                        nsnull, PR_TRUE, nsnull, nsnull);
+                        nsnull, PR_TRUE, nsnull, nsnull, nsnull);
 }
 
 
 NS_IMETHODIMP
 nsDocShell::Reload(PRUint32 aReloadFlags)
 {
     if (!IsNavigationAllowed()) {
       return NS_OK; // JS may not handle returning of an error code
@@ -4154,17 +4155,18 @@ nsDocShell::Reload(PRUint32 aReloadFlags
                           nsnull,         // No window target
                           NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
                           nsnull,         // No post data
                           nsnull,         // No headers data
                           loadType,       // Load type
                           nsnull,         // No SHEntry
                           PR_TRUE,
                           nsnull,         // No nsIDocShell
-                          nsnull);        // No nsIRequest
+                          nsnull,         // No nsIRequest
+                          nsnull);        // Use default HTTP method
     }
     
 
     return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::Stop(PRUint32 aStopFlags)
@@ -5864,32 +5866,63 @@ nsDocShell::OnStateChange(nsIWebProgress
 NS_IMETHODIMP
 nsDocShell::OnLocationChange(nsIWebProgress * aProgress,
                              nsIRequest * aRequest, nsIURI * aURI)
 {
     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
     return NS_OK;
 }
 
-void
+nsresult
 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
                                   nsIChannel* aNewChannel,
                                   PRUint32 aRedirectFlags,
                                   PRUint32 aStateFlags)
 {
     NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
                  "Calling OnRedirectStateChange when there is no redirect");
     if (!(aStateFlags & STATE_IS_DOCUMENT))
-        return; // not a toplevel document
+        return NS_OK; // not a toplevel document
 
     nsCOMPtr<nsIURI> oldURI, newURI;
     aOldChannel->GetURI(getter_AddRefs(oldURI));
     aNewChannel->GetURI(getter_AddRefs(newURI));
     if (!oldURI || !newURI) {
-        return;
+        return NS_OK;
+    }
+
+    // HTTP channel with unsafe methods should not be redirected to a cross-domain.
+    if (!ChannelIsSafeMethod(aNewChannel)) {
+        // This code is very similar to the code of nsSameOriginChecker in
+        // nsContentUtils but we can't use nsSameOriginChecker because it
+        // needs to use a channel callback (which we already use).
+        // If nsSameOriginChecker happens to not use a channel callback
+        // anymore, this code would be a good candidate for refactoring.
+        nsCOMPtr<nsIPrincipal> oldPrincipal;
+        nsresult rv;
+
+        nsCOMPtr<nsIScriptSecurityManager> secMan =
+            do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+
+        rv = secMan->GetChannelPrincipal(aOldChannel,
+                                         getter_AddRefs(oldPrincipal));
+        NS_ENSURE_SUCCESS(rv, NS_OK);
+
+        NS_ASSERTION(oldPrincipal, "oldPrincipal should not be null!");
+
+        nsCOMPtr<nsIURI> newOriginalURI;
+        aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
+
+        rv = oldPrincipal->CheckMayLoad(newURI, PR_FALSE);
+        if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
+            rv = oldPrincipal->CheckMayLoad(newOriginalURI, PR_FALSE);
+        }
+
+        // The requested tried to be redirected, we have to cancel it.
+        NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Below a URI visit is saved (see AddURIVisit method doc).
     // The visit chain looks something like:
     //   ...
     //   Site N - 1
     //                =>  Site N
     //   (redirect to =>) Site N + 1 (we are here!)
@@ -5930,16 +5963,18 @@ nsDocShell::OnRedirectStateChange(nsICha
         appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI));
     }
 
     if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) && 
         mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
         mLoadType = LOAD_NORMAL_REPLACE;
         SetHistoryEntry(&mLSHE, nsnull);
     }
+
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::OnStatusChange(nsIWebProgress * aWebProgress,
                            nsIRequest * aRequest,
                            nsresult aStatus, const PRUnichar * aMessage)
 {
     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
@@ -6301,17 +6336,18 @@ nsDocShell::EndPageLoad(nsIWebProgress *
                              nsnull,                            // No window target
                              nsnull,                            // No type hint
                              inputStream,                       // Post data stream
                              nsnull,                            // No headers stream
                              LOAD_RELOAD_BYPASS_PROXY_AND_CACHE,// Load type
                              nsnull,                            // No SHEntry
                              PR_TRUE,                           // first party site
                              nsnull,                            // No nsIDocShell
-                             nsnull);                           // No nsIRequest
+                             nsnull,                            // No nsIRequest
+                             nsnull);                           // Use default HTTP method
             }
             else {
                 DisplayLoadError(aStatus, url, nsnull, aChannel);
             }
         }
   } // if we have a host
 
   return NS_OK;
@@ -7745,17 +7781,17 @@ public:
             mTypeHint = aTypeHint;
         }
     }
     
     NS_IMETHOD Run() {
         return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
                                        nsnull, mTypeHint.get(),
                                        mPostData, mHeadersData, mLoadType,
-                                       mSHEntry, mFirstParty, nsnull, nsnull);
+                                       mSHEntry, mFirstParty, nsnull, nsnull, nsnull);
     }
 
 private:
 
     // Use IDL strings so .get() returns null by default
     nsXPIDLString mWindowTarget;
     nsXPIDLCString mTypeHint;
 
@@ -7779,17 +7815,18 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                          const PRUnichar *aWindowTarget,
                          const char* aTypeHint,
                          nsIInputStream * aPostData,
                          nsIInputStream * aHeadersData,
                          PRUint32 aLoadType,
                          nsISHEntry * aSHEntry,
                          PRBool aFirstParty,
                          nsIDocShell** aDocShell,
-                         nsIRequest** aRequest)
+                         nsIRequest** aRequest,
+                         const char* aHttpMethod)
 {
     nsresult rv = NS_OK;
 
 #ifdef PR_LOGGING
     if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
         nsCAutoString spec;
         if (aURI)
             aURI->GetSpec(spec);
@@ -7981,17 +8018,18 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                                               nsnull,         // No window target
                                               aTypeHint,
                                               aPostData,
                                               aHeadersData,
                                               aLoadType,
                                               aSHEntry,
                                               aFirstParty,
                                               aDocShell,
-                                              aRequest);
+                                              aRequest,
+                                              aHttpMethod);
             if (rv == NS_ERROR_NO_CONTENT) {
                 // XXXbz except we never reach this code!
                 if (isNewWindow) {
                     //
                     // At this point, a new window has been created, but the
                     // URI did not have any data associated with it...
                     //
                     // So, the best we can do, is to tear down the new window
@@ -8416,17 +8454,18 @@ nsDocShell::InternalLoad(nsIURI * aURI,
 
     nsCOMPtr<nsIRequest> req;
     rv = DoURILoad(aURI, aReferrer,
                    !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
                    owner, aTypeHint, aPostData, aHeadersData, aFirstParty,
                    aDocShell, getter_AddRefs(req),
                    (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
-                   (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0);
+                   (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
+                   aHttpMethod);
     if (req && aRequest)
         NS_ADDREF(*aRequest = req);
 
     if (NS_FAILED(rv)) {
         nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
         DisplayLoadError(rv, aURI, nsnull, chan);
     }
 
@@ -8497,17 +8536,18 @@ nsDocShell::DoURILoad(nsIURI * aURI,
                       const char * aTypeHint,
                       nsIInputStream * aPostData,
                       nsIInputStream * aHeadersData,
                       PRBool aFirstParty,
                       nsIDocShell ** aDocShell,
                       nsIRequest ** aRequest,
                       PRBool aIsNewWindowTarget,
                       PRBool aBypassClassifier,
-                      PRBool aForceAllowCookies)
+                      PRBool aForceAllowCookies,
+                      const char* aHttpMethod)
 {
     nsresult rv;
     nsCOMPtr<nsIURILoader> uriLoader;
 
     uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
     if (NS_FAILED(rv)) return rv;
 
     nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
@@ -8680,16 +8720,30 @@ nsDocShell::DoURILoad(nsIURI * aURI,
         if (aHeadersData) {
             rv = AddHeadersToChannel(aHeadersData, httpChannel);
         }
         // Set the referrer explicitly
         if (aReferrerURI && aSendReferrer) {
             // Referrer is currenly only set for link clicks here.
             httpChannel->SetReferrer(aReferrerURI);
         }
+
+        // If a specific HTTP method has been requested, set it.
+        if (aHttpMethod) {
+            // Tell the cache it _has_ to open a cache entry.
+            PRUint32 loadFlags;
+            if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
+              channel->SetLoadFlags(loadFlags | nsICachingChannel::FORCE_OPEN_CACHE_ENTRY);
+            }
+
+            // The method name have to be correct.
+            // Otherwise SetRequestMethod will return a failure.
+            rv = httpChannel->SetRequestMethod(nsDependentCString(aHttpMethod));
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
     }
     //
     // Set the owner of the channel, but only for channels that can't
     // provide their own security context.
     //
     // XXX: Is seems wrong that the owner is ignored - even if one is
     //      supplied) unless the URI is javascript or data or about:blank.
     // XXX: If this is ever changed, check all callers for what owners they're
@@ -8730,16 +8784,24 @@ nsDocShell::DoURILoad(nsIURI * aURI,
         if (secMan &&
             NS_SUCCEEDED(secMan->IsSystemPrincipal(ownerPrincipal,
                                                    &isSystem)) &&
             !isSystem) {
             channel->SetOwner(aOwner);
         }
     }
 
+    // If a specific HTTP channel has been set and it is not a safe method,
+    // we should prevent cross-origin requests.
+    if (aHttpMethod && ownerPrincipal && !ChannelIsSafeMethod(channel)) {
+        if (NS_FAILED(ownerPrincipal->CheckMayLoad(aURI, PR_FALSE))) {
+            return NS_OK;
+        }
+    }
+
     nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
     if (scriptChannel) {
         // Allow execution against our context if the principals match
         scriptChannel->
             SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
     }
 
     if (aIsNewWindowTarget) {
@@ -9941,17 +10003,18 @@ nsDocShell::LoadHistoryEntry(nsISHEntry 
                       nsnull,            // No window target
                       contentType.get(), // Type hint
                       postData,          // Post data stream
                       nsnull,            // No headers stream
                       aLoadType,         // Load type
                       aEntry,            // SHEntry
                       PR_TRUE,
                       nsnull,            // No nsIDocShell
-                      nsnull);           // No nsIRequest
+                      nsnull,            // No nsIRequest
+                      nsnull);           // Use default HTTP method
     return rv;
 }
 
 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(PRBool* aShould)
 {
     *aShould = PR_FALSE;
     if (mOSHE) {
         // Don't capture historystate and save it in history
@@ -10354,29 +10417,45 @@ NS_IMETHODIMP nsDocShell::GetHasEditingS
 NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
 {
   nsresult rv = EnsureEditorData();
   if (NS_FAILED(rv)) return rv;
 
   return mEditorData->MakeEditable(inWaitForUriLoad);
 }
 
+/* static */
 bool
 nsDocShell::ChannelIsPost(nsIChannel* aChannel)
 {
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
     if (!httpChannel) {
         return false;
     }
 
     nsCAutoString method;
     httpChannel->GetRequestMethod(method);
     return method.Equals("POST");
 }
 
+/* static */
+bool
+nsDocShell::ChannelIsSafeMethod(nsIChannel* aChannel)
+{
+    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
+    if (!httpChannel) {
+        return false;
+    }
+
+    nsCAutoString method;
+    httpChannel->GetRequestMethod(method);
+    return method.Equals("GET") || method.Equals("POST") ||
+           method.Equals("HEAD");
+}
+
 void
 nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
                              nsIURI** aURI,
                              PRUint32* aChannelRedirectFlags)
 {
     nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
     if (!props) {
         return;
@@ -11297,17 +11376,18 @@ nsDocShell::OnLinkClick(nsIContent* aCon
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClickSync(nsIContent *aContent,
                             nsIURI* aURI,
                             const PRUnichar* aTargetSpec,
                             nsIInputStream* aPostDataStream,
                             nsIInputStream* aHeadersDataStream,
                             nsIDocShell** aDocShell,
-                            nsIRequest** aRequest)
+                            nsIRequest** aRequest,
+                            const char* aHttpMethod)
 {
   // Initialize the DocShell / Request
   if (aDocShell) {
     *aDocShell = nsnull;
   }
   if (aRequest) {
     *aRequest = nsnull;
   }
@@ -11373,17 +11453,18 @@ nsDocShell::OnLinkClickSync(nsIContent *
                              target.get(),              // Window target
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
                              aPostDataStream,           // Post data stream
                              aHeadersDataStream,        // Headers stream
                              LOAD_LINK,                 // Load type
                              nsnull,                    // No SHEntry
                              PR_TRUE,                   // first party site
                              aDocShell,                 // DocShell out-param
-                             aRequest);                 // Request out-param
+                             aRequest,                  // Request out-param
+                             aHttpMethod);              // HTTP Method
   if (NS_SUCCEEDED(rv)) {
     DispatchPings(aContent, referer);
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::OnOverLink(nsIContent* aContent,
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -238,17 +238,18 @@ public:
         nsIInputStream* aPostDataStream = 0,
         nsIInputStream* aHeadersDataStream = 0);
     NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
         nsIURI* aURI,
         const PRUnichar* aTargetSpec,
         nsIInputStream* aPostDataStream = 0,
         nsIInputStream* aHeadersDataStream = 0,
         nsIDocShell** aDocShell = 0,
-        nsIRequest** aRequest = 0);
+        nsIRequest** aRequest = 0,
+        const char* aHttpMethod = 0);
     NS_IMETHOD OnOverLink(nsIContent* aContent,
         nsIURI* aURI,
         const PRUnichar* aTargetSpec);
     NS_IMETHOD OnLeaveLink();
 
     nsDocShellInfoLoadType ConvertLoadTypeToDocShellLoadInfo(PRUint32 aLoadType);
     PRUint32 ConvertDocShellLoadInfoToLoadType(nsDocShellInfoLoadType aDocShellLoadType);
 
@@ -320,17 +321,18 @@ protected:
                                const char * aTypeHint,
                                nsIInputStream * aPostData,
                                nsIInputStream * aHeadersData,
                                PRBool firstParty,
                                nsIDocShell ** aDocShell,
                                nsIRequest ** aRequest,
                                PRBool aIsNewWindowTarget,
                                PRBool aBypassClassifier,
-                               PRBool aForceAllowCookies);
+                               PRBool aForceAllowCookies,
+                               const char* aHttpMethod);
     NS_IMETHOD AddHeadersToChannel(nsIInputStream * aHeadersData, 
                                   nsIChannel * aChannel);
     virtual nsresult DoChannelLoad(nsIChannel * aChannel,
                                    nsIURILoader * aURILoader,
                                    PRBool aBypassClassifier);
 
     nsresult ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
                             PRUint32 aLoadType, PRBool * aDoHashchange);
@@ -429,30 +431,40 @@ protected:
     // is passed to the callback.
     static nsresult WalkHistoryEntries(nsISHEntry *aRootEntry,
                                        nsDocShell *aRootShell,
                                        WalkHistoryEntriesFunc aCallback,
                                        void *aData);
 
     // overridden from nsDocLoader, this provides more information than the
     // normal OnStateChange with flags STATE_REDIRECTING
-    virtual void OnRedirectStateChange(nsIChannel* aOldChannel,
-                                       nsIChannel* aNewChannel,
-                                       PRUint32 aRedirectFlags,
-                                       PRUint32 aStateFlags);
+    virtual nsresult OnRedirectStateChange(nsIChannel* aOldChannel,
+                                           nsIChannel* aNewChannel,
+                                           PRUint32 aRedirectFlags,
+                                           PRUint32 aStateFlags);
 
     /**
      * Helper function that determines if channel is an HTTP POST.
      *
      * @param aChannel
      *        The channel to test
      *
      * @return True iff channel is an HTTP post.
      */
-    bool ChannelIsPost(nsIChannel* aChannel);
+     static bool ChannelIsPost(nsIChannel* aChannel);
+
+     /**
+      * Helper function that determines if the HTTP channel has a safe method
+      *
+      * @param aChannel The channel to test
+      *
+      * @return Whether the channel has a safe HTTP method.
+      * @note Will return false if the channel isn't an HTTP channel.
+      */
+     static bool ChannelIsSafeMethod(nsIChannel* aChannel);
 
     /**
      * Helper function that finds the last URI and its transition flags for a
      * channel.
      *
      * This method first checks the channel's property bag to see if previous
      * info has been saved.  If not, it gives back the referrer of the channel.
      *
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -66,17 +66,17 @@ interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 
-[scriptable, uuid(74470127-87eb-4f79-8293-1616fe9cb689)]
+[scriptable, uuid(0e1e1ee5-5baa-4e27-98af-3197d70d0304)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -150,30 +150,35 @@ interface nsIDocShell : nsISupports
    * @param aWindowTarget   - Window target for the load.
    * @param aTypeHint       - A hint as to the content-type of the resulting
    *                          data.  May be null or empty if no hint.
    * @param aPostDataStream - Post data stream (if POSTing)
    * @param aHeadersStream  - Stream containing "extra" request headers...
    * @param aLoadFlags      - Flags to modify load behaviour. Flags are defined
    *                          in nsIWebNavigation.
    * @param aSHEntry        - Active Session History entry (if loading from SH)
+   * @param firstParty      - 
+   * @param aDocShell       - 
+   * @param aRequest        - 
+   * @param aHttpMethod     - Force the HTTP channel to use a specific HTTP method
    */
   [noscript]void internalLoad(in nsIURI aURI,
                               in nsIURI aReferrer,
                               in nsISupports aOwner,
                               in PRUint32 aFlags,
                               in wstring aWindowTarget,
                               in string aTypeHint,
                               in nsIInputStream aPostDataStream,
                               in nsIInputStream aHeadersStream,
                               in unsigned long aLoadFlags,
                               in nsISHEntry aSHEntry,
                               in boolean firstParty,
                               out nsIDocShell aDocShell,
-                              out nsIRequest aRequest);
+                              out nsIRequest aRequest,
+                              in string aHttpMethod);
 
   /**
    * Do either a history.pushState() or history.replaceState() operation,
    * depending on the value of aReplace.
    */
   void addState(in nsIVariant aData, in DOMString aTitle,
                 in DOMString aURL, in boolean aReplace);
 
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -1604,17 +1604,19 @@ NS_IMETHODIMP nsDocLoader::AsyncOnChanne
       stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
 
 #if defined(DEBUG)
       nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
       NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
 #endif /* DEBUG */
     }
 
-    OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
+    nsresult rv = OnRedirectStateChange(aOldChannel, aNewChannel, aFlags,
+                                        stateFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
     FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
   }
 
   cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 /*
--- a/uriloader/base/nsDocLoader.h
+++ b/uriloader/base/nsDocLoader.h
@@ -64,21 +64,21 @@
 struct nsRequestInfo;
 struct nsListenerInfo;
 
 /****************************************************************************
  * nsDocLoader implementation...
  ****************************************************************************/
 
 #define NS_THIS_DOCLOADER_IMPL_CID                    \
- { /* b4ec8387-98aa-4c08-93b6-6d23069c06f2 */         \
-     0xb4ec8387,                                      \
-     0x98aa,                                          \
-     0x4c08,                                          \
-     {0x93, 0xb6, 0x6d, 0x23, 0x06, 0x9c, 0x06, 0xf2} \
+ {                                                    \
+     0x306cae1f,                                      \
+     0x90ee,                                          \
+     0x4e51,                                          \
+     {0xaf, 0x27, 0x9c, 0x13, 0xde, 0xba, 0x91, 0xb6} \
  }
 
 class nsDocLoader : public nsIDocumentLoader, 
                     public nsIRequestObserver,
                     public nsSupportsWeakReference,
                     public nsIProgressEventSink,
                     public nsIWebProgress,
                     public nsIInterfaceRequestor,
@@ -174,20 +174,22 @@ protected:
                             PRBool aSameURI);
 
     // this function is overridden by the docshell, it is provided so that we
     // can pass more information about redirect state (the normal OnStateChange
     // doesn't get the new channel).
     // @param aRedirectFlags The flags being sent to OnStateChange that
     //                       indicate the type of redirect.
     // @param aStateFlags    The channel flags normally sent to OnStateChange.
-    virtual void OnRedirectStateChange(nsIChannel* aOldChannel,
-                                       nsIChannel* aNewChannel,
-                                       PRUint32 aRedirectFlags,
-                                       PRUint32 aStateFlags) {}
+    //
+    // @return Something else than NS_OK if the redirection should be cancelled.
+    virtual nsresult OnRedirectStateChange(nsIChannel* aOldChannel,
+                                           nsIChannel* aNewChannel,
+                                           PRUint32 aRedirectFlags,
+                                           PRUint32 aStateFlags) { return NS_OK; }
 
     void doStartDocumentLoad();
     void doStartURLLoad(nsIRequest *request);
     void doStopURLLoad(nsIRequest *request, nsresult aStatus);
     void doStopDocumentLoad(nsIRequest *request, nsresult aStatus);
 
     // Inform a parent docloader that aChild is about to call its onload
     // handler.
--- a/webshell/public/nsILinkHandler.h
+++ b/webshell/public/nsILinkHandler.h
@@ -43,17 +43,17 @@
 class nsIInputStream;
 class nsIDocShell;
 class nsIRequest;
 class nsString;
 class nsGUIEvent;
 
 // Interface ID for nsILinkHandler
 #define NS_ILINKHANDLER_IID \
- { 0x71627c30, 0xd3c5, 0x4ad0,{0xb5, 0x33, 0x6e, 0x01, 0x91, 0xf2, 0x79, 0x32}}
+ { 0x1fa72627, 0x646b, 0x4573,{0xb5, 0xc8, 0xb4, 0x65, 0xc6, 0x78, 0xd4, 0x9d}}
 
 /**
  * Interface used for handling clicks on links
  */
 class nsILinkHandler : public nsISupports {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILINKHANDLER_IID)
 
@@ -82,24 +82,26 @@ public:
    * @param aContent the content for the frame that generated the trigger
    * @param aURI a URI obect 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 aDocShell (out-param) the DocShell that the request was opened on
    * @param aRequest the request that was opened
+   * @param aHttpMethod forces the http channel to use a specific method
    */
   NS_IMETHOD OnLinkClickSync(nsIContent* aContent, 
                              nsIURI* aURI,
                              const PRUnichar* aTargetSpec,
                              nsIInputStream* aPostDataStream = 0,
                              nsIInputStream* aHeadersDataStream = 0,
                              nsIDocShell** aDocShell = 0,
-                             nsIRequest** aRequest = 0) = 0;
+                             nsIRequest** aRequest = 0,
+                             const char* aHttpMethod = 0) = 0;
 
   /**
    * Process a mouse-over a link.
    *
    * @param aContent the linked content.
    * @param aURI an URI object that defines the destination for the link
    * @param aTargetSpec indicates where the link is targeted (it may be an empty
    *        string)