Bug 771555 - GCLI needs a addon, resize, restart, cookie and pagemanip commands; r=dcamp,jwalker

This commit is contained in:
Michael Ratcliffe 2012-07-10 17:53:04 +01:00
parent 1376907704
commit 1849406afb
14 changed files with 1760 additions and 4 deletions

View File

@ -23,6 +23,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers",
XPCOMUtils.defineLazyModuleGetter(this, "console", XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource:///modules/devtools/Console.jsm"); "resource:///modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
let prefSvc = "@mozilla.org/preferences-service;1"; let prefSvc = "@mozilla.org/preferences-service;1";
XPCOMUtils.defineLazyGetter(this, "prefBranch", function() { XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
let prefService = Cc[prefSvc].getService(Ci.nsIPrefService); let prefService = Cc[prefSvc].getService(Ci.nsIPrefService);
@ -30,6 +33,7 @@ XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
}); });
Cu.import("resource:///modules/devtools/GcliTiltCommands.jsm", {}); Cu.import("resource:///modules/devtools/GcliTiltCommands.jsm", {});
Cu.import("resource:///modules/devtools/GcliCookieCommands.jsm", {});
/** /**
* A place to store the names of the commands that we have added as a result of * A place to store the names of the commands that we have added as a result of
@ -332,6 +336,51 @@ gcli.addCommand({
} }
}); });
/**
* Restart command
*
* @param boolean nocache
* Disables loading content from cache upon restart.
*
* Examples :
* >> restart
* - restarts browser immediately
* >> restart --nocache
* - restarts immediately and starts Firefox without using cache
*/
gcli.addCommand({
name: "restart",
description: gcli.lookup("restartFirefoxDesc"),
params: [
{
name: "nocache",
type: "boolean",
defaultValue: false,
description: gcli.lookup("restartFirefoxNocacheDesc")
}
],
returnType: "string",
exec: function Restart(args, context) {
let canceled = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(canceled, "quit-application-requested", "restart");
if (canceled.data) {
return gcli.lookup("restartFirefoxRequestCancelled");
}
// disable loading content from cache.
if (args.nocache) {
Services.appinfo.invalidateCachesOnRestart();
}
// restart
Cc['@mozilla.org/toolkit/app-startup;1']
.getService(Ci.nsIAppStartup)
.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
return gcli.lookup("restartFirefoxRestarting");
}
});
/** /**
* 'inspect' command * 'inspect' command
*/ */
@ -542,3 +591,615 @@ gcli.addCommand({
return promise; return promise;
} }
}); });
/**
* 'export' command
*/
gcli.addCommand({
name: "export",
description: gcli.lookup("exportDesc"),
});
/**
* The 'export html' command. This command allows the user to export the page to
* HTML after they do DOM changes.
*/
gcli.addCommand({
name: "export html",
description: gcli.lookup("exportHtmlDesc"),
exec: function(args, context) {
let document = context.environment.contentDocument;
let window = document.defaultView;
let page = document.documentElement.outerHTML;
window.open('data:text/plain;charset=utf8,' + encodeURIComponent(page));
}
});
/**
* 'pagemod' command
*/
gcli.addCommand({
name: "pagemod",
description: gcli.lookup("pagemodDesc"),
});
/**
* The 'pagemod replace' command. This command allows the user to search and
* replace within text nodes and attributes.
*/
gcli.addCommand({
name: "pagemod replace",
description: gcli.lookup("pagemodReplaceDesc"),
params: [
{
name: "search",
type: "string",
description: gcli.lookup("pagemodReplaceSearchDesc"),
},
{
name: "replace",
type: "string",
description: gcli.lookup("pagemodReplaceReplaceDesc"),
},
{
name: "ignoreCase",
type: "boolean",
description: gcli.lookup("pagemodReplaceIgnoreCaseDesc"),
},
{
name: "selector",
type: "string",
description: gcli.lookup("pagemodReplaceSelectorDesc"),
defaultValue: "*:not(script):not(style):not(embed):not(object):not(frame):not(iframe):not(frameset)",
},
{
name: "root",
type: "node",
description: gcli.lookup("pagemodReplaceRootDesc"),
defaultValue: null,
},
{
name: "attrOnly",
type: "boolean",
description: gcli.lookup("pagemodReplaceAttrOnlyDesc"),
},
{
name: "contentOnly",
type: "boolean",
description: gcli.lookup("pagemodReplaceContentOnlyDesc"),
},
{
name: "attributes",
type: "string",
description: gcli.lookup("pagemodReplaceAttributesDesc"),
defaultValue: null,
},
],
exec: function(args, context) {
let document = context.environment.contentDocument;
let searchTextNodes = !args.attrOnly;
let searchAttributes = !args.contentOnly;
let regexOptions = args.ignoreCase ? 'ig' : 'g';
let search = new RegExp(escapeRegex(args.search), regexOptions);
let attributeRegex = null;
if (args.attributes) {
attributeRegex = new RegExp(args.attributes, regexOptions);
}
let root = args.root || document;
let elements = root.querySelectorAll(args.selector);
elements = Array.prototype.slice.call(elements);
let replacedTextNodes = 0;
let replacedAttributes = 0;
function replaceAttribute() {
replacedAttributes++;
return args.replace;
}
function replaceTextNode() {
replacedTextNodes++;
return args.replace;
}
for (let i = 0; i < elements.length; i++) {
let element = elements[i];
if (searchTextNodes) {
for (let y = 0; y < element.childNodes.length; y++) {
let node = element.childNodes[y];
if (node.nodeType == node.TEXT_NODE) {
node.textContent = node.textContent.replace(search, replaceTextNode);
}
}
}
if (searchAttributes) {
if (!element.attributes) {
continue;
}
for (let y = 0; y < element.attributes.length; y++) {
let attr = element.attributes[y];
if (!attributeRegex || attributeRegex.test(attr.name)) {
attr.value = attr.value.replace(search, replaceAttribute);
}
}
}
}
return gcli.lookupFormat("pagemodReplaceResult",
[elements.length, replacedTextNodes,
replacedAttributes]);
}
});
/**
* 'pagemod remove' command
*/
gcli.addCommand({
name: "pagemod remove",
description: gcli.lookup("pagemodRemoveDesc"),
});
/**
* The 'pagemod remove element' command.
*/
gcli.addCommand({
name: "pagemod remove element",
description: gcli.lookup("pagemodRemoveElementDesc"),
params: [
{
name: "search",
type: "string",
description: gcli.lookup("pagemodRemoveElementSearchDesc"),
},
{
name: "root",
type: "node",
description: gcli.lookup("pagemodRemoveElementRootDesc"),
defaultValue: null,
},
{
name: 'stripOnly',
type: 'boolean',
description: gcli.lookup("pagemodRemoveElementStripOnlyDesc"),
},
{
name: 'ifEmptyOnly',
type: 'boolean',
description: gcli.lookup("pagemodRemoveElementIfEmptyOnlyDesc"),
},
],
exec: function(args, context) {
let document = context.environment.contentDocument;
let root = args.root || document;
let elements = Array.prototype.slice.call(root.querySelectorAll(args.search));
let removed = 0;
for (let i = 0; i < elements.length; i++) {
let element = elements[i];
let parentNode = element.parentNode;
if (!parentNode || !element.removeChild) {
continue;
}
if (args.stripOnly) {
while (element.hasChildNodes()) {
parentNode.insertBefore(element.childNodes[0], element);
}
}
if (!args.ifEmptyOnly || !element.hasChildNodes()) {
element.parentNode.removeChild(element);
removed++;
}
}
return gcli.lookupFormat("pagemodRemoveElementResultMatchedAndRemovedElements",
[elements.length, removed]);
}
});
/**
* The 'pagemod remove attribute' command.
*/
gcli.addCommand({
name: "pagemod remove attribute",
description: gcli.lookup("pagemodRemoveAttributeDesc"),
params: [
{
name: "searchAttributes",
type: "string",
description: gcli.lookup("pagemodRemoveAttributeSearchAttributesDesc"),
},
{
name: "searchElements",
type: "string",
description: gcli.lookup("pagemodRemoveAttributeSearchElementsDesc"),
},
{
name: "root",
type: "node",
description: gcli.lookup("pagemodRemoveAttributeRootDesc"),
defaultValue: null,
},
{
name: "ignoreCase",
type: "boolean",
description: gcli.lookup("pagemodRemoveAttributeIgnoreCaseDesc"),
},
],
exec: function(args, context) {
let document = context.environment.contentDocument;
let root = args.root || document;
let regexOptions = args.ignoreCase ? 'ig' : 'g';
let attributeRegex = new RegExp(args.searchAttributes, regexOptions);
let elements = root.querySelectorAll(args.searchElements);
elements = Array.prototype.slice.call(elements);
let removed = 0;
for (let i = 0; i < elements.length; i++) {
let element = elements[i];
if (!element.attributes) {
continue;
}
var attrs = Array.prototype.slice.call(element.attributes);
for (let y = 0; y < attrs.length; y++) {
let attr = attrs[y];
if (attributeRegex.test(attr.name)) {
element.removeAttribute(attr.name);
removed++;
}
}
}
return gcli.lookupFormat("pagemodRemoveAttributeResult",
[elements.length, removed]);
}
});
/**
* Make a given string safe to use in a regular expression.
*
* @param string aString
* The string you want to use in a regex.
* @return string
* The equivalent of |aString| but safe to use in a regex.
*/
function escapeRegex(aString) {
return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}
/**
* 'addon' command.
*/
gcli.addCommand({
name: "addon",
description: gcli.lookup("addonDesc")
});
/**
* 'addon list' command.
*/
gcli.addCommand({
name: "addon list",
description: gcli.lookup("addonListDesc"),
params: [{
name: 'type',
type: {
name: 'selection',
data: ["dictionary", "extension", "locale", "plugin", "theme", "all"]
},
defaultValue: 'all',
description: gcli.lookup("addonListTypeDesc"),
}],
exec: function(aArgs, context) {
function representEnabledAddon(aAddon) {
return "<li><![CDATA[" + aAddon.name + "\u2002" + aAddon.version +
getAddonStatus(aAddon) + "]]></li>";
}
function representDisabledAddon(aAddon) {
return "<li class=\"gcli-addon-disabled\">" +
"<![CDATA[" + aAddon.name + "\u2002" + aAddon.version + aAddon.version +
"]]></li>";
}
function getAddonStatus(aAddon) {
let operations = [];
if (aAddon.pendingOperations & AddonManager.PENDING_ENABLE) {
operations.push("PENDING_ENABLE");
}
if (aAddon.pendingOperations & AddonManager.PENDING_DISABLE) {
operations.push("PENDING_DISABLE");
}
if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
operations.push("PENDING_UNINSTALL");
}
if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL) {
operations.push("PENDING_INSTALL");
}
if (aAddon.pendingOperations & AddonManager.PENDING_UPGRADE) {
operations.push("PENDING_UPGRADE");
}
if (operations.length) {
return " (" + operations.join(", ") + ")";
}
return "";
}
/**
* Compares two addons by their name. Used in sorting.
*/
function compareAddonNames(aNameA, aNameB) {
return String.localeCompare(aNameA.name, aNameB.name);
}
/**
* Resolves the promise which is the scope (this) of this function, filling
* it with an HTML representation of the passed add-ons.
*/
function list(aType, aAddons) {
if (!aAddons.length) {
this.resolve(gcli.lookup("addonNoneOfType"));
}
// Separate the enabled add-ons from the disabled ones.
let enabledAddons = [];
let disabledAddons = [];
aAddons.forEach(function(aAddon) {
if (aAddon.isActive) {
enabledAddons.push(aAddon);
} else {
disabledAddons.push(aAddon);
}
});
let header;
switch(aType) {
case "dictionary":
header = gcli.lookup("addonListDictionaryHeading");
break;
case "extension":
header = gcli.lookup("addonListExtensionHeading");
break;
case "locale":
header = gcli.lookup("addonListLocaleHeading");
break;
case "plugin":
header = gcli.lookup("addonListPluginHeading");
break;
case "theme":
header = gcli.lookup("addonListThemeHeading");
case "all":
header = gcli.lookup("addonListAllHeading");
break;
default:
header = gcli.lookup("addonListUnknownHeading");
}
// Map and sort the add-ons, and create an HTML list.
this.resolve(header +
"<ol>" +
enabledAddons.sort(compareAddonNames).map(representEnabledAddon).join("") +
disabledAddons.sort(compareAddonNames).map(representDisabledAddon).join("") +
"</ol>");
}
// Create the promise that will be resolved when the add-on listing has
// been finished.
let promise = context.createPromise();
let types = aArgs.type == "all" ? null : [aArgs.type];
AddonManager.getAddonsByTypes(types, list.bind(promise, aArgs.type));
return promise;
}
});
// We need a list of addon names for the enable and disable commands. Because
// getting the name list is async we do not add the commands until we have the
// list.
AddonManager.getAllAddons(function addonAsync(aAddons) {
// We listen for installs to keep our addon list up to date. There is no need
// to listen for uninstalls because uninstalled addons are simply disabled
// until restart (to enable undo functionality).
AddonManager.addAddonListener({
onInstalled: function(aAddon) {
addonNameCache.push({
name: representAddon(aAddon).replace(/\s/g, "_"),
value: aAddon.name
});
},
onUninstalled: function(aAddon) {
let name = representAddon(aAddon).replace(/\s/g, "_");
for (let i = 0; i < addonNameCache.length; i++) {
if(addonNameCache[i].name == name) {
addonNameCache.splice(i, 1);
break;
}
}
},
});
/**
* Returns a string that represents the passed add-on.
*/
function representAddon(aAddon) {
let name = aAddon.name + " " + aAddon.version;
return name.trim();
}
let addonNameCache = [];
// The name parameter, used in "addon enable" and "addon disable."
let nameParameter = {
name: "name",
type: {
name: "selection",
lookup: addonNameCache
},
description: gcli.lookup("addonNameDesc")
};
for (let addon of aAddons) {
addonNameCache.push({
name: representAddon(addon).replace(/\s/g, "_"),
value: addon.name
});
}
/**
* 'addon enable' command.
*/
gcli.addCommand({
name: "addon enable",
description: gcli.lookup("addonEnableDesc"),
params: [nameParameter],
exec: function(aArgs, context) {
/**
* Enables the addon in the passed list which has a name that matches
* according to the passed name comparer, and resolves the promise which
* is the scope (this) of this function to display the result of this
* enable attempt.
*/
function enable(aName, addons) {
// Find the add-on.
let addon = null;
addons.some(function(candidate) {
if (candidate.name == aName) {
addon = candidate;
return true;
} else {
return false;
}
});
let name = representAddon(addon);
if (!addon.userDisabled) {
this.resolve("<![CDATA[" +
gcli.lookupFormat("addonAlreadyEnabled", [name]) + "]]>");
} else {
addon.userDisabled = false;
// nl-nl: {$1} is ingeschakeld.
this.resolve("<![CDATA[" +
gcli.lookupFormat("addonEnabled", [name]) + "]]>");
}
}
let promise = context.createPromise();
// List the installed add-ons, enable one when done listing.
AddonManager.getAllAddons(enable.bind(promise, aArgs.name));
return promise;
}
});
/**
* 'addon disable' command.
*/
gcli.addCommand({
name: "addon disable",
description: gcli.lookup("addonDisableDesc"),
params: [nameParameter],
exec: function(aArgs, context) {
/**
* Like enable, but ... you know ... the exact opposite.
*/
function disable(aName, addons) {
// Find the add-on.
let addon = null;
addons.some(function(candidate) {
if (candidate.name == aName) {
addon = candidate;
return true;
} else {
return false;
}
});
let name = representAddon(addon);
if (addon.userDisabled) {
this.resolve("<![CDATA[" +
gcli.lookupFormat("addonAlreadyDisabled", [name]) + "]]>");
} else {
addon.userDisabled = true;
// nl-nl: {$1} is uitgeschakeld.
this.resolve("<![CDATA[" +
gcli.lookupFormat("addonDisabled", [name]) + "]]>");
}
}
let promise = context.createPromise();
// List the installed add-ons, disable one when done listing.
AddonManager.getAllAddons(disable.bind(promise, aArgs.name));
return promise;
}
});
Services.obs.notifyObservers(null, "gcli_addon_commands_ready", null);
});
/* Responsive Mode commands */
(function gcli_cmd_resize_container() {
function gcli_cmd_resize(args, context) {
let browserDoc = context.environment.chromeDocument;
let browserWindow = browserDoc.defaultView;
let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager;
mgr.handleGcliCommand(browserWindow,
browserWindow.gBrowser.selectedTab,
this.name,
args);
}
gcli.addCommand({
name: 'resize',
description: gcli.lookup('resizeModeDesc')
});
gcli.addCommand({
name: 'resize on',
description: gcli.lookup('resizeModeOnDesc'),
manual: gcli.lookup('resizeModeManual'),
exec: gcli_cmd_resize
});
gcli.addCommand({
name: 'resize off',
description: gcli.lookup('resizeModeOffDesc'),
manual: gcli.lookup('resizeModeManual'),
exec: gcli_cmd_resize
});
gcli.addCommand({
name: 'resize toggle',
description: gcli.lookup('resizeModeToggleDesc'),
manual: gcli.lookup('resizeModeManual'),
exec: gcli_cmd_resize
});
gcli.addCommand({
name: 'resize to',
description: gcli.lookup('resizeModeToDesc'),
params: [
{
name: 'width',
type: 'number',
description: gcli.lookup("resizePageArgWidthDesc"),
},
{
name: 'height',
type: 'number',
description: gcli.lookup("resizePageArgHeightDesc"),
},
],
exec: gcli_cmd_resize
});
})();

View File

@ -0,0 +1,204 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let EXPORTED_SYMBOLS = [ ];
Components.utils.import("resource:///modules/devtools/gcli.jsm");
// We should really be using nsICookieManager so we can read more than just the
// key/value of cookies. The difficulty is filtering the cookies that are
// relevant to the current page. See
// https://github.com/firebug/firebug/blob/master/extension/content/firebug/cookies/cookieObserver.js#L123
// For details on how this is done with Firebug
/**
* 'cookie' command
*/
gcli.addCommand({
name: "cookie",
description: gcli.lookup("cookieDesc"),
manual: gcli.lookup("cookieManual")
});
/**
* The template for the 'cookie list' command.
*/
var cookieListHtml = "" +
"<table>" +
" <tr>" +
" <th>" + gcli.lookup("cookieListOutKey") + "</th>" +
" <th>" + gcli.lookup("cookieListOutValue") + "</th>" +
" <th>" + gcli.lookup("cookieListOutActions") + "</th>" +
" </tr>" +
" <tr foreach='cookie in ${cookies}'>" +
" <td>${cookie.key}</td>" +
" <td>${cookie.value}</td>" +
" <td>" +
" <span class='gcli-out-shortcut'" +
" onclick='${onclick}' ondblclick='${ondblclick}'" +
" data-command='cookie set ${cookie.key}'" +
" >" + gcli.lookup("cookieListOutEdit") + "</span>" +
" <span class='gcli-out-shortcut'" +
" onclick='${onclick}' ondblclick='${ondblclick}'" +
" data-command='cookie remove ${cookie.key}'" +
" >" + gcli.lookup("cookieListOutRemove") + "</span>" +
" </td>" +
" </tr>" +
"</table>" +
"";
/**
* 'cookie list' command
*/
gcli.addCommand({
name: "cookie list",
description: gcli.lookup("cookieListDesc"),
manual: gcli.lookup("cookieListManual"),
returnType: "string",
exec: function Command_cookieList(args, context) {
// Parse out an array of { key:..., value:... } objects for each cookie
var doc = context.environment.contentDocument;
var cookies = doc.cookie.split("; ").map(function(cookieStr) {
var equalsPos = cookieStr.indexOf("=");
return {
key: cookieStr.substring(0, equalsPos),
value: cookieStr.substring(equalsPos + 1)
};
});
return context.createView({
html: cookieListHtml,
data: {
cookies: cookies,
onclick: createUpdateHandler(context),
ondblclick: createExecuteHandler(context),
}
});
}
});
/**
* 'cookie remove' command
*/
gcli.addCommand({
name: "cookie remove",
description: gcli.lookup("cookieRemoveDesc"),
manual: gcli.lookup("cookieRemoveManual"),
params: [
{
name: "key",
type: "string",
description: gcli.lookup("cookieRemoveKeyDesc"),
}
],
exec: function Command_cookieRemove(args, context) {
let document = context.environment.contentDocument;
let expDate = new Date();
expDate.setDate(expDate.getDate() - 1);
document.cookie = escape(args.key) + "=; expires=" + expDate.toGMTString();
}
});
/**
* 'cookie set' command
*/
gcli.addCommand({
name: "cookie set",
description: gcli.lookup("cookieSetDesc"),
manual: gcli.lookup("cookieSetManual"),
params: [
{
name: "key",
type: "string",
description: gcli.lookup("cookieSetKeyDesc")
},
{
name: "value",
type: "string",
description: gcli.lookup("cookieSetValueDesc")
},
{
group: gcli.lookup("cookieSetOptionsDesc"),
params: [
{
name: "path",
type: "string",
defaultValue: "/",
description: gcli.lookup("cookieSetPathDesc")
},
{
name: "domain",
type: "string",
defaultValue: null,
description: gcli.lookup("cookieSetDomainDesc")
},
{
name: "secure",
type: "boolean",
description: gcli.lookup("cookieSetSecureDesc")
}
]
}
],
exec: function Command_cookieSet(args, context) {
let document = context.environment.contentDocument;
document.cookie = escape(args.key) + "=" + escape(args.value) +
(args.domain ? "; domain=" + args.domain : "") +
(args.path ? "; path=" + args.path : "") +
(args.secure ? "; secure" : "");
}
});
/**
* Helper to find the 'data-command' attribute and call some action on it.
* @see |updateCommand()| and |executeCommand()|
*/
function withCommand(element, action) {
var command = element.getAttribute("data-command");
if (!command) {
command = element.querySelector("*[data-command]")
.getAttribute("data-command");
}
if (command) {
action(command);
}
else {
console.warn("Missing data-command for " + util.findCssSelector(element));
}
}
/**
* Create a handler to update the requisition to contain the text held in the
* first matching data-command attribute under the currentTarget of the event.
* @param context Either a Requisition or an ExecutionContext or another object
* that contains an |update()| function that follows a similar contract.
*/
function createUpdateHandler(context) {
return function(ev) {
withCommand(ev.currentTarget, function(command) {
context.update(command);
});
}
}
/**
* Create a handler to execute the text held in the data-command attribute
* under the currentTarget of the event.
* @param context Either a Requisition or an ExecutionContext or another object
* that contains an |update()| function that follows a similar contract.
*/
function createExecuteHandler(context) {
return function(ev) {
withCommand(ev.currentTarget, function(command) {
context.exec({
visible: true,
typed: command
});
});
}
}

View File

@ -12,12 +12,17 @@ relativesrcdir = browser/devtools/commandline/test
include $(DEPTH)/config/autoconf.mk include $(DEPTH)/config/autoconf.mk
MOCHITEST_BROWSER_FILES = \ MOCHITEST_BROWSER_FILES = \
browser_gcli_addon.js \
browser_gcli_break.js \ browser_gcli_break.js \
browser_gcli_commands.js \ browser_gcli_commands.js \
browser_gcli_cookie.js \
browser_gcli_edit.js \ browser_gcli_edit.js \
browser_gcli_inspect.js \ browser_gcli_inspect.js \
browser_gcli_integrate.js \ browser_gcli_integrate.js \
browser_gcli_pagemod_export.js \
browser_gcli_pref.js \ browser_gcli_pref.js \
browser_gcli_responsivemode.js \
browser_gcli_restart.js \
browser_gcli_settings.js \ browser_gcli_settings.js \
browser_gcli_web.js \ browser_gcli_web.js \
head.js \ head.js \
@ -33,3 +38,4 @@ MOCHITEST_BROWSER_FILES += \
$(NULL) $(NULL)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,43 @@
function test() {
DeveloperToolbarTest.test("about:blank", function GAT_test() {
function GAT_ready() {
Services.obs.removeObserver(GAT_ready, "gcli_addon_commands_ready", false);
DeveloperToolbarTest.checkInputStatus({
typed: "addon list dictionary",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list extension",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list locale",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list plugin",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list theme",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon list all",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon disable Test_Plug-in_1.0.0.0",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "addon enable Test_Plug-in_1.0.0.0",
status: "VALID"
});
DeveloperToolbarTest.exec({ completed: false });
finish();
}
Services.obs.addObserver(GAT_ready, "gcli_addon_commands_ready", false);
});
}

View File

@ -0,0 +1,68 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that the cookie commands works as they should
const TEST_URI = "data:text/html;charset=utf-8,gcli-cookie";
function test() {
DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
testCookieCommands();
finish();
});
}
function testCookieCommands() {
DeveloperToolbarTest.checkInputStatus({
typed: "cook",
directTabText: "ie",
status: "ERROR"
});
DeveloperToolbarTest.checkInputStatus({
typed: "cookie l",
directTabText: "ist",
status: "ERROR"
});
DeveloperToolbarTest.checkInputStatus({
typed: "cookie list",
status: "VALID",
emptyParameters: [ ]
});
DeveloperToolbarTest.checkInputStatus({
typed: "cookie remove",
status: "ERROR",
emptyParameters: [ " <key>" ]
});
DeveloperToolbarTest.checkInputStatus({
typed: "cookie set",
status: "ERROR",
emptyParameters: [ " <key>", " <value>" ],
});
DeveloperToolbarTest.exec({
typed: "cookie set fruit banana",
args: {
key: "fruit",
value: "banana",
path: "/",
domain: null,
secure: false
},
blankOutput: true,
});
DeveloperToolbarTest.exec({
typed: "cookie list",
outputMatch: /Key/
});
DeveloperToolbarTest.exec({
typed: "cookie remove fruit",
args: { key: "fruit" },
blankOutput: true,
});
}

View File

@ -0,0 +1,272 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that the inspect command works as it should
const TEST_URI = "http://example.com/browser/browser/devtools/commandline/test/browser_gcli_inspect.html";
function test() {
let initialHtml = "";
DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
initialHtml = content.document.documentElement.innerHTML;
testExportHtml();
testPageModReplace();
testPageModRemoveElement();
testPageModRemoveAttribute();
finish();
});
function testExportHtml() {
DeveloperToolbarTest.checkInputStatus({
typed: "export html",
status: "VALID"
});
let oldOpen = content.open;
let openURL = "";
content.open = function(aUrl) {
openURL = aUrl;
};
DeveloperToolbarTest.exec({ blankOutput: true });
openURL = decodeURIComponent(openURL);
isnot(openURL.indexOf('<html lang="en">'), -1, "export html works: <html>");
isnot(openURL.indexOf("<title>GCLI"), -1, "export html works: <title>");
isnot(openURL.indexOf('<p id="someid">#'), -1, "export html works: <p>");
content.open = oldOpen;
}
function getContent() {
return content.document.documentElement.innerHTML;
}
function resetContent() {
content.document.documentElement.innerHTML = initialHtml;
}
function testPageModReplace() {
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod replace",
emptyParameters: [" <search>", " <replace>", " [ignoreCase]",
" [selector]", " [root]", " [attrOnly]",
" [contentOnly]", " [attributes]"],
status: "ERROR"
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod replace some foo",
emptyParameters: [" [ignoreCase]", " [selector]", " [root]",
" [attrOnly]", " [contentOnly]", " [attributes]"],
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod replace some foo true",
emptyParameters: [" [selector]", " [root]", " [attrOnly]",
" [contentOnly]", " [attributes]"],
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod replace some foo true --attrOnly",
emptyParameters: [" [selector]", " [root]", " [contentOnly]",
" [attributes]"],
status: "VALID"
});
DeveloperToolbarTest.exec({
typed: "pagemod replace sOme foOBar",
outputMatch: /^[^:]+: 13\. [^:]+: 0\. [^:]+: 0\.\s*$/
});
is(getContent(), initialHtml, "no change in the page");
DeveloperToolbarTest.exec({
typed: "pagemod replace sOme foOBar true",
outputMatch: /^[^:]+: 13\. [^:]+: 2\. [^:]+: 2\.\s*$/
});
isnot(getContent().indexOf('<p class="foOBarclass">.foOBarclass'), -1,
".someclass changed to .foOBarclass");
isnot(getContent().indexOf('<p id="foOBarid">#foOBarid'), -1,
"#someid changed to #foOBarid");
resetContent();
DeveloperToolbarTest.exec({
typed: "pagemod replace some foobar --contentOnly",
outputMatch: /^[^:]+: 13\. [^:]+: 2\. [^:]+: 0\.\s*$/
});
isnot(getContent().indexOf('<p class="someclass">.foobarclass'), -1,
".someclass changed to .foobarclass (content only)");
isnot(getContent().indexOf('<p id="someid">#foobarid'), -1,
"#someid changed to #foobarid (content only)");
resetContent();
DeveloperToolbarTest.exec({
typed: "pagemod replace some foobar --attrOnly",
outputMatch: /^[^:]+: 13\. [^:]+: 0\. [^:]+: 2\.\s*$/
});
isnot(getContent().indexOf('<p class="foobarclass">.someclass'), -1,
".someclass changed to .foobarclass (attr only)");
isnot(getContent().indexOf('<p id="foobarid">#someid'), -1,
"#someid changed to #foobarid (attr only)");
resetContent();
DeveloperToolbarTest.exec({
typed: "pagemod replace some foobar --root head",
outputMatch: /^[^:]+: 2\. [^:]+: 0\. [^:]+: 0\.\s*$/
});
is(getContent(), initialHtml, "nothing changed");
DeveloperToolbarTest.exec({
typed: "pagemod replace some foobar --selector .someclass,div,span",
outputMatch: /^[^:]+: 4\. [^:]+: 1\. [^:]+: 1\.\s*$/
});
isnot(getContent().indexOf('<p class="foobarclass">.foobarclass'), -1,
".someclass changed to .foobarclass");
isnot(getContent().indexOf('<p id="someid">#someid'), -1,
"#someid did not change");
resetContent();
}
function testPageModRemoveElement() {
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove",
status: "ERROR"
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove element",
emptyParameters: [" <search>", " [root]", " [stripOnly]", " [ifEmptyOnly]"],
status: "ERROR"
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove element foo",
emptyParameters: [" [root]", " [stripOnly]", " [ifEmptyOnly]"],
status: "VALID"
});
DeveloperToolbarTest.exec({
typed: "pagemod remove element p",
outputMatch: /^[^:]+: 3\. [^:]+: 3\.\s*$/
});
is(getContent().indexOf('<p class="someclass">'), -1, "p.someclass removed");
is(getContent().indexOf('<p id="someid">'), -1, "p#someid removed");
is(getContent().indexOf("<p><strong>"), -1, "<p> wrapping <strong> removed");
isnot(getContent().indexOf("<span>"), -1, "<span> not removed");
resetContent();
DeveloperToolbarTest.exec({
typed: "pagemod remove element p head",
outputMatch: /^[^:]+: 0\. [^:]+: 0\.\s*$/
});
is(getContent(), initialHtml, "nothing changed in the page");
DeveloperToolbarTest.exec({
typed: "pagemod remove element p --ifEmptyOnly",
outputMatch: /^[^:]+: 3\. [^:]+: 0\.\s*$/
});
is(getContent(), initialHtml, "nothing changed in the page");
DeveloperToolbarTest.exec({
typed: "pagemod remove element meta,title --ifEmptyOnly",
outputMatch: /^[^:]+: 2\. [^:]+: 1\.\s*$/
});
is(getContent().indexOf("<meta charset="), -1, "<meta> removed");
isnot(getContent().indexOf("<title>"), -1, "<title> not removed");
resetContent();
DeveloperToolbarTest.exec({
typed: "pagemod remove element p --stripOnly",
outputMatch: /^[^:]+: 3\. [^:]+: 3\.\s*$/
});
is(getContent().indexOf('<p class="someclass">'), -1, "p.someclass removed");
is(getContent().indexOf('<p id="someid">'), -1, "p#someid removed");
is(getContent().indexOf("<p><strong>"), -1, "<p> wrapping <strong> removed");
isnot(getContent().indexOf(".someclass"), -1, ".someclass still exists");
isnot(getContent().indexOf("#someid"), -1, "#someid still exists");
isnot(getContent().indexOf("<strong>p"), -1, "<strong> still exists");
resetContent();
}
function testPageModRemoveAttribute() {
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove attribute",
emptyParameters: [" <searchAttributes>", " <searchElements>", " [root]", " [ignoreCase]"],
status: "ERROR"
});
DeveloperToolbarTest.checkInputStatus({
typed: "pagemod remove attribute foo bar",
emptyParameters: [" [root]", " [ignoreCase]"],
status: "VALID"
});
DeveloperToolbarTest.exec({
typed: "pagemod remove attribute foo bar",
outputMatch: /^[^:]+: 0\. [^:]+: 0\.\s*$/
});
is(getContent(), initialHtml, "nothing changed in the page");
DeveloperToolbarTest.exec({
typed: "pagemod remove attribute foo p",
outputMatch: /^[^:]+: 3\. [^:]+: 0\.\s*$/
});
is(getContent(), initialHtml, "nothing changed in the page");
DeveloperToolbarTest.exec({
typed: "pagemod remove attribute id p,span",
outputMatch: /^[^:]+: 5\. [^:]+: 1\.\s*$/
});
is(getContent().indexOf('<p id="someid">#someid'), -1,
"p#someid attribute removed");
isnot(getContent().indexOf("<p>#someid"), -1,
"p with someid content still exists");
resetContent();
DeveloperToolbarTest.exec({
typed: "pagemod remove attribute Class p",
outputMatch: /^[^:]+: 3\. [^:]+: 0\.\s*$/
});
is(getContent(), initialHtml, "nothing changed in the page");
DeveloperToolbarTest.exec({
typed: "pagemod remove attribute Class p --ignoreCase",
outputMatch: /^[^:]+: 3\. [^:]+: 1\.\s*$/
});
is(getContent().indexOf('<p class="someclass">.someclass'), -1,
"p.someclass attribute removed");
isnot(getContent().indexOf("<p>.someclass"), -1,
"p with someclass content still exists");
resetContent();
}
}

View File

@ -0,0 +1,58 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
DeveloperToolbarTest.test("about:blank", function GAT_test() {
DeveloperToolbarTest.checkInputStatus({
typed: "resize toggle",
status: "VALID"
});
DeveloperToolbarTest.exec();
ok(isOpen(), "responsive mode is open");
DeveloperToolbarTest.checkInputStatus({
typed: "resize toggle",
status: "VALID"
});
DeveloperToolbarTest.exec();
ok(isClosed(), "responsive mode is closed");
DeveloperToolbarTest.checkInputStatus({
typed: "resize on",
status: "VALID"
});
DeveloperToolbarTest.exec();
ok(isOpen(), "responsive mode is open");
DeveloperToolbarTest.checkInputStatus({
typed: "resize off",
status: "VALID"
});
DeveloperToolbarTest.exec();
ok(isClosed(), "responsive mode is closed");
DeveloperToolbarTest.checkInputStatus({
typed: "resize to 400 400",
status: "VALID"
});
DeveloperToolbarTest.exec();
ok(isOpen(), "responsive mode is open");
DeveloperToolbarTest.checkInputStatus({
typed: "resize off",
status: "VALID"
});
DeveloperToolbarTest.exec();
ok(isClosed(), "responsive mode is closed");
executeSoon(finish);
});
function isOpen() {
return !!gBrowser.selectedTab.__responsiveUI;
}
function isClosed() {
return !isOpen();
}
}

View File

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that restart command works properly (input wise)
const TEST_URI = "data:text/html;charset=utf-8,gcli-command-restart";
function test() {
DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
testRestart();
finish();
});
}
function testRestart() {
DeveloperToolbarTest.checkInputStatus({
typed: "restart",
markup: "VVVVVVV",
status: "VALID",
emptyParameters: [ " [nocache]" ],
});
DeveloperToolbarTest.checkInputStatus({
typed: "restart ",
markup: "VVVVVVVV",
status: "VALID",
directTabText: "false"
});
DeveloperToolbarTest.checkInputStatus({
typed: "restart t",
markup: "VVVVVVVVI",
status: "ERROR",
directTabText: "rue"
});
DeveloperToolbarTest.checkInputStatus({
typed: "restart --nocache",
markup: "VVVVVVVVVVVVVVVVV",
status: "VALID"
});
DeveloperToolbarTest.checkInputStatus({
typed: "restart --noca",
markup: "VVVVVVVVEEEEEE",
status: "ERROR",
});
}

View File

@ -34,6 +34,38 @@ let ResponsiveUIManager = {
aTab.__responsiveUI = new ResponsiveUI(aWindow, aTab); aTab.__responsiveUI = new ResponsiveUI(aWindow, aTab);
} }
}, },
/**
* Handle gcli commands.
*
* @param aWindow the browser window.
* @param aTab the tab targeted.
* @param aCommand the command name.
* @param aArgs command arguments.
*/
handleGcliCommand: function(aWindow, aTab, aCommand, aArgs) {
switch (aCommand) {
case "resize to":
if (!aTab.__responsiveUI) {
aTab.__responsiveUI = new ResponsiveUI(aWindow, aTab);
}
aTab.__responsiveUI.setSize(aArgs.width, aArgs.height);
break;
case "resize on":
if (!aTab.__responsiveUI) {
aTab.__responsiveUI = new ResponsiveUI(aWindow, aTab);
}
break;
case "resize off":
if (aTab.__responsiveUI) {
aTab.__responsiveUI.close();
}
break;
case "resize toggle":
this.toggle(aWindow, aTab);
default:
}
},
} }
let presets = [ let presets = [
@ -376,8 +408,8 @@ ResponsiveUI.prototype = {
* @param aHeight height of the browser. * @param aHeight height of the browser.
*/ */
setSize: function RUI_setSize(aWidth, aHeight) { setSize: function RUI_setSize(aWidth, aHeight) {
this.currentWidth = aWidth; this.currentWidth = Math.min(Math.max(aWidth, MIN_WIDTH), MAX_WIDTH);
this.currentHeight = aHeight; this.currentHeight = Math.min(Math.max(aHeight, MIN_HEIGHT), MAX_WIDTH);
// We resize the containing stack. // We resize the containing stack.
let style = "max-width: %width;" + let style = "max-width: %width;" +

View File

@ -80,6 +80,24 @@ screenshotFullPageDesc=Entire webpage? (true/false)
# asks for help on what it does. # asks for help on what it does.
screenshotFullPageManual=True if the screenshot should also include parts of the webpage which are outside the current scrolled bounds. screenshotFullPageManual=True if the screenshot should also include parts of the webpage which are outside the current scrolled bounds.
# LOCALIZATION NOTE (restartFirefoxDesc) A very short description of the
# 'restart' command. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
restartFirefoxDesc=Restart Firefox
# LOCALIZATION NOTE (restartFirefoxNocacheDesc) A very short string to
# describe the 'nocache' parameter to the 'restart' command, which is
# displayed in a dialog when the user is using this command.
restartFirefoxNocacheDesc=Disables loading content from cache upon restart
# LOCALIZATION NOTE (restartFirefoxRequestCancelled) A string dispalyed to the
# user when a scheduled restart has been aborted by the user.
restartFirefoxRequestCancelled=Restart request cancelled by user.
# LOCALIZATION NOTE (restartFirefoxRestarting) A string dispalyed to the
# user when a restart has been initiated without a delay.
restartFirefoxRestarting=Restarting Firefox...
# LOCALIZATION NOTE (inspectDesc) A very short description of the 'inspect' # LOCALIZATION NOTE (inspectDesc) A very short description of the 'inspect'
# command. See inspectManual for a fuller description of what it does. This # command. See inspectManual for a fuller description of what it does. This
# string is designed to be shown in a menu alongside the command name, which # string is designed to be shown in a menu alongside the command name, which
@ -333,6 +351,50 @@ editResourceDesc=URL to edit
# when the user is using this command. # when the user is using this command.
editLineToJumpToDesc=Line to jump to editLineToJumpToDesc=Line to jump to
# LOCALIZATION NOTE (resizePageDesc) A very short string to describe the
# 'resizepage' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizePageDesc=Resize the page
# LOCALIZATION NOTE (resizePageArgWidthDesc) A very short string to describe the
# 'width' parameter to the 'resizepage' command, which is displayed in a dialog
# when the user is using this command.
resizePageArgWidthDesc=Width in pixels
# LOCALIZATION NOTE (resizePageArgWidthDesc) A very short string to describe the
# 'height' parameter to the 'resizepage' command, which is displayed in a dialog
# when the user is using this command.
resizePageArgHeightDesc=Height in pixels
# LOCALIZATION NOTE (resizeModeOnDesc) A very short string to describe the
# 'resizeon ' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeOnDesc=Enter Responsive Design Mode
# LOCALIZATION NOTE (resizeModeOffDesc) A very short string to describe the
# 'resize off' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeOffDesc=Exit Responsive Design Mode
# LOCALIZATION NOTE (resizeModeToggleDesc) A very short string to describe the
# 'resize toggle' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeToggleDesc=Toggle Responsive Design Mode
# LOCALIZATION NOTE (resizeModeToDesc) A very short string to describe the
# 'resize to' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeToDesc=Alter page size
# LOCALIZATION NOTE (resizeModeDesc) A very short string to describe the
# 'resize' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
resizeModeDesc=Control Responsive Design Mode
# LOCALIZATION NOTE (resizeModeManual) A fuller description of the 'resize'
# command, displayed when the user asks for help on what it does.
resizeModeManual=Responsive websites respond to their environment, so they look good on a mobile display, a cinema display and everything in-between. Responsive Design Mode allows you to easily test a variety of page sizes in Firefox without needing to resize your whole browser.
# LOCALIZATION NOTE (cmdDesc) A very short description of the 'cmd' # LOCALIZATION NOTE (cmdDesc) A very short description of the 'cmd'
# command. This string is designed to be shown in a menu alongside the command # command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible. # name, which is why it should be as short as possible.
@ -342,3 +404,290 @@ cmdDesc=Manipulate the commands
# command. This string is designed to be shown in a menu alongside the command # command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible. # name, which is why it should be as short as possible.
cmdRefreshDesc=Re-read mozcmd directory cmdRefreshDesc=Re-read mozcmd directory
# LOCALIZATION NOTE (addonDesc) A very short description of the 'addon'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
addonDesc=Manipulate add-ons
# LOCALIZATION NOTE (addonListDesc) A very short description of the 'addon list'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
addonListDesc=List installed add-ons
# LOCALIZATION NOTE (addonListTypeDesc) A very short description of the
# 'addon list <type>' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
addonListTypeDesc=Select an addon type
# LOCALIZATION NOTE (addonListDictionaryHeading, addonListExtensionHeading,
# addonListLocaleHeading, addonListPluginHeading, addonListThemeHeading,
# addonListUnknownHeading) Used in the output of the 'addon list' command as the
# first line of output.
addonListDictionaryHeading=The following dictionaries are currently installed:
addonListExtensionHeading=The following extensions are currently installed:
addonListLocaleHeading=The following locales are currently installed:
addonListPluginHeading=The following plugins are currently installed:
addonListThemeHeading=The following themes are currently installed:
addonListAllHeading=The following addons are currently installed:
addonListUnknownHeading=The following addons of the selected type are currently installed:
# LOCALIZATION NOTE (addonNameDesc) A very short description of the
# name parameter of numerous addon commands. This string is designed to be shown
# in a menu alongside the command name, which is why it should be as short as
# possible.
addonNameDesc=The name of the add-on
# LOCALIZATION NOTE (addonNoneOfType) Used in the output of the 'addon list'
# command when a search for addons of a particular type were not found.
addonNoneOfType=There are no addons of that type installed.
# LOCALIZATION NOTE (addonEnableDesc) A very short description of the
# 'addon enable <type>' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
addonEnableDesc=Enable the specified add-on
# LOCALIZATION NOTE (addonAlreadyEnabled) Used in the output of the
# 'addon enable' command when an attempt is made to enable an addon is already
# enabled.
addonAlreadyEnabled=%S is already enabled.
# LOCALIZATION NOTE (addonEnabled) Used in the output of the 'addon enable'
# command when an addon is enabled.
addonEnabled=%S enabled.
# LOCALIZATION NOTE (addonDisableDesc) A very short description of the
# 'addon disable <type>' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
addonDisableDesc=Disable the specified add-on
# LOCALIZATION NOTE (addonAlreadyDisabled) Used in the output of the
# 'addon disable' command when an attempt is made to disable an addon is already
# disabled.
addonAlreadyDisabled=%S is already disabled.
# LOCALIZATION NOTE (addonDisabled) Used in the output of the 'addon disable'
# command when an addon is disabled.
addonDisabled=%S disabled.
# LOCALIZATION NOTE (exportDesc) A very short description of the 'export'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
exportDesc=Export resources
# LOCALIZATION NOTE (exportHtmlDesc) A very short description of the 'export
# html' command. This string is designed to be shown in a menu alongside the
# command name, which is why it should be as short as possible.
exportHtmlDesc=Export HTML from page
# LOCALIZATION NOTE (pagemodDesc) A very short description of the 'pagemod'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible.
pagemodDesc=Make page changes
# LOCALIZATION NOTE (pagemodReplaceDesc) A very short description of the
# 'pagemod replace' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
pagemodReplaceDesc=Search and replace in page elements
# LOCALIZATION NOTE (pagemodReplaceSearchDesc) A very short string to describe
# the 'search' parameter to the 'pagemod replace' command, which is displayed in
# a dialog when the user is using this command.
pagemodReplaceSearchDesc=What to search for
# LOCALIZATION NOTE (pagemodReplaceReplaceDesc) A very short string to describe
# the 'replace' parameter to the 'pagemod replace' command, which is displayed in
# a dialog when the user is using this command.
pagemodReplaceReplaceDesc=Replacement string
# LOCALIZATION NOTE (pagemodReplaceIgnoreCaseDesc) A very short string to
# describe the 'ignoreCase' parameter to the 'pagemod replace' command, which is
# displayed in a dialog when the user is using this command.
pagemodReplaceIgnoreCaseDesc=Perform case-insensitive search
# LOCALIZATION NOTE (pagemodReplaceRootDesc) A very short string to describe the
# 'root' parameter to the 'pagemod replace' command, which is displayed in
# a dialog when the user is using this command.
pagemodReplaceRootDesc=CSS selector to root of search
# LOCALIZATION NOTE (pagemodReplaceSelectorDesc) A very short string to describe
# the 'selector' parameter to the 'pagemod replace' command, which is displayed
# in a dialog when the user is using this command.
pagemodReplaceSelectorDesc=CSS selector to match in search
# LOCALIZATION NOTE (pagemodReplaceAttributesDesc) A very short string to
# describe the 'attributes' parameter to the 'pagemod replace' command, which is
# displayed in a dialog when the user is using this command.
pagemodReplaceAttributesDesc=Attribute match regexp
# LOCALIZATION NOTE (pagemodReplaceAttrOnlyDesc) A very short string to describe
# the 'attrOnly' parameter to the 'pagemod replace' command, which is displayed
# in a dialog when the user is using this command.
pagemodReplaceAttrOnlyDesc=Restrict search to attributes
# LOCALIZATION NOTE (pagemodReplaceContentOnlyDesc) A very short string to
# describe the 'contentOnly' parameter to the 'pagemod replace' command, which
# is displayed in a dialog when the user is using this command.
pagemodReplaceContentOnlyDesc=Restrict search to text nodes
# LOCALIZATION NOTE (pagemodReplaceResultMatchedElements) A string displayed as
# the result of the 'pagemod replace' command.
pagemodReplaceResult=Elements matched by selector: %1$S. Replaces in text nodes: %2$S. Replaces in attributes: %3$S.
# LOCALIZATION NOTE (pagemodRemoveDesc) A very short description of the
# 'pagemod remove' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
pagemodRemoveDesc=Remove elements and attributes from page
# LOCALIZATION NOTE (pagemodRemoveElementDesc) A very short description of the
# 'pagemod remove element' command. This string is designed to be shown in
# a menu alongside the command name, which is why it should be as short as
# possible.
pagemodRemoveElementDesc=Remove elements from page
# LOCALIZATION NOTE (pagemodRemoveElementSearchDesc) A very short string to
# describe the 'search' parameter to the 'pagemod remove element' command, which
# is displayed in a dialog when the user is using this command.
pagemodRemoveElementSearchDesc=CSS selector specifying elements to remove
# LOCALIZATION NOTE (pagemodRemoveElementRootDesc) A very short string to
# describe the 'root' parameter to the 'pagemod remove element' command, which
# is displayed in a dialog when the user is using this command.
pagemodRemoveElementRootDesc=CSS selector specifying root of search
# LOCALIZATION NOTE (pagemodRemoveElementStripOnlyDesc) A very short string to
# describe the 'stripOnly' parameter to the 'pagemod remove element' command,
# which is displayed in a dialog when the user is using this command.
pagemodRemoveElementStripOnlyDesc=Remove element, but leave content
# LOCALIZATION NOTE (pagemodRemoveElementIfEmptyOnlyDesc) A very short string to
# describe the 'ifEmptyOnly' parameter to the 'pagemod remove element' command,
# which is displayed in a dialog when the user is using this command.
pagemodRemoveElementIfEmptyOnlyDesc=Remove only empty elements
# LOCALIZATION NOTE (pagemodRemoveElementResultMatchedAndRemovedElements)
# A string displayed as the result of the 'pagemod remove element' command.
pagemodRemoveElementResultMatchedAndRemovedElements=Elements matched by selector: %1$S. Elements removed: %2$S.
# LOCALIZATION NOTE (pagemodRemoveAttributeDesc) A very short description of the
# 'pagemod remove attribute' command. This string is designed to be shown in
# a menu alongside the command name, which is why it should be as short as
# possible.
pagemodRemoveAttributeDesc=Remove matching atributes
# LOCALIZATION NOTE (pagemodRemoveAttributeSearchAttributesDesc) A very short
# string to describe the 'searchAttributes' parameter to the 'pagemod remove
# attribute' command, which is displayed in a dialog when the user is using this
# command.
pagemodRemoveAttributeSearchAttributesDesc=Regexp specifying attributes to remove
# LOCALIZATION NOTE (pagemodRemoveAttributeSearchElementsDesc) A very short
# string to describe the 'searchElements' parameter to the 'pagemod remove
# attribute' command, which is displayed in a dialog when the user is using this
# command.
pagemodRemoveAttributeSearchElementsDesc=CSS selector of elements to include
# LOCALIZATION NOTE (pagemodRemoveAttributeRootDesc) A very short string to
# describe the 'root' parameter to the 'pagemod remove attribute' command, which
# is displayed in a dialog when the user is using this command.
pagemodRemoveAttributeRootDesc=CSS selector of root of search
# LOCALIZATION NOTE (pagemodRemoveAttributeIgnoreCaseDesc) A very short string
# to describe the 'ignoreCase' parameter to the 'pagemod remove attribute'
# command, which is displayed in a dialog when the user is using this command.
pagemodRemoveAttributeIgnoreCaseDesc=Perform case-insensitive search
# LOCALIZATION NOTE (pagemodRemoveAttributeResult) A string displayed as the
# result of the 'pagemod remove attribute' command.
pagemodRemoveAttributeResult=Elements matched by selector: %1$S. Attributes removed: %2$S.
# LOCALIZATION NOTE (cookieDesc) A very short description of the 'cookie'
# command. See cookieManual for a fuller description of what it does. This
# string is designed to be shown in a menu alongside the command name, which
# is why it should be as short as possible.
cookieDesc=Display and alter cookies
# LOCALIZATION NOTE (cookieManual) A fuller description of the 'cookie'
# command, displayed when the user asks for help on what it does.
cookieManual=Commands to list, create, delete and alter cookies for the current domain.
# LOCALIZATION NOTE (cookieListDesc) A very short description of the
# 'cookie list' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
cookieListDesc=Display cookies
# LOCALIZATION NOTE (cookieListManual) A fuller description of the 'cookie list'
# command, displayed when the user asks for help on what it does.
cookieListManual=Display a list of the cookies relevant to the current page.
# LOCALIZATION NOTE (cookieListOutKey) A heading used in the output from the
# 'cookie list' command above a list of cookie keys
cookieListOutKey=Key
# LOCALIZATION NOTE (cookieListOutValue) A heading used in the output from the
# 'cookie list' command above a list of cookie values
cookieListOutValue=Value
# LOCALIZATION NOTE (cookieListOutActions) A heading used in the output from the
# 'cookie list' command above a list of actions to take on cookies
cookieListOutActions=Actions
# LOCALIZATION NOTE (cookieListOutEdit) A title used in the output from the
# 'cookie list' command on a button which can be used to edit cookie values
cookieListOutEdit=Edit
# LOCALIZATION NOTE (cookieListOutRemove) A title used in the output from the
# 'cookie list' command on a button which can be used to remove cookies
cookieListOutRemove=Remove
# LOCALIZATION NOTE (cookieRemoveDesc) A very short description of the
# 'cookie remove' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
cookieRemoveDesc=Remove a cookie
# LOCALIZATION NOTE (cookieRemoveManual) A fuller description of the 'cookie remove'
# command, displayed when the user asks for help on what it does.
cookieRemoveManual=Remove a cookie, given its key
# LOCALIZATION NOTE (cookieRemoveKeyDesc) A very short string to describe the
# 'key' parameter to the 'cookie remove' command, which is displayed in a dialog
# when the user is using this command.
cookieRemoveKeyDesc=The key of the cookie to remove
# LOCALIZATION NOTE (cookieSetDesc) A very short description of the
# 'cookie set' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
cookieSetDesc=Set a cookie
# LOCALIZATION NOTE (cookieSetManual) A fuller description of the 'cookie set'
# command, displayed when the user asks for help on what it does.
cookieSetManual=Set a cookie by specifying a key name, it's value and optionally one or more of the following attributes: expires (max-age in seconds or the expires date in GMTString format), path, domain, secure
# LOCALIZATION NOTE (cookieSetKeyDesc) A very short string to describe the
# 'key' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetKeyDesc=The key of the cookie to set
# LOCALIZATION NOTE (cookieSetValueDesc) A very short string to describe the
# 'value' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetValueDesc=The value of the cookie to set
# LOCALIZATION NOTE (cookieSetOptionsDesc) The title of a set of options to
# the 'cookie set' command, displayed as a heading to the list of option.
cookieSetOptionsDesc=Options
# LOCALIZATION NOTE (cookieSetPathDesc) A very short string to describe the
# 'path' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetPathDesc=The path of the cookie to set
# LOCALIZATION NOTE (cookieSetDomainDesc) A very short string to describe the
# 'domain' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetDomainDesc=The domain of the cookie to set
# LOCALIZATION NOTE (cookieSetSecureDesc) A very short string to describe the
# 'secure' parameter to the 'cookie set' command, which is displayed in a dialog
# when the user is using this command.
cookieSetSecureDesc=Only transmitted over https

View File

@ -129,3 +129,8 @@
text-align: right; text-align: right;
-moz-padding-end: 8px; -moz-padding-end: 8px;
} }
.gcli-addon-disabled {
opacity: 0.6;
text-decoration: line-through;
}

View File

@ -131,3 +131,8 @@
text-align: right; text-align: right;
-moz-padding-end: 8px; -moz-padding-end: 8px;
} }
.gcli-addon-disabled {
opacity: 0.6;
text-decoration: line-through;
}

View File

@ -129,3 +129,8 @@
text-align: right; text-align: right;
-moz-padding-end: 8px; -moz-padding-end: 8px;
} }
.gcli-addon-disabled {
opacity: 0.6;
text-decoration: line-through;
}

View File

@ -69,8 +69,8 @@ using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
using namespace mozilla::widget; using namespace mozilla::widget;
//#define DEBUG_FOCUS 1 #define DEBUG_FOCUS 1
//#define DEBUG_FOCUS_NAVIGATION 1 #define DEBUG_FOCUS_NAVIGATION 1
#define PRINTTAGF(format, content) \ #define PRINTTAGF(format, content) \
{ \ { \
nsAutoString tag(NS_LITERAL_STRING("(none)")); \ nsAutoString tag(NS_LITERAL_STRING("(none)")); \