Bug 482659. Give about:blank documents the base URI of the document that did the load. r=dcamp, sr=jst
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -154,17 +154,17 @@ nsFrameLoader::LoadFrame()
const char *charset = doc_charset.IsEmpty() ? nsnull : doc_charset.get();
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), src, charset, base_uri);
// If the URI was malformed, try to recover by loading about:blank.
if (rv == NS_ERROR_MALFORMED_URI) {
rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("about:blank"),
- charset);
+ charset, base_uri);
}
NS_ENSURE_SUCCESS(rv, rv);
return LoadURI(uri);
}
NS_IMETHODIMP
nsFrameLoader::LoadURI(nsIURI* aURI)
--- a/content/html/document/test/Makefile.in
+++ b/content/html/document/test/Makefile.in
@@ -84,12 +84,13 @@ include $(topsrcdir)/config/rules.mk
test_bug446483.html \
bug446483-iframe.html \
test_bug448564.html \
bug448564-iframe-1.html \
bug448564-iframe-2.html \
bug448564-iframe-3.html \
bug448564-echo.sjs \
bug448564-submit.js \
+ test_bug482659.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/document/test/test_bug482659.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=482659
+-->
+<head>
+ <title>Test for Bug 482659</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.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=482659">Mozilla Bug 482659</a>
+<p id="display">
+ <iframe></iframe>
+ <iframe src="about:blank"></iframe>
+ <iframe></iframe>
+ <iframe src="about:blank"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 482659 **/
+SimpleTest.waitForExplicitFinish()
+
+function testFrame(num) {
+ is(window.frames[num].document.baseURI, document.baseURI,
+ "Unexpected base URI in frame " + num);
+ is(window.frames[num].document.documentURI, "about:blank",
+ "Unexpected document URI in frame " + num);
+}
+
+function appendScript(doc) {
+ var s = doc.createElement("script");
+ s.textContent = "document.write('executed'); document.close()";
+ doc.body.appendChild(s);
+}
+
+function verifyScriptRan(num) {
+ is(window.frames[num].document.documentElement.textContent, "executed",
+ "write didn't happen in frame " + num);
+}
+
+addLoadEvent(function() {
+ appendScript(window.frames[2].document);
+ appendScript(window.frames[3].document);
+
+ verifyScriptRan(2);
+ verifyScriptRan(3);
+
+ for (var i = 0; i < 4; ++i) {
+ testFrame(i);
+ }
+
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=482659
+-->
+<head>
+ <title>Test for Bug 482659</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.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=482659">Mozilla Bug 482659</a>
+<p id="display">
+ <iframe></iframe>
+ <iframe src="about:blank"></iframe>
+ <iframe></iframe>
+ <iframe src="about:blank"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 482659 **/
+SimpleTest.waitForExplicitFinish()
+
+function testFrame(num) {
+ is(window.frames[num].document.baseURI, document.baseURI,
+ "Unexpected base URI in frame " + num);
+ is(window.frames[num].document.documentURI, "about:blank",
+ "Unexpected document URI in frame " + num);
+}
+
+function appendScript(doc) {
+ var s = doc.createElement("script");
+ s.textContent = "document.write('executed'); document.close()";
+ doc.body.appendChild(s);
+}
+
+function verifyScriptRan(num) {
+ is(window.frames[num].document.documentElement.textContent, "executed",
+ "write didn't happen in frame " + num);
+}
+
+addLoadEvent(function() {
+ appendScript(window.frames[2].document);
+ appendScript(window.frames[3].document);
+
+ verifyScriptRan(2);
+ verifyScriptRan(3);
+
+ for (var i = 0; i < 4; ++i) {
+ testFrame(i);
+ }
+
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/482659-1-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+ <iframe src="subdir/445004-ref-subsubframe.html"></iframe>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/482659-1a.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <script>
+ window.onload = function() {
+ window.frames[0].document.body.innerHTML =
+ "<img src='passouter.png' " +
+ "onload='window.parent.document.documentElement.className = ""'";
+ }
+ </script>
+ </head>
+ <body>
+ <iframe></iframe>
+ </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/482659-1b.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <script>
+ window.onload = function() {
+ window.frames[0].document.body.innerHTML =
+ "<img src='passouter.png' " +
+ "onload='window.parent.document.documentElement.className = ""'";
+ }
+ </script>
+ </head>
+ <body>
+ <iframe src="about:blank"></iframe>
+ </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/482659-1c.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <script>
+ window.onload = function() {
+ window.frames[0].location =
+ 'javascript:document.write(""); document.close(); ' +
+ 'parent.continueTest();'
+ }
+
+ function continueTest() {
+ // Do this part async just in case
+ setTimeout(function() {
+ window.frames[0].document.body.innerHTML =
+ "<img src='passouter.png' " +
+ "onload='window.parent.document.documentElement.className = ""'";
+ }, 0);
+ }
+ </script>
+ </head>
+ <body>
+ <iframe></iframe>
+ </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/482659-1d.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <script>
+ window.onload = function() {
+ window.frames[0].location =
+ 'javascript:document.write(""); document.close(); ' +
+ 'parent.continueTest();'
+ }
+
+ function continueTest() {
+ // Do this part async just in case
+ setTimeout(function() {
+ window.frames[0].document.body.innerHTML =
+ "<img src='passouter.png' " +
+ "onload='window.parent.document.documentElement.className = ""'";
+ }, 0);
+ }
+ </script>
+ </head>
+ <body>
+ <iframe src="about:blank"></iframe>
+ </body>
+</html>
+
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1102,8 +1102,12 @@ fails == 461512-1.html 461512-1-ref.html
== 480880-1c.html 480880-1-ref.html
== 480880-1d.html 480880-1-ref.html
== 480880-1e.html 480880-1-ref.html
== 480880-2a.html about:blank
== 480880-2b.html about:blank
== 480880-2c.html about:blank
== 482592-1a.xhtml 482592-1-ref.html
== 482592-1b.xhtml 482592-1-ref.html
+== 482659-1a.html 482659-1-ref.html
+== 482659-1b.html 482659-1-ref.html
+== 482659-1c.html 482659-1-ref.html
+== 482659-1d.html 482659-1-ref.html
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -118,16 +118,26 @@
#define NS_SIMPLENESTEDURI_CID \
{ /* 56388dad-287b-4240-a785-85c394012503 */ \
0x56388dad, \
0x287b, \
0x4240, \
{ 0xa7, 0x85, 0x85, 0xc3, 0x94, 0x01, 0x25, 0x03 } \
}
+// component inheriting from the nested simple URI component and also
+// carrying along its base URI
+#define NS_NESTEDABOUTURI_CID \
+{ /* 2f277c00-0eaf-4ddb-b936-41326ba48aae */ \
+ 0x2f277c00, \
+ 0x0eaf, \
+ 0x4ddb, \
+ { 0xb9, 0x36, 0x41, 0x32, 0x6b, 0xa4, 0x8a, 0xae } \
+}
+
// component implementing nsIStandardURL, nsIURI, nsIURL, nsISerializable,
// and nsIClassInfo.
#define NS_STANDARDURL_CLASSNAME \
"nsStandardURL"
#define NS_STANDARDURL_CONTRACTID \
"@mozilla.org/network/standard-url;1"
#define NS_STANDARDURL_CID \
{ /* de9472d0-8034-11d3-9399-00104ba0fd40 */ \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -55,16 +55,17 @@
#include "nsBufferedStreams.h"
#include "nsMIMEInputStream.h"
#include "nsSOCKSSocketProvider.h"
#include "nsCacheService.h"
#include "nsDiskCacheDeviceSQL.h"
#include "nsMimeTypes.h"
#include "nsNetStrings.h"
#include "nsDNSPrefetch.h"
+#include "nsAboutProtocolHandler.h"
#include "nsNetCID.h"
#if defined(XP_MACOSX)
#define BUILD_APPLEFILE_DECODER 1
#else
#define BUILD_BINHEX_DECODER 1
#endif
@@ -174,16 +175,17 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
// protocols
///////////////////////////////////////////////////////////////////////////////
// about:blank is mandatory
#include "nsAboutProtocolHandler.h"
#include "nsAboutBlank.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAboutProtocolHandler)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSafeAboutProtocolHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsNestedAboutURI)
#ifdef NECKO_PROTOCOL_about
// about
#ifdef NS_BUILD_REFCNT_LOGGING
#include "nsAboutBloat.h"
#endif
#include "nsAboutCache.h"
#include "nsAboutCacheEntry.h"
@@ -1009,16 +1011,20 @@ static const nsModuleComponentInfo gNetM
NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-safe-about",
nsSafeAboutProtocolHandlerConstructor
},
{ "about:blank",
NS_ABOUT_BLANK_MODULE_CID,
NS_ABOUT_MODULE_CONTRACTID_PREFIX "blank",
nsAboutBlank::Create
},
+ { "Nested about: URI",
+ NS_NESTEDABOUTURI_CID,
+ nsnull,
+ nsNestedAboutURIConstructor },
#ifdef NECKO_PROTOCOL_about
#ifdef NS_BUILD_REFCNT_LOGGING
{ "about:bloat",
NS_ABOUT_BLOAT_MODULE_CID,
NS_ABOUT_MODULE_CONTRACTID_PREFIX "bloat",
nsAboutBloat::Create
},
#endif
--- a/netwerk/protocol/about/src/nsAboutProtocolHandler.cpp
+++ b/netwerk/protocol/about/src/nsAboutProtocolHandler.cpp
@@ -44,19 +44,23 @@
#include "nsIServiceManager.h"
#include "nsIAboutModule.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsNetCID.h"
#include "nsAboutProtocolUtils.h"
#include "nsNetError.h"
#include "nsNetUtil.h"
-#include "nsSimpleNestedURI.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsAutoPtr.h"
+#include "nsIWritablePropertyBag2.h"
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
+static NS_DEFINE_CID(kNestedAboutURICID, NS_NESTEDABOUTURI_CID);
////////////////////////////////////////////////////////////////////////////////
NS_IMPL_ISUPPORTS1(nsAboutProtocolHandler, nsIProtocolHandler)
////////////////////////////////////////////////////////////////////////////////
// nsIProtocolHandler methods:
@@ -126,17 +130,17 @@ nsAboutProtocolHandler::NewURI(const nsA
NS_ENSURE_SUCCESS(rv, rv);
spec.Insert("moz-safe-about:", 0);
nsCOMPtr<nsIURI> inner;
rv = NS_NewURI(getter_AddRefs(inner), spec);
NS_ENSURE_SUCCESS(rv, rv);
- nsSimpleNestedURI* outer = new nsSimpleNestedURI(inner);
+ nsSimpleNestedURI* outer = new nsNestedAboutURI(inner, aBaseURI);
NS_ENSURE_TRUE(outer, NS_ERROR_OUT_OF_MEMORY);
// Take a ref to it in the COMPtr we plan to return
url = outer;
rv = outer->SetSpec(aSpec);
NS_ENSURE_SUCCESS(rv, rv);
}
@@ -153,17 +157,32 @@ nsAboutProtocolHandler::NewChannel(nsIUR
{
NS_ENSURE_ARG_POINTER(uri);
// about:what you ask?
nsCOMPtr<nsIAboutModule> aboutMod;
nsresult rv = NS_GetAboutModule(uri, getter_AddRefs(aboutMod));
if (NS_SUCCEEDED(rv)) {
// The standard return case:
- return aboutMod->NewChannel(uri, result);
+ rv = aboutMod->NewChannel(uri, result);
+ if (NS_SUCCEEDED(rv)) {
+ nsRefPtr<nsNestedAboutURI> aboutURI;
+ nsresult rv2 = uri->QueryInterface(kNestedAboutURICID,
+ getter_AddRefs(aboutURI));
+ if (NS_SUCCEEDED(rv2) && aboutURI->GetBaseURI()) {
+ nsCOMPtr<nsIWritablePropertyBag2> writableBag =
+ do_QueryInterface(*result);
+ if (writableBag) {
+ writableBag->
+ SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
+ aboutURI->GetBaseURI());
+ }
+ }
+ }
+ return rv;
}
// mumble...
if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) {
// This looks like an about: we don't know about. Convert
// this to an invalid URI error.
rv = NS_ERROR_MALFORMED_URI;
@@ -240,8 +259,81 @@ nsSafeAboutProtocolHandler::NewChannel(n
NS_IMETHODIMP
nsSafeAboutProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
{
// don't override anything.
*_retval = PR_FALSE;
return NS_OK;
}
+
+////////////////////////////////////////////////////////////
+// nsNestedAboutURI implementation
+NS_INTERFACE_MAP_BEGIN(nsNestedAboutURI)
+ if (aIID.Equals(kNestedAboutURICID))
+ foundInterface = static_cast<nsIURI*>(this);
+ else
+NS_INTERFACE_MAP_END_INHERITING(nsSimpleNestedURI)
+
+// nsISerializable
+NS_IMETHODIMP
+nsNestedAboutURI::Read(nsIObjectInputStream* aStream)
+{
+ nsresult rv = nsSimpleNestedURI::Read(aStream);
+ if (NS_FAILED(rv)) return rv;
+
+ PRBool haveBase;
+ rv = aStream->ReadBoolean(&haveBase);
+ if (NS_FAILED(rv)) return rv;
+
+ if (haveBase) {
+ rv = aStream->ReadObject(PR_TRUE, getter_AddRefs(mBaseURI));
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNestedAboutURI::Write(nsIObjectOutputStream* aStream)
+{
+ nsresult rv = nsSimpleNestedURI::Write(aStream);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = aStream->WriteBoolean(mBaseURI != nsnull);
+ if (NS_FAILED(rv)) return rv;
+
+ if (mBaseURI) {
+ rv = aStream->WriteObject(mBaseURI, PR_TRUE);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return NS_OK;
+}
+
+// nsSimpleURI
+/* virtual */ nsSimpleURI*
+nsNestedAboutURI::StartClone()
+{
+ // Sadly, we can't make use of nsSimpleNestedURI::StartClone here.
+ NS_ENSURE_TRUE(mInnerURI, nsnull);
+
+ nsCOMPtr<nsIURI> innerClone;
+ nsresult rv = mInnerURI->Clone(getter_AddRefs(innerClone));
+ if (NS_FAILED(rv)) {
+ return nsnull;
+ }
+
+ nsNestedAboutURI* url = new nsNestedAboutURI(innerClone, mBaseURI);
+ if (url) {
+ url->SetMutable(PR_FALSE);
+ }
+
+ return url;
+}
+
+// nsIClassInfo
+NS_IMETHODIMP
+nsNestedAboutURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
+{
+ *aClassIDNoAlloc = kNestedAboutURICID;
+ return NS_OK;
+}
--- a/netwerk/protocol/about/src/nsAboutProtocolHandler.h
+++ b/netwerk/protocol/about/src/nsAboutProtocolHandler.h
@@ -34,16 +34,17 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsAboutProtocolHandler_h___
#define nsAboutProtocolHandler_h___
#include "nsIProtocolHandler.h"
+#include "nsSimpleNestedURI.h"
class nsCString;
class nsIAboutModule;
class nsAboutProtocolHandler : public nsIProtocolHandler
{
public:
NS_DECL_ISUPPORTS
@@ -67,9 +68,41 @@ public:
// nsSafeAboutProtocolHandler methods:
nsSafeAboutProtocolHandler() {}
private:
~nsSafeAboutProtocolHandler() {}
};
+// Class to allow us to propagate the base URI to about:blank correctly
+class nsNestedAboutURI : public nsSimpleNestedURI {
+public:
+ nsNestedAboutURI(nsIURI* aInnerURI, nsIURI* aBaseURI)
+ : nsSimpleNestedURI(aInnerURI)
+ , mBaseURI(aBaseURI)
+ {}
+
+ // For use only from deserialization
+ nsNestedAboutURI() : nsSimpleNestedURI() {}
+
+ virtual ~nsNestedAboutURI() {}
+
+ // Override QI so we can QI to our CID as needed
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
+
+ // Override StartClone(), the nsISerializable methods, and
+ // GetClassIDNoAlloc; this last is needed to make our nsISerializable impl
+ // work right.
+ virtual nsSimpleURI* StartClone();
+ NS_IMETHOD Read(nsIObjectInputStream* aStream);
+ NS_IMETHOD Write(nsIObjectOutputStream* aStream);
+ NS_IMETHOD GetClassIDNoAlloc(nsCID *aClassIDNoAlloc);
+
+ nsIURI* GetBaseURI() const {
+ return mBaseURI;
+ }
+
+protected:
+ nsCOMPtr<nsIURI> mBaseURI;
+};
+
#endif /* nsAboutProtocolHandler_h___ */
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_aboutblank.js
@@ -0,0 +1,29 @@
+function run_test() {
+ var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+
+ var base = ioServ.newURI("http://www.example.com", null, null);
+
+ var about1 = ioServ.newURI("about:blank", null, null);
+ var about2 = ioServ.newURI("about:blank", null, base);
+
+ var chan1 = ioServ.newChannelFromURI(about1)
+ .QueryInterface(Components.interfaces.nsIPropertyBag2);
+ var chan2 = ioServ.newChannelFromURI(about2)
+ .QueryInterface(Components.interfaces.nsIPropertyBag2);
+
+ var haveProp = false;
+ var propVal = null;
+ try {
+ propVal = chan1.getPropertyAsInterface("baseURI",
+ Components.interfaces.nsIURI);
+ haveProp = true;
+ } catch (e if e.result == Components.results.NS_ERROR_NOT_AVAILABLE) {
+ // Property shouldn't be there.
+ }
+ do_check_eq(propVal, null);
+ do_check_eq(haveProp, false);
+ do_check_eq(chan2.getPropertyAsInterface("baseURI",
+ Components.interfaces.nsIURI),
+ base);
+}