Closed Bug 1441141 Opened 2 years ago Closed 2 years ago

postMessage() exception order

Categories

(Core :: DOM: Core & HTML, enhancement, P3)

enhancement

Tracking

()

RESOLVED FIXED
mozilla61
Tracking Status
firefox61 --- fixed

People

(Reporter: annevk, Assigned: baku)

References

Details

Attachments

(2 files, 2 obsolete files)

Per the specification we should check transferList errors before serializing. https://github.com/w3c/web-platform-tests/pull/9672 has tests.
This is changing a bit in https://github.com/whatwg/html/pull/3557 and I'm also working on updating the tests.
Tests are updated now and available at http://w3c-test.org/html/infrastructure/safe-passing-of-structured-data/transfer-errors.window.html.

This should be fairly trivial to fix Andrea.
This part makes the test happy from a DOM side point of view. ArrayBuffers are handled by the JS engine. I'm going to submit a separate patch for that part.
Assignee: nobody → amarchesini
Attachment #8960913 - Flags: review?(bugs)
Comment on attachment 8960913 [details] [diff] [review]
part 1 - DOM side

Hopefully wpt catches all these different cases
Attachment #8960913 - Flags: review?(bugs) → review+
Attached patch part 2 - JS + DOM (obsolete) — Splinter Review
Attachment #8961007 - Flags: review?(sphink)
Attachment #8961007 - Flags: review?(bugs)
Duplicate of this bug: 1447662
These patches fix this test too: https://github.com/w3c/web-platform-tests/pull/10123
Comment on attachment 8961007 [details] [diff] [review]
part 2 - JS + DOM

>+StructuredCloneHolder::CustomCanTransferHandler(JSContext* aCx,
>+                                                JS::Handle<JSObject*> aObj)
>+{
>+  if (!mSupportsTransferring) {
>+    return false;
>+  }
>+
>+  JS::Rooted<JSObject*> obj(aCx, aObj);
>+
>+  {
>+    MessagePort* port = nullptr;
>+    nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port);
>+    if (NS_SUCCEEDED(rv)) {
>+      return true;
>+    }
>+
>+    if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
>+        mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) {
>+      OffscreenCanvas* canvas = nullptr;
>+      rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas);
>+      if (NS_SUCCEEDED(rv)) {
>+        return true;
>+      }
>+
>+      ImageBitmap* bitmap = nullptr;
>+      rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap);
>+      if (NS_SUCCEEDED(rv)) {
>+        return true;
>+      }
>+    }
>+  }
It is rather annoying that we have this MessagePort/OffscreenCanvas/ImageBitmap check now at least in 3 places, but I don't have better ideas.
But could you at least document somewhere what all needs to be updated when adding support for new transferable DOM objects.

> MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
> {
>   MOZ_ASSERT(mIdentifier);
> 
>+  if (mHasBeenTransferredOrClosed) {
>+    return false;
>+  }
>+
>+  mHasBeenTransferredOrClosed = true;
Why we need this? Shouldn't mState be enough to cover this?
Or add some new state?
Adding a boolean makes the object kind of have two states, mState + mHasBeenTransferredOrClosed, and that is 
rather confusing.
But please explain, and fix or ask review again.
Attachment #8961007 - Flags: review?(bugs) → review-
Comment on attachment 8961007 [details] [diff] [review]
part 2 - JS + DOM

> >+  mHasBeenTransferredOrClosed = true;

This is needed because we want to deny the transferring of a port only when it has been already transferred or when it has manually been closed.
mState is about the state machine of MessagePort. When port1 is closed, port2 is sync or async closed as well. But that port can still be transferred by spec. mHasBeenTransferredOrClosed is used to know if transferring or manual close() happen.

I'm going to add this comment:

// mHasBeenTransferredOrClosed is used to know if this port has been manually closed or transferred via postMessage. Note that if the entangled port is closed, this port is closed as well (see mState) but, just because close() has not been called directly, by spec, this port can still be transferred.
Attachment #8961007 - Flags: review- → review?(bugs)
> MessagePort/OffscreenCanvas/ImageBitmap check now at least in 3 places, but
> I don't have better ideas.
> But could you at least document somewhere what all needs to be updated when
> adding support for new transferable DOM objects.

Would be nice if we move part of this logic directly in webIDL bindings introducing [serializable/transferable] keywords.
Attachment #8961007 - Flags: review?(bugs) → review+
Comment on attachment 8961007 [details] [diff] [review]
part 2 - JS + DOM

Review of attachment 8961007 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/vm/StructuredClone.cpp
@@ +1699,5 @@
>              JSAutoCompartment ac(cx, arrayBuffer);
> +
> +            if (arrayBuffer->isDetached())
> +                return false;
> +

This needs to report an error if it's returning false. I think

        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);

would do it. (Ugh, should rename that to JSMSG_ARRAY_BUFFER_DETACHED; the actual message only mentions ArrayBuffer.)

This appears to be a bug in the current implementation. You could clone while transferring an ArrayBuffer, then detach that ArrayBuffer during cloning. If you're DifferentProcess, you'll end up with an empty ArrayBuffer with no error. Otherwise, you'll get an uncatchable exception.

This can't be right. We have tests for this.... oh.

            // The throw is not yet implemented, bug 919259.
            //var copy = deserialize(serialize([ old, mutator ], [old]));

I'm looking into that now, trying to understand the full issue here and whether we're making it worse. It appears that this spec fix is related to the older bug 919259 spec bug. I'll r+ this patch because it's not making anything worse.

But at the very least: this cannot just return false without setting an exception. That would throw an uncatchable exception. It's a little to figure out, because there are two pre-existing problems of this sort in this code already. The main one is

1710:                    return false; // already transferred data

I'll fix them in another bug, I guess. (This one is pretty easy to fix here, but I'm not sure the other one is quite as straightforward, and I need to add a test along with this one.)
Attachment #8961007 - Flags: review?(sphink)
Attachment #8961007 - Flags: review?(bugs)
Attachment #8961007 - Flags: review+
Comment on attachment 8961007 [details] [diff] [review]
part 2 - JS + DOM

Oops, I think I overwrote smaug's r+ somehow.
Attachment #8961007 - Flags: review?(bugs) → review+
Attached patch js.patch (obsolete) — Splinter Review
Steve, I need you to take a look at this patch again. There is 1 only 1 diff: I have to unwrap the tObj in order to have positive checks in is<ArrayBuffer> is<SomethingElse>.

I hope this is correct.
Attachment #8961007 - Attachment is obsolete: true
Attachment #8962016 - Flags: review?(sphink)
Comment on attachment 8962016 [details] [diff] [review]
js.patch

Review of attachment 8962016 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/vm/StructuredClone.cpp
@@ +1102,5 @@
>              return reportDataCloneError(JS_SCERR_TRANSFERABLE);
>          tObj = &v.toObject();
>  
> +        RootedObject unwrappedObj(cx, CheckedUnwrap(tObj));
> +

I'm surprised we haven't been bitten by this already!

This should work, except (1) you should check the result of CheckedUnwrap and ReportAccessDenied(cx) if it fails; (2) you should use JSAutoCompartment ac(cx, unwrappedObj) so that we call canTransfer() from the matching compartment; and (3) the wrapped tObj should be stored in transferableObjects, not the unwrapped one, as otherwise it would create a cross-compartment edge and if we happened to GC one side and not the other, Bad Stuff would happen.

Hopefully it will still work with those changes. I'll look at creating a test case for the existing code.
Attachment #8962016 - Flags: review?(sphink)
Attachment #8962016 - Attachment is obsolete: true
Attachment #8962651 - Flags: review?(sphink)
Comment on attachment 8962651 [details] [diff] [review]
part 2 - JS + DOM

Review of attachment 8962651 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/vm/StructuredClone.cpp
@@ +1133,5 @@
> +                return reportDataCloneError(JS_SCERR_TRANSFERABLE);
> +
> +            JSAutoCompartment ac(cx, unwrappedObj);
> +            if (!callbacks->canTransfer(cx, unwrappedObj, closure))
> +                return false;

I guess this is fine.

I was imagining the JSAutoCompartment to happen right after the unwrapping, so that all of the type handling would be within the target compartment. But conceptually the transferableObjects lookup/insert is in the outer compartment, so that would require a separate scope... ok, what you have is fine.

If we ever touch this code again, I think the series of if statements should probably be lifted into a separate function that does a JSAutoCompartment as its first statement.
Attachment #8962651 - Flags: review?(sphink) → review+
Pushed by amarchesini@mozilla.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/0fa314a4f050
MessagePort cannot be cloned/transferred if disentanged, r=smaug
https://hg.mozilla.org/integration/mozilla-inbound/rev/9890a6738737
Update the StructuredCloneAlgorithm to follow the latest version of the spec, r=smaug, r=sfink
https://hg.mozilla.org/mozilla-central/rev/0fa314a4f050
https://hg.mozilla.org/mozilla-central/rev/9890a6738737
Status: NEW → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla61
Created web-platform-tests PR https://github.com/w3c/web-platform-tests/pull/10309 for changes under testing/web-platform/tests
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.