yikuansun / PhotopeaAPI

JS Photopea API simplifyer
https://www.npmjs.com/package/photopea
MIT License
13 stars 1 forks source link

TypeError: target is null: console spam #2

Closed spector700 closed 1 month ago

spector700 commented 1 month ago

Hello,

I am trying to use your class. If I try to close open documents before opening another file, It works, however the console gets spammed with target is null and doesn't stop:

TypeError: target is null
[index.js:5459:35](moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js)
    buildStyleSheet moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5459
    iterateReadyRules moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5454
    forEach self-hosted:160
    iterateReadyRules moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5449
    buildStyleSheet moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5458
    modifySheet moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5486
    buildOverrides moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5948
    syncStylePositionWatcher moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5939
    restore moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:302
    frameId moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:99
    (Async: FrameRequestCallback)
    throttled moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:96
    observer moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:312
    (Async: MutationCallback)
    watchForNodePosition moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:304
    prepareOverridesSheet moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5934
    modifySheet moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5427
    buildOverrides moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5948
    render moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:5961
    watchForUpdates moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7675
    forEach self-hosted:160
    watchForUpdates moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7674
    handleStyleOperations moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:6302
    handleMinorTreeMutations moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:6329
    observer moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:490
    forEach self-hosted:4288
    observer moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:489
    (Async: MutationCallback)
    createOptimizedTreeObserver moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:471
    observe moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:6404
    watchForStylePositions moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:6427
    watchForStyleChanges moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:6459
    watchForUpdates moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7652
    runDynamicStyle moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7597
    createThemeAndWatchForUpdates moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7604
    success moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7870
    ready moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7883
    createOrUpdateDynamicThemeInternal moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7887
    createOrUpdateDynamicTheme moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:7831
    onMessage moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js:8320
    apply self-hosted:2292
    raw resource://gre/modules/ExtensionCommon.sys.mjs:2825
    wrapResponse resource://gre/modules/ExtensionChild.sys.mjs:207
    responses resource://gre/modules/ExtensionChild.sys.mjs:176
    map self-hosted:178
    emit resource://gre/modules/ExtensionChild.sys.mjs:176
    recvRuntimeMessage resource://gre/modules/ExtensionChild.sys.mjs:377
    _recv resource://gre/modules/ConduitsChild.sys.mjs:90
    receiveMessage resource://gre/modules/ConduitsChild.sys.mjs:201
TypeError: target is null
17 [index.js:5459:35](moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js)
TypeError: target is null
[index.js:5459:35](moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js)
TypeError: target is null
16 [index.js:5459:35](moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js)
TypeError: target is null
2 [index.js:5459:35](moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js)
TypeError: target is null
16 [index.js:5459:35](moz-extension://b13e444e-d576-4ff5-b0a2-911be55e61a8/inject/index.js)
TypeError: target is null

Here is the code:

const container = document.getElementById("embed");
let photopea = await PhotopeaFrame.createEmbed(container);

async function _uploadPSD(file) {
  //Close all the open documents
  await photopea.runScript(`
        if (app.documents.length){
          app.activeDocument.close()
        }
        console.log("All documents closed");
    `);

    await photopea.loadAsset(file);
    // if it uploaded then show image button
    const imageButton = document.getElementById("imageInput");
    imageButton.hidden = false;
    imageButton.addEventListener("change", handleImageSelection);

}

If there is no open files, it opens fine and shows no errors.

If there is an open file, it closes fine and opens the next file fine. Then shows non-stop error.

yikuansun commented 1 month ago

@spector700 Hi Nick, thanks so much for the report. At the moment I'm not too sure what's going on (seems like a pretty weird bug), but I do see moz-extension URL's in the error message, and that seems like an indicator that it might have to do with certain browser extensions. Ivan Kutskir has announced that Photopea works best when browser extensions are disabled. If possible, could you try disabling your browser extensions? If that doesn't work, let me know and we can try to investigate this in more detail!

spector700 commented 1 month ago

Yeah, It looks like it was the Dark Reader extension. Thanks for the help.

Do you have any idea why that happens? If I have app.activeDocument.close() in a script to insert images into smart objects, it doesn't do that.

But If I do it separately, then it does the spamming.

yikuansun commented 1 month ago

@spector700 Glad to have helped. As for your error message, it's hard to say, but it looks like something to do with MutationObserver (called when DOM elements are created or destroyed). Seems like your Dark Reader extension has trouble iterating through the "tab" elements in Photopea, probably trying to access elements after they've been modified/deleted, hence the target is null.

yikuansun commented 1 month ago

@spector700 By the way, just out of curiosity, may I ask what your project is about?

spector700 commented 1 month ago

So far, I am building a site to add an image to all the smart objects, then resize to fit the screen, then show the result or download. Perhaps have a canvas on the page to position it with a live preview.

So I had A working version of all this, but I wanted to clean it up with your class. However, I'm also having another issue.

I have this function that I send to open all the smart objects:

  const smartObjects = await photopea.runScript(
    findSmartLayers.toString() + "findSmartLayers()",
  );
  console.log(smartObjects);
function findSmartLayers() {
  let smartObjects = [];
  const mainDoc = app.activeDocument;
  let index = { nameIndex: 0 };

  function loopLayers(parrentLayer) {
    let layers = parrentLayer.layers;

    for (let i = 0; i < layers.length; i++) {
      if (layers[i].typename == "LayerSet") {
        // loop throgh the layerSet (folder)
        loopLayers(layers[i]);
      } else if (
        layers[i].kind == LayerKind.SMARTOBJECT &&
        layers[i].positionLocked == false
      ) {
        // open the smart object
        app.activeDocument.activeLayer = layers[i];
        executeAction(stringIDToTypeID("placedLayerEditContents"));

        // rename to the index so each is unieque when useing getByName()
        app.activeDocument.name = index.nameIndex;
        index.nameIndex += 1;

        smartObjects.push({
          docName: app.activeDocument.name,
          name: layers[i].name,
          visible: layers[i].visible,
          width: app.activeDocument.width,
          height: app.activeDocument.height,
          resolution: app.activeDocument.resolution,
        });

        // reactivate the main document
        app.activeDocument = mainDoc;
      }
    }
  }

  loopLayers(mainDoc);
  app.echoToOE(JSON.stringify({ SMART_OBJECTS: smartObjects }));
}

It works and opens all of them. However, the app.echoToOE(JSON.stringify({ SMART_OBJECTS: smartObjects })); is never received in the array. I'm guessing it's because it's receiving 'done' too early? (I'm bad at JavaScript promises)

spector700 commented 1 month ago

I can do something like this to have a custom finish message, which works. But I don't know if this will cause me problems latter.

  async runScript(script) {
    await this._pause();
    let message = script + " \napp.echoToOE('FINISHED')";
    let waitForMessage = new Promise((res) => {
      let outputs = [];
      let messageHandle = (e) => {
        if (e.source == this.contentWindow) {
          if (e.data == "done") return;

          outputs.push(e.data);
          if (e.data == "FINISHED") {
            window.removeEventListener("message", messageHandle);
            res(outputs);
          }
        }
      };
      window.addEventListener("message", messageHandle);

      this.contentWindow.postMessage(message, "*");
    });
    return await waitForMessage;
  }
yikuansun commented 1 month ago

@spector700 I was able to replicate your issue. ["done"] was echoed the first time, but the second time it worked as expected. So I suppose you could get away with calling runScript until you get a response other than done:

let smartObjects = ["done"];
let reps = 0; // count loop repetitions to avoid infinite loop
while (smartObjects && smartObjects[0] == "done") {
    smartObjects = await photopea.runScript(
        findSmartLayers.toString() + "findSmartLayers()",
    );
    console.log(smartObjects);

    if (reps > 10) break; // avoid an infinite loop
}