☠☠ backed out by d429bacbed6c ☠ ☠ | |
author | Tim Nguyen <ntim.bugs@gmail.com> |
Tue, 08 Sep 2015 10:38:03 +0530 | |
changeset 261297 | 78a423ab972e6a7de5effb903f49ac7a056d60d8 |
parent 261296 | ec99f1dfd66858d2d8226d3bad55d95b7f9349dc |
child 261298 | eb7ef800ed124c654c8096dd86ec300a12a1c2ce |
push id | 64705 |
push user | cbook@mozilla.com |
push date | Tue, 08 Sep 2015 14:02:43 +0000 |
treeherder | mozilla-inbound@7fa38a962661 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | miker |
bugs | 1171903 |
milestone | 43.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
|
browser/devtools/shared/widgets/TableWidget.js | file | annotate | diff | comparison | revisions | |
browser/devtools/storage/ui.js | file | annotate | diff | comparison | revisions |
--- a/browser/devtools/shared/widgets/TableWidget.js +++ b/browser/devtools/shared/widgets/TableWidget.js @@ -1,29 +1,35 @@ /* 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"; const {Cc, Ci, Cu} = require("chrome"); +const EventEmitter = require("devtools/toolkit/event-emitter"); -const EventEmitter = require("devtools/toolkit/event-emitter"); +loader.lazyImporter(this, "setNamedTimeout", + "resource:///modules/devtools/ViewHelpers.jsm"); +loader.lazyImporter(this, "clearNamedTimeout", + "resource:///modules/devtools/ViewHelpers.jsm"); + const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; const HTML_NS = "http://www.w3.org/1999/xhtml"; - +const AFTER_SCROLL_DELAY = 100; // Different types of events emitted by the Various components of the TableWidget const EVENTS = { TABLE_CLEARED: "table-cleared", COLUMN_SORTED: "column-sorted", COLUMN_TOGGLED: "column-toggled", ROW_SELECTED: "row-selected", ROW_UPDATED: "row-updated", HEADER_CONTEXT_MENU: "header-context-menu", - ROW_CONTEXT_MENU: "row-context-menu" + ROW_CONTEXT_MENU: "row-context-menu", + SCROLL_END: "scroll-end" }; // Maximum number of character visible in any cell in the table. This is to avoid // making the cell take up all the space in a row. const MAX_VISIBLE_STRING_SIZE = 100; /** * A table widget with various features like resizble/toggleable columns, @@ -57,16 +63,18 @@ function TableWidget(node, options={}) { this.highlightUpdated = highlightUpdated || false; this.removableColumns = removableColumns !== false; this.tbody = this.document.createElementNS(XUL_NS, "hbox"); this.tbody.className = "table-widget-body theme-body"; this.tbody.setAttribute("flex", "1"); this.tbody.setAttribute("tabindex", "0"); this._parent.appendChild(this.tbody); + this.afterScroll = this.afterScroll.bind(this); + this.tbody.addEventListener("scroll", this.onScroll.bind(this)); this.placeholder = this.document.createElementNS(XUL_NS, "label"); this.placeholder.className = "plain table-widget-empty-text"; this.placeholder.setAttribute("flex", "1"); this._parent.appendChild(this.placeholder); this.items = new Map(); this.columns = new Map(); @@ -418,16 +426,37 @@ TableWidget.prototype = { } let sortedItems = this.columns.get(column).sort([...this.items.values()]); for (let [id, column] of this.columns) { if (id != column) { column.sort(sortedItems); } } + }, + + /** + * Calls the afterScroll function when the user has stopped scrolling + */ + onScroll: function() { + clearNamedTimeout("table-scroll"); + setNamedTimeout("table-scroll", AFTER_SCROLL_DELAY, this.afterScroll); + }, + + /** + * Emits the "scroll-end" event when the whole table is scrolled + */ + afterScroll: function() { + let scrollHeight = this.tbody.getBoundingClientRect().height - + this.tbody.querySelector(".table-widget-column-header").clientHeight; + + // Emit scroll-end event when 9/10 of the table is scrolled + if (this.tbody.scrollTop >= 0.9 * scrollHeight) { + this.emit("scroll-end"); + } } }; TableWidget.EVENTS = EVENTS; module.exports.TableWidget = TableWidget; /**
--- a/browser/devtools/storage/ui.js +++ b/browser/devtools/storage/ui.js @@ -67,16 +67,19 @@ let StorageUI = this.StorageUI = functio let tableNode = this._panelDoc.getElementById("storage-table"); this.table = new TableWidget(tableNode, { emptyText: L10N.getStr("table.emptyText"), highlightUpdated: true, }); this.displayObjectSidebar = this.displayObjectSidebar.bind(this); this.table.on(TableWidget.EVENTS.ROW_SELECTED, this.displayObjectSidebar); + this.handleScrollEnd = this.handleScrollEnd.bind(this); + this.table.on(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd); + this.sidebar = this._panelDoc.getElementById("storage-sidebar"); this.sidebar.setAttribute("width", "300"); this.view = new VariablesView(this.sidebar.firstChild, GENERIC_VARIABLES_VIEW_SETTINGS); this.front.listStores().then(storageTypes => { this.populateStorageTree(storageTypes); }).then(null, console.error); @@ -94,16 +97,17 @@ let StorageUI = this.StorageUI = functio }; exports.StorageUI = StorageUI; StorageUI.prototype = { storageTypes: null, shouldResetColumns: true, + shouldLoadMoreItems: true, destroy: function() { this.front.off("stores-update", this.onUpdate); this.front.off("stores-cleared", this.onCleared); this._panelDoc.removeEventListener("keypress", this.handleKeypress); this._telemetry.toolClosed("storage"); }, @@ -297,21 +301,24 @@ StorageUI.prototype = { * * @param {string} type * The type of storage. Ex. "cookies" * @param {string} host * Hostname * @param {array} names * Names of particular store objects. Empty if all are requested * @param {number} reason - * 2 for update, 1 for new row in an existing table and 0 when - * populating a table for the first time for the given host/type + * 3 for loading next 50 items, 2 for update, 1 for new row in an + * existing table and 0 when populating a table for the first time + * for the given host/type */ fetchStorageObjects: function(type, host, names, reason) { - this.storageTypes[type].getStoreObjects(host, names).then(({data}) => { + let fetchOpts = reason === 3 ? {offset: this.itemOffset} + : {}; + this.storageTypes[type].getStoreObjects(host, names, fetchOpts).then(({data}) => { if (!data.length) { this.emit("store-objects-updated"); return; } if (this.shouldResetColumns) { this.resetColumns(data[0], type); } this.populateTable(data, reason); @@ -532,16 +539,17 @@ StorageUI.prototype = { if (!host) { return; } if (item.length > 2) { names = [JSON.stringify(item.slice(2))]; } this.shouldResetColumns = true; this.fetchStorageObjects(type, host, names, 0); + this.itemOffset = 0; }, /** * Resets the column headers in the storage table with the pased object `data` * * @param {object} data * The object from which key and values will be used for naming the * headers of the columns @@ -564,19 +572,19 @@ StorageUI.prototype = { }, /** * Populates or updates the rows in the storage table. * * @param {array[object]} data * Array of objects to be populated in the storage table * @param {number} reason - * The reason of this populateTable call. 2 for update, 1 for new row - * in an existing table and 0 when populating a table for the first - * time for the given host/type + * The reason of this populateTable call. 3 for loading next 50 items, + * 2 for update, 1 for new row in an existing table and 0 when + * populating a table for the first time for the given host/type */ populateTable: function(data, reason) { for (let item of data) { if (item.value) { item.valueActor = item.value; item.value = item.value.initial || ""; } if (item.expires != null) { @@ -585,34 +593,52 @@ StorageUI.prototype = { : L10N.getStr("label.expires.session"); } if (item.creationTime != null) { item.creationTime = new Date(item.creationTime).toLocaleString(); } if (item.lastAccessed != null) { item.lastAccessed = new Date(item.lastAccessed).toLocaleString(); } - if (reason < 2) { + if (reason < 2 || reason == 3) { this.table.push(item, reason == 0); } else { this.table.update(item); if (item == this.table.selectedRow && !this.sidebar.hidden) { this.displayObjectSidebar(); } } + this.shouldLoadMoreItems = true; } }, /** * Handles keypress event on the body table to close the sidebar when open * * @param {DOMEvent} event * The event passed by the keypress event. */ handleKeypress: function(event) { if (event.keyCode == event.DOM_VK_ESCAPE && !this.sidebar.hidden) { // Stop Propagation to prevent opening up of split console this.hideSidebar(); event.stopPropagation(); event.preventDefault(); } + }, + + /** + * Handles endless scrolling for the table + */ + handleScrollEnd: function() { + if (!this.shouldLoadMoreItems) return; + this.shouldLoadMoreItems = false; + this.itemOffset += 50; + + let item = this.tree.selectedItem; + let [type, host, db, objectStore] = item; + let names = null; + if (item.length > 2) { + names = [JSON.stringify(item.slice(2))]; + } + this.fetchStorageObjects(type, host, names, 3); } };