3

I build xul template and I added js inside the xul.

the main page is contain a iframe tag. I want to call to specific js function(inside the xul) from the iframe page loaded.

for example:

this is the xul file:

<?xml version="1.0"?>

         <script type="application/x-javascript"
        src="chrome://tuttoolbar/content/general.js" />

<toolbox id="navigator-toolbox">

    <toolbar id="TutTB-Toolbar" toolbarname="Tutorial Toolbar" accesskey="T"
             class="chromeclass-toolbar" context="toolbar-context-menu" 
             hidden="false" persist="hidden">

        <toolbarbutton id="TutTB-MainMenu" type="menu"
                       tooltiptext="Tutorial Toolbar Main Menu">
            <menupopup>
                <menuitem label="Google Home Page" accesskey="G"
                          tooltiptext="Navigate to Google"
                          oncommand="countChild('rso','li')" />

                <menuseparator />

                <menuitem label="Born Geek Website" accesskey="B"
                          tooltiptext="Navigate to Born Geek"
                          oncommand="te()" />
            </menupopup>
        </toolbarbutton>


    </toolbar>



</toolbox>

This is the General JS file (inside the xul file):

function box(){
alert("Box Work");
}

test.php - this is the source page that load inside the iframe:

     <html>
    <head>
    </head> 
    <body>
   <div id="call" onclick="javascript:box();">
    </body>
    </html>

this is the main file:

    <html>
    <head>
    </head> 
    <body>
   <iframe src="test.php" width="400" height="40"></iframe>
    </body>
    </html>

is there any way to do it ?

David-SkyMesh
  • 5,041
  • 1
  • 31
  • 38
Roy
  • 251
  • 3
  • 10

1 Answers1

1

The content (untrusted) document (including any IFRAMEs) can't directly run functions defined in extensions or in other privileged code loaded by XulRunner. Even if your privileged code assigns a function to the untrusted content window, as soon as the function tries to use/call something privileged you'll get a security error.

You can, however, have the privileged code add an Event Listener to the untrusted content window! With a little wrangling, you can build up a scheme for easily installing "privileged" functions into untrusted content -- i.e: breaking out of the security sandbox.


Here's an example of allowing the untrusted domWindow to perform a (privileged) cross-domain HTTP GET operation:

/* 
   Give the end-user this API: 
     xdomainGET(sURL, 
                function(
                  iHTTPStatusCode, 
                  sHTTPStatusText, 
                  sHTTPResponseText
                ) { ... })             
*/

function install_xdomainGET_on_my_special_page(chromeWindow, domWindow) {

  install_privileged_method(
    /* this is the current browser chromeWindow (your XUL overlay is here!) */
    chromeWindow,     

    /* this is the unstrusted domWindow containing your IFRAME              */
    domWindow,  

    /* the name of the method we're adding to the untrusted domWindow       */
    'xdomainGET',     

    /* This is the methodFactory */
    function(priv) {
      /* This is the method that will be installed onto the untrusted 
         domWindow. It takes two arguments, 'sURL' and 'cb'. 'sURL' is a 
         cross-domain HTTP URL which we want to be able to GET from the 
         untrusted domWindow. 'cb' is a callback function which returns 
         3 values to the caller...                                          */
      return function(sURL, cb) {
        priv.call([sURL], function(rstatus, rdata, rerror){
          cb(rstatus ? rdata.statusCode   : 0,      // iHTTPStatusCode
             rstatus ? rdata.statusText   : rerror, // sHTTPStatusText
             rstatus ? rdata.responseText : null);  // sHTTPResponseText
        });
      };
    },

    /* This is the privileged 'handler code. It has access to the 
       chromeWindow, and all of the privileged APIs that are available 
       there. (e.g: nsI*, XPCom, js-ctypes, etc)                            */
    function (chromeWindow, args, cb) {
      var [url] = args;
      var xhr = new chromeWindow.XMLHttpRequest();
      xhr.onload = function(){
        cb(1, {
          statusCode: this.status,
          statusText: this.statusText,
          responseText: this.responseText
        }, null);
      };
      xhr.addEventListener('error', function(error_evt) { 
        cb(0, null, error_evt.error); 
      }, false);
      xhr.open('get', url, true);
      xhr.send();
    }
  );
}

And here's my library function which makes that possible. Basically, it does two things (from privileged code):

  1. It installs a function into the unstrusted domWindow which providing the expected API to the end-user, which when called packages up the arguments (including a callback function) and raises a synthetic event. It organises this as a Method Factory so that the end-user can have whatever shape of API they desire (as long as it takes a callback!).

  2. It installs an event handler on the untrusted window. When the untrusted window dispatches its synthetic event, the event handler catches it, unpacks the arguments performs the provided privileged code. It then calls the untrusted callback function provided within the event detail.


const Cc = Components.classes;
const Ci = Components.interfaces;
let consoleService = Cc["@mozilla.org/consoleservice;1"]
                       .getService(Ci.nsIConsoleService);
function LOG(msg) { 
  consoleService.logStringMessage("EXTENSION: "+msg); 
}

// install a privileged method on the given (untrusted) 'target' window 
var install_privileged_method = (function(){
  var gensym = (function (){
    var __sym = 0;
    return function () { return '__sym_'+(__sym++); }
  })();

  return function (chromeWindow, target, slot, handler, methodFactory) {
    try {

      // initialise target window with ticket/callback-dict/namespace
      target.__pmcache__ = target.hasOwnProperty('__pmcache__') 
        ? target.__pmcache__ 
        : { ticket_no: 0, callbacks: {}, namespace: gensym() };

      // install the user factory-generated dispatcher method on 
      // the 'target' untrusted content window.
      target[slot] = methodFactory({ call: function(fargs, fcb) {
        try {
          var ticket_no = target.__pmcache__.ticket_no++;
          target.__pmcache__.callbacks[ticket_no] = fcb;
          var cevent = target.document.createEvent("CustomEvent");
          cevent.initCustomEvent(
            target.__pmcache__.namespace+'.'+slot, 
            true, true, { fargs: fargs, ticket_no: ticket_no }
          );
          target.dispatchEvent(cevent);
        } catch (ue) {
          fcb(0, null, 'untrusted dispatcher error: '+ue);
        }
      }});
      LOG("installed untrusted dispatcher for method '"+slot+"'.");

      // Add an event listener to (untrusted) target window which 
      // listens for custom event generated by above dispatcher method.
      target.addEventListener(
        target.__pmcache__.namespace+'.'+slot, 
        function(cevent){
          var ticket_no = cevent.detail.ticket_no;
          var fargs = cevent.detail.fargs;
          var fcb = target.__pmcache__.callbacks[ticket_no];
          try {
            handler(chromeWindow, fargs, fcb);
          } catch (pe) {
            fcb(0, null, 'privileged handler error: '+pe);
          }
        }, 
        false, 
        true
      );
      LOG("installed privileged handler for method '"+slot+"'.");

    } catch (ie) {
      LOG("ERROR installing handler/factory for privileged "+
          "method '"+slot+"': "+ie);
    }
  };
})();

Now that we've found a way to break out of the sandbox, we need to make sure that we only add that possibility to our 'trusted' web pages. (i.e: the URL where you've hosted your PHP).

I've been doing this in Firefox, so I'm dealing with one or more TabBrowser XUL elements in the user interface. As you're using XulRunner, that might not be the case.

In order to find our 'trusted' page, we need to look at all current (and future) chromeWindows and install a 'load' handler on them.

let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
           .getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
  let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
  WindowListener.setupBrowserUI(domWindow);
}
wm.addListener(WindowListener);

where WindowListener is defined as:

var WindowListener = {
  setupBrowserUI: function(window, xulWindow, othWindow) {
    window.gBrowser.addEventListener('load', my_load_handler, true); 
  },
  tearDownBrowserUI: function(window) { 
    window.gBrowser.removeEventListener('load', my_load_handler, true); 
  },
  onOpenWindow: function(xulWindow) {
    let domWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindow);
    domWindow.addEventListener("load", function listener() {
      domWindow.removeEventListener("load", listener, false); 
      var domDocument = domWindow.document.documentElement;
      var windowType = domDocument.getAttribute("windowtype");
      if (windowType == "navigator:browser")
        WindowListener.setupBrowserUI(domWindow);
    }, false);
  },

  onCloseWindow: function(xulWindow) { },
  onWindowTitleChange: function(xulWindow, newTitle) { }
};

where my_load_handler is defined as:

var my_load_handler = function (evt) {
  try {
    var browserEnumerator = wm.getEnumerator("navigator:browser");
    while (browserEnumerator.hasMoreElements()) {
      var browserWin = browserEnumerator.getNext();
      var tabbrowser = browserWin.gBrowser;
      var numTabs = tabbrowser.browsers.length;
      for (var index = 0; index < numTabs; index++) {
        var currentBrowser = tabbrowser.getBrowserAtIndex(index);
        var domWindow = currentBrowser.contentWindow.wrappedJSObject;

        // identify your target page...
        if (domWindow.location.href == 'http://yourserver/yourpage') {

          // install the privileged method (if it's not already there!)
          if (!domWindow.hasOwnProperty('xdomainGET') {
            install_xdomainGET_on_my_special_page(browserWin, domWindow);
          } 
        } 

      }
    }
  } catch (e) {
    LOG(e);
  }
}
David-SkyMesh
  • 5,041
  • 1
  • 31
  • 38