Getting html of current page - firefox

I'm creating a Firefox addon using jetpack (jpm with node.js) to extend the Firefox developer tools to allow editing the current page's html. (I know this feature already exists; just trying to learn the ropes).
What API do I use to access the current page's HTML? I see that there is a Debugger.Source but I'm not sure if this is correct. If so, how do I retrieve this data?

As the first answer suggests, you can get at the html source using a content script injected the page. For example, here's a very simple approach that uses the tabs module to attach a content script into the current page:
const self = require('sdk/self');
const tabs = require('sdk/tabs');
let { ActionButton } = require("sdk/ui/button/action");
let button = ActionButton({
id: "my-button-id",
label: "Get HTML Source",
icon: {
"16": "chrome://mozapps/skin/extensions/extensionGeneric.png",
"32": "chrome://mozapps/skin/extensions/extensionGeneric.png"
},
onClick: (state) => {
let worker = tabs.activeTab.attach({
contentScript: 'self.port.emit("htmlSrc", {head: document.head.outerHTML, body: document.body.outerHTML});'
});
worker.port.on('htmlSrc', (result) => {
worker.destroy(); // clean up
let src = "<html>\n"+ result.head + "\n" + result.body + "\n</html>";
require('sdk/clipboard').set(src, 'text');
});
}
});

Direct access via SDK is impossible, but you can use content scripts to read and modify the page.

Related

Ideal way to ajax load a view in Drupal 8 with contextual filters

I have a taxonomy called category.
I have a menu with links to each of these taxonomy items.
The Taxonomy page for each of these items has that menu and also contains a view which uses a contextual filter from the URL to filter the content of the view to content with that taxonomy term.
I wanted to Ajax load the view content when one of these menu items is clicked.
I've been able to achieve the desired result by enabling ajax on the view and using the following JavaScript.
(function ($) {
Drupal.behaviors.course_browser = {
attach: function (context, settings) {
// should only be one menu, but guard against 0, and avoid the if statement.
context.querySelectorAll(".menu--categories").forEach((menu) => {
// for each link in the menu
menu.querySelectorAll(".nav-link").forEach((link) => {
// on click
link.addEventListener("click", (event) => {
event.preventDefault();
// fetch the taxonomy term id from menu link
let tid = event.target.dataset.drupalLinkSystemPath.replace(
"taxonomy/term/",
""
);
// make the ajax call
$.ajax({
url: "/views/ajax",
type: "post",
data: {
view_name: "course_browser",
view_display_id: "block_1",
view_args: tid,
},
success: (response) => {
response.forEach((action) => {
// the response contains a number of commands; I'm not sure
if (
action.command === "insert" &&
action.method === "replaceWith"
) {
let viewElement = document.querySelector(VIEW_SELECTOR);
// update the html of the course browser
viewElement.innerHTML = action.data;
// update the url in the browser
window.history.pushState("", "", event.target.href);
// seperate function to adjust my page title
updatePageTitle(event.target.textContent);
// call drupal behaviours passing context to ensure all the other js code gets a chance to manipulate the new content
Drupal.attachBehaviors(viewElement);
}
});
},
error: function (data) {
console("An error occured fetching the course browser");
},
});
});
});
});
},
};
})(jQuery);
I'm looking for feedback on my approach here; my main concern at the moment is the way I handle the response. When I look at the response I receive something like that shown below:
0: {command: "settings", settings: {…}, merge: true}
1: {command: "add_css", data: "<link rel="stylesheet" media="all" href="/core/modules/views/css/views.module.css?qcog4i" />↵"}
2: {command: "insert", method: "append", selector: "body", data: "<script src="/core/assets/vendor/jquery/jquery.min…/js/modules/views/ajax_view.js?qcog4i"></script>↵", settings: null}
3: {command: "insert", method: "replaceWith", selector: ".js-view-dom-id-", data: "The HTML"}
As you can see, I'm manually handling the response by cherry picking the part I want and replacing the HTML of view. Based on what I've seen around Drupal, I think there should be something I can pass this response to that handles it automatically. When I look at the window object of the browser, I can see Drupal.AjaxCommands which looks like it was designed to handle this, but I'm not sure how I should be using this.
I also note that in the case I can simply pass this response to something to have those AjaxCommands executed, the selector ".js-view-dom-id-" isn't right. So I could tweak the response before I pass it, or if someone knows a way to adjust the ajax request to perhaps get the right selector, that would be ideal.
Sorry if this info is readily available somewhere...there are quiet a few resources around related to Drupal and Ajax but I haven't been able to find examples of exactly what I'm doing here, the circumstances always seem to differ enough that I can't use them.
Thanks for any help.

How the tabs.open() function work with separate content script file?

I am creating Firefox addons that works like search in chrome. How can i use browser.tabs.create() function with 'url' option?
I have read this article. But In here, there is no documentation of How to use this tab creation with variable url.
//code for CMcontentScript.js-start
var tabs = require("../sdk/tabs");
self.on("click", function(node, data) {
textContent = window.getSelection().toString();
var searchURL = google.com?searchtid=" + textContent;
tabs.open(searchURL);//In here i want to know how we can add variable url to 'url' option
});
//code for CMcontentScript.js-ends
//code for index.js-start
searchMenu = cm.Item({
label: "Search With enadoc",
data: setURL,
context: cm.SelectionContext(),
image: self.data.url("./icon-16.png"),
contentScriptFile: "./CMcontentScript.js"
});
//code for index.js-ends
I think you are creating a JPM addon. Which is an SDK addon. This is not a WebExtension. And the chrome.browser.tabs.create is a webextension api.
To create a new tab you should do this:
var tabs = require("sdk/tabs");
self.on("click", function(node, data) {
var textContent = window.getSelection().toString();
var searchURL = 'http://www.google.com?searchtid=' + textContent;
tabs.open(searchURL);
});

How to remove an event listener?

I use the Mozilla's Add-on Builder. I am looking for a way to remove an event listener in a contentScript. I use the port way to communicate between add-on script code and the content script code.
The problem is the callback on event "response" is called more than once. I want it to be called once and declared in the callback of the event show.
Can someone help me with that?
main.js code:
var Panel = require("panel").Panel;
var popup_panel = Panel({
width: 286,
height: 340,
contentURL: require("self").data.url("popup.html"),
allow: { script: true },
contentScriptWhen: "end",
contentScriptFile : [
require("self").data.url("test.js")
],
onShow: function(){
this.port.emit("show");
var pan = this;
this.port.on("hide", function(){pan.hide();});
}
});
var Widget = require("widget").Widget;
var widget = Widget({
id: "mozilla-icon",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico",
panel: popup_panel
});
popup_panel.port.on("get", function(){
popup_panel.port.emit("response");
});
Content script (test.js):
self.port.on("show", function(){
console.log("show");
function response(){
console.log("reponse called");
}
self.port.emit("get");
self.port.once("response", response);
self.port.removeListener("response", response);
});
full source code
Finally I found the problem. It is a bug in the add-on kit. In the file api-utils/lib/content/content-worker.js in the function removeListener the index is always -1.
The parameter given in the indexOf is the name of the event and it search a function. It is incorrect.
So to solve the problem I replace the line let index = listeners[name].indexOf(name); by let index = listeners[name].indexOf(callback);.
EDIT
The bug has been fixed. It will publish in the version 1.10 see here

Find URL of current tab. Making a FireFox Browser add-on

I'm making a Firefox Browser Add-on and need to find the url of the current tab
I've tried this post Opening a URL in current tab/window from a Firefox Extension but it tells me that 'window' is not defined. (I think because I am making an add-on rather than an extension.)
Here's what I've tried to do:
var widgets = require('widget');
var tabs = require('tabs');
var widget1 = widgets.Widget({
id: "widget1",
label: "widget1",
contentURL: "http://www.mozilla.org/favicon",
onClick: function() {
console.log(tabs.url);
}
})
I've made a widget such that when I click it the url of the current tab should be 'console.log'ed.
Doesn't seem to happen! Keep getting "info: undefined" which clearly means that tabs.url isn't returning anything. But this seems to be the way to use it according to https://addons.mozilla.org/en-US/developers/docs/sdk/1.5/packages/addon-kit/docs/tabs.html
Anyone have any ideas?
Thanks,
Will
You're almost there:
const { ActionButton } = require("sdk/ui/button/action");
const clipboard = require("sdk/clipboard");
const tabs = require('sdk/tabs');
let button = ActionButton({
id: "my-button-id",
label: "Button Label",
icon: {
"32": "chrome://mozapps/skin/extensions/extensionGeneric.png"
},
onClick: function(state) {
let url = tabs.activeTab.url;
console.log("active tab url:", url);
require("sdk/notifications").notify({
title: "Active Tab's Url is "+url,
text: "Click to copy.",
onClick: function() {
clipboard.set(url);
}
});
}
});
You should check out the documentation on the tabs module.
Note: I've updated this code example to use the new ui modules available since Firefox 29 - the 'widget' module used in the original question was valid at the time but has since been deprecated and then removed.

"sessionstore-state-read" observer not working

Im currently making a firefox extension that will let you choose what tabs you want to reopen after startup, instead of opening everything (it opens about:sessionrestore page). My JS code looks like this:
Edit: code updated with working version
chrome.manifest
component {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} components/myextension.js
contract #example.com/MyExtension;1 {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
category profile-after-change MyExtension #example.com/MyExtension;1
components/myextension.js
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const CI = Components.interfaces, CC = Components.classes, CR = Components.results;
// class declaration
function MyExtension () {}
MyExtension.prototype = {
classDescription: "My Firefox Extension",
classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"),
contractID: "#example.com/MyExtension;1",
QueryInterface: XPCOMUtils.generateQI([CI.nsIObserver]),
// add to category manager
_xpcom_categories: [{
category: "profile-after-change"
}],
observe: function(aSubject, aTopic, aData)
{
var obs = CC["#mozilla.org/observer-service;1"].getService(CI.nsIObserverService);
switch (aTopic)
{
case "profile-after-change":
obs.addObserver(this, "sessionstore-state-read", false);
break;
case "sessionstore-state-read":
aSubject.QueryInterface(CI.nsISupportsString);
let newData = { windows: [{ tabs: [{ entries: [{
url: "about:sessionrestore",
formdata: { "#sessionData": aSubject.data }
}] }] }] };
aSubject.data = JSON.stringify(newData);
break;
default:
throw Components.Exception("Unknown topic: " + aTopic);
}
}
};
if (XPCOMUtils.generateNSGetFactory)
var NSGetFactory = XPCOMUtils.generateNSGetFactory([EmbarrassedObserver]);
else
var NSGetModule = XPCOMUtils.generateNSGetModule([EmbarrassedObserver]);
It works fine with sessionstore-state-write (commented lines), but i want to do it with the sessionstore-state-read event, cuz i dont want to edit what it writes to the disk. The problem is, when i change to the read event, my observer stops working. My guess is i register the observer too late (after the session is read), so i tried to register it as soon as possible (when the extension is first loaded, insted of window load event), but it still wont work. Any idea how to get it working?
You need to write your extension as an XPCOM component that listens to the profile-after-change notification (the first notification that extensions are allowed to listen to). Then your component will be able to listen to the sessionstore-state-read notification.

Resources