Merge f-t to m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 17 Mar 2014 22:42:42 -0700
changeset 173995 082761b7bc543109dbc6899ef118cf0e67a90d5a
parent 173986 89275f0ae29f3467eb022c62da0c3ea838cf4196 (current diff)
parent 173994 1e326e9bcc1b804a6b3aab934c5910e4801ccfb1 (diff)
child 174019 9946f75c3f1d034f79975cdfbf62dad2ff9008dd
child 174072 c8ec14fefc0acf39419e343d9a00a43128841a84
child 174114 9a220588c8ceaa992b63813ee5d1a675e120beca
push id26439
push userphilringnalda@gmail.com
push dateTue, 18 Mar 2014 05:42:57 +0000
treeherdermozilla-central@082761b7bc54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.0a1
first release with
nightly linux32
082761b7bc54 / 31.0a1 / 20140318030202 / files
nightly linux64
082761b7bc54 / 31.0a1 / 20140318030202 / files
nightly mac
082761b7bc54 / 31.0a1 / 20140318030202 / files
nightly win32
082761b7bc54 / 31.0a1 / 20140318030202 / files
nightly win64
082761b7bc54 / 31.0a1 / 20140318030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge f-t to m-c
toolkit/devtools/server/tests/unit/test_sources_backwards_compat-01.js
toolkit/devtools/server/tests/unit/test_sources_backwards_compat-02.js
toolkit/devtools/server/tests/unit/testcompatactors.js
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -227,16 +227,17 @@ support-files =
 [browser_dbg_variables-view-01.js]
 [browser_dbg_variables-view-02.js]
 [browser_dbg_variables-view-03.js]
 [browser_dbg_variables-view-04.js]
 [browser_dbg_variables-view-05.js]
 [browser_dbg_variables-view-accessibility.js]
 [browser_dbg_variables-view-data.js]
 [browser_dbg_variables-view-edit-cancel.js]
+[browser_dbg_variables-view-edit-click.js]
 [browser_dbg_variables-view-edit-getset-01.js]
 [browser_dbg_variables-view-edit-getset-02.js]
 [browser_dbg_variables-view-edit-value.js]
 [browser_dbg_variables-view-edit-watch.js]
 [browser_dbg_variables-view-filter-01.js]
 [browser_dbg_variables-view-filter-02.js]
 [browser_dbg_variables-view-filter-03.js]
 [browser_dbg_variables-view-filter-04.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-edit-click.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Check that the editing state of a Variable is correctly tracked. Clicking on
+ * the textbox while editing should not cancel editing.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_watch-expressions.html";
+
+function test() {
+  Task.spawn(function*() {
+    let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
+    let win = panel.panelWin;
+    let vars = win.DebuggerView.Variables;
+
+    win.DebuggerView.WatchExpressions.addExpression("this");
+
+    // Allow this generator function to yield first.
+    executeSoon(() => debuggee.ermahgerd());
+    yield waitForDebuggerEvents(panel, win.EVENTS.FETCHED_WATCH_EXPRESSIONS);
+
+    let exprScope = vars.getScopeAtIndex(0);
+    let exprVar = exprScope.get("this");
+    let name = exprVar.target.querySelector(".title > .name");
+
+    is(exprVar.editing, false,
+      "The expression should indicate it is not being edited.");
+
+    EventUtils.sendMouseEvent({ type: "dblclick" }, name, win);
+    let input = exprVar.target.querySelector(".title > .element-name-input");
+    is(exprVar.editing, true,
+      "The expression should indicate it is being edited.");
+    is(input.selectionStart !== input.selectionEnd, true,
+      "The expression text should be selected.");
+
+    EventUtils.synthesizeMouse(input, 2, 2, {}, win);
+    is(exprVar.editing, true,
+      "The expression should indicate it is still being edited after a click.");
+    is(input.selectionStart === input.selectionEnd, true,
+      "The expression text should not be selected.");
+
+    EventUtils.sendKey("ESCAPE", win);
+    is(exprVar.editing, false,
+      "The expression should indicate it is not being edited after cancelling.");
+
+    // Why is this needed?
+    EventUtils.synthesizeMouse(vars.parentNode, 2, 2, {}, win);
+
+    yield resumeDebuggerThenCloseAndFinish(panel);
+  });
+}
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -1825,17 +1825,18 @@ Scope.prototype = {
   _addEventListeners: function() {
     this._title.addEventListener("mousedown", this._onClick, false);
   },
 
   /**
    * The click listener for this scope's title.
    */
   _onClick: function(e) {
-    if (e.button != 0 ||
+    if (this.editing ||
+        e.button != 0 ||
         e.target == this._editNode ||
         e.target == this._deleteNode ||
         e.target == this._addPropertyNode) {
       return;
     }
     this.toggle();
     this.focus();
   },
@@ -2076,16 +2077,17 @@ Scope.prototype = {
 
   ownerView: null,
   eval: null,
   switch: null,
   delete: null,
   new: null,
   preventDisableOnChange: false,
   preventDescriptorModifiers: false,
+  editing: false,
   editableNameTooltip: "",
   editableValueTooltip: "",
   editButtonTooltip: "",
   deleteButtonTooltip: "",
   domNodeValueTooltip: "",
   contextMenuId: "",
   separatorStr: "",
 
@@ -3884,16 +3886,17 @@ Editable.prototype = {
     input.addEventListener("keypress", this._onKeypress);
     input.addEventListener("blur", this._onBlur);
 
     this._prevExpandable = this._variable.twisty;
     this._prevExpanded = this._variable.expanded;
     this._variable.collapse();
     this._variable.hideArrow();
     this._variable.locked = true;
+    this._variable.editing = true;
   },
 
   /**
    * Remove the input box and restore the Variable or Property to its previous
    * state.
    */
   deactivate: function() {
     this._input.removeEventListener("keypress", this._onKeypress);
@@ -3901,16 +3904,17 @@ Editable.prototype = {
     this._input.parentNode.replaceChild(this.label, this._input);
     this._input = null;
 
     let { boxObject } = this._variable._variablesView;
     boxObject.scrollBy(-this._variable._target, 0);
     this._variable.locked = false;
     this._variable.twisty = this._prevExpandable;
     this._variable.expanded = this._prevExpanded;
+    this._variable.editing = false;
     this._onCleanup && this._onCleanup();
   },
 
   /**
    * Save the current value and deactivate the Editable.
    */
   _save: function() {
     let initial = this.label.getAttribute("value");
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -157,18 +157,16 @@ toolbarbutton.chevron:-moz-locale-dir(rt
     width: 13px;
   }
 }
 
 /* ----- BOOKMARK BUTTONS ----- */
 
 toolbarbutton.bookmark-item:not(.subviewbutton):not(#bookmarks-menu-button),
 #personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder {
-  font-weight: bold;
-  color: #222;
   border: 0;
   border-radius: 10000px;
   padding: 1px 8px;
   margin: 0 0 1px;
 }
 
 #personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder {
   -moz-box-orient: horizontal;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7fad6f05b0a7f1162b9682d954612c7132cb15a2
GIT binary patch
literal 528
zc$@(c0`L8aP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0005kNkl<Zc-rmR
z&q@M89KdlA=@23z5_E`&uv2s@43WZ~p)OHGOD_;fFW{jlA&4+K6!j942Qi5x4~3XJ
zuVddsFdm%!SF_B*9zG{s@QXY1bJoOYG>Wu1AP$HF6fdk5w<pQq9(`1h2+6+$6wrbA
zWuqLGfFlebFs_Rk(*sUnLnu6%9ALqQaABDkkVhZFgFf=61=JyYXdq=$zz2j+Rg(hj
z00A~uObKX11W+<1;0D5n9`;PSg|;9(7~mW^<K}?NZS;wA%$q#OjvWGaqVQubg?B~3
zt@#1Kfy4C8MIj)GrXr$dc7Rk=47MQ(W(P>u5EHy29fg22I*N#AB+U(wiuhJUT$mjo
zRUj0C9P~?A4JSZ)Pz2aMBlgFxt0EOvKpHQO=K}XNkwtI9p=eoefHa3!r^CL>?sVxe
zAT7NC(jvMJ5pBhsSik_n!4t9?17w8DH!(yFvt!p>L3q$bSzmxOi>8Ms7j^97%jYrm
zOkaRBjYkL#`uYQ;igMTmcNb28v?15UpLLK4FF;D81{)%v0oL^{Vs`~J0zPB1G;E`R
zByoT@A0Pfi-yjQa3Km)}+wGzLN&I)4xJC;@^l*oz3Ga>nrHljOfH)uy(BlVOd6fmf
Ssrupo0000<MNUMnLSTZvG1R62
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..19308f870a906210e85c4915e42c7ff63f019e14
GIT binary patch
literal 689
zc$@*T0#5yjP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0007bNkl<Zc-rmT
zF>8}S7{KumLI|ZpD5AU{pc#s|#A3-%h@gX$YXp}NNI@!e(OD{S5flTCQXz{76<Q6p
z1w}(DMjDgGJYOgNw{u8ym%ML#dCwbuAE1A@x6eIC5~I=Rzvct@06u^Z-~;#oKET<o
z7jEiQ&_N3YB<9Gu2FT(K<R8b#!ubFNv>;>bxE|mhP9YPNT@A2_Aw;8xoNEC#FoYOt
zBIQbeTNps5wCzHG48B8VFhnu#0Ukg?IL1<(1AK&pv=iq5JxBz~aSbqlMBoJJI0iU^
zL|}l0I0kqH38{~2T#vvVNC@wdi}Ob?i64+DeMQNYNw5Yn^a&*-UAhYdI5n91)?HqR
zyoPAxT@J8pFcZ|=44^#M1n6MF<p7t_)kJvYW&mYN6QGO0<p3!hXd*P+44~XFSdUfQ
z44^zSnE&%G2gu-16QPB`y#UHJgN4awEV>s!*??$#Kn97B7Ll<KK&uYXco#B46@PGo
z%De^0U>~BfJL7R!8FLwXizWL3lmIP=MstP_<L-o&d(nOXWd$9GMth2Xk#`NQ;oC^r
z4WQ)lJEA>d3SXvJ#N_M-P}b3f7-&p5689lf=%H*kfU=4X#6TC1$6TeGAzq@g_5vt5
zv>}E%c#2gNBY2Z6*bATp*q`xbSwd>t4WOh^hg`6q^ASL)q6fJkRSN;MuHpc4&KmYU
zf_iDx)H#n_8<*?{&|AY1Btm_x*`EW<rLcuAB%~oqb_SvFuHYpGA^!MH`=fAp%c!Fd
zF*HEc?qw)?0iGB<&3{7?3AX|$N!-Q@9HNh3*uz83mdXEM!UymHd;lN72k-%WfHVCC
XR0nrRVAw?@00000NkvXXu0mjfe%&Bq
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3b4c8f9399308fb76dd26516a997b3a3fb9be234
GIT binary patch
literal 380
zc$@)z0fYXDP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80003)Nkl<Zc-rmP
zF-yZh7>DsgI*F9xhmayJ1woL4RM0_(PA-Cr;9@5i_tLFHzko|YsgA{=pjFVNqhj0S
z>%?cs5dK1rB;M(e;Wxhz?<Lp9L{apo%7FhDu)VvV)W8eIsK6AIDc}Nggt4B_fNO*z
zK+8u!8v#P78EUQro@A&Rx(-+(v^a4Y@NO9%;LK$}KV|p?P1i?IhdzW;9Q%9?ZzSLj
z#%I8WY+;GI@0)NSTe$ZfP{m9lI^F_ehjJ7P9JvlqUD?7Xb}WNVf!_&O!AK%Lum=Ox
zOl)n4TAl#4hDV8*;}RM>f&PHC@&u@Ljfjn~34?aZ_1!}yYk*qS<A^QsfGNTh<Gca7
z<}Lz+8NC(>PzQ2P*nFW;G(c4`fNdKA&htNnc3Zejgy`@q7*_!r(#IPXc$Po1<(E+z
a@WVG|>tH|YjcUID0000<MNUMnLSTZAC!`+$
new file mode 120000
--- /dev/null
+++ b/mobile/android/base/resources/layout/.#tabs_item_row.xml
@@ -0,0 +1,1 @@
+blassey@flyingfox.local.319
\ No newline at end of file
--- a/mobile/android/base/sync/PrefsBackoffHandler.java
+++ b/mobile/android/base/sync/PrefsBackoffHandler.java
@@ -1,36 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync;
 
-import org.mozilla.gecko.background.common.log.Logger;
-
-import android.content.ContentResolver;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
-import android.os.Bundle;
 
 public class PrefsBackoffHandler implements BackoffHandler {
-  private static final String LOG_TAG = "BackoffHandler";
-
   public static final String PREF_EARLIEST_NEXT = "earliestnext";
 
   private final SharedPreferences prefs;
-  private final String prefSuffix;
   private final String prefEarliest;
 
   public PrefsBackoffHandler(final SharedPreferences prefs, final String prefSuffix) {
     if (prefs == null) {
       throw new IllegalArgumentException("prefs must not be null.");
     }
     this.prefs = prefs;
-    this.prefSuffix = prefSuffix;
     this.prefEarliest = PREF_EARLIEST_NEXT + "." + prefSuffix;
   }
 
   @Override
   public synchronized long getEarliestNextRequest() {
     return prefs.getLong(prefEarliest, 0);
   }
 
--- a/mobile/android/base/sync/setup/activities/SendTabData.java
+++ b/mobile/android/base/sync/setup/activities/SendTabData.java
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync.setup.activities;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
 import android.content.Intent;
 import android.os.Bundle;
 
 /**
  * A static factory that extracts (title, uri) pairs suitable for send tab from
--- a/mobile/android/chrome/content/SelectionHandler.js
+++ b/mobile/android/chrome/content/SelectionHandler.js
@@ -1,8 +1,9 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 var SelectionHandler = {
   HANDLE_TYPE_START: "START",
   HANDLE_TYPE_MIDDLE: "MIDDLE",
@@ -270,16 +271,52 @@ var SelectionHandler = {
 
     // Double check results of successful selection operation
     let selection = this._getSelection();
     if (!selection || selection.rangeCount == 0 || selection.getRangeAt(0).collapsed) {
       this._deactivate();
       return false;
     }
 
+    if (this._isPhoneNumber(selection.toString())) {
+      let anchorNode = selection.anchorNode;
+      let anchorOffset = selection.anchorOffset;
+      let focusNode = null;
+      let focusOffset = null;
+      while (this._isPhoneNumber(selection.toString().trim())) {
+        focusNode = selection.focusNode;
+        focusOffset = selection.focusOffset;
+        selection.modify("extend", "forward", "word");
+        // if we hit the end of the text on the page, we can't advance the selection
+        if (focusNode == selection.focusNode && focusOffset == selection.focusOffset) {
+          break;
+        }
+      }
+
+      // reverse selection
+      selection.collapse(focusNode, focusOffset);
+      selection.extend(anchorNode, anchorOffset);
+
+      anchorNode = focusNode;
+      anchorOffset = focusOffset
+
+      while (this._isPhoneNumber(selection.toString().trim())) {
+        focusNode = selection.focusNode;
+        focusOffset = selection.focusOffset;
+        selection.modify("extend", "backward", "word");
+        // if we hit the end of the text on the page, we can't advance the selection
+        if (focusNode == selection.focusNode && focusOffset == selection.focusOffset) {
+          break;
+        }
+      }
+
+      selection.collapse(focusNode, focusOffset);
+      selection.extend(anchorNode, anchorOffset);
+    }
+
     // Add a listener to end the selection if it's removed programatically
     selection.QueryInterface(Ci.nsISelectionPrivate).addSelectionListener(this);
     this._activeType = this.TYPE_SELECTION;
 
     // Initialize the cache
     this._cache = { start: {}, end: {}};
     this._updateCacheForSelection();
 
@@ -489,16 +526,30 @@ var SelectionHandler = {
       action: function() {
         SelectionHandler.searchSelection();
         SelectionHandler._closeSelection();
       },
       order: 1,
       selector: ClipboardHelper.searchWithContext,
     },
 
+    CALL: {
+      label: Strings.browser.GetStringFromName("contextmenu.call"),
+      id: "call_action",
+      icon: "drawable://phone",
+      action: function() {
+        SelectionHandler.callSelection();
+      },
+      order: 1,
+      selector: {
+        matches: function isPhoneNumber(aElement, aX, aY) {
+          return null != SelectionHandler._getSelectedPhoneNumber();
+        }
+      },
+    },
   },
 
   /*
    * Called by BrowserEventHandler when the user taps in a form input.
    * Initializes SelectionHandler and positions the caret handle.
    *
    * @param aX, aY tap location in client coordinates.
    */
@@ -739,16 +790,34 @@ var SelectionHandler = {
       // Set current tab as parent of new tab, and set new tab as private if the parent is.
       BrowserApp.addTab(req.uri.spec, {parentId: parent.id,
                                        selected: true,
                                        isPrivate: isPrivate});
     }
     this._closeSelection();
   },
 
+  _phoneRegex: /^\+?[0-9\s,-.\(\)*#pw]{1,30}$/,
+
+  _getSelectedPhoneNumber: function sh_getSelectedPhoneNumber() {
+    return this._isPhoneNumber(this._getSelectedText().trim());
+  },
+
+  _isPhoneNumber: function sh_isPhoneNumber(selectedText) {
+    return (this._phoneRegex.test(selectedText) ? selectedText : null);
+  },
+
+  callSelection: function sh_callSelection() {
+    let selectedText = this._getSelectedPhoneNumber();
+    if (selectedText) {
+      BrowserApp.loadURI("tel:" + selectedText);
+    }
+    this._closeSelection();
+  },
+
   /*
    * Shuts SelectionHandler down.
    */
   _closeSelection: function sh_closeSelection() {
     // Bail if there's no active selection
     if (this._activeType == this.TYPE_NONE)
       return;
 
@@ -899,16 +968,17 @@ var SelectionHandler = {
     if (!positions) {
       positions = this._getHandlePositions(this._getScrollPos());
     }
     sendMessageToJava({
       type: "TextSelection:PositionHandles",
       positions: positions,
       rtl: this._isRTL
     });
+    this._updateMenu();
   },
 
   subdocumentScrolled: function sh_subdocumentScrolled(aElement) {
     if (this._activeType == this.TYPE_NONE) {
       return;
     }
     let scrollView = aElement.ownerDocument.defaultView;
     let view = this._contentWindow;
--- a/mobile/android/locales/en-US/chrome/browser.properties
+++ b/mobile/android/locales/en-US/chrome/browser.properties
@@ -196,16 +196,18 @@ contextmenu.saveAudio=Save Audio
 contextmenu.addToContacts=Add to Contacts
 contextmenu.castToScreen=Cast to Screen
 
 contextmenu.copy=Copy
 contextmenu.cut=Cut
 contextmenu.selectAll=Select All
 contextmenu.paste=Paste
 
+contextmenu.call=Call
+
 # Select UI
 selectHelper.closeMultipleSelectDialog=Done
 
 #Input widgets UI
 inputWidgetHelper.date=Pick a date
 inputWidgetHelper.datetime=Pick a date and a time
 inputWidgetHelper.datetime-local=Pick a date and a time
 inputWidgetHelper.time=Pick a time
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -241,19 +241,17 @@ this.DebuggerClient = function (aTranspo
   this._tabClients = new Map;
   this._tracerClients = new Map;
   this._consoleClients = new Map;
 
   this._pendingRequests = [];
   this._activeRequests = new Map;
   this._eventsEnabled = true;
 
-  this.compat = new ProtocolCompatibility(this, [
-    new SourcesShim(),
-  ]);
+  this.compat = new ProtocolCompatibility(this, []);
   this.traits = {};
 
   this.request = this.request.bind(this);
   this.localTransport = this._transport.onOutputStreamReady === undefined;
 
   /*
    * As the first thing on the connection, expect a greeting packet from
    * the connection's root actor.
@@ -928,51 +926,16 @@ const FeatureCompatibilityShim = {
    * it with a new packet, create an extra packet, or keep it.
    */
   translatePacket: function (aPacket, aReplacePacket, aExtraPacket, aKeepPacket) {
     throw new Error("Not yet implemented");
   }
 };
 
 /**
- * A shim to support the "sources" and "newSource" packets for older servers
- * which don't support them.
- */
-function SourcesShim() {
-  this._sourcesSeen = new Set();
-}
-
-SourcesShim.prototype = Object.create(FeatureCompatibilityShim);
-let SSProto = SourcesShim.prototype;
-
-SSProto.name = "sources";
-
-SSProto.onPacketTest = function (aPacket) {
-  if (aPacket.traits) {
-    return aPacket.traits.sources
-      ? SUPPORTED
-      : NOT_SUPPORTED;
-  }
-  return SKIP;
-};
-
-SSProto.translatePacket = function (aPacket, aReplacePacket, aExtraPacket,
-                                    aKeepPacket) {
-  if (aPacket.type !== "newScript" || this._sourcesSeen.has(aPacket.url)) {
-    return aKeepPacket();
-  }
-  this._sourcesSeen.add(aPacket.url);
-  return aExtraPacket({
-    from: aPacket.from,
-    type: "newSource",
-    source: aPacket.source
-  });
-};
-
-/**
  * Creates a tab client for the remote debugging protocol server. This client
  * is a front to the tab actor created in the server side, hiding the protocol
  * details in a traditional JavaScript API.
  *
  * @param aClient DebuggerClient
  *        The debugger client parent.
  * @param aForm object
  *        The protocol form for this tab.
@@ -1521,62 +1484,21 @@ ThreadClient.prototype = {
   }),
 
   /**
    * Request the loaded sources for the current thread.
    *
    * @param aOnResponse Function
    *        Called with the thread's response.
    */
-  getSources: function (aOnResponse) {
-    // This is how we should get sources if the server supports "sources"
-    // requests.
-    let getSources = DebuggerClient.requester({
-      type: "sources"
-    }, {
-      telemetry: "SOURCES"
-    });
-
-    // This is how we should deduct what sources exist from the existing scripts
-    // when the server does not support "sources" requests.
-    let getSourcesBackwardsCompat = DebuggerClient.requester({
-      type: "scripts"
-    }, {
-      after: function (aResponse) {
-        if (aResponse.error) {
-          return aResponse;
-        }
-
-        let sourceActorsByURL = aResponse.scripts
-          .reduce(function (aSourceActorsByURL, aScript) {
-            aSourceActorsByURL[aScript.url] = aScript.source;
-            return aSourceActorsByURL;
-          }, {});
-
-        return {
-          sources: [
-            { url: url, actor: sourceActorsByURL[url] }
-            for (url of Object.keys(sourceActorsByURL))
-          ]
-        }
-      },
-      telemetry: "SOURCES"
-    });
-
-    // On the first time `getSources` is called, patch the thread client with
-    // the best method for the server's capabilities.
-    let threadClient = this;
-    this.compat.supportsFeature("sources").then(function () {
-      threadClient.getSources = getSources;
-    }, function () {
-      threadClient.getSources = getSourcesBackwardsCompat;
-    }).then(function () {
-      threadClient.getSources(aOnResponse);
-    });
-  },
+  getSources: DebuggerClient.requester({
+    type: "sources"
+  }, {
+    telemetry: "SOURCES"
+  }),
 
   _doInterrupted: function (aAction, aError) {
     if (this.paused) {
       aAction();
       return;
     }
     this.interrupt(function (aResponse) {
       if (aResponse) {
--- a/toolkit/devtools/pretty-fast/pretty-fast.js
+++ b/toolkit/devtools/pretty-fast/pretty-fast.js
@@ -483,42 +483,65 @@
       }
       n >>= 1;
       str += str;
     }
     return result;
   }
 
   /**
-   * Make sure that we put "\n" into the output instead of actual newlines.
+   * Make sure that we output the escaped character combination inside string literals
+   * instead of various problematic characters.
    */
-  function sanitizeNewlines(str) {
-    return str.replace(/\n/g, "\\n");
-  }
+  var sanitize = (function () {
+    var escapeCharacters = {
+      // Backslash
+      "\\": "\\\\",
+      // Newlines
+      "\n": "\\n",
+      // Carriage return
+      "\r": "\\r",
+      // Tab
+      "\t": "\\t",
+      // Vertical tab
+      "\v": "\\v",
+      // Form feed
+      "\f": "\\f",
+      // Null character
+      "\0": "\\0",
+      // Single quotes
+      "'": "\\'"
+    };
 
-  /**
-   * Make sure that we put "\'" into the single-quoted output instead of raw single quotes.
-   */
-  function sanitizeSingleQuotes(str) {
-    return str.replace(/\'/g, "\\'");
-  }
+    var regExpString = "("
+      + Object.keys(escapeCharacters)
+              .map(function (c) { return escapeCharacters[c]; })
+              .join("|")
+      + ")";
+    var escapeCharactersRegExp = new RegExp(regExpString, "g");
 
+    return function(str) {
+      return str.replace(escapeCharactersRegExp, function (_, c) {
+        return escapeCharacters[c];
+      });
+    }
+  }());
   /**
    * Add the given token to the pretty printed results.
    *
    * @param Object token
    *        The token to add.
    * @param Function write
    *        The function to write pretty printed code to the result SourceNode.
    * @param Object options
    *        The options object.
    */
   function addToken(token, write, options) {
     if (token.type.type == "string") {
-      write("'" + sanitizeSingleQuotes(sanitizeNewlines(token.value)) + "'",
+      write("'" + sanitize(token.value) + "'",
             token.startLoc.line,
             token.startLoc.column);
     } else {
       write(String(token.value != null ? token.value : token.type.type),
             token.startLoc.line,
             token.startLoc.column);
     }
   }
--- a/toolkit/devtools/pretty-fast/tests/unit/test.js
+++ b/toolkit/devtools/pretty-fast/tests/unit/test.js
@@ -440,16 +440,52 @@ var testCases = [
             "    return this._foo\n" +
             "  },\n" +
             "  set foo(v) {\n" +
             "    this._foo = v\n" +
             "  }\n" +
             "}\n"
   },
 
+  {
+    name: "Escaping backslashes in strings",
+    input: "'\\\\'\n",
+    output: "'\\\\'\n"
+  },
+
+  {
+    name: "Escaping carriage return in strings",
+    input: "'\\r'\n",
+    output: "'\\r'\n"
+  },
+
+  {
+    name: "Escaping tab in strings",
+    input: "'\\t'\n",
+    output: "'\\t'\n"
+  },
+
+  {
+    name: "Escaping vertical tab in strings",
+    input: "'\\v'\n",
+    output: "'\\v'\n"
+  },
+
+  {
+    name: "Escaping form feed in strings",
+    input: "'\\f'\n",
+    output: "'\\f'\n"
+  },
+
+  {
+    name: "Escaping null character in strings",
+    input: "'\\0'\n",
+    output: "'\\0'\n"
+  },
+
 ];
 
 var sourceMap = this.sourceMap || require("source-map");
 
 function run_test() {
   testCases.forEach(function (test) {
     console.log(test.name);
 
--- a/toolkit/devtools/server/tests/unit/head_dbg.js
+++ b/toolkit/devtools/server/tests/unit/head_dbg.js
@@ -190,25 +190,16 @@ function initTestTracerServer()
   DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/root.js");
   DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js");
   DebuggerServer.addActors("resource://test/testactors.js");
   DebuggerServer.registerModule("devtools/server/actors/tracer");
   // Allow incoming connections.
   DebuggerServer.init(function () { return true; });
 }
 
-function initSourcesBackwardsCompatDebuggerServer()
-{
-  DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/root.js");
-  DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
-  DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/script.js");
-  DebuggerServer.addActors("resource://test/testcompatactors.js");
-  DebuggerServer.init(function () { return true; });
-}
-
 function finishClient(aClient)
 {
   aClient.close(function() {
     do_test_finished();
   });
 }
 
 /**
--- a/toolkit/devtools/server/tests/unit/test_source-01.js
+++ b/toolkit/devtools/server/tests/unit/test_source-01.js
@@ -39,19 +39,16 @@ const SOURCE_CONTENT = "stopMe()";
 function test_source()
 {
   DebuggerServer.LONG_STRING_LENGTH = 200;
 
   gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
     gThreadClient.getSources(function (aResponse) {
       do_check_true(!!aResponse);
       do_check_true(!!aResponse.sources);
-      gClient.compat.supportsFeature("sources").then(function (supported) {
-        do_check_true(supported);
-      });
 
       let source = aResponse.sources.filter(function (s) {
         return s.url === SOURCE_URL;
       })[0];
 
       do_check_true(!!source);
 
       let sourceClient = gThreadClient.source(source);
deleted file mode 100644
--- a/toolkit/devtools/server/tests/unit/test_sources_backwards_compat-01.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Check that "sources" and "newSource" packets are handled in a backwards
- * compatible way.
- */
-
-var gDebuggee;
-var gClient;
-var gTabClient;
-var gThreadClient;
-
-var gNumTimesSourcesSent = 0;
-
-function run_test()
-{
-  initSourcesBackwardsCompatDebuggerServer();
-  gDebuggee = addTestGlobal("test-sources-compat");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.request = (function (request) {
-    return function (aRequest, aOnResponse) {
-      if (aRequest.type === "sources") {
-        ++gNumTimesSourcesSent;
-      }
-      return request.call(this, aRequest, aOnResponse);
-    };
-  }(gClient.request));
-  gClient.connect(function() {
-    attachTestTabAndResume(gClient, "test-sources-compat", function (aResponse,
-                                                                     aTabClient,
-                                                                     aThreadClient) {
-      gTabClient = aTabClient;
-      gThreadClient = aThreadClient;
-      test_new_source_compatibility();
-    });
-  });
-  do_test_pending();
-}
-
-function test_new_source_compatibility()
-{
-  gClient.addOneTimeListener("newSource", function (aEvent, aPacket) {
-    do_check_eq(aEvent, "newSource");
-    do_check_eq(aPacket.type, "newSource");
-    do_check_true(!!aPacket.source);
-    do_check_true(
-      !!aPacket.source.url.match(/test_sources_backwards_compat-01.js$/));
-
-    gClient.compat.supportsFeature("sources").then(function () {
-      do_check_true(
-        false,
-        "The server shouldn't support sources since we mocked it not to.");
-    }, test_sources_compatibility);
-  });
-
-  gDebuggee.eval(function inc(n) {
-    return n+1;
-  }.toString());
-}
-
-function test_sources_compatibility()
-{
-  gThreadClient.getSources(function (aResponse) {
-    do_check_true(!aResponse.error);
-
-    do_check_true(aResponse.sources.some(function (s) {
-      return s.url.match(/test_sources_backwards_compat-01.js$/);
-    }));
-
-    do_check_true(gNumTimesSourcesSent <= 1,
-                  "Should only send one sources request at most, even though we"
-                  + " might have had to send one to determine feature support.");
-
-    finishClient(gClient);
-  });
-}
deleted file mode 100644
--- a/toolkit/devtools/server/tests/unit/test_sources_backwards_compat-02.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Check getting sources before there are any, in backwards compatibility mode.
- */
-
-var gDebuggee;
-var gClient;
-var gTabClient;
-var gThreadClient;
-
-var gNumTimesSourcesSent = 0;
-
-function run_test()
-{
-  initSourcesBackwardsCompatDebuggerServer();
-  gDebuggee = addTestGlobal("test-sources-compat");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.request = (function (request) {
-    return function (aRequest, aOnResponse) {
-      if (aRequest.type === "sources") {
-        ++gNumTimesSourcesSent;
-      }
-      return request.call(this, aRequest, aOnResponse);
-    };
-  }(gClient.request));
-  gClient.connect(function() {
-    attachTestTabAndResume(gClient, "test-sources-compat", function (aResponse,
-                                                                     aTabClient,
-                                                                     aThreadClient) {
-      gTabClient = aTabClient;
-      gThreadClient = aThreadClient;
-      test_listing_zero_sources_compat();
-    });
-  });
-  do_test_pending();
-}
-
-function test_listing_zero_sources_compat()
-{
-  gThreadClient.getSources(function (aPacket) {
-    do_check_true(!aPacket.error);
-    do_check_true(!!aPacket.sources);
-    do_check_eq(aPacket.sources.length, 0);
-    do_check_true(gNumTimesSourcesSent <= 1,
-                  "Should only send one sources request, even though we might "
-                  + "have had to send one to determine feature support.");
-    finishClient(gClient);
-  });
-}
deleted file mode 100644
--- a/toolkit/devtools/server/tests/unit/testcompatactors.js
+++ /dev/null
@@ -1,118 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-var gTestGlobals = [];
-
-function createRootActor()
-{
-  let actor = {
-    sayHello: function() {
-      this._tabActors = [];
-      for each (let g in gTestGlobals) {
-        let actor = new BrowserTabActor(this.conn);
-        actor.thread = new ThreadActor({}, g);
-
-        actor.json = function() {
-          return { actor: actor.actorID,
-                   url: "http://www.example.com/",
-                   title: actor.thread.global.__name };
-        };
-
-        actor.requestTypes["attach"] = function (aRequest) {
-          dump("actor.thread.actorID = " + actor.thread.actorID + "\n");
-          return {
-            from: actor.actorID,
-            type: "tabAttached",
-            threadActor: actor.thread.actorID
-          };
-        };
-
-        actor.thread.requestTypes["scripts"] = function (aRequest) {
-          return this._discoverSources().then(function () {
-            let scripts = [];
-            for (let s of this.dbg.findScripts()) {
-              if (!s.url) {
-                continue;
-              }
-              let script = {
-                url: s.url,
-                startLine: s.startLine,
-                lineCount: s.lineCount,
-                source: this.sources.source({ url: s.url }).form()
-              };
-              scripts.push(script);
-            }
-
-            return {
-              from: this.actorID,
-              scripts: scripts
-            };
-          }.bind(this));
-        };
-
-        // Pretend that we do not know about the "sources" packet to force the
-        // client to go into its backwards compatibility mode.
-        actor.thread.requestTypes["sources"] = function () {
-          return {
-            error: "unrecognizedPacketType"
-          };
-        };
-
-        let { conn } = this;
-        actor.thread.onNewScript = (function (oldOnNewScript) {
-          return function (aScript) {
-            oldOnNewScript.call(this, aScript);
-            conn.send({
-              from: actor.thread.actorID,
-              type: "newScript",
-              url: aScript.url,
-              startLine: aScript.startLine,
-              lineCount: aScript.lineCount,
-              source: actor.thread.sources.source({ url: aScript.url }).form()
-            });
-          };
-        }(actor.thread.onNewScript));
-
-        this.conn.addActor(actor);
-        this.conn.addActor(actor.thread);
-        this._tabActors.push(actor);
-      }
-
-      this.conn.send = (function (aOldSend) {
-        return function (aPacket) {
-          if (aPacket.type === "newSource") {
-            // Don't send newSource Packets b/c we are an old version of the
-            // RDP!
-            return undefined;
-          } else {
-            return aOldSend.call(this, aPacket);
-          }
-        };
-      }(this.conn.send));
-
-      return { from: "root",
-               applicationType: "xpcshell-tests",
-               traits: {} };
-    },
-
-    listTabs: function(aRequest) {
-      return {
-        from: "root",
-        selected: 0,
-        tabs: [ actor.json() for (actor of this._tabActors) ]
-      };
-    },
-  };
-
-  actor.requestTypes = {
-    "listTabs": actor.listTabs,
-    "echo": function(aRequest) { return aRequest; },
-  };
-  return actor;
-}
-
-DebuggerServer.addTestGlobal = function addTestGlobal(aGlobal)
-{
-  aGlobal.wrappedJSObject = aGlobal;
-  gTestGlobals.push(aGlobal);
-}
--- a/toolkit/devtools/server/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/server/tests/unit/xpcshell.ini
@@ -8,17 +8,16 @@ support-files =
   post_init_global_actors.js
   post_init_tab_actors.js
   pre_init_global_actors.js
   pre_init_tab_actors.js
   registertestactors-01.js
   registertestactors-02.js
   sourcemapped.js
   testactors.js
-  testcompatactors.js
   tracerlocations.js
 
 [test_nesting-01.js]
 [test_nesting-02.js]
 [test_nesting-03.js]
 [test_forwardingprefix.js]
 [test_getyoungestframe.js]
 [test_nsjsinspector.js]
@@ -114,18 +113,16 @@ reason = bug 820380
 [test_breakpoint-17.js]
 [test_breakpoint-18.js]
 [test_eventlooplag_actor.js]
 run-if = toolkit == "gonk"
 [test_listsources-01.js]
 [test_listsources-02.js]
 [test_listsources-03.js]
 [test_new_source-01.js]
-[test_sources_backwards_compat-01.js]
-[test_sources_backwards_compat-02.js]
 [test_sourcemaps-01.js]
 [test_sourcemaps-02.js]
 [test_sourcemaps-03.js]
 [test_sourcemaps-04.js]
 skip-if = toolkit == "gonk"
 reason = bug 820380
 [test_sourcemaps-05.js]
 skip-if = toolkit == "gonk"