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 @@
notifyReady,
notifyDeclareMethod,
gadget_ready = false,
iframe_top_gadget,
last_acquisition_gadget;
// Create the gadget class for the current url
......@@ -962,25 +963,33 @@
throw new Error("bootstrap should not be called twice");
}
loading_klass_promise = new RSVP.Promise(function (resolve, reject) {
if (window.self === window.top) {
last_acquisition_gadget = new RenderJSGadget();
last_acquisition_gadget.__acquired_method_dict = {
getTopURL: function () {
return url;
},
reportServiceError: function (param_list) {
letsCrash(param_list[0]);
}
};
// Stop acquisition on the last acquisition gadget
// Do not put this on the klass, as their could be multiple instances
last_acquisition_gadget.__aq_parent = function (method_name) {
throw new renderJS.AcquisitionError(
"No gadget provides " + method_name
);
};
last_acquisition_gadget = new RenderJSGadget();
last_acquisition_gadget.__acquired_method_dict = {
getTopURL: function () {
return url;
},
reportServiceError: function (param_list) {
letsCrash(param_list[0]);
}
};
// Stop acquisition on the last acquisition gadget
// Do not put this on the klass, as their could be multiple instances
last_acquisition_gadget.__aq_parent = function (method_name) {
throw new renderJS.AcquisitionError(
"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
tmp_constructor = function () {
RenderJSGadget.call(this);
......@@ -1019,15 +1028,6 @@
tmp_constructor.prototype.__path = url;
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
notifyReady = function () {
......@@ -1075,9 +1075,9 @@
tmp_constructor.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,
argument_list) {
argument_list, time_out) {
return new RSVP.Promise(function (resolve, reject) {
embedded_channel.call({
method: "acquire",
......@@ -1090,7 +1090,8 @@
},
error: function (e) {
reject(e);
}
},
timeout: time_out
});
});
};
......@@ -1219,13 +1220,40 @@
}
if (window.top !== window.self) {
tmp_constructor.ready(function () {
var base = document.createElement('base');
return root_gadget.__aq_parent('getTopURL', [])
//checking channel should be done before sub gadget's declaration
//__ready_list:
//0: clearGadgetInternalParameters
//1: loadSubGadgetDOMDeclaration
//.....
tmp_constructor.__ready_list.splice(1, 0, function () {
return root_gadget.__aq_parent('getTopURL', [], 100)
.then(function (topURL) {
var base = document.createElement('base');
base.href = topURL;
base.target = "_top";
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 @@
notifyReady();
})
.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;
});
}
......
<!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 @@
var gadget = new RenderJSGadget(),
url = "./embedded_fail.html";
gadget.__acquired_method_dict = {
getTopURL: function () {
return "http://example.org/topGadget";
}
};
stop();
gadget.declareGadget(url, {
sandbox: 'iframe',
......@@ -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));
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