Skip to content

Commit

Permalink
Bug 984365 - Split out 'pagemod' and 'export' commands; r=mratcliffe
Browse files Browse the repository at this point in the history
Changes:
* Add a destination option to 'export html' that allows us to send the
  exported html to the command-line or clipboard. This primarily helps testing
  but is also a useful feature.
* Be consistent in using " rather than ' where possible
* Convert from JSM style to JS style, which means:
  * Adjust require paths to fit with SDK loader conventions
  * Use exports.items rather than EXPORTED_SYMBOLS
  • Loading branch information
joewalker committed Apr 3, 2014
1 parent 3e46ec6 commit d63051e
Show file tree
Hide file tree
Showing 4 changed files with 295 additions and 287 deletions.
281 changes: 0 additions & 281 deletions browser/devtools/commandline/BuiltinCommands.jsm
Expand Up @@ -1094,32 +1094,6 @@ this.items = [];
});
}(this));

/* CmdExport --------------------------------------------------------------- */

(function(module) {
/**
* 'export' command
*/
this.items.push({
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.
*/
this.items.push({
name: "export html",
description: gcli.lookup("exportHtmlDesc"),
exec: function(args, context) {
let html = context.environment.document.documentElement.outerHTML;
let url = 'data:text/plain;charset=utf8,' + encodeURIComponent(html);
context.environment.window.open(url);
}
});
}(this));

/* CmdJsb ------------------------------------------------------------------ */

(function(module) {
Expand Down Expand Up @@ -1253,258 +1227,3 @@ this.items = [];
});
}(this));

/* CmdPagemod -------------------------------------------------------------- */

(function(module) {
/**
* 'pagemod' command
*/
this.items.push({
name: "pagemod",
description: gcli.lookup("pagemodDesc"),
});

/**
* The 'pagemod replace' command. This command allows the user to search and
* replace within text nodes and attributes.
*/
this.items.push({
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 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 || context.environment.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
*/
this.items.push({
name: "pagemod remove",
description: gcli.lookup("pagemodRemoveDesc"),
});


/**
* The 'pagemod remove element' command.
*/
this.items.push({
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 root = args.root || context.environment.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.
*/
this.items.push({
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 root = args.root || context.environment.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, "\\$&");
}
}(this));

1 change: 1 addition & 0 deletions browser/devtools/commandline/commands-index.js
Expand Up @@ -12,6 +12,7 @@ const commandModules = [
"gcli/commands/appcache",
"gcli/commands/listen",
"gcli/commands/media",
"gcli/commands/pagemod",
"gcli/commands/paintflashing",
"gcli/commands/restart",
"gcli/commands/screenshot",
Expand Down
32 changes: 26 additions & 6 deletions browser/devtools/commandline/test/browser_cmd_pagemod_export.js
Expand Up @@ -11,7 +11,6 @@ function test() {
}

function spawnTest() {
// Setup
let options = yield helpers.openTab(TEST_URI);
yield helpers.openToolbar(options);

Expand All @@ -31,12 +30,13 @@ function spawnTest() {

yield helpers.audit(options, [
{
setup: 'export html',
setup: 'export html',
skipIf: true,
check: {
input: 'export html',
hints: '',
hints: ' [destination]',
markup: 'VVVVVVVVVVV',
status: 'VALID'
status: 'VALID',
},
exec: {
output: ''
Expand All @@ -45,12 +45,32 @@ function spawnTest() {
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>");

options.window.open = oldOpen;
}
},
{
setup: 'export html stdout',
check: {
input: 'export html stdout',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVV',
status: 'VALID',
args: {
destination: { value: "stdout" }
},
},
exec: {
output: [
/<html lang="en">/,
/<title>GCLI/,
/<p id="someid">#/
]
}
}
]);

options.window.open = oldOpen;
oldOpen = undefined;

// Test 'pagemod replace'
yield helpers.audit(options, [
{
Expand Down

0 comments on commit d63051e

Please sign in to comment.