Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e13118c
Showing
12 changed files
with
1,968 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
Introduction | ||
------------ | ||
|
||
This is an implementation of a WebIntents-like API, as sketched out by Ian Hickson in http://lists.w3.org/Archives/Public/public-webapps/2011JulSep/1509.html | ||
|
||
This is implemented as a pure-JS shim - pages need only load `intents.js` and they can function as an invoker of intent or as a handler of intents. A rough outline of how it hangs together is: | ||
|
||
* intents.js arranges to create a hidden iframe back to localhost:8888 and uses a jschannel to communicate for registration and unregistration. localStorage on this host is the "repository". | ||
* When client code calls `invokeIntent()`, the picker is displayed an a jschannel created between the client code and the picker. | ||
* The picker loads the intent handlers in an iframe and yet another jschannel exists between the picker and the handler. | ||
Thus, the picker acts as a kind of relay - when the client communicates with the handler it goes over its jschannel to the picker, which then relays it over the jschannel to the handler. | ||
|
||
As Firefox doesn't implement MessagePorts yet, a simple port-like javascript object is used. | ||
|
||
Running the demo | ||
---------------- | ||
|
||
* Start `server.py` in the root of this tree. This will start a http server on port 8888. | ||
* Open http://localhost:8888/handlers/sample_handler.html in your browser. Not much will happen but it should call registerIntentHandler to register that page as a handler for a 'share' intent. Close the page. | ||
|
||
We also use the Firefox Share addon as an OAuth helper for the google etc plugins. This takes a few steps. | ||
|
||
* Grab the `experiment/hixtents` branch from the git repo at https://github.com/mhammond/fx-share-addon | ||
* Grab the `develop` branches from the repos at https://github.com/mozilla/oauthorizer/ and https://github.com/mozilla/activities | ||
* After activating the addon-sdk, run the command: | ||
`cfx run --pkgdir=path-to-fx-share-addon --package-path=path-to-oauthorizer package-path=path-to-activities` | ||
* Start `server.py' in the root of the fx-share-addon tree. This will start a http server on port 8889. | ||
* Open http://localhost:8889/data/apps/google/google.html - this will register that page as a share handler. Close the page. | ||
* Open clients/task.html in the browser - you can open this either as a file:/// URL or via the server (ie, http://localhost:8888/clients/simple.html. | ||
* Click on the link to invoke a share intent. | ||
|
||
You should now see the picker with the 2 handlers displayed in tabs. The google service should be fully functional - ie, you can login and send an email. | ||
|
||
Notable differences from Hixie's sketch | ||
--------------------------------------- | ||
|
||
Instead of: | ||
|
||
var port = navigator.handleIntent(intent, filter); | ||
port.postMessage(data); | ||
port.onmessage = function (event) { handle(event.data) }; | ||
|
||
we use a callback approach: | ||
|
||
navigator.handleIntent(intent, filter, function(port) { | ||
port.postMessage(data); | ||
port.onmessage = function (event) { handle(event.data) }; | ||
}); | ||
|
||
This is done as it make take some time for the UI to select an intent handler and we want to avoid blocking the main thread. | ||
|
||
Similarly, `registerIntentHandler`, `isIntentHandlerRegistered` and `unregisterIntentHandler` take a callback, although this was done for pragmatic reasons - the shim uses `postMessage` to a server for the respository implementation. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<html> | ||
<head> | ||
<script src="http://localhost:8888/intents.js"></script> | ||
<script> | ||
|
||
/*** | ||
var worker = new Worker('my_task.js'); | ||
worker.onmessage = function(event) { | ||
console.log("Called back by the worker!\n"); | ||
console.log("Navigator is " + navigator); | ||
console.log(event.data); | ||
console.log(event.target.port); | ||
for (var x in event) { | ||
try { | ||
console.log(x + ": " + event[x]); | ||
} catch (ex) { | ||
console.log(x + " failed: " + ex); | ||
} | ||
} | ||
}; | ||
worker.postMessage("hello"); | ||
console.log("created task") | ||
****/ | ||
function doit() { | ||
navigator.handleIntent("test", null, function(port) { | ||
console.log("handleIntent callback called!"); | ||
port.onmessage = function(response) { | ||
console.log("handleIntent onmessage called", response); | ||
} | ||
port.postMessage("here is the test data"); | ||
}); | ||
}; | ||
function shareit() { | ||
navigator.handleIntent("share", null, function(port) { | ||
console.log("share handleIntent callback called!"); | ||
port.onmessage = function(response) { | ||
console.log("share handleIntent onmessage called", response); | ||
} | ||
port.postMessage({url: window.location.href, subject: "a demo page"}); | ||
}); | ||
}; | ||
</script> | ||
</head> | ||
<body> | ||
<a href="#" onclick="testit();">Click to invoke 'test' intent...</a> | ||
<a href="#" onclick="shareit();">Click to invoke 'share' intent...</a> | ||
|
||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
This directory contains some demo "intent handlers". While they can be served by the server as a convenience, they don't rely on being on the same origin as the server. One way to test this out is to load them as file:/// URLs - but Browsers take a dim view of file:/// content hosted in iframes. To work around this, you can modify your profile's user.js and add the following lines: | ||
|
||
``` | ||
user_pref("capability.policy.policynames", "localfilelinks"); | ||
user_pref("capability.policy.localfilelinks.sites", "http://localhost:8888"); | ||
user_pref("capability.policy.localfilelinks.checkloaduri.enabled", "allAccess"); | ||
``` | ||
|
||
You will then be able to load them from a file:/// URL (to re-register it) and the picker will display them. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<html> | ||
<head> | ||
<title>A test handler</title> | ||
<link rel="icon" href="sample_handler.png"/> | ||
|
||
<script src="http://localhost:8888/intents.js"></script> | ||
<script> | ||
dump("handler loaded\n"); | ||
window.addEventListener("load", function() { | ||
console.log("doc loaded - adding intent handler"); | ||
navigator.registerIntentHandler('share', '*/*', location.href, 'inline'); | ||
}, false); | ||
|
||
window.onintent = function(evt) { | ||
console.log("handler onintent invoked");; | ||
document.getElementById("status").textContent = "I got the intent data: " + JSON.stringify(evt.data); | ||
evt.ports[0].postMessage({result: "it's all good!"}); | ||
} | ||
</script> | ||
</head> | ||
<body> | ||
Hey - I'm an intent handler and | ||
<span id="status">I'm waiting for intent data</span> | ||
</body> | ||
</html> |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<script src="jschannel.js"></script> | ||
<script src="intents-repo.js"></script> | ||
</head> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
|
||
(function() { | ||
|
||
function _getIntentRegistrations() { | ||
if (localStorage.intentHandlers) { | ||
return JSON.parse(localStorage.intentHandlers); | ||
} | ||
return {}; | ||
} | ||
|
||
function _setIntentRegistrations(ob) { | ||
localStorage.intentHandlers = JSON.stringify(ob); | ||
} | ||
|
||
var chan = Channel.build({window: window.parent, origin: "*", scope: "intents_channel"}); | ||
chan.bind("registerIntentHandler", function(trans, data) { | ||
var intentsOb = _getIntentRegistrations(); | ||
if (!intentsOb[data.intent]) { | ||
intentsOb[data.intent] = {}; | ||
} | ||
if (!intentsOb[data.intent][data.filter]) { | ||
intentsOb[data.intent][data.filter] = {}; | ||
} | ||
intentsOb[data.intent][data.filter][data.url] = data; | ||
_setIntentRegistrations(intentsOb); | ||
}); | ||
|
||
chan.bind("isIntentHandlerRegistered", function(trans, data) { | ||
var intentsOb = _getIntentRegistrations(); | ||
try { | ||
return !!intentsOb[data.intent][data.filter][data.url]; | ||
} catch(ex) { | ||
return false; | ||
} | ||
}); | ||
|
||
chan.bind("unregisterIntentandler", function(trans, data) { | ||
var intentsOb = _getIntentRegistrations(); | ||
try { | ||
delete localStorage.intentHandlers[data.intent][data.filter][data.url]; | ||
} catch (ex) { | ||
return; // nothing to do. | ||
} | ||
_setIntentRegistrations(intentsOb); | ||
}); | ||
|
||
})(); |
Oops, something went wrong.