Access Firefox extension XPCOM object from Javascript inside an HTML page - javascript

I'm trying to get the most basic XPCOM javascript object to be accessible to the javascript I load into my webpage. I'm using the example code from this tutorial:
https://developer.mozilla.org/en-US/docs/How_to_Build_an_XPCOM_Component_in_Javascript
Here is my set up:
install.rdf:
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>helloworld#thellamatesting.com</em:id>
<em:name>Hello World</em:name>
<em:version>1.0</em:version>
<em:type>2</em:type>
<em:creator>The Llama</em:creator>
<em:description>Testing</em:description>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>2.0</em:minVersion>
<em:maxVersion>20.0</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>
chrome.manifest
content helloworld chrome/content/
content helloworld chrome/content/ contentaccessible=yes
overlay chrome://browser/content/browser.xul chrome://helloworld/content/browser.xul
component {4762b5c0-5b32-11e2-bcfd-0800200c9a66} components/HelloWorld.js
contract #thellamatesting.com/helloworld;1 {4762b5c0-5b32-11e2-bcfd-0800200c9a66}
locale helloworld en-US locale/en-US/
components/HelloWorld.js
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function HelloWorld() {
// If you only need to access your component from Javascript, uncomment the following line:
this.wrappedJSObject = this;
}
HelloWorld.prototype = {
classDescription: "My Hello World Javascript XPCOM Component",
classID: Components.ID("{4762b5c0-5b32-11e2-bcfd-0800200c9a66}"),
//Also tried
//classID: Components.ID("4762b5c0-5b32-11e2-bcfd-0800200c9a66"),
contractID: "#thellamatesting.com/helloworld;1",
QueryInterface: XPCOMUtils.generateQI(),
// Also tried
//QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIHelloWorld]),
hello: function() {
return "Hello World!";
}
};
var components = [HelloWorld];
if ("generateNSGetFactory" in XPCOMUtils)
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components); // Firefox 4.0 and higher
else
var NSGetModule = XPCOMUtils.generateNSGetModule(components); // Firefox 3.x
Testing HTML:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="application/javascript">
function go() {
try {
var coms = Components;
alert(Components.classes);
var myComponent = Components.classes['#thellamatesting.com/helloworld;1'].getService().wrappedJSObject;
alert(myComponent.hello());
} catch (anError) {
dump("ERROR: " + anError);
}
};
</script>
</head>
<body>
<button onclick="javascript:go()">Click to go</button>
</body>
</html>
After all this, I end up with "Components.classes is undefined". Does anyone know what I'm doing wrong here?
Thanks so much!

In order to gain access to the Components object from a javascript context, you need to have extended capabilities, that is, run from a chrome:// URL. There used to be a way for a regular web page (served from http://) to request extended capabilities (called UniversalXPConnect) but it has been removed out of security concerns.
I think you should tell us a little more about what it is you're trying to achieve. If you're trying to export data from your addon into a webpage, the AddonSDK (see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/) has a very good protocol for doing that called page-mod; it allows you to inject data into web pages.

Thanks to Jonathan's advice I was able to come up with a great solution to this problem. Here is the code I'm using:
main.js:
var data = require("self").data;
var pageMod = require("page-mod");
const {Cc,Ci} = require("chrome");
pageMod.PageMod({
include: "*",
contentScriptFile: data.url("copy-helper.js"),
onAttach: function(worker) {
worker.port.on("handleCopy", function(copyInfo) {
var gClipboardHelper = Cc["#mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
gClipboardHelper.copyString(copyInfo.dataToCopy);
});
}
});
copy-helper.js:
window.addEventListener("copyEvent", function (event) {
self.port.emit('handleCopy', event.detail.copyInfo);
}, false);
in my apps javascript
var event = new CustomEvent("copyEvent", {
detail:{
copyInfo: {dataToCopy:"my string"}
}
});
window.dispatchEvent(event);
Hope this helps anyone else who's ran into this issue!

Related

Haskell Webviewhs getElementById from a webpage

Still most potential for a GUI to Haskell for me, but missing some essential info in the examples, being a noob Haskeller. Assuming one of the examples:
{-
webviewhs
(C) 2018 David Lettier
lettier.com
-}
{-# LANGUAGE
OverloadedStrings
#-}
import qualified Graphics.UI.Webviewhs as WHS
main :: IO ()
main =
WHS.createWindowAndBlock
WHS.WindowParams
{ WHS.windowParamsTitle = "webviewhs - How do I create a window and have it run itself?"
-- This could be a localhost URL to your single-page application (SPA).
, WHS.windowParamsUri = "https://lettier.github.com"
, WHS.windowParamsWidth = 800
, WHS.windowParamsHeight = 600
, WHS.windowParamsResizable = True
, WHS.windowParamsDebuggable = True
}
This creates a window in which I can load a custom webpage. Assuming this webpage has a <input type="text" id="mytext"> and there's a button next to it. Not that it matters but <button type="submit" id="sendtohaskell">. How would I go about getting the info in the textfield to Haskell by pressing the button? There's no example like that in the tutorial. For me it is the missing link to get info from a webapp, processing it in Haskell and returning it to eg. SQLite.
As the github page shows, you can receive data from JS with a callback, and execute arbitrary JS in the window from Haskell. This is enough to do any sort of communication you might want, here's an example that that executes some Haskell on a button press and then shows the result in the webpage:
{-# LANGUAGE OverloadedStrings, QuasiQuotes #-}
module Main where
import System.Directory(getCurrentDirectory)
import Text.Printf
import Control.Monad(void)
import Language.Javascript.JMacro
import qualified Graphics.UI.Webviewhs as WHS
import qualified Data.Text as T
windowCallback window = do
return True
handleJSRequest window request = void . WHS.runJavaScript window $ [jmacro|
show_response `(printf "'Got response: %s'" request :: String)`
|]
main :: IO ()
main = void $ do
dir <- getCurrentDirectory
WHS.withWindowLoop
WHS.WindowParams
{ WHS.windowParamsTitle = "Test"
, WHS.windowParamsUri = T.pack $ printf "file://%s/example.html" dir
, WHS.windowParamsWidth = 800
, WHS.windowParamsHeight = 600
, WHS.windowParamsResizable = True
, WHS.windowParamsDebuggable = True
}
handleJSRequest
windowCallback
<html>
<head>
<title>Example</title>
<meta charset="utf-8">
</head>
<body>
<script type="text/javascript">
function show_response(response) {
document.getElementById('response').innerHTML = response;
}
function submit() {
var value = document.getElementById('textbox').value;
window.external.invoke(value)
}
</script>
<input type="text" id="textbox"/>
<input value="say hello" type="button" onclick="submit()"/>
<p id="response"></p>
</body>
</html>
You should note though that the haskell webview library has only 2 commits, the last of which was more than 7 months ago, so it's not exactly being actively developed at the moment.

Is that possible to put Template7 code in a separate file rather than in html

I am using a framework called Framework7.
In my index.html, I have some Template7 code, like this format
<script type="text/template7" id="commentsTemplate">
{{#each this}}
<div> test this template 7 code </div>
</script>
However, I want to have this part of code into an another separated file (Just like I can have many other *.js files in, say, a static folder and refer to the file by "static/*.js).
I have tried to use a typical way to import js
<script type="text/template7" id="storiesTemplate" src="js/template.js"></script>
But it doesn't work, there is also no demo/sample code in the documentation.
Any help is appreciated!
You can do it. The idea behind is to include a HTML file in a HTML file. I can tell at least 3 ways that this can happen, but personally I fully validated only the third.
First there is a jQuery next sample is taken from this thread
a.html:
<html>
<head>
<script src="jquery.js"></script>
<script>
$(function(){
$("#includedContent").load("b.html");
});
</script>
</head>
<body>
<div id="includedContent"></div>
</body>
</html>
b.html:
<p> This is my include file </p>
Another solution, I found here and doesn't require jQuery but still it's not tested: there is a small function
My solution is a pure HTML5 and is probably not supported in the old browsers, but I don't care for them.
Add in the head of your html, link to your html with template
<link rel="import" href="html/templates/Hello.html">
Add your template code in Hello.html. Than use this utility function:
loadTemplate: function(templateName)
{
var link = document.querySelector('link[rel="import"][href="html/templates/' + templateName + '.html"]');
var content = link.import;
var script = content.querySelector('script').innerHTML || content.querySelector('script').innerText;
return script;
}
Finally, call the function where you need it:
var tpl = mobileUtils.loadTemplate('hello');
this.templates.compiledTpl = Template7.compile(tpl);
Now you have compiled template ready to be used.
=======UPDATE
After building my project for ios I found out that link import is not supported from all browsers yet and I failed to make it work on iphone. So I tried method number 2. It works but as you might see it makes get requests, which I didn't like. jquery load seems to have the same deficiency.
So I came out with method number 4.
<iframe id="iFrameId" src="html/templates/template1.html" style="display:none"></iframe>
and now my loadTemplate function is
loadTemplate: function(iframeId, id)
{
var iFrame = document.getElementById(iframeId);
if ( !iFrame || !iFrame.contentDocument ) {
console.log('missing iframe or iframe can not be retrieved ' + iframeId);
return "";
}
var el = iFrame.contentDocument.getElementById(id);
if ( !el ) {
console.log('iframe element can not be located ' + id );
return "";
}
return el.innerText || el.innerHTML;
}
How about lazy loading and inserting through the prescriptions?
(function (Template7) {
"use strict";
window.templater = new function(){
var cache = {};
var self = this;
this.load = function(url)
{
return new Promise(function(resolve,reject)
{
if(cache[url]){
resolve(cache[url]);
return true;
}
if(url in Template7.templates){
resolve(Template7.templates[url]);
return true;
}
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
if(this.status == 200 && this.response.search('<!DOCTYPE html>') == -1){
cache[url] = Template7.compile(this.response);
resolve(cache[url]);
}else{
reject(`Template ${url} not found`);
}
};
xhr.send();
})
}
this.render = function(url, data)
{
return self.load(url)
.then(function(tpl){
return tpl(data) ;
});
}
this.getCache = function()
{
return cache;
}
}
})(Template7);
Using :
templater.render('tpl.html').then((res)=>{ //res string })
Or :
templater.load('tpl.html').then( tpl => { Dom7('.selector').html( tpl(data) ) } )
It is possible to define your templates in .js-files. The template just needs to be a string.
Refer to this [JSFiddle] (https://jsfiddle.net/timverwaal/hxetm9rc/) and note the difference between 'template1' and 'template2'
var template1 = $$('#template').html();
var template2 = '<p>Hello, my name is still {{firstName}} {{lastName}}</p>'
template1 just extracts the content of the <script> and puts it in a string.
template2 directly defines the string

Backbone: getting the html code of a view?

In order to write the HTML code of social icons (Twitter, Linkedin, etc) to a textarea so that the user can use that code elsewhere, I would like to get the HTML code of the view element, but I'm having some issues. To help illustrate this better, here is the code that creates the view:
define(function(require, exports, module) {
var _ = require('underscore');
var GridControlView = require('pb/views/grid-control');
var SocialiconsControlDialog = require('pb/views/socialicons-control-dialog');
var template = require('text!pb/templates/socialicons-grid-control.html');
var SocialiconsGridControlView = GridControlView.extend({
template: _.template(template)
,templateVars: {
partials: {
facebook: require('text!pb/templates/socialicons-grid-control-facebook.html')
,twitter: require('text!pb/templates/socialicons-grid-control-twitter.html')
,googleplus: require('text!pb/templates/socialicons-grid-control-googleplus.html')
,pinterest: require('text!pb/templates/socialicons-grid-control-pinterest.html')
,linkedin: require('text!pb/templates/socialicons-grid-control-linkedin.html')
}
}
,control_dialog: SocialiconsControlDialog
});
return SocialiconsGridControlView;
});
And, for example, the Linkedin template looks like this:
<script src="//platform.linkedin.com/in.js?<%- t.cache_buster %>" type="text/javascript">lang: en_US</script>
<script type="IN/Share" data-counter="<%- t.linkedin_option_countmode %>"></script>
What I would like to retrieve, is the parsed template code as text, something such as:
<script type="text/javascript" src="//platform.linkedin.com/in.js?0.4670609195438331">
<script data-counter="top" type="IN/Share+init">
But using something such as:
control_view.render().$el.innerHTML;, control_view.render().$el.html().text() or control_view.render().$el.html().replace(/<\/?[a-z][a-z0-9]*[^<>]*>/ig, ""); doesn't return text; it returns the full HTML, and produces a Linkedin icon (when I just want the text to be written to a textarea).
Any thoughts?
Update **
I noticed that the code control_view.render().$el is working correctly on other places of the application, and returning HTML code, but for some reason in this view where I'm trying it doesn't. The code seems to break at:
$control = control_view.render().el;
and in the console I get an error which is:
TypeError: t is undefined - underscore-min.js (line 3)
Use the .outerHTML property of the $el.
var html = $('<script type="text/javascript" src="//platform.linkedin.com/in.js?0.4670609195438331">' +
'<script data-counter="top" type="IN/Share+init">');
var text = html[0].outerHTML;
$('textarea').val(text);
jsFiddle

JS works in IE, Safari, Chrome but not in Firefox?

Most my work is done running on Webkit stuff and using Chrome's dev tools. But this code doesn't run in Firefox; it doesn't even throw a console error so I have no idea what's wrong. Any ideas. I'm sure its terribly easy. My code is designed to look throw a div for a specific syntax "t_" "r_" which are references to an external document; I'm trying to find all said references and replace them with hyperlink. I"m a novice JS coder so sorry for the naiveness you'll undoubtedly see in here.
(I'm using FF 22.0)
<!DOCTYPE html>
<html>
<head>
<script>
function pre_forwarder(){ //First Step
patt_1=/\[r_/g;
patt_2=/\[t_/g;
patt_S_and_R_1 = "[r_";
patt_S_and_R_2 = "[t_";
forwarder(patt_1,patt_S_and_R_1);
forwarder(patt_2,patt_S_and_R_2);
}
function forwarder(Pattern,Pre_SearchReplace){
rule_string = document.getElementById("divid");
rule_string = rule_string.innerText;
var patt1 = Pattern;
while (patt1.test(rule_string)==true)
{
begin_string = patt1.lastIndex;
fullpar_string = patt1.lastIndex + 5;
partial_string = rule_string.slice(begin_string,fullpar_string); //5 characters
//alert("partial_string-"+partial_string);
refined_string_end = partial_string.indexOf("]");
refined_str = partial_string.slice(0,refined_string_end); //raw number
full_str = Pre_SearchReplace+refined_str+"]"; //restructured
SectionNum = refined_str;
SearchReplace = full_str; //Variable to pass to parse()
//alert("S&R="+SearchReplace+" >> full_string-"+full_str);
//alert("S&R"+SearchReplace+">>SecNum-"+SectionNum+">>Pre-Search-"+Pre_SearchReplace);
Parse(SearchReplace,SectionNum,Pre_SearchReplace);
}
//var patt_end=/\[t_/g;
}
function Parse(SearchReplace,SectionNum,Pre_SearchReplace){
if (Pre_SearchReplace == patt_S_and_R_1){
ref_URL = "UEB_Ref.html#";
}else if (Pre_SearchReplace == patt_S_and_R_2){
ref_URL = "UEB_Tech.html#";
}
linkCreate = 'Link to ch-'+SectionNum+' ';
document.getElementById("divid").innerHTML = document.getElementById("divid").innerHTML.replace(SearchReplace, linkCreate);
}
function Dude(SectionNum){
alert(SectionNum);
}
</script>
</head>
<body onload="pre_forwarder();">
<button onclick="pre_forwarder();">
New Tool</button>
<div id="divid">
hello [dude] ok the end [r_12.1][r_7] [r_22]
<br>bro try this [t_15.3][t_6] [t_5.5]
</div>
<div id="hyperlink">
</div>
</body>
</html>
You've rule_string = rule_string.innerText; at line 15. FF doesn't know innerText, you need to use textContent, or a fix like:
rule_string = rule_string.innerText || rule_string.textContent;
A live demo at jsFiddle.
As a side note: Looks like your app is based on global variables. Please don't use this kind of programming technique. It'll do the job, as long as there's not much of code, but when your apps are getting larger, you'll lose control over a ton of global variables.
For starters, you can read JavaScript Guide at MDN, especially chapters 4, 8 and 9.

Using Fzip lib under Adobe Air App

I'm currently working on a project for Adobe Air (1.5.3) and I need to unzip a file, copy some of its contents to another file.
Then I saw people talking about the Fzip (http://codeazur.com.br/lab/fzip) lib. The problem is that I don't know how to "import" or use this library with Javascript and Adobe Air, since Javascript doesn't have the import directive.
How can I manage to do that ?
I posted a demo of how to use FZip with Adobe Air and Javascript. I hope it hopes clear things up for you.
In short you need to pull the SWF file out of the compiled SWC (when applicable) and access the class.
The demo is pretty simple and really just a proof of concept but you should be able to extend it easily.
http://www.drybydesign.com/2010/05/12/adobe-air-fzip-without-flex/
-Ari
Ari's example is pretty good, and it got me started but he left out some pretty crucial stuff--like writing the uncompressed files back to the disk. And the zip file does not have to be hosted remotely--the thing about AIR is that it runs like a local Application...here is an example that build on the good start Ari gave us. (I am using HTML5 just to be cool and hip and modern! :)-
<!DOCTYPE HTML>
<html>
<head>
<title>Test Fzip</title>
<script type="application/x-shockwave-flash" src="scripts/fzip.swf"></script>
<script type="text/javascript" src="scripts/AIRAliases.js"></script>
<script type="text/javascript" src="scripts/AIRIntrospector.js"></script>
<script type="text/javascript" src="scripts/jquery-1.4.2.js"></script>
<script type="text/javascript">
var fzip;
if (window.runtime) {
if (!fzip)
fzip = {};
fzip.FZip = window.runtime.deng.fzip.FZip;
fzip.FZipFile = window.runtime.deng.fzip.FZipFile;
}
var file = air.File.documentsDirectory.resolvePath("test.zip");
//file.url
var zip = new fzip.FZip;
zip.addEventListener(air.Event.OPEN, onopen);
zip.addEventListener(air.Event.COMPLETE, oncomplete);
zip.load(new air.URLRequest(file.url.toString()));
function oncomplete(event) {
var count = zip.getFileCount();
alert(count);
for ( var idx = 0; idx < count; idx++)
{
var zfile = zip.getFileAt(idx);
// alert(zfile.filename);
var uzfile = air.File.applicationStorageDirectory.resolvePath(zfile.filename);
var stream = new air.FileStream();
stream.open( uzfile, air.FileMode.WRITE );
stream.writeBytes( zfile.content,0, zfile.content.length );
stream.close();
}
}
function onopen(event) {
alert("file is opened");
}
</script>
</head>
<body>
</body>
</html>

Categories

Resources