Bug 773347 - GCLI hidden commands don't execute properly; r=harth
authorJoe Walker <jwalker@mozilla.com>
Fri, 24 Aug 2012 11:05:04 +0100
changeset 103438 6b00d1edb2e717b0d658dc3408fae6f38add9b02
parent 103437 828067f7c340b7a62b17eeac5a0a2ba2cd059486
child 103439 7bf846af58d35674c1b02f8dbfa260f6012a78d5
push id13991
push userryanvm@gmail.com
push dateSun, 26 Aug 2012 02:29:03 +0000
treeherdermozilla-inbound@c4f20a024113 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersharth
bugs773347
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 773347 - GCLI hidden commands don't execute properly; r=harth
browser/devtools/commandline/CmdCmd.jsm
browser/devtools/commandline/gcli.jsm
browser/devtools/commandline/test/browser_cmd_commands.js
browser/devtools/commandline/test/browser_cmd_jsb.js
browser/devtools/commandline/test/browser_gcli_web.js
--- a/browser/devtools/commandline/CmdCmd.jsm
+++ b/browser/devtools/commandline/CmdCmd.jsm
@@ -15,16 +15,18 @@ XPCOMUtils.defineLazyGetter(this, "prefB
   return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource:///modules/devtools/Console.jsm");
 
+const PREF_DIR = "devtools.commands.dir";
+
 /**
  * A place to store the names of the commands that we have added as a result of
  * calling refreshAutoCommands(). Used by refreshAutoCommands to remove the
  * added commands.
  */
 let commands = [];
 
 /**
@@ -38,18 +40,18 @@ let CmdCommands = {
    * we eval the script from the .mozcmd file. This should be a chrome window.
    */
   refreshAutoCommands: function GC_refreshAutoCommands(aSandboxPrincipal) {
     // First get rid of the last set of commands
     commands.forEach(function(name) {
       gcli.removeCommand(name);
     });
 
-    let dirName = prefBranch.getComplexValue("devtools.commands.dir",
-                                             Ci.nsISupportsString).data;
+    let dirName = prefBranch.getComplexValue(PREF_DIR,
+                                             Ci.nsISupportsString).data.trim();
     if (dirName == "") {
       return;
     }
 
     let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
     dir.initWithPath(dirName);
     if (!dir.exists() || !dir.isDirectory()) {
       throw new Error('\'' + dirName + '\' is not a directory.');
@@ -104,23 +106,24 @@ function loadCommandFile(aFile, aSandbox
   }.bind(this));
 }
 
 /**
  * 'cmd' command
  */
 gcli.addCommand({
   name: "cmd",
-  description: gcli.lookup("cmdDesc"),
-  hidden: true
+  get hidden() { return !prefBranch.prefHasUserValue(PREF_DIR); },
+  description: gcli.lookup("cmdDesc")
 });
 
 /**
  * 'cmd refresh' command
  */
 gcli.addCommand({
   name: "cmd refresh",
   description: gcli.lookup("cmdRefreshDesc"),
-  hidden: true,
+  get hidden() { return !prefBranch.prefHasUserValue(PREF_DIR); },
   exec: function Command_cmdRefresh(args, context) {
-    GcliCmdCommands.refreshAutoCommands(context.environment.chromeDocument.defaultView);
+    let chromeWindow = context.environment.chromeDocument.defaultView;
+    GcliCommands.refreshAutoCommands(chromeWindow);
   }
 });
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -1792,29 +1792,40 @@ SelectionType.prototype._findPredictions
   // Cache lower case versions of all the option names
   for (i = 0; i < lookup.length; i++) {
     option = lookup[i];
     if (option._gcliLowerName == null) {
       option._gcliLowerName = option.name.toLowerCase();
     }
   }
 
+  // Exact hidden matches. If 'hidden: true' then we only allow exact matches
+  // All the tests after here check that !option.value.hidden
+  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
+    option = lookup[i];
+    if (option.name === arg.text) {
+      this._addToPredictions(predictions, option, arg);
+    }
+  }
+
   // Start with prefix matching
   for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
     option = lookup[i];
-    if (option._gcliLowerName.indexOf(match) === 0) {
-      this._addToPredictions(predictions, option, arg);
+    if (option._gcliLowerName.indexOf(match) === 0 && !option.value.hidden) {
+      if (predictions.indexOf(option) === -1) {
+        this._addToPredictions(predictions, option, arg);
+      }
     }
   }
 
   // Try infix matching if we get less half max matched
   if (predictions.length < (maxPredictions / 2)) {
     for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
       option = lookup[i];
-      if (option._gcliLowerName.indexOf(match) !== -1) {
+      if (option._gcliLowerName.indexOf(match) !== -1 && !option.value.hidden) {
         if (predictions.indexOf(option) === -1) {
           this._addToPredictions(predictions, option, arg);
         }
       }
     }
   }
 
   // Try fuzzy matching if we don't get a prefix match
@@ -2186,19 +2197,16 @@ CommandType.prototype.lookup = function(
     return { name: command.name, value: command };
   }, this);
 };
 
 /**
  * Add an option to our list of predicted options
  */
 CommandType.prototype._addToPredictions = function(predictions, option, arg) {
-  if (option.value.hidden) {
-    return;
-  }
   // The command type needs to exclude sub-commands when the CLI
   // is blank, but include them when we're filtering. This hack
   // excludes matches when the filter text is '' and when the
   // name includes a space.
   if (arg.text.length !== 0 || option.name.indexOf(' ') === -1) {
     predictions.push(option);
   }
 };
@@ -2505,16 +2513,26 @@ Object.defineProperty(Parameter.prototyp
 Object.defineProperty(Parameter.prototype, 'isDataRequired', {
   get: function() {
     return this.defaultValue === undefined;
   },
   enumerable: true
 });
 
 /**
+ * Reflect the paramSpec 'hidden' property (dynamically so it can change)
+ */
+Object.defineProperty(Parameter.prototype, 'hidden', {
+  get: function() {
+    return this.paramSpec.hidden;
+  },
+  enumerable: true
+});
+
+/**
  * Are we allowed to assign data to this parameter using positional
  * parameters?
  */
 Object.defineProperty(Parameter.prototype, 'isPositionalAllowed', {
   get: function() {
     return this.groupName == null;
   },
   enumerable: true
--- a/browser/devtools/commandline/test/browser_cmd_commands.js
+++ b/browser/devtools/commandline/test/browser_cmd_commands.js
@@ -8,23 +8,21 @@ Components.utils.import("resource:///mod
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-commands";
 
 function test() {
   DeveloperToolbarTest.test(TEST_URI, [ testEcho, testConsole ]);
 }
 
 function testEcho() {
-  /*
   DeveloperToolbarTest.exec({
     typed: "echo message",
     args: { message: "message" },
     outputMatch: /^message$/,
   });
-  */
 }
 
 function testConsole(browser, tab) {
   let hud = null;
   function onWebConsoleOpen(aSubject) {
     Services.obs.removeObserver(onWebConsoleOpen, "web-console-created");
 
     aSubject.QueryInterface(Ci.nsISupportsString);
--- a/browser/devtools/commandline/test/browser_cmd_jsb.js
+++ b/browser/devtools/commandline/test/browser_cmd_jsb.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the jsb command works as it should
 
 const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
                  "test/browser_cmd_jsb_script.jsi";
 
 function test() {
-  DeveloperToolbarTest.test("about:blank", [ /*GJT_test*/ ]);
+  DeveloperToolbarTest.test("about:blank", [ GJT_test ]);
 }
 
 function GJT_test() {
   DeveloperToolbarTest.exec({
     typed: "jsb AAA",
     outputMatch: /valid/
   });
 
--- a/browser/devtools/commandline/test/browser_gcli_web.js
+++ b/browser/devtools/commandline/test/browser_gcli_web.js
@@ -1728,16 +1728,17 @@ exports.setup = function() {
   canon.addCommand(exports.tsnExtend);
   canon.addCommand(exports.tsnDeep);
   canon.addCommand(exports.tsnDeepDown);
   canon.addCommand(exports.tsnDeepDownNested);
   canon.addCommand(exports.tsnDeepDownNestedCmd);
   canon.addCommand(exports.tselarr);
   canon.addCommand(exports.tsm);
   canon.addCommand(exports.tsg);
+  canon.addCommand(exports.tshidden);
   canon.addCommand(exports.tscook);
 };
 
 exports.shutdown = function() {
   canon.removeCommand(exports.tsv);
   canon.removeCommand(exports.tsr);
   canon.removeCommand(exports.tse);
   canon.removeCommand(exports.tsj);
@@ -1752,16 +1753,17 @@ exports.shutdown = function() {
   canon.removeCommand(exports.tsnExtend);
   canon.removeCommand(exports.tsnDeep);
   canon.removeCommand(exports.tsnDeepDown);
   canon.removeCommand(exports.tsnDeepDownNested);
   canon.removeCommand(exports.tsnDeepDownNestedCmd);
   canon.removeCommand(exports.tselarr);
   canon.removeCommand(exports.tsm);
   canon.removeCommand(exports.tsg);
+  canon.removeCommand(exports.tshidden);
   canon.removeCommand(exports.tscook);
 
   types.deregisterType(exports.optionType);
   types.deregisterType(exports.optionValue);
 };
 
 
 exports.option1 = { type: types.getType('string') };
@@ -1906,16 +1908,48 @@ exports.tsnDeepDownNested = {
   name: 'tsn deep down nested',
 };
 
 exports.tsnDeepDownNestedCmd = {
   name: 'tsn deep down nested cmd',
   exec: createExec('tsnDeepDownNestedCmd')
 };
 
+exports.tshidden = {
+  name: 'tshidden',
+  hidden: true,
+  params: [
+    {
+      group: 'Options',
+      params: [
+        {
+          name: 'visible',
+          type: 'string',
+          defaultValue: null,
+          description: 'visible'
+        },
+        {
+          name: 'invisiblestring',
+          type: 'string',
+          description: 'invisiblestring',
+          defaultValue: null,
+          hidden: true
+        },
+        {
+          name: 'invisibleboolean',
+          type: 'boolean',
+          description: 'invisibleboolean',
+          hidden: true
+        }
+      ]
+    }
+  ],
+  exec: createExec('tshidden')
+};
+
 exports.tselarr = {
   name: 'tselarr',
   params: [
     { name: 'num', type: { name: 'selection', data: [ '1', '2', '3' ] } },
     { name: 'arr', type: { name: 'array', subtype: 'string' } },
   ],
   exec: createExec('tselarr')
 };
@@ -3005,16 +3039,132 @@ exports.testIncomplete = function(option
   });
   test.is(requisition._unassigned[0], requisition.getAssignmentAt(5),
           'unassigned -');
   test.is(requisition._unassigned.length, 1, 'single unassigned - tsg -');
   test.is(requisition._unassigned[0].param.type.isIncompleteName, true,
           'unassigned.isIncompleteName: tsg -');
 };
 
+exports.testHidden = function(options) {
+  helpers.setInput('tshidde');
+  helpers.check({
+    input:  'tshidde',
+    markup: 'EEEEEEE',
+    directTabText: '',
+    arrowTabText: '',
+    status: 'ERROR',
+    emptyParameters: [ ],
+  });
+
+  helpers.setInput('tshidden');
+  helpers.check({
+    input:  'tshidden',
+    markup: 'VVVVVVVV',
+    directTabText: '',
+    arrowTabText: '',
+    status: 'VALID',
+    emptyParameters: [ ],
+    args: {
+      visible: { value: undefined, status: 'VALID' },
+      invisiblestring: { value: undefined, status: 'VALID' },
+      invisibleboolean: { value: undefined, status: 'VALID' }
+    }
+  });
+
+  helpers.setInput('tshidden --vis');
+  helpers.check({
+    input:  'tshidden --vis',
+    markup: 'VVVVVVVVVIIIII',
+    directTabText: 'ible',
+    arrowTabText: '',
+    status: 'ERROR',
+    emptyParameters: [ ],
+    args: {
+      visible: { value: undefined, status: 'VALID' },
+      invisiblestring: { value: undefined, status: 'VALID' },
+      invisibleboolean: { value: undefined, status: 'VALID' }
+    }
+  });
+
+  helpers.setInput('tshidden --invisiblestrin');
+  helpers.check({
+    input:  'tshidden --invisiblestrin',
+    markup: 'VVVVVVVVVEEEEEEEEEEEEEEEE',
+    directTabText: '',
+    arrowTabText: '',
+    status: 'ERROR',
+    emptyParameters: [ ],
+    args: {
+      visible: { value: undefined, status: 'VALID' },
+      invisiblestring: { value: undefined, status: 'VALID' },
+      invisibleboolean: { value: undefined, status: 'VALID' }
+    }
+  });
+
+  helpers.setInput('tshidden --invisiblestring');
+  helpers.check({
+    input:  'tshidden --invisiblestring',
+    markup: 'VVVVVVVVVIIIIIIIIIIIIIIIII',
+    directTabText: '',
+    arrowTabText: '',
+    status: 'ERROR',
+    emptyParameters: [ ],
+    args: {
+      visible: { value: undefined, status: 'VALID' },
+      invisiblestring: { value: undefined, status: 'INCOMPLETE' },
+      invisibleboolean: { value: undefined, status: 'VALID' }
+    }
+  });
+
+  helpers.setInput('tshidden --invisiblestring x');
+  helpers.check({
+    input:  'tshidden --invisiblestring x',
+    markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVV',
+    directTabText: '',
+    arrowTabText: '',
+    status: 'VALID',
+    emptyParameters: [ ],
+    args: {
+      visible: { value: undefined, status: 'VALID' },
+      invisiblestring: { value: 'x', status: 'VALID' },
+      invisibleboolean: { value: undefined, status: 'VALID' }
+    }
+  });
+
+  helpers.setInput('tshidden --invisibleboolea');
+  helpers.check({
+    input:  'tshidden --invisibleboolea',
+    markup: 'VVVVVVVVVEEEEEEEEEEEEEEEEE',
+    directTabText: '',
+    arrowTabText: '',
+    status: 'ERROR',
+    emptyParameters: [ ],
+    args: {
+      visible: { value: undefined, status: 'VALID' },
+      invisiblestring: { value: undefined, status: 'VALID' },
+      invisibleboolean: { value: undefined, status: 'VALID' }
+    }
+  });
+
+  helpers.setInput('tshidden --invisibleboolean');
+  helpers.check({
+    input:  'tshidden --invisibleboolean',
+    markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVV',
+    directTabText: '',
+    arrowTabText: '',
+    status: 'VALID',
+    emptyParameters: [ ],
+    args: {
+      visible: { value: undefined, status: 'VALID' },
+      invisiblestring: { value: undefined, status: 'VALID' },
+      invisibleboolean: { value: true, status: 'VALID' }
+    }
+  });
+};
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at