I am trying to load a frame script following this tutorial:
mm.loadFrameScript("chrome://myextension/content/content.js", true);
My extension is an Add-on SDK type. The tutorial says I need to specify content in chrome.manifest first. I did not even have that file (only package.json and index.js). So I've created myextension/chrome.manifest with a single line:
content content.js
And created a file myextension/content/content.js:
console.log("content script executed");
The problem is, when mm.loadFrameScript("chrome://myextension/content/content.js", true); is executed in index.js, I get error:
No chrome package registered for chrome://myextension/content/content.js
I guess it is because the manifest file is wrong or not being registered all together? But I couldn't find any clues to my problem on the chrome registration page.
That tutorial is generally targeted towards XUL or restartless/bootstrap add-ons. A more appropriate tutorial in that hierarchy is: Multiprocess Firefox and the SDK. The gist of that page is that if you stay with the Add-on SDK APIs your extension should just work in multiprocess Firefox, and that if it doesn't then it is a bug in the Add-on SDK.
In general, Add-on SDK extensions will use one of the High Level APIs to load content scripts. The primary APIs which would be used are tabs and page-mod, but there are others (e.g. context-menu and page-worker). There are also the Low-Level APIs of remote/parent and remote/child which deal with processes and frames.
However, you can use frame scripts if you desire to do so. Add-on SDK extensions are actually restartless/bootstrapped extensions which are wrapped by the add-on SDK. Thus, you can do most things which can be done in a bootstrapped extension within an Add-on SDK extension if you really want to do so. However, it is better to stay within the APIs provided by the Add-on SDK. If you go around those APIs then you loose some of the advantages which the Add-on SDK exists to provide to you (e.g. hiding the actual implementation behind APIs which are intended not to change).
Frame scripts and content scripts exist to perform the same function: access the content (e.g. HTML pages) which has been loaded into an area that is potentially under the control of a different process (e10s). Largely, the things that are referred to by the two different names are very similar. However, you will almost exclusively see the term "frame scripts" used only for XUL and bootstrap extensions while "content scripts" will almost always be referring to Add-on SDK scripts.
Again, you are probably better off using the Add-on SDK APIs to load the scripts you are using to access content that is potentially in another process.
Add-on SDK: Using the data directory:
For an Add-on SDK extension, instead of using a chrome:// URL it is more appropriate to use the [add-on base directory]/data/ directory and obtain the URL reference to your file using the sdk/self API. You would do this with:
var self = require("sdk/self");
let frameScriptUrl = self.data.url("content.js")
Then your loadFrameScript() line would be:
mm.loadFrameScript(frameScriptUrl, true);
Using chrome.manifest:
content lines:
The content line of the chrome.manifest file is:
content packagename uri/to/files/ [flags]
This is a minimum of 3 fields separated by whitespace with one or more optional additional fields for flags.
The first field is content which indicates that this is a line that defines how a chrome://packagename/content URL will be resolved.
The second field is a packagename. This is a name that you make up which must not conflict with other package names already in use by Firefox or those added by other extensions. Usually, this will be the name you are using for your add-on, or some permutation of the name, or id, if using multiple content lines. The name and ID of your Add-on SDK are usually the value of the name and id properties you included in your package.json file, but may be somewhat different. The packagename does not need to be your name or ID, it just needs to be unique to everything that was loaded into Firefox.
The third field is the URL to the directory which contains the files you are going to be referencing. This URL can either be absolute, or relative to the location of the chrome.manifest file. This URL must end with a /.
Firefox looks for the chrome.manifest file in the base directory of your add-on (the same one in which package.json is located). While it is possible to have additional chrome.manifest files using the manifest keyword, that would be unusual.
The prototypical add-on chrome.manifest would contain a line such as:
content packagename chrome/content
When using the chrome URLs made valid by that content line in your chrome.manifest you would use something like chrome://packagename/content/myScriptFile.js. This would reference a file called myScriptFile.js that is in the [add-on base directory]/chrome/content/ directory.
Another example:
chrome.manifest:
content myAddOnName my/special/directory/
In this case, chrome://myAddOnName/content/myScriptFile.js would reference the file [add-on base directory]/my/special/directory/myScriptFile.js.
Your specific issue:
While you stated you were following the tutorial, you did not actually do so for the contents of your chrome.manifest file. Nor when you changed what that file contained did you follow the description of what the content line should contain. However, even if you had followed the tutorial exactly, you would not have ended up with a functioning add-on because the tutorial was incorrect as to what should be in that file. The line in the tutorial was actually invalid. I have corrected that line in the MDN documentation.
What you need to use as the line in your chrome.manifest will depend on the directory structure you desire to use for your extension.
If you wanted your content.js to exist in a directory [add-on base directory]/chrome/content/ and be referenced by chrome://myAddOnName/content/content.js then the content line of your chrome.manifest would be:
content myAddOnName chrome/content/
Note: Even though you used myextension in your question (in your chrome:// URL only) for your packagename, I have not used it here to try to more clearly indicate that the packagename should be a name you choose which is unique to your add-on. It is not clear to me that you are using myextension in your question as a placeholder for what you are really using within your extension. If that is the actual text you are using rather than a placeholder for your question, then I would caution you that the packagename myextension is not very unique and has a reasonable chance to exist in some other random extension. If it does, it may, or may not, cause either or both of your extensions to malfunction.
The SDK provides an abstraction for frame scripts and process scripts (beyond the page-mod and tab attach mechanisms which also rely on frame scripts under the hood), the remote/parent and remote/child modules. You can use remote_require to load a process script and then use remote/child in the process script to enumerate the sdk wrappers for the frame script global objects.
Related
I use Squarespace as my CMS. I'd like to know if there's a way to have the actual .JS file load itself asynchronously to ultimately reduce the site load time.
For those who don't know, Squarespace gives very limited access to backend content and files. So I'd like to know if there are any alternatives to reduce render-blocking resources.
The file is located in [websitename]/scripts/site-bundle.js found through SFTP.
EDIT: Here's the full code https://codepen.io/anon/pen/MMKZyQ, as you can see I can't find any script tags to insert the defer or async values.
!function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={exports:{},id:o,loaded:!1};return e[o].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}n(2);var r=n(6),i=o(r),a=n(60),u=n(65),c=o(u),l=n(85),s=o(l);n(86);var d=n(87),f=o(d),p=n(100),h=o(p),v=n(101),y=o(v),m=n(104),A=o(m),g=n(121),b=o(g),w=n(189),_=o(w),x=n(190),E=o(x),k=n(191),S=o(k),T=n(192),L=o(T),O=n(193),M=o(O),P=n(194),j=o(P),C=n(195),R=o(C),I=n(103),F=o(I),V=n(196),N=o(V),D=n(199),B=o(D),G=n(200),U=o(G),H=n(207),z=o(H);i.default.register("AncillaryLayout",f.default),i.default.register("FooterBreakpoints",h.default),i.default.register("HashManager",y.default),i.default.register("IndexFirstSectionHeight",A.default),i.default.register("IndexGallery",b.default),i.default.register("IndexGalleryVideo",_.default),i.default.register("IndexNavigation",E.default),i.default.register("HeaderNavFolderTouch",S.default),i.default.register("HeaderOverlay",L.default),i.default.register("MobileClassname",M.default),i.default.register("MobileOverlayFolders",j.default),i.default.register("MobileOffset",R.default),i.default.register("MobileOverlayToggle",F.default),i.default.register("Parallax",N.default),i.default.register("ScrollIndicator",B.default),i.default.register("SiteLoader",U.default),i.default.register("UserAccountLink",z.default),i.default.register("VideoBackground",function(e){return(0,c.default)(e,function(e){var t=e.handleResize,n=e.handleTweak;(0,s.default)(t,105),a.Tweak.watch("tweak-overlay-parallax-enabled",n)})}),window.addEventListener("controller:refresh",i.default.refresh)},function(e,t,n){n(3).polyfill()},function(e,t,n){(function(t){for(var o=n(4),r="undefined"==typeof window?t:window,i=["moz","webkit"],a="AnimationFrame",u=r["request"+a],c=r["cancel"+a]||r["cancelRequest"+a],l=0;!u&&l<i.length;l++)u=r[i[l]+"Request"+a],c=r[i[l]+"Cancel"+a]||r[i[l]+"CancelRequest"+a];if(!u||!c){var s=0,d=0,f=[],p=1e3/60;u=function(e){if(0===f.length){var t=o(),n=Math.max(0,p-(t-s));s=n+t,setTimeout(function(){var e=f.slice(0);f.length=0;for(var t=0;t<e.length;t++)if(!e[t].cancelled)try{e[t].callback(s)}catch(e){setTimeout(function(){throw e},0)}},Math.round(n))}return f.push({handle:++d,callback:e,cancelled:!1}),d},c=function(e){for(var t=0;t<f.length;t++)f[t].handle===e&&(f[t].cancelled=!0)}}e.exports=function(e){return u.call(r,e)},e.exports.cancel=function()
This can be accomplished for the Brine/Wright family of templates by doing the following:
Enable Developer Mode (Settings > Advanced > Developer Mode)
Using SFTP or Git, obtain access to the template files.
Within site.region file, change:
<squarespace:script src="site-bundle.js" combo="false" />
to
<squarespace:script src="site-bundle.js" combo="false" async="true"/>
Use SFTP or Git to update your template files on Squarespace's servers.
You may also use <script src="/scripts/site-bundle.js" async></script> instead of using Squarespace's script loader. Simply replace the aforementioned line in step 3.
As an aside, the unbundled code can be found in Squarespace's Wright GitHub repository.
For other templates not in the Brine/Wright family, similar steps may apply, though file names may differ.
There are two properties on a script tag to do exactly what you want to do:
specifying defer will defer the loading of the script until the page has been fully parsed (and rendered)
specifying async will indicate to the browser that this script can be loaded asynchronously at any point of its choosing.
Those two properties are both well supported (defer, async) and as such, you can and should make use of them to achieve this.
Marvin JS (downloaded from http://dl.chemaxon.com/marvinjs/15.2.16/marvinjs-15.2.16-all.zip) is a JavaScript-based chemical structure editing program that I would like to be able to embed into MediaWiki articles on my local server. I have read https://www.mediawiki.org/wiki/Manual:Developing_extensions but my understanding of PHP is very rudimentary so it wasn't particularly helpful.
I would like such an extension to create the following parser extension tag:
<marvin src = URL width = W height = H></marvin>
where URL is the location (on the local server) of the file being loaded by the Marvin JS program, W is the width of the applet and H is its height.
My efforts to date to add Marvin JS to articles via other means
I have seen several examples of how this applet can be embedded into HTML pages and as I knew writing an extension would be very difficult for me with my lack of PHP knowledge I did attempt to embed this applet as a Widget and via the AddScriptCss extension, both have failed me.
AddScriptCss
For AddScriptCss after installing the extension I added:
<addScript src = "Marvin/js/lib/jquery-1.9.1.min.js"></addScript>
<addScript src="Marvin/js/lib/rainbow/rainbow-custom.min.js"></addScript>
<addScript src="Marvin/gui/lib/promise-0.1.1.min.js"></addScript>
<addScript src="Marvin/js/marvinjslauncher.js"></addScript>
to an article and received this output on the page after I saved this:
UNIQ5d2750bb5e9527b2-addScript-00000004-QINU UNIQ5d2750bb5e9527b2-addScript-00000005-QINU UNIQ5d2750bb5e9527b2-addScript-00000006-QINU UNIQ5d2750bb5e9527b2-addScript-00000007-QINU
adding the rest of the URL to the src field (e.g., http://127.0.0.1/mediawiki/) doesn't seem to correct this problem. Opening up 'Developer tools' (I'm using Chrome) shows two errors:
Uncaught SyntaxError: Unexpected string (# index.php line 154. Which is a WikEd line)
http://ads.dfgio.com/loader.js?client=topaz0001 Failed to load resource: net::ERR_BLOCKED_BY_CLIENT
I also saw this Mediawiki Extension add Javascript in Header stackoverflow question so I attempted following 2nd's answer (Mediawiki Extension add Javascript in Header) and after replacing script with addHeadItem and saving the page all I was left with was my original wikitext except with the URLs hyperlinked.
Widgets
As far as Widgets go I tried adding:
<includeonly><script type = "text/javascript" src="http://127.0.0.1/mediawiki/Marvin/gui/lib/promise-0.1.1.min.js"></script>
<script type = "text/javascript" src="http://127.0.0.1/mediawiki/Marvin/js/marvinjslauncher.js"></script></includeonly>
To Widget:Marvin, then I added {{#widget:Marvin}} to a page with no results (quite literally; when I loaded the page after saving this edit there was nothing there). Here is a screenshot showing the screen that came up upon opening developers tools on this page.
Try the Gadgets extension. It's the one Wikipedia uses; well-maintained and fairly easy to use.
Or, is this is a local wiki, only accessible to you, and you don't care about security at all, you can just enable arbitrary HTML in wikitext via $wgRawHtml.
I'm trying to write a Google Chrome extension for showing PDF files. As soon as I detect that browser is redirecting to a URL pointing to a PDF file, I want it to stop loading the default PDF viewer, but start showing my UI instead. The UI will use PDF.JS to render the PDF and jQuery-ui to show some other stuff.
Question: how do I make this? It's very important to block the original PDF viewer, because I don't want to double memory consumption by showing two instance of the document. Therefore, I should somehow navigate the tab to my own view.
As the main author of the PDF.js Chrome extension, I can share some insights about the logic behind building a PDF Viewer extension for Chrome.
How to detect a PDF file?
In a perfect world, every website would serve PDF files with the standard application/pdf MIME-type. Unfortunately, the real world is not perfect, and in practice there are many websites which use an incorrect MIME-type. You will catch the majority of the cases by selecting requests that satisfy any of the following conditions:
The resource is served with the Content-Type: application/pdf response header.
The resource is served with the Content-Type: application/octet-stream response header, and its URL contains ".pdf" (case-insensitive).
Besides that, you also have to detect whether the user wants to view the PDF file or download the PDF file. If you don't care about the distinction, it's easy: Just intercept the request if it matches any of the previous conditions.
Otherwise (and this is the approach I've taken), you need to check whether the Content-Disposition response header exists and its value starts with "attachment".
If you want to support PDF downloads (e.g. via your UI), then you need to add the Content-Disposition: attachment response header. If the header already exists, then you have to replace the existing disposition type (e.g. inline) with "attachment". Don't bother with trying to parse the full header value, just strip the first part up to the first semicolon, then put "attachment" in front of it. (If you really want to parse the header, read RFC 2616 (section 19.5.1) and RFC 6266).
Which Chrome (extension) APIs should I use to intercept PDF files?
The chrome.webRequest API can be used to intercept and redirect requests. With the following logic, you can intercept and redirect PDFs to your custom viewer that requests the PDF file from the given URL.
chrome.webRequest.onHeadersReceived.addListener(function(details) {
if (/* TODO: Detect if it is not a PDF file*/)
return; // Nope, not a PDF file. Ignore this request.
var viewerUrl = chrome.extension.getURL('viewer.html') +
'?file=' + encodeURIComponent(details.url);
return { redirectUrl: viewerUrl };
}, {
urls: ["<all_urls>"],
types: ["main_frame", "sub_frame"]
}, ["responseHeaders", "blocking"]);
(see https://github.com/mozilla/pdf.js/blob/master/extensions/chromium/pdfHandler.js for the actual implementation of the PDF detection using the logic described at the top of this answer)
With the above code, you can intercept any PDF file on http and https URLs.
If you want to view PDF files from the local filesystem and/or ftp, then you need to use the chrome.webRequest.onBeforeRequest event instead of onHeadersReceived. Fortunately, you can assume that if the file ends with ".pdf", then the resource is most likely a PDF file. Users who want to use the extension to view a local PDF file have to explicitly allow this at the extension settings page though.
On Chrome OS, use the chrome.fileBrowserHandler API to register your extension as a PDF Viewer (https://github.com/mozilla/pdf.js/blob/master/extensions/chromium/pdfHandler-vcros.js).
The methods based on the webRequest API only work for PDFs in top-level documents and frames, not for PDFs embedded via <object> and <embed>. Although they are rare, I still wanted to support them, so I came up with an unconventional method to detect and load the PDF viewer in these contexts. The implementation can be viewed at https://github.com/mozilla/pdf.js/pull/4549/files. This method relies on the fact that when an element is put in the document, it eventually have to be rendered. When it is rendered, CSS styles get applied. When I declare an animation for the embed/object elements in the CSS, animation events will be triggered. These events bubble up in the document. I can then add a listener for this event, and replace the content of the object/embed element with an iframe that loads my PDF Viewer.
There are several ways to replace an element or content, but I've used Shadow DOM to change the displayed content without affecting the DOM in the page.
Limitations and notes
The method described here has a few limitations:
The PDF file is requested at least two times from the server: First a usual request to get the headers, which gets aborted when the extension redirects to the PDF Viewer. Then another request to request the actual data.
Consequently, if a file is valid only once, then the PDF cannot be displayed (the first request invalidates the URL and the second request fails).
This method only works for GET requests. There is no public API to directly get response bodies from a request in a Chrome extension (crbug.com/104058).
The method to get PDFs to work for <object> and <embed> elements requires a script to run on every page. I've profiled the code and found that the impact on performance is negligible, but you still need to be careful if you want to change the logic.
(I first tried to use Mutation Observers for detection, which slowed down the page load by 3-20% on huge documents, and caused an additional 1.5 GB peak in memory usage in a complex DOM benchmark).
The method to detect <object> / <embed> tags might still cause any NPAPI/PPAPI-based PDF plugins to load, because it only replaced the <embed>/<object> tag's content when it has already been inserted and rendered. When a tab is inactive, animations are not scheduled, and hence the dispatch of the animation event will significantly be delayed.
Afterword
PDF.js is open-source, you can view the code for the Chrome extension at https://github.com/mozilla/pdf.js/tree/master/extensions/chromium. If you browse the source, you'll notice that the code is a bit more complex than I explained here. That's because extensions cannot redirect requests at the onHeadersReceived event until I implemented it a few months ago (crbug.com/280464, Chrome 35).
And there is also some logic to make the URL in the omnibox look a bit better.
The PDF.js extension continues to evolve, so unless you want to significantly change the UI of the PDF Viewer, I suggest to ask users to install the PDF.js's official PDF Viewer in the Chrome Web Store, and/or open issues on PDF.js's issue tracker for reasonable feature requests.
Custom PDF Viewer
Basically, to accomplish what you want to do you'll need to:
Interject the PDF's URL when it's loaded;
Stop the PDF from loading;
Start your own PDF viewer and load the PDF inside it.
How to
Using the chrome.webRequest API you can easily listen to the web requests made by Chrome, and, more specifically, the ones that are going to load .pdf files. Using the chrome.webRequest.onBeforeRequest event you can listen to all the requests that end with ".pdf" and get the URL of the requested resource.
Create a page, for example display_pdf.html where you will show the PDFs and do whatever you want with them.
In the chrome.webRequest.onBeforeRequest listener, prevent the resource from being loaded returning {redirectUrl: ...} to redirect to your display_pdf.html page.
Pass the PDF's URL to your page. This can be done in several ways, but, for me, the simplest one is to add the encoded PDF URL at the end of your page's url, like an encoded query string, something like display_pdf.html?url=http%3A%2F%2Fwww.example.com%2Fexample.pdf.
Inside the page, get the URL with JavaScript and process and render the PDF with any library you want, like PDF.js.
The code
Following the above steps, your extension will look like this:
<root>/
/background.js
/display_pdf.html
/display_pdf.js
/manifest.json
So, first of all, let's look at the manifest.json file: you will need to declare the permissions for webRequest and webRequestBlocking, so it should look like this:
{
"manifest_version": 2,
"name": "PDF Test",
"version": "0.0.1",
"background": {
"scripts": ["/background.js"]
},
"permissions": ["webRequest", "webRequestBlocking", "<all_urls>"],
}
Then, in your background.js you will listen to the chrome.webRequest.onBeforeRequest event and update the tab which is loading the PDF with the URL of your custom display_pdv.html page, like this:
chrome.webRequest.onBeforeRequest.addListener(function(details) {
var displayURL;
if (/\.pdf$/i.test(details.url)) { // if the resource is a PDF file ends with ".pdf"
displayURL = chrome.runtime.getURL('/display_pdf.html') + '?url=' + encodeURIComponent(details.url);
return {redirectUrl: displayURL};
// stop the request and proceed to your custom display page
}
}, {urls: ['*://*/*.pdf']}, ['blocking']);
And finally, in your display_pdf.js file you will extract the PDF's url from the query string and use it to do whatever you want:
var PDF_URL = decodeURIComponent(location.href.split('?url=')[1]);
// this will be something like http://www.somesite.com/path/to/example.pdf
alert('The PDF url is: ' + PDF_URL);
// do something with the pdf... like processing it with PDF.js
Working Example
A working example of what I said above can be found HERE.
Documentation links
I recommend you to take a look at the official documentation of the above specified APIs, that you can find following these links:
chrome.webRequest API
chrome.webRequest.onBeforeRequest event
chrome.runtime API
chrome.runtime.getURL method
I am creating a firefox extension that lets the operator perform various actions that modify the content of the HTML document. The operator does not edit HTML, they take other actions and my extension modifies the document by inserting elements, adding attributes, and so forth.
When the operator is finished, they need to be able to save the HTML document as a file (or have my extension send it to an internet destination, but this is not required since they can email the saved file).
I thought maybe the changes made by the javascript code in my extension would be reflected in the HTML document, but when I ask the firefox browser to "view source" after making modifications, it displays the original HTML text.
My questions are:
#1: What is the easiest way for the operator to save the HTML document with all the changes my extension has made?
#2: What is the easiest way for the javascript code in my extension to process the HTML document contents and write to an HTML file on the local disk?
#3: Is any valid HTML content incapable of accurate representation in the saved file?
#4: Is the TreeWalker part of the solution (see below)?
A couple observations from my research so far:
I've read about the TreeWalker object, which seems to provide a fairly painless way for an extension to walk through everything (?or almost everything?) in the HTML document. But does it expose everything so everything in the original (and my modifications) can be saved without losing anything of importance?
Does the TreeWalker walk through the HTML document in the "correct order" --- the order necessary for my extension to generate the original and/or modified HTML document?
Anything obscure or tricky about these problems?
Ok so I am assuming here you have access to page DOM. What you need to do it basically make changes to the dom and then get all the dom code and save it as a file. Here is how you can download the page's html code. This will create an a tag which the user needs to click for the file to download.
var a = document.createElement('a'), code = document.querySelectorAll('html')[0].innerHTML;
a.setAttribute('download', 'filename.html');
a.setAttribute('href', 'data:text/html,' + code);
Now you can insert this a tag anywhere in the DOM and the file will download when the user clicks it.
Note: This is sort of a hack, this injects entire html of the file in the a tag, it should in theory work in any up to date browser (except, surprise, IE). There are more stable and less hacky ways of doing it like storing it in a file system API file and then downloading that file instead.
Edit: The document.querySelectorAll line accesses the page DOM. For it to work the document must be accessible. You say you are modifying DOM so that should already be there. Make sure you are adding the code on the page and not your extension code. This code will be at the same place as your DOM modification code, not your extension pages that can't access the DOM.
And as for the a tag, it will be inserted in the page. I skipped the steps since I assumed you already know how to manipulate DOM and also because I don't know where you would like to add the link. And you can skip the user action of clicking the link too, but it's a hack and only works in modern browsers. You can insert the a tag somewhere in the original page where user won't see it and then call the a.click() function to simulate a click event on the link. But this is not a legit way and I personally only use it on my practice projects to call click event listeners.
I can only test this on chrome not on FF but try this code, this will not require you to even add the a link to DOM. You need to add this next to the DOM manipulation code. This will work if luck is on your side :)
var a = document.createElement('a'), code = document.querySelectorAll('html')[0].innerHTML;
a.setAttribute('download', 'filename.html');
a.setAttribute('href', 'data:text/html,' + code);
a.click();
There is no easy way to do this with the web API only, at least when you want a result that does not omit stuff like the doctype or comments. You could still write a serializer yourself that goes through document.childNodes and serialized according to the node type (Element.outerHTML, Comment.data and so on).
Luckily, you're writing a Firefox add-on, so you have access to a lot more (powerful) stuff.
While still not 100% perfect, the nsIDocumentEncoder implementations will produce pretty decent results, that should only differ in some whitespace and explicit charset declaration at most (everything else is a bug).
Here is an example on how one might use this component:
function serializeDocument(document) {
const {
classes: Cc,
interfaces: Ci,
utils: Cu
} = Components;
let encoder = Cc['#mozilla.org/layout/documentEncoder;1?type=text/html'].createInstance(Ci.nsIDocumentEncoder);
encoder.init(document, 'text/html', Ci.nsIDocumentEncoder.OutputLFLineBreak | Ci.nsIDocumentEncoder.OutputRaw);
encoder.setCharset("utf-8");
return encoder.encodeToString();
}
If you're writing an SDK add-on, stuff gets more complicated as the SDK abstracts some important stuff away. You'll need to go through the chrome module, and also figure out the active window and tab yourself. Something like Services.wm.getMostRecentWindow("navigator:browser").content.document (Services.jsm) should do the trick.
In XUL overlay add-ons, content.document should suffice to get the document of the currently active tab, and you have Components access already.
Still, you need to let the user choose a file destination, usually through nsIFilePicker and then actually write the file, by using something like a file stream or the fully async OS.File API.
Looks like I get to answer my own question, thanks to someone in mozilla #extdev IRC.
I got totally faked out by "view source". When I didn't see my modifications in the window displayed by "view source", I assumed the browser would not provide the information.
However, guess what? When I "file" ===>> "save page as...", then examine the page contents with a plain text editor... sure enough, that contained the modifications made by my firefox extension! Surprise!
A browser has no direct write access to the local filesystem. The only read access it has is when explicitly provide a file:// URL (see note 1 below)
In your case, we are explicitly talking about javascript - which can read and write cookies and local storage. It can also send stuff back to the server and retrieve it, e.g. using AJAX.
Stuff you put in local storage/cookies is effectively not accessible to other programs (such as email clients).
It is possible to create very long mailto: URLs (see note 2) but only handles inline content in the email and you're going to run into all sorts of encoding issues that you're not ready to deal with.
Hence I'd recommend pursuing storage serverside via AJAX - and look at local storage once you've got this sorted/working.
Note 1: this is not strictly true. a trusted, signed javascript has access to additional functions which may include direct file access.
Note 2: (the limit depends on the browser and the email client - Lotus Notes truncaets the content rather a lot)
I have a reference how to load an image from a file:
http://jsfiddle.net/eD2Ez/31/
but I don't know how to manually load the image by inputting or giving a string
parameter that contains path file (e.g. C:\myImage\img12.jpg)
in the link, the image is directly load from the file dialog.
Indeed, to my knowledge there is no established or even mildly known way to load local files programmatically due to security policies.
Aside from that you may want to use the onload handler on img to be sure that the image is loaded when you draw it on canvas. At the moment in some cases where the image is too big or the computer is slow you may not get the image rendered.
Check demo: http://jsfiddle.net/eD2Ez/38/
Experimental / Future
However, there could be a light on the horizon. There is something called FileSystem API in the works but didn't work for me (although it should).
Firstly the FileSystem API is currently implemented under vendor prefix on Chrome version 21+.
Also you have to use the flag --allow-file-access-from-files when you launch Chrome.
But even with all this, I still didn't manage to make it happen with Chrome v21 and still get SECURITY_ERR.
Check the "demo": http://jsfiddle.net/MQFGd/2/
Sources:
MDN FileSystem API
W3C FileSystem API
FileSystem API tutorial
$('<img src="'+img.src+'"/>').insertAfter('#cvs');
Here is demo http://jsfiddle.net/eD2Ez/32/