Bug 1342459 - Add a rule to automatically detect ContentTask.spawn and inject the relevant globals. r?Mossop draft
authorMark Banner <standard8@mozilla.com>
Wed, 05 Apr 2017 10:00:25 +0100
changeset 556967 8eb1297d38ed5e1e839d557e29756322b0ac7833
parent 556947 1df35d8cab1e3ef692f54f9832c8eb3b0f779523
child 556968 f85670d61e2806515c12b27693b3df97600ccf12
push id52640
push userbmo:standard8@mozilla.com
push dateThu, 06 Apr 2017 11:12:32 +0000
reviewersMossop
bugs1342459
milestone55.0a1
Bug 1342459 - Add a rule to automatically detect ContentTask.spawn and inject the relevant globals. r?Mossop This reduces the amount of places where we need to specify the mozilla/frame-script environment. It does have the side effect of allowing those globals in the whole file, but that is what specifying the environment would do, and this is also for mochitest test files only. MozReview-Commit-ID: 1LLFbn6fFJR
browser/base/content/test/forms/head.js
browser/base/content/test/general/browser_PageMetaData_pushstate.js
browser/base/content/test/general/browser_aboutCertError.js
browser/base/content/test/general/browser_backButtonFitts.js
browser/base/content/test/general/browser_documentnavigation.js
browser/base/content/test/general/browser_offlineQuotaNotification.js
browser/base/content/test/general/browser_tab_dragdrop.js
browser/base/content/test/general/head.js
browser/base/content/test/newtab/browser_newtab_bug734043.js
browser/base/content/test/newtab/browser_newtab_bug991111.js
browser/base/content/test/newtab/browser_newtab_bug998387.js
browser/base/content/test/newtab/browser_newtab_search.js
browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
browser/components/sessionstore/test/browser_367052.js
browser/components/sessionstore/test/browser_687710_2.js
browser/components/sessionstore/test/browser_async_remove_tab.js
browser/components/sessionstore/test/browser_async_window_flushing.js
browser/components/sessionstore/test/browser_history_persist.js
browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js
browser/components/sessionstore/test/browser_purge_shistory.js
browser/components/sessionstore/test/browser_replace_load.js
browser/components/sessionstore/test/browser_send_async_message_oom.js
browser/components/sessionstore/test/browser_sessionStoreContainer.js
browser/components/sessionstore/test/browser_switch_remoteness.js
browser/components/sessionstore/test/browser_windowStateContainer.js
browser/components/uitour/test/head.js
toolkit/components/remotebrowserutils/tests/browser/browser_RemoteWebNavigation.js
tools/lint/docs/linters/eslint-plugin-mozilla.rst
tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js
tools/lint/eslint/eslint-plugin-mozilla/lib/configs/chrome-test.js
tools/lint/eslint/eslint-plugin-mozilla/lib/configs/mochitest-test.js
tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-content-task-globals.js
tools/lint/eslint/eslint-plugin-mozilla/package.json
--- a/browser/base/content/test/forms/head.js
+++ b/browser/base/content/test/forms/head.js
@@ -1,10 +1,8 @@
-/* eslint-env mozilla/frame-script */
-
 function hideSelectPopup(selectPopup, mode = "enter", win = window) {
   let browser = win.gBrowser.selectedBrowser;
   let selectClosedPromise = ContentTask.spawn(browser, null, function*() {
     Cu.import("resource://gre/modules/SelectContentHelper.jsm");
     return ContentTaskUtils.waitForCondition(() => !SelectContentHelper.open);
   });
 
   if (mode == "escape") {
--- a/browser/base/content/test/general/browser_PageMetaData_pushstate.js
+++ b/browser/base/content/test/general/browser_PageMetaData_pushstate.js
@@ -1,14 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* eslint-env mozilla/frame-script */
-
 add_task(function* () {
   let rooturi = "https://example.com/browser/toolkit/modules/tests/browser/";
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, rooturi + "metadata_simple.html");
   yield ContentTask.spawn(gBrowser.selectedBrowser, { rooturi }, function* (args) {
     Components.utils.import("resource://gre/modules/PageMetadata.jsm");
 
     let result = PageMetadata.getData(content.document);
     // Result should have description.
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -1,13 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/* eslint-env mozilla/frame-script */
-
 "use strict";
 
 // This is testing the aboutCertError page (Bug 1207107).
 
 const GOOD_PAGE = "https://example.com/";
 const BAD_CERT = "https://expired.example.com/";
 const UNKNOWN_ISSUER = "https://self-signed.example.com ";
 const BAD_STS_CERT = "https://badchain.include-subdomains.pinning.example.com:443";
--- a/browser/base/content/test/general/browser_backButtonFitts.js
+++ b/browser/base/content/test/general/browser_backButtonFitts.js
@@ -1,14 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* eslint-env mozilla/frame-script */
-
 add_task(function* () {
   let firstLocation = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, firstLocation);
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
     // Push the state before maximizing the window and clicking below.
     content.history.pushState("page2", "page2", "page2");
 
--- a/browser/base/content/test/general/browser_documentnavigation.js
+++ b/browser/base/content/test/general/browser_documentnavigation.js
@@ -1,16 +1,14 @@
 /*
  * This test checks that focus is adjusted properly in a browser when pressing F6 and Shift+F6.
  * There are additional tests in dom/tests/mochitest/chrome/test_focus_docnav.xul which test
  * non-browser cases.
  */
 
-/* eslint-env mozilla/frame-script */
-
 var testPage1 = "data:text/html,<html id='html1'><body id='body1'><button id='button1'>Tab 1</button></body></html>";
 var testPage2 = "data:text/html,<html id='html2'><body id='body2'><button id='button2'>Tab 2</button></body></html>";
 var testPage3 = "data:text/html,<html id='html3'><body id='body3' contenteditable='true'><button id='button3'>Tab 3</button></body></html>";
 
 var fm = Services.focus;
 
 function* expectFocusOnF6(backward, expectedDocument, expectedElement, onContent, desc) {
   let focusChangedInChildResolver = null;
--- a/browser/base/content/test/general/browser_offlineQuotaNotification.js
+++ b/browser/base/content/test/general/browser_offlineQuotaNotification.js
@@ -1,15 +1,13 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-/* eslint-env mozilla/frame-script */
-
 // Test offline quota warnings - must be run as a mochitest-browser test or
 // else the test runner gets in the way of notifications due to bug 857897.
 
 const URL = "http://mochi.test:8888/browser/browser/base/content/test/general/offlineQuotaNotification.html";
 
 registerCleanupFunction(function() {
   // Clean up after ourself
   let uri = Services.io.newURI(URL);
--- a/browser/base/content/test/general/browser_tab_dragdrop.js
+++ b/browser/base/content/test/general/browser_tab_dragdrop.js
@@ -1,10 +1,8 @@
-/* eslint-env mozilla/frame-script */
-
 function swapTabsAndCloseOther(a, b) {
   gBrowser.swapBrowsersAndCloseOther(gBrowser.tabs[b], gBrowser.tabs[a]);
 }
 
 var getClicks = function(tab) {
   return ContentTask.spawn(tab.linkedBrowser, {}, function() {
     return content.wrappedJSObject.clicks;
   });
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -1,10 +1,8 @@
-/* eslint-env mozilla/frame-script */
-
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
--- a/browser/base/content/test/newtab/browser_newtab_bug734043.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug734043.js
@@ -1,13 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/* eslint-env mozilla/frame-script */
-
 add_task(function* () {
   yield setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   yield* addNewTabPageTab();
   yield* checkGrid("0,1,2,3,4,5,6,7,8");
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
--- a/browser/base/content/test/newtab/browser_newtab_bug991111.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug991111.js
@@ -1,13 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/* eslint-env mozilla/frame-script */
-
 add_task(function* () {
   // set max rows to 1, to avoid scroll events by clicking middle button
   yield pushPrefs(["browser.newtabpage.rows", 1]);
   yield setLinks("-1");
   yield* addNewTabPageTab();
   // we need a second newtab to honor max rows
   yield* addNewTabPageTab();
 
--- a/browser/base/content/test/newtab/browser_newtab_bug998387.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug998387.js
@@ -1,13 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/* eslint-env mozilla/frame-script */
-
 add_task(function* () {
   // set max rows to 1, to avoid scroll events by clicking middle button
   yield pushPrefs(["browser.newtabpage.rows", 1]);
   yield setLinks("0");
   yield* addNewTabPageTab();
   // we need a second newtab to honor max rows
   yield* addNewTabPageTab();
 
--- a/browser/base/content/test/newtab/browser_newtab_search.js
+++ b/browser/base/content/test/newtab/browser_newtab_search.js
@@ -1,13 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/* eslint-env mozilla/frame-script */
-
 // See browser/components/search/test/browser_*_behavior.js for tests of actual
 // searches.
 
 Cu.import("resource://gre/modules/Task.jsm");
 
 const ENGINE_NO_LOGO = {
   name: "searchEngineNoLogo.xml",
   numLogos: 0,
--- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
@@ -1,11 +1,8 @@
-// This file spawns content tasks.
-/* eslint-env mozilla/frame-script */
-
 const BASE_URL = "http://mochi.test:8888/browser/browser/components/originattributes/test/browser/";
 const BASE_DOMAIN = "mochi.test";
 
 add_task(function* setup() {
   Services.prefs.setBoolPref("privacy.firstparty.isolate", true);
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("privacy.firstparty.isolate");
   });
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
@@ -1,12 +1,10 @@
 "use strict";
 
-/* eslint-env mozilla/frame-script */
-
 /**
  * Given some window in the parent process, ensure that
  * the nsIXULWindow has the CHROME_PRIVATE_WINDOW chromeFlag,
  * and that the usePrivateBrowsing property is set to true on
  * both the window's nsILoadContext, as well as on the initial
  * browser's content docShell nsILoadContext.
  *
  * @param win (nsIDOMWindow)
--- a/browser/components/sessionstore/test/browser_367052.js
+++ b/browser/components/sessionstore/test/browser_367052.js
@@ -30,13 +30,12 @@ add_task(function* () {
   is(count, 0, "the tab was restored without any history whatsoever");
 
   yield promiseRemoveTab(tab);
   is(ss.getClosedTabCount(window), 0,
      "The closed blank tab wasn't added to Recently Closed Tabs");
 });
 
 function promiseSHistoryCount(browser) {
-  /* eslint-env mozilla/frame-script */
   return ContentTask.spawn(browser, null, function* () {
     return docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory.count;
   });
 }
--- a/browser/components/sessionstore/test/browser_687710_2.js
+++ b/browser/components/sessionstore/test/browser_687710_2.js
@@ -1,9 +1,8 @@
-/* eslint-env mozilla/frame-script */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that the fix for bug 687710 isn't too aggressive -- shentries which are
 // cousins should be able to share bfcache entries.
 
 var stateBackup = ss.getBrowserState();
 
--- a/browser/components/sessionstore/test/browser_async_remove_tab.js
+++ b/browser/components/sessionstore/test/browser_async_remove_tab.js
@@ -28,17 +28,16 @@ function restoreClosedTabWithValue(rval)
   if (index == -1) {
     throw new Error("no closed tab found for given rval");
   }
 
   return ss.undoCloseTab(window, index);
 }
 
 function promiseNewLocationAndHistoryEntryReplaced(browser, snippet) {
-  /* eslint-env mozilla/frame-script */
   return ContentTask.spawn(browser, snippet, function* (codeSnippet) {
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let shistory = webNavigation.sessionHistory;
 
     // Evaluate the snippet that the changes the location.
     eval(codeSnippet);
 
     return new Promise(resolve => {
--- a/browser/components/sessionstore/test/browser_async_window_flushing.js
+++ b/browser/components/sessionstore/test/browser_async_window_flushing.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// This file generates content tasks.
-/* eslint-env mozilla/frame-script */
-
 const PAGE = "http://example.com/";
 
 /**
  * Tests that if we initially discard a window as not interesting
  * to save in the closed windows array, that we revisit that decision
  * after a window flush has completed.
  */
 add_task(function* test_add_interesting_window() {
--- a/browser/components/sessionstore/test/browser_history_persist.js
+++ b/browser/components/sessionstore/test/browser_history_persist.js
@@ -1,9 +1,8 @@
-/* eslint-env mozilla/frame-script */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Ensure that history entries that should not be persisted are restored in the
  * same state.
--- a/browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js
+++ b/browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js
@@ -21,17 +21,16 @@ add_task(function*() {
   ok(browser.isRemoteBrowser, "Should have switched remoteness");
   yield TabStateFlusher.flush(browser);
   let state = JSON.parse(ss.getTabState(tab));
   let entries = state.entries;
   is(entries.length, 1, "There should only be one entry");
   is(entries[0].url, PAGE_2, "Should have PAGE_2 as the sole history entry");
   is(browser.currentURI.spec, PAGE_2, "Should have PAGE_2 as the browser currentURI");
 
-  /* eslint-env mozilla/frame-script */
   yield ContentTask.spawn(browser, PAGE_2, function*(expectedURL) {
     docShell.QueryInterface(Ci.nsIWebNavigation);
     Assert.equal(docShell.currentURI.spec, expectedURL,
        "Content should have PAGE_2 as the browser currentURI");
   });
 
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/components/sessionstore/test/browser_purge_shistory.js
+++ b/browser/components/sessionstore/test/browser_purge_shistory.js
@@ -1,11 +1,8 @@
-// This file spawns content tasks.
-/* eslint-env mozilla/frame-script */
-
 "use strict";
 
 /**
  * This test checks that pending tabs are treated like fully loaded tabs when
  * purging session history. Just like for fully loaded tabs we want to remove
  * every but the current shistory entry.
  */
 
--- a/browser/components/sessionstore/test/browser_replace_load.js
+++ b/browser/components/sessionstore/test/browser_replace_load.js
@@ -1,11 +1,8 @@
-// This file spawns content tasks.
-/* eslint-env mozilla/frame-script */
-
 "use strict";
 
 const STATE = {
   entries: [{url: "about:robots"}, {url: "about:mozilla"}],
   selected: 2
 };
 
 /**
--- a/browser/components/sessionstore/test/browser_send_async_message_oom.js
+++ b/browser/components/sessionstore/test/browser_send_async_message_oom.js
@@ -1,14 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-// This file spawns content tasks.
-/* eslint-env mozilla/frame-script */
-
 const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 const HISTOGRAM_NAME = "FX_SESSION_RESTORE_SEND_UPDATE_CAUSED_OOM";
 
 /**
  * Test that an OOM in sendAsyncMessage in a framescript will be reported
  * to Telemetry.
  */
--- a/browser/components/sessionstore/test/browser_sessionStoreContainer.js
+++ b/browser/components/sessionstore/test/browser_sessionStoreContainer.js
@@ -1,15 +1,12 @@
 /* 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/. */
 
-// This file spawns content tasks.
-/* eslint-env mozilla/frame-script */
-
 "use strict";
 
 add_task(function* () {
   for (let i = 0; i < 3; ++i) {
     let tab = gBrowser.addTab("http://example.com/", { userContextId: i });
     let browser = tab.linkedBrowser;
 
     yield promiseBrowserLoaded(browser);
--- a/browser/components/sessionstore/test/browser_switch_remoteness.js
+++ b/browser/components/sessionstore/test/browser_switch_remoteness.js
@@ -1,11 +1,8 @@
-// This file spawns content tasks.
-/* eslint-env mozilla/frame-script */
-
 "use strict";
 
 const URL = "http://example.com/browser_switch_remoteness_";
 
 function countHistoryEntries(browser, expected) {
   return ContentTask.spawn(browser, { expected }, function* (args) {
     let Ci = Components.interfaces;
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
--- a/browser/components/sessionstore/test/browser_windowStateContainer.js
+++ b/browser/components/sessionstore/test/browser_windowStateContainer.js
@@ -1,11 +1,8 @@
-// This file spawns content tasks.
-/* eslint-env mozilla/frame-script */
-
 "use strict";
 
 requestLongerTimeout(2);
 
 add_task(function* setup() {
   yield SpecialPowers.pushPrefEnv({
     set: [["dom.ipc.processCount", 1]]
   });
--- a/browser/components/uitour/test/head.js
+++ b/browser/components/uitour/test/head.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// This file spawns a content task.
-/* eslint-env mozilla/frame-script */
-
 // This file expects these globals to be defined by the test case.
 /* global gTestTab:true, gContentAPI:true, gContentWindow:true, tests:false */
 
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
                                   "resource:///modules/UITour.jsm");
 
--- a/toolkit/components/remotebrowserutils/tests/browser/browser_RemoteWebNavigation.js
+++ b/toolkit/components/remotebrowserutils/tests/browser/browser_RemoteWebNavigation.js
@@ -1,10 +1,8 @@
-/* eslint-env mozilla/frame-script */
-
 const DUMMY1 = "http://example.com/browser/toolkit/modules/tests/browser/dummy_page.html";
 const DUMMY2 = "http://example.org/browser/toolkit/modules/tests/browser/dummy_page.html"
 
 function waitForLoad(uri) {
   return BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
 }
 
 function waitForPageShow(browser = gBrowser.selectedBrowser) {
--- a/tools/lint/docs/linters/eslint-plugin-mozilla.rst
+++ b/tools/lint/docs/linters/eslint-plugin-mozilla.rst
@@ -57,16 +57,31 @@ Checks that for every occurence of 'addE
 occurence of 'removeEventListener' or 'off' with the same event name.
 
 import-browser-window-globals
 -----------------------------
 
 For scripts included in browser-window, this will automatically inject the
 browser-window global scopes into the file.
 
+import-content-task-globals
+---------------------------
+
+For files containing ContentTask.spawn calls, this will automatically declare
+the frame script variables in the global scope. ContentTask is only available
+to test files, so by default the configs only specify it for the mochitest based
+configurations.
+
+This saves setting the file as a mozilla/frame-script environment.
+
+Note: due to the way ESLint works, it appears it is only easy to declare these
+variables on a file global scope, rather than function global. This may mean that
+they are incorrectly allowed, but given they are test files, this should be
+detected during testing.
+
 import-globals
 --------------
 
 Checks the filename of imported files e.g. ``Cu.import("some/path/Blah.jsm")``
 adds Blah to the global scope.
 
 Note: uses modules.json for a list of globals listed in each file.
 
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/browser-test.js
@@ -1,13 +1,14 @@
 // Parent config file for all browser-chrome files.
 "use strict";
 
 module.exports = {
   "rules": {
+    "mozilla/import-content-task-globals": "error",
     "mozilla/import-headjs-globals": "warn",
     "mozilla/mark-test-function-used": "warn"
   },
 
   "env": {
     "browser": true,
     "mozilla/browser-window": true,
     "mozilla/simpletest": true
@@ -53,11 +54,13 @@ module.exports = {
     "SpecialPowers": false,
     "TestUtils": false,
     "thisTestLeaksUncaughtRejectionsAndShouldBeFixed": false,
     "todo": false,
     "todo_is": false,
     "todo_isnot": false,
     "waitForClipboard": false,
     "waitForExplicitFinish": false,
-    "waitForFocus": false
+    "waitForFocus": false,
+    "XPCNativeWrapper": false,
+    "XULDocument": false
   }
 };
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/chrome-test.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/chrome-test.js
@@ -1,13 +1,14 @@
 // Parent config file for all mochitest files.
 "use strict";
 
 module.exports = {
   rules: {
+    "mozilla/import-content-task-globals": "error",
     "mozilla/import-headjs-globals": "warn",
     "mozilla/mark-test-function-used": "warn"
   },
 
   "env": {
     "browser": true,
     "mozilla/browser-window": true,
     "mozilla/simpletest": true
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/mochitest-test.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/mochitest-test.js
@@ -1,13 +1,14 @@
 // Parent config file for all mochitest files.
 "use strict";
 
 module.exports = {
   "rules": {
+    "mozilla/import-content-task-globals": "error",
     "mozilla/import-headjs-globals": "warn",
     "mozilla/mark-test-function-used": "warn",
     "no-shadow": "error"
   },
 
   "env": {
     "browser": true,
     "mozilla/simpletest": true
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
@@ -31,16 +31,18 @@ module.exports = {
   },
   rules: {
     "avoid-removeChild": require("../lib/rules/avoid-removeChild"),
     "avoid-nsISupportsString-preferences":
       require("../lib/rules/avoid-nsISupportsString-preferences"),
     "balanced-listeners": require("../lib/rules/balanced-listeners"),
     "import-browser-window-globals":
       require("../lib/rules/import-browser-window-globals"),
+    "import-content-task-globals":
+      require("../lib/rules/import-content-task-globals"),
     "import-globals": require("../lib/rules/import-globals"),
     "import-headjs-globals": require("../lib/rules/import-headjs-globals"),
     "mark-test-function-used": require("../lib/rules/mark-test-function-used"),
     "no-aArgs": require("../lib/rules/no-aArgs"),
     "no-cpows-in-tests": require("../lib/rules/no-cpows-in-tests"),
     "no-single-arg-cu-import": require("../lib/rules/no-single-arg-cu-import"),
     "no-import-into-var-and-global":
       require("../lib/rules/no-import-into-var-and-global.js"),
@@ -55,16 +57,17 @@ module.exports = {
     "use-ownerGlobal": require("../lib/rules/use-ownerGlobal"),
     "var-only-at-top-level": require("../lib/rules/var-only-at-top-level")
   },
   rulesConfig: {
     "avoid-removeChild": "off",
     "avoid-nsISupportsString-preferences": "off",
     "balanced-listeners": "off",
     "import-browser-window-globals": "off",
+    "import-content-task-globals": "off",
     "import-globals": "off",
     "import-headjs-globals": "off",
     "mark-test-function-used": "off",
     "no-aArgs": "off",
     "no-cpows-in-tests": "off",
     "no-single-arg-cu-import": "off",
     "no-import-into-var-and-global": "off",
     "no-useless-parameters": "off",
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-content-task-globals.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview For ContentTask.spawn, this will automatically declare the
+ *               frame script variables in the global scope.
+ *               Note: due to the way ESLint works, it appears it is only
+ *               easy to declare these variables on a file-global scope, rather
+ *               than function global.
+ *
+ * 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/.
+ */
+
+"use strict";
+
+/* eslint max-len:"off" */
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+var path = require("path");
+var helpers = require("../helpers");
+var globals = require("../globals");
+var frameScriptEnv = require("../environments/frame-script");
+
+module.exports = function(context) {
+  // ---------------------------------------------------------------------------
+  // Public
+  // ---------------------------------------------------------------------------
+
+  return {
+    "CallExpression[callee.object.name='ContentTask'][callee.property.name='spawn']": function(node) {
+      for (let global in frameScriptEnv.globals) {
+        helpers.addVarToScope(global, context.getScope(),
+                              frameScriptEnv.globals[global]);
+      }
+    }
+  };
+};
--- a/tools/lint/eslint/eslint-plugin-mozilla/package.json
+++ b/tools/lint/eslint/eslint-plugin-mozilla/package.json
@@ -1,11 +1,11 @@
 {
   "name": "eslint-plugin-mozilla",
-  "version": "0.2.35",
+  "version": "0.2.36",
   "description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.",
   "keywords": [
     "eslint",
     "eslintplugin",
     "eslint-plugin",
     "mozilla",
     "firefox"
   ],