Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"load" event handler is called prematurely for iframe.srcdoc #243

Closed
t2ym opened this issue Apr 18, 2018 · 2 comments
Closed

"load" event handler is called prematurely for iframe.srcdoc #243

t2ym opened this issue Apr 18, 2018 · 2 comments

Comments

@t2ym
Copy link
Owner

t2ym commented Apr 18, 2018

"load" event handler is called prematurely for iframe.srcdoc

Root Cause

  • When empty-document.html is loaded into an iframe, "load" event is dispatched before the content is loaded to the iframe document via its onload handler in its attribute converted from srcdoc

Fix

Patterns

  • Static HTML
<iframe id="staticIframe" srcdoc="HTML contents"></iframe>
let iframe = document.querySelector('#staticIframe');
iframe.addEventListener('load', function (event) { /* load event handler */});
let iframe = document.querySelector('#staticIframe');
iframe.onload = function (event) { /* load event handler */};
  • Dynamic HTML
let iframe = document.createElement('iframe');
iframe.srcdoc('HTML contents');
iframe.onload = function (event) { /* load event handler */};
document.body.appendChild(iframe);

Note

  • Static HTML with onload attribute handler is called on srcdoc-load event as of 0.0.233
@t2ym t2ym closed this as completed in 189150f Apr 18, 2018
t2ym added a commit that referenced this issue Apr 18, 2018
@t2ym
Copy link
Owner Author

t2ym commented Apr 18, 2018

Notes on the Fix

  • Indirect (normalized) operations like these are not supported
iframe.addEventHandler.call(iframe, 'load', function (event) { /* handler */ });
Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'onload').set.call(iframe, function (event) { /* handler */});
  • event.type is set as "srcdoc-load" not "load"
  • iframe._onload property is set as a dummy property and ineffective
  • TODO iframe.removeEventListener('load', handler) is not supported yet

@t2ym
Copy link
Owner Author

t2ym commented Apr 18, 2018

ACL with the Fix

    HTMLIFrameElement: {
      [S_CHAIN]: () => acl.HTMLElement,
      [S_PROTOTYPE]: {
        [S_CHAIN]: S_CHAIN,
        [S_INSTANCE]: {
          [S_CHAIN]: S_CHAIN,
          addEventListener: {
            [S_DEFAULT]: '---',
            '@iframe_contentWindow_accessor': function _iframeAddEventListenerAcl(normalizedThisArg,
                                                                                  normalizedArgs /* ['property', args], ['property', value], etc. */,
                                                                                  aclArgs /* [name, isStatic, isObject, property, opType, context] */,
                                                                                  hookArgs /* [f, thisArg, args, context, newTarget] */,
                                                                                  applyAcl /* for recursive application of ACL */) {
              let opType = aclArgs[4];
              if (opType === 'x') {
                if (normalizedArgs[1] && normalizedArgs[1][0] === 'load') {
                  if (!normalizedThisArg.src) {
                    normalizedThisArg.src = emptyDocumentURL;
                  }
                  if (hookArgs[0] === '()' || hookArgs[0] === '#()') {
                    if (hookArgs[1] === normalizedThisArg && hookArgs[2][1][0] === 'load') {
                      let onloadAttribute = normalizedThisArg.getAttribute('onload');
                      if (onloadAttribute && onloadAttribute.startsWith('event.target.contentDocument.write(')) {
                        hookArgs[2][1][0] = 'srcdoc-load';
                      }
                    }
                  }
                }
              }
              return 'r-x'[opTypeMap[opType]] === opType; // equivalent to 'r-x' acl
            },
          },
          onload: {
            [S_DEFAULT]: '---',
            '@iframe_contentWindow_accessor': function _iframeOnloadAcl(normalizedThisArg,
                                                                        normalizedArgs /* ['property', args], ['property', value], etc. */,
                                                                        aclArgs /* [name, isStatic, isObject, property, opType, context] */,
                                                                        hookArgs /* [f, thisArg, args, context, newTarget] */,
                                                                        applyAcl /* for recursive application of ACL */) {
              let opType = aclArgs[4];
              if (opType === 'w') {
                if (hookArgs[1] === normalizedThisArg && hookArgs[2][0] === normalizedArgs[0]) {
                  let onloadAttribute = normalizedThisArg.getAttribute('onload');
                  if (onloadAttribute && onloadAttribute.startsWith('event.target.contentDocument.write(')) {
                    hookArgs[2][0] = '_onload'; // dummy to avoid overriding the existing onload event converted from srcdoc
                    normalizedThisArg.addEventListener('srcdoc-load', hookArgs[2][1]); // Redirect to srcdoc-load
                  }
                }
              }
              return 'rw-'[opTypeMap[opType]] === opType; // equivalent to 'rw-' acl
            },
          },
          contentDocument: {
            [S_DEFAULT]: '---',
            '@iframe_contentWindow_accessor': function _iframeContentDocumentAcl(normalizedThisArg,
                                                                                 normalizedArgs /* ['property', args], ['property', value], etc. */,
                                                                                 aclArgs /* [name, isStatic, isObject, property, opType, context] */,
                                                                                 hookArgs /* [f, thisArg, args, context, newTarget] */,
                                                                                 applyAcl /* for recursive application of ACL */) {
              let opType = aclArgs[4];
              if (!normalizedThisArg.getAttribute('src')) {
                return false; // reject on empty src
              }
              if (this.contentWindow && !this.contentWindow.__hook__) {
                return false; // reject on missing hook infrastructure
              }
              if (opType === 'r') {
                let contentWindow = normalizedThisArg.contentWindow;
                otherWindowObjects.set(contentWindow.Object, contentWindow);
                otherWindowObjectsStatus.set = true;
              }
              return 'r--'[opTypeMap[opType]] === opType; // equivalent to 'r--' acl
            },
          },
          contentWindow: {
            [S_DEFAULT]: '---',
            '@iframe_contentWindow_accessor': function _iframeContentWindowAcl(normalizedThisArg,
                                                                               normalizedArgs /* ['property', args], ['property', value], etc. */,
                                                                               aclArgs /* [name, isStatic, isObject, property, opType, context] */,
                                                                               hookArgs /* [f, thisArg, args, context, newTarget] */,
                                                                               applyAcl /* for recursive application of ACL */) {
              let opType = aclArgs[4];
              if (!normalizedThisArg.getAttribute('src')) {
                return false; // reject on empty src
              }
              if (this.contentWindow && !this.contentWindow.__hook__) {
                return false; // reject on missing hook infrastructure
              }
              if (opType === 'r') {
                let contentWindow = normalizedThisArg[normalizedArgs[0]]
                otherWindowObjects.set(contentWindow.Object, contentWindow);
                otherWindowObjectsStatus.set = true;
              }
              return 'r--'[opTypeMap[opType]] === opType; // equivalent to 'r--' acl
            },
          },
        },
      },
    },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant