Bug 780529. Make people poking XHR via random XPCOM interfaces actually work as long as they stick to GetInterface. r=peterv

This commit is contained in:
Boris Zbarsky 2012-09-07 10:51:35 -04:00
parent 583e8302be
commit 2295217399
4 changed files with 161 additions and 6 deletions

View File

@ -350,7 +350,8 @@ nsXMLHttpRequest::nsXMLHttpRequest()
mFirstStartRequestSeen(false),
mInLoadProgressEvent(false),
mResultJSON(JSVAL_VOID),
mResultArrayBuffer(nullptr)
mResultArrayBuffer(nullptr),
mXPCOMifier(nullptr)
{
nsLayoutStatics::AddRef();
@ -3695,13 +3696,11 @@ nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
// need to see these notifications for proper functioning.
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
mChannelEventSink = do_GetInterface(mNotificationCallbacks);
*aResult = static_cast<nsIChannelEventSink*>(this);
NS_ADDREF_THIS();
*aResult = static_cast<nsIChannelEventSink*>(EnsureXPCOMifier().get());
return NS_OK;
} else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
mProgressEventSink = do_GetInterface(mNotificationCallbacks);
*aResult = static_cast<nsIProgressEventSink*>(this);
NS_ADDREF_THIS();
*aResult = static_cast<nsIProgressEventSink*>(EnsureXPCOMifier().get());
return NS_OK;
}
@ -3742,7 +3741,21 @@ nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
return wwatch->GetPrompt(window, aIID,
reinterpret_cast<void**>(aResult));
}
// Now check for the various XHR non-DOM interfaces, except
// nsIProgressEventSink and nsIChannelEventSink which we already
// handled above.
else if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
*aResult = static_cast<nsIStreamListener*>(EnsureXPCOMifier().get());
return NS_OK;
}
else if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
*aResult = static_cast<nsIRequestObserver*>(EnsureXPCOMifier().get());
return NS_OK;
}
else if (aIID.Equals(NS_GET_IID(nsITimerCallback))) {
*aResult = static_cast<nsITimerCallback*>(EnsureXPCOMifier().get());
return NS_OK;
}
return QueryInterface(aIID, aResult);
@ -3860,6 +3873,16 @@ nsXMLHttpRequest::StartProgressEventTimer()
}
}
already_AddRefed<nsXMLHttpRequestXPCOMifier>
nsXMLHttpRequest::EnsureXPCOMifier()
{
if (!mXPCOMifier) {
mXPCOMifier = new nsXMLHttpRequestXPCOMifier(this);
}
nsRefPtr<nsXMLHttpRequestXPCOMifier> newRef(mXPCOMifier);
return newRef.forget();
}
NS_IMPL_ISUPPORTS1(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
NS_IMETHODIMP nsXMLHttpRequest::
@ -3957,3 +3980,47 @@ NS_IMETHODIMP nsXMLHttpProgressEvent::GetTotalSize(uint32_t *aTotalSize)
LL_L2UI(*aTotalSize, mMaxProgress);
return NS_OK;
}
// nsXMLHttpRequestXPCOMifier implementation
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
// Can't NS_IMPL_CYCLE_COLLECTION_1 because mXHR has ambiguous
// inheritance from nsISupports.
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier)
if (tmp->mXHR) {
tmp->mXHR->mXPCOMifier = nullptr;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXHR)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mXHR, nsIXMLHttpRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMETHODIMP
nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID & aIID, void **aResult)
{
// Return ourselves for the things we implement (except
// nsIInterfaceRequestor) and the XHR for the rest.
if (!aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
nsresult rv = QueryInterface(aIID, aResult);
if (NS_SUCCEEDED(rv)) {
return rv;
}
}
return mXHR->GetInterface(aIID, aResult);
}

View File

@ -114,6 +114,10 @@ public:
}
};
class nsXMLHttpRequestXPCOMifier;
// Make sure that any non-DOM interfaces added here are also added to
// nsXMLHttpRequestXPCOMifier.
class nsXMLHttpRequest : public nsXHREventTarget,
public nsIXMLHttpRequest,
public nsIJSXMLHttpRequest,
@ -126,6 +130,8 @@ class nsXMLHttpRequest : public nsXHREventTarget,
public nsITimerCallback
{
friend class nsXHRParseEndListener;
friend class nsXMLHttpRequestXPCOMifier;
public:
nsXMLHttpRequest();
virtual ~nsXMLHttpRequest();
@ -502,6 +508,8 @@ protected:
const mozilla::dom::Optional<nsAString>& user,
const mozilla::dom::Optional<nsAString>& password);
already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
nsCOMPtr<nsISupports> mContext;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIChannel> mChannel;
@ -657,10 +665,49 @@ protected:
nsCString value;
};
nsTArray<RequestHeader> mModifiedRequestHeaders;
// Helper object to manage our XPCOM scriptability bits
nsXMLHttpRequestXPCOMifier* mXPCOMifier;
};
#undef IMPL_EVENT_HANDLER
// A shim class designed to expose the non-DOM interfaces of
// XMLHttpRequest via XPCOM stuff.
class nsXMLHttpRequestXPCOMifier MOZ_FINAL : public nsIStreamListener,
public nsIChannelEventSink,
public nsIProgressEventSink,
public nsIInterfaceRequestor,
public nsITimerCallback,
public nsCycleCollectionParticipant
{
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXMLHttpRequestXPCOMifier,
nsIStreamListener)
nsXMLHttpRequestXPCOMifier(nsXMLHttpRequest* aXHR) :
mXHR(aXHR)
{
}
~nsXMLHttpRequestXPCOMifier() {
if (mXHR) {
mXHR->mXPCOMifier = nullptr;
}
}
NS_FORWARD_NSISTREAMLISTENER(mXHR->)
NS_FORWARD_NSIREQUESTOBSERVER(mXHR->)
NS_FORWARD_NSICHANNELEVENTSINK(mXHR->)
NS_FORWARD_NSIPROGRESSEVENTSINK(mXHR->)
NS_FORWARD_NSITIMERCALLBACK(mXHR->)
NS_DECL_NSIINTERFACEREQUESTOR
private:
nsRefPtr<nsXMLHttpRequest> mXHR;
};
// helper class to expose a progress DOM Event
class nsXMLHttpProgressEvent : public nsIDOMProgressEvent,

View File

@ -44,6 +44,7 @@ MOCHITEST_CHROME_FILES = \
test_bug752226-4.xul \
test_bug682305.html \
test_bug780199.xul \
test_bug780529.xul \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=780529
-->
<window title="Mozilla Bug 780529"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=780529"
target="_blank">Mozilla Bug 780529</a>
</body>
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
/** Test for Bug 780529 **/
var req = new XMLHttpRequest();
req.open("GET", "", true);
// Have to call send() to get the XHR hooked up as the notification callbacks
req.send();
var callbacks = req.channel.notificationCallbacks;
var sink = callbacks.getInterface(Components.interfaces.nsIChannelEventSink);
ok(sink instanceof Components.interfaces.nsIChannelEventSink,
"Should be a channel event sink")
ok("asyncOnChannelRedirect" in sink,
"Should have the right methods for an event sink");
is(callbacks.getInterface(Components.interfaces.nsIXMLHttpRequest), req,
"Should have the right object");
sinkReq = sink.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
isnot(sinkReq, callbacks, "Sink should not be the XHR object");
is(sinkReq.getInterface(Components.interfaces.nsIXMLHttpRequest), req,
"Should have the XHR object now");
]]>
</script>
</window>