I’m writing a user-script that uses WebComponents, i.e. class TestElement 
extends HTMLElement, this.attachShadow({ mode: "open" }), customElements.
define("test-element", TestElement), etc. But it seems, that due to the 
security system of GreaseMonkey, I can’t fully use WebComponents: instance 
methods, getters and setters aren’t applied to my <test-element> elements. 
Yet, I have found a work-around: the only method actually running is the 
constructor, within which I can define methods, getters and setters, which 
*do* work.

Here is my minimal example code, demonstrating the issue:

// ==UserScript==
// @name     WebComponents test
// @include  http://example.com/
// @run-at   document-start
// ==/UserScript==

addEventListener("DOMContentLoaded", function(){ "use strict";
  class TestElement extends HTMLElement{
    constructor(){
      super();
      
      const shadow = this.attachShadow({
          mode: "open"
        });
      
      this.content = shadow.appendChild(document.createElement("div"));
      Object.defineProperty(this, "testGetter", {
        get(){
          return this.content.textContent || "something";
        },
        set(value){
          this.content.textContent = value;
        }
      });
      Object.defineProperty(this, "testMethod", {
        value(){
          console.log("Hello, World");
        }
      });
      
      console.log("Constructor actually runs."); // It does.
    }
    
    get dataGetter(){
      return this.content.textContent || "something";
    }
    
    set dataGetter(value){
      this.content.textContent = value;
    }
    
    someMethod(){
      console.log("Hello, World");
    }
  }
  
  customElements.define("test-element", TestElement);
  
  const testElement = document.createElement("test-element");
  
  document.body.append(testElement);
  
  console.log("instance", testElement instanceof TestElement); // false
  console.log("dataGetter in", "dataGetter" in testElement); // false
  console.log("dataGetter property", testElement.dataGetter); // undefined
  testElement.dataGetter = "Test"; // Silently fails.
  Object.assign(testElement, {
    dataGetter: "Test"
  }); // Silently fails.
  console.log("dataGetter property", testElement.dataGetter); // "Test", 
but not from the Getter.
  
  console.log("testGetter in", "testGetter" in testElement); // true
  console.log("testGetter property", testElement.testGetter); // "something"
  testElement.testGetter = "Test"; // Works.
  Object.assign(testElement, {
    testGetter: "Test"
  }); // Works.
  console.log("testGetter property", testElement.testGetter); // "Test"
  
  testElement.testMethod(); // Logs "Hello, World"
  
  testElement.someMethod(); // TypeError: testElement.someMethod is not a 
function
});

The comments explain what’s going on. As the code is running on the webpage 
http://example.com/, the JavaScript context doesn’t seem to be allowed to 
access TestElement instance properties that were defined on the class 
itself. But since the constructor runs, I can define new instance 
properties, using Object.defineProperty. To be clear, all of this would 
work perfectly fine, if the script was a normal content script within a 
<script>, not using a user-script.

What I ended up using in my real script is this one-liner after super();—it 
takes all instance properties of the class (which are accessible, if the 
class is accessed directly), and redefines every property on the current 
instance, except constructor:

Object.defineProperties(this, Object.fromEntries(Object.entries(Object.
getOwnPropertyDescriptors(MyCustomElement.prototype))
  .filter(([key]) => key !== "constructor")));

This is the easiest work-around I came up with, but is it really not 
possible to use WebComponents *normally* in GreaseMonkey user-scripts? The 
work-around still doesn’t account for testElement instanceof TestElement.

-- 
You received this message because you are subscribed to the Google Groups 
"greasemonkey-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/greasemonkey-users/d4d410d4-49ba-463f-b5e2-9e1d994b6800%40googlegroups.com.

Reply via email to