New issue
Advanced search Search tips

Issue 598171 link

Starred by 4 users

Issue metadata

Status: WontFix
Owner: ----
Closed: Jul 2016
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: All
Pri: 2
Type: Bug



Sign in to add a comment

Event data lost when dispatching event from extension content script (and listening from page script)

Reported by teo8...@gmail.com, Mar 26 2016

Issue description

UserAgent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36

Steps to reproduce the problem:
1. From a script in a page:

document.body.addEventListener("myArbitraryEventType",function(e){
  console.log(e.detail);
});

2. From the content script of an extension:

document.body.dispatchEvent(new CustomEvent("myArbitraryEventType", {
  detail: {
    foo: "foo",
    bar: "bar"
  }
}));

What is the expected behavior?
Output:
{
    foo: "foo",
    bar: "bar"
  }

What went wrong?
Output: null.

WebStore page: 

Did this work before? N/A 

Chrome version: 49.0.2623.87  Channel: n/a
OS Version: 
Flash Version: Shockwave Flash 21.0 r0

A similar nonsense happens with KeyboardEvent events: some of the properties will have their values changed.

Either an extension cannot dispatch events on a page for security reasons (but I can't see why; there's already the "trusted" property to protect from untrusted events, plus if one installs an extension she trusts it in the first place; and finally, if an extension isn't allowed to trigger events then you may as well not support extensions at all because half of them couldn't be of any use), 
or it can. 

If it cannot , either the event should not be captured at all or an exception should be thrown when trying to dispatch it.
If it can, all the information should be intact.

This is especially true for CustomEvent events.

Having the event be succesfully triggered and captured, but with random parts of the information altered or thrown away, is total nonsense.
 

Comment 1 by rob@robwu.nl, Apr 28 2016

Components: Blink>Bindings
Labels: -OS-Linux OS-All
Status: Untriaged (was: Unconfirmed)
Reproduced in 49.0.2623.75 and 50.0.2661.75, but only after modifying snippet 1 (by adding a non-serializable DOM object in the mix), so using the next steps:

1. Visit example.com
2. Run snippet 1 below in the main world.
3. Switch the context to a content script.
4. Run snippet 2 from the original report in that content script context.
5. Observed the following message in the console: Object {foo: "foo", bar: "bar"}

document.body.dispatchEvent(new CustomEvent("myArbitraryEventType", {
  detail: {
    boo: document.documentElement,  // <-- DOM object
    foo: "foo",
    bar: "bar"
  }
}));

At the moment, when an event.detail is shared with another world (content script -> main world in this case), the value is serialized using the structured cloning algorithm [1] (SerializedScriptValue in Blink) and then deserialized. This is to prevent leaking JavaScript references between the different worlds.

If you only store serializable objects in event.detail, then everything will be fine.

[1] https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm

Comment 2 by teo8...@gmail.com, Apr 28 2016

Your steps, taken literally, make no sense.

So, are you dispatching the event from the main world (i.e. the "real web page") and listening to it from the content script, or vice versa?

I dispatch it from the content script, and listen to it from the main world, and there definitely was non-DOM, trivial data (e.g. some integer and string properties of a KeyboardEvent) that was lost.

Comment 3 by rob@robwu.nl, Apr 28 2016

Oh, I mixed up snippet 1 and 2 in my explanation. I first set up the listener as in the report (in the main world), and then dispatched the listener from the content script (using my snippet to get null, your snippet gives the object as expected).

I also did the reverse, i.e. sending the message from the content script to the main world and got the same result.

Comment 4 by teo8...@gmail.com, Apr 28 2016

I actually can't reproduce the issue with a CustomEvent with simple data as in my example: I guess I was passing some DOM object when I reproduced the issue.

However, if I dispatch a KeyboardEvent, all or many of its properties get altered, even elementary values such as keyCode and the like.
 
I guess at some level it's for the same reason (a KeyboardEvent is, I guess, deeply related to the DOM).

This is nonetheless a bug. Not being able to communicate with the scripts in the page through events and pass them DOM data from the extension (which does have access to the same DOM) is an unacceptable limitation. If the whatever-algorithm doesn't allow to serialize DOM data, some other way of passing the information should be used.

Also, being unable to forge and dispatch keyboard and mouse events and the like (which you *can* do from a regular, i.e. non-extension, script) is unacceptable.

Finally, silently discarding or, even worse, altering properties of the event (as is the case of KeyboardEvents), is an abomination (as opposed to throwing an exception on the dispatching side and not triggering the event at all on the "listening" side - which would still be an unacceptable crippling limitation but would at least be debuggable and have some consistency).

Comment 5 by teo8...@gmail.com, Apr 28 2016

(or at least having a warning on the dispatching side)

Comment 6 by rob@robwu.nl, Apr 29 2016

It seems not even possible to modify keyCode, etc. for KeyboardEvents in the SAME world since these properties are read-only. If you use JavaScript strict mode ('use strict';), then you would have received a TypeError upon setting e.g. keyCode:

'use strict';
var e = new KeyboardEvent("myArbitraryEventType");
e.keyCode = 1;
// => Uncaught TypeError: Cannot set property keyCode of #<KeyboardEvent> which has only a getter

// versus
var e = new KeyboardEvent("myArbitraryEventType");
e.keyCode = 1;          // No error.
console.log(e.keyCode); // 0 = unchanged...

(and it is neither possible to create a KeyboardEvent with some initial keyCode,  bug 381175 ).
(event.key and event.code CAN be initialized via the constructor, but in Chrome it is currently behind an experimental flag -  issue 327853 ).

Comment 7 by teo8...@gmail.com, Apr 29 2016

> It seems not even possible to modify keyCode, etc. for KeyboardEvents
> in the SAME world 

It definitely IS possible, at least to CREATE KeyboardEvents, as I have done and keep doing it using this library that I attach, which I found somewhere (can't find the source right now; I may have modified it slightly)


I do a console.log() of the event I have created prior to dispatching it, and it does have all the values I have set.
The relevant thing is that the event I dispatch from the Extension world (content script) is not equal to the event  that is caught in the main world.

However, if I create the exact same KeyboardEvents in the exact same way in the main world and dispatch them, and catch them in the same main world, they are not altered
crossBrowser_initKeyboardEvent.js
5.7 KB View Download

Comment 8 by teo8...@gmail.com, Apr 29 2016

(when I say not equal, obviously, I mean the values of the properties, such as KeyCode etc)

Comment 9 by rob@robwu.nl, Apr 29 2016

The "KeyboardEvent" from your snippet is not real. What you've done is simply replacing the property descriptors of the event. The internal keyCode (etc.) does not change at all.

When your "KeyboardEvent" is dispatched in a different world, the internal keyCode (etc.) are used instead of the bolted-on fake properties.

Comment 10 by teo8...@gmail.com, Apr 29 2016

> The "KeyboardEvent" from your snippet is not real.

What snippet?
All I know is that if I create the Keyboard event (using the library I attached) and dispatch and catch it in the same world, it maintains all the properties as set; when I dispatch it from the content script and catch it in the main world, most properties are reset. That cannot be right in any way. Either the properties cannot be modified at all, or the y can. The result of dispatching and catching the event in the same world should be identical to dispatching it from a world and catching it in the other.

Comment 11 by teo8...@gmail.com, Apr 29 2016

> When your "KeyboardEvent" is dispatched in a different world, the internal 
> keyCode (etc.) are used instead of the bolted-on fake properties.

If that's different from what happens when the event is dispatched in the same world, that's a bug.

Comment 12 by rob@robwu.nl, Apr 29 2016

> > The "KeyboardEvent" from your snippet is not real.
>
> What snippet?

The file that you attached in comment 7.

> All I know is that if I create the Keyboard event (using the library I attached) and dispatch and catch it in the same world, it maintains all the properties as set;

The properties as set, that is right. However the actual keyboard properties did not change. Let me illustrate what happens. Normally, you have this event:

+ KeyboardEvent instance
  + internal state (e.g. keyCode=0).
  + Inherit from KeyboardEvent.prototype:
    .keyCode (getter, returns internalState.keyCode)

With your library, the following happens:
+ KeyboardEvent instance
  + Fake .keyCode getter (shadows the real .keyCode getter from the prototype).
  + internal state (e.g. keyCode=0).

Your library reports a fake keyCode. Now, when you dispatch the event to another world, a different object (based on the event object and its internal details) is created, because of the strict isolation of JS objects between worlds. Consequently, the real (internal) keyCode is exposed again (because the fake keyCode getter is not copied to the other world).

Comment 13 by teo8...@gmail.com, Apr 29 2016

Oh I see. I hadn't realized the library defines its own derived event class. I thought it just offered a browser-independent way of *creating* the events and added/tweaked some properties (on the original event object).

So, is it completely impossible to forge and trigger a true keyboard (or mouse) event? If so, that sucks (though it's a separate issue). Is that dictated by the standard or a decision of Chrome? Either way, if true UI events (i.e. keyboard, mouse, touch events etc) cannot be created and dispatched in a standard JS way, then the Chrome extension API should provide an API for that. Trivial use case: create an extension that allows you to use some external device (e.g. a smartphone) as a keyboard or mouse. But again, that's a separate issue.



Anyway, at the very least this issue remains:
- when dispatching an event from a world to another, a warning (if not an error) should be issued if the event dispatched is, for whatever reason, not going to be identical to what it would be if dispatched in the same world (where what is preserved and what is changed is well defined by the standards)

I also think that this "strict isolation" of worlds sucks. There might be good reasons for it in general, but at least passing reference to actual DOM objects from a world to the other SHOULD be possible in some way.

Comment 14 by rob@robwu.nl, Apr 29 2016

> So, is it completely impossible to forge and trigger a true keyboard (or mouse) event?

Even in a normal web page, it is not possible to trigger a "true" keyboard/mouse event. For example, triggering Alt+F4 won't close the window.

If you're merely interested in triggering event listeners in the page, you can inject a script in the main world and use your current method of forging an event. Make sure that you do not inadvertently leak or modify the page's APIs to avoid side effects.


> when dispatching an event from a world to another, a warning (if not an error) should be issued if the event dispatched is, for whatever reason, not going to be identical to what it would be if dispatched in the same world (where what is preserved and what is changed is well defined by the standards)

There is no standard that dictates this, and people who care about the event will detect the missing value during development. In my opinion, a warning/error would be at most a nice-to-have, and not a must-have (note that I am not authorative nor responsible for the API, so my opinion is worth as much as any other random passerby). In comment 1 I described the de-facto requirements for event.detail, so now you can act upon it and make sure that your objects satisfy it.



> There might be good reasons for it in general, but at least passing reference to actual DOM objects from a world to the other SHOULD be possible in some way.

There is a way for elements: Insert the element in the document and dispatch a bubbling event.

Comment 15 by teo8...@gmail.com, Apr 29 2016

> Even in a normal web page, it is not possible to trigger a "true" 
> keyboard/mouse event. For example, triggering Alt+F4 won't close the window.

Then this holds: 
>> then the Chrome extension API should provide an API for that


[regarding the warning]
> There is no standard that dictates this

I think there's no standard at all for extensions for that matter.


> and people who care about the event will detect the missing value during development

Yeah, that could be argued about any kind of error. The problem is how many hours of debugging you have to do to figure out.


>If you're merely interested in triggering event listeners in the page, you can inject
> a script in the main world and use your current method of forging an event.

Yep, that's exactly the ugly hacky workaround I'm using.


> There is a way for elements: Insert the element in the document and 
> dispatch a bubbling event.

I was talking about elements that already exist in the document (which is the same document, seen by the main world and by the extension world).
Status: WontFix (was: Untriaged)
The issue seems not reproducible without having a DOM object in the event.  Serialization failure for a DOM object is intentional and expected.  Hence, I close this issue as WontFix.
Failing to serialize a DOM node as {} differs from what extension messaging does - JSON.stringify + JSON.parse.
Is this spec'd or just a quirk in implementation that was left untouched since inception?

Sign in to add a comment