зеркало из https://github.com/mozilla/gecko-dev.git
Bug 536317 - e10s HTTP: implement Cancel. r=jduell, a=blocker
This commit is contained in:
Родитель
66378f8796
Коммит
6d5a1d5e63
|
@ -90,7 +90,6 @@ HttpChannelChild::HttpChannelChild()
|
|||
, mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME)
|
||||
, mSendResumeAt(false)
|
||||
, mSuspendCount(0)
|
||||
, mState(HCC_NEW)
|
||||
, mIPCOpen(false)
|
||||
, mQueuePhase(PHASE_UNQUEUED)
|
||||
{
|
||||
|
@ -247,12 +246,8 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead,
|
|||
{
|
||||
LOG(("HttpChannelChild::RecvOnStartRequest [this=%x]\n", this));
|
||||
|
||||
mState = HCC_ONSTART;
|
||||
|
||||
if (useResponseHead)
|
||||
if (useResponseHead && !mCanceled)
|
||||
mResponseHead = new nsHttpResponseHead(responseHead);
|
||||
else
|
||||
mResponseHead = nsnull;
|
||||
|
||||
mIsFromCache = isFromCache;
|
||||
mCacheEntryAvailable = cacheEntryAvailable;
|
||||
|
@ -266,10 +261,7 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead,
|
|||
if (mResponseHead)
|
||||
SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
|
||||
} else {
|
||||
// TODO: Cancel request: (bug 536317)
|
||||
// - Send Cancel msg to parent
|
||||
// - drop any in flight OnDataAvail msgs we receive
|
||||
// - make sure we do call OnStopRequest eventually
|
||||
Cancel(rv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,9 +303,10 @@ HttpChannelChild::OnDataAvailable(const nsCString& data,
|
|||
const PRUint32& offset,
|
||||
const PRUint32& count)
|
||||
{
|
||||
LOG(("HttpChannelChild::RecvOnDataAvailable [this=%x]\n", this));
|
||||
LOG(("HttpChannelChild::OnDataAvailable [this=%x]\n", this));
|
||||
|
||||
mState = HCC_ONDATA;
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
// NOTE: the OnDataAvailable contract requires the client to read all the data
|
||||
// in the inputstream. This code relies on that ('data' will go away after
|
||||
|
@ -326,7 +319,7 @@ HttpChannelChild::OnDataAvailable(const nsCString& data,
|
|||
count,
|
||||
NS_ASSIGNMENT_DEPEND);
|
||||
if (NS_FAILED(rv)) {
|
||||
// TODO: what to do here? Cancel request? Very unlikely to fail.
|
||||
Cancel(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -336,7 +329,7 @@ HttpChannelChild::OnDataAvailable(const nsCString& data,
|
|||
stringStream, offset, count);
|
||||
stringStream->Close();
|
||||
if (NS_FAILED(rv)) {
|
||||
// TODO: Cancel request: see OnStartRequest. Bug 536317
|
||||
Cancel(rv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,19 +361,21 @@ HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode)
|
|||
void
|
||||
HttpChannelChild::OnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
LOG(("HttpChannelChild::RecvOnStopRequest [this=%x status=%u]\n",
|
||||
LOG(("HttpChannelChild::OnStopRequest [this=%x status=%u]\n",
|
||||
this, statusCode));
|
||||
|
||||
mState = HCC_ONSTOP;
|
||||
|
||||
mIsPending = PR_FALSE;
|
||||
mStatus = statusCode;
|
||||
|
||||
{ // We must flush the queue before we Send__delete__,
|
||||
if (!mCanceled)
|
||||
mStatus = statusCode;
|
||||
|
||||
{ // We must flush the queue before we Send__delete__
|
||||
// (although we really shouldn't receive any msgs after OnStop),
|
||||
// so make sure this goes out of scope before then.
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
|
||||
|
||||
mListener->OnStopRequest(this, mListenerContext, statusCode);
|
||||
|
||||
mListener = 0;
|
||||
mListenerContext = 0;
|
||||
mCacheEntryAvailable = PR_FALSE;
|
||||
|
@ -426,9 +421,12 @@ void
|
|||
HttpChannelChild::OnProgress(const PRUint64& progress,
|
||||
const PRUint64& progressMax)
|
||||
{
|
||||
LOG(("HttpChannelChild::RecvOnProgress [this=%p progress=%llu/%llu]\n",
|
||||
LOG(("HttpChannelChild::OnProgress [this=%p progress=%llu/%llu]\n",
|
||||
this, progress, progressMax));
|
||||
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
// cache the progress sink so we don't have to query for it each time.
|
||||
if (!mProgressSink)
|
||||
GetCallback(mProgressSink);
|
||||
|
@ -439,7 +437,7 @@ HttpChannelChild::OnProgress(const PRUint64& progress,
|
|||
if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending &&
|
||||
!(mLoadFlags & LOAD_BACKGROUND))
|
||||
{
|
||||
if (progress > 0) {
|
||||
if (progress > 0) {
|
||||
NS_ASSERTION(progress <= progressMax, "unexpected progress values");
|
||||
mProgressSink->OnProgress(this, nsnull, progress, progressMax);
|
||||
}
|
||||
|
@ -479,7 +477,10 @@ void
|
|||
HttpChannelChild::OnStatus(const nsresult& status,
|
||||
const nsString& statusArg)
|
||||
{
|
||||
LOG(("HttpChannelChild::RecvOnStatus [this=%p status=%x]\n", this, status));
|
||||
LOG(("HttpChannelChild::OnStatus [this=%p status=%x]\n", this, status));
|
||||
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
// cache the progress sink so we don't have to query for it each time.
|
||||
if (!mProgressSink)
|
||||
|
@ -495,6 +496,57 @@ HttpChannelChild::OnStatus(const nsresult& status,
|
|||
}
|
||||
}
|
||||
|
||||
class CancelEvent : public ChildChannelEvent
|
||||
{
|
||||
public:
|
||||
CancelEvent(HttpChannelChild* child, const nsresult& status)
|
||||
: mChild(child)
|
||||
, mStatus(status) {}
|
||||
|
||||
void Run() { mChild->OnCancel(mStatus); }
|
||||
private:
|
||||
HttpChannelChild* mChild;
|
||||
nsresult mStatus;
|
||||
};
|
||||
|
||||
bool
|
||||
HttpChannelChild::RecvCancelEarly(const nsresult& status)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new CancelEvent(this, status));
|
||||
} else {
|
||||
OnCancel(status);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::OnCancel(const nsresult& status)
|
||||
{
|
||||
LOG(("HttpChannelChild::OnCancel [this=%p status=%x]\n", this, status));
|
||||
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
mCanceled = true;
|
||||
mStatus = status;
|
||||
|
||||
mIsPending = false;
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
|
||||
|
||||
if (mListener) {
|
||||
mListener->OnStartRequest(this, mListenerContext);
|
||||
mListener->OnStopRequest(this, mListenerContext, mStatus);
|
||||
}
|
||||
|
||||
mListener = NULL;
|
||||
mListenerContext = NULL;
|
||||
|
||||
if (mIPCOpen)
|
||||
PHttpChannelChild::Send__delete__(this);
|
||||
}
|
||||
|
||||
class Redirect1Event : public ChildChannelEvent
|
||||
{
|
||||
public:
|
||||
|
@ -550,8 +602,12 @@ HttpChannelChild::Redirect1Begin(PHttpChannelChild* newChannel,
|
|||
nsresult rv =
|
||||
newHttpChannelChild->HttpBaseChannel::Init(uri, mCaps,
|
||||
mConnectionInfo->ProxyInfo());
|
||||
if (NS_FAILED(rv))
|
||||
return; // TODO Bug 536317
|
||||
if (NS_FAILED(rv)) {
|
||||
// Cancel the channel and veto the redirect.
|
||||
Cancel(rv);
|
||||
SendRedirect2Result(rv, mRedirectChannelChild->mRequestHeaders);
|
||||
return;
|
||||
}
|
||||
|
||||
// We won't get OnStartRequest, set cookies here.
|
||||
mResponseHead = new nsHttpResponseHead(responseHead);
|
||||
|
@ -559,16 +615,20 @@ HttpChannelChild::Redirect1Begin(PHttpChannelChild* newChannel,
|
|||
|
||||
PRBool preserveMethod = (mResponseHead->Status() == 307);
|
||||
rv = SetupReplacementChannel(uri, newHttpChannelChild, preserveMethod);
|
||||
if (NS_FAILED(rv))
|
||||
return; // TODO Bug 536317
|
||||
if (NS_FAILED(rv)) {
|
||||
// Cancel the channel and veto the redirect.
|
||||
Cancel(rv);
|
||||
SendRedirect2Result(rv, mRedirectChannelChild->mRequestHeaders);
|
||||
return;
|
||||
}
|
||||
|
||||
mRedirectChannelChild = newHttpChannelChild;
|
||||
|
||||
nsresult result = gHttpHandler->AsyncOnChannelRedirect(this,
|
||||
newHttpChannelChild,
|
||||
redirectFlags);
|
||||
if (NS_FAILED(result))
|
||||
OnRedirectVerifyCallback(result);
|
||||
rv = gHttpHandler->AsyncOnChannelRedirect(this,
|
||||
newHttpChannelChild,
|
||||
redirectFlags);
|
||||
if (NS_FAILED(rv))
|
||||
OnRedirectVerifyCallback(rv);
|
||||
}
|
||||
|
||||
class Redirect3Event : public ChildChannelEvent
|
||||
|
@ -604,7 +664,7 @@ HttpChannelChild::Redirect3Complete()
|
|||
rv = mRedirectChannelChild->CompleteRedirectSetup(mListener,
|
||||
mListenerContext);
|
||||
if (NS_FAILED(rv))
|
||||
; // TODO Cancel: Bug 536317
|
||||
Cancel(rv);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -628,9 +688,9 @@ HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
|
|||
if (mLoadGroup)
|
||||
mLoadGroup->AddRequest(this, nsnull);
|
||||
|
||||
// TODO: may have been canceled by on-modify-request observers: bug 536317
|
||||
|
||||
mState = HCC_OPENED;
|
||||
// We already have an open IPDL connection to the parent. If on-modify-request
|
||||
// listeners or load group observers canceled us, let the parent handle it
|
||||
// and send it back to us naturally.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -656,7 +716,14 @@ HttpChannelChild::OnRedirectVerifyCallback(nsresult result)
|
|||
NS_IMETHODIMP
|
||||
HttpChannelChild::Cancel(nsresult status)
|
||||
{
|
||||
// FIXME: bug 536317
|
||||
if (!mCanceled) {
|
||||
// If this cancel occurs before nsHttpChannel has been set up, AsyncOpen
|
||||
// is responsible for cleaning up.
|
||||
mCanceled = true;
|
||||
mStatus = status;
|
||||
if (mIPCOpen)
|
||||
SendCancel(status);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -699,6 +766,9 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
|
|||
{
|
||||
LOG(("HttpChannelChild::AsyncOpen [this=%x uri=%s]\n", this, mSpec.get()));
|
||||
|
||||
if (mCanceled)
|
||||
return mStatus;
|
||||
|
||||
NS_ENSURE_TRUE(gNeckoChild != nsnull, NS_ERROR_FAILURE);
|
||||
NS_ENSURE_ARG_POINTER(listener);
|
||||
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
|
||||
|
@ -758,19 +828,16 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
|
|||
if (mLoadGroup)
|
||||
mLoadGroup->AddRequest(this, nsnull);
|
||||
|
||||
// FIXME: bug 536317: We may have been cancelled already, either by
|
||||
// on-modify-request listeners or by load group observers; in that case,
|
||||
// don't create IPDL connection. See nsHttpChannel::AsyncOpen(): I think
|
||||
// we'll need something like
|
||||
//
|
||||
// if (mCanceled) {
|
||||
// LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
|
||||
// AsyncAbort(rv);
|
||||
// return NS_OK;
|
||||
// }
|
||||
//
|
||||
// (This is assuming on-modify-request/loadgroup observers will still be able
|
||||
// to cancel synchronously)
|
||||
if (mCanceled) {
|
||||
// We may have been canceled already, either by on-modify-request
|
||||
// listeners or by load group observers; in that case, don't create IPDL
|
||||
// connection. See nsHttpChannel::AsyncOpen().
|
||||
|
||||
// Clear mCanceled here, or we will bail out at top of OnCancel().
|
||||
mCanceled = false;
|
||||
OnCancel(mStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Send request to the chrome process...
|
||||
|
@ -798,7 +865,6 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
|
|||
mAllowPipelining, mForceAllowThirdPartyCookie, mSendResumeAt,
|
||||
mStartPos, mEntityID);
|
||||
|
||||
mState = HCC_OPENED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ protected:
|
|||
bool RecvOnStopRequest(const nsresult& statusCode);
|
||||
bool RecvOnProgress(const PRUint64& progress, const PRUint64& progressMax);
|
||||
bool RecvOnStatus(const nsresult& status, const nsString& statusArg);
|
||||
bool RecvCancelEarly(const nsresult& status);
|
||||
bool RecvRedirect1Begin(PHttpChannelChild* newChannel,
|
||||
const URI& newURI,
|
||||
const PRUint32& redirectFlags,
|
||||
|
@ -161,8 +162,6 @@ private:
|
|||
// Current suspension depth for this channel object
|
||||
PRUint32 mSuspendCount;
|
||||
|
||||
// FIXME: replace with IPDL states (bug 536319)
|
||||
enum HttpChannelChildState mState;
|
||||
bool mIPCOpen;
|
||||
|
||||
// Workaround for Necko re-entrancy dangers. We buffer IPDL messages in a
|
||||
|
@ -196,6 +195,7 @@ private:
|
|||
void OnStopRequest(const nsresult& statusCode);
|
||||
void OnProgress(const PRUint64& progress, const PRUint64& progressMax);
|
||||
void OnStatus(const nsresult& status, const nsString& statusArg);
|
||||
void OnCancel(const nsresult& status);
|
||||
void Redirect1Begin(PHttpChannelChild* newChannel, const URI& newURI,
|
||||
const PRUint32& redirectFlags,
|
||||
const nsHttpResponseHead& responseHead);
|
||||
|
@ -207,6 +207,7 @@ private:
|
|||
friend class DataAvailableEvent;
|
||||
friend class ProgressEvent;
|
||||
friend class StatusEvent;
|
||||
friend class CancelEvent;
|
||||
friend class Redirect1Event;
|
||||
friend class Redirect3Event;
|
||||
};
|
||||
|
|
|
@ -123,11 +123,11 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
|
|||
|
||||
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
|
||||
if (NS_FAILED(rv))
|
||||
return false; // TODO: cancel request (bug 536317), return true
|
||||
return SendCancelEarly(rv);
|
||||
|
||||
rv = NS_NewChannel(getter_AddRefs(mChannel), uri, ios, nsnull, nsnull, loadFlags);
|
||||
if (NS_FAILED(rv))
|
||||
return false; // TODO: cancel request (bug 536317), return true
|
||||
return SendCancelEarly(rv);
|
||||
|
||||
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
httpChan->SetRemoteChannel(true);
|
||||
|
@ -159,9 +159,9 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
|
|||
if (uploadStreamInfo != eUploadStream_null) {
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = NS_NewPostDataStream(getter_AddRefs(stream), false, uploadStreamData, 0);
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
return false; // TODO: cancel request (bug 536317), return true
|
||||
}
|
||||
if (NS_FAILED(rv))
|
||||
return SendCancelEarly(rv);
|
||||
|
||||
httpChan->InternalSetUploadStream(stream);
|
||||
// We're casting uploadStreamInfo into PRBool here on purpose because
|
||||
// we know possible values are either 0 or 1. See uploadStreamInfoType.
|
||||
|
@ -176,7 +176,7 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
|
|||
|
||||
rv = httpChan->AsyncOpen(mChannelListener, nsnull);
|
||||
if (NS_FAILED(rv))
|
||||
return false; // TODO: cancel request (bug 536317), return true
|
||||
return SendCancelEarly(rv);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -203,6 +203,18 @@ HttpChannelParent::RecvResume()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HttpChannelParent::RecvCancel(const nsresult& status)
|
||||
{
|
||||
// May receive cancel before channel has been constructed!
|
||||
if (mChannel) {
|
||||
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
httpChan->Cancel(status);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset)
|
||||
{
|
||||
|
@ -281,19 +293,13 @@ HttpChannelParent::OnDataAvailable(nsIRequest *aRequest,
|
|||
LOG(("HttpChannelParent::OnDataAvailable [this=%x]\n", this));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCString data;
|
||||
data.SetLength(aCount);
|
||||
char * p = data.BeginWriting();
|
||||
PRUint32 bytesRead;
|
||||
rv = aInputStream->Read(p, aCount, &bytesRead);
|
||||
data.EndWriting();
|
||||
if (!NS_SUCCEEDED(rv) || bytesRead != aCount) {
|
||||
return rv; // TODO: cancel request locally (bug 536317)
|
||||
}
|
||||
|
||||
if (mIPCClosed || !SendOnDataAvailable(data, aOffset, bytesRead))
|
||||
rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
|
||||
if (NS_FAILED(rv) ||
|
||||
bytesRead != aCount ||
|
||||
mIPCClosed ||
|
||||
!SendOnDataAvailable(data, aOffset, bytesRead))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,7 @@ protected:
|
|||
virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset);
|
||||
virtual bool RecvSuspend();
|
||||
virtual bool RecvResume();
|
||||
virtual bool RecvCancel(const nsresult& status);
|
||||
virtual bool RecvRedirect2Result(const nsresult& result,
|
||||
const RequestHeaderTuples& changedHeaders);
|
||||
|
||||
|
|
|
@ -85,6 +85,8 @@ parent:
|
|||
Suspend();
|
||||
Resume();
|
||||
|
||||
Cancel(nsresult status);
|
||||
|
||||
// Reports approval/veto of redirect by child process redirect observers
|
||||
Redirect2Result(nsresult result, RequestHeaderTuples changedHeaders);
|
||||
|
||||
|
@ -106,6 +108,10 @@ child:
|
|||
|
||||
OnStatus(nsresult status, nsString statusArg);
|
||||
|
||||
// Used to cancel child channel if we hit errors during creating and
|
||||
// AsyncOpen of nsHttpChannel on the parent.
|
||||
CancelEarly(nsresult status);
|
||||
|
||||
// Called to initiate content channel redirect, starts talking to sinks
|
||||
// on the content process and reports result via OnRedirect2Result above
|
||||
Redirect1Begin(PHttpChannel newChannel,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
do_load_httpd_js();
|
||||
|
||||
const NS_BINDING_ABORTED = 0x804b0002;
|
||||
const TEST_COUNT = 2;
|
||||
|
||||
var observer = {
|
||||
QueryInterface: function eventsink_qi(iid) {
|
||||
|
@ -33,8 +34,14 @@ var listener = {
|
|||
},
|
||||
|
||||
onStopRequest: function test_onStopR(request, ctx, status) {
|
||||
httpserv.stop(do_test_finished);
|
||||
}
|
||||
this.currentTest++;
|
||||
if (this.currentTest == TEST_COUNT)
|
||||
httpserv.stop(do_test_finished);
|
||||
else
|
||||
execute_test(this.currentTest)
|
||||
},
|
||||
|
||||
currentTest : 0
|
||||
};
|
||||
|
||||
function makeChan(url) {
|
||||
|
@ -48,18 +55,31 @@ function makeChan(url) {
|
|||
|
||||
var httpserv = null;
|
||||
|
||||
function execute_test(id) {
|
||||
if (id < 0 || id >= TEST_COUNT)
|
||||
do_throw("Invalid test id.");
|
||||
|
||||
var chan = makeChan("http://localhost:4444/failtest");
|
||||
|
||||
if (id == 0) {
|
||||
listener.shouldNotStart = true;
|
||||
var obs = Components.classes["@mozilla.org/observer-service;1"].getService();
|
||||
obs = obs.QueryInterface(Components.interfaces.nsIObserverService);
|
||||
obs.addObserver(observer, "http-on-modify-request", false);
|
||||
}
|
||||
|
||||
chan.asyncOpen(listener, null);
|
||||
|
||||
if (id == 1)
|
||||
chan.cancel(NS_BINDING_ABORTED);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
httpserv = new nsHttpServer();
|
||||
httpserv.registerPathHandler("/failtest", failtest);
|
||||
httpserv.start(4444);
|
||||
|
||||
var obs = Components.classes["@mozilla.org/observer-service;1"].getService();
|
||||
obs = obs.QueryInterface(Components.interfaces.nsIObserverService);
|
||||
obs.addObserver(observer, "http-on-modify-request", false);
|
||||
|
||||
var chan = makeChan("http://localhost:4444/failtest");
|
||||
|
||||
chan.asyncOpen(listener, null);
|
||||
|
||||
execute_test(0);
|
||||
|
||||
do_test_pending();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// Run test script in content process instead of chrome (xpcshell's default)
|
||||
//
|
||||
|
||||
function run_test() {
|
||||
run_test_in_child("../unit/test_httpcancel.js");
|
||||
dump("And here");
|
||||
}
|
||||
|
Загрузка…
Ссылка в новой задаче