Firefox Addon SDK: Loading addon file into iframe

后端 未结 3 1422
猫巷女王i
猫巷女王i 2020-12-09 13:52

I want to load a resource:// link, respectively a local file from my Firefox addon into an iframe in a web page.

The reason is, that the re

相关标签:
3条回答
  • 2020-12-09 14:05

    I'm using zer0's idea. However, on pages with a content-security-policy I got an exception (...result = 2153644038). For that reason, I add my domain to the content-security-policy of the response header:

    //main.js 
    var { Cc, Ci } = require('chrome');
    
    var observer = {
        observe : function(aSubject, aTopic, aData) {
          if (aTopic == "http-on-examine-response") {
            aSubject.QueryInterface(Ci.nsIHttpChannel);
            var csp = aSubject.getResponseHeader("content-security-policy");
            if(csp.indexOf('frame-src') > -1) {
                var cspParts = csp.split(';');
                for (var i=0; i<cspParts.length; i++) {
                    if(cspParts[i].indexOf('frame-src') > -1) {
                        cspParts[i] += ' yourdomain.tld';
                        break;
                    }
                }
                aSubject.setResponseHeader("content-security-policy", cspParts.join(';'), false);
            } 
          }
        }
    }; 
    var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
    observerService.addObserver(observer, "http-on-examine-response", false);
    
    //content-script.js 
    iframe.src = yourdomain.tld/...
    

    EDIT: This code might be rejected by a FF reviewer with: "Modifying 'content-security-policy' is not allowed for security reasons."

    0 讨论(0)
  • 2020-12-09 14:08

    I think I suggested in the bug or in the ML of jetpack a terrible workaround, that basically is convert your resource:// in a data: url (using data.load to load the HTML content, and then encode and append as prefix, so something like that should works:

    /* main.js */
    const { data } = require('sdk/self');
    
    // just an example, you can use `tab.attach` too
    require('sdk/page-mod').PageMod({
      include: '*',
      contentScriptFile: data.url('content.js'),
      contentScriptOptions: {
        content: encodeURIComponent(data.load('index.html'))
      }
    });
    
    /* content.js */
    let iframe = document.body.appendChild(document.createElement('iframe'));
    
    iframe.setAttribute('sandbox', 'allow-scripts');
    // maybe you want also use the seamless attribute, see:
    // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
    
    iframe.contentWindow.location.href = 'data:text/html;charset=utf-8,' + self.options.content;
    

    It's a workaround of course, and I hope that the bug you mentioned will be fixed soon. Notice that in this way you cannot communicate directly from the iframe to the parent's document, but it means also that you can't do the way around, that is what you want to prevent.

    Of course, you can still use the add-on code to communicate between your iframe and the parent's document (you need to attach content scripts and use port and postMessage).

    Edit: changed the way the url is set, otherwise getting the src attribute from the parent's document is still possible, and contains whole HTML.

    0 讨论(0)
  • 2020-12-09 14:18

    I used a solution proposed here.

    It is implemented by creating a special protocol that is handled by your Firefox addon, which in turn requests the resources from its folder.
    Note that in case the resource folder may contain something non-public then I would add additional checks to allow only these resources that really are intended to be web-accessible.

    The custom-protocol code attached to the post mentioned above is available here:

    /*
    Makes any file within the data directory available to use in an iframe.
    Replace this: require("sdk/self").data.url(...)
    With this: require("name-of-this-file").url(...)
    */
    var { Class } = require('sdk/core/heritage');
    var { Unknown, Factory } = require('sdk/platform/xpcom');
    var { Cc, Ci, Cr } = require('chrome');
    var self = require("sdk/self");
    
    var resourceProtocolHandler = Cc["@mozilla.org/network/io-service;1"]
        .getService(Ci.nsIIOService)
        .getProtocolHandler('resource');
    
    var scheme = "res-" + self.id.toLowerCase().replace(/[^a-z0-9+\-\.]/g, "-");
    
    var AddonProtocolHandler = Class({
        extends: Unknown,
        interfaces: ['nsIProtocolHandler'],
    
        scheme: scheme,
        defaultPort: -1,
        protocolFlags: Ci.nsIProtocolHandler.URI_STD
            | Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE
            | Ci.nsIProtocolHandler.URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT,
    
        newURI: function(spec, originCharset, baseURI) {
            let uri = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIStandardURL);
            uri.init(uri.URLTYPE_STANDARD, this.defaultPort, spec, originCharset, baseURI);
            return uri.QueryInterface(Ci.nsIURI);
        },
    
        newChannel: function(uri) {
            if (uri.spec.indexOf(exports.url("")) != 0) {
                throw Cr.NS_ERROR_ILLEGAL_VALUE;
            }
            var resourceUri = resourceProtocolHandler.newURI(uri.spec.replace(scheme + "://", "resource://"), uri.originCharset, null);
            var channel = resourceProtocolHandler.newChannel(resourceUri);
            channel.originalURI = uri;
            return channel;
        },
    
        allowPort: (port, scheme) => false
    });
    
    Factory({
        contract: "@mozilla.org/network/protocol;1?name=" + scheme,
        Component: AddonProtocolHandler
    });
    
    exports.url = function(url) {
        return self.data.url(url).replace("resource://", scheme + "://");
    };
    

    Additional note: for page scripts (not content scripts) this custom protocol helps in loading an iframe and other HTML elements, but not in loading a XMLHttpRequest or Worker. For the latter, still cross-origin restrictions apply and a security error is raised: "Access to restricted URI denied" in case of XMLHttpRequest, and "The operation is insecure." for Worker.
    In contrast, under Chrome the XMLHttpRequest to web_accessible_resources is permitted. Worker does not implement access to web_accessible_resources under Chrome either.
    BUT for Workers you can use this custom-protocol url for importScripts method. Using that you can also work around the problem of loading custom resources into Workers. This code works also in Chrome, as an alternative to using XMLHttpRequest.

    var code = "self.onmessage = function (message)\
    {\
        self.onmessage = null;\
        self.importScripts(message.data);\
    };";
    
    var blob = new Blob([code], {type: 'application/javascript'});
    var blobUrl = URL.createObjectURL(blob);
    var w = new Worker(blobUrl);
    w.postMessage(*webAccessibleResourceUrl*);
    URL.revokeObjectURL(blobUrl);
    
    0 讨论(0)
提交回复
热议问题