Bug 332176 - Change eval-ed scripts to have more descriptive filenames indicating the chain of evaluation, and give them absolute line numbers instead of the offset-from-lineno-of-eval-in-caller-script approach used currently. r=jimb
authorKannan Vijayan <kvijayan@mozilla.com>
Tue, 04 Feb 2014 16:23:20 -0500
changeset 166826 2c84be83868929432b43b59eda7d5c56a5b7764c
parent 166825 326a283714a8330ac1749c8cacafe857f1da2e02
child 166827 a15aa9ce56e20174e700ca47df0553b216e9a70d
push id39296
push userkvijayan@mozilla.com
push dateTue, 04 Feb 2014 21:23:43 +0000
treeherdermozilla-inbound@2c84be838689 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs332176
milestone30.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 332176 - Change eval-ed scripts to have more descriptive filenames indicating the chain of evaluation, and give them absolute line numbers instead of the offset-from-lineno-of-eval-in-caller-script approach used currently. r=jimb
browser/devtools/debugger/test/browser_dbg_aaa_run_first_leaktest.js
browser/devtools/debugger/test/browser_dbg_bfcache.js
browser/devtools/debugger/test/browser_dbg_breadcrumbs-access.js
browser/devtools/debugger/test/browser_dbg_breakpoints-actual-location.js
browser/devtools/debugger/test/browser_dbg_breakpoints-disabled-reload.js
browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js
browser/devtools/debugger/test/browser_dbg_breakpoints-pane.js
browser/devtools/debugger/test/browser_dbg_cmd-break.js
browser/devtools/debugger/test/browser_dbg_controller-evaluate-01.js
browser/devtools/debugger/test/browser_dbg_controller-evaluate-02.js
browser/devtools/debugger/test/browser_dbg_editor-contextmenu.js
browser/devtools/debugger/test/browser_dbg_editor-mode.js
browser/devtools/debugger/test/browser_dbg_scripts-switching-01.js
browser/devtools/debugger/test/browser_dbg_scripts-switching-02.js
browser/devtools/debugger/test/browser_dbg_search-basic-02.js
browser/devtools/debugger/test/browser_dbg_search-basic-03.js
browser/devtools/debugger/test/browser_dbg_search-global-01.js
browser/devtools/debugger/test/browser_dbg_search-global-02.js
browser/devtools/debugger/test/browser_dbg_search-global-03.js
browser/devtools/debugger/test/browser_dbg_search-global-04.js
browser/devtools/debugger/test/browser_dbg_search-global-05.js
browser/devtools/debugger/test/browser_dbg_search-global-06.js
browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js
browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js
browser/devtools/debugger/test/browser_dbg_stack-02.js
browser/devtools/debugger/test/browser_dbg_stack-04.js
browser/devtools/debugger/test/browser_dbg_stack-05.js
browser/devtools/debugger/test/browser_dbg_stack-06.js
browser/devtools/debugger/test/browser_dbg_stack-07.js
js/src/builtin/Eval.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/jit-test/tests/basic/spread-call-eval.js
js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfun.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/Debugger.cpp
--- a/browser/devtools/debugger/test/browser_dbg_aaa_run_first_leaktest.js
+++ b/browser/devtools/debugger/test/browser_dbg_aaa_run_first_leaktest.js
@@ -14,15 +14,15 @@ function test() {
   // GC from previous tests does not interfere with the debugger suite.
   requestLongerTimeout(2);
 
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     ok(aTab, "Should have a tab available.");
     ok(aDebuggee, "Should have a debuggee available.");
     ok(aPanel, "Should have a debugger pane available.");
 
-    waitForSourceAndCaretAndScopes(aPanel, "-02.js", 6).then(() => {
+    waitForSourceAndCaretAndScopes(aPanel, "-02.js", 1).then(() => {
       resumeDebuggerThenCloseAndFinish(aPanel);
     });
 
     aDebuggee.firstCall();
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_bfcache.js
+++ b/browser/devtools/debugger/test/browser_dbg_bfcache.js
@@ -33,17 +33,17 @@ function test() {
 
 function testFirstPage() {
   info("Testing first page.");
 
   // Spin the event loop before causing the debuggee to pause, to allow
   // this function to return first.
   executeSoon(() => gDebuggee.firstCall());
 
-  return waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(() => {
+  return waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1).then(() => {
     validateFirstPage();
   });
 }
 
 function testLocationChange() {
   info("Navigating to a different page.");
 
   return navigateActiveTabTo(gPanel, TAB_URL_2, gDebugger.EVENTS.SOURCES_ADDED).then(() => {
--- a/browser/devtools/debugger/test/browser_dbg_breadcrumbs-access.js
+++ b/browser/devtools/debugger/test/browser_dbg_breadcrumbs-access.js
@@ -14,75 +14,75 @@ function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gFrames = gDebugger.DebuggerView.StackFrames;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(checkNavigationWhileNotFocused)
       .then(focusCurrentStackFrame)
       .then(checkNavigationWhileFocused)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
     gDebuggee.firstCall();
   });
 
   function checkNavigationWhileNotFocused() {
-    checkState({ frame: 3, source: 1, line: 6 });
+    checkState({ frame: 3, source: 1, line: 1 });
 
     EventUtils.sendKey("DOWN", gDebugger);
-    checkState({ frame: 3, source: 1, line: 7 });
+    checkState({ frame: 3, source: 1, line: 2 });
 
     EventUtils.sendKey("UP", gDebugger);
-    checkState({ frame: 3, source: 1, line: 6 });
+    checkState({ frame: 3, source: 1, line: 1 });
   }
 
   function focusCurrentStackFrame() {
     EventUtils.sendMouseEvent({ type: "mousedown" },
       gFrames.selectedItem.target,
       gDebugger);
   }
 
   function checkNavigationWhileFocused() {
     return Task.spawn(function() {
       yield promise.all([
         waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
         EventUtils.sendKey("UP", gDebugger)
       ]);
-      checkState({ frame: 2, source: 1, line: 6 });
+      checkState({ frame: 2, source: 1, line: 1 });
 
       yield promise.all([
         waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
-        waitForSourceAndCaret(gPanel, "-01.js", 5),
+        waitForSourceAndCaret(gPanel, "-01.js", 1),
         EventUtils.sendKey("UP", gDebugger)
       ]);
-      checkState({ frame: 1, source: 0, line: 5 });
+      checkState({ frame: 1, source: 0, line: 1 });
 
       yield promise.all([
         waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
         EventUtils.sendKey("UP", gDebugger)
       ]);
       checkState({ frame: 0, source: 0, line: 5 });
 
       yield promise.all([
         waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
-        waitForSourceAndCaret(gPanel, "-02.js", 6),
+        waitForSourceAndCaret(gPanel, "-02.js", 1),
         EventUtils.sendKey("END", gDebugger)
       ]);
-      checkState({ frame: 3, source: 1, line: 6 });
+      checkState({ frame: 3, source: 1, line: 1 });
 
       yield promise.all([
         waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES),
-        waitForSourceAndCaret(gPanel, "-01.js", 5),
+        waitForSourceAndCaret(gPanel, "-01.js", 1),
         EventUtils.sendKey("HOME", gDebugger)
       ]);
       checkState({ frame: 0, source: 0, line: 5 });
     });
   }
 
   function checkState({ frame, source, line }) {
     is(gFrames.selectedIndex, frame,
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-actual-location.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-actual-location.js
@@ -18,17 +18,17 @@ function test() {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gBreakpoints = gDebugger.DebuggerController.Breakpoints;
     gBreakpointsAdded = gBreakpoints._added;
     gBreakpointsRemoving = gBreakpoints._removing;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1).then(performTest);
     gDebuggee.firstCall();
   });
 
   function performTest() {
     is(gBreakpointsAdded.size, 0,
       "No breakpoints currently added.");
     is(gBreakpointsRemoving.size, 0,
       "No breakpoints currently being removed.");
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-disabled-reload.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-disabled-reload.js
@@ -68,48 +68,48 @@ function test() {
 
         executeSoon(() => aDebuggee.firstCall());
         yield waitForDebuggerEvents(aPanel, gEvents.FETCHED_SCOPES);
         yield ensureSourceIs(aPanel, "-01.js");
         yield ensureCaretAt(aPanel, 5);
         yield verifyView({ disabled: false, visible: true });
 
         executeSoon(() => gDebugger.gThreadClient.resume());
-        yield waitForSourceAndCaretAndScopes(aPanel, "-02.js", 6);
+        yield waitForSourceAndCaretAndScopes(aPanel, "-02.js", 1);
         yield verifyView({ disabled: false, visible: false });
       });
     }
 
     function testWhenBreakpointEnabledAndSecondSourceShown() {
       return Task.spawn(function() {
         yield ensureSourceIs(aPanel, "-02.js", true);
         yield verifyView({ disabled: false, visible: false });
 
         executeSoon(() => aDebuggee.firstCall());
-        yield waitForSourceAndCaretAndScopes(aPanel, "-01.js", 5);
+        yield waitForSourceAndCaretAndScopes(aPanel, "-01.js", 1);
         yield verifyView({ disabled: false, visible: true });
 
         executeSoon(() => gDebugger.gThreadClient.resume());
-        yield waitForSourceAndCaretAndScopes(aPanel, "-02.js", 6);
+        yield waitForSourceAndCaretAndScopes(aPanel, "-02.js", 1);
         yield verifyView({ disabled: false, visible: false });
       });
     }
 
     function testWhenBreakpointDisabledAndSecondSourceShown() {
       return Task.spawn(function() {
         yield ensureSourceIs(aPanel, "-02.js", true);
         yield verifyView({ disabled: true, visible: false });
 
         executeSoon(() => aDebuggee.firstCall());
         yield waitForDebuggerEvents(aPanel, gEvents.FETCHED_SCOPES);
         yield ensureSourceIs(aPanel, "-02.js");
-        yield ensureCaretAt(aPanel, 6);
+        yield ensureCaretAt(aPanel, 1);
         yield verifyView({ disabled: true, visible: false });
 
         executeSoon(() => gDebugger.gThreadClient.resume());
         yield waitForDebuggerEvents(aPanel, gEvents.AFTER_FRAMES_CLEARED);
         yield ensureSourceIs(aPanel, "-02.js");
-        yield ensureCaretAt(aPanel, 6);
+        yield ensureCaretAt(aPanel, 1);
         yield verifyView({ disabled: true, visible: false });
       });
     }
   });
 }
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js
@@ -18,17 +18,17 @@ function test() {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gBreakpoints = gDebugger.DebuggerController.Breakpoints;
     gBreakpointsAdded = gBreakpoints._added;
     gBreakpointsRemoving = gBreakpoints._removing;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1).then(performTest);
     gDebuggee.firstCall();
   });
 
   function performTest() {
     is(gDebugger.gThreadClient.state, "paused",
       "Should only be getting stack frames while paused.");
     is(gSources.itemCount, 2,
       "Found the expected number of sources.");
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-pane.js
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-pane.js
@@ -18,17 +18,17 @@ function test() {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gBreakpoints = gDebugger.DebuggerController.Breakpoints;
     gBreakpointsAdded = gBreakpoints._added;
     gBreakpointsRemoving = gBreakpoints._removing;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1).then(performTest);
     gDebuggee.firstCall();
   });
 
   let breakpointsAdded = 0;
   let breakpointsDisabled = 0;
   let breakpointsRemoved = 0;
 
   function performTest() {
--- a/browser/devtools/debugger/test/browser_dbg_cmd-break.js
+++ b/browser/devtools/debugger/test/browser_dbg_cmd-break.js
@@ -43,43 +43,43 @@ function test() {
       {
         name: 'open toolbox',
         setup: function() {
           return initDebugger(gBrowser.selectedTab).then(([aTab, aDebuggee, aPanel]) => {
             // Spin the event loop before causing the debuggee to pause, to allow
             // this function to return first.
             executeSoon(() => aDebuggee.firstCall());
 
-            return waitForSourceAndCaretAndScopes(aPanel, ".html", 17).then(() => {
+            return waitForSourceAndCaretAndScopes(aPanel, ".html", 1).then(() => {
               gPanel = aPanel;
               gDebugger = gPanel.panelWin;
               gThreadClient = gPanel.panelWin.gThreadClient;
               gLineNumber = '' + aOptions.window.wrappedJSObject.gLineNumber;
             });
           });
         },
         post: function() {
           ok(gThreadClient, "Debugger client exists.");
-          is(gLineNumber, 14, "gLineNumber is correct.");
+          is(gLineNumber, 1, "gLineNumber is correct.");
         },
       },
       {
         name: 'break add line .../doc_cmd-break.html 14',
         setup: function() {
           // We have to setup in a function to allow gLineNumber to be initialized.
           let line = 'break add line ' + TAB_URL + ' ' + gLineNumber;
           return helpers.setInput(aOptions, line);
         },
         check: {
           hints: '',
           status: 'VALID',
           message: '',
           args: {
             file: { value: TAB_URL, message: '' },
-            line: { value: 14 }
+            line: { value: 1 }
           }
         },
         exec: {
           output: 'Added breakpoint',
           completed: false
         }
       },
       {
@@ -104,53 +104,53 @@ function test() {
           input:  'break list',
           hints:            '',
           markup: 'VVVVVVVVVV',
           status: 'VALID'
         },
         exec: {
           output: [
             /Source/, /Remove/,
-            /doc_cmd-break\.html:14/,
-            /doc_cmd-break\.html:17/
+            /doc_cmd-break\.html:1/,
+            /doc_cmd-break\.html:1/
           ]
         }
       },
       {
         name: 'cleanup',
         setup: function() {
           let deferred = promise.defer();
           gThreadClient.resume(deferred.resolve);
           return deferred.promise;
         }
       },
       {
-        setup: 'break del 14',
+        setup: 'break del 1',
         check: {
-          input:  'break del 14',
-          hints:              ' -> doc_cmd-break.html:14',
-          markup: 'VVVVVVVVVVII',
+          input:  'break del 1',
+          hints:              ' -> doc_cmd-break.html:1',
+          markup: 'VVVVVVVVVVI',
           status: 'ERROR',
           args: {
             breakpoint: {
               status: 'INCOMPLETE',
               message: ''
             }
           }
         }
       },
       {
-        setup: 'break del doc_cmd-break.html:14',
+        setup: 'break del doc_cmd-break.html:1',
         check: {
-          input:  'break del doc_cmd-break.html:14',
+          input:  'break del doc_cmd-break.html:1',
           hints:                                 '',
-          markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
+          markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
           status: 'VALID',
           args: {
-            breakpoint: { arg: ' doc_cmd-break.html:14' },
+            breakpoint: { arg: ' doc_cmd-break.html:1' },
           }
         },
         exec: {
           output: 'Breakpoint removed',
           completed: false
         }
       },
       {
--- a/browser/devtools/debugger/test/browser_dbg_controller-evaluate-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_controller-evaluate-01.js
@@ -42,42 +42,42 @@ function test() {
       yield frames.evaluate("foo");
     } catch (error) {
       is(error.message, "No stack frame available.",
         "Evaluating shouldn't work while the debuggee isn't paused.");
     }
 
     // Allow this generator function to yield first.
     executeSoon(() => debuggee.firstCall());
-    yield waitForSourceAndCaretAndScopes(panel, "-02.js", 6);
-    checkView(0, 1, 6, [/secondCall/, 118]);
+    yield waitForSourceAndCaretAndScopes(panel, "-02.js", 1);
+    checkView(0, 1, 1, [/secondCall/, 118]);
 
     // Eval in the topmost frame, while paused.
     let updatedView = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
     let result = yield frames.evaluate("foo");
     ok(!result.throw, "The evaluation hasn't thrown.");
     is(result.return.type, "object", "The evaluation return type is correct.");
     is(result.return.class, "Function", "The evaluation return class is correct.");
 
     yield updatedView;
-    checkView(0, 1, 6, [/secondCall/, 118]);
+    checkView(0, 1, 1, [/secondCall/, 118]);
     ok(true, "Evaluating in the topmost frame works properly.");
 
     // Eval in a different frame, while paused.
     let updatedView = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
     try {
       yield frames.evaluate("foo", { depth: 3 }); // oldest frame
     } catch (result) {
       is(result.return.type, "object", "The evaluation thrown type is correct.");
       is(result.return.class, "Error", "The evaluation thrown class is correct.");
       ok(!result.return, "The evaluation hasn't returned.");
     }
 
     yield updatedView;
-    checkView(0, 1, 6, [/secondCall/, 118]);
+    checkView(0, 1, 1, [/secondCall/, 118]);
     ok(true, "Evaluating in a custom frame works properly.");
 
     // Eval in a non-existent frame, while paused.
     waitForDebuggerEvents(panel, events.FETCHED_SCOPES).then(() => {
       ok(false, "Shouldn't have updated the view when trying to evaluate " +
         "an expression in a non-existent stack frame.");
     });
     try {
--- a/browser/devtools/debugger/test/browser_dbg_controller-evaluate-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_controller-evaluate-02.js
@@ -34,18 +34,18 @@ function test() {
     }
 
     // Cache the sources text to avoid having to wait for their retrieval.
     yield promise.all(sourcesView.attachments.map(e => sources.getText(e.source)));
     is(sources._cache.size, 2, "There should be two cached sources in the cache.");
 
     // Allow this generator function to yield first.
     executeSoon(() => debuggee.firstCall());
-    yield waitForSourceAndCaretAndScopes(panel, "-02.js", 6);
-    checkView(0, 1, 6, [/secondCall/, 118]);
+    yield waitForSourceAndCaretAndScopes(panel, "-02.js", 1);
+    checkView(0, 1, 1, [/secondCall/, 118]);
 
     // Change the selected frame and eval inside it.
     let updatedFrame = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
     framesView.selectedDepth = 3; // oldest frame
     yield updatedFrame;
     checkView(3, 0, 5, [/firstCall/, 118]);
 
     let updatedView = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
--- a/browser/devtools/debugger/test/browser_dbg_editor-contextmenu.js
+++ b/browser/devtools/debugger/test/browser_dbg_editor-contextmenu.js
@@ -15,17 +15,17 @@ function test() {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gContextMenu = gDebugger.document.getElementById("sourceEditorContextMenu");
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest).then(null, info);
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1).then(performTest).then(null, info);
     gDebuggee.firstCall();
   });
 
   function performTest() {
     is(gDebugger.gThreadClient.state, "paused",
       "Should only be getting stack frames while paused.");
     is(gSources.itemCount, 2,
       "Found the expected number of sources.");
--- a/browser/devtools/debugger/test/browser_dbg_editor-mode.js
+++ b/browser/devtools/debugger/test/browser_dbg_editor-mode.js
@@ -15,17 +15,17 @@ function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
 
-    waitForSourceAndCaretAndScopes(gPanel, "code_test-editor-mode", 5)
+    waitForSourceAndCaretAndScopes(gPanel, "code_test-editor-mode", 1)
       .then(testInitialSource)
       .then(testSwitch1)
       .then(testSwitch2)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
--- a/browser/devtools/debugger/test/browser_dbg_scripts-switching-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-switching-01.js
@@ -17,17 +17,17 @@ function test() {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
 
     ok(gDebugger.document.title.endsWith(EXAMPLE_URL + gLabel1),
       "Title with first source is correct.");
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(testSourcesDisplay)
       .then(testSwitchPaused1)
       .then(testSwitchPaused2)
       .then(testSwitchRunning)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
@@ -70,25 +70,25 @@ function testSourcesDisplay() {
   is(gEditor.getText().search(/firstCall/), -1,
     "The first source is not displayed.");
   is(gEditor.getText().search(/debugger/), 172,
     "The second source is displayed.");
 
   ok(gDebugger.document.title.endsWith(EXAMPLE_URL + gLabel2),
     "Title with second source is correct.");
 
-  ok(isCaretPos(gPanel, 6),
+  ok(isCaretPos(gPanel, 1),
     "Editor caret location is correct.");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
-    is(gEditor.getDebugLocation(), 5,
+    is(gEditor.getDebugLocation(), 0,
       "Editor debugger location is correct.");
-    ok(gEditor.hasLineClass(5, "debug-line"),
-      "The debugged line is highlighted appropriately.");
+    ok(gEditor.hasLineClass(0, "debug-line"),
+      "The debugged line is highlighted appropriately (1).");
 
     waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
     gSources.selectedIndex = 0;
   });
 
   return deferred.promise;
 }
 
@@ -131,22 +131,22 @@ function testSwitchPaused2() {
 
   is(gEditor.getText().search(/firstCall/), -1,
     "The first source is not displayed.");
   is(gEditor.getText().search(/debugger/), 172,
     "The second source is displayed.");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
-    ok(isCaretPos(gPanel, 6),
+    ok(isCaretPos(gPanel, 1),
       "Editor caret location is correct.");
-    is(gEditor.getDebugLocation(), 5,
+    is(gEditor.getDebugLocation(), 0,
       "Editor debugger location is correct.");
-    ok(gEditor.hasLineClass(5, "debug-line"),
-      "The debugged line is highlighted appropriately.");
+    ok(gEditor.hasLineClass(0, "debug-line"),
+      "The debugged line is highlighted appropriately (2).");
 
     // Step out three times.
     waitForThreadEvents(gPanel, "paused").then(() => {
       waitForThreadEvents(gPanel, "paused").then(() => {
         waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
         gDebugger.gThreadClient.stepOut();
       });
       gDebugger.gThreadClient.stepOut();
@@ -167,22 +167,22 @@ function testSwitchRunning() {
 
   is(gEditor.getText().search(/firstCall/), 118,
     "The first source is displayed.");
   is(gEditor.getText().search(/debugger/), -1,
     "The second source is not displayed.");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
-    ok(isCaretPos(gPanel, 5),
+    ok(isCaretPos(gPanel, 1),
       "Editor caret location is correct.");
-    is(gEditor.getDebugLocation(), 4,
+    is(gEditor.getDebugLocation(), 0,
       "Editor debugger location is correct.");
-    ok(gEditor.hasLineClass(4, "debug-line"),
-      "The debugged line is highlighted appropriately.");
+    ok(gEditor.hasLineClass(0, "debug-line"),
+      "The debugged line is highlighted appropriately (3).");
 
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
 registerCleanupFunction(function() {
--- a/browser/devtools/debugger/test/browser_dbg_scripts-switching-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-switching-02.js
@@ -14,17 +14,17 @@ function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(testSourcesDisplay)
       .then(testSwitchPaused1)
       .then(testSwitchPaused2)
       .then(testSwitchRunning)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
@@ -58,24 +58,24 @@ function testSourcesDisplay() {
   is(gSources.selectedValue, EXAMPLE_URL + gLabel2 + gParams,
     "The selected value is the sources pane is incorrect.");
 
   is(gEditor.getText().search(/firstCall/), -1,
     "The first source is not displayed.");
   is(gEditor.getText().search(/debugger/), 172,
     "The second source is displayed.");
 
-  ok(isCaretPos(gPanel, 6),
+  ok(isCaretPos(gPanel, 1),
     "Editor caret location is correct.");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
-    is(gEditor.getDebugLocation(), 5,
+    is(gEditor.getDebugLocation(), 0,
       "Editor debugger location is correct.");
-    ok(gEditor.hasLineClass(5, "debug-line"),
+    ok(gEditor.hasLineClass(0, "debug-line"),
       "The debugged line is highlighted appropriately.");
 
     waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
     gSources.selectedItem = e => e.attachment.label == gLabel1;
   });
 
   return deferred.promise;
 }
@@ -120,21 +120,21 @@ function testSwitchPaused2() {
 
   is(gEditor.getText().search(/firstCall/), -1,
     "The first source is not displayed.");
   is(gEditor.getText().search(/debugger/), 172,
     "The second source is displayed.");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
-    ok(isCaretPos(gPanel, 6),
+    ok(isCaretPos(gPanel, 1),
       "Editor caret location is correct.");
-    is(gEditor.getDebugLocation(), 5,
+    is(gEditor.getDebugLocation(), 0,
       "Editor debugger location is correct.");
-    ok(gEditor.hasLineClass(5, "debug-line"),
+    ok(gEditor.hasLineClass(0, "debug-line"),
       "The debugged line is highlighted appropriately.");
 
     // Step out three times.
     waitForThreadEvents(gPanel, "paused").then(() => {
       waitForThreadEvents(gPanel, "paused").then(() => {
         waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
         gDebugger.gThreadClient.stepOut();
       });
@@ -156,21 +156,21 @@ function testSwitchRunning() {
 
   is(gEditor.getText().search(/firstCall/), 118,
     "The first source is displayed.");
   is(gEditor.getText().search(/debugger/), -1,
     "The second source is not displayed.");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
-    ok(isCaretPos(gPanel, 5),
+    ok(isCaretPos(gPanel, 1),
       "Editor caret location is correct.");
-    is(gEditor.getDebugLocation(), 4,
+    is(gEditor.getDebugLocation(), 0,
       "Editor debugger location is correct.");
-    ok(gEditor.hasLineClass(4, "debug-line"),
+    ok(gEditor.hasLineClass(0, "debug-line"),
       "The debugged line is highlighted appropriately.");
 
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-basic-02.js
@@ -14,17 +14,17 @@ function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(performSimpleSearch)
       .then(() => verifySourceAndCaret("-01.js", 1, 1, [1, 1]))
       .then(combineWithLineSearch)
       .then(() => verifySourceAndCaret("-01.js", 2, 1, [53, 53]))
       .then(combineWithTokenSearch)
       .then(() => verifySourceAndCaret("-01.js", 2, 48, [96, 100]))
       .then(combineWithTokenColonSearch)
       .then(() => verifySourceAndCaret("-01.js", 2, 11, [56, 63]))
@@ -35,17 +35,17 @@ function test() {
 
     gDebuggee.firstCall();
   });
 }
 
 function performSimpleSearch() {
   let finished = promise.all([
     ensureSourceIs(gPanel, "-02.js"),
-    ensureCaretAt(gPanel, 6),
+    ensureCaretAt(gPanel, 1),
     waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FILE_SEARCH_MATCH_FOUND),
     waitForSourceShown(gPanel, "-01.js")
   ]);
 
   setText(gSearchBox, "1");
 
   return finished.then(() => promise.all([
     ensureSourceIs(gPanel, "-01.js"),
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-basic-03.js
@@ -15,17 +15,17 @@ function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(performFileSearch)
       .then(escapeAndHide)
       .then(escapeAndClear)
       .then(() => verifySourceAndCaret("-01.js", 1, 1))
       .then(performFunctionSearch)
       .then(escapeAndHide)
       .then(escapeAndClear)
       .then(() => verifySourceAndCaret("-01.js", 4, 10))
@@ -39,17 +39,17 @@ function test() {
 
     gDebuggee.firstCall();
   });
 }
 
 function performFileSearch() {
   let finished = promise.all([
     ensureSourceIs(gPanel, "-02.js"),
-    ensureCaretAt(gPanel, 6),
+    ensureCaretAt(gPanel, 1),
     once(gDebugger, "popupshown"),
     waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FILE_SEARCH_MATCH_FOUND),
     waitForSourceShown(gPanel, "-01.js")
   ]);
 
   setText(gSearchBox, ".");
 
   return finished.then(() => promise.all([
--- a/browser/devtools/debugger/test/browser_dbg_search-global-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-01.js
@@ -17,17 +17,17 @@ function test() {
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(firstSearch)
       .then(secondSearch)
       .then(clearSearch)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
@@ -47,17 +47,17 @@ function firstSearch() {
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
     // to avoid blocking UI, thus giving the impression of faster searching.
     executeSoon(() => {
       info("Current source url:\n" + gSources.selectedValue);
       info("Debugger editor text:\n" + gEditor.getText());
 
-      ok(isCaretPos(gPanel, 6),
+      ok(isCaretPos(gPanel, 1),
         "The editor shouldn't have jumped to a matching line yet.");
       ok(gSources.selectedValue.contains("-02.js"),
         "The current source shouldn't have changed after a global search.");
       is(gSources.visibleItems.length, 2,
         "Not all the sources are shown after the global search.");
 
       let sourceResults = gDebugger.document.querySelectorAll(".dbg-source-results");
       is(sourceResults.length, 2,
@@ -161,17 +161,17 @@ function secondSearch() {
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
     // to avoid blocking UI, thus giving the impression of faster searching.
     executeSoon(() => {
       info("Current source url:\n" + gSources.selectedValue);
       info("Debugger editor text:\n" + gEditor.getText());
 
-      ok(isCaretPos(gPanel, 6),
+      ok(isCaretPos(gPanel, 1),
         "The editor shouldn't have jumped to a matching line yet.");
       ok(gSources.selectedValue.contains("-02.js"),
         "The current source shouldn't have changed after a global search.");
       is(gSources.visibleItems.length, 2,
         "Not all the sources are shown after the global search.");
 
       let sourceResults = gDebugger.document.querySelectorAll(".dbg-source-results");
       is(sourceResults.length, 2,
--- a/browser/devtools/debugger/test/browser_dbg_search-global-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-02.js
@@ -17,17 +17,17 @@ function test() {
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(firstSearch)
       .then(doFirstJump)
       .then(doSecondJump)
       .then(doWrapAroundJump)
       .then(doBackwardsWrapAroundJump)
       .then(testSearchTokenEmpty)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
@@ -50,17 +50,17 @@ function firstSearch() {
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
     // to avoid blocking UI, thus giving the impression of faster searching.
     executeSoon(() => {
       info("Current source url:\n" + gSources.selectedValue);
       info("Debugger editor text:\n" + gEditor.getText());
 
-      ok(isCaretPos(gPanel, 6),
+      ok(isCaretPos(gPanel, 1),
         "The editor shouldn't have jumped to a matching line yet.");
       ok(gSources.selectedValue.contains("-02.js"),
         "The current source shouldn't have changed after a global search.");
       is(gSources.visibleItems.length, 2,
         "Not all the sources are shown after the global search.");
 
       deferred.resolve();
     });
@@ -69,17 +69,17 @@ function firstSearch() {
   setText(gSearchBox, "!eval");
 
   return deferred.promise;
 }
 
 function doFirstJump() {
   let deferred = promise.defer();
 
-  waitForSourceAndCaret(gPanel, "-01.js", 5).then(() => {
+  waitForSourceAndCaret(gPanel, "-01.js", 1).then(() => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
     ok(gSources.selectedValue.contains("-01.js"),
       "The currently shown source is incorrect (1).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (1).");
 
@@ -97,17 +97,17 @@ function doFirstJump() {
   EventUtils.sendKey("DOWN", gDebugger);
 
   return deferred.promise;
 }
 
 function doSecondJump() {
   let deferred = promise.defer();
 
-  waitForSourceAndCaret(gPanel, "-02.js", 6).then(() => {
+  waitForSourceAndCaret(gPanel, "-02.js", 1).then(() => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
     ok(gSources.selectedValue.contains("-02.js"),
       "The currently shown source is incorrect (2).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (2).");
 
@@ -125,17 +125,17 @@ function doSecondJump() {
   EventUtils.sendKey("DOWN", gDebugger);
 
   return deferred.promise;
 }
 
 function doWrapAroundJump() {
   let deferred = promise.defer();
 
-  waitForSourceAndCaret(gPanel, "-01.js", 5).then(() => {
+  waitForSourceAndCaret(gPanel, "-01.js", 1).then(() => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
     ok(gSources.selectedValue.contains("-01.js"),
       "The currently shown source is incorrect (3).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (3).");
 
@@ -153,17 +153,17 @@ function doWrapAroundJump() {
   EventUtils.sendKey("DOWN", gDebugger);
 
   return deferred.promise;
 }
 
 function doBackwardsWrapAroundJump() {
   let deferred = promise.defer();
 
-  waitForSourceAndCaret(gPanel, "-02.js", 6).then(() => {
+  waitForSourceAndCaret(gPanel, "-02.js", 1).then(() => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
     ok(gSources.selectedValue.contains("-02.js"),
       "The currently shown source is incorrect (4).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (4).");
 
--- a/browser/devtools/debugger/test/browser_dbg_search-global-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-03.js
@@ -17,17 +17,17 @@ function test() {
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(firstSearch)
       .then(performTest)
       .then(() => closeDebuggerAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
     gDebuggee.firstCall();
@@ -46,17 +46,17 @@ function firstSearch() {
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
     // to avoid blocking UI, thus giving the impression of faster searching.
     executeSoon(() => {
       info("Current source url:\n" + gSources.selectedValue);
       info("Debugger editor text:\n" + gEditor.getText());
 
-      ok(isCaretPos(gPanel, 6),
+      ok(isCaretPos(gPanel, 1),
         "The editor shouldn't have jumped to a matching line yet.");
       ok(gSources.selectedValue.contains("-02.js"),
         "The current source shouldn't have changed after a global search.");
       is(gSources.visibleItems.length, 2,
         "Not all the sources are shown after the global search.");
 
       deferred.resolve();
     });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-04.js
@@ -17,17 +17,17 @@ function test() {
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(firstSearch)
       .then(secondSearch)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
     gDebuggee.firstCall();
@@ -39,17 +39,17 @@ function firstSearch() {
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
     // to avoid blocking UI, thus giving the impression of faster searching.
     executeSoon(() => {
       info("Current source url:\n" + gSources.selectedValue);
       info("Debugger editor text:\n" + gEditor.getText());
 
-      ok(isCaretPos(gPanel, 6),
+      ok(isCaretPos(gPanel, 1),
         "The editor shouldn't have jumped to a matching line yet.");
       ok(gSources.selectedValue.contains("-02.js"),
         "The current source shouldn't have changed after a global search.");
       is(gSources.visibleItems.length, 2,
         "Not all the sources are shown after the global search.");
 
       deferred.resolve();
     });
@@ -62,17 +62,17 @@ function firstSearch() {
 
 function secondSearch() {
   let deferred = promise.defer();
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_NOT_FOUND, () => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
-    ok(isCaretPos(gPanel, 6),
+    ok(isCaretPos(gPanel, 1),
       "The editor shouldn't have jumped to a matching line yet.");
     ok(gSources.selectedValue.contains("-02.js"),
       "The current source shouldn't have changed after a global search.");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search.");
 
     deferred.resolve();
   });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-05.js
@@ -18,17 +18,17 @@ function test() {
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(doSearch)
       .then(testExpandCollapse)
       .then(testClickLineToJump)
       .then(testClickMatchToJump)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
@@ -42,17 +42,17 @@ function doSearch() {
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
     // to avoid blocking UI, thus giving the impression of faster searching.
     executeSoon(() => {
       info("Current source url:\n" + gSources.selectedValue);
       info("Debugger editor text:\n" + gEditor.getText());
 
-      ok(isCaretPos(gPanel, 6),
+      ok(isCaretPos(gPanel, 1),
         "The editor shouldn't have jumped to a matching line yet.");
       ok(gSources.selectedValue.contains("-02.js"),
         "The current source shouldn't have changed after a global search.");
       is(gSources.visibleItems.length, 2,
         "Not all the sources are shown after the global search.");
 
       deferred.resolve();
     });
@@ -89,23 +89,23 @@ function testExpandCollapse() {
 
 function testClickLineToJump() {
   let deferred = promise.defer();
 
   let sourceResults = gDebugger.document.querySelectorAll(".dbg-source-results");
   let firstHeader = sourceResults[0].querySelector(".dbg-results-header");
   let firstLine = sourceResults[0].querySelector(".dbg-results-line-contents");
 
-  waitForSourceAndCaret(gPanel, "-01.js", 1, 5).then(() => {
+  waitForSourceAndCaret(gPanel, "-01.js", 1, 1).then(() => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
-    ok(isCaretPos(gPanel, 1, 5),
+    ok(isCaretPos(gPanel, 1, 1),
       "The editor didn't jump to the correct line (1).");
-    is(gEditor.getSelection(), "A",
+    is(gEditor.getSelection(), "",
       "The editor didn't select the correct text (1).");
     ok(gSources.selectedValue.contains("-01.js"),
       "The currently shown source is incorrect (1).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (1).");
 
     deferred.resolve();
   });
@@ -118,23 +118,23 @@ function testClickLineToJump() {
 function testClickMatchToJump() {
   let deferred = promise.defer();
 
   let sourceResults = gDebugger.document.querySelectorAll(".dbg-source-results");
   let secondHeader = sourceResults[1].querySelector(".dbg-results-header");
   let secondMatches = sourceResults[1].querySelectorAll(".dbg-results-line-contents-string[match=true]");
   let lastMatch = Array.slice(secondMatches).pop();
 
-  waitForSourceAndCaret(gPanel, "-02.js", 6, 6).then(() => {
+  waitForSourceAndCaret(gPanel, "-02.js", 1, 1).then(() => {
     info("Current source url:\n" + gSources.selectedValue);
     info("Debugger editor text:\n" + gEditor.getText());
 
-    ok(isCaretPos(gPanel, 6, 6),
+    ok(isCaretPos(gPanel, 1, 1),
       "The editor didn't jump to the correct line (2).");
-    is(gEditor.getSelection(), "a",
+    is(gEditor.getSelection(), "",
       "The editor didn't select the correct text (2).");
     ok(gSources.selectedValue.contains("-02.js"),
       "The currently shown source is incorrect (2).");
     is(gSources.visibleItems.length, 2,
       "Not all the sources are shown after the global search (2).");
 
     deferred.resolve();
   });
--- a/browser/devtools/debugger/test/browser_dbg_search-global-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_search-global-06.js
@@ -17,17 +17,17 @@ function test() {
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gSearchView = gDebugger.DebuggerView.GlobalSearch;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(doSearch)
       .then(testFocusLost)
       .then(doSearch)
       .then(testEscape)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
@@ -48,17 +48,17 @@ function doSearch() {
 
   gDebugger.once(gDebugger.EVENTS.GLOBAL_SEARCH_MATCH_FOUND, () => {
     // Some operations are synchronously dispatched on the main thread,
     // to avoid blocking UI, thus giving the impression of faster searching.
     executeSoon(() => {
       info("Current source url:\n" + gSources.selectedValue);
       info("Debugger editor text:\n" + gEditor.getText());
 
-      ok(isCaretPos(gPanel, 6),
+      ok(isCaretPos(gPanel, 1),
         "The editor shouldn't have jumped to a matching line yet.");
       ok(gSources.selectedValue.contains("-02.js"),
         "The current source shouldn't have changed after a global search.");
       is(gSources.visibleItems.length, 2,
         "Not all the sources are shown after the global search.");
 
       deferred.resolve();
     });
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js
@@ -15,17 +15,17 @@ function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
     gSearchBoxPanel = gDebugger.DebuggerView.Filtering._searchboxHelpPanel;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(showPopup)
       .then(hidePopup)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
     gDebuggee.firstCall();
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js
@@ -20,17 +20,17 @@ function test() {
     gEditor = gDebugger.DebuggerView.editor;
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
     gSearchBoxPanel = gDebugger.DebuggerView.Filtering._searchboxHelpPanel;
 
     once(gSearchBoxPanel, "popupshown").then(() => {
       ok(false, "Damn it, this shouldn't have happened.");
     });
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(tryShowPopup)
       .then(focusEditor)
       .then(testFocusLost)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
@@ -59,19 +59,19 @@ function focusEditor() {
   // Focusing the editor takes a tick to update the caret and selection.
   gEditor.focus();
   executeSoon(deferred.resolve);
 
   return deferred.promise;
 }
 
 function testFocusLost() {
-  ok(isCaretPos(gPanel, 6, 1),
+  ok(isCaretPos(gPanel, 1, 1),
     "The editor caret position appears to be correct after gaining focus.");
-  ok(isEditorSel(gPanel, [165, 165]),
+  ok(isEditorSel(gPanel, [1, 1]),
     "The editor selection appears to be correct after gaining focus.");
   is(gEditor.getSelection(), "",
     "The editor selected text appears to be correct after gaining focus.");
 
   is(gSearchBoxPanel.state, "closed",
     "The search box panel should still not be visible.");
 }
 
--- a/browser/devtools/debugger/test/browser_dbg_stack-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-02.js
@@ -14,17 +14,17 @@ function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
 
-    waitForSourceAndCaretAndScopes(gPanel, ".html", 18).then(performTest);
+    waitForSourceAndCaretAndScopes(gPanel, ".html", 1).then(performTest);
     gDebuggee.evalCall();
   });
 }
 
 function performTest() {
   is(gDebugger.gThreadClient.state, "paused",
     "Should only be getting stack frames while paused.");
   is(gFrames.itemCount, 2,
--- a/browser/devtools/debugger/test/browser_dbg_stack-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-04.js
@@ -14,17 +14,17 @@ function test() {
   initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
 
-    waitForSourceAndCaretAndScopes(gPanel, ".html", 18).then(performTest);
+    waitForSourceAndCaretAndScopes(gPanel, ".html", 1).then(performTest);
     gDebuggee.evalCall();
   });
 }
 
 function performTest() {
   is(gDebugger.gThreadClient.state, "paused",
     "Should only be getting stack frames while paused.");
   is(gFrames.itemCount, 2,
--- a/browser/devtools/debugger/test/browser_dbg_stack-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-05.js
@@ -17,17 +17,17 @@ function test() {
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
       .then(initialChecks)
       .then(testNewestTwoFrames)
       .then(testOldestTwoFrames)
       .then(testAfterResume)
       .then(() => closeDebuggerAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
@@ -49,79 +49,79 @@ function testNewestTwoFrames() {
   let deferred = promise.defer();
 
   is(gFrames.selectedIndex, 3,
     "Newest frame should be selected by default.");
   is(gClassicFrames.selectedIndex, 0,
     "Newest frame should be selected in the mirrored view as well.");
   is(gSources.selectedIndex, 1,
     "The second source is selected in the widget.");
-  ok(isCaretPos(gPanel, 6),
-    "Editor caret location is correct.");
+  ok(isCaretPos(gPanel, 1),
+    "Editor caret location is correct (1).");
 
   // The editor's debug location takes a tick to update.
   executeSoon(() => {
-    is(gEditor.getDebugLocation(), 5,
+    is(gEditor.getDebugLocation(), 0,
       "Editor debug location is correct.");
 
     EventUtils.sendMouseEvent({ type: "mousedown" },
       gFrames.getItemAtIndex(2).target,
       gDebugger);
 
     is(gFrames.selectedIndex, 2,
       "Third frame should be selected after click.");
     is(gClassicFrames.selectedIndex, 1,
       "Third frame should be selected in the mirrored view as well.");
     is(gSources.selectedIndex, 1,
       "The second source is still selected in the widget.");
     ok(isCaretPos(gPanel, 6),
-      "Editor caret location is correct.");
+      "Editor caret location is correct (2).");
 
     // The editor's debug location takes a tick to update.
     executeSoon(() => {
       is(gEditor.getDebugLocation(), 5,
         "Editor debug location is correct.");
 
       deferred.resolve();
     });
   });
 
   return deferred.promise;
 }
 
 function testOldestTwoFrames() {
   let deferred = promise.defer();
 
-  waitForSourceAndCaret(gPanel, "-01.js", 5).then(waitForTick).then(() => {
+  waitForSourceAndCaret(gPanel, "-01.js", 1).then(waitForTick).then(() => {
     is(gFrames.selectedIndex, 1,
       "Second frame should be selected after click.");
     is(gClassicFrames.selectedIndex, 2,
       "Second frame should be selected in the mirrored view as well.");
     is(gSources.selectedIndex, 0,
       "The first source is now selected in the widget.");
-    ok(isCaretPos(gPanel, 5),
-      "Editor caret location is correct.");
+    ok(isCaretPos(gPanel, 1),
+      "Editor caret location is correct (3).");
 
     // The editor's debug location takes a tick to update.
     executeSoon(() => {
-      is(gEditor.getDebugLocation(), 4,
+      is(gEditor.getDebugLocation(), 0,
         "Editor debug location is correct.");
 
       EventUtils.sendMouseEvent({ type: "mousedown" },
         gFrames.getItemAtIndex(0).target,
         gDebugger);
 
       is(gFrames.selectedIndex, 0,
         "Oldest frame should be selected after click.");
       is(gClassicFrames.selectedIndex, 3,
         "Oldest frame should be selected in the mirrored view as well.");
       is(gSources.selectedIndex, 0,
         "The first source is still selected in the widget.");
       ok(isCaretPos(gPanel, 5),
-        "Editor caret location is correct.");
+        "Editor caret location is correct (4).");
 
       // The editor's debug location takes a tick to update.
       executeSoon(() => {
         is(gEditor.getDebugLocation(), 4,
           "Editor debug location is correct.");
 
         deferred.resolve();
       });
--- a/browser/devtools/debugger/test/browser_dbg_stack-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-06.js
@@ -17,46 +17,46 @@ function test() {
     gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1).then(performTest);
     gDebuggee.firstCall();
   });
 }
 
 function performTest() {
   is(gFrames.selectedIndex, 3,
     "Newest frame should be selected by default.");
   is(gClassicFrames.selectedIndex, 0,
     "Newest frame should also be selected in the mirrored view.");
   is(gSources.selectedIndex, 1,
     "The second source is selected in the widget.");
   is(gEditor.getText().search(/firstCall/), -1,
     "The first source is not displayed.");
   is(gEditor.getText().search(/debugger/), 172,
     "The second source is displayed.");
 
-  waitForSourceAndCaret(gPanel, "-01.js", 6).then(waitForTick).then(() => {
+  waitForSourceAndCaret(gPanel, "-01.js", 1).then(waitForTick).then(() => {
     is(gFrames.selectedIndex, 0,
       "Oldest frame should be selected after click.");
     is(gClassicFrames.selectedIndex, 3,
       "Oldest frame should also be selected in the mirrored view.");
     is(gSources.selectedIndex, 0,
       "The first source is now selected in the widget.");
     is(gEditor.getText().search(/firstCall/), 118,
       "The first source is displayed.");
     is(gEditor.getText().search(/debugger/), -1,
       "The second source is not displayed.");
 
-    waitForSourceAndCaret(gPanel, "-02.js", 6).then(waitForTick).then(() => {
+    waitForSourceAndCaret(gPanel, "-02.js", 1).then(waitForTick).then(() => {
       is(gFrames.selectedIndex, 3,
         "Newest frame should be selected again after click.");
       is(gClassicFrames.selectedIndex, 0,
         "Newest frame should also be selected again in the mirrored view.");
       is(gSources.selectedIndex, 1,
         "The second source is selected in the widget.");
       is(gEditor.getText().search(/firstCall/), -1,
         "The first source is not displayed.");
--- a/browser/devtools/debugger/test/browser_dbg_stack-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-07.js
@@ -19,25 +19,25 @@ function test() {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gEditor = gDebugger.DebuggerView.editor;
     gSources = gDebugger.DebuggerView.Sources;
     gFrames = gDebugger.DebuggerView.StackFrames;
     gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
     gToolbar = gDebugger.DebuggerView.Toolbar;
 
-    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
+    waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1).then(performTest);
     gDebuggee.firstCall();
   });
 }
 
 function performTest() {
   return Task.spawn(function() {
     yield selectBottomFrame();
-    testBottomFrame(5);
+    testBottomFrame(0);
 
     yield performStep("StepOver");
     testTopFrame(3);
 
     yield selectBottomFrame();
     testBottomFrame(4);
 
     yield performStep("StepIn");
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -295,30 +295,37 @@ EvalKernel(JSContext *cx, const CallArgs
         return ejr == EvalJSON_Success;
 
     EvalScriptGuard esg(cx);
 
     if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
         esg.lookupInEvalCache(flatStr, callerScript, pc);
 
     if (!esg.foundScript()) {
+        JSScript *script;
         unsigned lineno;
         const char *filename;
         JSPrincipals *originPrincipals;
-        CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
+        uint32_t pcOffset;
+        CurrentScriptFileLineOrigin(cx, &script, &filename, &lineno, &pcOffset, &originPrincipals,
                                     evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL
                                                             : NOT_CALLED_FROM_JSOP_EVAL);
 
+        const char *introducerFilename = filename;
+        if (script && script->scriptSource()->introducerFilename())
+            introducerFilename = script->scriptSource()->introducerFilename();
+
         CompileOptions options(cx);
-        options.setFileAndLine(filename, lineno)
+        options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setPrincipals(principals)
-               .setOriginPrincipals(originPrincipals);
+               .setOriginPrincipals(originPrincipals)
+               .setIntroductionInfo(introducerFilename, "eval", lineno, pcOffset);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                      scopeobj, callerScript, options,
                                                      chars.get(), length, flatStr, staticLevel);
         if (!compiled)
             return false;
 
         MarkFunctionsWithinEvalScript(compiled);
 
@@ -361,29 +368,36 @@ js::DirectEvalStringFromIon(JSContext *c
     EvalScriptGuard esg(cx);
 
     // Ion will not perform cross compartment direct eval calls.
     JSPrincipals *principals = cx->compartment()->principals;
 
     esg.lookupInEvalCache(flatStr, callerScript, pc);
 
     if (!esg.foundScript()) {
+        JSScript *script;
+        const char *filename;
         unsigned lineno;
-        const char *filename;
         JSPrincipals *originPrincipals;
-        CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
-                                    CALLED_FROM_JSOP_EVAL);
+        uint32_t pcOffset;
+        CurrentScriptFileLineOrigin(cx, &script, &filename, &lineno, &pcOffset,
+                                    &originPrincipals, CALLED_FROM_JSOP_EVAL);
+
+        const char *introducerFilename = filename;
+        if (script && script->scriptSource()->introducerFilename())
+            introducerFilename = script->scriptSource()->introducerFilename();
 
         CompileOptions options(cx);
-        options.setFileAndLine(filename, lineno)
+        options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setPrincipals(principals)
-               .setOriginPrincipals(originPrincipals);
+               .setOriginPrincipals(originPrincipals)
+               .setIntroductionInfo(introducerFilename, "eval", lineno, pcOffset);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                      scopeobj, callerScript, options,
                                                      chars.get(), length, flatStr, staticLevel);
         if (!compiled)
             return false;
 
         MarkFunctionsWithinEvalScript(compiled);
 
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -159,16 +159,37 @@ frontend::MaybeCallSourceHandler(JSConte
 
     if (listener) {
         void *listenerTSData;
         listener(options.filename(), options.lineno, chars, length,
                  &listenerTSData, listenerData);
     }
 }
 
+static bool
+SetScriptSourceFilename(ExclusiveContext *cx, ScriptSource *ss,
+                        const ReadOnlyCompileOptions &options)
+{
+    if (options.hasIntroductionInfo) {
+        const char *filename = options.filename() ? options.filename() : "<unknown>";
+        JS_ASSERT(options.introducer != nullptr);
+
+        if (!ss->setIntroducedFilename(cx, filename, options.introductionLineno,
+                                       options.introducer, options.introducerFilename()))
+            return false;
+
+        ss->setIntroductionOffset(options.introductionOffset);
+    } else {
+        if (options.filename() && !ss->setFilename(cx, options.filename()))
+            return false;
+    }
+
+    return true;
+}
+
 JSScript *
 frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain,
                         HandleScript evalCaller,
                         const ReadOnlyCompileOptions &options,
                         const jschar *chars, size_t length,
                         JSString *source_ /* = nullptr */,
                         unsigned staticLevel /* = 0 */,
                         SourceCompressionTask *extraSct /* = nullptr */)
@@ -195,17 +216,18 @@ frontend::CompileScript(ExclusiveContext
     JS_ASSERT_IF(staticLevel != 0, evalCaller);
 
     if (!CheckLength(cx, length))
         return nullptr;
     JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE);
     ScriptSource *ss = cx->new_<ScriptSource>(options.originPrincipals());
     if (!ss)
         return nullptr;
-    if (options.filename() && !ss->setFilename(cx, options.filename()))
+
+    if (!SetScriptSourceFilename(cx, ss, options))
         return nullptr;
 
     RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss, options));
     if (!sourceObject)
         return nullptr;
 
     SourceCompressionTask mysct(cx);
     SourceCompressionTask *sct = extraSct ? extraSct : &mysct;
@@ -490,17 +512,17 @@ CompileFunctionBody(JSContext *cx, Mutab
 
     MaybeCallSourceHandler(cx, options, chars, length);
 
     if (!CheckLength(cx, length))
         return false;
     ScriptSource *ss = cx->new_<ScriptSource>(options.originPrincipals());
     if (!ss)
         return false;
-    if (options.filename() && !ss->setFilename(cx, options.filename()))
+    if (!SetScriptSourceFilename(cx, ss, options))
         return false;
     RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss, options));
     if (!sourceObject)
         return false;
     SourceCompressionTask sct(cx);
     JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE);
     if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) {
         if (!ss->setSourceCopy(cx, chars, length, true, &sct))
--- a/js/src/jit-test/tests/basic/spread-call-eval.js
+++ b/js/src/jit-test/tests/basic/spread-call-eval.js
@@ -15,17 +15,17 @@ assertEq(eval(...["a + b"]), 11);
 with ({ a: 30 }) {
   assertEq(eval(...["a + b"]), 31);
 }
 
 let line0 = Error().lineNumber;
 try {             // line0 + 1
   eval(...["("]); // line0 + 2
 } catch (e) {
-  assertEq(e.lineNumber, line0 + 2);
+  assertEq(e.lineNumber, 1);
 }
 
 // other iterable objects
 assertEq(eval(...["a + b"][std_iterator]()), 11);
 assertEq(eval(...Set(["a + b"])), 11);
 let itr = {};
 itr[std_iterator] = function() {
     return {
--- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js
+++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js
@@ -1,17 +1,17 @@
 // getColumnOffsets correctly places the various parts of a ForStatement.
 
 var global = newGlobal();
 Debugger(global).onDebuggerStatement = function (frame) {
     var script = frame.eval("f").return.script;
     script.getAllColumnOffsets().forEach(function (offset) {
         script.setBreakpoint(offset.offset, {
             hit: function (frame) {
-                assertEq(offset.lineNumber, 17);
+                assertEq(offset.lineNumber, 1);
                 global.log += offset.columnNumber + " ";
             }
         });
     });
 };
 
 global.log = '';
 global.eval("function f(n) { for (var i = 0; i < n; ++i) log += '. '; log += '! '; } debugger;");
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4303,16 +4303,20 @@ JS::ReadOnlyCompileOptions::copyPODOptio
     noScriptRval = rhs.noScriptRval;
     selfHostingMode = rhs.selfHostingMode;
     canLazilyParse = rhs.canLazilyParse;
     strictOption = rhs.strictOption;
     extraWarningsOption = rhs.extraWarningsOption;
     werrorOption = rhs.werrorOption;
     asmJSOption = rhs.asmJSOption;
     sourcePolicy = rhs.sourcePolicy;
+    introducer = rhs.introducer;
+    introductionLineno = rhs.introductionLineno;
+    introductionOffset = rhs.introductionOffset;
+    hasIntroductionInfo = rhs.hasIntroductionInfo;
 }
 
 JSPrincipals *
 JS::ReadOnlyCompileOptions::originPrincipals() const
 {
     return NormalizeOriginPrincipals(principals_, originPrincipals_);
 }
 
@@ -4329,29 +4333,31 @@ JS::OwningCompileOptions::~OwningCompile
     if (principals_)
         JS_DropPrincipals(runtime, principals_);
     if (originPrincipals_)
         JS_DropPrincipals(runtime, originPrincipals_);
 
     // OwningCompileOptions always owns these, so these casts are okay.
     js_free(const_cast<char *>(filename_));
     js_free(const_cast<jschar *>(sourceMapURL_));
+    js_free(const_cast<char *>(introducerFilename_));
 }
 
 bool
 JS::OwningCompileOptions::copy(JSContext *cx, const ReadOnlyCompileOptions &rhs)
 {
     copyPODOptions(rhs);
 
     setPrincipals(rhs.principals());
     setOriginPrincipals(rhs.originPrincipals());
     setElement(rhs.element());
 
     return (setFileAndLine(cx, rhs.filename(), rhs.lineno) &&
-            setSourceMapURL(cx, rhs.sourceMapURL()));
+            setSourceMapURL(cx, rhs.sourceMapURL()) &&
+            setIntroducerFilename(cx, rhs.introducerFilename()));
 }
 
 bool
 JS::OwningCompileOptions::setFile(JSContext *cx, const char *f)
 {
     char *copy = nullptr;
     if (f) {
         copy = JS_strdup(cx, f);
@@ -4389,16 +4395,33 @@ JS::OwningCompileOptions::setSourceMapUR
     // OwningCompileOptions always owns sourceMapURL_, so this cast is okay.
     js_free(const_cast<jschar *>(sourceMapURL_));
 
     sourceMapURL_ = copy;
     return true;
 }
 
 bool
+JS::OwningCompileOptions::setIntroducerFilename(JSContext *cx, const char *s)
+{
+    char *copy = nullptr;
+    if (s) {
+        copy = JS_strdup(cx, s);
+        if (!copy)
+            return false;
+    }
+
+    // OwningCompileOptions always owns introducerFilename_, so this cast is okay.
+    js_free(const_cast<char *>(introducerFilename_));
+
+    introducerFilename_ = copy;
+    return true;
+}
+
+bool
 JS::OwningCompileOptions::wrap(JSContext *cx, JSCompartment *compartment)
 {
     if (!compartment->wrap(cx, &elementRoot))
         return false;
     if (elementAttributeNameRoot) {
         if (!compartment->wrap(cx, elementAttributeNameRoot.address()))
             return false;
     }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3444,55 +3444,62 @@ namespace JS {
  * CompileOptions and OwningCompileOptions.
  */
 class JS_FRIEND_API(ReadOnlyCompileOptions)
 {
   protected:
     JSPrincipals *principals_;
     JSPrincipals *originPrincipals_;
     const char *filename_;
+    const char *introducerFilename_;
     const jschar *sourceMapURL_;
 
     // This constructor leaves 'version' set to JSVERSION_UNKNOWN. The structure
     // is unusable until that's set to something more specific; the derived
     // classes' constructors take care of that, in ways appropriate to their
     // purpose.
     ReadOnlyCompileOptions()
       : principals_(nullptr),
         originPrincipals_(nullptr),
         filename_(nullptr),
+        introducerFilename_(nullptr),
         sourceMapURL_(nullptr),
         version(JSVERSION_UNKNOWN),
         versionSet(false),
         utf8(false),
         lineno(1),
         column(0),
         compileAndGo(false),
         forEval(false),
         noScriptRval(false),
         selfHostingMode(false),
         canLazilyParse(true),
         strictOption(false),
         extraWarningsOption(false),
         werrorOption(false),
         asmJSOption(false),
         forceAsync(false),
-        sourcePolicy(SAVE_SOURCE)
+        sourcePolicy(SAVE_SOURCE),
+        introducer(nullptr),
+        introductionLineno(0),
+        introductionOffset(0),
+        hasIntroductionInfo(false)
     { }
 
     // Set all POD options (those not requiring reference counts, copies,
     // rooting, or other hand-holding) to their values in |rhs|.
     void copyPODOptions(const ReadOnlyCompileOptions &rhs);
 
   public:
     // Read-only accessors for non-POD options. The proper way to set these
     // depends on the derived type.
     JSPrincipals *principals() const { return principals_; }
     JSPrincipals *originPrincipals() const;
     const char *filename() const { return filename_; }
+    const char *introducerFilename() const { return introducerFilename_; }
     const jschar *sourceMapURL() const { return sourceMapURL_; }
     virtual JSObject *element() const = 0;
     virtual JSString *elementAttributeName() const = 0;
 
     // POD options.
     JSVersion version;
     bool versionSet;
     bool utf8;
@@ -3509,16 +3516,23 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
     bool asmJSOption;
     bool forceAsync;
     enum SourcePolicy {
         NO_SOURCE,
         LAZY_SOURCE,
         SAVE_SOURCE
     } sourcePolicy;
 
+    // |introducer| is a statically allocated C string:
+    // one of "eval", "Function", or "GeneratorFunction".
+    const char *introducer;
+    unsigned introductionLineno;
+    uint32_t introductionOffset;
+    bool hasIntroductionInfo;
+
     // Wrap any compilation option values that need it as appropriate for
     // use from |compartment|.
     virtual bool wrap(JSContext *cx, JSCompartment *compartment) = 0;
 
   private:
     static JSObject * const nullObjectPtr;
     void operator=(const ReadOnlyCompileOptions &) MOZ_DELETE;
 };
@@ -3555,16 +3569,17 @@ class JS_FRIEND_API(OwningCompileOptions
 
     // Set this to a copy of |rhs|. Return false on OOM.
     bool copy(JSContext *cx, const ReadOnlyCompileOptions &rhs);
 
     /* These setters make copies of their string arguments, and are fallible. */
     bool setFile(JSContext *cx, const char *f);
     bool setFileAndLine(JSContext *cx, const char *f, unsigned l);
     bool setSourceMapURL(JSContext *cx, const jschar *s);
+    bool setIntroducerFilename(JSContext *cx, const char *s);
 
     /* These setters are infallible, and can be chained. */
     OwningCompileOptions &setLine(unsigned l)             { lineno = l;              return *this; }
     OwningCompileOptions &setElement(JSObject *e) {
         elementRoot = e;
         return *this;
     }
     OwningCompileOptions &setElementAttributeName(JSString *p) {
@@ -3591,16 +3606,27 @@ class JS_FRIEND_API(OwningCompileOptions
     OwningCompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     OwningCompileOptions &setColumn(unsigned c) { column = c; return *this; }
     OwningCompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     OwningCompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     OwningCompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     OwningCompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     OwningCompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     OwningCompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
+    bool setIntroductionInfo(JSContext *cx, const char *introducerFn, const char *intro,
+                             unsigned line, uint32_t offset)
+    {
+        if (!setIntroducerFilename(cx, introducerFn))
+            return false;
+        introducer = intro;
+        introductionLineno = line;
+        introductionOffset = offset;
+        hasIntroductionInfo = true;
+        return true;
+    }
 
     virtual bool wrap(JSContext *cx, JSCompartment *compartment) MOZ_OVERRIDE;
 };
 
 /*
  * Compilation options stored on the stack. An instance of this type
  * simply holds references to dynamically allocated resources (element;
  * filename; source map URL) that are owned by something else. If you
@@ -3657,16 +3683,26 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
     CompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     CompileOptions &setColumn(unsigned c) { column = c; return *this; }
     CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
+    CompileOptions &setIntroductionInfo(const char *introducerFn, const char *intro,
+                                        unsigned line, uint32_t offset)
+    {
+        introducerFilename_ = introducerFn;
+        introducer = intro;
+        introductionLineno = line;
+        introductionOffset = offset;
+        hasIntroductionInfo = true;
+        return *this;
+    }
 
     virtual bool wrap(JSContext *cx, JSCompartment *compartment) MOZ_OVERRIDE;
 };
 
 extern JS_PUBLIC_API(JSScript *)
 Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options,
         const char *bytes, size_t length);
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1473,28 +1473,39 @@ FunctionConstructor(JSContext *cx, unsig
     AutoKeepAtoms keepAtoms(cx->perThreadData);
     AutoNameVector formals(cx);
 
     bool hasRest = false;
 
     bool isStarGenerator = generatorKind == StarGenerator;
     JS_ASSERT(generatorKind != LegacyGenerator);
 
+    JSScript *script = nullptr;
     const char *filename;
     unsigned lineno;
     JSPrincipals *originPrincipals;
-    CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals);
+    uint32_t pcOffset;
+    CurrentScriptFileLineOrigin(cx, &script, &filename, &lineno, &pcOffset, &originPrincipals);
     JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
 
+    const char *introducer = "Function";
+    if (generatorKind != NotGenerator)
+        introducer = "GeneratorFunction";
+
+    const char *introducerFilename = filename;
+    if (script && script->scriptSource()->introducerFilename())
+        introducerFilename = script->scriptSource()->introducerFilename();
+
     CompileOptions options(cx);
     options.setPrincipals(principals)
            .setOriginPrincipals(originPrincipals)
-           .setFileAndLine(filename, lineno)
+           .setFileAndLine(filename, 1)
            .setNoScriptRval(false)
-           .setCompileAndGo(true);
+           .setCompileAndGo(true)
+           .setIntroductionInfo(introducerFilename, introducer, lineno, pcOffset);
 
     unsigned n = args.length() ? args.length() - 1 : 0;
     if (n > 0) {
         /*
          * Collect the function-argument arguments into one string, separated
          * by commas, then make a tokenstream from that string, and scan it to
          * get the arguments.  We need to throw the full scanner at the
          * problem, because the argument string can legitimately contain
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -5,29 +5,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * JS script operations.
  */
 
 #include "jsscriptinlines.h"
 
+#include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsopcode.h"
+#include "jsprf.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswrapper.h"
 
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/SharedContext.h"
 #include "gc/Marking.h"
 #include "jit/BaselineJIT.h"
@@ -1397,16 +1399,18 @@ SourceCompressionTask::work()
     return true;
 }
 
 void
 ScriptSource::destroy()
 {
     JS_ASSERT(ready());
     adjustDataSize(0);
+    if (introducerFilename_ != filename_)
+        js_free(introducerFilename_);
     js_free(filename_);
     js_free(displayURL_);
     js_free(sourceMapURL_);
     if (originPrincipals_)
         JS_DropPrincipals(TlsPerThreadData.get()->runtimeFromMainThread(), originPrincipals_);
     ready_ = false;
     js_free(this);
 }
@@ -1536,23 +1540,19 @@ ScriptSource::performXDR(XDRState<mode> 
 
     return true;
 }
 
 bool
 ScriptSource::setFilename(ExclusiveContext *cx, const char *filename)
 {
     JS_ASSERT(!filename_);
-    size_t len = strlen(filename) + 1;
-    if (len == 1)
-        return true;
-    filename_ = cx->pod_malloc<char>(len);
+    filename_ = js_strdup(cx, filename);
     if (!filename_)
         return false;
-    js_memcpy(filename_, filename, len);
     return true;
 }
 
 bool
 ScriptSource::setDisplayURL(ExclusiveContext *cx, const jschar *displayURL)
 {
     JS_ASSERT(displayURL);
     if (hasDisplayURL()) {
@@ -1577,16 +1577,58 @@ ScriptSource::setDisplayURL(ExclusiveCon
 const jschar *
 ScriptSource::displayURL()
 {
     JS_ASSERT(hasDisplayURL());
     return displayURL_;
 }
 
 bool
+ScriptSource::setIntroducedFilename(ExclusiveContext *cx,
+                                    const char *callerFilename, unsigned callerLineno,
+                                    const char *introducer, const char *introducerFilename)
+{
+    JS_ASSERT(!filename_);
+    JS_ASSERT(!introducerFilename_);
+
+    introducerType_ = introducer;
+
+    if (introducerFilename) {
+        introducerFilename_ = js_strdup(cx, introducerFilename);
+        if (!introducerFilename_)
+            return false;
+    }
+
+    // Final format:  "{callerFilename} line {callerLineno} > {introducer}"
+    // Len = strlen(callerFilename) + strlen(" line ") +
+    //       strlen(toStr(callerLineno)) + strlen(" > ") + strlen(introducer);
+    char linenoBuf[15];
+    size_t filenameLen = strlen(callerFilename);
+    size_t linenoLen = JS_snprintf(linenoBuf, 15, "%u", callerLineno);
+    size_t introducerLen = strlen(introducer);
+    size_t len = filenameLen                    +
+                 6 /* == strlen(" line ") */    +
+                 linenoLen                      +
+                 3 /* == strlen(" > ") */       +
+                 introducerLen                  +
+                 1 /* \0 */;
+    filename_ = cx->pod_malloc<char>(len);
+    if (!filename_)
+        return false;
+    mozilla::DebugOnly<int> checkLen = JS_snprintf(filename_, len, "%s line %s > %s",
+                                                   callerFilename, linenoBuf, introducer);
+    JS_ASSERT(checkLen == len - 1);
+
+    if (!introducerFilename_)
+        introducerFilename_ = filename_;
+
+    return true;
+}
+
+bool
 ScriptSource::setSourceMapURL(ExclusiveContext *cx, const jschar *sourceMapURL)
 {
     JS_ASSERT(sourceMapURL);
     if (hasSourceMapURL()) {
         if (cx->isJSContext() &&
             !JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING,
                                           js_GetErrorMessage, nullptr,
                                           JSMSG_ALREADY_HAS_PRAGMA, filename_,
@@ -2412,45 +2454,50 @@ js_GetScriptLineExtent(JSScript *script)
         if (maxLineNo < lineno)
             maxLineNo = lineno;
     }
 
     return 1 + maxLineNo - script->lineno();
 }
 
 void
-js::CurrentScriptFileLineOrigin(JSContext *cx, const char **file, unsigned *linenop,
-                                JSPrincipals **origin, LineOption opt)
+js::CurrentScriptFileLineOrigin(JSContext *cx, JSScript **script,
+                                const char **file, unsigned *linenop,
+                                uint32_t *pcOffset, JSPrincipals **origin, LineOption opt)
 {
     if (opt == CALLED_FROM_JSOP_EVAL) {
         jsbytecode *pc = nullptr;
-        JSScript *script = cx->currentScript(&pc);
+        *script = cx->currentScript(&pc);
         JS_ASSERT(JSOp(*pc) == JSOP_EVAL || JSOp(*pc) == JSOP_SPREADEVAL);
         JS_ASSERT(*(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
                                                  : JSOP_SPREADEVAL_LENGTH)) == JSOP_LINENO);
-        *file = script->filename();
+        *file = (*script)->filename();
         *linenop = GET_UINT16(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
                                                            : JSOP_SPREADEVAL_LENGTH));
-        *origin = script->originPrincipals();
+        *pcOffset = pc - (*script)->code();
+        *origin = (*script)->originPrincipals();
         return;
     }
 
     NonBuiltinScriptFrameIter iter(cx);
 
     if (iter.done()) {
+        *script = nullptr;
         *file = nullptr;
         *linenop = 0;
+        *pcOffset = 0;
         *origin = cx->compartment()->principals;
         return;
     }
 
-    JSScript *script = iter.script();
-    *file = script->filename();
-    *linenop = PCToLineNumber(iter.script(), iter.pc());
-    *origin = script->originPrincipals();
+    *script = iter.script();
+    *file = (*script)->filename();
+    *linenop = PCToLineNumber(*script, iter.pc());
+    *pcOffset = iter.pc() - (*script)->code();
+    *origin = (*script)->originPrincipals();
 }
 
 template <class T>
 static inline T *
 Rebase(JSScript *dst, JSScript *src, T *srcp)
 {
     size_t off = reinterpret_cast<uint8_t *>(srcp) - src->data;
     return reinterpret_cast<T *>(dst->data + off);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -376,35 +376,66 @@ class ScriptSource
     uint32_t refs;
     uint32_t length_;
     uint32_t compressedLength_;
     char *filename_;
     jschar *displayURL_;
     jschar *sourceMapURL_;
     JSPrincipals *originPrincipals_;
 
+    // bytecode offset in caller script that generated this code.
+    // This is present for eval-ed code, as well as "new Function(...)"-introduced
+    // scripts.
+    uint32_t introductionOffset_;
+
+    // If this ScriptSource was generated by a code-introduction mechanism such as |eval|
+    // or |new Function|, the debugger needs access to the "raw" filename of the top-level
+    // script that contains the eval-ing code.  To keep track of this, we must preserve
+    // the original outermost filename (of the original introducer script), so that instead
+    // of a filename of "foo.js line 30 > eval line 10 > Function", we can obtain the
+    // original raw filename of "foo.js".
+    char *introducerFilename_;
+
+    // A string indicating how this source code was introduced into the system.
+    // This accessor returns one of the following values:
+    //      "eval" for code passed to |eval|.
+    //      "Function" for code passed to the |Function| constructor.
+    //      "Worker" for code loaded by calling the Web worker constructor&mdash;the worker's main script.
+    //      "importScripts" for code by calling |importScripts| in a web worker.
+    //      "handler" for code assigned to DOM elements' event handler IDL attributes.
+    //      "scriptElement" for code belonging to <script> elements.
+    //      undefined if the implementation doesn't know how the code was introduced.
+    // This is a constant, statically allocated C string, so does not need
+    // memory management.
+    const char *introducerType_;
+
     // True if we can call JSRuntime::sourceHook to load the source on
     // demand. If sourceRetrievable_ and hasSourceData() are false, it is not
     // possible to get source at all.
     bool sourceRetrievable_:1;
     bool argumentsNotIncluded_:1;
     bool ready_:1;
+    bool hasIntroductionOffset_:1;
 
   public:
     ScriptSource(JSPrincipals *originPrincipals)
       : refs(0),
         length_(0),
         compressedLength_(0),
         filename_(nullptr),
         displayURL_(nullptr),
         sourceMapURL_(nullptr),
         originPrincipals_(originPrincipals),
+        introductionOffset_(0),
+        introducerFilename_(nullptr),
+        introducerType_(nullptr),
         sourceRetrievable_(false),
         argumentsNotIncluded_(false),
-        ready_(true)
+        ready_(true),
+        hasIntroductionOffset_(false)
     {
         data.source = nullptr;
         if (originPrincipals_)
             JS_HoldPrincipals(originPrincipals_);
     }
     void incref() { refs++; }
     void decref() {
         JS_ASSERT(refs != 0);
@@ -433,32 +464,57 @@ class ScriptSource
     JSFlatString *substring(JSContext *cx, uint32_t start, uint32_t stop);
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     // XDR handling
     template <XDRMode mode>
     bool performXDR(XDRState<mode> *xdr);
 
     bool setFilename(ExclusiveContext *cx, const char *filename);
+    bool setIntroducedFilename(ExclusiveContext *cx,
+                               const char *callerFilename, unsigned callerLineno,
+                               const char *introducer, const char *introducerFilename);
+    const char *introducerFilename() const {
+        return introducerFilename_;
+    }
+    bool hasIntroducerType() const {
+        return introducerType_;
+    }
+    const char *introducerType() const {
+        JS_ASSERT(hasIntroducerType());
+        return introducerType_;
+    }
     const char *filename() const {
         return filename_;
     }
 
     // Display URLs
     bool setDisplayURL(ExclusiveContext *cx, const jschar *displayURL);
     const jschar *displayURL();
     bool hasDisplayURL() const { return displayURL_ != nullptr; }
 
     // Source maps
     bool setSourceMapURL(ExclusiveContext *cx, const jschar *sourceMapURL);
     const jschar *sourceMapURL();
     bool hasSourceMapURL() const { return sourceMapURL_ != nullptr; }
 
     JSPrincipals *originPrincipals() const { return originPrincipals_; }
 
+    bool hasIntroductionOffset() const { return hasIntroductionOffset_; }
+    uint32_t introductionOffset() const {
+        JS_ASSERT(hasIntroductionOffset());
+        return introductionOffset_;
+    }
+    void setIntroductionOffset(uint32_t offset) {
+        JS_ASSERT(!hasIntroductionOffset());
+        JS_ASSERT(offset <= (uint32_t)INT32_MAX);
+        introductionOffset_ = offset;
+        hasIntroductionOffset_ = true;
+    }
+
   private:
     void destroy();
     bool compressed() const { return compressedLength_ != 0; }
     size_t computedSizeOfData() const {
         return compressed() ? compressedLength_ : sizeof(jschar) * length_;
     }
     bool adjustDataSize(size_t nbytes);
     const jschar *getOffThreadCompressionChars(ExclusiveContext *cx);
@@ -1900,18 +1956,20 @@ PCToLineNumber(unsigned startLine, jssrc
  */
 
 enum LineOption {
     CALLED_FROM_JSOP_EVAL,
     NOT_CALLED_FROM_JSOP_EVAL
 };
 
 extern void
-CurrentScriptFileLineOrigin(JSContext *cx, const char **file, unsigned *linenop,
-                            JSPrincipals **origin, LineOption opt = NOT_CALLED_FROM_JSOP_EVAL);
+CurrentScriptFileLineOrigin(JSContext *cx, JSScript **script,
+                            const char **file, unsigned *linenop,
+                            uint32_t *pcOffset, JSPrincipals **origin,
+                            LineOption opt = NOT_CALLED_FROM_JSOP_EVAL);
 
 bool
 CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone,
                     NewObjectKind newKind = GenericObject);
 
 /*
  * JSAPI clients are allowed to leave CompileOptions.originPrincipals nullptr in
  * which case the JS engine sets options.originPrincipals = origin.principals.
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2618,17 +2618,27 @@ class Debugger::ScriptQuery {
         // the script was created and thus exposed to GC, but *not* fully
         // initialized from fullyInit{FromEmitter,Trivial} due to errors.
         if (oom || script->selfHosted() || !script->code())
             return;
         JSCompartment *compartment = script->compartment();
         if (!compartments.has(compartment))
             return;
         if (urlCString.ptr()) {
-            if (!script->filename() || strcmp(script->filename(), urlCString.ptr()) != 0)
+            bool gotFilename = false;
+            if (script->filename() && strcmp(script->filename(), urlCString.ptr()) == 0)
+                gotFilename = true;
+
+            bool gotSourceURL = false;
+            if (!gotFilename && script->scriptSource()->introducerFilename() &&
+                strcmp(script->scriptSource()->introducerFilename(), urlCString.ptr()) == 0)
+            {
+                gotSourceURL = true;
+            }
+            if (!gotFilename && !gotSourceURL)
                 return;
         }
         if (hasLine) {
             if (line < script->lineno() || script->lineno() + js_GetScriptLineExtent(script) < line)
                 return;
         }
         if (displayURLChars) {
             if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL())
@@ -2919,17 +2929,21 @@ DebuggerScript_checkThis(JSContext *cx, 
     Rooted<JSScript*> script(cx, GetScriptReferent(obj))
 
 static bool
 DebuggerScript_getUrl(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get url)", args, obj, script);
 
     if (script->filename()) {
-        JSString *str = js_NewStringCopyZ<CanGC>(cx, script->filename());
+        JSString *str;
+        if (script->scriptSource()->introducerFilename())
+            str = js_NewStringCopyZ<CanGC>(cx, script->scriptSource()->introducerFilename());
+        else
+            str = js_NewStringCopyZ<CanGC>(cx, script->filename());
         if (!str)
             return false;
         args.rval().setString(str);
     } else {
         args.rval().setNull();
     }
     return true;
 }
@@ -3899,21 +3913,53 @@ DebuggerSource_getElement(JSContext *cx,
 static bool
 DebuggerSource_getElementProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get elementAttributeName)", args, obj, sourceObject);
     args.rval().set(sourceObject->elementAttributeName());
     return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval());
 }
 
+static bool
+DebuggerSource_getIntroductionOffset(JSContext *cx, unsigned argc, Value *vp)
+{
+    THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj, sourceObject);
+
+    ScriptSource *ss = sourceObject->source();
+    if (ss->hasIntroductionOffset())
+        args.rval().setInt32(ss->introductionOffset());
+    else
+        args.rval().setUndefined();
+    return true;
+}
+
+static bool
+DebuggerSource_getIntroductionType(JSContext *cx, unsigned argc, Value *vp)
+{
+    THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj, sourceObject);
+
+    ScriptSource *ss = sourceObject->source();
+    if (ss->hasIntroducerType()) {
+        JSString *str = js_NewStringCopyZ<CanGC>(cx, ss->introducerType());
+        if (!str)
+            return false;
+        args.rval().setString(str);
+    } else {
+        args.rval().setUndefined();
+    }
+    return true;
+}
+
 static const JSPropertySpec DebuggerSource_properties[] = {
     JS_PSG("text", DebuggerSource_getText, 0),
     JS_PSG("url", DebuggerSource_getUrl, 0),
     JS_PSG("element", DebuggerSource_getElement, 0),
     JS_PSG("displayURL", DebuggerSource_getDisplayURL, 0),
+    JS_PSG("introductionOffset", DebuggerSource_getIntroductionOffset, 0),
+    JS_PSG("introductionType", DebuggerSource_getIntroductionType, 0),
     JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0),
     JS_PS_END
 };
 
 static const JSFunctionSpec DebuggerSource_methods[] = {
     JS_FS_END
 };