Bug 1218029 - Adds ScriptLoadHandler and implements OnIncrementalData callback. r=djvj

This commit is contained in:
Yury Delendik 2015-12-01 08:00:58 -06:00
parent 65bc1071b3
commit d12373bf98
8 changed files with 271 additions and 44 deletions

View File

@ -160,7 +160,7 @@ nsScriptLoader::~nsScriptLoader()
}
}
NS_IMPL_ISUPPORTS(nsScriptLoader, nsIIncrementalStreamLoaderObserver)
NS_IMPL_ISUPPORTS(nsScriptLoader, nsISupports)
// Helper method for checking if the script element is an event-handler
// This means that it has both a for-attribute and a event-attribute.
@ -269,37 +269,6 @@ nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument,
return NS_OK;
}
class ContextMediator : public nsIIncrementalStreamLoaderObserver
{
public:
explicit ContextMediator(nsScriptLoader *aScriptLoader, nsISupports *aContext)
: mScriptLoader(aScriptLoader)
, mContext(aContext) {}
NS_DECL_ISUPPORTS
NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
private:
virtual ~ContextMediator() {}
RefPtr<nsScriptLoader> mScriptLoader;
nsCOMPtr<nsISupports> mContext;
};
NS_IMPL_ISUPPORTS(ContextMediator, nsIIncrementalStreamLoaderObserver)
NS_IMETHODIMP
ContextMediator::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
uint32_t aStringLen,
const uint8_t* aString)
{
// pass arguments through except for the aContext,
// we have to mediate and use mContext instead.
return mScriptLoader->OnStreamComplete(aLoader, mContext, aStatus,
aStringLen, aString);
}
nsresult
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
bool aScriptFromHead)
@ -383,10 +352,10 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
timedChannel->SetInitiatorType(NS_LITERAL_STRING("script"));
}
RefPtr<ContextMediator> mediator = new ContextMediator(this, aRequest);
RefPtr<nsScriptLoadHandler> handler = new nsScriptLoadHandler(this, aRequest);
nsCOMPtr<nsIIncrementalStreamLoader> loader;
rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), mediator);
rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), handler);
NS_ENSURE_SUCCESS(rv, rv);
return channel->AsyncOpen2(loader);
@ -1438,7 +1407,7 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
return rv;
}
NS_IMETHODIMP
nsresult
nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
@ -1739,3 +1708,41 @@ nsScriptLoader::MaybeRemovedDeferRequests()
}
return false;
}
//////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////
nsScriptLoadHandler::nsScriptLoadHandler(nsScriptLoader *aScriptLoader,
nsScriptLoadRequest *aRequest)
: mScriptLoader(aScriptLoader),
mRequest(aRequest)
{}
nsScriptLoadHandler::~nsScriptLoadHandler()
{}
NS_IMPL_ISUPPORTS(nsScriptLoadHandler, nsIIncrementalStreamLoaderObserver)
NS_IMETHODIMP
nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
uint32_t aDataLength,
const uint8_t* aData,
uint32_t *aConsumedLength)
{
return NS_OK;
}
NS_IMETHODIMP
nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
uint32_t aStringLen,
const uint8_t* aString)
{
// pass arguments through except for the aContext,
// we have to mediate and use mRequest instead.
return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus,
aStringLen, aString);
}

View File

@ -195,7 +195,7 @@ public:
// Script loader implementation
//////////////////////////////////////////////////////////////
class nsScriptLoader final : public nsIIncrementalStreamLoaderObserver
class nsScriptLoader final : public nsISupports
{
class MOZ_STACK_CLASS AutoCurrentScriptUpdater
{
@ -223,7 +223,6 @@ public:
explicit nsScriptLoader(nsIDocument* aDocument);
NS_DECL_ISUPPORTS
NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
/**
* The loader maintains a weak reference to the document with
@ -342,6 +341,17 @@ public:
nsIDocument* aDocument,
char16_t*& aBufOut, size_t& aLengthOut);
/**
* Handle the completion of a stream. This is called by the
* nsScriptLoadHandler object which observes the IncrementalStreamLoader
* loading the script.
*/
nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsresult aStatus,
uint32_t aStringLen,
const uint8_t* aString);
/**
* Processes any pending requests that are ready for processing.
*/
@ -538,6 +548,22 @@ private:
bool mBlockingDOMContentLoaded;
};
class nsScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver
{
public:
explicit nsScriptLoadHandler(nsScriptLoader* aScriptLoader,
nsScriptLoadRequest *aRequest);
NS_DECL_ISUPPORTS
NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
private:
virtual ~nsScriptLoadHandler();
RefPtr<nsScriptLoader> mScriptLoader;
RefPtr<nsScriptLoadRequest> mRequest;
};
class nsAutoScriptLoaderDisabler
{
public:

View File

@ -325,6 +325,16 @@ class MOZ_STACK_CLASS AutoRejectPromise
nsCOMPtr<nsIGlobalObject> mGlobalObject;
};
NS_IMETHODIMP
AsyncScriptLoader::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
uint32_t aDataLength,
const uint8_t* aData,
uint32_t *aConsumedData)
{
return NS_OK;
}
NS_IMETHODIMP
AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
@ -769,6 +779,16 @@ NotifyPrecompilationCompleteRunnable::Run(void)
return NS_OK;
}
NS_IMETHODIMP
ScriptPrecompiler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
uint32_t aDataLength,
const uint8_t* aData,
uint32_t *aConsumedData)
{
return NS_OK;
}
NS_IMETHODIMP
ScriptPrecompiler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,

View File

@ -8,9 +8,36 @@
interface nsIRequest;
interface nsIIncrementalStreamLoader;
[scriptable, uuid(2143eaad-674e-4613-87f8-359d4c40f590)]
[scriptable, uuid(07c3d2cc-5454-4618-9f4f-cd93de9504a4)]
interface nsIIncrementalStreamLoaderObserver : nsISupports
{
/**
* Called when new data has arrived on the stream.
*
* @param loader the stream loader that loaded the stream.
* @param ctxt the context parameter of the underlying channel
* @param dataLength the length of the new data received
* @param data the contents of the new data received.
*
* This method will always be called asynchronously by the
* nsIIncrementalStreamLoader involved, on the thread that called the
* loader's init() method.
*
* If the observer wants to not accumulate all or portional of the data in
* the internal buffer, the consumedLength shall be set to the value of
* the dataLength or less. By default the consumedLength value is assumed 0.
* The data and dataLength reflect the non-consumed data and will be
* accumulated if consumedLength is not set.
*
* In comparison with onStreamComplete(), the data buffer cannot be
* adopted if this method returns NS_SUCCESS_ADOPTED_DATA.
*/
void onIncrementalData(in nsIIncrementalStreamLoader loader,
in nsISupports ctxt,
in unsigned long dataLength,
[const,array,size_is(dataLength)] in octet data,
inout unsigned long consumedLength);
/**
* Called when the entire stream has been loaded.
*

View File

@ -12,7 +12,7 @@
#include <limits>
nsIncrementalStreamLoader::nsIncrementalStreamLoader()
: mData()
: mData(), mBytesConsumed(0)
{
}
@ -49,7 +49,7 @@ NS_IMPL_ISUPPORTS(nsIncrementalStreamLoader, nsIIncrementalStreamLoader,
NS_IMETHODIMP
nsIncrementalStreamLoader::GetNumBytesRead(uint32_t* aNumBytes)
{
*aNumBytes = mData.length();
*aNumBytes = mBytesConsumed + mData.length();
return NS_OK;
}
@ -121,11 +121,66 @@ nsIncrementalStreamLoader::WriteSegmentFun(nsIInputStream *inStr,
{
nsIncrementalStreamLoader *self = (nsIncrementalStreamLoader *) closure;
if (!self->mData.append(fromSegment, count)) {
self->mData.clearAndFree();
return NS_ERROR_OUT_OF_MEMORY;
const uint8_t *data = reinterpret_cast<const uint8_t *>(fromSegment);
uint32_t consumedCount = 0;
nsresult rv;
if (self->mData.empty()) {
// Shortcut when observer wants to keep the listener's buffer empty.
rv = self->mObserver->OnIncrementalData(self, self->mContext,
count, data, &consumedCount);
if (rv != NS_OK) {
return rv;
}
if (consumedCount > count) {
return NS_ERROR_INVALID_ARG;
}
if (consumedCount < count) {
if (!self->mData.append(fromSegment + consumedCount,
count - consumedCount)) {
self->mData.clearAndFree();
return NS_ERROR_OUT_OF_MEMORY;
}
}
} else {
// We have some non-consumed data from previous OnIncrementalData call,
// appending new data and reporting combined data.
if (!self->mData.append(fromSegment, count)) {
self->mData.clearAndFree();
return NS_ERROR_OUT_OF_MEMORY;
}
size_t length = self->mData.length();
uint32_t reportCount = length > UINT32_MAX ? UINT32_MAX : (uint32_t)length;
uint8_t* elems = self->mData.extractRawBuffer();
rv = self->mObserver->OnIncrementalData(self, self->mContext,
reportCount, elems, &consumedCount);
// We still own elems, freeing its memory when exiting scope.
if (rv != NS_OK) {
free(elems);
return rv;
}
if (consumedCount > reportCount) {
free(elems);
return NS_ERROR_INVALID_ARG;
}
if (consumedCount == length) {
free(elems); // good case -- fully consumed data
} else {
// Adopting elems back (at least its portion).
self->mData.replaceRawBuffer(elems, length);
if (consumedCount > 0) {
self->mData.erase(self->mData.begin() + consumedCount);
}
}
}
self->mBytesConsumed += consumedCount;
*writeCount = count;
return NS_OK;
@ -136,8 +191,14 @@ nsIncrementalStreamLoader::OnDataAvailable(nsIRequest* request, nsISupports *ctx
nsIInputStream *inStr,
uint64_t sourceOffset, uint32_t count)
{
if (mObserver) {
// provide nsIIncrementalStreamLoader::request during call to OnStreamComplete
mRequest = request;
}
uint32_t countRead;
return inStr->ReadSegments(WriteSegmentFun, this, count, &countRead);
nsresult rv = inStr->ReadSegments(WriteSegmentFun, this, count, &countRead);
mRequest = 0;
return rv;
}
void

View File

@ -46,6 +46,9 @@ protected:
// Buffer to accumulate incoming data. We preallocate if contentSize is
// available.
mozilla::Vector<uint8_t, 0> mData;
// Number of consumed bytes from the mData.
size_t mBytesConsumed;
};
#endif // nsIncrementalStreamLoader_h__

View File

@ -0,0 +1,82 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var tests = [
{data: '', chunks: [], status: Cr.NS_OK, consume: [],
dataChunks: ['']},
{data: 'TWO-PARTS', chunks: [4, 5], status: Cr.NS_OK, consume: [4, 5],
dataChunks: ['TWO-', 'PARTS', '']},
{data: 'TWO-PARTS', chunks: [4, 5], status: Cr.NS_OK, consume: [0, 0],
dataChunks: ['TWO-', 'TWO-PARTS', 'TWO-PARTS']},
{data: '3-PARTS', chunks: [1, 1, 5], status: Cr.NS_OK, consume: [0, 2, 5],
dataChunks: ['3', '3-', 'PARTS', '']},
{data: 'ALL-AT-ONCE', chunks: [11], status: Cr.NS_OK, consume: [0],
dataChunks: ['ALL-AT-ONCE', 'ALL-AT-ONCE']},
{data: 'ALL-AT-ONCE', chunks: [11], status: Cr.NS_OK, consume: [11],
dataChunks: ['ALL-AT-ONCE', '']},
{data: 'ERROR', chunks: [1], status: Cr.NS_ERROR_OUT_OF_MEMORY, consume: [0],
dataChunks: ['E', 'E']}
];
/**
* @typedef TestData
* @property {string} data - data for the test.
* @property {Array} chunks - lengths of the chunks that are incrementally sent
* to the loader.
* @property {number} status - final status sent on onStopRequest.
* @property {Array} consume - lengths of consumed data that is reported at
* the onIncrementalData callback.
* @property {Array} dataChunks - data chunks that are reported at the
* onIncrementalData and onStreamComplete callbacks.
*/
function execute_test(test) {
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
stream.data = test.data;
let channel = {
contentLength: -1,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel])
};
let chunkIndex = 0;
let observer = {
onStreamComplete: function(loader, context, status, length, data) {
equal(chunkIndex, test.dataChunks.length - 1);
var expectedChunk = test.dataChunks[chunkIndex];
equal(length, expectedChunk.length);
equal(String.fromCharCode.apply(null, data), expectedChunk);
equal(status, test.status);
},
onIncrementalData: function (loader, context, length, data, consumed) {
ok(chunkIndex < test.dataChunks.length - 1);
var expectedChunk = test.dataChunks[chunkIndex];
equal(length, expectedChunk.length);
equal(String.fromCharCode.apply(null, data), expectedChunk);
consumed.value = test.consume[chunkIndex];
chunkIndex++;
},
QueryInterface:
XPCOMUtils.generateQI([Ci.nsIIncrementalStreamLoaderObserver])
};
let listener = Cc["@mozilla.org/network/incremental-stream-loader;1"]
.createInstance(Ci.nsIIncrementalStreamLoader);
listener.init(observer);
listener.onStartRequest(channel, null);
var offset = 0;
test.chunks.forEach(function (chunkLength) {
listener.onDataAvailable(channel, null, stream, offset, chunkLength);
offset += chunkLength;
});
listener.onStopRequest(channel, null, test.status);
}
function run_test() {
tests.forEach(execute_test);
}

View File

@ -169,6 +169,7 @@ skip-if = os == "android"
skip-if = bits != 32
[test_bug935499.js]
[test_bug1064258.js]
[test_bug1218029.js]
[test_udpsocket.js]
[test_doomentry.js]
[test_cacheflags.js]