[DevTools] store primary UISourceCode in breakpoint instead of projectId

Breakpoint uses primary UISourceCode to set fake breakpoint location and
to track lifetime of breakpoint in UI.
As long as primary UISourceCode exists, we show all breakpoints related
source decorations, when primary UISourceCode is removed, we remove all
decorations and move breakpoint to temporary storage (provisional
breakpoints). As soon as new UISourceCode with breakpoint url is added
we update breakpoint state and restore decorations.

+ migrated Storage to Map.

R=dgozman@chromium.org

Bug: chromium:767571
Change-Id: Ic700d53149e58c912668f954a141f8584713d794
Reviewed-on: https://chromium-review.googlesource.com/915133
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#536405}
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-breakpoints/dynamic-scripts-breakpoints.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-breakpoints/dynamic-scripts-breakpoints.js
index d3bc447..c880aee 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-breakpoints/dynamic-scripts-breakpoints.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-breakpoints/dynamic-scripts-breakpoints.js
@@ -10,7 +10,7 @@
   await TestRunner.navigatePromise(
       'resources/dynamic-scripts-breakpoints.html');
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
   var panel = UI.panels.sources;
 
   SourcesTestRunner.startDebuggerTest();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js
index 1fbd3fe..3057ee2 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js
@@ -26,7 +26,7 @@
     return Promise.resolve();
   }
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
   var panel = UI.panels.sources;
   var scriptFormatter;
   var formattedSourceFrame;
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js
index f34261c..df7b747 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js
@@ -8,7 +8,7 @@
   await TestRunner.showPanel('sources');
   await TestRunner.addScriptTag('../debugger/resources/unformatted2.js');
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
   var panel = UI.panels.sources;
   var scriptFormatter;
   var formattedSourceFrame;
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js
index d0404fc..0fc92e2 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js
@@ -8,7 +8,7 @@
   await TestRunner.showPanel('sources');
   await TestRunner.addScriptTag('../debugger/resources/unformatted3.js');
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
   var panel = UI.panels.sources;
   var scriptFormatter;
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/live-edit-breakpoints.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/live-edit-breakpoints.js
index e492c06..b5c4f9a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/live-edit-breakpoints.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/live-edit-breakpoints.js
@@ -38,7 +38,7 @@
     TestRunner.addResult('    Dumping breakpoints');
     for (var i = 0; i < breakpoints.length; ++i) {
       var breakpoint = breakpoints[i];
-      var uiSourceCode = breakpointManager._workspace.uiSourceCode(breakpoint.projectId(), breakpoint.url());
+      var uiSourceCode = breakpoint._primaryUISourceCode;
       var lineNumber = breakpoint.lineNumber();
       var url = uiSourceCode.url();
       var project = uiSourceCode.project();
@@ -47,7 +47,7 @@
     }
   }
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
 
   SourcesTestRunner.runDebuggerTestSuite([
     function testEditUndo(next) {
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/source-frame-breakpoint-decorations.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/source-frame-breakpoint-decorations.js
index 2d6d5c6..c9782e4 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/source-frame-breakpoint-decorations.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/source-frame-breakpoint-decorations.js
@@ -13,7 +13,7 @@
         .then(() => SourcesTestRunner.dumpJavaScriptSourceFrameBreakpoints(sourceFrame));
   }
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
   SourcesTestRunner.runDebuggerTestSuite([
     function testAddRemoveBreakpoint(next) {
       var javaScriptSourceFrame;
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/source-frame-inline-breakpoint-decorations.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/source-frame-inline-breakpoint-decorations.js
index e5b5f24c..f2cc54d 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/source-frame-inline-breakpoint-decorations.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/source-frame-inline-breakpoint-decorations.js
@@ -33,7 +33,7 @@
         .then(() => SourcesTestRunner.dumpJavaScriptSourceFrameBreakpoints(sourceFrame));
   }
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
   SourcesTestRunner.runDebuggerTestSuite([
     function testAddRemoveBreakpoint(next) {
       var javaScriptSourceFrame;
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1.js b/third_party/WebKit/LayoutTests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1.js
index 23e5782..6b3e221 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1.js
@@ -8,7 +8,7 @@
   await TestRunner.loadModule('sources_test_runner');
   await TestRunner.showPanel('sources');
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
   var panel = UI.panels.sources;
   var scriptFormatter;
   var sourceFrame;
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-4.js b/third_party/WebKit/LayoutTests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-4.js
index e6ce8de..f87e447 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-4.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-4.js
@@ -8,7 +8,7 @@
   await TestRunner.loadModule('sources_test_runner');
   await TestRunner.showPanel('sources');
 
-  Bindings.breakpointManager._storage._breakpoints = {};
+  Bindings.breakpointManager._storage._breakpoints = new Map();
   var panel = UI.panels.sources;
   var scriptFormatter;
 
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/BreakpointManager.js b/third_party/WebKit/Source/devtools/front_end/bindings/BreakpointManager.js
index 795d64e30..2d2ad0d 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/BreakpointManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/BreakpointManager.js
@@ -33,14 +33,13 @@
  */
 Bindings.BreakpointManager = class extends Common.Object {
   /**
-   * @param {?Common.Setting} breakpointsSetting
    * @param {!Workspace.Workspace} workspace
    * @param {!SDK.TargetManager} targetManager
    * @param {!Bindings.DebuggerWorkspaceBinding} debuggerWorkspaceBinding
    */
-  constructor(breakpointsSetting, workspace, targetManager, debuggerWorkspaceBinding) {
+  constructor(workspace, targetManager, debuggerWorkspaceBinding) {
     super();
-    this._storage = new Bindings.BreakpointManager.Storage(this, breakpointsSetting);
+    this._storage = new Bindings.BreakpointManager.Storage();
     this._workspace = workspace;
     this._targetManager = targetManager;
     this._debuggerWorkspaceBinding = debuggerWorkspaceBinding;
@@ -48,8 +47,6 @@
     this._breakpointsActive = true;
     /** @type {!Map<!Workspace.UISourceCode, !Map<number, !Map<number, !Array<!Bindings.BreakpointManager.Breakpoint>>>>} */
     this._breakpointsForUISourceCode = new Map();
-    /** @type {!Map<!Workspace.UISourceCode, !Array<!Bindings.BreakpointManager.Breakpoint>>} */
-    this._breakpointsForPrimaryUISourceCode = new Map();
     /** @type {!Multimap.<string, !Bindings.BreakpointManager.Breakpoint>} */
     this._provisionalBreakpoints = new Multimap();
 
@@ -133,9 +130,7 @@
           breakpointItem.url, breakpointItem.lineNumber, breakpointItem.columnNumber);
       var provisionalBreakpoint = provisionalBreakpoints.get(itemStorageId);
       if (provisionalBreakpoint) {
-        if (!this._breakpointsForPrimaryUISourceCode.get(uiSourceCode))
-          this._breakpointsForPrimaryUISourceCode.set(uiSourceCode, []);
-        this._breakpointsForPrimaryUISourceCode.get(uiSourceCode).push(provisionalBreakpoint);
+        provisionalBreakpoint.setPrimaryUISourceCode(uiSourceCode);
         provisionalBreakpoint._updateBreakpoint();
       } else {
         this._innerSetBreakpoint(
@@ -167,13 +162,12 @@
    * @param {!Workspace.UISourceCode} uiSourceCode
    */
   _removeUISourceCode(uiSourceCode) {
-    var breakpoints = this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [];
-    for (var i = 0; i < breakpoints.length; ++i) {
-      breakpoints[i]._resetLocations();
-      if (breakpoints[i].enabled())
-        this._provisionalBreakpoints.set(uiSourceCode.url(), breakpoints[i]);
+    var breakpoints = uiSourceCode[Bindings.BreakpointManager._breakpointsSymbol] || new Set();
+    for (var breakpoint of breakpoints) {
+      breakpoint._resetLocations();
+      if (breakpoint.enabled())
+        this._provisionalBreakpoints.set(uiSourceCode.url(), breakpoint);
     }
-    this._breakpointsForPrimaryUISourceCode.remove(uiSourceCode);
   }
 
   /**
@@ -210,12 +204,8 @@
       breakpoint._updateState(condition, enabled);
       return breakpoint;
     }
-    var projectId = uiSourceCode.project().id();
     breakpoint = new Bindings.BreakpointManager.Breakpoint(
-        this, projectId, uiSourceCode.url(), lineNumber, columnNumber, condition, enabled);
-    if (!this._breakpointsForPrimaryUISourceCode.get(uiSourceCode))
-      this._breakpointsForPrimaryUISourceCode.set(uiSourceCode, []);
-    this._breakpointsForPrimaryUISourceCode.get(uiSourceCode).push(breakpoint);
+        this, uiSourceCode, uiSourceCode.url(), lineNumber, columnNumber, condition, enabled);
     return breakpoint;
   }
 
@@ -362,7 +352,7 @@
   }
 
   /**
-   * @param {!Set.<!Bindings.BreakpointManager.Breakpoint>} selectedBreakpoints
+   * @param {!Set<!Bindings.BreakpointManager.Breakpoint>} selectedBreakpoints
    */
   removeOtherBreakpoints(selectedBreakpoints) {
     var allBreakpoints = this._allBreakpoints();
@@ -384,9 +374,6 @@
    * @param {boolean} removeFromStorage
    */
   _removeBreakpoint(breakpoint, removeFromStorage) {
-    var uiSourceCode = breakpoint.uiSourceCode();
-    var breakpoints = uiSourceCode ? this._breakpointsForPrimaryUISourceCode.get(uiSourceCode) || [] : [];
-    breakpoints.remove(breakpoint);
     if (removeFromStorage)
       this._storage._removeBreakpoint(breakpoint);
     this._provisionalBreakpoints.delete(breakpoint._url, breakpoint);
@@ -481,24 +468,24 @@
 Bindings.BreakpointManager.Breakpoint = class {
   /**
    * @param {!Bindings.BreakpointManager} breakpointManager
-   * @param {string} projectId
+   * @param {!Workspace.UISourceCode} primaryUISourceCode
    * @param {string} url
    * @param {number} lineNumber
    * @param {number} columnNumber
    * @param {string} condition
    * @param {boolean} enabled
    */
-  constructor(breakpointManager, projectId, url, lineNumber, columnNumber, condition, enabled) {
+  constructor(breakpointManager, primaryUISourceCode, url, lineNumber, columnNumber, condition, enabled) {
     this._breakpointManager = breakpointManager;
-    this._projectId = projectId;
     this._url = url;
     this._lineNumber = lineNumber;
     this._columnNumber = columnNumber;
 
+    this.setPrimaryUISourceCode(primaryUISourceCode);
+
     /** @type {!Map<string, number>} */
     this._numberOfDebuggerLocationForUILocation = new Map();
 
-    // Force breakpoint update.
     /** @type {string} */ this._condition;
     /** @type {boolean} */ this._enabled;
     /** @type {boolean} */ this._isRemoved;
@@ -532,10 +519,18 @@
   }
 
   /**
-   * @return {string}
+   * @param {?Workspace.UISourceCode} primaryUISourceCode
    */
-  projectId() {
-    return this._projectId;
+  setPrimaryUISourceCode(primaryUISourceCode) {
+    var symbol = Bindings.BreakpointManager._breakpointsSymbol;
+    if (this._primaryUISourceCode)
+      this._primaryUISourceCode[symbol].delete(this);
+    this._primaryUISourceCode = primaryUISourceCode;
+    if (!primaryUISourceCode)
+      return;
+    if (!this._primaryUISourceCode[symbol])
+      this._primaryUISourceCode[symbol] = new Set();
+    this._primaryUISourceCode[symbol].add(this);
   }
 
   /**
@@ -560,13 +555,6 @@
   }
 
   /**
-   * @return {?Workspace.UISourceCode}
-   */
-  uiSourceCode() {
-    return this._breakpointManager._workspace.uiSourceCode(this._projectId, this._url);
-  }
-
-  /**
    * @param {?Workspace.UILocation} oldUILocation
    * @param {!Workspace.UILocation} newUILocation
    */
@@ -663,6 +651,7 @@
       modelBreakpoints[i]._removeEventListeners();
     }
 
+    this.setPrimaryUISourceCode(null);
     this._breakpointManager._removeBreakpoint(this, removeFromStorage);
     this._breakpointManager._targetManager.unobserveModels(SDK.DebuggerModel, this);
   }
@@ -685,11 +674,10 @@
     if (this._isRemoved || this._numberOfDebuggerLocationForUILocation.size || this._fakePrimaryLocation)
       return;
 
-    var uiSourceCode = this._breakpointManager._workspace.uiSourceCode(this._projectId, this._url);
-    if (!uiSourceCode)
+    if (!this._primaryUISourceCode)
       return;
 
-    this._fakePrimaryLocation = uiSourceCode.uiLocation(this._lineNumber, this._columnNumber);
+    this._fakePrimaryLocation = this._primaryUISourceCode.uiLocation(this._lineNumber, this._columnNumber);
     if (this._fakePrimaryLocation)
       this._breakpointManager._uiLocationAdded(this, this._fakePrimaryLocation);
   }
@@ -702,6 +690,7 @@
   }
 
   _resetLocations() {
+    this.setPrimaryUISourceCode(null);
     this._removeFakeBreakpointAtPrimaryLocation();
     var modelBreakpoints = this._modelBreakpoints.valuesArray();
     for (var i = 0; i < modelBreakpoints.length; ++i)
@@ -709,6 +698,8 @@
   }
 };
 
+Bindings.BreakpointManager._breakpointsSymbol = Symbol('breakpoints');
+
 /**
  * @unrestricted
  */
@@ -769,7 +760,7 @@
    * @return {boolean}
    */
   _scriptDiverged() {
-    var uiSourceCode = this._breakpoint.uiSourceCode();
+    var uiSourceCode = this._breakpoint._primaryUISourceCode;
     if (!uiSourceCode)
       return false;
     var scriptFile = this._debuggerWorkspaceBinding.scriptFile(uiSourceCode, this._debuggerModel);
@@ -787,7 +778,7 @@
       return;
     }
 
-    var uiSourceCode = this._breakpoint.uiSourceCode();
+    var uiSourceCode = this._breakpoint._primaryUISourceCode;
     var lineNumber = this._breakpoint._lineNumber;
     var columnNumber = this._breakpoint._columnNumber;
     var condition = this._breakpoint.condition();
@@ -952,9 +943,6 @@
   }
 };
 
-/**
- * @unrestricted
- */
 Bindings.BreakpointManager.Breakpoint.State = class {
   /**
    * @param {?string} url
@@ -988,25 +976,18 @@
 };
 
 
-/**
- * @unrestricted
- */
 Bindings.BreakpointManager.Storage = class {
-  /**
-   * @param {!Bindings.BreakpointManager} breakpointManager
-   * @param {?Common.Setting} setting
-   */
-  constructor(breakpointManager, setting) {
-    this._breakpointManager = breakpointManager;
-    this._setting = setting || Common.settings.createLocalSetting('breakpoints', []);
-    var breakpoints = this._setting.get();
-    /** @type {!Object.<string, !Bindings.BreakpointManager.Storage.Item>} */
-    this._breakpoints = {};
-    for (var i = 0; i < breakpoints.length; ++i) {
-      var breakpoint = /** @type {!Bindings.BreakpointManager.Storage.Item} */ (breakpoints[i]);
-      breakpoint.columnNumber = breakpoint.columnNumber || 0;
-      this._breakpoints[breakpoint.url + ':' + breakpoint.lineNumber + ':' + breakpoint.columnNumber] = breakpoint;
+  constructor() {
+    this._setting = Common.settings.createLocalSetting('breakpoints', []);
+    /** @type {!Map<string, !Bindings.BreakpointManager.Storage.Item>} */
+    this._breakpoints = new Map();
+    var items = /** @type {!Array<!Bindings.BreakpointManager.Storage.Item>} */ (this._setting.get());
+    for (var item of items) {
+      item.columnNumber = item.columnNumber || 0;
+      this._breakpoints.set(
+          Bindings.BreakpointManager._breakpointStorageId(item.url, item.columnNumber, item.lineNumber), item);
     }
+    /** @type {boolean|undefined} */ this._muted;
   }
 
   mute() {
@@ -1019,16 +1000,10 @@
 
   /**
    * @param {string} url
-   * @return {!Array.<!Bindings.BreakpointManager.Storage.Item>}
+   * @return {!Array<!Bindings.BreakpointManager.Storage.Item>}
    */
   breakpointItems(url) {
-    var result = [];
-    for (var id in this._breakpoints) {
-      var breakpoint = this._breakpoints[id];
-      if (breakpoint.url === url)
-        result.push(breakpoint);
-    }
-    return result;
+    return Array.from(this._breakpoints.values()).filter(item => item.url === url);
   }
 
   /**
@@ -1037,7 +1012,7 @@
   _updateBreakpoint(breakpoint) {
     if (this._muted || !breakpoint._breakpointStorageId())
       return;
-    this._breakpoints[breakpoint._breakpointStorageId()] = new Bindings.BreakpointManager.Storage.Item(breakpoint);
+    this._breakpoints.set(breakpoint._breakpointStorageId(), new Bindings.BreakpointManager.Storage.Item(breakpoint));
     this._save();
   }
 
@@ -1047,15 +1022,12 @@
   _removeBreakpoint(breakpoint) {
     if (this._muted)
       return;
-    delete this._breakpoints[breakpoint._breakpointStorageId()];
+    this._breakpoints.delete(breakpoint._breakpointStorageId());
     this._save();
   }
 
   _save() {
-    var breakpointsArray = [];
-    for (var id in this._breakpoints)
-      breakpointsArray.push(this._breakpoints[id]);
-    this._setting.set(breakpointsArray);
+    this._setting.set(Array.from(this._breakpoints.values()));
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index 17816ab..8702031a 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -189,7 +189,7 @@
     Bindings.cssWorkspaceBinding = new Bindings.CSSWorkspaceBinding(SDK.targetManager, Workspace.workspace);
     Bindings.debuggerWorkspaceBinding = new Bindings.DebuggerWorkspaceBinding(SDK.targetManager, Workspace.workspace);
     Bindings.breakpointManager =
-        new Bindings.BreakpointManager(null, Workspace.workspace, SDK.targetManager, Bindings.debuggerWorkspaceBinding);
+        new Bindings.BreakpointManager(Workspace.workspace, SDK.targetManager, Bindings.debuggerWorkspaceBinding);
     Extensions.extensionServer = new Extensions.ExtensionServer();
 
     new Persistence.FileSystemWorkspaceBinding(Persistence.isolatedFileSystemManager, Workspace.workspace);