mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
merge m-c to fx-team
This commit is contained in:
commit
a0f0cb5e8d
@ -15,6 +15,8 @@
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/utilityOverlay.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
|
@ -10,6 +10,9 @@
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/utilityOverlay.js"/>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
var gOpenerWnd = window.opener.wrappedJSObject;
|
||||
|
@ -15,6 +15,8 @@
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/utilityOverlay.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/utilityOverlay.js"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
|
@ -1110,5 +1110,11 @@ pref("prompts.tab_modal.enabled", true);
|
||||
// Whether the Panorama should animate going in/out of tabs
|
||||
pref("browser.panorama.animate_zoom", true);
|
||||
|
||||
// Defines the url to be used for new tabs.
|
||||
pref("browser.newtab.url", "about:blank");
|
||||
|
||||
// Toggles the content of 'about:newtab'. Shows the grid when enabled.
|
||||
pref("browser.newtabpage.enabled", false);
|
||||
|
||||
// Enable the DOM full-screen API.
|
||||
pref("full-screen-api.enabled", true);
|
||||
|
@ -225,7 +225,7 @@ var FullZoom = {
|
||||
return;
|
||||
|
||||
// Avoid the cps roundtrip and apply the default/global pref.
|
||||
if (aURI.spec == "about:blank") {
|
||||
if (isBlankPageURL(aURI.spec)) {
|
||||
this._applyPrefToSetting(undefined, aBrowser);
|
||||
return;
|
||||
}
|
||||
|
@ -1012,7 +1012,7 @@ var PlacesStarButton = {
|
||||
}
|
||||
|
||||
// We can load about:blank before the actual page, but there is no point in handling that page.
|
||||
if (this._uri.spec == "about:blank") {
|
||||
if (isBlankPageURL(this._uri.spec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -188,6 +188,7 @@ XPCOMUtils.defineLazyGetter(this, "Tilt", function() {
|
||||
|
||||
let gInitialPages = [
|
||||
"about:blank",
|
||||
"about:newtab",
|
||||
"about:privatebrowsing",
|
||||
"about:sessionrestore"
|
||||
];
|
||||
@ -2204,7 +2205,7 @@ function openLocation() {
|
||||
else {
|
||||
// If there are no open browser windows, open a new one
|
||||
win = window.openDialog("chrome://browser/content/", "_blank",
|
||||
"chrome,all,dialog=no", "about:blank");
|
||||
"chrome,all,dialog=no", BROWSER_NEW_TAB_URL);
|
||||
win.addEventListener("load", openLocationCallback, false);
|
||||
}
|
||||
return;
|
||||
@ -2222,7 +2223,7 @@ function openLocationCallback()
|
||||
|
||||
function BrowserOpenTab()
|
||||
{
|
||||
openUILinkIn("about:blank", "tab");
|
||||
openUILinkIn(BROWSER_NEW_TAB_URL, "tab");
|
||||
}
|
||||
|
||||
/* Called from the openLocation dialog. This allows that dialog to instruct
|
||||
@ -2557,7 +2558,7 @@ function URLBarSetURI(aURI) {
|
||||
else
|
||||
value = losslessDecodeURI(uri);
|
||||
|
||||
valid = (uri.spec != "about:blank");
|
||||
valid = !isBlankPageURL(uri.spec);
|
||||
}
|
||||
|
||||
gURLBar.value = value;
|
||||
@ -2874,7 +2875,7 @@ function getMeOutOfHere() {
|
||||
// Get the start page from the *default* pref branch, not the user's
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService).getDefaultBranch(null);
|
||||
var url = "about:blank";
|
||||
var url = BROWSER_NEW_TAB_URL;
|
||||
try {
|
||||
url = prefs.getComplexValue("browser.startup.homepage",
|
||||
Ci.nsIPrefLocalizedString).data;
|
||||
@ -5192,7 +5193,7 @@ nsBrowserAccess.prototype = {
|
||||
case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
|
||||
// FIXME: Bug 408379. So how come this doesn't send the
|
||||
// referrer like the other loads do?
|
||||
var url = aURI ? aURI.spec : "about:blank";
|
||||
var url = aURI ? aURI.spec : BROWSER_NEW_TAB_URL;
|
||||
// Pass all params to openDialog to ensure that "url" isn't passed through
|
||||
// loadOneOrMoreURIs, which splits based on "|"
|
||||
newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
|
||||
@ -5225,7 +5226,7 @@ nsBrowserAccess.prototype = {
|
||||
let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
|
||||
let referrer = aOpener ? makeURI(aOpener.location.href) : null;
|
||||
|
||||
let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
|
||||
let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : BROWSER_NEW_TAB_URL, {
|
||||
referrerURI: referrer,
|
||||
fromExternal: isExternal,
|
||||
inBackground: loadInBackground});
|
||||
@ -7774,9 +7775,11 @@ function undoCloseWindow(aIndex) {
|
||||
*/
|
||||
function isTabEmpty(aTab) {
|
||||
let browser = aTab.linkedBrowser;
|
||||
let uri = browser.currentURI.spec;
|
||||
let body = browser.contentDocument.body;
|
||||
return browser.sessionHistory.count < 2 &&
|
||||
browser.currentURI.spec == "about:blank" &&
|
||||
!browser.contentDocument.body.hasChildNodes() &&
|
||||
isBlankPageURL(uri) &&
|
||||
(!body || !body.hasChildNodes()) &&
|
||||
!aTab.hasAttribute("busy");
|
||||
}
|
||||
|
||||
|
@ -391,7 +391,7 @@
|
||||
<panel id="customizeToolbarSheetPopup"
|
||||
noautohide="true">
|
||||
<iframe id="customizeToolbarSheetIFrame"
|
||||
style="&dialog.style;"
|
||||
style="&dialog.dimensions;"
|
||||
hidden="true"/>
|
||||
</panel>
|
||||
|
||||
|
76
browser/base/content/newtab/batch.js
Normal file
76
browser/base/content/newtab/batch.js
Normal file
@ -0,0 +1,76 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This class makes it easy to wait until a batch of callbacks has finished.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* let batch = new Batch(function () alert("finished"));
|
||||
* let pop = batch.pop.bind(batch);
|
||||
*
|
||||
* for (let i = 0; i < 5; i++) {
|
||||
* batch.push();
|
||||
* setTimeout(pop, i * 1000);
|
||||
* }
|
||||
*
|
||||
* batch.close();
|
||||
*/
|
||||
function Batch(aCallback) {
|
||||
this._callback = aCallback;
|
||||
}
|
||||
|
||||
Batch.prototype = {
|
||||
/**
|
||||
* The number of batch entries.
|
||||
*/
|
||||
_count: 0,
|
||||
|
||||
/**
|
||||
* Whether this batch is closed.
|
||||
*/
|
||||
_closed: false,
|
||||
|
||||
/**
|
||||
* Increases the number of batch entries by one.
|
||||
*/
|
||||
push: function Batch_push() {
|
||||
if (!this._closed)
|
||||
this._count++;
|
||||
},
|
||||
|
||||
/**
|
||||
* Decreases the number of batch entries by one.
|
||||
*/
|
||||
pop: function Batch_pop() {
|
||||
if (this._count)
|
||||
this._count--;
|
||||
|
||||
if (this._closed)
|
||||
this._check();
|
||||
},
|
||||
|
||||
/**
|
||||
* Closes the batch so that no new entries can be added.
|
||||
*/
|
||||
close: function Batch_close() {
|
||||
if (this._closed)
|
||||
return;
|
||||
|
||||
this._closed = true;
|
||||
this._check();
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the batch has finished.
|
||||
*/
|
||||
_check: function Batch_check() {
|
||||
if (this._count == 0 && this._callback) {
|
||||
this._callback();
|
||||
this._callback = null;
|
||||
}
|
||||
}
|
||||
};
|
137
browser/base/content/newtab/cells.js
Normal file
137
browser/base/content/newtab/cells.js
Normal file
@ -0,0 +1,137 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This class manages a cell's DOM node (not the actually cell content, a site).
|
||||
* It's mostly read-only, i.e. all manipulation of both position and content
|
||||
* aren't handled here.
|
||||
*/
|
||||
function Cell(aGrid, aNode) {
|
||||
this._grid = aGrid;
|
||||
this._node = aNode;
|
||||
this._node._newtabCell = this;
|
||||
|
||||
// Register drag-and-drop event handlers.
|
||||
["DragEnter", "DragOver", "DragExit", "Drop"].forEach(function (aType) {
|
||||
let method = "on" + aType;
|
||||
this[method] = this[method].bind(this);
|
||||
this._node.addEventListener(aType.toLowerCase(), this[method], false);
|
||||
}, this);
|
||||
}
|
||||
|
||||
Cell.prototype = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
_grid: null,
|
||||
|
||||
/**
|
||||
* The cell's DOM node.
|
||||
*/
|
||||
get node() this._node,
|
||||
|
||||
/**
|
||||
* The cell's offset in the grid.
|
||||
*/
|
||||
get index() {
|
||||
let index = this._grid.cells.indexOf(this);
|
||||
|
||||
// Cache this value, overwrite the getter.
|
||||
Object.defineProperty(this, "index", {value: index, enumerable: true});
|
||||
|
||||
return index;
|
||||
},
|
||||
|
||||
/**
|
||||
* The previous cell in the grid.
|
||||
*/
|
||||
get previousSibling() {
|
||||
let prev = this.node.previousElementSibling;
|
||||
prev = prev && prev._newtabCell;
|
||||
|
||||
// Cache this value, overwrite the getter.
|
||||
Object.defineProperty(this, "previousSibling", {value: prev, enumerable: true});
|
||||
|
||||
return prev;
|
||||
},
|
||||
|
||||
/**
|
||||
* The next cell in the grid.
|
||||
*/
|
||||
get nextSibling() {
|
||||
let next = this.node.nextElementSibling;
|
||||
next = next && next._newtabCell;
|
||||
|
||||
// Cache this value, overwrite the getter.
|
||||
Object.defineProperty(this, "nextSibling", {value: next, enumerable: true});
|
||||
|
||||
return next;
|
||||
},
|
||||
|
||||
/**
|
||||
* The site contained in the cell, if any.
|
||||
*/
|
||||
get site() {
|
||||
let firstChild = this.node.firstElementChild;
|
||||
return firstChild && firstChild._newtabSite;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the cell contains a pinned site.
|
||||
* @return Whether the cell contains a pinned site.
|
||||
*/
|
||||
containsPinnedSite: function Cell_containsPinnedSite() {
|
||||
let site = this.site;
|
||||
return site && site.isPinned();
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the cell contains a site (is empty).
|
||||
* @return Whether the cell is empty.
|
||||
*/
|
||||
isEmpty: function Cell_isEmpty() {
|
||||
return !this.site;
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragenter' event.
|
||||
* @param aEvent The dragenter event.
|
||||
*/
|
||||
onDragEnter: function Cell_onDragEnter(aEvent) {
|
||||
if (gDrag.isValid(aEvent)) {
|
||||
aEvent.preventDefault();
|
||||
gDrop.enter(this, aEvent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragover' event.
|
||||
* @param aEvent The dragover event.
|
||||
*/
|
||||
onDragOver: function Cell_onDragOver(aEvent) {
|
||||
if (gDrag.isValid(aEvent))
|
||||
aEvent.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragexit' event.
|
||||
* @param aEvent The dragexit event.
|
||||
*/
|
||||
onDragExit: function Cell_onDragExit(aEvent) {
|
||||
gDrop.exit(this, aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'drop' event.
|
||||
* @param aEvent The drop event.
|
||||
*/
|
||||
onDrop: function Cell_onDrop(aEvent) {
|
||||
if (gDrag.isValid(aEvent)) {
|
||||
aEvent.preventDefault();
|
||||
gDrop.drop(this, aEvent);
|
||||
}
|
||||
}
|
||||
};
|
140
browser/base/content/newtab/drag.js
Normal file
140
browser/base/content/newtab/drag.js
Normal file
@ -0,0 +1,140 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton implements site dragging functionality.
|
||||
*/
|
||||
let gDrag = {
|
||||
/**
|
||||
* The site offset to the drag start point.
|
||||
*/
|
||||
_offsetX: null,
|
||||
_offsetY: null,
|
||||
|
||||
/**
|
||||
* The site that is dragged.
|
||||
*/
|
||||
_draggedSite: null,
|
||||
get draggedSite() this._draggedSite,
|
||||
|
||||
/**
|
||||
* The cell width/height at the point the drag started.
|
||||
*/
|
||||
_cellWidth: null,
|
||||
_cellHeight: null,
|
||||
get cellWidth() this._cellWidth,
|
||||
get cellHeight() this._cellHeight,
|
||||
|
||||
/**
|
||||
* Start a new drag operation.
|
||||
* @param aSite The site that's being dragged.
|
||||
* @param aEvent The 'dragstart' event.
|
||||
*/
|
||||
start: function Drag_start(aSite, aEvent) {
|
||||
this._draggedSite = aSite;
|
||||
|
||||
// Prevent moz-transform for left, top.
|
||||
aSite.node.setAttribute("dragged", "true");
|
||||
|
||||
// Make sure the dragged site is floating above the grid.
|
||||
aSite.node.setAttribute("ontop", "true");
|
||||
|
||||
this._setDragData(aSite, aEvent);
|
||||
|
||||
// Store the cursor offset.
|
||||
let node = aSite.node;
|
||||
let rect = node.getBoundingClientRect();
|
||||
this._offsetX = aEvent.clientX - rect.left;
|
||||
this._offsetY = aEvent.clientY - rect.top;
|
||||
|
||||
// Store the cell dimensions.
|
||||
let cellNode = aSite.cell.node;
|
||||
this._cellWidth = cellNode.offsetWidth;
|
||||
this._cellHeight = cellNode.offsetHeight;
|
||||
|
||||
gTransformation.freezeSitePosition(aSite);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'drag' event.
|
||||
* @param aSite The site that's being dragged.
|
||||
* @param aEvent The 'drag' event.
|
||||
*/
|
||||
drag: function Drag_drag(aSite, aEvent) {
|
||||
// Get the viewport size.
|
||||
let {clientWidth, clientHeight} = document.documentElement;
|
||||
|
||||
// We'll want a padding of 5px.
|
||||
let border = 5;
|
||||
|
||||
// Enforce minimum constraints to keep the drag image inside the window.
|
||||
let left = Math.max(scrollX + aEvent.clientX - this._offsetX, border);
|
||||
let top = Math.max(scrollY + aEvent.clientY - this._offsetY, border);
|
||||
|
||||
// Enforce maximum constraints to keep the drag image inside the window.
|
||||
left = Math.min(left, scrollX + clientWidth - this.cellWidth - border);
|
||||
top = Math.min(top, scrollY + clientHeight - this.cellHeight - border);
|
||||
|
||||
// Update the drag image's position.
|
||||
gTransformation.setSitePosition(aSite, {left: left, top: top});
|
||||
},
|
||||
|
||||
/**
|
||||
* Ends the current drag operation.
|
||||
* @param aSite The site that's being dragged.
|
||||
* @param aEvent The 'dragend' event.
|
||||
*/
|
||||
end: function Drag_end(aSite, aEvent) {
|
||||
aSite.node.removeAttribute("dragged");
|
||||
|
||||
// Slide the dragged site back into its cell (may be the old or the new cell).
|
||||
gTransformation.slideSiteTo(aSite, aSite.cell, {
|
||||
unfreeze: true,
|
||||
callback: function () aSite.node.removeAttribute("ontop")
|
||||
});
|
||||
|
||||
this._draggedSite = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether we're responsible for a given drag event.
|
||||
* @param aEvent The drag event to check.
|
||||
* @return Whether we should handle this drag and drop operation.
|
||||
*/
|
||||
isValid: function Drag_isValid(aEvent) {
|
||||
let dt = aEvent.dataTransfer;
|
||||
return dt && dt.types.contains("text/x-moz-url");
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the drag data for the current drag operation.
|
||||
* @param aSite The site that's being dragged.
|
||||
* @param aEvent The 'dragstart' event.
|
||||
*/
|
||||
_setDragData: function Drag_setDragData(aSite, aEvent) {
|
||||
let {url, title} = aSite;
|
||||
|
||||
let dt = aEvent.dataTransfer;
|
||||
dt.mozCursor = "default";
|
||||
dt.effectAllowed = "move";
|
||||
dt.setData("text/plain", url);
|
||||
dt.setData("text/uri-list", url);
|
||||
dt.setData("text/x-moz-url", url + "\n" + title);
|
||||
dt.setData("text/html", "<a href=\"" + url + "\">" + url + "</a>");
|
||||
|
||||
// Create and use an empty drag element. We don't want to use the default
|
||||
// drag image with its default opacity.
|
||||
let dragElement = document.createElementNS(HTML_NAMESPACE, "div");
|
||||
dragElement.classList.add("drag-element");
|
||||
let body = document.getElementById("body");
|
||||
body.appendChild(dragElement);
|
||||
dt.setDragImage(dragElement, 0, 0);
|
||||
|
||||
// After the 'dragstart' event has been processed we can remove the
|
||||
// temporary drag element from the DOM.
|
||||
setTimeout(function () body.removeChild(dragElement), 0);
|
||||
}
|
||||
};
|
147
browser/base/content/newtab/drop.js
Normal file
147
browser/base/content/newtab/drop.js
Normal file
@ -0,0 +1,147 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
// A little delay that prevents the grid from being too sensitive when dragging
|
||||
// sites around.
|
||||
const DELAY_REARRANGE_MS = 100;
|
||||
|
||||
/**
|
||||
* This singleton implements site dropping functionality.
|
||||
*/
|
||||
let gDrop = {
|
||||
/**
|
||||
* The last drop target.
|
||||
*/
|
||||
_lastDropTarget: null,
|
||||
|
||||
/**
|
||||
* Handles the 'dragenter' event.
|
||||
* @param aCell The drop target cell.
|
||||
*/
|
||||
enter: function Drop_enter(aCell) {
|
||||
this._delayedRearrange(aCell);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragexit' event.
|
||||
* @param aCell The drop target cell.
|
||||
* @param aEvent The 'dragexit' event.
|
||||
*/
|
||||
exit: function Drop_exit(aCell, aEvent) {
|
||||
if (aEvent.dataTransfer && !aEvent.dataTransfer.mozUserCancelled) {
|
||||
this._delayedRearrange();
|
||||
} else {
|
||||
// The drag operation has been cancelled.
|
||||
this._cancelDelayedArrange();
|
||||
this._rearrange();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'drop' event.
|
||||
* @param aCell The drop target cell.
|
||||
* @param aEvent The 'dragexit' event.
|
||||
* @param aCallback The callback to call when the drop is finished.
|
||||
*/
|
||||
drop: function Drop_drop(aCell, aEvent, aCallback) {
|
||||
// The cell that is the drop target could contain a pinned site. We need
|
||||
// to find out where that site has gone and re-pin it there.
|
||||
if (aCell.containsPinnedSite())
|
||||
this._repinSitesAfterDrop(aCell);
|
||||
|
||||
// Pin the dragged or insert the new site.
|
||||
this._pinDraggedSite(aCell, aEvent);
|
||||
|
||||
this._cancelDelayedArrange();
|
||||
|
||||
// Update the grid and move all sites to their new places.
|
||||
gUpdater.updateGrid(aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Re-pins all pinned sites in their (new) positions.
|
||||
* @param aCell The drop target cell.
|
||||
*/
|
||||
_repinSitesAfterDrop: function Drop_repinSitesAfterDrop(aCell) {
|
||||
let sites = gDropPreview.rearrange(aCell);
|
||||
|
||||
// Filter out pinned sites.
|
||||
let pinnedSites = sites.filter(function (aSite) {
|
||||
return aSite && aSite.isPinned();
|
||||
});
|
||||
|
||||
// Re-pin all shifted pinned cells.
|
||||
pinnedSites.forEach(function (aSite) aSite.pin(sites.indexOf(aSite)), this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Pins the dragged site in its new place.
|
||||
* @param aCell The drop target cell.
|
||||
* @param aEvent The 'dragexit' event.
|
||||
*/
|
||||
_pinDraggedSite: function Drop_pinDraggedSite(aCell, aEvent) {
|
||||
let index = aCell.index;
|
||||
let draggedSite = gDrag.draggedSite;
|
||||
|
||||
if (draggedSite) {
|
||||
// Pin the dragged site at its new place.
|
||||
if (aCell != draggedSite.cell)
|
||||
draggedSite.pin(index);
|
||||
} else {
|
||||
// A new link was dragged onto the grid. Create it by pinning its URL.
|
||||
let dt = aEvent.dataTransfer;
|
||||
let [url, title] = dt.getData("text/x-moz-url").split(/[\r\n]+/);
|
||||
gPinnedLinks.pin({url: url, title: title}, index);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Time a rearrange with a little delay.
|
||||
* @param aCell The drop target cell.
|
||||
*/
|
||||
_delayedRearrange: function Drop_delayedRearrange(aCell) {
|
||||
// The last drop target didn't change so there's no need to re-arrange.
|
||||
if (this._lastDropTarget == aCell)
|
||||
return;
|
||||
|
||||
let self = this;
|
||||
|
||||
function callback() {
|
||||
self._rearrangeTimeout = null;
|
||||
self._rearrange(aCell);
|
||||
}
|
||||
|
||||
this._cancelDelayedArrange();
|
||||
this._rearrangeTimeout = setTimeout(callback, DELAY_REARRANGE_MS);
|
||||
|
||||
// Store the last drop target.
|
||||
this._lastDropTarget = aCell;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancels a timed rearrange, if any.
|
||||
*/
|
||||
_cancelDelayedArrange: function Drop_cancelDelayedArrange() {
|
||||
if (this._rearrangeTimeout) {
|
||||
clearTimeout(this._rearrangeTimeout);
|
||||
this._rearrangeTimeout = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Rearrange all sites in the grid depending on the current drop target.
|
||||
* @param aCell The drop target cell.
|
||||
*/
|
||||
_rearrange: function Drop_rearrange(aCell) {
|
||||
let sites = gGrid.sites;
|
||||
|
||||
// We need to rearrange the grid only if there's a current drop target.
|
||||
if (aCell)
|
||||
sites = gDropPreview.rearrange(aCell);
|
||||
|
||||
gTransformation.rearrangeSites(sites, {unfreeze: !aCell});
|
||||
}
|
||||
};
|
222
browser/base/content/newtab/dropPreview.js
Normal file
222
browser/base/content/newtab/dropPreview.js
Normal file
@ -0,0 +1,222 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton provides the ability to re-arrange the current grid to
|
||||
* indicate the transformation that results from dropping a cell at a certain
|
||||
* position.
|
||||
*/
|
||||
let gDropPreview = {
|
||||
/**
|
||||
* Rearranges the sites currently contained in the grid when a site would be
|
||||
* dropped onto the given cell.
|
||||
* @param aCell The drop target cell.
|
||||
* @return The re-arranged array of sites.
|
||||
*/
|
||||
rearrange: function DropPreview_rearrange(aCell) {
|
||||
let sites = gGrid.sites;
|
||||
|
||||
// Insert the dragged site into the current grid.
|
||||
this._insertDraggedSite(sites, aCell);
|
||||
|
||||
// After the new site has been inserted we need to correct the positions
|
||||
// of all pinned tabs that have been moved around.
|
||||
this._repositionPinnedSites(sites, aCell);
|
||||
|
||||
return sites;
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts the currently dragged site into the given array of sites.
|
||||
* @param aSites The array of sites to insert into.
|
||||
* @param aCell The drop target cell.
|
||||
*/
|
||||
_insertDraggedSite: function DropPreview_insertDraggedSite(aSites, aCell) {
|
||||
let dropIndex = aCell.index;
|
||||
let draggedSite = gDrag.draggedSite;
|
||||
|
||||
// We're currently dragging a site.
|
||||
if (draggedSite) {
|
||||
let dragCell = draggedSite.cell;
|
||||
let dragIndex = dragCell.index;
|
||||
|
||||
// Move the dragged site into its new position.
|
||||
if (dragIndex != dropIndex) {
|
||||
aSites.splice(dragIndex, 1);
|
||||
aSites.splice(dropIndex, 0, draggedSite);
|
||||
}
|
||||
// We're handling an external drag item.
|
||||
} else {
|
||||
aSites.splice(dropIndex, 0, null);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Correct the position of all pinned sites that might have been moved to
|
||||
* different positions after the dragged site has been inserted.
|
||||
* @param aSites The array of sites containing the dragged site.
|
||||
* @param aCell The drop target cell.
|
||||
*/
|
||||
_repositionPinnedSites:
|
||||
function DropPreview_repositionPinnedSites(aSites, aCell) {
|
||||
|
||||
// Collect all pinned sites.
|
||||
let pinnedSites = this._filterPinnedSites(aSites, aCell);
|
||||
|
||||
// Correct pinned site positions.
|
||||
pinnedSites.forEach(function (aSite) {
|
||||
aSites[aSites.indexOf(aSite)] = aSites[aSite.cell.index];
|
||||
aSites[aSite.cell.index] = aSite;
|
||||
}, this);
|
||||
|
||||
// There might be a pinned cell that got pushed out of the grid, try to
|
||||
// sneak it in by removing a lower-priority cell.
|
||||
if (this._hasOverflowedPinnedSite(aSites, aCell))
|
||||
this._repositionOverflowedPinnedSite(aSites, aCell);
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter pinned sites out of the grid that are still on their old positions
|
||||
* and have not moved.
|
||||
* @param aSites The array of sites to filter.
|
||||
* @param aCell The drop target cell.
|
||||
* @return The filtered array of sites.
|
||||
*/
|
||||
_filterPinnedSites: function DropPreview_filterPinnedSites(aSites, aCell) {
|
||||
let draggedSite = gDrag.draggedSite;
|
||||
|
||||
// When dropping on a cell that contains a pinned site make sure that all
|
||||
// pinned cells surrounding the drop target are moved as well.
|
||||
let range = this._getPinnedRange(aCell);
|
||||
|
||||
return aSites.filter(function (aSite, aIndex) {
|
||||
// The site must be valid, pinned and not the dragged site.
|
||||
if (!aSite || aSite == draggedSite || !aSite.isPinned())
|
||||
return false;
|
||||
|
||||
let index = aSite.cell.index;
|
||||
|
||||
// If it's not in the 'pinned range' it's a valid pinned site.
|
||||
return (index > range.end || index < range.start);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the range of pinned sites surrounding the drop target cell.
|
||||
* @param aCell The drop target cell.
|
||||
* @return The range of pinned cells.
|
||||
*/
|
||||
_getPinnedRange: function DropPreview_getPinnedRange(aCell) {
|
||||
let dropIndex = aCell.index;
|
||||
let range = {start: dropIndex, end: dropIndex};
|
||||
|
||||
// We need a pinned range only when dropping on a pinned site.
|
||||
if (aCell.containsPinnedSite()) {
|
||||
let links = gPinnedLinks.links;
|
||||
|
||||
// Find all previous siblings of the drop target that are pinned as well.
|
||||
while (range.start && links[range.start - 1])
|
||||
range.start--;
|
||||
|
||||
let maxEnd = links.length - 1;
|
||||
|
||||
// Find all next siblings of the drop target that are pinned as well.
|
||||
while (range.end < maxEnd && links[range.end + 1])
|
||||
range.end++;
|
||||
}
|
||||
|
||||
return range;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the given array of sites contains a pinned site that has
|
||||
* been pushed out of the grid.
|
||||
* @param aSites The array of sites to check.
|
||||
* @param aCell The drop target cell.
|
||||
* @return Whether there is an overflowed pinned cell.
|
||||
*/
|
||||
_hasOverflowedPinnedSite:
|
||||
function DropPreview_hasOverflowedPinnedSite(aSites, aCell) {
|
||||
|
||||
// If the drop target isn't pinned there's no way a pinned site has been
|
||||
// pushed out of the grid so we can just exit here.
|
||||
if (!aCell.containsPinnedSite())
|
||||
return false;
|
||||
|
||||
let cells = gGrid.cells;
|
||||
|
||||
// No cells have been pushed out of the grid, nothing to do here.
|
||||
if (aSites.length <= cells.length)
|
||||
return false;
|
||||
|
||||
let overflowedSite = aSites[cells.length];
|
||||
|
||||
// Nothing to do if the site that got pushed out of the grid is not pinned.
|
||||
return (overflowedSite && overflowedSite.isPinned());
|
||||
},
|
||||
|
||||
/**
|
||||
* We have a overflowed pinned site that we need to re-position so that it's
|
||||
* visible again. We try to find a lower-priority cell (empty or containing
|
||||
* an unpinned site) that we can move it to.
|
||||
* @param aSites The array of sites.
|
||||
* @param aCell The drop target cell.
|
||||
*/
|
||||
_repositionOverflowedPinnedSite:
|
||||
function DropPreview_repositionOverflowedPinnedSite(aSites, aCell) {
|
||||
|
||||
// Try to find a lower-priority cell (empty or containing an unpinned site).
|
||||
let index = this._indexOfLowerPrioritySite(aSites, aCell);
|
||||
|
||||
if (index > -1) {
|
||||
let cells = gGrid.cells;
|
||||
let dropIndex = aCell.index;
|
||||
|
||||
// Move all pinned cells to their new positions to let the overflowed
|
||||
// site fit into the grid.
|
||||
for (let i = index + 1, lastPosition = index; i < aSites.length; i++) {
|
||||
if (i != dropIndex) {
|
||||
aSites[lastPosition] = aSites[i];
|
||||
lastPosition = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, remove the overflowed site from its previous position.
|
||||
aSites.splice(cells.length, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the index of the last cell that is empty or contains an unpinned
|
||||
* site. These are considered to be of a lower priority.
|
||||
* @param aSites The array of sites.
|
||||
* @param aCell The drop target cell.
|
||||
* @return The cell's index.
|
||||
*/
|
||||
_indexOfLowerPrioritySite:
|
||||
function DropPreview_indexOfLowerPrioritySite(aSites, aCell) {
|
||||
|
||||
let cells = gGrid.cells;
|
||||
let dropIndex = aCell.index;
|
||||
|
||||
// Search (beginning with the last site in the grid) for a site that is
|
||||
// empty or unpinned (an thus lower-priority) and can be pushed out of the
|
||||
// grid instead of the pinned site.
|
||||
for (let i = cells.length - 1; i >= 0; i--) {
|
||||
// The cell that is our drop target is not a good choice.
|
||||
if (i == dropIndex)
|
||||
continue;
|
||||
|
||||
let site = aSites[i];
|
||||
|
||||
// We can use the cell only if it's empty or the site is un-pinned.
|
||||
if (!site || !site.isPinned())
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
178
browser/base/content/newtab/dropTargetShim.js
Normal file
178
browser/base/content/newtab/dropTargetShim.js
Normal file
@ -0,0 +1,178 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton provides a custom drop target detection. We need this because
|
||||
* the default DnD target detection relies on the cursor's position. We want
|
||||
* to pick a drop target based on the dragged site's position.
|
||||
*/
|
||||
let gDropTargetShim = {
|
||||
/**
|
||||
* Cache for the position of all cells, cleaned after drag finished.
|
||||
*/
|
||||
_cellPositions: null,
|
||||
|
||||
/**
|
||||
* The last drop target that was hovered.
|
||||
*/
|
||||
_lastDropTarget: null,
|
||||
|
||||
/**
|
||||
* Initializes the drop target shim.
|
||||
*/
|
||||
init: function DropTargetShim_init() {
|
||||
let node = gGrid.node;
|
||||
|
||||
this._dragover = this._dragover.bind(this);
|
||||
|
||||
// Add drag event handlers.
|
||||
node.addEventListener("dragstart", this._start.bind(this), true);
|
||||
// XXX bug 505521 - Don't listen for drag, it's useless at the moment.
|
||||
//node.addEventListener("drag", this._drag.bind(this), false);
|
||||
node.addEventListener("dragend", this._end.bind(this), true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragstart' event.
|
||||
* @param aEvent The 'dragstart' event.
|
||||
*/
|
||||
_start: function DropTargetShim_start(aEvent) {
|
||||
gGrid.lock();
|
||||
|
||||
// XXX bug 505521 - Listen for dragover on the document.
|
||||
document.documentElement.addEventListener("dragover", this._dragover, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'drag' event and determines the current drop target.
|
||||
* @param aEvent The 'drag' event.
|
||||
*/
|
||||
_drag: function DropTargetShim_drag(aEvent) {
|
||||
// Let's see if we find a drop target.
|
||||
let target = this._findDropTarget(aEvent);
|
||||
|
||||
if (target == this._lastDropTarget) {
|
||||
// XXX bug 505521 - Don't fire dragover for now (causes recursion).
|
||||
/*if (target)
|
||||
// The last drop target is valid and didn't change.
|
||||
this._dispatchEvent(aEvent, "dragover", target);*/
|
||||
} else {
|
||||
if (this._lastDropTarget)
|
||||
// We left the last drop target.
|
||||
this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
|
||||
|
||||
if (target)
|
||||
// We're now hovering a (new) drop target.
|
||||
this._dispatchEvent(aEvent, "dragenter", target);
|
||||
|
||||
if (this._lastDropTarget)
|
||||
// We left the last drop target.
|
||||
this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget);
|
||||
|
||||
this._lastDropTarget = target;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragover' event as long as bug 505521 isn't fixed to get
|
||||
* current mouse cursor coordinates while dragging.
|
||||
* @param aEvent The 'dragover' event.
|
||||
*/
|
||||
_dragover: function DropTargetShim_dragover(aEvent) {
|
||||
let sourceNode = aEvent.dataTransfer.mozSourceNode;
|
||||
gDrag.drag(sourceNode._newtabSite, aEvent);
|
||||
|
||||
this._drag(aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragend' event.
|
||||
* @param aEvent The 'dragend' event.
|
||||
*/
|
||||
_end: function DropTargetShim_end(aEvent) {
|
||||
// Make sure to determine the current drop target in case the dragenter
|
||||
// event hasn't been fired.
|
||||
this._drag(aEvent);
|
||||
|
||||
if (this._lastDropTarget) {
|
||||
if (aEvent.dataTransfer.mozUserCancelled) {
|
||||
// The drag operation was cancelled.
|
||||
this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
|
||||
this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget);
|
||||
} else {
|
||||
// A site was successfully dropped.
|
||||
this._dispatchEvent(aEvent, "drop", this._lastDropTarget);
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
this._lastDropTarget = null;
|
||||
this._cellPositions = null;
|
||||
}
|
||||
|
||||
gGrid.unlock();
|
||||
|
||||
// XXX bug 505521 - Remove the document's dragover listener.
|
||||
document.documentElement.removeEventListener("dragover", this._dragover, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the current drop target by matching the dragged site's position
|
||||
* against all cells in the grid.
|
||||
* @return The currently hovered drop target or null.
|
||||
*/
|
||||
_findDropTarget: function DropTargetShim_findDropTarget() {
|
||||
// These are the minimum intersection values - we want to use the cell if
|
||||
// the site is >= 50% hovering its position.
|
||||
let minWidth = gDrag.cellWidth / 2;
|
||||
let minHeight = gDrag.cellHeight / 2;
|
||||
|
||||
let cellPositions = this._getCellPositions();
|
||||
let rect = gTransformation.getNodePosition(gDrag.draggedSite.node);
|
||||
|
||||
// Compare each cell's position to the dragged site's position.
|
||||
for (let i = 0; i < cellPositions.length; i++) {
|
||||
let inter = rect.intersect(cellPositions[i].rect);
|
||||
|
||||
// If the intersection is big enough we found a drop target.
|
||||
if (inter.width >= minWidth && inter.height >= minHeight)
|
||||
return cellPositions[i].cell;
|
||||
}
|
||||
|
||||
// No drop target found.
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the positions of all cell nodes.
|
||||
* @return The (cached) cell positions.
|
||||
*/
|
||||
_getCellPositions: function DropTargetShim_getCellPositions() {
|
||||
if (this._cellPositions)
|
||||
return this._cellPositions;
|
||||
|
||||
return this._cellPositions = gGrid.cells.map(function (cell) {
|
||||
return {cell: cell, rect: gTransformation.getNodePosition(cell.node)};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatches a custom DragEvent on the given target node.
|
||||
* @param aEvent The source event.
|
||||
* @param aType The event type.
|
||||
* @param aTarget The target node that receives the event.
|
||||
*/
|
||||
_dispatchEvent:
|
||||
function DropTargetShim_dispatchEvent(aEvent, aType, aTarget) {
|
||||
|
||||
let node = aTarget.node;
|
||||
let event = document.createEvent("DragEvents");
|
||||
|
||||
event.initDragEvent(aType, true, true, window, 0, 0, 0, 0, 0, false, false,
|
||||
false, false, 0, node, aEvent.dataTransfer);
|
||||
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
};
|
132
browser/base/content/newtab/grid.js
Normal file
132
browser/base/content/newtab/grid.js
Normal file
@ -0,0 +1,132 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton represents the grid that contains all sites.
|
||||
*/
|
||||
let gGrid = {
|
||||
/**
|
||||
* The DOM node of the grid.
|
||||
*/
|
||||
_node: null,
|
||||
get node() this._node,
|
||||
|
||||
/**
|
||||
* The cached DOM fragment for sites.
|
||||
*/
|
||||
_siteFragment: null,
|
||||
|
||||
/**
|
||||
* All cells contained in the grid.
|
||||
*/
|
||||
get cells() {
|
||||
let children = this.node.querySelectorAll("li");
|
||||
let cells = [new Cell(this, child) for each (child in children)];
|
||||
|
||||
// Replace the getter with our cached value.
|
||||
Object.defineProperty(this, "cells", {value: cells, enumerable: true});
|
||||
|
||||
return cells;
|
||||
},
|
||||
|
||||
/**
|
||||
* All sites contained in the grid's cells. Sites may be empty.
|
||||
*/
|
||||
get sites() [cell.site for each (cell in this.cells)],
|
||||
|
||||
/**
|
||||
* Initializes the grid.
|
||||
* @param aSelector The query selector of the grid.
|
||||
*/
|
||||
init: function Grid_init(aSelector) {
|
||||
this._node = document.querySelector(aSelector);
|
||||
this._createSiteFragment();
|
||||
this._draw();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new site in the grid.
|
||||
* @param aLink The new site's link.
|
||||
* @param aCell The cell that will contain the new site.
|
||||
* @return The newly created site.
|
||||
*/
|
||||
createSite: function Grid_createSite(aLink, aCell) {
|
||||
let node = aCell.node;
|
||||
node.appendChild(this._siteFragment.cloneNode(true));
|
||||
return new Site(node.firstElementChild, aLink);
|
||||
},
|
||||
|
||||
/**
|
||||
* Refreshes the grid and re-creates all sites.
|
||||
*/
|
||||
refresh: function Grid_refresh() {
|
||||
// Remove all sites.
|
||||
this.cells.forEach(function (cell) {
|
||||
let node = cell.node;
|
||||
let child = node.firstElementChild;
|
||||
|
||||
if (child)
|
||||
node.removeChild(child);
|
||||
}, this);
|
||||
|
||||
// Draw the grid again.
|
||||
this._draw();
|
||||
},
|
||||
|
||||
/**
|
||||
* Locks the grid to block all pointer events.
|
||||
*/
|
||||
lock: function Grid_lock() {
|
||||
this.node.setAttribute("locked", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
* Unlocks the grid to allow all pointer events.
|
||||
*/
|
||||
unlock: function Grid_unlock() {
|
||||
this.node.removeAttribute("locked");
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the DOM fragment that is re-used when creating sites.
|
||||
*/
|
||||
_createSiteFragment: function Grid_createSiteFragment() {
|
||||
let site = document.createElementNS(HTML_NAMESPACE, "a");
|
||||
site.classList.add("site");
|
||||
site.setAttribute("draggable", "true");
|
||||
|
||||
// Create the site's inner HTML code.
|
||||
site.innerHTML =
|
||||
'<img class="site-img" width="' + THUMB_WIDTH +'" ' +
|
||||
' height="' + THUMB_HEIGHT + '" alt=""/>' +
|
||||
'<span class="site-title"/>' +
|
||||
'<span class="site-strip">' +
|
||||
' <input class="button strip-button strip-button-pin" type="button"' +
|
||||
' tabindex="-1" title="' + newTabString("pin") + '"/>' +
|
||||
' <input class="button strip-button strip-button-block" type="button"' +
|
||||
' tabindex="-1" title="' + newTabString("block") + '"/>' +
|
||||
'</span>';
|
||||
|
||||
this._siteFragment = document.createDocumentFragment();
|
||||
this._siteFragment.appendChild(site);
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws the grid, creates all sites and puts them into their cells.
|
||||
*/
|
||||
_draw: function Grid_draw() {
|
||||
let cells = this.cells;
|
||||
|
||||
// Put sites into the cells.
|
||||
let links = gLinks.getLinks();
|
||||
let length = Math.min(links.length, cells.length);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (links[i])
|
||||
this.createSite(links[i], cells[i]);
|
||||
}
|
||||
}
|
||||
};
|
173
browser/base/content/newtab/newTab.css
Normal file
173
browser/base/content/newtab/newTab.css
Normal file
@ -0,0 +1,173 @@
|
||||
:root {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
#scrollbox:not([page-disabled]) {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#body {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
min-width: 675px;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* TOOLBAR */
|
||||
#toolbar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#toolbar[page-disabled] {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#toolbar:-moz-locale-dir(rtl) {
|
||||
left: 8px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.toolbar-button {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
-moz-transition: opacity 200ms ease-out;
|
||||
}
|
||||
|
||||
#toolbar-button-show,
|
||||
#toolbar-button-reset {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#toolbar-button-reset[modified],
|
||||
#toolbar-button-show[page-disabled] {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#toolbar-button-hide[page-disabled],
|
||||
#toolbar-button-reset[page-disabled] {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* GRID */
|
||||
#grid {
|
||||
width: 637px;
|
||||
height: 411px;
|
||||
overflow: hidden;
|
||||
list-style-type: none;
|
||||
-moz-transition: opacity 200ms ease-out;
|
||||
}
|
||||
|
||||
#grid[page-disabled] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#grid[page-disabled],
|
||||
#grid[locked] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* CELLS */
|
||||
.cell {
|
||||
float: left;
|
||||
width: 201px;
|
||||
height: 127px;
|
||||
margin-bottom: 15px;
|
||||
-moz-margin-end: 16px;
|
||||
}
|
||||
|
||||
.cell:-moz-locale-dir(rtl) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.cell:nth-child(3n+3) {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
/* SITES */
|
||||
.site {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 201px;
|
||||
height: 127px;
|
||||
}
|
||||
|
||||
.site[frozen] {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.site[ontop] {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* SITE IMAGE */
|
||||
.site-img {
|
||||
display: block;
|
||||
opacity: 0.75;
|
||||
-moz-transition: opacity 200ms ease-out;
|
||||
}
|
||||
|
||||
.site:hover > .site-img,
|
||||
.site[ontop] > .site-img,
|
||||
.site:-moz-focusring > .site-img {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.site-img[loading] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* SITE TITLE */
|
||||
.site-title {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* SITE STRIP */
|
||||
.site-strip {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 195px;
|
||||
height: 17px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
-moz-transition: opacity 200ms ease-out;
|
||||
}
|
||||
|
||||
.site:hover:not([frozen]) > .site-strip {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.strip-button-pin,
|
||||
.strip-button-block:-moz-locale-dir(rtl) {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.strip-button-block,
|
||||
.strip-button-pin:-moz-locale-dir(rtl) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* DRAG & DROP */
|
||||
|
||||
/*
|
||||
* This is just a temporary drag element used for dataTransfer.setDragImage()
|
||||
* so that we can use custom drag images and elements. It needs an opacity of
|
||||
* 0.01 so that the core code detects that it's in fact a visible element.
|
||||
*/
|
||||
.drag-element {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
background-color: #fff;
|
||||
opacity: 0.01;
|
||||
}
|
47
browser/base/content/newtab/newTab.js
Normal file
47
browser/base/content/newtab/newTab.js
Normal file
@ -0,0 +1,47 @@
|
||||
/* 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";
|
||||
|
||||
let Cu = Components.utils;
|
||||
let Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/PageThumbs.jsm");
|
||||
Cu.import("resource:///modules/NewTabUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Rect",
|
||||
"resource://gre/modules/Geometry.jsm");
|
||||
|
||||
let {
|
||||
links: gLinks,
|
||||
allPages: gAllPages,
|
||||
pinnedLinks: gPinnedLinks,
|
||||
blockedLinks: gBlockedLinks
|
||||
} = NewTabUtils;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
|
||||
return Services.strings.
|
||||
createBundle("chrome://browser/locale/newTab.properties");
|
||||
});
|
||||
|
||||
function newTabString(name) gStringBundle.GetStringFromName('newtab.' + name);
|
||||
|
||||
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
||||
const THUMB_WIDTH = 201;
|
||||
const THUMB_HEIGHT = 127;
|
||||
|
||||
#include batch.js
|
||||
#include transformations.js
|
||||
#include page.js
|
||||
#include toolbar.js
|
||||
#include grid.js
|
||||
#include cells.js
|
||||
#include sites.js
|
||||
#include drag.js
|
||||
#include drop.js
|
||||
#include dropTargetShim.js
|
||||
#include dropPreview.js
|
||||
#include updater.js
|
42
browser/base/content/newtab/newTab.xul
Normal file
42
browser/base/content/newtab/newTab.xul
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
# 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/.
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
|
||||
%newTabDTD;
|
||||
]>
|
||||
|
||||
<xul:window xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
disablefastfind="true" title="&newtab.pageTitle;">
|
||||
<xul:vbox id="scrollbox" flex="1" title=" ">
|
||||
<body id="body">
|
||||
<div id="toolbar">
|
||||
<input class="button toolbar-button" id="toolbar-button-show"
|
||||
type="button" title="&newtab.show;"/>
|
||||
<input class="button toolbar-button" id="toolbar-button-hide"
|
||||
type="button" title="&newtab.hide;"/>
|
||||
<input class="button toolbar-button" id="toolbar-button-reset"
|
||||
type="button" title="&newtab.reset;"/>
|
||||
</div>
|
||||
|
||||
<ul id="grid">
|
||||
<li class="cell"/><li class="cell"/><li class="cell"/>
|
||||
<li class="cell"/><li class="cell"/><li class="cell"/>
|
||||
<li class="cell"/><li class="cell"/><li class="cell"/>
|
||||
</ul>
|
||||
|
||||
<xul:script type="text/javascript;version=1.8" src="chrome://browser/content/newtab/newTab.js"/>
|
||||
<xul:script type="text/javascript;version=1.8">
|
||||
gPage.init("#toolbar", "#grid");
|
||||
</xul:script>
|
||||
</body>
|
||||
</xul:vbox>
|
||||
</xul:window>
|
173
browser/base/content/newtab/page.js
Normal file
173
browser/base/content/newtab/page.js
Normal file
@ -0,0 +1,173 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton represents the whole 'New Tab Page' and takes care of
|
||||
* initializing all its components.
|
||||
*/
|
||||
let gPage = {
|
||||
/**
|
||||
* Initializes the page.
|
||||
* @param aToolbarSelector The query selector for the page toolbar.
|
||||
* @param aGridSelector The query selector for the grid.
|
||||
*/
|
||||
init: function Page_init(aToolbarSelector, aGridSelector) {
|
||||
gToolbar.init(aToolbarSelector);
|
||||
this._gridSelector = aGridSelector;
|
||||
|
||||
// Add ourselves to the list of pages to receive notifications.
|
||||
gAllPages.register(this);
|
||||
|
||||
// Listen for 'unload' to unregister this page.
|
||||
function unload() gAllPages.unregister(self);
|
||||
addEventListener("unload", unload, false);
|
||||
|
||||
// Check if the new tab feature is enabled.
|
||||
if (gAllPages.enabled)
|
||||
this._init();
|
||||
else
|
||||
this._updateAttributes(false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Listens for notifications specific to this page.
|
||||
*/
|
||||
observe: function Page_observe() {
|
||||
let enabled = gAllPages.enabled;
|
||||
this._updateAttributes(enabled);
|
||||
|
||||
// Initialize the whole page if we haven't done that, yet.
|
||||
if (enabled)
|
||||
this._init();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the whole page and the grid when the storage has changed.
|
||||
*/
|
||||
update: function Page_update() {
|
||||
this.updateModifiedFlag();
|
||||
gGrid.refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the page is modified and sets the CSS class accordingly
|
||||
*/
|
||||
updateModifiedFlag: function Page_updateModifiedFlag() {
|
||||
let node = document.getElementById("toolbar-button-reset");
|
||||
let modified = this._isModified();
|
||||
|
||||
if (modified)
|
||||
node.setAttribute("modified", "true");
|
||||
else
|
||||
node.removeAttribute("modified");
|
||||
|
||||
this._updateTabIndices(gAllPages.enabled, modified);
|
||||
},
|
||||
|
||||
/**
|
||||
* Internally initializes the page. This runs only when/if the feature
|
||||
* is/gets enabled.
|
||||
*/
|
||||
_init: function Page_init() {
|
||||
if (this._initialized)
|
||||
return;
|
||||
|
||||
this._initialized = true;
|
||||
|
||||
gLinks.populateCache(function () {
|
||||
// Check if the grid is modified.
|
||||
this.updateModifiedFlag();
|
||||
|
||||
// Initialize and render the grid.
|
||||
gGrid.init(this._gridSelector);
|
||||
|
||||
// Initialize the drop target shim.
|
||||
gDropTargetShim.init();
|
||||
|
||||
// Workaround to prevent a delay on MacOSX due to a slow drop animation.
|
||||
let doc = document.documentElement;
|
||||
doc.addEventListener("dragover", this.onDragOver, false);
|
||||
doc.addEventListener("drop", this.onDrop, false);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the 'page-disabled' attributes of the respective DOM nodes.
|
||||
* @param aValue Whether to set or remove attributes.
|
||||
*/
|
||||
_updateAttributes: function Page_updateAttributes(aValue) {
|
||||
let nodes = document.querySelectorAll("#grid, #scrollbox, #toolbar, .toolbar-button");
|
||||
|
||||
// Set the nodes' states.
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let node = nodes[i];
|
||||
if (aValue)
|
||||
node.removeAttribute("page-disabled");
|
||||
else
|
||||
node.setAttribute("page-disabled", "true");
|
||||
}
|
||||
|
||||
this._updateTabIndices(aValue, this._isModified());
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the page is modified.
|
||||
* @return Whether the page is modified or not.
|
||||
*/
|
||||
_isModified: function Page_isModified() {
|
||||
// The page is considered modified only if sites have been removed.
|
||||
return !gBlockedLinks.isEmpty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the tab indices of focusable elements.
|
||||
* @param aEnabled Whether the page is currently enabled.
|
||||
* @param aModified Whether the page is currently modified.
|
||||
*/
|
||||
_updateTabIndices: function Page_updateTabIndices(aEnabled, aModified) {
|
||||
function setFocusable(aNode, aFocusable) {
|
||||
if (aFocusable)
|
||||
aNode.removeAttribute("tabindex");
|
||||
else
|
||||
aNode.setAttribute("tabindex", "-1");
|
||||
}
|
||||
|
||||
// Sites and the 'hide' button are always focusable when the grid is shown.
|
||||
let nodes = document.querySelectorAll(".site, #toolbar-button-hide");
|
||||
for (let i = 0; i < nodes.length; i++)
|
||||
setFocusable(nodes[i], aEnabled);
|
||||
|
||||
// The 'show' button is focusable when the grid is hidden.
|
||||
let btnShow = document.getElementById("toolbar-button-show");
|
||||
setFocusable(btnShow, !aEnabled);
|
||||
|
||||
// The 'reset' button is focusable when the grid is shown and modified.
|
||||
let btnReset = document.getElementById("toolbar-button-reset");
|
||||
setFocusable(btnReset, aEnabled && aModified);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragover' event. Workaround to prevent a delay on MacOSX
|
||||
* due to a slow drop animation.
|
||||
* @param aEvent The 'dragover' event.
|
||||
*/
|
||||
onDragOver: function Page_onDragOver(aEvent) {
|
||||
if (gDrag.isValid(aEvent))
|
||||
aEvent.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'drop' event. Workaround to prevent a delay on MacOSX due to
|
||||
* a slow drop animation.
|
||||
* @param aEvent The 'drop' event.
|
||||
*/
|
||||
onDrop: function Page_onDrop(aEvent) {
|
||||
if (gDrag.isValid(aEvent)) {
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
209
browser/base/content/newtab/sites.js
Normal file
209
browser/base/content/newtab/sites.js
Normal file
@ -0,0 +1,209 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This class represents a site that is contained in a cell and can be pinned,
|
||||
* moved around or deleted.
|
||||
*/
|
||||
function Site(aNode, aLink) {
|
||||
this._node = aNode;
|
||||
this._node._newtabSite = this;
|
||||
|
||||
this._link = aLink;
|
||||
|
||||
this._render();
|
||||
this._addEventHandlers();
|
||||
}
|
||||
|
||||
Site.prototype = {
|
||||
/**
|
||||
* The site's DOM node.
|
||||
*/
|
||||
get node() this._node,
|
||||
|
||||
/**
|
||||
* The site's link.
|
||||
*/
|
||||
get link() this._link,
|
||||
|
||||
/**
|
||||
* The url of the site's link.
|
||||
*/
|
||||
get url() this.link.url,
|
||||
|
||||
/**
|
||||
* The title of the site's link.
|
||||
*/
|
||||
get title() this.link.title,
|
||||
|
||||
/**
|
||||
* The site's parent cell.
|
||||
*/
|
||||
get cell() {
|
||||
let parentNode = this.node.parentNode;
|
||||
return parentNode && parentNode._newtabCell;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pins the site on its current or a given index.
|
||||
* @param aIndex The pinned index (optional).
|
||||
*/
|
||||
pin: function Site_pin(aIndex) {
|
||||
if (typeof aIndex == "undefined")
|
||||
aIndex = this.cell.index;
|
||||
|
||||
this._updateAttributes(true);
|
||||
gPinnedLinks.pin(this._link, aIndex);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unpins the site and calls the given callback when done.
|
||||
* @param aCallback The callback to be called when finished.
|
||||
*/
|
||||
unpin: function Site_unpin(aCallback) {
|
||||
if (this.isPinned()) {
|
||||
this._updateAttributes(false);
|
||||
gPinnedLinks.unpin(this._link);
|
||||
gUpdater.updateGrid(aCallback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether this site is pinned.
|
||||
* @return Whether this site is pinned.
|
||||
*/
|
||||
isPinned: function Site_isPinned() {
|
||||
return gPinnedLinks.isPinned(this._link);
|
||||
},
|
||||
|
||||
/**
|
||||
* Blocks the site (removes it from the grid) and calls the given callback
|
||||
* when done.
|
||||
* @param aCallback The callback to be called when finished.
|
||||
*/
|
||||
block: function Site_block(aCallback) {
|
||||
gBlockedLinks.block(this._link);
|
||||
gUpdater.updateGrid(aCallback);
|
||||
gPage.updateModifiedFlag();
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the DOM node specified by the given query selector.
|
||||
* @param aSelector The query selector.
|
||||
* @return The DOM node we found.
|
||||
*/
|
||||
_querySelector: function Site_querySelector(aSelector) {
|
||||
return this.node.querySelector(aSelector);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates attributes for all nodes which status depends on this site being
|
||||
* pinned or unpinned.
|
||||
* @param aPinned Whether this site is now pinned or unpinned.
|
||||
*/
|
||||
_updateAttributes: function (aPinned) {
|
||||
let buttonPin = this._querySelector(".strip-button-pin");
|
||||
|
||||
if (aPinned) {
|
||||
this.node.setAttribute("pinned", true);
|
||||
buttonPin.setAttribute("title", newTabString("unpin"));
|
||||
} else {
|
||||
this.node.removeAttribute("pinned");
|
||||
buttonPin.setAttribute("title", newTabString("pin"));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the site's data (fills the HTML fragment).
|
||||
*/
|
||||
_render: function Site_render() {
|
||||
let title = this.title || this.url;
|
||||
this.node.setAttribute("title", title);
|
||||
this.node.setAttribute("href", this.url);
|
||||
this._querySelector(".site-title").textContent = title;
|
||||
|
||||
if (this.isPinned())
|
||||
this._updateAttributes(true);
|
||||
|
||||
this._renderThumbnail();
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the site's thumbnail.
|
||||
*/
|
||||
_renderThumbnail: function Site_renderThumbnail() {
|
||||
let img = this._querySelector(".site-img")
|
||||
img.setAttribute("alt", this.title || this.url);
|
||||
img.setAttribute("loading", "true");
|
||||
|
||||
// Wait until the image has loaded.
|
||||
img.addEventListener("load", function onLoad() {
|
||||
img.removeEventListener("load", onLoad, false);
|
||||
img.removeAttribute("loading");
|
||||
}, false);
|
||||
|
||||
// Set the thumbnail url.
|
||||
img.setAttribute("src", PageThumbs.getThumbnailURL(this.url));
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds event handlers for the site and its buttons.
|
||||
*/
|
||||
_addEventHandlers: function Site_addEventHandlers() {
|
||||
// Register drag-and-drop event handlers.
|
||||
["DragStart", /*"Drag",*/ "DragEnd"].forEach(function (aType) {
|
||||
let method = "_on" + aType;
|
||||
this[method] = this[method].bind(this);
|
||||
this._node.addEventListener(aType.toLowerCase(), this[method], false);
|
||||
}, this);
|
||||
|
||||
let self = this;
|
||||
|
||||
function pin(aEvent) {
|
||||
if (aEvent)
|
||||
aEvent.preventDefault();
|
||||
|
||||
if (self.isPinned())
|
||||
self.unpin();
|
||||
else
|
||||
self.pin();
|
||||
}
|
||||
|
||||
function block(aEvent) {
|
||||
if (aEvent)
|
||||
aEvent.preventDefault();
|
||||
|
||||
self.block();
|
||||
}
|
||||
|
||||
this._querySelector(".strip-button-pin").addEventListener("click", pin, false);
|
||||
this._querySelector(".strip-button-block").addEventListener("click", block, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragstart' event.
|
||||
* @param aEvent The drag event.
|
||||
*/
|
||||
_onDragStart: function Site_onDragStart(aEvent) {
|
||||
gDrag.start(this, aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'drag' event.
|
||||
* @param aEvent The drag event.
|
||||
*/
|
||||
_onDrag: function Site_onDrag(aEvent) {
|
||||
gDrag.drag(this, aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for the 'dragend' event.
|
||||
* @param aEvent The drag event.
|
||||
*/
|
||||
_onDragEnd: function Site_onDragEnd(aEvent) {
|
||||
gDrag.end(this, aEvent);
|
||||
}
|
||||
};
|
87
browser/base/content/newtab/toolbar.js
Normal file
87
browser/base/content/newtab/toolbar.js
Normal file
@ -0,0 +1,87 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton represents the page's toolbar that allows to enable/disable
|
||||
* the 'New Tab Page' feature and to reset the whole page.
|
||||
*/
|
||||
let gToolbar = {
|
||||
/**
|
||||
* Initializes the toolbar.
|
||||
* @param aSelector The query selector of the toolbar.
|
||||
*/
|
||||
init: function Toolbar_init(aSelector) {
|
||||
this._node = document.querySelector(aSelector);
|
||||
let buttons = this._node.querySelectorAll("input");
|
||||
|
||||
// Listen for 'click' events on the toolbar buttons.
|
||||
["show", "hide", "reset"].forEach(function (aType, aIndex) {
|
||||
let self = this;
|
||||
let button = buttons[aIndex];
|
||||
let handler = function () self[aType]();
|
||||
|
||||
button.addEventListener("click", handler, false);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Per default buttons lose focus after being clicked on Mac OS X.
|
||||
// So when the URL bar has focus and a toolbar button is clicked the
|
||||
// URL bar regains focus and the history pops up. We need to prevent
|
||||
// that by explicitly removing its focus.
|
||||
button.addEventListener("mousedown", function () {
|
||||
window.focus();
|
||||
}, false);
|
||||
#endif
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables the 'New Tab Page' feature.
|
||||
*/
|
||||
show: function Toolbar_show() {
|
||||
this._passButtonFocus("show", "hide");
|
||||
gAllPages.enabled = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disables the 'New Tab Page' feature.
|
||||
*/
|
||||
hide: function Toolbar_hide() {
|
||||
this._passButtonFocus("hide", "show");
|
||||
gAllPages.enabled = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the whole page and forces it to re-render its content.
|
||||
* @param aCallback The callback to call when the page has been reset.
|
||||
*/
|
||||
reset: function Toolbar_reset(aCallback) {
|
||||
this._passButtonFocus("reset", "hide");
|
||||
let node = gGrid.node;
|
||||
|
||||
// animate the page reset
|
||||
gTransformation.fadeNodeOut(node, function () {
|
||||
NewTabUtils.reset();
|
||||
|
||||
gLinks.populateCache(function () {
|
||||
gAllPages.update();
|
||||
|
||||
// Without the setTimeout() we have a strange flicker.
|
||||
setTimeout(function () gTransformation.fadeNodeIn(node, aCallback));
|
||||
}, true);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Passes the focus from the current button to the next.
|
||||
* @param aCurrent The button that currently has focus.
|
||||
* @param aNext The button that is focused next.
|
||||
*/
|
||||
_passButtonFocus: function Toolbar_passButtonFocus(aCurrent, aNext) {
|
||||
if (document.querySelector("#toolbar-button-" + aCurrent + ":-moz-focusring"))
|
||||
document.getElementById("toolbar-button-" + aNext).focus();
|
||||
}
|
||||
};
|
||||
|
226
browser/base/content/newtab/transformations.js
Normal file
226
browser/base/content/newtab/transformations.js
Normal file
@ -0,0 +1,226 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton allows to transform the grid by repositioning a site's node
|
||||
* in the DOM and by showing or hiding the node. It additionally provides
|
||||
* convenience methods to work with a site's DOM node.
|
||||
*/
|
||||
let gTransformation = {
|
||||
/**
|
||||
* Gets a DOM node's position.
|
||||
* @param aNode The DOM node.
|
||||
* @return A Rect instance with the position.
|
||||
*/
|
||||
getNodePosition: function Transformation_getNodePosition(aNode) {
|
||||
let {left, top, width, height} = aNode.getBoundingClientRect();
|
||||
return new Rect(left + scrollX, top + scrollY, width, height);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fades a given node from zero to full opacity.
|
||||
* @param aNode The node to fade.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) {
|
||||
this._setNodeOpacity(aNode, 1, function () {
|
||||
// Clear the style property.
|
||||
aNode.style.opacity = "";
|
||||
|
||||
if (aCallback)
|
||||
aCallback();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fades a given node from full to zero opacity.
|
||||
* @param aNode The node to fade.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) {
|
||||
this._setNodeOpacity(aNode, 0, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fades a given site from zero to full opacity.
|
||||
* @param aSite The site to fade.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
showSite: function Transformation_showSite(aSite, aCallback) {
|
||||
this.fadeNodeIn(aSite.node, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fades a given site from full to zero opacity.
|
||||
* @param aSite The site to fade.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
hideSite: function Transformation_hideSite(aSite, aCallback) {
|
||||
this.fadeNodeOut(aSite.node, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows to set a site's position.
|
||||
* @param aSite The site to re-position.
|
||||
* @param aPosition The desired position for the given site.
|
||||
*/
|
||||
setSitePosition: function Transformation_setSitePosition(aSite, aPosition) {
|
||||
let style = aSite.node.style;
|
||||
let {top, left} = aPosition;
|
||||
|
||||
style.top = top + "px";
|
||||
style.left = left + "px";
|
||||
},
|
||||
|
||||
/**
|
||||
* Freezes a site in its current position by positioning it absolute.
|
||||
* @param aSite The site to freeze.
|
||||
*/
|
||||
freezeSitePosition: function Transformation_freezeSitePosition(aSite) {
|
||||
aSite.node.setAttribute("frozen", "true");
|
||||
this.setSitePosition(aSite, this.getNodePosition(aSite.node));
|
||||
},
|
||||
|
||||
/**
|
||||
* Unfreezes a site by removing its absolute positioning.
|
||||
* @param aSite The site to unfreeze.
|
||||
*/
|
||||
unfreezeSitePosition: function Transformation_unfreezeSitePosition(aSite) {
|
||||
let style = aSite.node.style;
|
||||
style.left = style.top = "";
|
||||
aSite.node.removeAttribute("frozen");
|
||||
},
|
||||
|
||||
/**
|
||||
* Slides the given site to the target node's position.
|
||||
* @param aSite The site to move.
|
||||
* @param aTarget The slide target.
|
||||
* @param aOptions Set of options (see below).
|
||||
* unfreeze - unfreeze the site after sliding
|
||||
* callback - the callback to call when finished
|
||||
*/
|
||||
slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) {
|
||||
let currentPosition = this.getNodePosition(aSite.node);
|
||||
let targetPosition = this.getNodePosition(aTarget.node)
|
||||
let callback = aOptions && aOptions.callback;
|
||||
|
||||
let self = this;
|
||||
|
||||
function finish() {
|
||||
if (aOptions && aOptions.unfreeze)
|
||||
self.unfreezeSitePosition(aSite);
|
||||
|
||||
if (callback)
|
||||
callback();
|
||||
}
|
||||
|
||||
// Nothing to do here if the positions already match.
|
||||
if (currentPosition.equals(targetPosition)) {
|
||||
finish();
|
||||
} else {
|
||||
this.setSitePosition(aSite, targetPosition);
|
||||
this._whenTransitionEnded(aSite.node, finish);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Rearranges a given array of sites and moves them to their new positions or
|
||||
* fades in/out new/removed sites.
|
||||
* @param aSites An array of sites to rearrange.
|
||||
* @param aOptions Set of options (see below).
|
||||
* unfreeze - unfreeze the site after rearranging
|
||||
* callback - the callback to call when finished
|
||||
*/
|
||||
rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) {
|
||||
let batch;
|
||||
let cells = gGrid.cells;
|
||||
let callback = aOptions && aOptions.callback;
|
||||
let unfreeze = aOptions && aOptions.unfreeze;
|
||||
|
||||
if (callback) {
|
||||
batch = new Batch(callback);
|
||||
callback = function () batch.pop();
|
||||
}
|
||||
|
||||
aSites.forEach(function (aSite, aIndex) {
|
||||
// Do not re-arrange empty cells or the dragged site.
|
||||
if (!aSite || aSite == gDrag.draggedSite)
|
||||
return;
|
||||
|
||||
if (batch)
|
||||
batch.push();
|
||||
|
||||
if (!cells[aIndex])
|
||||
// The site disappeared from the grid, hide it.
|
||||
this.hideSite(aSite, callback);
|
||||
else if (this._getNodeOpacity(aSite.node) != 1)
|
||||
// The site disappeared before but is now back, show it.
|
||||
this.showSite(aSite, callback);
|
||||
else
|
||||
// The site's position has changed, move it around.
|
||||
this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: callback});
|
||||
}, this);
|
||||
|
||||
if (batch)
|
||||
batch.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Listens for the 'transitionend' event on a given node and calls the given
|
||||
* callback.
|
||||
* @param aNode The node that is transitioned.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
_whenTransitionEnded:
|
||||
function Transformation_whenTransitionEnded(aNode, aCallback) {
|
||||
|
||||
aNode.addEventListener("transitionend", function onEnd() {
|
||||
aNode.removeEventListener("transitionend", onEnd, false);
|
||||
aCallback();
|
||||
}, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a given node's opacity value.
|
||||
* @param aNode The node to get the opacity value from.
|
||||
* @return The node's opacity value.
|
||||
*/
|
||||
_getNodeOpacity: function Transformation_getNodeOpacity(aNode) {
|
||||
let cstyle = window.getComputedStyle(aNode, null);
|
||||
return cstyle.getPropertyValue("opacity");
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a given node's opacity.
|
||||
* @param aNode The node to set the opacity value for.
|
||||
* @param aOpacity The opacity value to set.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
_setNodeOpacity:
|
||||
function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) {
|
||||
|
||||
if (this._getNodeOpacity(aNode) == aOpacity) {
|
||||
if (aCallback)
|
||||
aCallback();
|
||||
} else {
|
||||
if (aCallback)
|
||||
this._whenTransitionEnded(aNode, aCallback);
|
||||
|
||||
aNode.style.opacity = aOpacity;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Moves a site to the cell with the given index.
|
||||
* @param aSite The site to move.
|
||||
* @param aIndex The target cell's index.
|
||||
* @param aOptions Options that are directly passed to slideSiteTo().
|
||||
*/
|
||||
_moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) {
|
||||
this.freezeSitePosition(aSite);
|
||||
this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
|
||||
}
|
||||
};
|
182
browser/base/content/newtab/updater.js
Normal file
182
browser/base/content/newtab/updater.js
Normal file
@ -0,0 +1,182 @@
|
||||
#ifdef 0
|
||||
/* 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/. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This singleton provides functionality to update the current grid to a new
|
||||
* set of pinned and blocked sites. It adds, moves and removes sites.
|
||||
*/
|
||||
let gUpdater = {
|
||||
/**
|
||||
* Updates the current grid according to its pinned and blocked sites.
|
||||
* This removes old, moves existing and creates new sites to fill gaps.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
updateGrid: function Updater_updateGrid(aCallback) {
|
||||
let links = gLinks.getLinks().slice(0, gGrid.cells.length);
|
||||
|
||||
// Find all sites that remain in the grid.
|
||||
let sites = this._findRemainingSites(links);
|
||||
|
||||
let self = this;
|
||||
|
||||
// Remove sites that are no longer in the grid.
|
||||
this._removeLegacySites(sites, function () {
|
||||
// Freeze all site positions so that we can move their DOM nodes around
|
||||
// without any visual impact.
|
||||
self._freezeSitePositions(sites);
|
||||
|
||||
// Move the sites' DOM nodes to their new position in the DOM. This will
|
||||
// have no visual effect as all the sites have been frozen and will
|
||||
// remain in their current position.
|
||||
self._moveSiteNodes(sites);
|
||||
|
||||
// Now it's time to animate the sites actually moving to their new
|
||||
// positions.
|
||||
self._rearrangeSites(sites, function () {
|
||||
// Try to fill empty cells and finish.
|
||||
self._fillEmptyCells(links, aCallback);
|
||||
|
||||
// Update other pages that might be open to keep them synced.
|
||||
gAllPages.update(gPage);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an array of links and tries to correlate them to sites contained in
|
||||
* the current grid. If no corresponding site can be found (i.e. the link is
|
||||
* new and a site will be created) then just set it to null.
|
||||
* @param aLinks The array of links to find sites for.
|
||||
* @return Array of sites mapped to the given links (can contain null values).
|
||||
*/
|
||||
_findRemainingSites: function Updater_findRemainingSites(aLinks) {
|
||||
let map = {};
|
||||
|
||||
// Create a map to easily retrieve the site for a given URL.
|
||||
gGrid.sites.forEach(function (aSite) {
|
||||
if (aSite)
|
||||
map[aSite.url] = aSite;
|
||||
});
|
||||
|
||||
// Map each link to its corresponding site, if any.
|
||||
return aLinks.map(function (aLink) {
|
||||
return aLink && (aLink.url in map) && map[aLink.url];
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Freezes the given sites' positions.
|
||||
* @param aSites The array of sites to freeze.
|
||||
*/
|
||||
_freezeSitePositions: function Updater_freezeSitePositions(aSites) {
|
||||
aSites.forEach(function (aSite) {
|
||||
if (aSite)
|
||||
gTransformation.freezeSitePosition(aSite);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Moves the given sites' DOM nodes to their new positions.
|
||||
* @param aSites The array of sites to move.
|
||||
*/
|
||||
_moveSiteNodes: function Updater_moveSiteNodes(aSites) {
|
||||
let cells = gGrid.cells;
|
||||
|
||||
// Truncate the given array of sites to not have more sites than cells.
|
||||
// This can happen when the user drags a bookmark (or any other new kind
|
||||
// of link) onto the grid.
|
||||
let sites = aSites.slice(0, cells.length);
|
||||
|
||||
sites.forEach(function (aSite, aIndex) {
|
||||
let cell = cells[aIndex];
|
||||
let cellSite = cell.site;
|
||||
|
||||
// The site's position didn't change.
|
||||
if (!aSite || cellSite != aSite) {
|
||||
let cellNode = cell.node;
|
||||
|
||||
// Empty the cell if necessary.
|
||||
if (cellSite)
|
||||
cellNode.removeChild(cellSite.node);
|
||||
|
||||
// Put the new site in place, if any.
|
||||
if (aSite)
|
||||
cellNode.appendChild(aSite.node);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Rearranges the given sites and slides them to their new positions.
|
||||
* @param aSites The array of sites to re-arrange.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
_rearrangeSites: function Updater_rearrangeSites(aSites, aCallback) {
|
||||
let options = {callback: aCallback, unfreeze: true};
|
||||
gTransformation.rearrangeSites(aSites, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all sites from the grid that are not in the given links array or
|
||||
* exceed the grid.
|
||||
* @param aSites The array of sites remaining in the grid.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
_removeLegacySites: function Updater_removeLegacySites(aSites, aCallback) {
|
||||
let batch = new Batch(aCallback);
|
||||
|
||||
// Delete sites that were removed from the grid.
|
||||
gGrid.sites.forEach(function (aSite) {
|
||||
// The site must be valid and not in the current grid.
|
||||
if (!aSite || aSites.indexOf(aSite) != -1)
|
||||
return;
|
||||
|
||||
batch.push();
|
||||
|
||||
// Fade out the to-be-removed site.
|
||||
gTransformation.hideSite(aSite, function () {
|
||||
let node = aSite.node;
|
||||
|
||||
// Remove the site from the DOM.
|
||||
node.parentNode.removeChild(node);
|
||||
batch.pop();
|
||||
});
|
||||
});
|
||||
|
||||
batch.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Tries to fill empty cells with new links if available.
|
||||
* @param aLinks The array of links.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
_fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) {
|
||||
let {cells, sites} = gGrid;
|
||||
let batch = new Batch(aCallback);
|
||||
|
||||
// Find empty cells and fill them.
|
||||
sites.forEach(function (aSite, aIndex) {
|
||||
if (aSite || !aLinks[aIndex])
|
||||
return;
|
||||
|
||||
batch.push();
|
||||
|
||||
// Create the new site and fade it in.
|
||||
let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]);
|
||||
|
||||
// Set the site's initial opacity to zero.
|
||||
site.node.style.opacity = 0;
|
||||
|
||||
// Without the setTimeout() the node would just appear instead of fade in.
|
||||
setTimeout(function () {
|
||||
gTransformation.showSite(site, function () batch.pop());
|
||||
}, 0);
|
||||
});
|
||||
|
||||
batch.close();
|
||||
}
|
||||
};
|
@ -632,7 +632,7 @@
|
||||
autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
|
||||
delete this.mBrowser.registeredOpenURI;
|
||||
}
|
||||
if (aLocation.spec != "about:blank") {
|
||||
if (!isBlankPageURL(aLocation.spec)) {
|
||||
autocomplete.registerOpenPage(aLocation);
|
||||
this.mBrowser.registeredOpenURI = aLocation;
|
||||
}
|
||||
@ -1065,7 +1065,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (title && title != "about:blank") {
|
||||
if (title && !isBlankPageURL(title)) {
|
||||
// At this point, we now have a URI.
|
||||
// Let's try to unescape it using a character set
|
||||
// in case the URI is not ASCII.
|
||||
@ -1589,7 +1589,7 @@
|
||||
aTab.closing = true;
|
||||
this._removingTabs.push(aTab);
|
||||
if (newTab)
|
||||
this.addTab("about:blank", {skipAnimation: true});
|
||||
this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
|
||||
else
|
||||
this.tabContainer.updateVisibility();
|
||||
|
||||
|
@ -40,6 +40,10 @@ srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = browser/base/content/test
|
||||
|
||||
DIRS += \
|
||||
newtab \
|
||||
$(NULL)
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
27
browser/base/content/test/newtab/Makefile.in
Normal file
27
browser/base/content/test/newtab/Makefile.in
Normal file
@ -0,0 +1,27 @@
|
||||
# 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/.
|
||||
|
||||
DEPTH = ../../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = browser/base/content/test/newtab
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_FILES = \
|
||||
browser_newtab_block.js \
|
||||
browser_newtab_disable.js \
|
||||
browser_newtab_drag_drop.js \
|
||||
browser_newtab_drop_preview.js \
|
||||
browser_newtab_private_browsing.js \
|
||||
browser_newtab_reset.js \
|
||||
browser_newtab_tabsync.js \
|
||||
browser_newtab_unpin.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
61
browser/base/content/test/newtab/browser_newtab_block.js
Normal file
61
browser/base/content/test/newtab/browser_newtab_block.js
Normal file
@ -0,0 +1,61 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* These tests make sure that blocking/removing sites from the grid works
|
||||
* as expected. Pinned tabs should not be moved. Gaps will be re-filled
|
||||
* if more sites are available.
|
||||
*/
|
||||
function runTests() {
|
||||
// we remove sites and expect the gaps to be filled as long as there still
|
||||
// are some sites available
|
||||
setLinks("0,1,2,3,4,5,6,7,8,9");
|
||||
setPinnedLinks("");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
|
||||
yield blockCell(cells[4]);
|
||||
checkGrid("0,1,2,3,5,6,7,8,9");
|
||||
|
||||
yield blockCell(cells[4]);
|
||||
checkGrid("0,1,2,3,6,7,8,9,");
|
||||
|
||||
yield blockCell(cells[4]);
|
||||
checkGrid("0,1,2,3,7,8,9,,");
|
||||
|
||||
// we removed a pinned site
|
||||
reset();
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",1");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1p,2,3,4,5,6,7,8");
|
||||
|
||||
yield blockCell(cells[1]);
|
||||
checkGrid("0,2,3,4,5,6,7,8,");
|
||||
|
||||
// we remove the last site on the grid (which is pinned) and expect the gap
|
||||
// to be re-filled and the new site to be unpinned
|
||||
reset();
|
||||
setLinks("0,1,2,3,4,5,6,7,8,9");
|
||||
setPinnedLinks(",,,,,,,,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8p");
|
||||
|
||||
yield blockCell(cells[8]);
|
||||
checkGrid("0,1,2,3,4,5,6,7,9");
|
||||
|
||||
// we remove the first site on the grid with the last one pinned. all cells
|
||||
// but the last one should shift to the left and a new site fades in
|
||||
reset();
|
||||
setLinks("0,1,2,3,4,5,6,7,8,9");
|
||||
setPinnedLinks(",,,,,,,,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8p");
|
||||
|
||||
yield blockCell(cells[0]);
|
||||
checkGrid("1,2,3,4,5,6,7,9,8p");
|
||||
}
|
34
browser/base/content/test/newtab/browser_newtab_disable.js
Normal file
34
browser/base/content/test/newtab/browser_newtab_disable.js
Normal file
@ -0,0 +1,34 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* These tests make sure that the 'New Tab Page' feature can be disabled if the
|
||||
* decides not to use it.
|
||||
*/
|
||||
function runTests() {
|
||||
// create a new tab page and hide it.
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
let gridNode = cw.gGrid.node;
|
||||
|
||||
ok(!gridNode.hasAttribute("page-disabled"), "page is not disabled");
|
||||
|
||||
cw.gToolbar.hide();
|
||||
ok(gridNode.hasAttribute("page-disabled"), "page is disabled");
|
||||
|
||||
let oldGridNode = cw.gGrid.node;
|
||||
|
||||
// create a second new tage page and make sure it's disabled. enable it
|
||||
// again and check if the former page gets enabled as well.
|
||||
yield addNewTabPageTab();
|
||||
ok(gridNode.hasAttribute("page-disabled"), "page is disabled");
|
||||
|
||||
// check that no sites have been rendered
|
||||
is(0, cw.document.querySelectorAll(".site").length, "no sites have been rendered");
|
||||
|
||||
cw.gToolbar.show();
|
||||
ok(!gridNode.hasAttribute("page-disabled"), "page is not disabled");
|
||||
ok(!oldGridNode.hasAttribute("page-disabled"), "old page is not disabled");
|
||||
}
|
115
browser/base/content/test/newtab/browser_newtab_drag_drop.js
Normal file
115
browser/base/content/test/newtab/browser_newtab_drag_drop.js
Normal file
@ -0,0 +1,115 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* These tests make sure that dragging and dropping sites works as expected.
|
||||
* Sites contained in the grid need to shift around to indicate the result
|
||||
* of the drag-and-drop operation. If the grid is full and we're dragging
|
||||
* a new site into it another one gets pushed out.
|
||||
*/
|
||||
function runTests() {
|
||||
// test a simple drag-and-drop scenario
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
|
||||
yield simulateDrop(cells[1], cells[0]);
|
||||
checkGrid("1,0p,2,3,4,5,6,7,8");
|
||||
|
||||
// drag a cell to its current cell and make sure it's not pinned afterwards
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
|
||||
yield simulateDrop(cells[0], cells[0]);
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
|
||||
// ensure that pinned pages aren't moved if that's not necessary
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",1,2");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1p,2p,3,4,5,6,7,8");
|
||||
|
||||
yield simulateDrop(cells[3], cells[0]);
|
||||
checkGrid("3,1p,2p,0p,4,5,6,7,8");
|
||||
|
||||
// pinned sites should always be moved around as blocks. if a pinned site is
|
||||
// moved around, neighboring pinned are affected as well
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("0,1");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0p,1p,2,3,4,5,6,7,8");
|
||||
|
||||
yield simulateDrop(cells[0], cells[2]);
|
||||
checkGrid("2p,0p,1p,3,4,5,6,7,8");
|
||||
|
||||
// pinned sites should not be pushed out of the grid (unless there are only
|
||||
// pinned ones left on the grid)
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",,,,,,,7,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7p,8p");
|
||||
|
||||
yield simulateDrop(cells[8], cells[2]);
|
||||
checkGrid("0,1,3,4,5,6,7p,8p,2p");
|
||||
|
||||
// make sure that pinned sites are re-positioned correctly
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("0,1,2,,,5");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0p,1p,2p,3,4,5p,6,7,8");
|
||||
|
||||
yield simulateDrop(cells[4], cells[0]);
|
||||
checkGrid("3,1p,2p,4,0p,5p,6,7,8");
|
||||
|
||||
// drag a new site onto the very first cell
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",,,,,,,7,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7p,8p");
|
||||
|
||||
yield simulateDrop(cells[0]);
|
||||
checkGrid("99p,0,1,2,3,4,5,7p,8p");
|
||||
|
||||
// drag a new site onto the grid and make sure that pinned cells don't get
|
||||
// pushed out
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",,,,,,,7,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7p,8p");
|
||||
|
||||
yield simulateDrop(cells[7]);
|
||||
checkGrid("0,1,2,3,4,5,7p,99p,8p");
|
||||
|
||||
// drag a new site beneath a pinned cell and make sure the pinned cell is
|
||||
// not moved
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",,,,,,,,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8p");
|
||||
|
||||
yield simulateDrop(cells[7]);
|
||||
checkGrid("0,1,2,3,4,5,6,99p,8p");
|
||||
|
||||
// drag a new site onto a block of pinned sites and make sure they're shifted
|
||||
// around accordingly
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("0,1,2,,,,,,");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0p,1p,2p");
|
||||
|
||||
yield simulateDrop(cells[1]);
|
||||
checkGrid("0p,99p,1p,2p,3,4,5,6,7");
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* These tests ensure that the drop preview correctly arranges sites when
|
||||
* dragging them around.
|
||||
*/
|
||||
function runTests() {
|
||||
// the first three sites are pinned - make sure they're re-arranged correctly
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("0,1,2,,,5");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0p,1p,2p,3,4,5p,6,7,8");
|
||||
|
||||
cw.gDrag._draggedSite = cells[0].site;
|
||||
let sites = cw.gDropPreview.rearrange(cells[4]);
|
||||
cw.gDrag._draggedSite = null;
|
||||
|
||||
checkGrid("3,1p,2p,4,0p,5p,6,7,8", sites);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* These tests ensure that all changes made to the new tab page in private
|
||||
* browsing mode are discarded after switching back to normal mode again.
|
||||
* The private browsing mode should start with the current grid shown in normal
|
||||
* mode.
|
||||
*/
|
||||
let pb = Cc["@mozilla.org/privatebrowsing;1"]
|
||||
.getService(Ci.nsIPrivateBrowsingService);
|
||||
|
||||
function runTests() {
|
||||
// prepare the grid
|
||||
setLinks("0,1,2,3,4,5,6,7,8,9");
|
||||
ok(!pb.privateBrowsingEnabled, "private browsing is disabled");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
pinCell(cells[0]);
|
||||
checkGrid("0p,1,2,3,4,5,6,7,8");
|
||||
|
||||
// enter private browsing mode
|
||||
yield togglePrivateBrowsing();
|
||||
ok(pb.privateBrowsingEnabled, "private browsing is enabled");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0p,1,2,3,4,5,6,7,8");
|
||||
|
||||
// modify the grid while we're in pb mode
|
||||
yield blockCell(cells[1]);
|
||||
checkGrid("0p,2,3,4,5,6,7,8");
|
||||
|
||||
yield unpinCell(cells[0]);
|
||||
checkGrid("0,2,3,4,5,6,7,8");
|
||||
|
||||
// exit private browsing mode
|
||||
yield togglePrivateBrowsing();
|
||||
ok(!pb.privateBrowsingEnabled, "private browsing is disabled");
|
||||
|
||||
// check that the grid is the same as before entering pb mode
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0p,1,2,3,4,5,6,7,8");
|
||||
}
|
||||
|
||||
function togglePrivateBrowsing() {
|
||||
let topic = "private-browsing-transition-complete";
|
||||
|
||||
Services.obs.addObserver(function observe() {
|
||||
Services.obs.removeObserver(observe, topic);
|
||||
executeSoon(TestRunner.next);
|
||||
}, topic, false);
|
||||
|
||||
pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
|
||||
}
|
25
browser/base/content/test/newtab/browser_newtab_reset.js
Normal file
25
browser/base/content/test/newtab/browser_newtab_reset.js
Normal file
@ -0,0 +1,25 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* These tests make sure that resetting the 'New Tage Page' works as expected.
|
||||
*/
|
||||
function runTests() {
|
||||
// create a new tab page and check its modified state after blocking a site
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
let resetButton = cw.document.getElementById("toolbar-button-reset");
|
||||
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
ok(!resetButton.hasAttribute("modified"), "page is not modified");
|
||||
|
||||
yield blockCell(cells[4]);
|
||||
checkGrid("0,1,2,3,5,6,7,8,");
|
||||
ok(resetButton.hasAttribute("modified"), "page is modified");
|
||||
|
||||
yield cw.gToolbar.reset(TestRunner.next);
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
ok(!resetButton.hasAttribute("modified"), "page is not modified");
|
||||
}
|
58
browser/base/content/test/newtab/browser_newtab_tabsync.js
Normal file
58
browser/base/content/test/newtab/browser_newtab_tabsync.js
Normal file
@ -0,0 +1,58 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* These tests make sure that all changes that are made to a specific
|
||||
* 'New Tab Page' are synchronized with all other open 'New Tab Pages'
|
||||
* automatically. All about:newtab pages should always be in the same
|
||||
* state.
|
||||
*/
|
||||
function runTests() {
|
||||
setLinks("0,1,2,3,4,5,6,7,8,9");
|
||||
setPinnedLinks(",1");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1p,2,3,4,5,6,7,8");
|
||||
|
||||
let resetButton = cw.document.getElementById("toolbar-button-reset");
|
||||
ok(!resetButton.hasAttribute("modified"), "page is not modified");
|
||||
|
||||
let oldCw = cw;
|
||||
let oldResetButton = resetButton;
|
||||
|
||||
// create the new tab page
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1p,2,3,4,5,6,7,8");
|
||||
|
||||
resetButton = cw.document.getElementById("toolbar-button-reset");
|
||||
ok(!resetButton.hasAttribute("modified"), "page is not modified");
|
||||
|
||||
// unpin a cell
|
||||
yield unpinCell(cells[1]);
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
checkGrid("0,1,2,3,4,5,6,7,8", oldCw.gGrid.sites);
|
||||
|
||||
// remove a cell
|
||||
yield blockCell(cells[1]);
|
||||
checkGrid("0,2,3,4,5,6,7,8,9");
|
||||
checkGrid("0,2,3,4,5,6,7,8,9", oldCw.gGrid.sites);
|
||||
ok(resetButton.hasAttribute("modified"), "page is modified");
|
||||
ok(oldResetButton.hasAttribute("modified"), "page is modified");
|
||||
|
||||
// insert a new cell by dragging
|
||||
yield simulateDrop(cells[1]);
|
||||
checkGrid("0,99p,2,3,4,5,6,7,8");
|
||||
checkGrid("0,99p,2,3,4,5,6,7,8", oldCw.gGrid.sites);
|
||||
|
||||
// drag a cell around
|
||||
yield simulateDrop(cells[1], cells[2]);
|
||||
checkGrid("0,2p,99p,3,4,5,6,7,8");
|
||||
checkGrid("0,2p,99p,3,4,5,6,7,8", oldCw.gGrid.sites);
|
||||
|
||||
// reset the new tab page
|
||||
yield cw.gToolbar.reset(TestRunner.next);
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
checkGrid("0,1,2,3,4,5,6,7,8", oldCw.gGrid.sites);
|
||||
ok(!resetButton.hasAttribute("modified"), "page is not modified");
|
||||
ok(!oldResetButton.hasAttribute("modified"), "page is not modified");
|
||||
}
|
56
browser/base/content/test/newtab/browser_newtab_unpin.js
Normal file
56
browser/base/content/test/newtab/browser_newtab_unpin.js
Normal file
@ -0,0 +1,56 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* These tests make sure that when a site gets unpinned it is either moved to
|
||||
* its actual place in the grid or removed in case it's not on the grid anymore.
|
||||
*/
|
||||
function runTests() {
|
||||
// we have a pinned link that didn't change its position since it was pinned.
|
||||
// nothing should happend when we unpin it.
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",1");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1p,2,3,4,5,6,7,8");
|
||||
|
||||
yield unpinCell(cells[1]);
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
|
||||
// we have a pinned link that is not anymore in the list of the most-visited
|
||||
// links. this should disappear, the remaining links adjust their positions
|
||||
// and a new link will appear at the end of the grid.
|
||||
setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",99");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,99p,1,2,3,4,5,6,7");
|
||||
|
||||
yield unpinCell(cells[1]);
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
|
||||
// we have a pinned link that changed its position since it was pinned. it
|
||||
// should be moved to its new position after being unpinned.
|
||||
setLinks("0,1,2,3,4,5,6,7");
|
||||
setPinnedLinks(",1,,,,,,,0");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("2,1p,3,4,5,6,7,,0p");
|
||||
|
||||
yield unpinCell(cells[1]);
|
||||
checkGrid("1,2,3,4,5,6,7,,0p");
|
||||
|
||||
yield unpinCell(cells[8]);
|
||||
checkGrid("0,1,2,3,4,5,6,7,");
|
||||
|
||||
// we have pinned link that changed its position since it was pinned. the
|
||||
// link will disappear from the grid because it's now a much lower priority
|
||||
setLinks("0,1,2,3,4,5,6,7,8,9");
|
||||
setPinnedLinks("9");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("9p,0,1,2,3,4,5,6,7");
|
||||
|
||||
yield unpinCell(cells[0]);
|
||||
checkGrid("0,1,2,3,4,5,6,7,8");
|
||||
}
|
262
browser/base/content/test/newtab/head.js
Normal file
262
browser/base/content/test/newtab/head.js
Normal file
@ -0,0 +1,262 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
|
||||
|
||||
Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
|
||||
|
||||
Cu.import("resource:///modules/NewTabUtils.jsm");
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
reset();
|
||||
|
||||
while (gBrowser.tabs.length > 1)
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
|
||||
Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
|
||||
});
|
||||
|
||||
/**
|
||||
* Global variables that are accessed by tests.
|
||||
*/
|
||||
let cw;
|
||||
let cells;
|
||||
|
||||
/**
|
||||
* We'll want to restore the original links provider later.
|
||||
*/
|
||||
let originalProvider = NewTabUtils.links._provider;
|
||||
|
||||
/**
|
||||
* Provide the default test function to start our test runner.
|
||||
*/
|
||||
function test() {
|
||||
TestRunner.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* The test runner that controls the execution flow of our tests.
|
||||
*/
|
||||
let TestRunner = {
|
||||
/**
|
||||
* Starts the test runner.
|
||||
*/
|
||||
run: function () {
|
||||
waitForExplicitFinish();
|
||||
|
||||
this._iter = runTests();
|
||||
this.next();
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs the next available test or finishes if there's no test left.
|
||||
*/
|
||||
next: function () {
|
||||
try {
|
||||
TestRunner._iter.next();
|
||||
} catch (e if e instanceof StopIteration) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows to provide a list of links that is used to construct the grid.
|
||||
* @param aLinksPattern the pattern (see below)
|
||||
*
|
||||
* Example: setLinks("1,2,3")
|
||||
* Result: [{url: "about:blank#1", title: "site#1"},
|
||||
* {url: "about:blank#2", title: "site#2"}
|
||||
* {url: "about:blank#3", title: "site#3"}]
|
||||
*/
|
||||
function setLinks(aLinksPattern) {
|
||||
let links = aLinksPattern.split(/\s*,\s*/).map(function (id) {
|
||||
return {url: "about:blank#" + id, title: "site#" + id};
|
||||
});
|
||||
|
||||
NewTabUtils.links._provider = {getLinks: function (c) c(links)};
|
||||
NewTabUtils.links._links = links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to specify the list of pinned links (that have a fixed position in
|
||||
* the grid.
|
||||
* @param aLinksPattern the pattern (see below)
|
||||
*
|
||||
* Example: setPinnedLinks("3,,1")
|
||||
* Result: 'about:blank#3' is pinned in the first cell. 'about:blank#1' is
|
||||
* pinned in the third cell.
|
||||
*/
|
||||
function setPinnedLinks(aLinksPattern) {
|
||||
let pinnedLinks = [];
|
||||
|
||||
aLinksPattern.split(/\s*,\s*/).forEach(function (id, index) {
|
||||
let link;
|
||||
|
||||
if (id)
|
||||
link = {url: "about:blank#" + id, title: "site#" + id};
|
||||
|
||||
pinnedLinks[index] = link;
|
||||
});
|
||||
|
||||
// Inject the list of pinned links to work with.
|
||||
NewTabUtils.pinnedLinks._links = pinnedLinks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the lists of blocked and pinned links and clears the storage.
|
||||
*/
|
||||
function reset() {
|
||||
NewTabUtils.reset();
|
||||
|
||||
// Restore the old provider to prevent memory leaks.
|
||||
NewTabUtils.links._provider = originalProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tab containing 'about:newtab'.
|
||||
*/
|
||||
function addNewTabPageTab() {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:newtab");
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
// Wait for the new tab page to be loaded.
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
|
||||
cw = browser.contentWindow;
|
||||
|
||||
if (NewTabUtils.allPages.enabled) {
|
||||
cells = cw.gGrid.cells;
|
||||
|
||||
// Continue when the link cache has been populated.
|
||||
NewTabUtils.links.populateCache(TestRunner.next);
|
||||
} else {
|
||||
TestRunner.next();
|
||||
}
|
||||
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the current grid arrangement with the given pattern.
|
||||
* @param the pattern (see below)
|
||||
* @param the array of sites to compare with (optional)
|
||||
*
|
||||
* Example: checkGrid("3p,2,,1p")
|
||||
* Result: We expect the first cell to contain the pinned site 'about:blank#3'.
|
||||
* The second cell contains 'about:blank#2'. The third cell is empty.
|
||||
* The fourth cell contains the pinned site 'about:blank#4'.
|
||||
*/
|
||||
function checkGrid(aSitesPattern, aSites) {
|
||||
let valid = true;
|
||||
|
||||
aSites = aSites || cw.gGrid.sites;
|
||||
|
||||
aSitesPattern.split(/\s*,\s*/).forEach(function (id, index) {
|
||||
let site = aSites[index];
|
||||
let match = id.match(/^\d+/);
|
||||
|
||||
// We expect the cell to be empty.
|
||||
if (!match) {
|
||||
if (site) {
|
||||
valid = false;
|
||||
ok(false, "expected cell#" + index + " to be empty");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We expect the cell to contain a site.
|
||||
if (!site) {
|
||||
valid = false;
|
||||
ok(false, "didn't expect cell#" + index + " to be empty");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let num = match[0];
|
||||
|
||||
// Check the site's url.
|
||||
if (site.url != "about:blank#" + num) {
|
||||
valid = false;
|
||||
is(site.url, "about:blank#" + num, "cell#" + index + " has the wrong url");
|
||||
}
|
||||
|
||||
let shouldBePinned = /p$/.test(id);
|
||||
let cellContainsPinned = site.isPinned();
|
||||
let cssClassPinned = site.node && site.node.hasAttribute("pinned");
|
||||
|
||||
// Check if the site should be and is pinned.
|
||||
if (shouldBePinned) {
|
||||
if (!cellContainsPinned) {
|
||||
valid = false;
|
||||
ok(false, "expected cell#" + index + " to be pinned");
|
||||
} else if (!cssClassPinned) {
|
||||
valid = false;
|
||||
ok(false, "expected cell#" + index + " to have css class 'pinned'");
|
||||
}
|
||||
} else {
|
||||
if (cellContainsPinned) {
|
||||
valid = false;
|
||||
ok(false, "didn't expect cell#" + index + " to be pinned");
|
||||
} else if (cssClassPinned) {
|
||||
valid = false;
|
||||
ok(false, "didn't expect cell#" + index + " to have css class 'pinned'");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If every test passed, say so.
|
||||
if (valid)
|
||||
ok(true, "grid status = " + aSitesPattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks the given cell's site from the grid.
|
||||
* @param aCell the cell that contains the site to block
|
||||
*/
|
||||
function blockCell(aCell) {
|
||||
aCell.site.block(function () executeSoon(TestRunner.next));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pins a given cell's site on a given position.
|
||||
* @param aCell the cell that contains the site to pin
|
||||
* @param aIndex the index the defines where the site should be pinned
|
||||
*/
|
||||
function pinCell(aCell, aIndex) {
|
||||
aCell.site.pin(aIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpins the given cell's site.
|
||||
* @param aCell the cell that contains the site to unpin
|
||||
*/
|
||||
function unpinCell(aCell) {
|
||||
aCell.site.unpin(function () executeSoon(TestRunner.next));
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a drop and drop operation.
|
||||
* @param aDropTarget the cell that is the drop target
|
||||
* @param aDragSource the cell that contains the dragged site (optional)
|
||||
*/
|
||||
function simulateDrop(aDropTarget, aDragSource) {
|
||||
let event = {
|
||||
dataTransfer: {
|
||||
mozUserCancelled: false,
|
||||
setData: function () null,
|
||||
setDragImage: function () null,
|
||||
getData: function () "about:blank#99\nblank"
|
||||
}
|
||||
};
|
||||
|
||||
if (aDragSource)
|
||||
cw.gDrag.start(aDragSource.site, event);
|
||||
|
||||
cw.gDrop.drop(aDropTarget, event, function () executeSoon(TestRunner.next));
|
||||
|
||||
if (aDragSource)
|
||||
cw.gDrag.end(aDragSource.site);
|
||||
}
|
@ -41,11 +41,23 @@
|
||||
|
||||
// Services = object with smart getters for common XPCOM services
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () {
|
||||
return Services.prefs.getCharPref("browser.newtab.url") || "about:blank";
|
||||
});
|
||||
|
||||
var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
|
||||
|
||||
var gBidiUI = false;
|
||||
|
||||
/**
|
||||
* Determines whether the given url is considered a special URL for new tabs.
|
||||
*/
|
||||
function isBlankPageURL(aURL) {
|
||||
return aURL == "about:blank" || aURL == BROWSER_NEW_TAB_URL;
|
||||
}
|
||||
|
||||
function getBrowserURL()
|
||||
{
|
||||
return "chrome://browser/content/browser.xul";
|
||||
@ -299,7 +311,7 @@ function openLinkIn(url, where, params) {
|
||||
else
|
||||
w.gBrowser.selectedBrowser.focus();
|
||||
|
||||
if (!loadInBackground && url == "about:blank")
|
||||
if (!loadInBackground && isBlankPageURL(url))
|
||||
w.focusAndSelectUrlBar();
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,9 @@ browser.jar:
|
||||
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
|
||||
* content/browser/content.js (content/content.js)
|
||||
* content/browser/fullscreen-video.xhtml (content/fullscreen-video.xhtml)
|
||||
* content/browser/newtab/newTab.xul (content/newtab/newTab.xul)
|
||||
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
|
||||
content/browser/newtab/newTab.css (content/newtab/newTab.css)
|
||||
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
|
||||
* content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js)
|
||||
* content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css)
|
||||
|
@ -105,6 +105,8 @@ static RedirEntry kRedirMap[] = {
|
||||
{ "home", "chrome://browser/content/aboutHome.xhtml",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "newtab", "chrome://browser/content/newtab/newTab.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
};
|
||||
|
@ -143,6 +143,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-progress", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
#endif
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
#if defined(XP_WIN) && !defined(__MINGW32__)
|
||||
{ NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX "ie", &kNS_WINIEPROFILEMIGRATOR_CID },
|
||||
|
@ -18,10 +18,9 @@ EXTRA_PP_JS_MODULES = \
|
||||
PageThumbs.jsm \
|
||||
$(NULL)
|
||||
|
||||
# FIXME Bug 721422 - Re-enable tests and make them work with URI_DANGEROUS_TO_LOAD
|
||||
#ifdef ENABLE_TESTS
|
||||
# DIRS += test
|
||||
#endif
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += test
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
@ -12,7 +12,6 @@ include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_FILES = \
|
||||
browser_thumbnails_cache.js \
|
||||
browser_thumbnails_capture.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
@ -1,34 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests ensure that saving a thumbnail to the cache works. They also
|
||||
* retrieve the thumbnail and display it using an <img> element to compare
|
||||
* its pixel colors.
|
||||
*/
|
||||
function runTests() {
|
||||
// Create a new tab with a red background.
|
||||
yield addTab("data:text/html,<body bgcolor=ff0000></body>");
|
||||
let cw = gBrowser.selectedTab.linkedBrowser.contentWindow;
|
||||
|
||||
// Capture a thumbnail for the tab.
|
||||
let canvas = PageThumbs.capture(cw);
|
||||
|
||||
// Store the tab into the thumbnail cache.
|
||||
yield PageThumbs.store("key", canvas, next);
|
||||
|
||||
let {width, height} = canvas;
|
||||
let thumb = PageThumbs.getThumbnailURL("key", width, height);
|
||||
|
||||
// Create a new tab with an image displaying the previously stored thumbnail.
|
||||
yield addTab("data:text/html,<img src='" + thumb + "'/>" +
|
||||
"<canvas width=" + width + " height=" + height + "/>");
|
||||
|
||||
cw = gBrowser.selectedTab.linkedBrowser.contentWindow;
|
||||
let [img, canvas] = cw.document.querySelectorAll("img, canvas");
|
||||
|
||||
// Draw the image to a canvas and compare the pixel color values.
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
checkCanvasColor(ctx, 255, 0, 0, "we have a red image and canvas");
|
||||
}
|
@ -2,37 +2,19 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* These tests ensure that capturing a site's screenshot to a canvas actually
|
||||
* works.
|
||||
* These tests ensure that capturing a sites's thumbnail, saving it and
|
||||
* retrieving it from the cache works.
|
||||
*/
|
||||
function runTests() {
|
||||
// Create a tab with a red background.
|
||||
yield addTab("data:text/html,<body bgcolor=ff0000></body>");
|
||||
checkCurrentThumbnailColor(255, 0, 0, "we have a red thumbnail");
|
||||
yield captureAndCheckColor(255, 0, 0, "we have a red thumbnail");
|
||||
|
||||
// Load a page with a green background.
|
||||
yield navigateTo("data:text/html,<body bgcolor=00ff00></body>");
|
||||
checkCurrentThumbnailColor(0, 255, 0, "we have a green thumbnail");
|
||||
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
|
||||
|
||||
// Load a page with a blue background.
|
||||
yield navigateTo("data:text/html,<body bgcolor=0000ff></body>");
|
||||
checkCurrentThumbnailColor(0, 0, 255, "we have a blue thumbnail");
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a thumbnail of the currently selected tab and checks the color of
|
||||
* the resulting canvas.
|
||||
* @param aRed The red component's intensity.
|
||||
* @param aGreen The green component's intensity.
|
||||
* @param aBlue The blue component's intensity.
|
||||
* @param aMessage The info message to print when checking the pixel color.
|
||||
*/
|
||||
function checkCurrentThumbnailColor(aRed, aGreen, aBlue, aMessage) {
|
||||
let tab = gBrowser.selectedTab;
|
||||
let cw = tab.linkedBrowser.contentWindow;
|
||||
|
||||
let canvas = PageThumbs.capture(cw);
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
|
||||
yield captureAndCheckColor(0, 0, 255, "we have a blue thumbnail");
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ registerCleanupFunction(function () {
|
||||
gBrowser.removeTab(gBrowser.tabs[1]);
|
||||
});
|
||||
|
||||
let cachedXULDocument;
|
||||
|
||||
/**
|
||||
* Provide the default test function to start our test runner.
|
||||
*/
|
||||
@ -54,7 +56,7 @@ function next() {
|
||||
*/
|
||||
function addTab(aURI) {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(aURI);
|
||||
whenBrowserLoaded(tab.linkedBrowser);
|
||||
whenLoaded(tab.linkedBrowser);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,22 +65,88 @@ function addTab(aURI) {
|
||||
*/
|
||||
function navigateTo(aURI) {
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
whenBrowserLoaded(browser);
|
||||
whenLoaded(browser);
|
||||
browser.loadURI(aURI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues the current test execution when a load event for the given browser
|
||||
* has been received
|
||||
* @param aBrowser The browser to listen on.
|
||||
* Continues the current test execution when a load event for the given element
|
||||
* has been received.
|
||||
* @param aElement The DOM element to listen on.
|
||||
* @param aCallback The function to call when the load event was dispatched.
|
||||
*/
|
||||
function whenBrowserLoaded(aBrowser) {
|
||||
aBrowser.addEventListener("load", function onLoad() {
|
||||
aBrowser.removeEventListener("load", onLoad, true);
|
||||
executeSoon(next);
|
||||
function whenLoaded(aElement, aCallback) {
|
||||
aElement.addEventListener("load", function onLoad() {
|
||||
aElement.removeEventListener("load", onLoad, true);
|
||||
executeSoon(aCallback || next);
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a screenshot for the currently selected tab, stores it in the cache,
|
||||
* retrieves it from the cache and compares pixel color values.
|
||||
* @param aRed The red component's intensity.
|
||||
* @param aGreen The green component's intensity.
|
||||
* @param aBlue The blue component's intensity.
|
||||
* @param aMessage The info message to print when comparing the pixel color.
|
||||
*/
|
||||
function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
|
||||
let window = gBrowser.selectedTab.linkedBrowser.contentWindow;
|
||||
|
||||
let key = Date.now();
|
||||
let data = PageThumbs.capture(window);
|
||||
|
||||
// Store the thumbnail in the cache.
|
||||
PageThumbs.store(key, data, function () {
|
||||
let width = 100, height = 100;
|
||||
let thumb = PageThumbs.getThumbnailURL(key, width, height);
|
||||
|
||||
getXULDocument(function (aDocument) {
|
||||
let htmlns = "http://www.w3.org/1999/xhtml";
|
||||
let img = aDocument.createElementNS(htmlns, "img");
|
||||
img.setAttribute("src", thumb);
|
||||
|
||||
whenLoaded(img, function () {
|
||||
let canvas = aDocument.createElementNS(htmlns, "canvas");
|
||||
canvas.setAttribute("width", width);
|
||||
canvas.setAttribute("height", height);
|
||||
|
||||
// Draw the image to a canvas and compare the pixel color values.
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
|
||||
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes a XUL document (created if necessary) to the given callback.
|
||||
* @param aCallback The function to be called when the XUL document has been
|
||||
* created. The first argument will be the document.
|
||||
*/
|
||||
function getXULDocument(aCallback) {
|
||||
let hiddenWindow = Services.appShell.hiddenDOMWindow;
|
||||
let doc = cachedXULDocument || hiddenWindow.document;
|
||||
|
||||
if (doc instanceof XULDocument) {
|
||||
aCallback(cachedXULDocument = doc);
|
||||
return;
|
||||
}
|
||||
|
||||
let iframe = doc.createElement("iframe");
|
||||
iframe.setAttribute("src", "chrome://global/content/mozilla.xhtml");
|
||||
|
||||
iframe.addEventListener("DOMContentLoaded", function onLoad() {
|
||||
iframe.removeEventListener("DOMContentLoaded", onLoad, false);
|
||||
aCallback(cachedXULDocument = iframe.contentDocument);
|
||||
}, false);
|
||||
|
||||
doc.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the top-left pixel of a given canvas' 2d context for a given color.
|
||||
* @param aContext The 2D context of a canvas.
|
||||
|
@ -118,6 +118,51 @@ LayoutHelpers = {
|
||||
return rect;
|
||||
},
|
||||
|
||||
/**
|
||||
* Compute the absolute position and the dimensions of a node, relativalely
|
||||
* to the root window.
|
||||
*
|
||||
* @param nsIDOMNode aNode
|
||||
* a DOM element to get the bounds for
|
||||
* @param nsIWindow aContentWindow
|
||||
* the content window holding the node
|
||||
*/
|
||||
getRect: function LH_getRect(aNode, aContentWindow) {
|
||||
let frameWin = aNode.ownerDocument.defaultView;
|
||||
let clientRect = aNode.getBoundingClientRect();
|
||||
|
||||
// Go up in the tree of frames to determine the correct rectangle.
|
||||
// clientRect is read-only, we need to be able to change properties.
|
||||
rect = {top: clientRect.top + aContentWindow.pageYOffset,
|
||||
left: clientRect.left + aContentWindow.pageXOffset,
|
||||
width: clientRect.width,
|
||||
height: clientRect.height};
|
||||
|
||||
// We iterate through all the parent windows.
|
||||
while (true) {
|
||||
|
||||
// Are we in the top-level window?
|
||||
if (frameWin.parent === frameWin || !frameWin.frameElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We are in an iframe.
|
||||
// We take into account the parent iframe position and its
|
||||
// offset (borders and padding).
|
||||
let frameRect = frameWin.frameElement.getBoundingClientRect();
|
||||
|
||||
let [offsetTop, offsetLeft] =
|
||||
this.getIframeContentOffset(frameWin.frameElement);
|
||||
|
||||
rect.top += frameRect.top + offsetTop;
|
||||
rect.left += frameRect.left + offsetLeft;
|
||||
|
||||
frameWin = frameWin.parent;
|
||||
}
|
||||
|
||||
return rect;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns iframe content offset (iframe border + padding).
|
||||
* Note: this function shouldn't need to exist, had the platform provided a
|
||||
@ -135,6 +180,11 @@ LayoutHelpers = {
|
||||
getIframeContentOffset: function LH_getIframeContentOffset(aIframe) {
|
||||
let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
|
||||
|
||||
// In some cases, the computed style is null
|
||||
if (!style) {
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
let paddingTop = parseInt(style.getPropertyValue("padding-top"));
|
||||
let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
|
||||
|
||||
|
@ -36,8 +36,6 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
***** END LICENSE BLOCK *****/
|
||||
|
||||
/*global Components, Services, TiltGL, TiltUtils, TiltVisualizer */
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
@ -45,17 +43,37 @@ const Cu = Components.utils;
|
||||
// Tilt notifications dispatched through the nsIObserverService.
|
||||
const TILT_NOTIFICATIONS = {
|
||||
|
||||
// Fires when Tilt completes the initialization.
|
||||
// Fires when Tilt starts the initialization.
|
||||
INITIALIZING: "tilt-initializing",
|
||||
|
||||
// Fires immediately after initialization is complete.
|
||||
// (when the canvas overlay is visible and the 3D mesh is completely created)
|
||||
INITIALIZED: "tilt-initialized",
|
||||
|
||||
// Fires when Tilt is destroyed.
|
||||
// Fires immediately before the destruction is started.
|
||||
DESTROYING: "tilt-destroying",
|
||||
|
||||
// Fires immediately before the destruction is finished.
|
||||
// (just before the canvas overlay is removed from its parent node)
|
||||
BEFORE_DESTROYED: "tilt-before-destroyed",
|
||||
|
||||
// Fires when Tilt is completely destroyed.
|
||||
DESTROYED: "tilt-destroyed",
|
||||
|
||||
// Fires when Tilt is shown (after a tab-switch).
|
||||
SHOWN: "tilt-shown",
|
||||
|
||||
// Fires when Tilt is hidden (after a tab-switch).
|
||||
HIDDEN: "tilt-hidden"
|
||||
HIDDEN: "tilt-hidden",
|
||||
|
||||
// Fires once Tilt highlights an element in the page.
|
||||
HIGHLIGHTING: "tilt-highlighting",
|
||||
|
||||
// Fires once Tilt stops highlighting any element.
|
||||
UNHIGHLIGHTING: "tilt-unhighlighting",
|
||||
|
||||
// Fires when a node is removed from the 3D mesh.
|
||||
NODE_REMOVED: "tilt-node-removed"
|
||||
};
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
@ -106,10 +124,11 @@ Tilt.prototype = {
|
||||
|
||||
// create a visualizer instance for the current tab
|
||||
this.visualizers[id] = new TiltVisualizer({
|
||||
parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode,
|
||||
chromeWindow: this.chromeWindow,
|
||||
contentWindow: this.chromeWindow.gBrowser.selectedBrowser.contentWindow,
|
||||
parentNode: this.chromeWindow.gBrowser.selectedBrowser.parentNode,
|
||||
requestAnimationFrame: this.chromeWindow.mozRequestAnimationFrame,
|
||||
inspectorUI: this.chromeWindow.InspectorUI
|
||||
notifications: this.NOTIFICATIONS
|
||||
});
|
||||
|
||||
// make sure the visualizer object was initialized properly
|
||||
@ -118,7 +137,7 @@ Tilt.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.INITIALIZED, null);
|
||||
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.INITIALIZING, null);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -157,11 +176,12 @@ Tilt.prototype = {
|
||||
let controller = this.visualizers[aId].controller;
|
||||
let presenter = this.visualizers[aId].presenter;
|
||||
|
||||
TiltUtils.setDocumentZoom(presenter.transforms.zoom);
|
||||
|
||||
let content = presenter.contentWindow;
|
||||
let pageXOffset = content.pageXOffset * TiltUtils.getDocumentZoom();
|
||||
let pageYOffset = content.pageYOffset * TiltUtils.getDocumentZoom();
|
||||
let pageXOffset = content.pageXOffset * presenter.transforms.zoom;
|
||||
let pageYOffset = content.pageYOffset * presenter.transforms.zoom;
|
||||
|
||||
Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.DESTROYING, null);
|
||||
TiltUtils.setDocumentZoom(this.chromeWindow, presenter.transforms.zoom);
|
||||
|
||||
controller.removeEventListeners();
|
||||
controller.arcball.reset([-pageXOffset, -pageYOffset]);
|
||||
@ -171,9 +191,9 @@ Tilt.prototype = {
|
||||
|
||||
/**
|
||||
* Handles any supplementary post-initialization work, done immediately
|
||||
* after a TILT_NOTIFICATIONS.INITIALIZED notification.
|
||||
* after a TILT_NOTIFICATIONS.INITIALIZING notification.
|
||||
*/
|
||||
_whenInitialized: function T__whenInitialized()
|
||||
_whenInitializing: function T__whenInitializing()
|
||||
{
|
||||
this._whenShown();
|
||||
},
|
||||
@ -250,7 +270,7 @@ Tilt.prototype = {
|
||||
|
||||
// add the necessary observers to handle specific notifications
|
||||
Services.obs.addObserver(
|
||||
this._whenInitialized.bind(this), TILT_NOTIFICATIONS.INITIALIZED, false);
|
||||
this._whenInitializing.bind(this), TILT_NOTIFICATIONS.INITIALIZING, false);
|
||||
Services.obs.addObserver(
|
||||
this._whenDestroyed.bind(this), TILT_NOTIFICATIONS.DESTROYED, false);
|
||||
Services.obs.addObserver(
|
||||
@ -284,7 +304,7 @@ Tilt.prototype = {
|
||||
Services.obs.addObserver(onClosed,
|
||||
this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
|
||||
Services.obs.addObserver(onOpened,
|
||||
TILT_NOTIFICATIONS.INITIALIZED, false);
|
||||
TILT_NOTIFICATIONS.INITIALIZING, false);
|
||||
Services.obs.addObserver(onClosed,
|
||||
TILT_NOTIFICATIONS.DESTROYED, false);
|
||||
|
||||
|
@ -36,8 +36,6 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
***** END LICENSE BLOCK *****/
|
||||
|
||||
/*global Components, Services, TiltMath, TiltUtils, mat4 */
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
|
@ -36,8 +36,6 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
***** END LICENSE BLOCK *****/
|
||||
|
||||
/*global Components, TiltUtils */
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
@ -36,8 +36,6 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
***** END LICENSE BLOCK *****/
|
||||
|
||||
/*global Components, Services, XPCOMUtils */
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
@ -46,6 +44,7 @@ const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
|
||||
let EXPORTED_SYMBOLS = ["TiltUtils"];
|
||||
|
||||
@ -402,108 +401,6 @@ TiltUtils.DOM = {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the absolute x, y, width and height coordinates of a node, or null
|
||||
* if the passed node is not an ELEMENT_NODE.
|
||||
*
|
||||
* @param {Element} aNode
|
||||
* the node which coordinates need to be calculated
|
||||
* @param {Window} aContentWindow
|
||||
* optional, the window content holding the the document
|
||||
*
|
||||
* @return {Object} an object containing the top, left, width, height coords
|
||||
*/
|
||||
getNodeCoordinates: function TUD_getNodeCoordinates(aNode, aContentWindow) {
|
||||
// make sure the contentWindow parameter is a valid object
|
||||
aContentWindow = aContentWindow || {};
|
||||
|
||||
if (aNode.nodeType !== 1) { // Node.ELEMENT_NODE
|
||||
return null;
|
||||
}
|
||||
|
||||
let rect = {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
|
||||
// the preferred way of getting the bounding client rectangle
|
||||
let clientRect = aNode.getBoundingClientRect();
|
||||
rect.top = clientRect.top + aContentWindow.pageYOffset;
|
||||
rect.left = clientRect.left + aContentWindow.pageXOffset;
|
||||
rect.width = clientRect.width;
|
||||
rect.height = clientRect.height;
|
||||
|
||||
// compute the iframe position and its offset if necessary
|
||||
let frameRect = this.getFrameOffset(
|
||||
aNode.ownerDocument.defaultView.frameElement, aContentWindow);
|
||||
|
||||
if (frameRect) {
|
||||
rect.top += frameRect.top;
|
||||
rect.left += frameRect.left;
|
||||
}
|
||||
|
||||
return rect;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retuns the parent iframe position and its offset (borders and padding),
|
||||
* or null if the passed frame is not valid.
|
||||
*
|
||||
* @param {Element} aNode
|
||||
* the iframe which offset need to be calculated
|
||||
* @param {Window} aContentWindow
|
||||
* optional, the window content holding the the document
|
||||
*
|
||||
* @return {Object} an object containing the top and left coords
|
||||
*/
|
||||
getFrameOffset: (function() {
|
||||
let cache = {};
|
||||
|
||||
return function TUD_getFrameOffset(aFrame, aContentWindow) {
|
||||
// make sure the contentWindow parameter is a valid object
|
||||
aContentWindow = aContentWindow || {};
|
||||
|
||||
if (!aFrame) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let id = TiltUtils.getWindowId(aFrame.contentWindow) + "," +
|
||||
aContentWindow.pageXOffset || 0 + "," +
|
||||
aContentWindow.pageYOffset || 0;
|
||||
|
||||
// check the cache to see if this iframe offset wasn't calculated already
|
||||
if (cache[id] !== undefined) {
|
||||
return cache[id];
|
||||
}
|
||||
|
||||
let offset = {
|
||||
top: 0,
|
||||
left: 0
|
||||
};
|
||||
|
||||
// take the parent iframe bounding rect position into account
|
||||
let frameRect = aFrame.getBoundingClientRect();
|
||||
offset.top = frameRect.top;
|
||||
offset.left = frameRect.left;
|
||||
|
||||
// compute the iframe content offset (iframe border + padding)
|
||||
// bug #626359
|
||||
let style = aFrame.contentWindow.getComputedStyle(aFrame, null);
|
||||
if (style) {
|
||||
offset.top +=
|
||||
parseInt(style.getPropertyValue("padding-top")) +
|
||||
parseInt(style.getPropertyValue("border-top-width"));
|
||||
offset.left +=
|
||||
parseInt(style.getPropertyValue("padding-left")) +
|
||||
parseInt(style.getPropertyValue("border-left-width"));
|
||||
}
|
||||
|
||||
return (cache[id] = offset);
|
||||
};
|
||||
}()),
|
||||
|
||||
/**
|
||||
* Traverses a document object model & calculates useful info for each node.
|
||||
*
|
||||
@ -549,7 +446,7 @@ TiltUtils.DOM = {
|
||||
}
|
||||
|
||||
// get the x, y, width and height coordinates of the node
|
||||
let coord = this.getNodeCoordinates(node, aContentWindow);
|
||||
let coord = LayoutHelpers.getRect(node, aContentWindow);
|
||||
if (!coord) {
|
||||
continue;
|
||||
}
|
||||
@ -631,18 +528,6 @@ TiltUtils.destroyObject = function TU_destroyObject(aScope)
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the most recent browser window.
|
||||
*
|
||||
* @return {Window} the window
|
||||
*/
|
||||
TiltUtils.getBrowserWindow = function TU_getBrowserWindow()
|
||||
{
|
||||
return Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator)
|
||||
.getMostRecentWindow("navigator:browser");
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the unique ID of a window object.
|
||||
*
|
||||
@ -665,32 +550,38 @@ TiltUtils.getWindowId = function TU_getWindowId(aWindow)
|
||||
/**
|
||||
* Gets the markup document viewer zoom for the currently selected browser.
|
||||
*
|
||||
* @param {Window} aChromeWindow
|
||||
* the top-level browser window
|
||||
*
|
||||
* @return {Number} the zoom ammount
|
||||
*/
|
||||
TiltUtils.getDocumentZoom = function TU_getDocumentZoom() {
|
||||
return TiltUtils.getBrowserWindow()
|
||||
.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
|
||||
TiltUtils.getDocumentZoom = function TU_getDocumentZoom(aChromeWindow) {
|
||||
return aChromeWindow.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the markup document viewer zoom for the currently selected browser.
|
||||
*
|
||||
* @param {Window} aChromeWindow
|
||||
* the top-level browser window
|
||||
*
|
||||
* @param {Number} the zoom ammount
|
||||
*/
|
||||
TiltUtils.setDocumentZoom = function TU_getDocumentZoom(aZoom) {
|
||||
TiltUtils.getBrowserWindow()
|
||||
.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = aZoom;
|
||||
TiltUtils.setDocumentZoom = function TU_setDocumentZoom(aChromeWindow, aZoom) {
|
||||
aChromeWindow.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = aZoom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs a garbage collection.
|
||||
*
|
||||
* @param {Window} aChromeWindow
|
||||
* the top-level browser window
|
||||
*/
|
||||
TiltUtils.gc = function TU_gc()
|
||||
TiltUtils.gc = function TU_gc(aChromeWindow)
|
||||
{
|
||||
TiltUtils.getBrowserWindow()
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
aChromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -36,9 +36,6 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
***** END LICENSE BLOCK *****/
|
||||
|
||||
/*global Components, ChromeWorker */
|
||||
/*global TiltGL, TiltMath, EPSILON, vec3, mat4, quat4, TiltUtils */
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
@ -78,6 +75,7 @@ const ARCBALL_RESET_INTERVAL = 1000 / 60;
|
||||
const TILT_CRAFTER = "resource:///modules/devtools/TiltWorkerCrafter.js";
|
||||
const TILT_PICKER = "resource:///modules/devtools/TiltWorkerPicker.js";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/devtools/TiltGL.jsm");
|
||||
Cu.import("resource:///modules/devtools/TiltMath.jsm");
|
||||
Cu.import("resource:///modules/devtools/TiltUtils.jsm");
|
||||
@ -90,10 +88,11 @@ let EXPORTED_SYMBOLS = ["TiltVisualizer"];
|
||||
*
|
||||
* @param {Object} aProperties
|
||||
* an object containing the following properties:
|
||||
* {Element} parentNode: the parent node to hold the visualization
|
||||
* {Window} chromeWindow: a reference to the top level window
|
||||
* {Window} contentWindow: the content window holding the visualized doc
|
||||
* {Element} parentNode: the parent node to hold the visualization
|
||||
* {Function} requestAnimationFrame: responsible with scheduling loops
|
||||
* {InspectorUI} inspectorUI: necessary instance of the InspectorUI
|
||||
* {Object} notifications: necessary notifications for Tilt
|
||||
* {Function} onError: optional, function called if initialization failed
|
||||
* {Function} onLoad: optional, function called if initialization worked
|
||||
*/
|
||||
@ -102,6 +101,11 @@ function TiltVisualizer(aProperties)
|
||||
// make sure the properties parameter is a valid object
|
||||
aProperties = aProperties || {};
|
||||
|
||||
/**
|
||||
* Save a reference to the top-level window.
|
||||
*/
|
||||
this.chromeWindow = aProperties.chromeWindow;
|
||||
|
||||
/**
|
||||
* The canvas element used for rendering the visualization.
|
||||
*/
|
||||
@ -114,9 +118,10 @@ function TiltVisualizer(aProperties)
|
||||
* Visualization logic and drawing loop.
|
||||
*/
|
||||
this.presenter = new TiltVisualizer.Presenter(this.canvas,
|
||||
aProperties.chromeWindow,
|
||||
aProperties.contentWindow,
|
||||
aProperties.requestAnimationFrame,
|
||||
aProperties.inspectorUI,
|
||||
aProperties.notifications,
|
||||
aProperties.onError || null,
|
||||
aProperties.onLoad || null);
|
||||
|
||||
@ -160,9 +165,12 @@ TiltVisualizer.prototype = {
|
||||
if (this.presenter) {
|
||||
TiltUtils.destroyObject(this.presenter);
|
||||
}
|
||||
|
||||
let chromeWindow = this.chromeWindow;
|
||||
|
||||
TiltUtils.destroyObject(this);
|
||||
TiltUtils.clearCache();
|
||||
TiltUtils.gc();
|
||||
TiltUtils.gc(chromeWindow);
|
||||
}
|
||||
};
|
||||
|
||||
@ -171,25 +179,42 @@ TiltVisualizer.prototype = {
|
||||
*
|
||||
* @param {HTMLCanvasElement} aCanvas
|
||||
* the canvas element used for rendering
|
||||
* @param {Object} aContentWindow
|
||||
* @param {Window} aChromeWindow
|
||||
* a reference to the top-level window
|
||||
* @param {Window} aContentWindow
|
||||
* the content window holding the document to be visualized
|
||||
* @param {Function} aRequestAnimationFrame
|
||||
* function responsible with scheduling loop frames
|
||||
* @param {InspectorUI} aInspectorUI
|
||||
* necessary instance of the InspectorUI
|
||||
* @param {Object} aNotifications
|
||||
* necessary notifications for Tilt
|
||||
* @param {Function} onError
|
||||
* function called if initialization failed
|
||||
* @param {Function} onLoad
|
||||
* function called if initialization worked
|
||||
*/
|
||||
TiltVisualizer.Presenter = function TV_Presenter(
|
||||
aCanvas, aContentWindow, aRequestAnimationFrame, aInspectorUI,
|
||||
aCanvas, aChromeWindow, aContentWindow, aRequestAnimationFrame, aNotifications,
|
||||
onError, onLoad)
|
||||
{
|
||||
/**
|
||||
* A canvas overlay used for drawing the visualization.
|
||||
*/
|
||||
this.canvas = aCanvas;
|
||||
|
||||
/**
|
||||
* Save a reference to the top-level window, to access InspectorUI or Tilt.
|
||||
*/
|
||||
this.chromeWindow = aChromeWindow;
|
||||
|
||||
/**
|
||||
* The content window generating the visualization
|
||||
*/
|
||||
this.contentWindow = aContentWindow;
|
||||
this.inspectorUI = aInspectorUI;
|
||||
this.tiltUI = aInspectorUI.chromeWin.Tilt;
|
||||
|
||||
/**
|
||||
* Shortcut for accessing notifications strings.
|
||||
*/
|
||||
this.NOTIFICATIONS = aNotifications;
|
||||
|
||||
/**
|
||||
* Create the renderer, containing useful functions for easy drawing.
|
||||
@ -225,7 +250,7 @@ TiltVisualizer.Presenter = function TV_Presenter(
|
||||
* Modified by events in the controller through delegate functions.
|
||||
*/
|
||||
this.transforms = {
|
||||
zoom: TiltUtils.getDocumentZoom(),
|
||||
zoom: TiltUtils.getDocumentZoom(aChromeWindow),
|
||||
offset: vec3.create(), // mesh offset, aligned to the viewport center
|
||||
translation: vec3.create(), // scene translation, on the [x, y, z] axis
|
||||
rotation: quat4.create() // scene rotation, expressed as a quaternion
|
||||
@ -234,8 +259,9 @@ TiltVisualizer.Presenter = function TV_Presenter(
|
||||
/**
|
||||
* Variables holding information about the initial and current node selected.
|
||||
*/
|
||||
this._initialSelection = false; // true if an initial selection was made
|
||||
this._currentSelection = -1; // the selected node index
|
||||
this._initialSelection = false; // true if an initial selection was made
|
||||
this._initialMeshConfiguration = false; // true if the 3D mesh was configured
|
||||
|
||||
/**
|
||||
* Variable specifying if the scene should be redrawn.
|
||||
@ -295,28 +321,12 @@ TiltVisualizer.Presenter = function TV_Presenter(
|
||||
this.drawVisualization();
|
||||
}
|
||||
|
||||
// call the attached ondraw event handler if specified (by the controller)
|
||||
// call the attached ondraw function and handle all keyframe notifications
|
||||
if ("function" === typeof this.ondraw) {
|
||||
this.ondraw(this.frames);
|
||||
}
|
||||
|
||||
if (!TiltVisualizer.Prefs.introTransition && !this.isExecutingDestruction) {
|
||||
this.frames = INTRO_TRANSITION_DURATION;
|
||||
}
|
||||
if (!TiltVisualizer.Prefs.outroTransition && this.isExecutingDestruction) {
|
||||
this.frames = OUTRO_TRANSITION_DURATION;
|
||||
}
|
||||
|
||||
if ("function" === typeof this.onInitializationFinished &&
|
||||
this.frames === INTRO_TRANSITION_DURATION &&
|
||||
!this.isExecutingDestruction) {
|
||||
this.onInitializationFinished();
|
||||
}
|
||||
if ("function" === typeof this.onDestructionFinished &&
|
||||
this.frames === OUTRO_TRANSITION_DURATION &&
|
||||
this.isExecutingDestruction) {
|
||||
this.onDestructionFinished();
|
||||
}
|
||||
this.handleKeyframeNotifications();
|
||||
}.bind(this);
|
||||
|
||||
setup();
|
||||
@ -495,7 +505,7 @@ TiltVisualizer.Presenter.prototype = {
|
||||
* @param {Object} aData
|
||||
* object containing the necessary mesh verts, texcoord etc.
|
||||
*/
|
||||
setupMesh: function TVP_setupMesh(aData) /*global TiltVisualizerStyle */
|
||||
setupMesh: function TVP_setupMesh(aData)
|
||||
{
|
||||
let renderer = this.renderer;
|
||||
|
||||
@ -530,13 +540,13 @@ TiltVisualizer.Presenter.prototype = {
|
||||
// if there's no initial selection made, highlight the required node
|
||||
if (!this._initialSelection) {
|
||||
this._initialSelection = true;
|
||||
this.highlightNode(this.inspectorUI.selection);
|
||||
this.highlightNode(this.chromeWindow.InspectorUI.selection);
|
||||
}
|
||||
|
||||
if (!this._initialMeshConfiguration) {
|
||||
this._initialMeshConfiguration = true;
|
||||
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let zoom = this.transforms.zoom;
|
||||
let width = Math.min(aData.meshWidth * zoom, renderer.width);
|
||||
let height = Math.min(aData.meshHeight * zoom, renderer.height);
|
||||
|
||||
@ -610,7 +620,7 @@ TiltVisualizer.Presenter.prototype = {
|
||||
*/
|
||||
onResize: function TVP_onResize(e)
|
||||
{
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let zoom = TiltUtils.getDocumentZoom(this.chromeWindow);
|
||||
let width = e.target.innerWidth * zoom;
|
||||
let height = e.target.innerHeight * zoom;
|
||||
|
||||
@ -629,10 +639,6 @@ TiltVisualizer.Presenter.prototype = {
|
||||
*/
|
||||
highlightNode: function TVP_highlightNode(aNode)
|
||||
{
|
||||
if (!aNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.highlightNodeFor(this.traverseData.nodes.indexOf(aNode));
|
||||
},
|
||||
|
||||
@ -701,10 +707,13 @@ TiltVisualizer.Presenter.prototype = {
|
||||
if (this._currentSelection === aNodeIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if an invalid or nonexisted node is specified, disable the highlight
|
||||
if (aNodeIndex < 0) {
|
||||
this._currentSelection = -1;
|
||||
this.highlight.disabled = true;
|
||||
|
||||
Services.obs.notifyObservers(null, this.NOTIFICATIONS.UNHIGHLIGHTING, null);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -730,8 +739,12 @@ TiltVisualizer.Presenter.prototype = {
|
||||
vec3.set([x, y + h, z * STACK_THICKNESS], highlight.v3);
|
||||
|
||||
this._currentSelection = aNodeIndex;
|
||||
this.inspectorUI.inspectNode(node, this.contentWindow.innerHeight < y ||
|
||||
this.contentWindow.pageYOffset > 0);
|
||||
|
||||
this.chromeWindow.InspectorUI.inspectNode(node,
|
||||
this.contentWindow.innerHeight < y ||
|
||||
this.contentWindow.pageYOffset > 0);
|
||||
|
||||
Services.obs.notifyObservers(null, this.NOTIFICATIONS.HIGHLIGHTING, null);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -758,6 +771,9 @@ TiltVisualizer.Presenter.prototype = {
|
||||
this.meshStacks.vertices = new renderer.VertexBuffer(meshData.vertices, 3);
|
||||
this.highlight.disabled = true;
|
||||
this.redraw = true;
|
||||
|
||||
Services.obs.notifyObservers(null,
|
||||
this.NOTIFICATIONS.NODE_REMOVED, null);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -797,7 +813,7 @@ TiltVisualizer.Presenter.prototype = {
|
||||
}
|
||||
}, false);
|
||||
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let zoom = TiltUtils.getDocumentZoom(this.chromeWindow);
|
||||
let width = this.renderer.width * zoom;
|
||||
let height = this.renderer.height * zoom;
|
||||
let mesh = this.meshStacks;
|
||||
@ -867,17 +883,40 @@ TiltVisualizer.Presenter.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if this object was initialized properly.
|
||||
*
|
||||
* @return {Boolean} true if the object was initialized properly
|
||||
* Handles notifications at specific frame counts.
|
||||
*/
|
||||
isInitialized: function TVP_isInitialized()
|
||||
handleKeyframeNotifications: function TV_handleKeyframeNotifications()
|
||||
{
|
||||
return this.renderer && this.renderer.context;
|
||||
if (!TiltVisualizer.Prefs.introTransition && !this.isExecutingDestruction) {
|
||||
this.frames = INTRO_TRANSITION_DURATION;
|
||||
}
|
||||
if (!TiltVisualizer.Prefs.outroTransition && this.isExecutingDestruction) {
|
||||
this.frames = OUTRO_TRANSITION_DURATION;
|
||||
}
|
||||
|
||||
if (this.frames === INTRO_TRANSITION_DURATION &&
|
||||
!this.isExecutingDestruction) {
|
||||
|
||||
Services.obs.notifyObservers(null, this.NOTIFICATIONS.INITIALIZED, null);
|
||||
|
||||
if ("function" === typeof this.onInitializationFinished) {
|
||||
this.onInitializationFinished();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.frames === OUTRO_TRANSITION_DURATION &&
|
||||
this.isExecutingDestruction) {
|
||||
|
||||
Services.obs.notifyObservers(null, this.NOTIFICATIONS.BEFORE_DESTROYED, null);
|
||||
|
||||
if ("function" === typeof this.onDestructionFinished) {
|
||||
this.onDestructionFinished();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts executing a destruction animation and executes a callback function
|
||||
* Starts executing the destruction sequence and issues a callback function
|
||||
* when finished.
|
||||
*
|
||||
* @param {Function} aCallback
|
||||
@ -889,6 +928,10 @@ TiltVisualizer.Presenter.prototype = {
|
||||
this.isExecutingDestruction = true;
|
||||
this.onDestructionFinished = aCallback;
|
||||
|
||||
// if we execute the destruction after the initialization finishes,
|
||||
// proceed normally; otherwise, skip everything and immediately issue
|
||||
// the callback
|
||||
|
||||
if (this.frames > OUTRO_TRANSITION_DURATION) {
|
||||
this.frames = 0;
|
||||
this.redraw = true;
|
||||
@ -898,6 +941,16 @@ TiltVisualizer.Presenter.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if this object was initialized properly.
|
||||
*
|
||||
* @return {Boolean} true if the object was initialized properly
|
||||
*/
|
||||
isInitialized: function TVP_isInitialized()
|
||||
{
|
||||
return this.renderer && this.renderer.context;
|
||||
},
|
||||
|
||||
/**
|
||||
* Function called when this object is destroyed.
|
||||
*/
|
||||
@ -935,26 +988,34 @@ TiltVisualizer.Presenter.prototype = {
|
||||
*/
|
||||
TiltVisualizer.Controller = function TV_Controller(aCanvas, aPresenter)
|
||||
{
|
||||
/**
|
||||
* A canvas overlay on which mouse and keyboard event listeners are attached.
|
||||
*/
|
||||
this.canvas = aCanvas;
|
||||
|
||||
/**
|
||||
* Save a reference to the presenter to modify its model-view transforms.
|
||||
*/
|
||||
this.presenter = aPresenter;
|
||||
|
||||
/**
|
||||
* The initial controller dimensions and offset, in pixels.
|
||||
*/
|
||||
this.left = aPresenter.contentWindow.pageXOffset || 0;
|
||||
this.top = aPresenter.contentWindow.pageYOffset || 0;
|
||||
this.zoom = aPresenter.transforms.zoom;
|
||||
this.left = (aPresenter.contentWindow.pageXOffset || 0) * this.zoom;
|
||||
this.top = (aPresenter.contentWindow.pageYOffset || 0) * this.zoom;
|
||||
this.width = aCanvas.width;
|
||||
this.height = aCanvas.height;
|
||||
|
||||
this.left *= TiltUtils.getDocumentZoom();
|
||||
this.top *= TiltUtils.getDocumentZoom();
|
||||
|
||||
/**
|
||||
* Arcball used to control the visualization using the mouse.
|
||||
*/
|
||||
this.arcball = new TiltVisualizer.Arcball(this.width, this.height, 0,
|
||||
[this.width + this.left < aPresenter.maxTextureSize ? -this.left : 0,
|
||||
this.height + this.top < aPresenter.maxTextureSize ? -this.top : 0]);
|
||||
this.arcball = new TiltVisualizer.Arcball(
|
||||
this.presenter.chromeWindow, this.width, this.height, 0,
|
||||
[
|
||||
this.width + this.left < aPresenter.maxTextureSize ? -this.left : 0,
|
||||
this.height + this.top < aPresenter.maxTextureSize ? -this.top : 0
|
||||
]);
|
||||
|
||||
/**
|
||||
* Object containing the rotation quaternion and the translation amount.
|
||||
@ -975,7 +1036,7 @@ TiltVisualizer.Controller = function TV_Controller(aCanvas, aPresenter)
|
||||
TiltVisualizer.Controller.prototype = {
|
||||
|
||||
/**
|
||||
* Adds all added events listeners required by this controller.
|
||||
* Adds events listeners required by this controller.
|
||||
*/
|
||||
addEventListeners: function TVC_addEventListeners()
|
||||
{
|
||||
@ -1156,9 +1217,10 @@ TiltVisualizer.Controller.prototype = {
|
||||
onKeyUp: function TVC_onKeyUp(e)
|
||||
{
|
||||
let code = e.keyCode || e.which;
|
||||
let tilt = this.presenter.chromeWindow.Tilt;
|
||||
|
||||
if (code === e.DOM_VK_ESCAPE) {
|
||||
this.presenter.tiltUI.destroy(this.presenter.tiltUI.currentWindowId, 1);
|
||||
tilt.destroy(tilt.currentWindowId, true);
|
||||
return;
|
||||
}
|
||||
if (code === e.DOM_VK_X) {
|
||||
@ -1184,7 +1246,7 @@ TiltVisualizer.Controller.prototype = {
|
||||
*/
|
||||
onResize: function TVC_onResize(e)
|
||||
{
|
||||
let zoom = TiltUtils.getDocumentZoom();
|
||||
let zoom = TiltUtils.getDocumentZoom(this.presenter.chromeWindow);
|
||||
let width = e.target.innerWidth * zoom;
|
||||
let height = e.target.innerHeight * zoom;
|
||||
|
||||
@ -1219,6 +1281,8 @@ TiltVisualizer.Controller.prototype = {
|
||||
* in the Graphics Interface ’92 Proceedings. It features good behavior
|
||||
* easy implementation, cheap execution.
|
||||
*
|
||||
* @param {Window} aChromeWindow
|
||||
* a reference to the top-level window
|
||||
* @param {Number} aWidth
|
||||
* the width of canvas
|
||||
* @param {Number} aHeight
|
||||
@ -1231,8 +1295,13 @@ TiltVisualizer.Controller.prototype = {
|
||||
* optional, initial quaternion rotation
|
||||
*/
|
||||
TiltVisualizer.Arcball = function TV_Arcball(
|
||||
aWidth, aHeight, aRadius, aInitialTrans, aInitialRot)
|
||||
aChromeWindow, aWidth, aHeight, aRadius, aInitialTrans, aInitialRot)
|
||||
{
|
||||
/**
|
||||
* Save a reference to the top-level window to set/remove intervals.
|
||||
*/
|
||||
this.chromeWindow = aChromeWindow;
|
||||
|
||||
/**
|
||||
* Values retaining the current horizontal and vertical mouse coordinates.
|
||||
*/
|
||||
@ -1688,17 +1757,17 @@ TiltVisualizer.Arcball.prototype = {
|
||||
this.onResetStart = null;
|
||||
}
|
||||
|
||||
let func = this._nextResetIntervalStep.bind(this);
|
||||
|
||||
this.cancelMouseEvents();
|
||||
this.cancelKeyEvents();
|
||||
this._cancelResetInterval();
|
||||
|
||||
let window = TiltUtils.getBrowserWindow();
|
||||
let func = this._nextResetIntervalStep.bind(this);
|
||||
|
||||
this._save();
|
||||
this._resetFinalTranslation = vec3.create(aFinalTranslation);
|
||||
this._resetFinalRotation = quat4.create(aFinalRotation);
|
||||
this._resetInterval = window.setInterval(func, ARCBALL_RESET_INTERVAL);
|
||||
this._resetInterval =
|
||||
this.chromeWindow.setInterval(func, ARCBALL_RESET_INTERVAL);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1707,9 +1776,8 @@ TiltVisualizer.Arcball.prototype = {
|
||||
_cancelResetInterval: function TVA__cancelResetInterval()
|
||||
{
|
||||
if (this._resetInterval) {
|
||||
let window = TiltUtils.getBrowserWindow();
|
||||
this.chromeWindow.clearInterval(this._resetInterval);
|
||||
|
||||
window.clearInterval(this._resetInterval);
|
||||
this._resetInterval = null;
|
||||
this._save();
|
||||
|
||||
@ -1758,8 +1826,8 @@ TiltVisualizer.Arcball.prototype = {
|
||||
/**
|
||||
* Loads the keys to control this arcball.
|
||||
*/
|
||||
_loadKeys: function TVA__loadKeys() {
|
||||
|
||||
_loadKeys: function TVA__loadKeys()
|
||||
{
|
||||
this.rotateKeys = {
|
||||
"up": Ci.nsIDOMKeyEvent["DOM_VK_W"],
|
||||
"down": Ci.nsIDOMKeyEvent["DOM_VK_S"],
|
||||
|
@ -36,8 +36,6 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
***** END LICENSE BLOCK *****/
|
||||
|
||||
/*global Components, TiltMath */
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
@ -36,8 +36,6 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
***** END LICENSE BLOCK *****/
|
||||
|
||||
/*global self*/
|
||||
"use strict";
|
||||
|
||||
const SIXTEEN_OVER_255 = 16 / 255;
|
||||
|
@ -36,8 +36,6 @@
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
***** END LICENSE BLOCK *****/
|
||||
|
||||
/*global self*/
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
|
@ -47,6 +47,7 @@ include $(topsrcdir)/config/rules.mk
|
||||
_BROWSER_TEST_FILES = \
|
||||
head.js \
|
||||
browser_tilt_01_lazy_getter.js \
|
||||
browser_tilt_02_notifications-seq.js \
|
||||
browser_tilt_02_notifications.js \
|
||||
browser_tilt_03_tab_switch.js \
|
||||
browser_tilt_04_initialization-key.js \
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, Tilt */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -0,0 +1,77 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
let tabEvents = "";
|
||||
|
||||
function test() {
|
||||
if (!isTiltEnabled()) {
|
||||
info("Skipping notifications test because Tilt isn't enabled.");
|
||||
return;
|
||||
}
|
||||
if (!isWebGLSupported()) {
|
||||
info("Skipping notifications test because WebGL isn't supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
requestLongerTimeout(10);
|
||||
waitForExplicitFinish();
|
||||
|
||||
createTab(function() {
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
|
||||
Services.obs.addObserver(obs_INITIALIZING, INITIALIZING, false);
|
||||
Services.obs.addObserver(obs_INITIALIZED, INITIALIZED, false);
|
||||
Services.obs.addObserver(obs_DESTROYING, DESTROYING, false);
|
||||
Services.obs.addObserver(obs_BEFORE_DESTROYED, BEFORE_DESTROYED, false);
|
||||
Services.obs.addObserver(obs_DESTROYED, DESTROYED, false);
|
||||
|
||||
info("Starting up the Tilt notifications test.");
|
||||
createTilt({});
|
||||
});
|
||||
}
|
||||
|
||||
function obs_INITIALIZING() {
|
||||
info("Handling the INITIALIZING notification.");
|
||||
tabEvents += "INITIALIZING;";
|
||||
}
|
||||
|
||||
function obs_INITIALIZED() {
|
||||
info("Handling the INITIALIZED notification.");
|
||||
tabEvents += "INITIALIZED;";
|
||||
|
||||
Tilt.destroy(Tilt.currentWindowId, true);
|
||||
}
|
||||
|
||||
function obs_DESTROYING() {
|
||||
info("Handling the DESTROYING( notification.");
|
||||
tabEvents += "DESTROYING;";
|
||||
}
|
||||
|
||||
function obs_BEFORE_DESTROYED() {
|
||||
info("Handling the BEFORE_DESTROYED notification.");
|
||||
tabEvents += "BEFORE_DESTROYED;";
|
||||
}
|
||||
|
||||
function obs_DESTROYED() {
|
||||
info("Handling the DESTROYED notification.");
|
||||
tabEvents += "DESTROYED;";
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
info("Cleaning up the notifications test.");
|
||||
|
||||
is(tabEvents, "INITIALIZING;INITIALIZED;DESTROYING;BEFORE_DESTROYED;DESTROYED;",
|
||||
"The notifications weren't fired in the correct order.");
|
||||
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
|
||||
Services.obs.removeObserver(obs_INITIALIZING, INITIALIZING, false);
|
||||
Services.obs.removeObserver(obs_INITIALIZED, INITIALIZED, false);
|
||||
Services.obs.removeObserver(obs_DESTROYING, DESTROYING, false);
|
||||
Services.obs.removeObserver(obs_BEFORE_DESTROYED, BEFORE_DESTROYED, false);
|
||||
Services.obs.removeObserver(obs_DESTROYED, DESTROYED, false);
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, executeSoon, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt, Tilt */
|
||||
/*global Services, TILT_INITIALIZED, TILT_DESTROYED, TILT_SHOWN, TILT_HIDDEN */
|
||||
"use strict";
|
||||
|
||||
let tab0, tab1;
|
||||
@ -20,6 +16,7 @@ function test() {
|
||||
return;
|
||||
}
|
||||
|
||||
requestLongerTimeout(10);
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.tabContainer.addEventListener("TabSelect", tabSelect, false);
|
||||
@ -30,12 +27,14 @@ function createNewTab() {
|
||||
tab0 = gBrowser.selectedTab;
|
||||
|
||||
tab1 = createTab(function() {
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(tab_TILT_INITIALIZED, TILT_INITIALIZED, false);
|
||||
Services.obs.addObserver(tab_TILT_DESTROYED, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(tab_TILT_SHOWN, TILT_SHOWN, false);
|
||||
Services.obs.addObserver(tab_TILT_HIDDEN, TILT_HIDDEN, false);
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
|
||||
Services.obs.addObserver(tab_INITIALIZING, INITIALIZING, false);
|
||||
Services.obs.addObserver(tab_DESTROYING, DESTROYING, false);
|
||||
Services.obs.addObserver(tab_SHOWN, SHOWN, false);
|
||||
Services.obs.addObserver(tab_HIDDEN, HIDDEN, false);
|
||||
|
||||
info("Starting up the Tilt notifications test.");
|
||||
createTilt({
|
||||
onTiltOpen: function()
|
||||
{
|
||||
@ -46,46 +45,56 @@ function createNewTab() {
|
||||
});
|
||||
}
|
||||
|
||||
function tab_TILT_INITIALIZED() {
|
||||
tabEvents += "ti;";
|
||||
function tab_INITIALIZING() {
|
||||
info("Handling the INITIALIZING notification.");
|
||||
tabEvents += "INITIALIZING;";
|
||||
}
|
||||
|
||||
function tab_TILT_DESTROYED() {
|
||||
tabEvents += "td;";
|
||||
function tab_DESTROYING() {
|
||||
info("Handling the DESTROYING notification.");
|
||||
tabEvents += "DESTROYING;";
|
||||
}
|
||||
|
||||
function tab_TILT_SHOWN() {
|
||||
tabEvents += "ts;";
|
||||
function tab_SHOWN() {
|
||||
info("Handling the SHOWN notification.");
|
||||
tabEvents += "SHOWN;";
|
||||
}
|
||||
|
||||
function tab_TILT_HIDDEN() {
|
||||
tabEvents += "th;";
|
||||
function tab_HIDDEN() {
|
||||
info("Handling the HIDDEN notification.");
|
||||
tabEvents += "HIDDEN;";
|
||||
}
|
||||
|
||||
let testSteps = [
|
||||
function step0() {
|
||||
info("Selecting tab0.");
|
||||
gBrowser.selectedTab = tab0;
|
||||
},
|
||||
function step1() {
|
||||
info("Selecting tab1.");
|
||||
gBrowser.selectedTab = tab1;
|
||||
},
|
||||
function step2() {
|
||||
Tilt.destroy(Tilt.currentWindowId);
|
||||
info("Killing it.");
|
||||
Tilt.destroy(Tilt.currentWindowId, true);
|
||||
}
|
||||
];
|
||||
|
||||
function cleanup() {
|
||||
is(tabEvents, "ti;th;ts;td;",
|
||||
info("Cleaning up the notifications test.");
|
||||
|
||||
is(tabEvents, "INITIALIZING;HIDDEN;SHOWN;DESTROYING;",
|
||||
"The notifications weren't fired in the correct order.");
|
||||
|
||||
tab0 = null;
|
||||
tab1 = null;
|
||||
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(tab_TILT_INITIALIZED, TILT_INITIALIZED, false);
|
||||
Services.obs.removeObserver(tab_TILT_DESTROYED, TILT_DESTROYED, false);
|
||||
Services.obs.removeObserver(tab_TILT_SHOWN, TILT_SHOWN, false);
|
||||
Services.obs.removeObserver(tab_TILT_HIDDEN, TILT_HIDDEN, false);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
|
||||
Services.obs.removeObserver(tab_INITIALIZING, INITIALIZING, false);
|
||||
Services.obs.removeObserver(tab_DESTROYING, DESTROYING, false);
|
||||
Services.obs.removeObserver(tab_SHOWN, SHOWN, false);
|
||||
Services.obs.removeObserver(tab_HIDDEN, HIDDEN, false);
|
||||
|
||||
gBrowser.tabContainer.removeEventListener("TabSelect", tabSelect, false);
|
||||
gBrowser.removeCurrentTab();
|
||||
|
@ -1,8 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, executeSoon, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt, Tilt */
|
||||
"use strict";
|
||||
|
||||
let tab0, tab1, tab2;
|
||||
|
@ -1,10 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, executeSoon, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, EventUtils, Tilt, TiltUtils, TiltVisualizer, InspectorUI */
|
||||
/*global Ci, TILT_INITIALIZED, TILT_DESTROYED, INSPECTOR_OPENED */
|
||||
"use strict";
|
||||
|
||||
let id;
|
||||
@ -45,13 +40,13 @@ function onInspectorOpen() {
|
||||
|
||||
info("Pressing the accesskey should open Tilt.");
|
||||
|
||||
Services.obs.addObserver(onTiltOpen, TILT_INITIALIZED, false);
|
||||
Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
|
||||
EventUtils.synthesizeKey(tiltKey, eventType);
|
||||
});
|
||||
}
|
||||
|
||||
function onTiltOpen() {
|
||||
Services.obs.removeObserver(onTiltOpen, TILT_INITIALIZED);
|
||||
Services.obs.removeObserver(onTiltOpen, INITIALIZING);
|
||||
|
||||
executeSoon(function() {
|
||||
ok(Tilt.visualizers[id] instanceof TiltVisualizer,
|
||||
@ -61,7 +56,7 @@ function onTiltOpen() {
|
||||
|
||||
info("Pressing the accesskey again should close Tilt.");
|
||||
|
||||
Services.obs.addObserver(onTiltClose, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(onTiltClose, DESTROYED, false);
|
||||
EventUtils.synthesizeKey(tiltKey, eventType);
|
||||
});
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Tilt, TiltUtils, TiltVisualizer */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, EventUtils, Tilt, TiltUtils, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
@ -22,7 +18,7 @@ function test() {
|
||||
createTilt({
|
||||
onTiltOpen: function()
|
||||
{
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
EventUtils.sendKey("ESCAPE");
|
||||
}
|
||||
});
|
||||
@ -35,7 +31,7 @@ function cleanup() {
|
||||
is(Tilt.visualizers[id], null,
|
||||
"The current instance of the visualizer wasn't destroyed properly.");
|
||||
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, EventUtils, Tilt, TiltUtils, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
@ -22,7 +18,7 @@ function test() {
|
||||
createTilt({
|
||||
onTiltOpen: function()
|
||||
{
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
window.content.location = "about:mozilla";
|
||||
}
|
||||
});
|
||||
@ -35,7 +31,7 @@ function cleanup() {
|
||||
is(Tilt.visualizers[id], null,
|
||||
"The current instance of the visualizer wasn't destroyed properly.");
|
||||
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, Tilt, TiltUtils, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
@ -22,7 +18,7 @@ function test() {
|
||||
createTilt({
|
||||
onTiltOpen: function()
|
||||
{
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
}
|
||||
});
|
||||
@ -35,7 +31,7 @@ function cleanup() {
|
||||
is(Tilt.visualizers[id], null,
|
||||
"The current instance of the visualizer wasn't destroyed properly.");
|
||||
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isApproxVec, waitForExplicitFinish, executeSoon, finish */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, EventUtils, InspectorUI, TiltVisualizer, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
@ -30,7 +26,7 @@ function test() {
|
||||
info("Killing arcball reset test.");
|
||||
|
||||
Services.prefs.setBoolPref("accessibility.typeaheadfind", false);
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
});
|
||||
}
|
||||
@ -47,23 +43,23 @@ function performTest(canvas, arcball, callback) {
|
||||
|
||||
// start translating and rotating sometime at random
|
||||
|
||||
executeSoon(function() {
|
||||
window.setTimeout(function() {
|
||||
info("Synthesizing key down events.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_W", { type: "keydown" });
|
||||
EventUtils.synthesizeKey("VK_LEFT", { type: "keydown" });
|
||||
EventUtils.synthesizeKey("VK_S", { type: "keydown" }); // add a little
|
||||
EventUtils.synthesizeKey("VK_RIGHT", { type: "keydown" }); // diversity
|
||||
|
||||
// wait for some arcball translations and rotations to happen
|
||||
|
||||
executeSoon(function() {
|
||||
window.setTimeout(function() {
|
||||
info("Synthesizing key up events.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_W", { type: "keyup" });
|
||||
EventUtils.synthesizeKey("VK_LEFT", { type: "keyup" });
|
||||
EventUtils.synthesizeKey("VK_S", { type: "keyup" });
|
||||
EventUtils.synthesizeKey("VK_RIGHT", { type: "keyup" });
|
||||
|
||||
// ok, transformations finished, we can now try to reset the model view
|
||||
|
||||
executeSoon(function() {
|
||||
window.setTimeout(function() {
|
||||
info("Synthesizing arcball reset key press.");
|
||||
|
||||
arcball.onResetStart = function() {
|
||||
@ -98,15 +94,16 @@ function performTest(canvas, arcball, callback) {
|
||||
};
|
||||
|
||||
EventUtils.synthesizeKey("VK_R", { type: "keydown" });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}, Math.random() * 1000); // leave enough time for transforms to happen
|
||||
}, Math.random() * 1000);
|
||||
}, Math.random() * 1000);
|
||||
}
|
||||
|
||||
function cleanup() { /*global gBrowser */
|
||||
function cleanup() {
|
||||
info("Cleaning up arcball reset test.");
|
||||
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isApproxVec, waitForExplicitFinish, executeSoon, finish */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, EventUtils, InspectorUI, TiltVisualizer, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
@ -28,7 +24,7 @@ function test() {
|
||||
|
||||
info("Killing arcball reset test.");
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
});
|
||||
}
|
||||
@ -45,7 +41,7 @@ function performTest(canvas, arcball, callback) {
|
||||
|
||||
// start translating and rotating sometime at random
|
||||
|
||||
executeSoon(function() {
|
||||
window.setTimeout(function() {
|
||||
info("Synthesizing key down events.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_W", { type: "keydown" });
|
||||
@ -53,7 +49,7 @@ function performTest(canvas, arcball, callback) {
|
||||
|
||||
// wait for some arcball translations and rotations to happen
|
||||
|
||||
executeSoon(function() {
|
||||
window.setTimeout(function() {
|
||||
info("Synthesizing key up events.");
|
||||
|
||||
EventUtils.synthesizeKey("VK_W", { type: "keyup" });
|
||||
@ -61,7 +57,7 @@ function performTest(canvas, arcball, callback) {
|
||||
|
||||
// ok, transformations finished, we can now try to reset the model view
|
||||
|
||||
executeSoon(function() {
|
||||
window.setTimeout(function() {
|
||||
info("Synthesizing arcball reset key press.");
|
||||
|
||||
arcball.onResetStart = function() {
|
||||
@ -96,15 +92,16 @@ function performTest(canvas, arcball, callback) {
|
||||
};
|
||||
|
||||
EventUtils.synthesizeKey("VK_R", { type: "keydown" });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}, Math.random() * 1000); // leave enough time for transforms to happen
|
||||
}, Math.random() * 1000);
|
||||
}, Math.random() * 1000);
|
||||
}
|
||||
|
||||
function cleanup() { /*global gBrowser */
|
||||
function cleanup() {
|
||||
info("Cleaning up arcball reset test.");
|
||||
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isApprox, isApproxVec, vec3, quat4 */
|
||||
/*global TiltVisualizer */
|
||||
"use strict";
|
||||
|
||||
function cloneUpdate(update) {
|
||||
@ -28,7 +25,7 @@ function isExpectedUpdate(update1, update2) {
|
||||
}
|
||||
|
||||
function test() {
|
||||
let arcball1 = new TiltVisualizer.Arcball(123, 456);
|
||||
let arcball1 = new TiltVisualizer.Arcball(window, 123, 456);
|
||||
|
||||
is(arcball1.width, 123,
|
||||
"The first arcball width wasn't set correctly.");
|
||||
@ -38,7 +35,7 @@ function test() {
|
||||
"The first arcball radius wasn't implicitly set correctly.");
|
||||
|
||||
|
||||
let arcball2 = new TiltVisualizer.Arcball(987, 654);
|
||||
let arcball2 = new TiltVisualizer.Arcball(window, 987, 654);
|
||||
|
||||
is(arcball2.width, 987,
|
||||
"The second arcball width wasn't set correctly.");
|
||||
@ -48,7 +45,7 @@ function test() {
|
||||
"The second arcball radius wasn't implicitly set correctly.");
|
||||
|
||||
|
||||
let arcball3 = new TiltVisualizer.Arcball(512, 512);
|
||||
let arcball3 = new TiltVisualizer.Arcball(window, 512, 512);
|
||||
|
||||
let sphereVec = vec3.create();
|
||||
arcball3.pointToSphere(123, 456, 256, 512, 512, sphereVec);
|
||||
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, executeSoon, gBrowser */
|
||||
/*global isEqualVec, isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, EventUtils, vec3, mat4, quat4 */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,8 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isApproxVec, isWebGLSupported, createCanvas, TiltGL */
|
||||
/*global WebGLRenderingContext, WebGLProgram */
|
||||
"use strict";
|
||||
|
||||
let isWebGLAvailable;
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isApproxVec, isWebGLSupported, createCanvas, TiltGL */
|
||||
"use strict";
|
||||
|
||||
let isWebGLAvailable;
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isApproxVec, isWebGLSupported, createCanvas, TiltGL */
|
||||
"use strict";
|
||||
|
||||
let isWebGLAvailable;
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isApproxVec, isWebGLSupported, createCanvas, TiltGL */
|
||||
"use strict";
|
||||
|
||||
let isWebGLAvailable;
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isWebGLSupported, createCanvas, TiltGL */
|
||||
"use strict";
|
||||
|
||||
let isWebGLAvailable;
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isWebGLSupported, createCanvas, TiltGL */
|
||||
"use strict";
|
||||
|
||||
let isWebGLAvailable;
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isnot, info, isWebGLSupported, createCanvas, TiltGL */
|
||||
"use strict";
|
||||
|
||||
let isWebGLAvailable;
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isnot, info, isWebGLSupported, createCanvas, TiltGL */
|
||||
"use strict";
|
||||
|
||||
let isWebGLAvailable;
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isApprox, isApproxVec, TiltMath */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isApproxVec, vec3, mat4 */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isApproxVec, mat3 */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isApprox, isApproxVec, mat4 */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isApproxVec, mat4 */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isApprox, isApproxVec, quat4 */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isApproxVec, quat4 */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,9 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
@ -35,7 +31,7 @@ function test() {
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After only picking a node, it shouldn't be highlighted.");
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
}
|
||||
});
|
||||
@ -46,7 +42,7 @@ function test() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
let presenter;
|
||||
|
||||
function test() {
|
||||
if (!isTiltEnabled()) {
|
||||
info("Skipping picking delete test because Tilt isn't enabled.");
|
||||
@ -22,12 +20,12 @@ function test() {
|
||||
createTilt({
|
||||
onTiltOpen: function(instance)
|
||||
{
|
||||
let presenter = instance.presenter;
|
||||
let canvas = presenter.canvas;
|
||||
presenter = instance.presenter;
|
||||
Services.obs.addObserver(whenNodeRemoved, NODE_REMOVED, false);
|
||||
|
||||
presenter.onSetupMesh = function() {
|
||||
|
||||
presenter.highlightNodeAt(canvas.width / 2, canvas.height / 2, {
|
||||
presenter.highlightNodeAt(presenter.canvas.width / 2,
|
||||
presenter.canvas.height / 2, {
|
||||
onpick: function()
|
||||
{
|
||||
ok(presenter._currentSelection > 0,
|
||||
@ -36,22 +34,6 @@ function test() {
|
||||
"After highlighting a node, it should be highlighted. D'oh.");
|
||||
|
||||
presenter.deleteNode();
|
||||
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Deleting a node shouldn't change the current selection.");
|
||||
ok(presenter.highlight.disabled,
|
||||
"After deleting a node, it shouldn't be highlighted.");
|
||||
|
||||
let nodeIndex = presenter._currentSelection;
|
||||
let meshData = presenter.meshData;
|
||||
|
||||
for (let i = 0, k = 36 * nodeIndex; i < 36; i++) {
|
||||
is(meshData.vertices[i + k], 0,
|
||||
"The stack vertices weren't degenerated properly.");
|
||||
}
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -60,8 +42,28 @@ function test() {
|
||||
});
|
||||
}
|
||||
|
||||
function whenNodeRemoved() {
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Deleting a node shouldn't change the current selection.");
|
||||
ok(presenter.highlight.disabled,
|
||||
"After deleting a node, it shouldn't be highlighted.");
|
||||
|
||||
let nodeIndex = presenter._currentSelection;
|
||||
let meshData = presenter.meshData;
|
||||
|
||||
for (let i = 0, k = 36 * nodeIndex; i < 36; i++) {
|
||||
is(meshData.vertices[i + k], 0,
|
||||
"The stack vertices weren't degenerated properly.");
|
||||
}
|
||||
|
||||
executeSoon(function() {
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
let presenter;
|
||||
|
||||
function test() {
|
||||
if (!isTiltEnabled()) {
|
||||
info("Skipping highlight test because Tilt isn't enabled.");
|
||||
@ -22,29 +20,48 @@ function test() {
|
||||
createTilt({
|
||||
onTiltOpen: function(instance)
|
||||
{
|
||||
let presenter = instance.presenter;
|
||||
presenter = instance.presenter;
|
||||
Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
|
||||
|
||||
presenter.onSetupMesh = function() {
|
||||
let contentDocument = presenter.contentWindow.document;
|
||||
let body = contentDocument.getElementsByTagName("body")[0];
|
||||
let div = contentDocument.getElementById("first-law");
|
||||
|
||||
presenter.highlightNode(body);
|
||||
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Highlighting a node didn't work properly.");
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After highlighting a node, it should be highlighted. D'oh.");
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
presenter.highlightNode(div);
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function whenHighlighting() {
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Highlighting a node didn't work properly.");
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After highlighting a node, it should be highlighted. D'oh.");
|
||||
|
||||
executeSoon(function() {
|
||||
Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false);
|
||||
presenter.highlightNode(null);
|
||||
});
|
||||
}
|
||||
|
||||
function whenUnhighlighting() {
|
||||
ok(presenter._currentSelection < 0,
|
||||
"Unhighlighting a should remove the current selection.");
|
||||
ok(presenter.highlight.disabled,
|
||||
"After unhighlighting a node, it shouldn't be highlighted anymore. D'oh.");
|
||||
|
||||
executeSoon(function() {
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING);
|
||||
Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
let presenter;
|
||||
|
||||
function test() {
|
||||
if (!isTiltEnabled()) {
|
||||
info("Skipping highlight test because Tilt isn't enabled.");
|
||||
@ -22,31 +20,46 @@ function test() {
|
||||
createTilt({
|
||||
onTiltOpen: function(instance)
|
||||
{
|
||||
let presenter = instance.presenter;
|
||||
let canvas = presenter.canvas;
|
||||
presenter = instance.presenter;
|
||||
Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
|
||||
|
||||
presenter.onSetupMesh = function() {
|
||||
|
||||
presenter.highlightNodeAt(canvas.width / 2, canvas.height / 2, {
|
||||
onpick: function()
|
||||
{
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Highlighting a node didn't work properly.");
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After highlighting a node, it should be highlighted. D'oh.");
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
}
|
||||
});
|
||||
presenter.highlightNodeAt(presenter.canvas.width / 2,
|
||||
presenter.canvas.height / 2);
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function whenHighlighting() {
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Highlighting a node didn't work properly.");
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After highlighting a node, it should be highlighted. D'oh.");
|
||||
|
||||
executeSoon(function() {
|
||||
Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false);
|
||||
presenter.highlightNodeAt(-1, -1);
|
||||
});
|
||||
}
|
||||
|
||||
function whenUnhighlighting() {
|
||||
ok(presenter._currentSelection < 0,
|
||||
"Unhighlighting a should remove the current selection.");
|
||||
ok(presenter.highlight.disabled,
|
||||
"After unhighlighting a node, it shouldn't be highlighted anymore. D'oh.");
|
||||
|
||||
executeSoon(function() {
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING);
|
||||
Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
|
||||
/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
let presenter;
|
||||
|
||||
function test() {
|
||||
if (!isTiltEnabled()) {
|
||||
info("Skipping highlight test because Tilt isn't enabled.");
|
||||
@ -22,26 +20,45 @@ function test() {
|
||||
createTilt({
|
||||
onTiltOpen: function(instance)
|
||||
{
|
||||
let presenter = instance.presenter;
|
||||
presenter = instance.presenter;
|
||||
Services.obs.addObserver(whenHighlighting, HIGHLIGHTING, false);
|
||||
|
||||
presenter.onSetupMesh = function() {
|
||||
presenter.highlightNodeFor(1);
|
||||
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Highlighting a node didn't work properly.");
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After highlighting a node, it should be highlighted. D'oh.");
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
presenter.highlightNodeFor(5); // 1 = html, 2 = body, 3 = first div
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function whenHighlighting() {
|
||||
ok(presenter._currentSelection > 0,
|
||||
"Highlighting a node didn't work properly.");
|
||||
ok(!presenter.highlight.disabled,
|
||||
"After highlighting a node, it should be highlighted. D'oh.");
|
||||
|
||||
executeSoon(function() {
|
||||
Services.obs.addObserver(whenUnhighlighting, UNHIGHLIGHTING, false);
|
||||
presenter.highlightNodeFor(-1);
|
||||
});
|
||||
}
|
||||
|
||||
function whenUnhighlighting() {
|
||||
ok(presenter._currentSelection < 0,
|
||||
"Unhighlighting a should remove the current selection.");
|
||||
ok(presenter.highlight.disabled,
|
||||
"After unhighlighting a node, it shouldn't be highlighted anymore. D'oh.");
|
||||
|
||||
executeSoon(function() {
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(whenHighlighting, HIGHLIGHTING);
|
||||
Services.obs.removeObserver(whenUnhighlighting, UNHIGHLIGHTING);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, TiltUtils */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, TiltUtils */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, TiltUtils */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, gBrowser, TiltUtils */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
|
@ -1,9 +1,9 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, waitForExplicitFinish, finish, gBrowser, TiltUtils */
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
|
||||
function init(callback) {
|
||||
let iframe = gBrowser.ownerDocument.createElement("iframe");
|
||||
|
||||
@ -55,15 +55,15 @@ function test() {
|
||||
iframe.contentWindow.innerHeight,
|
||||
"The content window height wasn't calculated correctly.");
|
||||
|
||||
|
||||
let nodeCoordinates = dom.getNodeCoordinates(
|
||||
let nodeCoordinates = LayoutHelpers.getRect(
|
||||
iframe.contentDocument.getElementById("test-div"), iframe.contentWindow);
|
||||
|
||||
let frameOffset = dom.getFrameOffset(iframe);
|
||||
let frameOffset = LayoutHelpers.getIframeContentOffset(iframe);
|
||||
let frameRect = iframe.getBoundingClientRect();
|
||||
|
||||
is(nodeCoordinates.top, frameOffset.top + 98,
|
||||
is(nodeCoordinates.top, frameRect.top + frameOffset[0] + 98,
|
||||
"The node coordinates top value wasn't calculated correctly.");
|
||||
is(nodeCoordinates.left, frameOffset.left + 76,
|
||||
is(nodeCoordinates.left, frameRect.left + frameOffset[1] + 76,
|
||||
"The node coordinates left value wasn't calculated correctly.");
|
||||
is(nodeCoordinates.width, 123,
|
||||
"The node coordinates width value wasn't calculated correctly.");
|
||||
|
@ -1,7 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, isnot, gBrowser, TiltUtils */
|
||||
"use strict";
|
||||
|
||||
let someObject = {
|
||||
@ -43,8 +41,4 @@ function test() {
|
||||
"Not all members of the destroyed object were deleted.");
|
||||
is(typeof someObject.func, "undefined",
|
||||
"Not all function members of the destroyed object were deleted.");
|
||||
|
||||
|
||||
is(TiltUtils.getBrowserWindow(), window,
|
||||
"The getBrowserWindow() function didn't return the correct window.");
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, isApproxVec, isTiltEnabled, isWebGLSupported, gBrowser*/
|
||||
/*global TiltVisualizer */
|
||||
"use strict";
|
||||
|
||||
function test() {
|
||||
@ -19,8 +16,9 @@ function test() {
|
||||
let webGLLoad = false;
|
||||
|
||||
let visualizer = new TiltVisualizer({
|
||||
parentNode: gBrowser.selectedBrowser.parentNode,
|
||||
chromeWindow: window,
|
||||
contentWindow: gBrowser.selectedBrowser.contentWindow,
|
||||
parentNode: gBrowser.selectedBrowser.parentNode,
|
||||
requestAnimationFrame: window.mozRequestAnimationFrame,
|
||||
inspectorUI: window.InspectorUI,
|
||||
|
||||
|
@ -1,25 +1,15 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global ok, is, info, waitForExplicitFinish, finish, executeSoon, gBrowser */
|
||||
/*global isApprox, isTiltEnabled, isWebGLSupported, createTab, createTilt */
|
||||
/*global Services, EventUtils, TiltUtils, InspectorUI, TILT_DESTROYED */
|
||||
"use strict";
|
||||
|
||||
const ZOOM = 2;
|
||||
const RESIZE = 50;
|
||||
|
||||
function setZoom(value) {
|
||||
gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = value;
|
||||
}
|
||||
|
||||
function getZoom() {
|
||||
return gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
|
||||
}
|
||||
|
||||
function test() {
|
||||
TiltUtils.setDocumentZoom(Math.random());
|
||||
is(getZoom(), TiltUtils.getDocumentZoom(),
|
||||
let random = Math.random() * 10;
|
||||
|
||||
TiltUtils.setDocumentZoom(window, random);
|
||||
ok(isApprox(TiltUtils.getDocumentZoom(window), random),
|
||||
"The getDocumentZoom utility function didn't return the expected results.");
|
||||
|
||||
|
||||
@ -38,7 +28,7 @@ function test() {
|
||||
createTilt({
|
||||
onInspectorOpen: function()
|
||||
{
|
||||
setZoom(ZOOM);
|
||||
TiltUtils.setDocumentZoom(window, ZOOM);
|
||||
},
|
||||
onTiltOpen: function(instance)
|
||||
{
|
||||
@ -53,38 +43,38 @@ function test() {
|
||||
let arcball = instance.controller.arcball;
|
||||
|
||||
ok(isApprox(contentWindow.innerWidth * ZOOM, renderer.width, 1),
|
||||
"The renderer width wasn't set correctly.");
|
||||
"The renderer width wasn't set correctly before the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight * ZOOM, renderer.height, 1),
|
||||
"The renderer height wasn't set correctly.");
|
||||
"The renderer height wasn't set correctly before the resize.");
|
||||
|
||||
ok(isApprox(contentWindow.innerWidth * ZOOM, arcball.width, 1),
|
||||
"The arcball width wasn't set correctly.");
|
||||
"The arcball width wasn't set correctly before the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight * ZOOM, arcball.height, 1),
|
||||
"The arcball height wasn't set correctly.");
|
||||
"The arcball height wasn't set correctly before the resize.");
|
||||
|
||||
|
||||
window.resizeBy(-RESIZE * ZOOM, -RESIZE * ZOOM);
|
||||
|
||||
executeSoon(function() {
|
||||
ok(isApprox(contentWindow.innerWidth + RESIZE, initialWidth, 1),
|
||||
"The content window width wasn't set correctly.");
|
||||
"The content window width wasn't set correctly after the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight + RESIZE, initialHeight, 1),
|
||||
"The content window height wasn't set correctly.");
|
||||
"The content window height wasn't set correctly after the resize.");
|
||||
|
||||
ok(isApprox(contentWindow.innerWidth * ZOOM, renderer.width, 1),
|
||||
"The renderer width wasn't set correctly.");
|
||||
"The renderer width wasn't set correctly after the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight * ZOOM, renderer.height, 1),
|
||||
"The renderer height wasn't set correctly.");
|
||||
"The renderer height wasn't set correctly after the resize.");
|
||||
|
||||
ok(isApprox(contentWindow.innerWidth * ZOOM, arcball.width, 1),
|
||||
"The arcball width wasn't set correctly.");
|
||||
"The arcball width wasn't set correctly after the resize.");
|
||||
ok(isApprox(contentWindow.innerHeight * ZOOM, arcball.height, 1),
|
||||
"The arcball height wasn't set correctly.");
|
||||
"The arcball height wasn't set correctly after the resize.");
|
||||
|
||||
|
||||
window.resizeBy(RESIZE * ZOOM, RESIZE * ZOOM);
|
||||
|
||||
Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(cleanup, DESTROYED, false);
|
||||
InspectorUI.closeInspectorUI();
|
||||
});
|
||||
},
|
||||
@ -93,7 +83,7 @@ function test() {
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Services.obs.removeObserver(cleanup, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(cleanup, DESTROYED);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global Services, Components, gBrowser, executeSoon, info */
|
||||
/*global InspectorUI, Tilt, TiltGL, EPSILON */
|
||||
"use strict";
|
||||
|
||||
let tempScope = {};
|
||||
@ -28,16 +25,16 @@ const DEFAULT_HTML = "data:text/html," +
|
||||
"<title>Three Laws</title>" +
|
||||
"</head>" +
|
||||
"<body>" +
|
||||
"<div>" +
|
||||
"A robot may not injure a human being or, through inaction, allow a" +
|
||||
"<div id='first-law'>" +
|
||||
"A robot may not injure a human being or, through inaction, allow a " +
|
||||
"human being to come to harm." +
|
||||
"</div>" +
|
||||
"<div>" +
|
||||
"A robot must obey the orders given to it by human beings, except" +
|
||||
"A robot must obey the orders given to it by human beings, except " +
|
||||
"where such orders would conflict with the First Law." +
|
||||
"</div>" +
|
||||
"<div>" +
|
||||
"A robot must protect its own existence as long as such protection" +
|
||||
"A robot must protect its own existence as long as such protection " +
|
||||
"does not conflict with the First or Second Laws." +
|
||||
"</div>" +
|
||||
"<body>" +
|
||||
@ -46,10 +43,16 @@ const DEFAULT_HTML = "data:text/html," +
|
||||
const INSPECTOR_OPENED = InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED;
|
||||
const INSPECTOR_CLOSED = InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED;
|
||||
|
||||
const TILT_INITIALIZED = Tilt.NOTIFICATIONS.INITIALIZED;
|
||||
const TILT_DESTROYED = Tilt.NOTIFICATIONS.DESTROYED;
|
||||
const TILT_SHOWN = Tilt.NOTIFICATIONS.SHOWN;
|
||||
const TILT_HIDDEN = Tilt.NOTIFICATIONS.HIDDEN;
|
||||
const INITIALIZING = Tilt.NOTIFICATIONS.INITIALIZING;
|
||||
const INITIALIZED = Tilt.NOTIFICATIONS.INITIALIZED;
|
||||
const DESTROYING = Tilt.NOTIFICATIONS.DESTROYING;
|
||||
const BEFORE_DESTROYED = Tilt.NOTIFICATIONS.BEFORE_DESTROYED;
|
||||
const DESTROYED = Tilt.NOTIFICATIONS.DESTROYED;
|
||||
const SHOWN = Tilt.NOTIFICATIONS.SHOWN;
|
||||
const HIDDEN = Tilt.NOTIFICATIONS.HIDDEN;
|
||||
const HIGHLIGHTING = Tilt.NOTIFICATIONS.HIGHLIGHTING;
|
||||
const UNHIGHLIGHTING = Tilt.NOTIFICATIONS.UNHIGHLIGHTING;
|
||||
const NODE_REMOVED = Tilt.NOTIFICATIONS.NODE_REMOVED;
|
||||
|
||||
const TILT_ENABLED = Services.prefs.getBoolPref("devtools.tilt.enabled");
|
||||
const INSP_ENABLED = Services.prefs.getBoolPref("devtools.inspector.enabled");
|
||||
@ -122,6 +125,8 @@ function createTab(callback, location) {
|
||||
|
||||
|
||||
function createTilt(callbacks, close) {
|
||||
Services.prefs.setBoolPref("webgl.verbose", true);
|
||||
|
||||
Services.obs.addObserver(onInspectorOpen, INSPECTOR_OPENED, false);
|
||||
InspectorUI.toggleInspectorUI();
|
||||
|
||||
@ -132,27 +137,27 @@ function createTilt(callbacks, close) {
|
||||
if ("function" === typeof callbacks.onInspectorOpen) {
|
||||
callbacks.onInspectorOpen();
|
||||
}
|
||||
Services.obs.addObserver(onTiltOpen, TILT_INITIALIZED, false);
|
||||
Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
|
||||
Tilt.initialize();
|
||||
});
|
||||
}
|
||||
|
||||
function onTiltOpen() {
|
||||
Services.obs.removeObserver(onTiltOpen, TILT_INITIALIZED);
|
||||
Services.obs.removeObserver(onTiltOpen, INITIALIZING);
|
||||
|
||||
executeSoon(function() {
|
||||
if ("function" === typeof callbacks.onTiltOpen) {
|
||||
callbacks.onTiltOpen(Tilt.visualizers[Tilt.currentWindowId]);
|
||||
}
|
||||
if (close) {
|
||||
Services.obs.addObserver(onTiltClose, TILT_DESTROYED, false);
|
||||
Services.obs.addObserver(onTiltClose, DESTROYED, false);
|
||||
Tilt.destroy(Tilt.currentWindowId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onTiltClose() {
|
||||
Services.obs.removeObserver(onTiltClose, TILT_DESTROYED);
|
||||
Services.obs.removeObserver(onTiltClose, DESTROYED);
|
||||
|
||||
executeSoon(function() {
|
||||
if ("function" === typeof callbacks.onTiltClose) {
|
||||
|
@ -77,9 +77,18 @@ gcli.addCommand({
|
||||
gcli.addCommand({
|
||||
name: "console clear",
|
||||
description: gcli.lookup("consoleclearDesc"),
|
||||
exec: function(args, context) {
|
||||
exec: function Command_consoleClear(args, context) {
|
||||
let window = context.environment.chromeDocument.defaultView;
|
||||
let hud = HUDService.getHudReferenceById(context.environment.hudId);
|
||||
hud.gcliterm.clearOutput();
|
||||
|
||||
// Use a timeout so we also clear the reporting of the clear command
|
||||
let threadManager = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService(Components.interfaces.nsIThreadManager);
|
||||
threadManager.mainThread.dispatch({
|
||||
run: function() {
|
||||
hud.gcliterm.clearOutput();
|
||||
}
|
||||
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
});
|
||||
|
||||
@ -90,7 +99,7 @@ gcli.addCommand({
|
||||
gcli.addCommand({
|
||||
name: "console close",
|
||||
description: gcli.lookup("consolecloseDesc"),
|
||||
exec: function(args, context) {
|
||||
exec: function Command_consoleClose(args, context) {
|
||||
let tab = HUDService.getHudReferenceById(context.environment.hudId).tab;
|
||||
HUDService.deactivateHUDForContext(tab);
|
||||
}
|
||||
@ -112,8 +121,7 @@ gcli.addCommand({
|
||||
}
|
||||
],
|
||||
exec: function Command_inspect(args, context) {
|
||||
let hud = HUDService.getHudReferenceById(context.environment.hudId);
|
||||
let InspectorUI = hud.gcliterm.document.defaultView.InspectorUI;
|
||||
InspectorUI.openInspectorUI(args.node);
|
||||
let document = context.environment.chromeDocument;
|
||||
document.defaultView.InspectorUI.openInspectorUI(args.node);
|
||||
}
|
||||
});
|
||||
|
@ -2067,7 +2067,16 @@ HUD_SERVICE.prototype =
|
||||
// Pipe the message to createMessageNode().
|
||||
let hud = HUDService.hudReferences[aHUDId];
|
||||
function formatResult(x) {
|
||||
return (typeof(x) == "string") ? x : hud.jsterm.formatResult(x);
|
||||
if (typeof(x) == "string") {
|
||||
return x;
|
||||
}
|
||||
if (hud.gcliterm) {
|
||||
return hud.gcliterm.formatResult(x);
|
||||
}
|
||||
if (hud.jsterm) {
|
||||
return hud.jsterm.formatResult(x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
let body = null;
|
||||
@ -4699,7 +4708,7 @@ function JSTermHelper(aJSTerm)
|
||||
aJSTerm.sandbox.inspectrules = function JSTH_inspectrules(aNode)
|
||||
{
|
||||
aJSTerm.helperEvaluated = true;
|
||||
let doc = aJSTerm.parentNode.ownerDocument;
|
||||
let doc = aJSTerm.inputNode.ownerDocument;
|
||||
let win = doc.defaultView;
|
||||
let panel = createElement(doc, "panel", {
|
||||
label: "CSS Rules",
|
||||
@ -4813,6 +4822,7 @@ function JSTerm(aContext, aParentNode, aMixin, aConsole)
|
||||
this.parentNode = aParentNode;
|
||||
this.mixins = aMixin;
|
||||
this.console = aConsole;
|
||||
this.document = aParentNode.ownerDocument
|
||||
|
||||
this.setTimeout = aParentNode.ownerDocument.defaultView.setTimeout;
|
||||
|
||||
@ -5018,7 +5028,7 @@ JSTerm.prototype = {
|
||||
});
|
||||
}
|
||||
|
||||
let doc = self.parentNode.ownerDocument;
|
||||
let doc = self.document;
|
||||
let parent = doc.getElementById("mainPopupSet");
|
||||
let title = (aEvalString
|
||||
? HUDService.getFormatStr("jsPropertyInspectTitle", [aEvalString])
|
||||
@ -5047,7 +5057,7 @@ JSTerm.prototype = {
|
||||
*/
|
||||
writeOutputJS: function JST_writeOutputJS(aEvalString, aOutputObject, aOutputString)
|
||||
{
|
||||
let node = ConsoleUtils.createMessageNode(this.parentNode.ownerDocument,
|
||||
let node = ConsoleUtils.createMessageNode(this.document,
|
||||
CATEGORY_OUTPUT,
|
||||
SEVERITY_LOG,
|
||||
aOutputString,
|
||||
@ -5096,7 +5106,7 @@ JSTerm.prototype = {
|
||||
*/
|
||||
writeOutput: function JST_writeOutput(aOutputMessage, aCategory, aSeverity)
|
||||
{
|
||||
let node = ConsoleUtils.createMessageNode(this.parentNode.ownerDocument,
|
||||
let node = ConsoleUtils.createMessageNode(this.document,
|
||||
aCategory, aSeverity,
|
||||
aOutputMessage, this.hudId);
|
||||
|
||||
@ -6819,6 +6829,7 @@ function GcliTerm(aContentWindow, aHudId, aDocument, aConsole, aHintNode, aConso
|
||||
this.document = aDocument;
|
||||
this.console = aConsole;
|
||||
this.hintNode = aHintNode;
|
||||
this._window = this.context.get().QueryInterface(Ci.nsIDOMWindow);
|
||||
|
||||
this.createUI();
|
||||
this.createSandbox();
|
||||
@ -6841,7 +6852,11 @@ function GcliTerm(aContentWindow, aHudId, aDocument, aConsole, aHintNode, aConso
|
||||
};
|
||||
|
||||
this.opts = {
|
||||
environment: { hudId: this.hudId },
|
||||
environment: {
|
||||
hudId: this.hudId,
|
||||
chromeDocument: this.document,
|
||||
contentDocument: aContentWindow.document
|
||||
},
|
||||
chromeDocument: this.document,
|
||||
contentDocument: aContentWindow.document,
|
||||
jsEnvironment: {
|
||||
@ -6911,6 +6926,7 @@ GcliTerm.prototype = {
|
||||
delete this.document;
|
||||
delete this.console;
|
||||
delete this.hintNode;
|
||||
delete this._window;
|
||||
|
||||
delete this.sandbox;
|
||||
delete this.element
|
||||
@ -6970,14 +6986,59 @@ GcliTerm.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this.writeOutput(aEvent.output.typed, { category: CATEGORY_INPUT });
|
||||
this.writeOutput(aEvent.output.typed, CATEGORY_INPUT);
|
||||
|
||||
if (aEvent.output.output == null) {
|
||||
// This is an experiment to see how much people yell when we stop reporting
|
||||
// undefined replies.
|
||||
if (aEvent.output.output === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let output = aEvent.output.output;
|
||||
if (aEvent.output.command.returnType == "html" && typeof output == "string") {
|
||||
let declaredType = aEvent.output.command.returnType || "";
|
||||
|
||||
if (declaredType == "object") {
|
||||
let actualType = typeof output;
|
||||
if (output === null) {
|
||||
output = "null";
|
||||
}
|
||||
else if (actualType == "string") {
|
||||
output = "\"" + output + "\"";
|
||||
}
|
||||
else if (actualType == "object" || actualType == "function") {
|
||||
let formatOpts = [ nameObject(output) ];
|
||||
output = stringBundle.formatStringFromName('gcliterm.instanceLabel',
|
||||
formatOpts, formatOpts.length);
|
||||
let linkNode = this.document.createElementNS(HTML_NS, 'html:span');
|
||||
linkNode.appendChild(this.document.createTextNode(output));
|
||||
linkNode.classList.add("hud-clickable");
|
||||
linkNode.setAttribute("aria-haspopup", "true");
|
||||
|
||||
// Make the object bring up the property panel.
|
||||
linkNode.addEventListener("mousedown", function(aEv) {
|
||||
this._startX = aEv.clientX;
|
||||
this._startY = aEv.clientY;
|
||||
}.bind(this), false);
|
||||
|
||||
linkNode.addEventListener("click", function(aEv) {
|
||||
if (aEv.detail != 1 || aEv.button != 0 ||
|
||||
(this._startX != aEv.clientX && this._startY != aEv.clientY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._panelOpen) {
|
||||
let propPanel = this.openPropertyPanel(aEvent.output.typed, aEvent.output.output, this);
|
||||
propPanel.panel.setAttribute("hudId", this.hudId);
|
||||
this._panelOpen = true;
|
||||
}
|
||||
}.bind(this), false);
|
||||
|
||||
output = linkNode;
|
||||
}
|
||||
// else if (actualType == number/boolean/undefined) do nothing
|
||||
}
|
||||
|
||||
if (declaredType == "html" && typeof output == "string") {
|
||||
output = this.document.createRange().createContextualFragment(
|
||||
'<div xmlns="' + HTML_NS + '" xmlns:xul="' + XUL_NS + '">' +
|
||||
output + '</div>');
|
||||
@ -7017,14 +7078,14 @@ GcliTerm.prototype = {
|
||||
*/
|
||||
createSandbox: function Gcli_createSandbox()
|
||||
{
|
||||
let win = this.context.get().QueryInterface(Ci.nsIDOMWindow);
|
||||
|
||||
// create a JS Sandbox out of this.context
|
||||
this.sandbox = new Cu.Sandbox(win, {
|
||||
sandboxPrototype: win,
|
||||
this.sandbox = new Cu.Sandbox(this._window, {
|
||||
sandboxPrototype: this._window,
|
||||
wantXrays: false
|
||||
});
|
||||
this.sandbox.console = this.console;
|
||||
|
||||
JSTermHelper(this);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -7036,7 +7097,31 @@ GcliTerm.prototype = {
|
||||
*/
|
||||
evalInSandbox: function Gcli_evalInSandbox(aString)
|
||||
{
|
||||
return Cu.evalInSandbox(aString, this.sandbox, "1.8", "Web Console", 1);
|
||||
let window = unwrap(this.sandbox.window);
|
||||
let temp$ = null;
|
||||
let temp$$ = null;
|
||||
|
||||
// We prefer to execute the page-provided implementations for the $() and
|
||||
// $$() functions.
|
||||
if (typeof window.$ == "function") {
|
||||
temp$ = this.sandbox.$;
|
||||
delete this.sandbox.$;
|
||||
}
|
||||
if (typeof window.$$ == "function") {
|
||||
temp$$ = this.sandbox.$$;
|
||||
delete this.sandbox.$$;
|
||||
}
|
||||
|
||||
let result = Cu.evalInSandbox(aString, this.sandbox, "1.8", "Web Console", 1);
|
||||
|
||||
if (temp$) {
|
||||
this.sandbox.$ = temp$;
|
||||
}
|
||||
if (temp$$) {
|
||||
this.sandbox.$$ = temp$$;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -7050,14 +7135,14 @@ GcliTerm.prototype = {
|
||||
* @param number aSeverity
|
||||
* One of the SEVERITY_ constants.
|
||||
*/
|
||||
writeOutput: function Gcli_writeOutput(aOutputMessage, aOptions)
|
||||
writeOutput: function Gcli_writeOutput(aOutputMessage, aCategory, aSeverity, aOptions)
|
||||
{
|
||||
aOptions = aOptions || {};
|
||||
|
||||
let node = ConsoleUtils.createMessageNode(
|
||||
this.document,
|
||||
aOptions.category || CATEGORY_OUTPUT,
|
||||
aOptions.severity || SEVERITY_LOG,
|
||||
aCategory || CATEGORY_OUTPUT,
|
||||
aSeverity || SEVERITY_LOG,
|
||||
aOutputMessage,
|
||||
this.hudId,
|
||||
aOptions.sourceUrl || undefined,
|
||||
@ -7068,5 +7153,21 @@ GcliTerm.prototype = {
|
||||
},
|
||||
|
||||
clearOutput: JSTerm.prototype.clearOutput,
|
||||
openPropertyPanel: JSTerm.prototype.openPropertyPanel,
|
||||
|
||||
formatResult: JSTerm.prototype.formatResult,
|
||||
getResultType: JSTerm.prototype.getResultType,
|
||||
formatString: JSTerm.prototype.formatString,
|
||||
};
|
||||
|
||||
/**
|
||||
* A fancy version of toString()
|
||||
*/
|
||||
function nameObject(aObj) {
|
||||
if (aObj.constructor && aObj.constructor.name) {
|
||||
return aObj.constructor.name;
|
||||
}
|
||||
// If that fails, use Objects toString which sometimes gives something
|
||||
// better than 'Object', and at least defaults to Object if nothing better
|
||||
return Object.prototype.toString.call(aObj).slice(8, -1);
|
||||
}
|
||||
|
@ -200,6 +200,11 @@ var console = {};
|
||||
* The constructor name
|
||||
*/
|
||||
function getCtorName(aObj) {
|
||||
if (aObj.constructor && aObj.constructor.name) {
|
||||
return aObj.constructor.name;
|
||||
}
|
||||
// If that fails, use Objects toString which sometimes gives something
|
||||
// better than 'Object', and at least defaults to Object if nothing better
|
||||
return Object.prototype.toString.call(aObj).slice(8, -1);
|
||||
}
|
||||
|
||||
@ -870,6 +875,10 @@ function Command(commandSpec) {
|
||||
// parameter groups.
|
||||
var usingGroups = false;
|
||||
|
||||
if (this.returnType == null) {
|
||||
this.returnType = 'string';
|
||||
}
|
||||
|
||||
// In theory this could easily be made recursive, so param groups could
|
||||
// contain nested param groups. Current thinking is that the added
|
||||
// complexity for the UI probably isn't worth it, so this implementation
|
||||
@ -3020,6 +3029,15 @@ JavascriptType.prototype.parse = function(arg) {
|
||||
var typed = arg.text;
|
||||
var scope = globalObject;
|
||||
|
||||
// Just accept numbers
|
||||
if (!isNaN(parseFloat(typed)) && isFinite(typed)) {
|
||||
return new Conversion(typed, arg);
|
||||
}
|
||||
// Just accept constants like true/false/null/etc
|
||||
if (typed.trim().match(/(null|undefined|NaN|Infinity|true|false)/)) {
|
||||
return new Conversion(typed, arg);
|
||||
}
|
||||
|
||||
// Analyze the input text and find the beginning of the last part that
|
||||
// should be completed.
|
||||
var beginning = this._findCompletionBeginning(typed);
|
||||
@ -3603,15 +3621,8 @@ define('gcli/host', ['require', 'exports', 'module' ], function(require, exports
|
||||
* There is likely a better way to do this, but this will do for now.
|
||||
*/
|
||||
exports.flashNode = function(node, color) {
|
||||
if (!node.__gcliHighlighting) {
|
||||
node.__gcliHighlighting = true;
|
||||
var original = node.style.background;
|
||||
node.style.background = color;
|
||||
setTimeout(function() {
|
||||
node.style.background = original;
|
||||
delete node.__gcliHighlighting;
|
||||
}, 1000);
|
||||
}
|
||||
// We avoid changing the DOM under firefox developer tools so this is a no-op
|
||||
// In future we will use the multi-highlighter implemented in bug 653545.
|
||||
};
|
||||
|
||||
|
||||
@ -3991,34 +4002,12 @@ var evalCommandSpec = {
|
||||
description: ''
|
||||
}
|
||||
],
|
||||
returnType: 'html',
|
||||
returnType: 'object',
|
||||
description: { key: 'cliEvalJavascript' },
|
||||
exec: function(args, context) {
|
||||
// → is right arrow. We use explicit entities to ensure XML validity
|
||||
var resultPrefix = '<em>{ ' + args.javascript + ' }</em> → ';
|
||||
try {
|
||||
var result = customEval(args.javascript);
|
||||
|
||||
if (result === null) {
|
||||
return resultPrefix + 'null.';
|
||||
}
|
||||
|
||||
if (result === undefined) {
|
||||
return resultPrefix + 'undefined.';
|
||||
}
|
||||
|
||||
if (typeof result === 'function') {
|
||||
//   is
|
||||
return resultPrefix +
|
||||
(result + '').replace(/\n/g, '<br>').replace(/ /g, ' ');
|
||||
}
|
||||
|
||||
return resultPrefix + result;
|
||||
}
|
||||
catch (ex) {
|
||||
return resultPrefix + 'Exception: ' + ex.message;
|
||||
}
|
||||
}
|
||||
return customEval(args.javascript);
|
||||
},
|
||||
evalRegexp: /^\s*{\s*/
|
||||
};
|
||||
|
||||
|
||||
@ -4202,8 +4191,9 @@ Requisition.prototype._onAssignmentChange = function(ev) {
|
||||
// Refactor? See bug 660765
|
||||
// Do preceding arguments need to have dummy values applied so we don't
|
||||
// get a hole in the command line?
|
||||
var i;
|
||||
if (ev.assignment.param.isPositionalAllowed()) {
|
||||
for (var i = 0; i < ev.assignment.paramIndex; i++) {
|
||||
for (i = 0; i < ev.assignment.paramIndex; i++) {
|
||||
var assignment = this.getAssignment(i);
|
||||
if (assignment.param.isPositionalAllowed()) {
|
||||
if (assignment.ensureVisibleArgument()) {
|
||||
@ -4215,7 +4205,7 @@ Requisition.prototype._onAssignmentChange = function(ev) {
|
||||
|
||||
// Remember where we found the first match
|
||||
var index = MORE_THAN_THE_MOST_ARGS_POSSIBLE;
|
||||
for (var i = 0; i < this._args.length; i++) {
|
||||
for (i = 0; i < this._args.length; i++) {
|
||||
if (this._args[i].assignment === ev.assignment) {
|
||||
if (i < index) {
|
||||
index = i;
|
||||
@ -4231,7 +4221,7 @@ Requisition.prototype._onAssignmentChange = function(ev) {
|
||||
else {
|
||||
// Is there a way to do this that doesn't involve a loop?
|
||||
var newArgs = ev.conversion.arg.getArgs();
|
||||
for (var i = 0; i < newArgs.length; i++) {
|
||||
for (i = 0; i < newArgs.length; i++) {
|
||||
this._args.splice(index + i, 0, newArgs[i]);
|
||||
}
|
||||
}
|
||||
@ -4274,14 +4264,14 @@ Requisition.prototype.getAssignment = function(nameOrNumber) {
|
||||
nameOrNumber :
|
||||
Object.keys(this._assignments)[nameOrNumber];
|
||||
return this._assignments[name] || undefined;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Where parameter name == assignment names - they are the same
|
||||
*/
|
||||
Requisition.prototype.getParameterNames = function() {
|
||||
return Object.keys(this._assignments);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A *shallow* clone of the assignments.
|
||||
@ -4414,14 +4404,15 @@ Requisition.prototype.createInputArgTrace = function() {
|
||||
}
|
||||
|
||||
var args = [];
|
||||
var i;
|
||||
this._args.forEach(function(arg) {
|
||||
for (var i = 0; i < arg.prefix.length; i++) {
|
||||
for (i = 0; i < arg.prefix.length; i++) {
|
||||
args.push({ arg: arg, char: arg.prefix[i], part: 'prefix' });
|
||||
}
|
||||
for (var i = 0; i < arg.text.length; i++) {
|
||||
for (i = 0; i < arg.text.length; i++) {
|
||||
args.push({ arg: arg, char: arg.text[i], part: 'text' });
|
||||
}
|
||||
for (var i = 0; i < arg.suffix.length; i++) {
|
||||
for (i = 0; i < arg.suffix.length; i++) {
|
||||
args.push({ arg: arg, char: arg.suffix[i], part: 'suffix' });
|
||||
}
|
||||
});
|
||||
@ -4582,10 +4573,18 @@ Requisition.prototype.exec = function(input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Display JavaScript input without the initial { or closing }
|
||||
var typed = this.toString();
|
||||
if (evalCommandSpec.evalRegexp.test(typed)) {
|
||||
typed = typed.replace(evalCommandSpec.evalRegexp, '');
|
||||
// Bug 717763: What if the JavaScript naturally ends with a }?
|
||||
typed = typed.replace(/\s*}\s*$/, '');
|
||||
}
|
||||
|
||||
var outputObject = {
|
||||
command: command,
|
||||
args: args,
|
||||
typed: this.toString(),
|
||||
typed: typed,
|
||||
canonical: this.toCanonicalString(),
|
||||
completed: false,
|
||||
start: new Date()
|
||||
@ -4593,7 +4592,7 @@ Requisition.prototype.exec = function(input) {
|
||||
|
||||
this.commandOutputManager.sendCommandOutput(outputObject);
|
||||
|
||||
var onComplete = (function(output, error) {
|
||||
var onComplete = function(output, error) {
|
||||
if (visible) {
|
||||
outputObject.end = new Date();
|
||||
outputObject.duration = outputObject.end.getTime() - outputObject.start.getTime();
|
||||
@ -4602,7 +4601,7 @@ Requisition.prototype.exec = function(input) {
|
||||
outputObject.completed = true;
|
||||
this.commandOutputManager.sendCommandOutput(outputObject);
|
||||
}
|
||||
}).bind(this);
|
||||
}.bind(this);
|
||||
|
||||
try {
|
||||
var context = new ExecutionContext(this);
|
||||
@ -4621,6 +4620,7 @@ Requisition.prototype.exec = function(input) {
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
console.error(ex);
|
||||
onComplete(ex, true);
|
||||
}
|
||||
|
||||
@ -4775,6 +4775,7 @@ Requisition.prototype._tokenize = function(typed) {
|
||||
|
||||
while (true) {
|
||||
var c = typed[i];
|
||||
var str;
|
||||
switch (mode) {
|
||||
case In.WHITESPACE:
|
||||
if (c === '\'') {
|
||||
@ -4807,7 +4808,7 @@ Requisition.prototype._tokenize = function(typed) {
|
||||
// There is an edge case of xx'xx which we are assuming to
|
||||
// be a single parameter (and same with ")
|
||||
if (c === ' ') {
|
||||
var str = unescape2(typed.substring(start, i));
|
||||
str = unescape2(typed.substring(start, i));
|
||||
args.push(new Argument(str, prefix, ''));
|
||||
mode = In.WHITESPACE;
|
||||
start = i;
|
||||
@ -4817,7 +4818,7 @@ Requisition.prototype._tokenize = function(typed) {
|
||||
|
||||
case In.SINGLE_Q:
|
||||
if (c === '\'') {
|
||||
var str = unescape2(typed.substring(start, i));
|
||||
str = unescape2(typed.substring(start, i));
|
||||
args.push(new Argument(str, prefix, c));
|
||||
mode = In.WHITESPACE;
|
||||
start = i + 1;
|
||||
@ -4827,7 +4828,7 @@ Requisition.prototype._tokenize = function(typed) {
|
||||
|
||||
case In.DOUBLE_Q:
|
||||
if (c === '"') {
|
||||
var str = unescape2(typed.substring(start, i));
|
||||
str = unescape2(typed.substring(start, i));
|
||||
args.push(new Argument(str, prefix, c));
|
||||
mode = In.WHITESPACE;
|
||||
start = i + 1;
|
||||
@ -4842,7 +4843,7 @@ Requisition.prototype._tokenize = function(typed) {
|
||||
else if (c === '}') {
|
||||
blockDepth--;
|
||||
if (blockDepth === 0) {
|
||||
var str = unescape2(typed.substring(start, i));
|
||||
str = unescape2(typed.substring(start, i));
|
||||
args.push(new ScriptArgument(str, prefix, c));
|
||||
mode = In.WHITESPACE;
|
||||
start = i + 1;
|
||||
@ -4871,11 +4872,11 @@ Requisition.prototype._tokenize = function(typed) {
|
||||
}
|
||||
}
|
||||
else if (mode === In.SCRIPT) {
|
||||
var str = unescape2(typed.substring(start, i + 1));
|
||||
str = unescape2(typed.substring(start, i + 1));
|
||||
args.push(new ScriptArgument(str, prefix, ''));
|
||||
}
|
||||
else {
|
||||
var str = unescape2(typed.substring(start, i + 1));
|
||||
str = unescape2(typed.substring(start, i + 1));
|
||||
args.push(new Argument(str, prefix, ''));
|
||||
}
|
||||
break;
|
||||
@ -4908,16 +4909,16 @@ Requisition.prototype._split = function(args) {
|
||||
// Handle the special case of the user typing { javascript(); }
|
||||
// We use the hidden 'eval' command directly rather than shift()ing one of
|
||||
// the parameters, and parse()ing it.
|
||||
var conversion;
|
||||
if (args[0] instanceof ScriptArgument) {
|
||||
// Special case: if the user enters { console.log('foo'); } then we need to
|
||||
// use the hidden 'eval' command
|
||||
var conversion = new Conversion(evalCommand, new Argument());
|
||||
conversion = new Conversion(evalCommand, new Argument());
|
||||
this.commandAssignment.setConversion(conversion);
|
||||
return;
|
||||
}
|
||||
|
||||
var argsUsed = 1;
|
||||
var conversion;
|
||||
|
||||
while (argsUsed <= args.length) {
|
||||
var arg = (argsUsed === 1) ?
|
||||
@ -6544,6 +6545,7 @@ define('gcli/ui/field', ['require', 'exports', 'module' , 'gcli/util', 'gcli/l10
|
||||
|
||||
var dom = require('gcli/util').dom;
|
||||
var createEvent = require('gcli/util').createEvent;
|
||||
var KeyEvent = require('gcli/util').event.KeyEvent;
|
||||
var l10n = require('gcli/l10n');
|
||||
|
||||
var Argument = require('gcli/argument').Argument;
|
||||
@ -6572,16 +6574,21 @@ var Menu = require('gcli/ui/menu').Menu;
|
||||
* This class is designed to be inherited from. It's important that all
|
||||
* subclasses have a similar constructor signature because they are created
|
||||
* via getField(...)
|
||||
* @param document The document we use in calling createElement
|
||||
* @param type The type to use in conversions
|
||||
* @param named Is this parameter named? That is to say, are positional
|
||||
* arguments disallowed, if true, then we need to provide updates to the
|
||||
* command line that explicitly name the parameter in use (e.g. --verbose, or
|
||||
* --name Fred rather than just true or Fred)
|
||||
* @param name If this parameter is named, what name should we use
|
||||
* @param requ The requisition that we're attached to
|
||||
* @param options A set of properties to help fields configure themselves:
|
||||
* - document: The document we use in calling createElement
|
||||
* - named: Is this parameter named? That is to say, are positional
|
||||
* arguments disallowed, if true, then we need to provide updates to
|
||||
* the command line that explicitly name the parameter in use
|
||||
* (e.g. --verbose, or --name Fred rather than just true or Fred)
|
||||
* - name: If this parameter is named, what name should we use
|
||||
* - requisition: The requisition that we're attached to
|
||||
* - required: Boolean to indicate if this is a mandatory field
|
||||
*/
|
||||
function Field(document, type, named, name, requ) {
|
||||
function Field(type, options) {
|
||||
this.type = type;
|
||||
this.document = options.document;
|
||||
this.requisition = options.requisition;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -6638,10 +6645,14 @@ Field.prototype.setMessage = function(message) {
|
||||
* Method to be called by subclasses when their input changes, which allows us
|
||||
* to properly pass on the fieldChanged event.
|
||||
*/
|
||||
Field.prototype.onInputChange = function() {
|
||||
Field.prototype.onInputChange = function(ev) {
|
||||
var conversion = this.getConversion();
|
||||
this.fieldChanged({ conversion: conversion });
|
||||
this.setMessage(conversion.message);
|
||||
|
||||
if (ev.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.requisition.exec();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -6715,8 +6726,7 @@ exports.getField = getField;
|
||||
* A field that allows editing of strings
|
||||
*/
|
||||
function StringField(type, options) {
|
||||
this.document = options.document;
|
||||
this.type = type;
|
||||
Field.call(this, type, options);
|
||||
this.arg = new Argument();
|
||||
|
||||
this.element = dom.createElement(this.document, 'input');
|
||||
@ -6763,8 +6773,7 @@ addField(StringField);
|
||||
* A field that allows editing of numbers using an [input type=number] field
|
||||
*/
|
||||
function NumberField(type, options) {
|
||||
this.document = options.document;
|
||||
this.type = type;
|
||||
Field.call(this, type, options);
|
||||
this.arg = new Argument();
|
||||
|
||||
this.element = dom.createElement(this.document, 'input');
|
||||
@ -6818,8 +6827,8 @@ addField(NumberField);
|
||||
* A field that uses a checkbox to toggle a boolean field
|
||||
*/
|
||||
function BooleanField(type, options) {
|
||||
this.document = options.document;
|
||||
this.type = type;
|
||||
Field.call(this, type, options);
|
||||
|
||||
this.name = options.name;
|
||||
this.named = options.named;
|
||||
|
||||
@ -6876,8 +6885,8 @@ addField(BooleanField);
|
||||
* </ul>
|
||||
*/
|
||||
function SelectionField(type, options) {
|
||||
this.document = options.document;
|
||||
this.type = type;
|
||||
Field.call(this, type, options);
|
||||
|
||||
this.items = [];
|
||||
|
||||
this.element = dom.createElement(this.document, 'select');
|
||||
@ -6944,9 +6953,7 @@ addField(SelectionField);
|
||||
* A field that allows editing of javascript
|
||||
*/
|
||||
function JavascriptField(type, options) {
|
||||
this.document = options.document;
|
||||
this.type = type;
|
||||
this.requ = options.requisition;
|
||||
Field.call(this, type, options);
|
||||
|
||||
this.onInputChange = this.onInputChange.bind(this);
|
||||
this.arg = new Argument('', '{ ', ' }');
|
||||
@ -7050,10 +7057,8 @@ addField(JavascriptField);
|
||||
* last possible time
|
||||
*/
|
||||
function DeferredField(type, options) {
|
||||
this.document = options.document;
|
||||
this.type = type;
|
||||
Field.call(this, type, options);
|
||||
this.options = options;
|
||||
this.requisition = options.requisition;
|
||||
this.requisition.assignmentChange.add(this.update, this);
|
||||
|
||||
this.element = dom.createElement(this.document, 'div');
|
||||
@ -7111,8 +7116,8 @@ addField(DeferredField);
|
||||
* BlankFields are not for general use.
|
||||
*/
|
||||
function BlankField(type, options) {
|
||||
this.document = options.document;
|
||||
this.type = type;
|
||||
Field.call(this, type, options);
|
||||
|
||||
this.element = dom.createElement(this.document, 'div');
|
||||
|
||||
this.fieldChanged = createEvent('BlankField.fieldChanged');
|
||||
@ -7139,10 +7144,8 @@ addField(BlankField);
|
||||
* given for a parameter.
|
||||
*/
|
||||
function ArrayField(type, options) {
|
||||
this.document = options.document;
|
||||
this.type = type;
|
||||
Field.call(this, type, options);
|
||||
this.options = options;
|
||||
this.requ = options.requisition;
|
||||
|
||||
this._onAdd = this._onAdd.bind(this);
|
||||
this.members = [];
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user