Commit a289140e authored by Xiaowu Zhang's avatar Xiaowu Zhang

Make renderjs works in iframe

When a gadget is in iframe, it checks its communication's channel by using timeout 100ms (the only way...).
If a timeout occurs, renderjs considers this gadget has no parent gadget and so, consider it as parent gadget by stopping acquisition.
parent 90e6dfc0
...@@ -955,6 +955,7 @@ ...@@ -955,6 +955,7 @@
notifyReady, notifyReady,
notifyDeclareMethod, notifyDeclareMethod,
gadget_ready = false, gadget_ready = false,
iframe_top_gadget,
last_acquisition_gadget; last_acquisition_gadget;
// Create the gadget class for the current url // Create the gadget class for the current url
...@@ -962,25 +963,33 @@ ...@@ -962,25 +963,33 @@
throw new Error("bootstrap should not be called twice"); throw new Error("bootstrap should not be called twice");
} }
loading_klass_promise = new RSVP.Promise(function (resolve, reject) { loading_klass_promise = new RSVP.Promise(function (resolve, reject) {
if (window.self === window.top) {
last_acquisition_gadget = new RenderJSGadget(); last_acquisition_gadget = new RenderJSGadget();
last_acquisition_gadget.__acquired_method_dict = { last_acquisition_gadget.__acquired_method_dict = {
getTopURL: function () { getTopURL: function () {
return url; return url;
}, },
reportServiceError: function (param_list) { reportServiceError: function (param_list) {
letsCrash(param_list[0]); letsCrash(param_list[0]);
} }
}; };
// Stop acquisition on the last acquisition gadget // Stop acquisition on the last acquisition gadget
// Do not put this on the klass, as their could be multiple instances // Do not put this on the klass, as their could be multiple instances
last_acquisition_gadget.__aq_parent = function (method_name) { last_acquisition_gadget.__aq_parent = function (method_name) {
throw new renderJS.AcquisitionError( throw new renderJS.AcquisitionError(
"No gadget provides " + method_name "No gadget provides " + method_name
); );
}; };
//we need to determine tmp_constructor's value before exit bootstrap
//because of function : renderJS
//but since the channel checking is async,
//we can't use code structure like:
// if channel communication is ok
// tmp_constructor = RenderJSGadget
// else
// tmp_constructor = RenderJSEmbeddedGadget
if (window.self === window.top) {
// XXX Copy/Paste from declareGadgetKlass // XXX Copy/Paste from declareGadgetKlass
tmp_constructor = function () { tmp_constructor = function () {
RenderJSGadget.call(this); RenderJSGadget.call(this);
...@@ -1019,15 +1028,6 @@ ...@@ -1019,15 +1028,6 @@
tmp_constructor.prototype.__path = url; tmp_constructor.prototype.__path = url;
root_gadget = new RenderJSEmbeddedGadget(); root_gadget = new RenderJSEmbeddedGadget();
// Bind calls to renderJS method on the instance
embedded_channel.bind("methodCall", function (trans, v) {
root_gadget[v[0]].apply(root_gadget, v[1]).then(function (g) {
trans.complete(g);
}).fail(function (e) {
trans.error(e.toString());
});
trans.delayReturn(true);
});
// Notify parent about gadget instanciation // Notify parent about gadget instanciation
notifyReady = function () { notifyReady = function () {
...@@ -1075,9 +1075,9 @@ ...@@ -1075,9 +1075,9 @@
tmp_constructor.allowPublicAcquisition = tmp_constructor.allowPublicAcquisition =
RenderJSGadget.allowPublicAcquisition; RenderJSGadget.allowPublicAcquisition;
// Define __aq_parent to inform parent window //Default: Define __aq_parent to inform parent window
tmp_constructor.prototype.__aq_parent = function (method_name, tmp_constructor.prototype.__aq_parent = function (method_name,
argument_list) { argument_list, time_out) {
return new RSVP.Promise(function (resolve, reject) { return new RSVP.Promise(function (resolve, reject) {
embedded_channel.call({ embedded_channel.call({
method: "acquire", method: "acquire",
...@@ -1090,7 +1090,8 @@ ...@@ -1090,7 +1090,8 @@
}, },
error: function (e) { error: function (e) {
reject(e); reject(e);
} },
timeout: time_out
}); });
}); });
}; };
...@@ -1219,13 +1220,40 @@ ...@@ -1219,13 +1220,40 @@
} }
if (window.top !== window.self) { if (window.top !== window.self) {
tmp_constructor.ready(function () { //checking channel should be done before sub gadget's declaration
var base = document.createElement('base'); //__ready_list:
return root_gadget.__aq_parent('getTopURL', []) //0: clearGadgetInternalParameters
//1: loadSubGadgetDOMDeclaration
//.....
tmp_constructor.__ready_list.splice(1, 0, function () {
return root_gadget.__aq_parent('getTopURL', [], 100)
.then(function (topURL) { .then(function (topURL) {
var base = document.createElement('base');
base.href = topURL; base.href = topURL;
base.target = "_top"; base.target = "_top";
document.head.appendChild(base); document.head.appendChild(base);
//the channel is ok
//so bind calls to renderJS method on the instance
embedded_channel.bind("methodCall", function (trans, v) {
root_gadget[v[0]].apply(root_gadget, v[1])
.then(function (g) {
trans.complete(g);
}).fail(function (e) {
trans.error(e.toString());
});
trans.delayReturn(true);
});
})
.fail(function (error) {
if (error === "timeout_error") {
//the channel fail
//we consider current gadget is parent gadget
//redifine last acquisition gadget
iframe_top_gadget = true;
setAqParent(root_gadget, last_acquisition_gadget);
} else {
throw error;
}
}); });
}); });
} }
...@@ -1257,7 +1285,12 @@ ...@@ -1257,7 +1285,12 @@
notifyReady(); notifyReady();
}) })
.fail(function (e) { .fail(function (e) {
embedded_channel.notify({method: "failed", params: e.toString()}); //top gadget in iframe
if (iframe_top_gadget) {
letsCrash(e);
} else {
embedded_channel.notify({method: "failed", params: e.toString()});
}
throw e; throw e;
}); });
} }
......
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Not declared page for renderJS test</title>
<meta name="viewport" content="width=device-width, height=device-height"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="../node_modules/rsvp/dist/rsvp-2.0.4.js" type="text/javascript"></script>
<script src="../dist/renderjs-latest.js" type="text/javascript"></script>
<script src="./not_declared_gadget.js" type="text/javascript"></script>
</head>
<body>
<div class="getTopUrl"></div>
<div class="acquisitionError"></div>
<div class="klass"></div>
</body>
</html>
/*jslint nomen: true*/
(function (window, rJS) {
"use strict";
var gk = rJS(window);
gk.ready(function (g) {
g.props = {};
return g.getElement()
.push(function (element) {
g.props.element = element;
});
})
.declareAcquiredMethod('getTopURL', 'getTopURL')
.declareAcquiredMethod('willFail', 'willFail')
.declareService(function () {
var context = this;
return RSVP.all([
context.checkTopUrl(),
context.checkAcquisitionError(),
context.checkKlass()
]);
})
.declareMethod('checkTopUrl', function () {
var g = this;
return g.getTopURL()
.push(function (top_url) {
g.props.element.querySelector('.getTopUrl')
.innerHTML = top_url;
});
})
.declareMethod('checkAcquisitionError', function () {
var g = this;
return g.willFail()
.push(undefined , function (e) {
g.props.element.querySelector('.acquisitionError')
.innerHTML = e;
});
})
.declareMethod('checkKlass', function () {
var g = this;
g.props.element.querySelector('.klass')
.innerHTML = "klass" +
((g instanceof window.__RenderJSEmbeddedGadget)? " = embedded" : " != embedded");
});
}(window, rJS));
...@@ -3369,6 +3369,11 @@ ...@@ -3369,6 +3369,11 @@
var gadget = new RenderJSGadget(), var gadget = new RenderJSGadget(),
url = "./embedded_fail.html"; url = "./embedded_fail.html";
gadget.__acquired_method_dict = {
getTopURL: function () {
return "http://example.org/topGadget";
}
};
stop(); stop();
gadget.declareGadget(url, { gadget.declareGadget(url, {
sandbox: 'iframe', sandbox: 'iframe',
...@@ -3612,5 +3617,32 @@ ...@@ -3612,5 +3617,32 @@
}); });
}); });
test('check working of parent gadget in iframe', function () {
var fixture = document.getElementById("qunit-fixture");
fixture.innerHTML =
"<iframe id=renderjsIframe src='./not_declared_gadget.html'></iframe>";
stop();
return RSVP.delay(900)
.then(function () {
var iframe = document.getElementById('renderjsIframe'),
url_div = iframe.contentWindow.document.querySelector('.getTopUrl'),
acquisition_div = iframe.contentWindow.
document.querySelector('.acquisitionError'),
klass_div = iframe.contentWindow.document.querySelector('.klass');
equal(url_div.innerHTML,
"http://localhost:9000/test/not_declared_gadget.html");
equal(acquisition_div.innerHTML,
"AcquisitionError: No gadget provides willFail");
equal(klass_div.innerHTML,
"klass = embedded");
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}(document, renderJS, QUnit, sinon, URI)); }(document, renderJS, QUnit, sinon, URI));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment