Bug 1127885 - Console API should display blobs when used in workers, r=smaug

This commit is contained in:
Andrea Marchesini 2015-01-31 19:12:00 +01:00
parent 22c09069b1
commit ffb8a11eea
4 changed files with 125 additions and 27 deletions

View File

@ -6,7 +6,9 @@
#include "mozilla/dom/Console.h" #include "mozilla/dom/Console.h"
#include "mozilla/dom/ConsoleBinding.h" #include "mozilla/dom/ConsoleBinding.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/Exceptions.h" #include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/ToJSValue.h"
#include "mozilla/Maybe.h" #include "mozilla/Maybe.h"
#include "nsCycleCollectionParticipant.h" #include "nsCycleCollectionParticipant.h"
@ -41,9 +43,10 @@
// console.trace(). // console.trace().
#define DEFAULT_MAX_STACKTRACE_DEPTH 200 #define DEFAULT_MAX_STACKTRACE_DEPTH 200
// This tag is used in the Structured Clone Algorithm to move js values from // This tags are used in the Structured Clone Algorithm to move js values from
// worker thread to main thread // worker thread to main thread
#define CONSOLE_TAG JS_SCTAG_USER_MIN #define CONSOLE_TAG_STRING JS_SCTAG_USER_MIN
#define CONSOLE_TAG_BLOB JS_SCTAG_USER_MIN + 1
using namespace mozilla::dom::exceptions; using namespace mozilla::dom::exceptions;
using namespace mozilla::dom::workers; using namespace mozilla::dom::workers;
@ -51,6 +54,14 @@ using namespace mozilla::dom::workers;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
struct
ConsoleStructuredCloneData
{
nsCOMPtr<nsISupports> mParent;
nsTArray<nsString> mStrings;
nsTArray<nsRefPtr<FileImpl>> mFiles;
};
/** /**
* Console API in workers uses the Structured Clone Algorithm to move any value * Console API in workers uses the Structured Clone Algorithm to move any value
* from the worker thread to the main-thread. Some object cannot be moved and, * from the worker thread to the main-thread. Some object cannot be moved and,
@ -63,29 +74,41 @@ namespace dom {
static JSObject* static JSObject*
ConsoleStructuredCloneCallbacksRead(JSContext* aCx, ConsoleStructuredCloneCallbacksRead(JSContext* aCx,
JSStructuredCloneReader* /* unused */, JSStructuredCloneReader* /* unused */,
uint32_t aTag, uint32_t aData, uint32_t aTag, uint32_t aIndex,
void* aClosure) void* aClosure)
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
ConsoleStructuredCloneData* data =
static_cast<ConsoleStructuredCloneData*>(aClosure);
MOZ_ASSERT(data);
MOZ_ASSERT(data->mParent);
if (aTag != CONSOLE_TAG) { if (aTag == CONSOLE_TAG_STRING) {
return nullptr; MOZ_ASSERT(data->mStrings.Length() > aIndex);
JS::Rooted<JS::Value> value(aCx);
if (!xpc::StringToJsval(aCx, data->mStrings.ElementAt(aIndex), &value)) {
return nullptr;
}
JS::Rooted<JSObject*> obj(aCx);
if (!JS_ValueToObject(aCx, value, &obj)) {
return nullptr;
}
return obj;
} }
nsTArray<nsString>* strings = static_cast<nsTArray<nsString>*>(aClosure); if (aTag == CONSOLE_TAG_BLOB) {
MOZ_ASSERT(strings->Length() > aData); MOZ_ASSERT(data->mFiles.Length() > aIndex);
JS::Rooted<JS::Value> value(aCx); nsRefPtr<File> file =
if (!xpc::StringToJsval(aCx, strings->ElementAt(aData), &value)) { new File(data->mParent, data->mFiles.ElementAt(aIndex));
return nullptr; return file->WrapObject(aCx);
} }
JS::Rooted<JSObject*> obj(aCx); MOZ_CRASH("No other tags are supported.");
if (!JS_ValueToObject(aCx, value, &obj)) { return nullptr;
return nullptr;
}
return obj;
} }
// This method is called by the Structured Clone Algorithm when some data has // This method is called by the Structured Clone Algorithm when some data has
@ -96,6 +119,21 @@ ConsoleStructuredCloneCallbacksWrite(JSContext* aCx,
JS::Handle<JSObject*> aObj, JS::Handle<JSObject*> aObj,
void* aClosure) void* aClosure)
{ {
ConsoleStructuredCloneData* data =
static_cast<ConsoleStructuredCloneData*>(aClosure);
MOZ_ASSERT(data);
nsRefPtr<File> file;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, file)) &&
file->Impl()->MayBeClonedToOtherThreads()) {
if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB, data->mFiles.Length())) {
return false;
}
data->mFiles.AppendElement(file->Impl());
return true;
}
JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj)); JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value)); JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
if (!jsString) { if (!jsString) {
@ -107,14 +145,12 @@ ConsoleStructuredCloneCallbacksWrite(JSContext* aCx,
return false; return false;
} }
nsTArray<nsString>* strings = static_cast<nsTArray<nsString>*>(aClosure); if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_STRING,
data->mStrings.Length())) {
if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG, strings->Length())) {
return false; return false;
} }
strings->AppendElement(string); data->mStrings.AppendElement(string);
return true; return true;
} }
@ -414,7 +450,7 @@ private:
JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments)); JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
if (!mArguments.write(aCx, value, &gConsoleCallbacks, &mStrings)) { if (!mArguments.write(aCx, value, &gConsoleCallbacks, &mData)) {
return false; return false;
} }
@ -451,8 +487,13 @@ private:
mCallData->SetIDs(id, frame.mFilename); mCallData->SetIDs(id, frame.mFilename);
} }
// Now we could have the correct window (if we are not window-less).
mData.mParent = aInnerWindow;
ProcessCallData(aCx); ProcessCallData(aCx);
mCallData->CleanupJSObjects(); mCallData->CleanupJSObjects();
mData.mParent = nullptr;
} }
private: private:
@ -462,7 +503,7 @@ private:
ClearException ce(aCx); ClearException ce(aCx);
JS::Rooted<JS::Value> argumentsValue(aCx); JS::Rooted<JS::Value> argumentsValue(aCx);
if (!mArguments.read(aCx, &argumentsValue, &gConsoleCallbacks, &mStrings)) { if (!mArguments.read(aCx, &argumentsValue, &gConsoleCallbacks, &mData)) {
return; return;
} }
@ -494,7 +535,7 @@ private:
ConsoleCallData* mCallData; ConsoleCallData* mCallData;
JSAutoStructuredCloneBuffer mArguments; JSAutoStructuredCloneBuffer mArguments;
nsTArray<nsString> mStrings; ConsoleStructuredCloneData mData;
}; };
// This runnable calls ProfileMethod() on the console on the main-thread. // This runnable calls ProfileMethod() on the console on the main-thread.
@ -539,7 +580,7 @@ private:
JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments)); JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
if (!mBuffer.write(aCx, value, &gConsoleCallbacks, &mStrings)) { if (!mBuffer.write(aCx, value, &gConsoleCallbacks, &mData)) {
return false; return false;
} }
@ -552,8 +593,14 @@ private:
{ {
ClearException ce(aCx); ClearException ce(aCx);
// Now we could have the correct window (if we are not window-less).
mData.mParent = aInnerWindow;
JS::Rooted<JS::Value> argumentsValue(aCx); JS::Rooted<JS::Value> argumentsValue(aCx);
if (!mBuffer.read(aCx, &argumentsValue, &gConsoleCallbacks, &mStrings)) { bool ok = mBuffer.read(aCx, &argumentsValue, &gConsoleCallbacks, &mData);
mData.mParent = nullptr;
if (!ok) {
return; return;
} }
@ -585,7 +632,7 @@ private:
Sequence<JS::Value> mArguments; Sequence<JS::Value> mArguments;
JSAutoStructuredCloneBuffer mBuffer; JSAutoStructuredCloneBuffer mBuffer;
nsTArray<nsString> mStrings; ConsoleStructuredCloneData mData;
}; };
NS_IMPL_CYCLE_COLLECTION_CLASS(Console) NS_IMPL_CYCLE_COLLECTION_CLASS(Console)

View File

@ -97,6 +97,7 @@ support-files =
bug1062920_worker.js bug1062920_worker.js
webSocket_sharedWorker.js webSocket_sharedWorker.js
bug1104064_worker.js bug1104064_worker.js
worker_consoleAndBlobs.js
[test_404.html] [test_404.html]
[test_atob.html] [test_atob.html]
@ -197,3 +198,4 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828
[test_websocket_pref.html] [test_websocket_pref.html]
[test_bug1104064.html] [test_bug1104064.html]
[test_consoleAndBlobs.html]

View File

@ -0,0 +1,41 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test for console API and blobs</title>
<script src="/tests/SimpleTest/SimpleTest.js">
</script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
</head>
<body>
<script type="text/javascript">
function consoleListener() {
SpecialPowers.addObserver(this, "console-api-log-event", false);
}
var order = 0;
consoleListener.prototype = {
observe: function(aSubject, aTopic, aData) {
ok(true, "Something has been received");
is(aTopic, "console-api-log-event");
SpecialPowers.removeObserver(this, "console-api-log-event");
var obj = aSubject.wrappedJSObject;
is(obj.arguments[0].size, 3, "The size is correct");
is(obj.arguments[0].type, 'foo/bar', "The type is correct");
SimpleTest.finish();
}
}
var cl = new consoleListener();
new Worker('worker_consoleAndBlobs.js');
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View File

@ -0,0 +1,8 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
var b = new Blob(['123'], { type: 'foo/bar'});
console.log(b);