Each webAppOS app should include the webappos.js script, which ensures the existence of the webappos JavaScript object for calling webAppOS-related functions and services.
Browser-side code of webAppOS apps can run in their own browser windows (tabs) or be embedded in iframes. In particular, these iframes can be embedded into the default webAppOS Desktop app. Alternative desktop implementations (embedding other apps in iframes) can be created as well. Each desktop application must implement Desktop API in JavaScript webappos.desktop object. In addition, a desktop app must set the value:
webappos.in_desktop = true;
Since the default Desktop app usually runs on its own subdomain, browsers won't allow child apps to access Desktop API implemented in window.parent.webappos.desktop directly.
To communicate with the parent desktop, we use the HTML5 messaging mechanism.
The webappos.js script tries to get the parent webappos object first. If the parent object exists, but is not accessible, the child app send the following message to it:
window.parent.postMessage({ protocol: "webappos_desktop", method: "are_you_desktop?", caller_id:webappos.caller_id }, "*");
If the parent or some of its ascendants implements Desktop API, the parent will reply to the child with the message
event.source.postMessage({ protocol: "webappos_desktop", method: "i_am_desktop!", caller_id:webappos.caller_id }, event.origin);
Then the child implements other webAppOS functions by its own, but forwards desktop-related functions to the parent using the messages explained below. In addition, webappos.js sets the following value:
webappos.parent_desktop = true;
If the parent does not provide desktop functionality, the child implements Desktop API by its own. The default implementation is tailored for single-page webAppOS applications that do not use the desktop. However, the shipped Desktop app redefines that API.
In case the desktop and the child iframe are on the same domain, messages are not used. In this case, webappos.in_desktop will be true for both the desktop window and the child iframe.
The following messages correspond to functions from Desktop API.
window.parent.postMessage({ protocol: "webappos_desktop", method: "launch_in_desktop", url: url, app_url_name: app_url_name, caller_id:webappos.caller_id }, "*");
window.parent.postMessage({ protocol: "webappos_desktop", method: "set_shared_value", key: key, value: value, caller_id:webappos.caller_id }, "*");
window.parent.postMessage({ protocol: "webappos_desktop", method: "get_shared_value", key: key, callback_id: callback_id_counter, caller_id:webappos.caller_id }, "*");
// since the child frame needs to process the shared value to be received from the parent,
// it has to identify the call; callback_id is a number used to specify the child-side function
// that has to be called when the value will be received from the parent
window.parent.postMessage({ protocol: "webappos_desktop", method: "show_dialog", title: title, content: content, width:w, height:h, handle:dialog_counter, callback_id: null|callback_id_counter, caller_id:webappos.caller_id }, "*");
// handle is a number used to identify the same dialog when calling close_dialog;
// callback_id is a number used to specify the child-side function
// that has to be called when the dialog window is being closed via the "x" button
window.parent.postMessage({ protocol: "webappos_desktop", method: "close_dialog", handle: handle, caller_id:webappos.caller_id }, "*");
The caller_id is a number that can be used by the desktop to uniquely identify the child. The child sends caller_id in each message.
Callbacks (for get_shared_value and show_dialog) are called via the following message:
event.source.postMessage({protocol:"webappos_desktop", method:"callback", callback_id:callback_id, value:null|value}, event.origin);
In webappos.js, there is the callbacks map (webappos.desktop.callback_map), which associates callback_id-s with JS functions. The map within the child frame contains the functions defined at the child iframe. The map in the desktop window contains stubs that forward callbacks to the child via postMessage.
Functions from webappos.desktop use the callback_id counter webappos.desktop.callback_id_counter, which is incremented for each new call with a callback.
For set_shared_value/get_shared_value, webappos.js maintains the values map webappos.desktop.shared_values.
The implementation of webappos.desktop.browse is based on the shipped (hidden) FileDialog app, which provides FileDialog App API.
The webappos.desktop.browse_for_file function puts FileDialog.html inside an iframe and passes 3 arguments: type, filter, and browse_id (in accordance with FileDialog App API).
In webappos.js, each driver is put into the drivers set webappos.drivers_set as well as pushed into the drivers stack webappos.drivers_stack (if the driver is called multiple times, it is pushed to the stack only for the first time). The stack is needed to revoke access of all requested scopes in the reverse order.