Bug 1195869 - Refresh tabs in sidebar mode. r=jryans
authorJennifer Fong <jfong@mozilla.com>
Fri, 21 Aug 2015 11:09:00 -0400
changeset 259137 6aea2f75ee3068897159d68b1395bf4092dfc63e
parent 259136 e059e455e5b37d713bd198c6ebbc3ffa4cfe8f75
child 259138 731d8351c98d9b11ff62e654833a0776ecd182ce
push id29269
push userryanvm@gmail.com
push dateTue, 25 Aug 2015 00:57:59 +0000
treeherdermozilla-central@04b8c412d9f5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1195869
milestone43.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 1195869 - Refresh tabs in sidebar mode. r=jryans
browser/devtools/webide/content/project-listing.js
browser/devtools/webide/content/project-listing.xhtml
browser/devtools/webide/content/webide.js
browser/devtools/webide/modules/project-list.js
browser/devtools/webide/test/sidebars/browser_tabs.js
browser/devtools/webide/test/sidebars/test_duplicate_import.html
browser/devtools/webide/test/sidebars/test_import.html
browser/devtools/webide/themes/panel-listing.css
browser/locales/en-US/chrome/browser/devtools/webide.dtd
--- a/browser/devtools/webide/content/project-listing.js
+++ b/browser/devtools/webide/content/project-listing.js
@@ -1,32 +1,39 @@
 /* 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/. */
 
+/* eslint-env browser */
+
 const Cu = Components.utils;
 const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const ProjectList = require("devtools/webide/project-list");
 
 let projectList = new ProjectList(window, window.parent);
 
 window.addEventListener("load", function onLoad() {
   window.removeEventListener("load", onLoad, true);
   document.getElementById("new-app").onclick = CreateNewApp;
   document.getElementById("hosted-app").onclick = ImportHostedApp;
   document.getElementById("packaged-app").onclick = ImportPackagedApp;
+  document.getElementById("refresh-tabs").onclick = RefreshTabs;
   projectList.update();
   projectList.updateCommands();
 }, true);
 
 window.addEventListener("unload", function onUnload() {
   window.removeEventListener("unload", onUnload);
   projectList.destroy();
 });
 
+function RefreshTabs() {
+  projectList.refreshTabs();
+}
+
 function CreateNewApp() {
   projectList.newApp();
 }
 
 function ImportHostedApp() {
   projectList.importHostedApp();
 }
 
--- a/browser/devtools/webide/content/project-listing.xhtml
+++ b/browser/devtools/webide/content/project-listing.xhtml
@@ -20,14 +20,16 @@
       <div id="project-panel-box">
         <button class="panel-item project-panel-item-newapp" id="new-app">&projectMenu_newApp_label;</button>
         <button class="panel-item project-panel-item-openpackaged" id="packaged-app">&projectMenu_importPackagedApp_label;</button>
         <button class="panel-item project-panel-item-openhosted" id="hosted-app">&projectMenu_importHostedApp_label;</button>
         <label class="panel-header">&projectPanel_myProjects;</label>
         <div id="project-panel-projects"></div>
         <label class="panel-header" id="panel-header-runtimeapps" hidden="true">&projectPanel_runtimeApps;</label>
         <div id="project-panel-runtimeapps"/>
-        <label class="panel-header" id="panel-header-tabs" hidden="true">&projectPanel_tabs;</label>
+        <label class="panel-header" id="panel-header-tabs" hidden="true">&projectPanel_tabs;
+          <button class="panel-item project-panel-item-refreshtabs" id="refresh-tabs">&projectMenu_refreshTabs_label;</button>
+        </label>
         <div id="project-panel-tabs"/>
       </div>
     </div>
   </body>
 </html>
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -1110,24 +1110,19 @@ let Cmds = {
 
   showProjectPanel: function() {
     if (projectList.sidebarsEnabled) {
       ProjectPanel.toggleSidebar();
     } else {
       ProjectPanel.showPopup();
     }
 
-    // There are currently no available events to listen for when an unselected
-    // tab navigates.  Since we show every tab's location in the project menu,
-    // we re-list all the tabs each time the menu is displayed.
-    // TODO: An event-based solution will be needed for the sidebar UI.
+    // TODO: Remove this check if/when we remove the dropdown view.
     if (!projectList.sidebarsEnabled && AppManager.connected) {
-      return AppManager.listTabs().then(() => {
-        projectList.updateTabs();
-      }).catch(console.error);
+      projectList.refreshTabs();
     }
 
     return promise.resolve();
   },
 
   showRuntimePanel: function() {
     RuntimeScanners.scan();
 
--- a/browser/devtools/webide/modules/project-list.js
+++ b/browser/devtools/webide/modules/project-list.js
@@ -143,16 +143,24 @@ ProjectList.prototype = {
       opts.panel.appendChild(icon);
       opts.panel.appendChild(span);
     } else {
       opts.panel.setAttribute("label", opts.name);
       opts.panel.setAttribute("image", opts.icon);
     }
   },
 
+  refreshTabs: function() {
+    if (AppManager.connected) {
+      return AppManager.listTabs().then(() => {
+        this.updateTabs();
+      }).catch(console.error);
+    }
+  },
+
   updateTabs: function() {
     let tabsHeaderNode = this._doc.querySelector("#panel-header-tabs");
     let tabsNode = this._doc.querySelector("#project-panel-tabs");
 
     while (tabsNode.hasChildNodes()) {
       tabsNode.firstChild.remove();
     }
 
@@ -178,37 +186,39 @@ ProjectList.prototype = {
       } catch (e) {
         // Don't try to handle invalid URLs, especially from Valence.
         continue;
       }
       // Wanted to use nsIFaviconService here, but it only works for visited
       // tabs, so that's no help for any remote tabs.  Maybe some favicon wizard
       // knows how to get high-res favicons easily, or we could offer actor
       // support for this (bug 1061654).
-      tab.favicon = url.origin + "/favicon.ico";
+      if (url.origin) {
+        tab.favicon = url.origin + "/favicon.ico";
+      }
       tab.name = tab.title || Strings.GetStringFromName("project_tab_loading");
       if (url.protocol.startsWith("http")) {
         tab.name = url.hostname + ": " + tab.name;
       }
       let panelItemNode = this._doc.createElement(this._panelNodeEl);
       panelItemNode.className = "panel-item";
       tabsNode.appendChild(panelItemNode);
       this._renderProjectItem({
         panel: panelItemNode,
         name: tab.name,
-        icon: tab.favicon
+        icon: tab.favicon || AppManager.DEFAULT_PROJECT_ICON
       });
       panelItemNode.addEventListener("click", () => {
         if (!this._sidebarsEnabled) {
           this._UI.hidePanels();
         }
         AppManager.selectedProject = {
           type: "tab",
           app: tab,
-          icon: tab.favicon,
+          icon: tab.favicon || AppManager.DEFAULT_PROJECT_ICON,
           location: tab.url,
           name: tab.name
         };
       }, true);
     }
 
     return promise.resolve();
   },
--- a/browser/devtools/webide/test/sidebars/browser_tabs.js
+++ b/browser/devtools/webide/test/sidebars/browser_tabs.js
@@ -41,16 +41,30 @@ function test() {
     // Ensure tab list changes are noticed
     let tabsNode = docProject.querySelector("#project-panel-tabs");
     is(tabsNode.querySelectorAll(".panel-item").length, 2, "2 tabs available");
     yield removeTab(tab);
     yield waitForUpdate(win, "project");
     yield waitForUpdate(win, "runtime-targets");
     is(tabsNode.querySelectorAll(".panel-item").length, 1, "1 tab available");
 
+    tab = yield addTab(TEST_URI);
+
+    is(tabsNode.querySelectorAll(".panel-item").length, 2, "2 tabs available");
+
+    yield removeTab(tab);
+
+    is(tabsNode.querySelectorAll(".panel-item").length, 2, "2 tabs available");
+
+    docProject.querySelector("#refresh-tabs").click();
+
+    yield waitForUpdate(win, "runtime-targets");
+
+    is(tabsNode.querySelectorAll(".panel-item").length, 1, "1 tab available");
+
     yield win.Cmds.disconnectRuntime();
     yield closeWebIDE(win);
 
     DebuggerServer.destroy();
   }).then(finish, handleError);
 }
 
 function connectToLocal(win, docRuntime) {
--- a/browser/devtools/webide/test/sidebars/test_duplicate_import.html
+++ b/browser/devtools/webide/test/sidebars/test_duplicate_import.html
@@ -49,18 +49,18 @@
           yield win.projectList.importHostedApp(hostedAppManifest);
           yield waitForUpdate(win, "project-validated");
           project = win.AppManager.selectedProject;
           is(project.location, hostedAppManifest, "Correctly reselected existing hosted app.");
           yield nextTick();
 
           let panelNode = docProject.querySelector("#project-panel");
           let items = panelNode.querySelectorAll(".panel-item");
-          // 3 controls, + 2 projects
-          is(items.length, 5, "5 projects in panel");
+          // 4 controls, + 2 projects
+          is(items.length, 6, "6 projects in panel");
           is(items[3].querySelector("span").textContent, "A name (in app directory)", "Panel text is correct");
           is(items[4].querySelector("span").textContent, "hosted manifest name property", "Panel text is correct");
 
           yield closeWebIDE(win);
 
           yield removeAllProjects();
 
           SimpleTest.finish();
--- a/browser/devtools/webide/test/sidebars/test_import.html
+++ b/browser/devtools/webide/test/sidebars/test_import.html
@@ -54,18 +54,18 @@
           yield win.projectList.importHostedApp(hostedAppManifest);
           yield waitForUpdate(win, "project-validated");
 
           project = win.AppManager.selectedProject;
           ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");
 
           let panelNode = docProject.querySelector("#project-panel");
           let items = panelNode.querySelectorAll(".panel-item");
-          // 3 controls, + 2 projects
-          is(items.length, 6, "6 projects in panel");
+          // 4 controls, + 2 projects
+          is(items.length, 7, "7 projects in panel");
           is(items[3].querySelector("span").textContent, "A name (in app directory)", "Panel text is correct");
           is(items[4].querySelector("span").textContent, "hosted manifest name property", "Panel text is correct");
 
           yield closeWebIDE(win);
 
           yield removeAllProjects();
 
           SimpleTest.finish();
--- a/browser/devtools/webide/themes/panel-listing.css
+++ b/browser/devtools/webide/themes/panel-listing.css
@@ -67,16 +67,25 @@ label,
 button.panel-item {
   background-position: 8px 8px;
   background-repeat: no-repeat;
   background-size: 14px 14px;
   padding-left: 25px;
   width: 100%;
 }
 
+button.project-panel-item-refreshtabs {
+  display: inline-block;
+  float: right;
+  padding: 3px;
+  text-transform: none;
+  width: auto;
+  margin: 0 4px 5px 5px;
+}
+
 .panel-item:disabled {
   background-color: #FFF;
   color: #5A5A5A;
   opacity: 0.5;
 }
 
 .panel-item:not(:disabled):hover {
   background-color: #CCF0FD;
--- a/browser/locales/en-US/chrome/browser/devtools/webide.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/webide.dtd
@@ -21,16 +21,18 @@
 <!ENTITY projectMenu_debug_label "Debug App">
 <!ENTITY projectMenu_debug_accesskey "D">
 <!ENTITY projectMenu_remove_label "Remove Project">
 <!ENTITY projectMenu_remove_accesskey "R">
 <!ENTITY projectMenu_showPrefs_label "Preferences">
 <!ENTITY projectMenu_showPrefs_accesskey "e">
 <!ENTITY projectMenu_manageComponents_label "Manage Extra Components">
 <!ENTITY projectMenu_manageComponents_accesskey "M">
+<!ENTITY projectMenu_refreshTabs_label "Refresh Tabs">
+<!ENTITY projectMenu_refreshTabs_accesskey "U">
 
 <!ENTITY runtimeMenu_label "Runtime">
 <!ENTITY runtimeMenu_accesskey "R">
 <!ENTITY runtimeMenu_disconnect_label "Disconnect">
 <!ENTITY runtimeMenu_disconnect_accesskey "D">
 <!ENTITY runtimeMenu_showPermissionTable_label "Permissions Table">
 <!ENTITY runtimeMenu_showPermissionTable_accesskey "P">
 <!ENTITY runtimeMenu_takeScreenshot_label "Screenshot">