constructor (frame, allowedOrigins) {
    const that = this;
    this.allowedOrigins = allowedOrigins || [];
    // Initialize communication
    this.frame = frame;
    this.callbacks = {};
    // List of functions extracted with this:
    // Run in firebug on http://svg-edit.googlecode.com/svn/trunk/docs/files/svgcanvas-js.html

    // for (const i=0,q=[],f = document.querySelectorAll('div.CFunction h3.CTitle a'); i < f.length; i++) { q.push(f[i].name); }; q
    // const functions = ['clearSelection', 'addToSelection', 'removeFromSelection', 'open', 'save', 'getSvgString', 'setSvgString',
    // 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility',
    // 'moveSelectedToLayer', 'clear'];

    // Newer, well, it extracts things that aren't documented as well. All functions accessible through the normal thingy can now be accessed though the API
    // const {svgCanvas} = frame.contentWindow;
    // const l = [];
    // for (const i in svgCanvas) { if (typeof svgCanvas[i] === 'function') { l.push(i);} };
    // alert("['" + l.join("', '") + "']");
    // Run in svgedit itself
    const functions = [
      'addExtension',
      'addSVGElementFromJson',
      'addToSelection',
      'alignSelectedElements',
      'assignAttributes',
      'bind',
      'call',
      'changeSelectedAttribute',
      'cleanupElement',
      'clear',
      'clearSelection',
      'clearSvgContentElement',
      'cloneLayer',
      'cloneSelectedElements',
      'convertGradients',
      'convertToGroup',
      'convertToNum',
      'convertToPath',
      'copySelectedElements',
      'createLayer',
      'cutSelectedElements',
      'cycleElement',
      'deleteCurrentLayer',
      'deleteSelectedElements',
      'embedImage',
      'exportPDF',
      'findDefs',
      'getBBox',
      'getBlur',
      'getBold',
      'getColor',
      'getContentElem',
      'getCurrentDrawing',
      'getDocumentTitle',
      'getEditorNS',
      'getElem',
      'getFillOpacity',
      'getFontColor',
      'getFontFamily',
      'getFontSize',
      'getHref',
      'getId',
      'getIntersectionList',
      'getItalic',
      'getMode',
      'getMouseTarget',
      'getNextId',
      'getOffset',
      'getOpacity',
      'getPaintOpacity',
      'getPrivateMethods',
      'getRefElem',
      'getResolution',
      'getRootElem',
      'getRotationAngle',
      'getSelectedElems',
      'getStrokeOpacity',
      'getStrokeWidth',
      'getStrokedBBox',
      'getStyle',
      'getSvgString',
      'getText',
      'getTitle',
      'getTransformList',
      'getUIStrings',
      'getUrlFromAttr',
      'getVersion',
      'getVisibleElements',
      'getVisibleElementsAndBBoxes',
      'getZoom',
      'groupSelectedElements',
      'groupSvgElem',
      'hasMatrixTransform',
      'identifyLayers',
      'importSvgString',
      'leaveContext',
      'linkControlPoints',
      'makeHyperlink',
      'matrixMultiply',
      'mergeAllLayers',
      'mergeLayer',
      'moveSelectedElements',
      'moveSelectedToLayer',
      'moveToBottomSelectedElement',
      'moveToTopSelectedElement',
      'moveUpDownSelected',
      'open',
      'pasteElements',
      'prepareSvg',
      'pushGroupProperties',
      'randomizeIds',
      'rasterExport',
      'ready',
      'recalculateAllSelectedDimensions',
      'recalculateDimensions',
      'remapElement',
      'removeFromSelection',
      'removeHyperlink',
      'removeUnusedDefElems',
      'renameCurrentLayer',
      'round',
      'runExtensions',
      'sanitizeSvg',
      'save',
      'selectAllInCurrentLayer',
      'selectOnly',
      'setBBoxZoom',
      'setBackground',
      'setBlur',
      'setBlurNoUndo',
      'setBlurOffsets',
      'setBold',
      'setColor',
      'setConfig',
      'setContext',
      'setCurrentLayer',
      'setCurrentLayerPosition',
      'setDocumentTitle',
      'setFillPaint',
      'setFontColor',
      'setFontFamily',
      'setFontSize',
      'setGoodImage',
      'setGradient',
      'setGroupTitle',
      'setHref',
      'setIdPrefix',
      'setImageURL',
      'setItalic',
      'setLayerVisibility',
      'setLinkURL',
      'setMode',
      'setOpacity',
      'setPaint',
      'setPaintOpacity',
      'setRectRadius',
      'setResolution',
      'setRotationAngle',
      'setSegType',
      'setStrokeAttr',
      'setStrokePaint',
      'setStrokeWidth',
      'setSvgString',
      'setTextContent',
      'setUiStrings',
      'setUseData',
      'setZoom',
      'svgCanvasToString',
      'svgToString',
      'transformListToTransform',
      'ungroupSelectedElement',
      'uniquifyElems',
      'updateCanvas',
      'zoomChanged'
    ];

    // TODO: rewrite the following, it's pretty scary.
    for (const func of functions) {
      this[func] = getCallbackSetter(func);
    }

    // Older IE may need a polyfill for addEventListener, but so it would for SVG
    window.addEventListener('message', getMessageListener(this));
    window.addEventListener('keydown', (e) => {
      const { type, key } = e;
      if (key === 'Backspace') {
        e.preventDefault();
        const keyboardEvent = new KeyboardEvent(type, { key });
        that.frame.contentDocument.dispatchEvent(keyboardEvent);
      }
    });
  }
@param {HTMLIFrameElement} frame
@param {string[]} [allowedOrigins=[]] Array of origins from which incoming
  messages will be allowed when same origin is not used; defaults to none.
  If supplied, it should probably be the same as svgEditor's allowedOrigins