mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 13:25:37 +00:00
Merge fx-team to m-c
This commit is contained in:
commit
c5ca8fd03c
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 934756 causes the Fennec build to fail unless we clobber.
|
||||
Bug 944533 requires clobber to force a Proguard refresh
|
||||
|
@ -4,6 +4,8 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#endif
|
||||
|
||||
Cu.import("resource://gre/modules/NewTabUtils.jsm");
|
||||
|
||||
/**
|
||||
* Keeps thumbnails of open web pages up-to-date.
|
||||
*/
|
||||
@ -120,6 +122,11 @@ let gBrowserThumbnails = {
|
||||
},
|
||||
|
||||
_shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
|
||||
// Capture only if it's a top site in about:newtab.
|
||||
if (!NewTabUtils.links.getLinks().some(
|
||||
(link) => link && link.url == aBrowser.currentURI.spec))
|
||||
return false;
|
||||
|
||||
// Capture only if it's the currently selected tab.
|
||||
if (aBrowser != gBrowser.selectedBrowser)
|
||||
return false;
|
||||
|
@ -1,15 +1,11 @@
|
||||
/* 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/. */
|
||||
|
||||
|
||||
.highlighter-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.highlighter-controls {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.highlighter-outline-container {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
@ -34,20 +30,23 @@
|
||||
/*
|
||||
* Node Infobar
|
||||
*/
|
||||
|
||||
.highlighter-nodeinfobar-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-positioner {
|
||||
position: absolute;
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-container[hidden] {
|
||||
.highlighter-nodeinfobar-positioner[hidden] {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-container:not([disable-transitions]),
|
||||
.highlighter-nodeinfobar-container[disable-transitions][force-transitions] {
|
||||
.highlighter-nodeinfobar-positioner:not([disable-transitions]),
|
||||
.highlighter-nodeinfobar-positioner[disable-transitions][force-transitions] {
|
||||
transition-property: transform, opacity, top, left;
|
||||
transition-duration: 0.1s;
|
||||
transition-timing-function: linear;
|
||||
@ -60,19 +59,6 @@
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-button > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-container:not([locked]):not(:hover) > .highlighter-nodeinfobar > .highlighter-nodeinfobar-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-container[locked] > .highlighter-nodeinfobar,
|
||||
.highlighter-nodeinfobar-container:not([locked]):hover > .highlighter-nodeinfobar {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
html|*.highlighter-nodeinfobar-id,
|
||||
html|*.highlighter-nodeinfobar-classes,
|
||||
html|*.highlighter-nodeinfobar-pseudo-classes,
|
||||
@ -86,15 +72,15 @@ html|*.highlighter-nodeinfobar-tagname {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-container[position="top"]:not([hide-arrow]) > .highlighter-nodeinfobar-arrow-bottom {
|
||||
.highlighter-nodeinfobar-positioner[position="top"]:not([hide-arrow]) > .highlighter-nodeinfobar-arrow-bottom {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-container[position="bottom"]:not([hide-arrow]) > .highlighter-nodeinfobar-arrow-top {
|
||||
.highlighter-nodeinfobar-positioner[position="bottom"]:not([hide-arrow]) > .highlighter-nodeinfobar-arrow-top {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-container[disabled] {
|
||||
.highlighter-nodeinfobar-positioner[disabled] {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
@ -1106,6 +1106,10 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
|
||||
CustomizableUI.removeWidgetFromArea(aDraggedItemId);
|
||||
// Special widgets are removed outright, we can return here:
|
||||
if (CustomizableUI.isSpecialWidget(aDraggedItemId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
draggedItem = draggedItem.parentNode;
|
||||
|
||||
|
@ -59,4 +59,5 @@ skip-if = os == "linux"
|
||||
[browser_947987_removable_default.js]
|
||||
[browser_948985_non_removable_defaultArea.js]
|
||||
[browser_952963_areaType_getter_no_area.js]
|
||||
[browser_956602_remove_special_widget.js]
|
||||
[browser_panel_toggle.js]
|
||||
|
@ -0,0 +1,31 @@
|
||||
/* 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";
|
||||
|
||||
|
||||
// Adding a separator and then dragging it out of the navbar shouldn't throw
|
||||
add_task(function() {
|
||||
try {
|
||||
let navbar = document.getElementById("nav-bar");
|
||||
let separatorSelector = "toolbarseparator[id^=customizableui-special-separator]";
|
||||
ok(!navbar.querySelector(separatorSelector), "Shouldn't be a separator in the navbar");
|
||||
CustomizableUI.addWidgetToArea('separator', 'nav-bar');
|
||||
yield startCustomizing();
|
||||
let separator = navbar.querySelector(separatorSelector);
|
||||
ok(separator, "There should be a separator in the navbar now.");
|
||||
let palette = document.getElementById("customization-palette");
|
||||
simulateItemDrag(separator, palette);
|
||||
ok(!palette.querySelector(separatorSelector), "No separator in the palette.");
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
ok(false, "Shouldn't throw an exception moving an item to the navbar.");
|
||||
} finally {
|
||||
yield endCustomizing();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
resetCustomization();
|
||||
});
|
@ -583,25 +583,6 @@ nsBrowserContentHandler.prototype = {
|
||||
|
||||
overridePage = overridePage.replace("%OLD_VERSION%", old_mstone);
|
||||
break;
|
||||
|
||||
// Temporary case for Australis whatsnew
|
||||
case OVERRIDE_NEW_BUILD_ID:
|
||||
let locale = "en-US";
|
||||
try {
|
||||
locale = Services.prefs.getCharPref("general.useragent.locale");
|
||||
} catch (e) {}
|
||||
|
||||
let showedAustralisWhatsNew = false;
|
||||
try {
|
||||
showedAustralisWhatsNew = Services.prefs.getBoolPref("browser.showedAustralisWhatsNew");
|
||||
} catch(e) {}
|
||||
|
||||
// Show the Australis whatsnew page for en-US if we haven't yet shown it
|
||||
if (!showedAustralisWhatsNew && locale == "en-US") {
|
||||
Services.prefs.setBoolPref("browser.showedAustralisWhatsNew", true);
|
||||
overridePage = "https://www.mozilla.org/en-US/firefox/29.0a1/whatsnew/";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (ex) {}
|
||||
|
@ -87,6 +87,10 @@
|
||||
this._addedObserver = true;
|
||||
|
||||
this.searchService.init((function search_init_cb(aStatus) {
|
||||
// Bail out if the binding's been destroyed
|
||||
if (this._destroyed)
|
||||
return;
|
||||
|
||||
if (Components.isSuccessCode(aStatus)) {
|
||||
// Refresh the display (updating icon, etc)
|
||||
this.updateDisplay();
|
||||
@ -97,6 +101,8 @@
|
||||
]]></constructor>
|
||||
|
||||
<destructor><![CDATA[
|
||||
this._destroyed = true;
|
||||
|
||||
if (this._addedObserver) {
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
|
@ -132,7 +132,7 @@ let UI = {
|
||||
}
|
||||
}
|
||||
if (!icon)
|
||||
return null;
|
||||
return "chrome://browser/skin/devtools/app-manager/default-app-icon.png";
|
||||
if (project.type == "hosted") {
|
||||
let manifestURL = Services.io.newURI(project.location, null, null);
|
||||
let origin = Services.io.newURI(manifestURL.prePath, null, null);
|
||||
|
@ -31,11 +31,12 @@ ToolbarView.prototype = {
|
||||
dumpn("Initializing the ToolbarView");
|
||||
|
||||
this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
|
||||
this._resumeOrderPanel = document.getElementById("resumption-order-panel");
|
||||
this._resumeButton = document.getElementById("resume");
|
||||
this._stepOverButton = document.getElementById("step-over");
|
||||
this._stepInButton = document.getElementById("step-in");
|
||||
this._stepOutButton = document.getElementById("step-out");
|
||||
this._resumeOrderTooltip = new Tooltip(document);
|
||||
this._resumeOrderTooltip.defaultPosition = TOOLBAR_ORDER_POPUP_POSITION;
|
||||
|
||||
let resumeKey = ShortcutUtils.prettifyShortcut(document.getElementById("resumeKey"));
|
||||
let stepOverKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOverKey"));
|
||||
@ -80,10 +81,8 @@ ToolbarView.prototype = {
|
||||
*/
|
||||
showResumeWarning: function(aPausedUrl) {
|
||||
let label = L10N.getFormatStr("resumptionOrderPanelTitle", aPausedUrl);
|
||||
let descriptionNode = document.getElementById("resumption-panel-desc");
|
||||
descriptionNode.setAttribute("value", label);
|
||||
|
||||
this._resumeOrderPanel.openPopup(this._resumeButton);
|
||||
this._resumeOrderTooltip.setTextContent([label]);
|
||||
this._resumeOrderTooltip.show(this._resumeButton);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -163,11 +162,11 @@ ToolbarView.prototype = {
|
||||
},
|
||||
|
||||
_instrumentsPaneToggleButton: null,
|
||||
_resumeOrderPanel: null,
|
||||
_resumeButton: null,
|
||||
_stepOverButton: null,
|
||||
_stepInButton: null,
|
||||
_stepOutButton: null,
|
||||
_resumeOrderTooltip: null,
|
||||
_resumeTooltip: "",
|
||||
_pauseTooltip: "",
|
||||
_stepOverTooltip: "",
|
||||
|
@ -28,6 +28,7 @@ const SEARCH_LINE_FLAG = ":";
|
||||
const SEARCH_VARIABLE_FLAG = "*";
|
||||
const EDITOR_VARIABLE_HOVER_DELAY = 350; // ms
|
||||
const EDITOR_VARIABLE_POPUP_POSITION = "topcenter bottomleft";
|
||||
const TOOLBAR_ORDER_POPUP_POSITION = "topcenter bottomleft";
|
||||
|
||||
/**
|
||||
* Object defining the debugger view components.
|
||||
|
@ -513,15 +513,4 @@
|
||||
</vbox>
|
||||
</panel>
|
||||
|
||||
<panel id="resumption-order-panel"
|
||||
type="arrow"
|
||||
position="before_start"
|
||||
noautofocus="true"
|
||||
consumeoutsideclicks="false">
|
||||
<hbox align="start">
|
||||
<image class="alert-icon"/>
|
||||
<label id="resumption-panel-desc" class="description"/>
|
||||
</hbox>
|
||||
</panel>
|
||||
|
||||
</window>
|
||||
|
@ -20,12 +20,8 @@ FontInspector.prototype = {
|
||||
init: function FI_init() {
|
||||
this.update = this.update.bind(this);
|
||||
this.onNewNode = this.onNewNode.bind(this);
|
||||
this.onHighlighterLocked = this.onHighlighterLocked.bind(this);
|
||||
this.inspector.selection.on("new-node", this.onNewNode);
|
||||
this.inspector.sidebar.on("fontinspector-selected", this.onNewNode);
|
||||
if (this.inspector.highlighter) {
|
||||
this.inspector.highlighter.on("locked", this.onHighlighterLocked);
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
|
||||
@ -44,9 +40,6 @@ FontInspector.prototype = {
|
||||
this.chromeDoc = null;
|
||||
this.inspector.sidebar.off("layoutview-selected", this.onNewNode);
|
||||
this.inspector.selection.off("new-node", this.onNewNode);
|
||||
if (this.inspector.highlighter) {
|
||||
this.inspector.highlighter.off("locked", this.onHighlighterLocked);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -56,8 +49,7 @@ FontInspector.prototype = {
|
||||
if (this.isActive() &&
|
||||
this.inspector.selection.isLocal() &&
|
||||
this.inspector.selection.isConnected() &&
|
||||
this.inspector.selection.isElementNode() &&
|
||||
this.inspector.selection.reason != "highlighter") {
|
||||
this.inspector.selection.isElementNode()) {
|
||||
this.undim();
|
||||
this.update();
|
||||
} else {
|
||||
@ -65,14 +57,6 @@ FontInspector.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlighter 'locked' event handler
|
||||
*/
|
||||
onHighlighterLocked: function FI_onHighlighterLocked() {
|
||||
this.undim();
|
||||
this.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the font list. No node are selected.
|
||||
*/
|
||||
@ -210,7 +194,7 @@ FontInspector.prototype = {
|
||||
!this.inspector.selection.isElementNode()) {
|
||||
return;
|
||||
}
|
||||
let node = this.inspector.selection.node;
|
||||
let node = this.inspector.selection.nodeFront;
|
||||
let contentDocument = node.ownerDocument;
|
||||
let root = contentDocument.documentElement;
|
||||
if (contentDocument.body) {
|
||||
|
@ -17,6 +17,7 @@ function test()
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
doc = content.document;
|
||||
node = doc.querySelector("h1");
|
||||
waitForFocus(setupKeyBindingsTest, content);
|
||||
}, true);
|
||||
|
||||
@ -63,24 +64,37 @@ function test()
|
||||
keysetMap.inspector.synthesizeKey();
|
||||
}
|
||||
|
||||
function moveMouseOver(aElement, aInspector, cb)
|
||||
{
|
||||
EventUtils.synthesizeMouse(aElement, 2, 2, {type: "mousemove"},
|
||||
aElement.ownerDocument.defaultView);
|
||||
aInspector.toolbox.once("picker-node-hovered", () => {
|
||||
executeSoon(cb);
|
||||
});
|
||||
}
|
||||
|
||||
function isHighlighting()
|
||||
{
|
||||
let outline = gBrowser.selectedBrowser.parentNode
|
||||
.querySelector(".highlighter-container .highlighter-outline");
|
||||
return outline && !outline.hasAttribute("hidden");
|
||||
}
|
||||
|
||||
function inspectorShouldBeOpenAndHighlighting(aInspector, aToolbox)
|
||||
{
|
||||
is (aToolbox.currentToolId, "inspector", "Correct tool has been loaded");
|
||||
is (aInspector.highlighter.locked, true, "Highlighter should be locked");
|
||||
|
||||
aInspector.highlighter.once("unlocked", () => {
|
||||
is (aInspector.highlighter.locked, false, "Highlighter should be unlocked");
|
||||
aToolbox.once("picker-started", () => {
|
||||
ok(true, "picker-started event received, highlighter started");
|
||||
keysetMap.inspector.synthesizeKey();
|
||||
is (aInspector.highlighter.locked, true, "Highlighter should be locked");
|
||||
keysetMap.inspector.synthesizeKey();
|
||||
is (aInspector.highlighter.locked, false, "Highlighter should be unlocked");
|
||||
keysetMap.inspector.synthesizeKey();
|
||||
is (aInspector.highlighter.locked, true, "Highlighter should be locked");
|
||||
|
||||
aToolbox.once("webconsole-ready", (e, panel) => {
|
||||
webconsoleShouldBeSelected(aToolbox, panel);
|
||||
aToolbox.once("picker-stopped", () => {
|
||||
ok(true, "picker-stopped event received, highlighter stopped");
|
||||
aToolbox.once("webconsole-ready", (e, panel) => {
|
||||
webconsoleShouldBeSelected(aToolbox, panel);
|
||||
});
|
||||
keysetMap.webconsole.synthesizeKey();
|
||||
});
|
||||
keysetMap.webconsole.synthesizeKey();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,9 @@ loader.lazyGetter(this, "Requisition", () => {
|
||||
return require("gcli/cli").Requisition;
|
||||
});
|
||||
|
||||
loader.lazyGetter(this, "Selection", () => require("devtools/framework/selection").Selection);
|
||||
loader.lazyGetter(this, "InspectorFront", () => require("devtools/server/actors/inspector").InspectorFront);
|
||||
|
||||
/**
|
||||
* A "Toolbox" is the component that holds all the tools for one specific
|
||||
* target. Visually, it's a document that includes the tools tabs and all
|
||||
@ -183,6 +186,42 @@ Toolbox.prototype = {
|
||||
return parseFloat(Services.prefs.getCharPref(ZOOM_PREF));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toolbox highlighter front. Note that it may not always have been
|
||||
* initialized first. Use `initInspector()` if needed.
|
||||
*/
|
||||
get highlighter() {
|
||||
if (this.isRemoteHighlightable) {
|
||||
return this._highlighter;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toolbox's inspector front. Note that it may not always have been
|
||||
* initialized first. Use `initInspector()` if needed.
|
||||
*/
|
||||
get inspector() {
|
||||
return this._inspector;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toolbox's walker front. Note that it may not always have been
|
||||
* initialized first. Use `initInspector()` if needed.
|
||||
*/
|
||||
get walker() {
|
||||
return this._walker;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toolbox's node selection. Note that it may not always have been
|
||||
* initialized first. Use `initInspector()` if needed.
|
||||
*/
|
||||
get selection() {
|
||||
return this._selection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toggled state of the split console
|
||||
*/
|
||||
@ -214,11 +253,14 @@ Toolbox.prototype = {
|
||||
this._addZoomKeys();
|
||||
this._loadInitialZoom();
|
||||
|
||||
this._telemetry.toolOpened("toolbox");
|
||||
// Load the toolbox-level actor fronts and utilities now
|
||||
this._target.makeRemote().then(() => {
|
||||
this._telemetry.toolOpened("toolbox");
|
||||
|
||||
this.selectTool(this._defaultToolId).then(panel => {
|
||||
this.emit("ready");
|
||||
deferred.resolve();
|
||||
this.selectTool(this._defaultToolId).then(panel => {
|
||||
this.emit("ready");
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -415,7 +457,6 @@ Toolbox.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Handle any custom key events. Returns true if there was a custom key binding run
|
||||
* @param {string} toolId
|
||||
@ -484,9 +525,11 @@ Toolbox.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Add buttons to the UI as specified in the devtools.window.toolbarSpec pref
|
||||
* Add buttons to the UI as specified in the devtools.toolbox.toolbarSpec pref
|
||||
*/
|
||||
_buildButtons: function() {
|
||||
this._buildPickerButton();
|
||||
|
||||
if (!this.target.isLocalTab) {
|
||||
return;
|
||||
}
|
||||
@ -500,6 +543,23 @@ Toolbox.prototype = {
|
||||
buttons.forEach(container.appendChild.bind(container));
|
||||
},
|
||||
|
||||
/**
|
||||
* Adding the element picker button is done here unlike the other buttons
|
||||
* since we want it to work for remote targets too
|
||||
*/
|
||||
_buildPickerButton: function() {
|
||||
this._pickerButton = this.doc.createElement("toolbarbutton");
|
||||
this._pickerButton.id = "command-button-pick";
|
||||
this._pickerButton.className = "command-button";
|
||||
this._pickerButton.setAttribute("tooltiptext", toolboxStrings("pickButton.tooltip"));
|
||||
|
||||
let container = this.doc.querySelector("#toolbox-buttons");
|
||||
container.appendChild(this._pickerButton);
|
||||
|
||||
this.togglePicker = this.togglePicker.bind(this);
|
||||
this._pickerButton.addEventListener("command", this.togglePicker, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Build a tab for one tool definition and add to the toolbox
|
||||
*
|
||||
@ -600,6 +660,12 @@ Toolbox.prototype = {
|
||||
* The id of the tool to load.
|
||||
*/
|
||||
loadTool: function(id) {
|
||||
if (id === "inspector" && !this._inspector) {
|
||||
return this.initInspector().then(() => {
|
||||
return this.loadTool(id);
|
||||
});
|
||||
}
|
||||
|
||||
let deferred = promise.defer();
|
||||
let iframe = this.doc.getElementById("toolbox-panel-iframe-" + id);
|
||||
|
||||
@ -957,6 +1023,154 @@ Toolbox.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the inspector/walker/selection/highlighter fronts.
|
||||
* Returns a promise that resolves when the fronts are initialized
|
||||
*/
|
||||
initInspector: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
if (!this._inspector) {
|
||||
this._inspector = InspectorFront(this._target.client, this._target.form);
|
||||
this._inspector.getWalker().then(walker => {
|
||||
this._walker = walker;
|
||||
this._selection = new Selection(this._walker);
|
||||
if (this.isRemoteHighlightable) {
|
||||
this._inspector.getHighlighter().then(highlighter => {
|
||||
this._highlighter = highlighter;
|
||||
deferred.resolve();
|
||||
});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the inspector/walker/selection fronts
|
||||
* Returns a promise that resolves when the fronts are destroyed
|
||||
*/
|
||||
destroyInspector: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
if (this._inspector) {
|
||||
this._selection.destroy();
|
||||
this._selection = null;
|
||||
this._walker.release().then(
|
||||
() => {
|
||||
this._inspector.destroy();
|
||||
this._highlighter.destroy();
|
||||
},
|
||||
(e) => {
|
||||
console.error("Walker.release() failed: " + e);
|
||||
this._inspector.destroy();
|
||||
return this._highlighter.destroy();
|
||||
}
|
||||
).then(() => {
|
||||
this._inspector = null;
|
||||
this._highlighter = null;
|
||||
this._walker = null;
|
||||
deferred.resolve();
|
||||
});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Start/stop the element picker on the debuggee target.
|
||||
*/
|
||||
togglePicker: function() {
|
||||
if (this._isPicking) {
|
||||
return this.stopPicker();
|
||||
} else {
|
||||
return this.startPicker();
|
||||
}
|
||||
},
|
||||
|
||||
get isRemoteHighlightable() {
|
||||
return this._target.client.traits.highlightable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the element picker on the debuggee target.
|
||||
* This will request the inspector actor to start listening for mouse/touch
|
||||
* events on the target to highlight the hovered/picked element.
|
||||
* Depending on the server-side capabilities, this may fire events when nodes
|
||||
* are hovered.
|
||||
* @return A promise that resolves when the picker has started
|
||||
*/
|
||||
startPicker: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let done = () => {
|
||||
this.emit("picker-started");
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
this.initInspector().then(() => {
|
||||
this._isPicking = true;
|
||||
this._pickerButton.setAttribute("checked", "true");
|
||||
|
||||
if (this.isRemoteHighlightable) {
|
||||
this.highlighter.pick().then(done);
|
||||
|
||||
this._onPickerNodeHovered = res => {
|
||||
this.emit("picker-node-hovered", res.node);
|
||||
};
|
||||
this.walker.on("picker-node-hovered", this._onPickerNodeHovered);
|
||||
|
||||
this._onPickerNodePicked = res => {
|
||||
this.selection.setNodeFront(res.node, "picker-node-picked");
|
||||
this.stopPicker();
|
||||
};
|
||||
this.walker.on("picker-node-picked", this._onPickerNodePicked);
|
||||
} else {
|
||||
this.walker.pick().then(node => {
|
||||
this.selection.setNodeFront(node, "picker-node-picked");
|
||||
this.stopPicker();
|
||||
});
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop the element picker
|
||||
* @return A promise that resolves when the picker has stopped
|
||||
*/
|
||||
stopPicker: function() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let done = () => {
|
||||
this.emit("picker-stopped");
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
this.initInspector().then(() => {
|
||||
this._isPicking = false;
|
||||
this._pickerButton.removeAttribute("checked");
|
||||
if (this.isRemoteHighlightable) {
|
||||
this.highlighter.cancelPick().then(done);
|
||||
this.walker.off("picker-node-hovered", this._onPickerNodeHovered);
|
||||
this.walker.off("picker-node-picked", this._onPickerNodePicked);
|
||||
} else {
|
||||
this.walker.cancelPick().then(done);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toolbox's notification box
|
||||
*
|
||||
@ -1004,6 +1218,12 @@ Toolbox.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
// Destroying the walker and inspector fronts
|
||||
outstanding.push(this.destroyInspector());
|
||||
|
||||
// Removing buttons
|
||||
this._pickerButton.removeEventListener("command", this.togglePicker, false);
|
||||
this._pickerButton = null;
|
||||
let container = this.doc.getElementById("toolbox-buttons");
|
||||
while (container.firstChild) {
|
||||
container.removeChild(container.firstChild);
|
||||
|
@ -1,877 +0,0 @@
|
||||
/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
const {Cu, Cc, Ci} = require("chrome");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
|
||||
// add ":visited" and ":link" after bug 713106 is fixed
|
||||
|
||||
exports._forceBasic = {value: false};
|
||||
|
||||
exports.Highlighter = function Highlighter(aTarget, aInspector, aToolbox) {
|
||||
if (aTarget.isLocalTab && !exports._forceBasic.value) {
|
||||
return new LocalHighlighter(aTarget, aInspector, aToolbox);
|
||||
} else {
|
||||
return new BasicHighlighter(aTarget, aInspector, aToolbox);
|
||||
}
|
||||
}
|
||||
|
||||
exports.LocalHighlighter = LocalHighlighter;
|
||||
exports.BasicHighlighter = BasicHighlighter;
|
||||
|
||||
/**
|
||||
* A highlighter mechanism.
|
||||
*
|
||||
* The highlighter is built dynamically into the browser element.
|
||||
* The caller is in charge of destroying the highlighter (ie, the highlighter
|
||||
* won't be destroyed if a new tab is selected for example).
|
||||
*
|
||||
* API:
|
||||
*
|
||||
* // Constructor and destructor.
|
||||
* Highlighter(aTab, aInspector)
|
||||
* void destroy();
|
||||
*
|
||||
* // Show and hide the highlighter
|
||||
* void show();
|
||||
* void hide();
|
||||
* boolean isHidden();
|
||||
*
|
||||
* // Redraw the highlighter if the visible portion of the node has changed.
|
||||
* void invalidateSize(aScroll);
|
||||
*
|
||||
* Events:
|
||||
*
|
||||
* "closed" - Highlighter is closing
|
||||
* "highlighting" - Highlighter is highlighting
|
||||
* "locked" - The selected node has been locked
|
||||
* "unlocked" - The selected ndoe has been unlocked
|
||||
*
|
||||
* Structure:
|
||||
* <stack class="highlighter-container">
|
||||
* <box class="highlighter-outline-container">
|
||||
* <box class="highlighter-outline" locked="true/false"/>
|
||||
* </box>
|
||||
* <box class="highlighter-controls">
|
||||
* <box class="highlighter-nodeinfobar-container" position="top/bottom" locked="true/false">
|
||||
* <box class="highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-top"/>
|
||||
* <hbox class="highlighter-nodeinfobar">
|
||||
* <toolbarbutton class="highlighter-nodeinfobar-inspectbutton highlighter-nodeinfobar-button"/>
|
||||
* <hbox class="highlighter-nodeinfobar-text">tagname#id.class1.class2</hbox>
|
||||
* <toolbarbutton class="highlighter-nodeinfobar-menu highlighter-nodeinfobar-button">…</toolbarbutton>
|
||||
* </hbox>
|
||||
* <box class="highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-bottom"/>
|
||||
* </box>
|
||||
* </box>
|
||||
* </stack>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param aTarget The inspection target.
|
||||
* @param aInspector Inspector panel.
|
||||
* @param aToolbox The toolbox holding the inspector.
|
||||
*/
|
||||
function LocalHighlighter(aTarget, aInspector, aToolbox)
|
||||
{
|
||||
this.target = aTarget;
|
||||
this.tab = aTarget.tab;
|
||||
this.toolbox = aToolbox;
|
||||
this.browser = this.tab.linkedBrowser;
|
||||
this.chromeDoc = this.tab.ownerDocument;
|
||||
this.chromeWin = this.chromeDoc.defaultView;
|
||||
this.inspector = aInspector
|
||||
this.layoutHelpers = new LayoutHelpers(this.browser.contentWindow);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
LocalHighlighter.prototype = {
|
||||
get selection() {
|
||||
return this.inspector.selection;
|
||||
},
|
||||
|
||||
_init: function LocalHighlighter__init()
|
||||
{
|
||||
this.toggleLockState = this.toggleLockState.bind(this);
|
||||
this.unlockAndFocus = this.unlockAndFocus.bind(this);
|
||||
this.updateInfobar = this.updateInfobar.bind(this);
|
||||
this.highlight = this.highlight.bind(this);
|
||||
|
||||
let stack = this.browser.parentNode;
|
||||
this.win = this.browser.contentWindow;
|
||||
this._highlighting = false;
|
||||
|
||||
this.highlighterContainer = this.chromeDoc.createElement("stack");
|
||||
this.highlighterContainer.className = "highlighter-container";
|
||||
|
||||
this.outline = this.chromeDoc.createElement("box");
|
||||
this.outline.className = "highlighter-outline";
|
||||
|
||||
let outlineContainer = this.chromeDoc.createElement("box");
|
||||
outlineContainer.appendChild(this.outline);
|
||||
outlineContainer.className = "highlighter-outline-container";
|
||||
|
||||
// The controlsBox will host the different interactive
|
||||
// elements of the highlighter (buttons, toolbars, ...).
|
||||
let controlsBox = this.chromeDoc.createElement("box");
|
||||
controlsBox.className = "highlighter-controls";
|
||||
this.highlighterContainer.appendChild(outlineContainer);
|
||||
this.highlighterContainer.appendChild(controlsBox);
|
||||
|
||||
// Insert the highlighter right after the browser
|
||||
stack.insertBefore(this.highlighterContainer, stack.childNodes[1]);
|
||||
|
||||
this.buildInfobar(controlsBox);
|
||||
|
||||
this.transitionDisabler = null;
|
||||
this.pageEventsMuter = null;
|
||||
|
||||
this.selection.on("new-node", this.highlight);
|
||||
this.selection.on("new-node", this.updateInfobar);
|
||||
this.selection.on("pseudoclass", this.updateInfobar);
|
||||
this.selection.on("attribute-changed", this.updateInfobar);
|
||||
|
||||
this.onToolSelected = function(event, id) {
|
||||
if (id != "inspector") {
|
||||
this.chromeWin.clearTimeout(this.pageEventsMuter);
|
||||
this.detachMouseListeners();
|
||||
this.disabled = true;
|
||||
this.hide();
|
||||
} else {
|
||||
if (!this.locked) {
|
||||
this.attachMouseListeners();
|
||||
}
|
||||
this.disabled = false;
|
||||
this.show();
|
||||
}
|
||||
}.bind(this);
|
||||
this.toolbox.on("select", this.onToolSelected);
|
||||
|
||||
this.hidden = true;
|
||||
this.highlight();
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the nodes. Remove listeners.
|
||||
*/
|
||||
destroy: function LocalHighlighter_destroy()
|
||||
{
|
||||
this.inspectButton.removeEventListener("command", this.unlockAndFocus);
|
||||
this.inspectButton = null;
|
||||
|
||||
this.toolbox.off("select", this.onToolSelected);
|
||||
this.toolbox = null;
|
||||
|
||||
this.selection.off("new-node", this.highlight);
|
||||
this.selection.off("new-node", this.updateInfobar);
|
||||
this.selection.off("pseudoclass", this.updateInfobar);
|
||||
this.selection.off("attribute-changed", this.updateInfobar);
|
||||
|
||||
this.detachMouseListeners();
|
||||
this.detachPageListeners();
|
||||
|
||||
this.chromeWin.clearTimeout(this.transitionDisabler);
|
||||
this.chromeWin.clearTimeout(this.pageEventsMuter);
|
||||
this.boundCloseEventHandler = null;
|
||||
this._contentRect = null;
|
||||
this._highlightRect = null;
|
||||
this._highlighting = false;
|
||||
this.outline = null;
|
||||
this.nodeInfo = null;
|
||||
this.highlighterContainer.parentNode.removeChild(this.highlighterContainer);
|
||||
this.highlighterContainer = null;
|
||||
this.win = null
|
||||
this.browser = null;
|
||||
this.chromeDoc = null;
|
||||
this.chromeWin = null;
|
||||
this.tabbrowser = null;
|
||||
|
||||
this.emit("closed");
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the outline, and select a node.
|
||||
*/
|
||||
highlight: function LocalHighlighter_highlight()
|
||||
{
|
||||
if (this.selection.reason != "highlighter") {
|
||||
this.lock();
|
||||
}
|
||||
|
||||
let canHighlightNode = this.selection.isNode() &&
|
||||
this.selection.isConnected() &&
|
||||
this.selection.isElementNode();
|
||||
|
||||
if (canHighlightNode) {
|
||||
if (this.selection.reason != "navigateaway") {
|
||||
this.disabled = false;
|
||||
}
|
||||
this.show();
|
||||
this.updateInfobar();
|
||||
this.invalidateSize();
|
||||
if (!this._highlighting &&
|
||||
this.selection.reason != "highlighter") {
|
||||
this.layoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
|
||||
}
|
||||
} else {
|
||||
this.disabled = true;
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the highlighter size and position.
|
||||
*/
|
||||
invalidateSize: function LocalHighlighter_invalidateSize()
|
||||
{
|
||||
let canHiglightNode = this.selection.isNode() &&
|
||||
this.selection.isConnected() &&
|
||||
this.selection.isElementNode();
|
||||
|
||||
if (!canHiglightNode)
|
||||
return;
|
||||
|
||||
// The highlighter runs locally while the selection runs remotely,
|
||||
// so we can't quite trust the selection's isConnected to protect us
|
||||
// here, do the check manually.
|
||||
if (!this.selection.node ||
|
||||
!this.selection.node.ownerDocument ||
|
||||
!this.selection.node.ownerDocument.defaultView) {
|
||||
return;
|
||||
}
|
||||
|
||||
let clientRect = this.selection.node.getBoundingClientRect();
|
||||
let rect = this.layoutHelpers.getDirtyRect(this.selection.node);
|
||||
this.highlightRectangle(rect);
|
||||
|
||||
this.moveInfobar();
|
||||
|
||||
if (this._highlighting) {
|
||||
this.showOutline();
|
||||
this.emit("highlighting");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the highlighter if it has been hidden.
|
||||
*/
|
||||
show: function() {
|
||||
if (!this.hidden || this.disabled) return;
|
||||
this.showOutline();
|
||||
this.showInfobar();
|
||||
this.computeZoomFactor();
|
||||
this.attachPageListeners();
|
||||
this.invalidateSize();
|
||||
this.hidden = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the highlighter, the outline and the infobar.
|
||||
*/
|
||||
hide: function() {
|
||||
if (this.hidden) return;
|
||||
this.hideOutline();
|
||||
this.hideInfobar();
|
||||
this.detachPageListeners();
|
||||
this.hidden = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Is the highlighter visible?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
isHidden: function() {
|
||||
return this.hidden;
|
||||
},
|
||||
|
||||
/**
|
||||
* Lock a node. Stops the inspection.
|
||||
*/
|
||||
lock: function() {
|
||||
if (this.locked === true) return;
|
||||
this.outline.setAttribute("locked", "true");
|
||||
this.nodeInfo.container.setAttribute("locked", "true");
|
||||
this.detachMouseListeners();
|
||||
this.locked = true;
|
||||
this.emit("locked");
|
||||
},
|
||||
|
||||
/**
|
||||
* Start inspecting.
|
||||
* Unlock the current node (if any), and select any node being hovered.
|
||||
*/
|
||||
unlock: function() {
|
||||
if (this.locked === false) return;
|
||||
this.outline.removeAttribute("locked");
|
||||
this.nodeInfo.container.removeAttribute("locked");
|
||||
this.attachMouseListeners();
|
||||
this.locked = false;
|
||||
if (this.selection.isElementNode() &&
|
||||
this.selection.isConnected()) {
|
||||
this.showOutline();
|
||||
}
|
||||
this.emit("unlocked");
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle between locked and unlocked
|
||||
*/
|
||||
toggleLockState: function() {
|
||||
if (this.locked) {
|
||||
this.startNode = this.selection.node;
|
||||
this.unlockAndFocus();
|
||||
} else {
|
||||
this.selection.setNode(this.startNode);
|
||||
this.lock();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the browser before unlocking.
|
||||
*/
|
||||
unlockAndFocus: function LocalHighlighter_unlockAndFocus() {
|
||||
if (this.locked === false) return;
|
||||
this.chromeWin.focus();
|
||||
this.unlock();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the infobar
|
||||
*/
|
||||
hideInfobar: function LocalHighlighter_hideInfobar() {
|
||||
this.nodeInfo.container.setAttribute("force-transitions", "true");
|
||||
this.nodeInfo.container.setAttribute("hidden", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the infobar
|
||||
*/
|
||||
showInfobar: function LocalHighlighter_showInfobar() {
|
||||
this.nodeInfo.container.removeAttribute("hidden");
|
||||
this.moveInfobar();
|
||||
this.nodeInfo.container.removeAttribute("force-transitions");
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the outline
|
||||
*/
|
||||
hideOutline: function LocalHighlighter_hideOutline() {
|
||||
this.outline.setAttribute("hidden", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the outline
|
||||
*/
|
||||
showOutline: function LocalHighlighter_showOutline() {
|
||||
if (this._highlighting)
|
||||
this.outline.removeAttribute("hidden");
|
||||
},
|
||||
|
||||
/**
|
||||
* Build the node Infobar.
|
||||
*
|
||||
* <box class="highlighter-nodeinfobar-container">
|
||||
* <box class="highlighter-nodeinfobar-arrow-top"/>
|
||||
* <hbox class="highlighter-nodeinfobar">
|
||||
* <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"/>
|
||||
* <hbox class="highlighter-nodeinfobar-text">
|
||||
* <xhtml:span class="highlighter-nodeinfobar-tagname"/>
|
||||
* <xhtml:span class="highlighter-nodeinfobar-id"/>
|
||||
* <xhtml:span class="highlighter-nodeinfobar-classes"/>
|
||||
* <xhtml:span class="highlighter-nodeinfobar-pseudo-classes"/>
|
||||
* </hbox>
|
||||
* <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"/>
|
||||
* </hbox>
|
||||
* <box class="highlighter-nodeinfobar-arrow-bottom"/>
|
||||
* </box>
|
||||
*
|
||||
* @param nsIDOMElement aParent
|
||||
* The container of the infobar.
|
||||
*/
|
||||
buildInfobar: function LocalHighlighter_buildInfobar(aParent)
|
||||
{
|
||||
let container = this.chromeDoc.createElement("box");
|
||||
container.className = "highlighter-nodeinfobar-container";
|
||||
container.setAttribute("position", "top");
|
||||
container.setAttribute("disabled", "true");
|
||||
|
||||
let nodeInfobar = this.chromeDoc.createElement("hbox");
|
||||
nodeInfobar.className = "highlighter-nodeinfobar";
|
||||
|
||||
let arrowBoxTop = this.chromeDoc.createElement("box");
|
||||
arrowBoxTop.className = "highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-top";
|
||||
|
||||
let arrowBoxBottom = this.chromeDoc.createElement("box");
|
||||
arrowBoxBottom.className = "highlighter-nodeinfobar-arrow highlighter-nodeinfobar-arrow-bottom";
|
||||
|
||||
let tagNameLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
||||
tagNameLabel.className = "highlighter-nodeinfobar-tagname";
|
||||
|
||||
let idLabel = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
||||
idLabel.className = "highlighter-nodeinfobar-id";
|
||||
|
||||
let classesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
||||
classesBox.className = "highlighter-nodeinfobar-classes";
|
||||
|
||||
let pseudoClassesBox = this.chromeDoc.createElementNS("http://www.w3.org/1999/xhtml", "span");
|
||||
pseudoClassesBox.className = "highlighter-nodeinfobar-pseudo-classes";
|
||||
|
||||
// Add some content to force a better boundingClientRect down below.
|
||||
pseudoClassesBox.textContent = " ";
|
||||
|
||||
// Create buttons
|
||||
|
||||
this.inspectButton = this.chromeDoc.createElement("toolbarbutton");
|
||||
this.inspectButton.className = "highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"
|
||||
let toolbarInspectButton = this.inspector.panelDoc.getElementById("inspector-inspect-toolbutton");
|
||||
this.inspectButton.setAttribute("tooltiptext", toolbarInspectButton.getAttribute("tooltiptext"));
|
||||
this.inspectButton.addEventListener("command", this.toggleLockState);
|
||||
|
||||
let nodemenu = this.chromeDoc.createElement("toolbarbutton");
|
||||
nodemenu.setAttribute("type", "menu");
|
||||
nodemenu.className = "highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"
|
||||
nodemenu.setAttribute("tooltiptext",
|
||||
this.strings.GetStringFromName("nodeMenu.tooltiptext"));
|
||||
|
||||
nodemenu.onclick = function() {
|
||||
this.inspector.showNodeMenu(nodemenu, "after_start");
|
||||
}.bind(this);
|
||||
|
||||
// <hbox class="highlighter-nodeinfobar-text"/>
|
||||
let texthbox = this.chromeDoc.createElement("hbox");
|
||||
texthbox.className = "highlighter-nodeinfobar-text";
|
||||
texthbox.setAttribute("align", "center");
|
||||
texthbox.setAttribute("flex", "1");
|
||||
|
||||
texthbox.addEventListener("mousedown", function(aEvent) {
|
||||
// On click, show the node:
|
||||
if (this.selection.isElementNode()) {
|
||||
this.layoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
|
||||
}
|
||||
}.bind(this), true);
|
||||
|
||||
texthbox.appendChild(tagNameLabel);
|
||||
texthbox.appendChild(idLabel);
|
||||
texthbox.appendChild(classesBox);
|
||||
texthbox.appendChild(pseudoClassesBox);
|
||||
|
||||
nodeInfobar.appendChild(this.inspectButton);
|
||||
nodeInfobar.appendChild(texthbox);
|
||||
nodeInfobar.appendChild(nodemenu);
|
||||
|
||||
container.appendChild(arrowBoxTop);
|
||||
container.appendChild(nodeInfobar);
|
||||
container.appendChild(arrowBoxBottom);
|
||||
|
||||
aParent.appendChild(container);
|
||||
|
||||
let barHeight = container.getBoundingClientRect().height;
|
||||
|
||||
this.nodeInfo = {
|
||||
tagNameLabel: tagNameLabel,
|
||||
idLabel: idLabel,
|
||||
classesBox: classesBox,
|
||||
pseudoClassesBox: pseudoClassesBox,
|
||||
container: container,
|
||||
barHeight: barHeight,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlight a rectangular region.
|
||||
*
|
||||
* @param object aRect
|
||||
* The rectangle region to highlight.
|
||||
* @returns boolean
|
||||
* True if the rectangle was highlighted, false otherwise.
|
||||
*/
|
||||
highlightRectangle: function LocalHighlighter_highlightRectangle(aRect)
|
||||
{
|
||||
if (!aRect) {
|
||||
this.unhighlight();
|
||||
return;
|
||||
}
|
||||
|
||||
let oldRect = this._contentRect;
|
||||
|
||||
if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
|
||||
aRect.width == oldRect.width && aRect.height == oldRect.height) {
|
||||
return; // same rectangle
|
||||
}
|
||||
|
||||
let aRectScaled = this.layoutHelpers.getZoomedRect(this.win, aRect);
|
||||
|
||||
if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
|
||||
aRectScaled.width > 0 && aRectScaled.height > 0) {
|
||||
|
||||
this.showOutline();
|
||||
|
||||
// The bottom div and the right div are flexibles (flex=1).
|
||||
// We don't need to resize them.
|
||||
let top = "top:" + aRectScaled.top + "px;";
|
||||
let left = "left:" + aRectScaled.left + "px;";
|
||||
let width = "width:" + aRectScaled.width + "px;";
|
||||
let height = "height:" + aRectScaled.height + "px;";
|
||||
this.outline.setAttribute("style", top + left + width + height);
|
||||
|
||||
this._highlighting = true;
|
||||
} else {
|
||||
this.unhighlight();
|
||||
}
|
||||
|
||||
this._contentRect = aRect; // save orig (non-scaled) rect
|
||||
this._highlightRect = aRectScaled; // and save the scaled rect.
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the highlighter surface.
|
||||
*/
|
||||
unhighlight: function LocalHighlighter_unhighlight()
|
||||
{
|
||||
this._highlighting = false;
|
||||
this.hideOutline();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update node information (tagName#id.class)
|
||||
*/
|
||||
updateInfobar: function LocalHighlighter_updateInfobar()
|
||||
{
|
||||
if (!this.selection.isElementNode()) {
|
||||
this.nodeInfo.tagNameLabel.textContent = "";
|
||||
this.nodeInfo.idLabel.textContent = "";
|
||||
this.nodeInfo.classesBox.textContent = "";
|
||||
this.nodeInfo.pseudoClassesBox.textContent = "";
|
||||
return;
|
||||
}
|
||||
|
||||
let node = this.selection.node;
|
||||
|
||||
// Tag name
|
||||
this.nodeInfo.tagNameLabel.textContent = node.tagName;
|
||||
|
||||
// ID
|
||||
this.nodeInfo.idLabel.textContent = node.id ? "#" + node.id : "";
|
||||
|
||||
// Classes
|
||||
let classes = this.nodeInfo.classesBox;
|
||||
|
||||
classes.textContent = node.classList.length ?
|
||||
"." + Array.join(node.classList, ".") : "";
|
||||
|
||||
// Pseudo-classes
|
||||
let pseudos = PSEUDO_CLASSES.filter(function(pseudo) {
|
||||
return DOMUtils.hasPseudoClassLock(node, pseudo);
|
||||
}, this);
|
||||
|
||||
let pseudoBox = this.nodeInfo.pseudoClassesBox;
|
||||
pseudoBox.textContent = pseudos.join("");
|
||||
},
|
||||
|
||||
/**
|
||||
* Move the Infobar to the right place in the highlighter.
|
||||
*/
|
||||
moveInfobar: function LocalHighlighter_moveInfobar()
|
||||
{
|
||||
if (this._highlightRect) {
|
||||
let winHeight = this.win.innerHeight * this.zoom;
|
||||
let winWidth = this.win.innerWidth * this.zoom;
|
||||
|
||||
let rect = {top: this._highlightRect.top,
|
||||
left: this._highlightRect.left,
|
||||
width: this._highlightRect.width,
|
||||
height: this._highlightRect.height};
|
||||
|
||||
rect.top = Math.max(rect.top, 0);
|
||||
rect.left = Math.max(rect.left, 0);
|
||||
rect.width = Math.max(rect.width, 0);
|
||||
rect.height = Math.max(rect.height, 0);
|
||||
|
||||
rect.top = Math.min(rect.top, winHeight);
|
||||
rect.left = Math.min(rect.left, winWidth);
|
||||
|
||||
this.nodeInfo.container.removeAttribute("disabled");
|
||||
// Can the bar be above the node?
|
||||
if (rect.top < this.nodeInfo.barHeight) {
|
||||
// No. Can we move the toolbar under the node?
|
||||
if (rect.top + rect.height +
|
||||
this.nodeInfo.barHeight > winHeight) {
|
||||
// No. Let's move it inside.
|
||||
this.nodeInfo.container.style.top = rect.top + "px";
|
||||
this.nodeInfo.container.setAttribute("position", "overlap");
|
||||
} else {
|
||||
// Yes. Let's move it under the node.
|
||||
this.nodeInfo.container.style.top = rect.top + rect.height + "px";
|
||||
this.nodeInfo.container.setAttribute("position", "bottom");
|
||||
}
|
||||
} else {
|
||||
// Yes. Let's move it on top of the node.
|
||||
this.nodeInfo.container.style.top =
|
||||
rect.top - this.nodeInfo.barHeight + "px";
|
||||
this.nodeInfo.container.setAttribute("position", "top");
|
||||
}
|
||||
|
||||
let barWidth = this.nodeInfo.container.getBoundingClientRect().width;
|
||||
let left = rect.left + rect.width / 2 - barWidth / 2;
|
||||
|
||||
// Make sure the whole infobar is visible
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
this.nodeInfo.container.setAttribute("hide-arrow", "true");
|
||||
} else {
|
||||
if (left + barWidth > winWidth) {
|
||||
left = winWidth - barWidth;
|
||||
this.nodeInfo.container.setAttribute("hide-arrow", "true");
|
||||
} else {
|
||||
this.nodeInfo.container.removeAttribute("hide-arrow");
|
||||
}
|
||||
}
|
||||
this.nodeInfo.container.style.left = left + "px";
|
||||
} else {
|
||||
this.nodeInfo.container.style.left = "0";
|
||||
this.nodeInfo.container.style.top = "0";
|
||||
this.nodeInfo.container.setAttribute("position", "top");
|
||||
this.nodeInfo.container.setAttribute("hide-arrow", "true");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Store page zoom factor.
|
||||
*/
|
||||
computeZoomFactor: function LocalHighlighter_computeZoomFactor() {
|
||||
this.zoom =
|
||||
this.win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.fullZoom;
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//// Event Handling
|
||||
|
||||
attachMouseListeners: function LocalHighlighter_attachMouseListeners()
|
||||
{
|
||||
this.browser.addEventListener("mousemove", this, true);
|
||||
this.browser.addEventListener("click", this, true);
|
||||
this.browser.addEventListener("dblclick", this, true);
|
||||
this.browser.addEventListener("mousedown", this, true);
|
||||
this.browser.addEventListener("mouseup", this, true);
|
||||
},
|
||||
|
||||
detachMouseListeners: function LocalHighlighter_detachMouseListeners()
|
||||
{
|
||||
this.browser.removeEventListener("mousemove", this, true);
|
||||
this.browser.removeEventListener("click", this, true);
|
||||
this.browser.removeEventListener("dblclick", this, true);
|
||||
this.browser.removeEventListener("mousedown", this, true);
|
||||
this.browser.removeEventListener("mouseup", this, true);
|
||||
},
|
||||
|
||||
attachPageListeners: function LocalHighlighter_attachPageListeners()
|
||||
{
|
||||
this.browser.addEventListener("resize", this, true);
|
||||
this.browser.addEventListener("scroll", this, true);
|
||||
this.browser.addEventListener("MozAfterPaint", this, true);
|
||||
},
|
||||
|
||||
detachPageListeners: function LocalHighlighter_detachPageListeners()
|
||||
{
|
||||
this.browser.removeEventListener("resize", this, true);
|
||||
this.browser.removeEventListener("scroll", this, true);
|
||||
this.browser.removeEventListener("MozAfterPaint", this, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generic event handler.
|
||||
*
|
||||
* @param nsIDOMEvent aEvent
|
||||
* The DOM event object.
|
||||
*/
|
||||
handleEvent: function LocalHighlighter_handleEvent(aEvent)
|
||||
{
|
||||
switch (aEvent.type) {
|
||||
case "click":
|
||||
this.handleClick(aEvent);
|
||||
break;
|
||||
case "mousemove":
|
||||
this.brieflyIgnorePageEvents();
|
||||
this.handleMouseMove(aEvent);
|
||||
break;
|
||||
case "resize":
|
||||
this.computeZoomFactor();
|
||||
break;
|
||||
case "MozAfterPaint":
|
||||
case "scroll":
|
||||
this.brieflyDisableTransitions();
|
||||
this.invalidateSize();
|
||||
break;
|
||||
case "dblclick":
|
||||
case "mousedown":
|
||||
case "mouseup":
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable the CSS transitions for a short time to avoid laggy animations
|
||||
* during scrolling or resizing.
|
||||
*/
|
||||
brieflyDisableTransitions: function LocalHighlighter_brieflyDisableTransitions()
|
||||
{
|
||||
if (this.transitionDisabler) {
|
||||
this.chromeWin.clearTimeout(this.transitionDisabler);
|
||||
} else {
|
||||
this.outline.setAttribute("disable-transitions", "true");
|
||||
this.nodeInfo.container.setAttribute("disable-transitions", "true");
|
||||
}
|
||||
this.transitionDisabler =
|
||||
this.chromeWin.setTimeout(function() {
|
||||
this.outline.removeAttribute("disable-transitions");
|
||||
this.nodeInfo.container.removeAttribute("disable-transitions");
|
||||
this.transitionDisabler = null;
|
||||
}.bind(this), 500);
|
||||
},
|
||||
|
||||
/**
|
||||
* Don't listen to page events while inspecting with the mouse.
|
||||
*/
|
||||
brieflyIgnorePageEvents: function LocalHighlighter_brieflyIgnorePageEvents()
|
||||
{
|
||||
// The goal is to keep smooth animations while inspecting.
|
||||
// CSS Transitions might be interrupted because of a MozAfterPaint
|
||||
// event that would triger an invalidateSize() call.
|
||||
// So we don't listen to events that would trigger an invalidateSize()
|
||||
// call.
|
||||
//
|
||||
// Side effect, zoom levels are not updated during this short period.
|
||||
// It's very unlikely this would happen, but just in case, we call
|
||||
// computeZoomFactor() when reattaching the events.
|
||||
if (this.pageEventsMuter) {
|
||||
this.chromeWin.clearTimeout(this.pageEventsMuter);
|
||||
} else {
|
||||
this.detachPageListeners();
|
||||
}
|
||||
this.pageEventsMuter =
|
||||
this.chromeWin.setTimeout(function() {
|
||||
this.attachPageListeners();
|
||||
// Just in case the zoom level changed while ignoring the paint events
|
||||
this.computeZoomFactor();
|
||||
this.pageEventsMuter = null;
|
||||
}.bind(this), 500);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle clicks.
|
||||
*
|
||||
* @param nsIDOMEvent aEvent
|
||||
* The DOM event.
|
||||
*/
|
||||
handleClick: function LocalHighlighter_handleClick(aEvent)
|
||||
{
|
||||
// Stop inspection when the user clicks on a node.
|
||||
if (aEvent.button == 0) {
|
||||
this.lock();
|
||||
let node = this.selection.node;
|
||||
this.selection.setNode(node, "highlighter-lock");
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle mousemoves in panel.
|
||||
*
|
||||
* @param nsiDOMEvent aEvent
|
||||
* The MouseEvent triggering the method.
|
||||
*/
|
||||
handleMouseMove: function LocalHighlighter_handleMouseMove(aEvent)
|
||||
{
|
||||
let doc = aEvent.target.ownerDocument;
|
||||
|
||||
// This should never happen, but just in case, we don't let the
|
||||
// highlighter highlight browser nodes.
|
||||
if (doc && doc != this.chromeDoc) {
|
||||
let element = this.layoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
|
||||
aEvent.clientX, aEvent.clientY);
|
||||
if (element && element != this.selection.node) {
|
||||
this.selection.setNode(element, "highlighter");
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// BasicHighlighter. Doesn't implement any fancy features. Just change
|
||||
// the outline of the selected node. Works with remote target.
|
||||
|
||||
function BasicHighlighter(aTarget, aInspector)
|
||||
{
|
||||
this.walker = aInspector.walker;
|
||||
this.selection = aInspector.selection;
|
||||
this.highlight = this.highlight.bind(this);
|
||||
this.selection.on("new-node-front", this.highlight);
|
||||
EventEmitter.decorate(this);
|
||||
this.locked = true;
|
||||
}
|
||||
|
||||
BasicHighlighter.prototype = {
|
||||
destroy: function() {
|
||||
this.walker.highlight(null);
|
||||
this.selection.off("new-node-front", this.highlight);
|
||||
this.walker = null;
|
||||
this.selection = null;
|
||||
},
|
||||
toggleLockState: function() {
|
||||
this.locked = !this.locked;
|
||||
if (this.locked) {
|
||||
this.walker.cancelPick();
|
||||
} else {
|
||||
this.emit("unlocked");
|
||||
this.walker.pick().then(
|
||||
(node) => this._onPick(node),
|
||||
() => this._onPick(null)
|
||||
);
|
||||
}
|
||||
},
|
||||
highlight: function() {
|
||||
this.walker.highlight(this.selection.nodeFront);
|
||||
},
|
||||
_onPick: function(node) {
|
||||
if (node) {
|
||||
this.selection.setNodeFront(node);
|
||||
}
|
||||
this.locked = true;
|
||||
this.emit("locked");
|
||||
},
|
||||
hide: function() {},
|
||||
show: function() {},
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
|
||||
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(LocalHighlighter.prototype, "strings", function () {
|
||||
return Services.strings.createBundle(
|
||||
"chrome://browser/locale/devtools/inspector.properties");
|
||||
});
|
@ -13,20 +13,16 @@ let EventEmitter = require("devtools/shared/event-emitter");
|
||||
let {CssLogic} = require("devtools/styleinspector/css-logic");
|
||||
|
||||
loader.lazyGetter(this, "MarkupView", () => require("devtools/markupview/markup-view").MarkupView);
|
||||
loader.lazyGetter(this, "Selection", () => require("devtools/inspector/selection").Selection);
|
||||
loader.lazyGetter(this, "HTMLBreadcrumbs", () => require("devtools/inspector/breadcrumbs").HTMLBreadcrumbs);
|
||||
loader.lazyGetter(this, "Highlighter", () => require("devtools/inspector/highlighter").Highlighter);
|
||||
loader.lazyGetter(this, "ToolSidebar", () => require("devtools/framework/sidebar").ToolSidebar);
|
||||
loader.lazyGetter(this, "SelectorSearch", () => require("devtools/inspector/selector-search").SelectorSearch);
|
||||
loader.lazyGetter(this, "InspectorFront", () => require("devtools/server/actors/inspector").InspectorFront);
|
||||
|
||||
const LAYOUT_CHANGE_TIMER = 250;
|
||||
|
||||
/**
|
||||
* Represents an open instance of the Inspector for a tab.
|
||||
* The inspector controls the highlighter, the breadcrumbs,
|
||||
* the markup view, and the sidebar (computed view, rule view
|
||||
* and layout view).
|
||||
* The inspector controls the breadcrumbs, the markup view, and the sidebar
|
||||
* (computed view, rule view, font view and layout view).
|
||||
*
|
||||
* Events:
|
||||
* - ready
|
||||
@ -77,7 +73,7 @@ InspectorPanel.prototype = {
|
||||
*/
|
||||
open: function InspectorPanel_open() {
|
||||
return this.target.makeRemote().then(() => {
|
||||
return this._getWalker();
|
||||
return this._getPageStyle();
|
||||
}).then(() => {
|
||||
return this._getDefaultNodeForSelection();
|
||||
}).then(defaultSelection => {
|
||||
@ -85,21 +81,29 @@ InspectorPanel.prototype = {
|
||||
}).then(null, console.error);
|
||||
},
|
||||
|
||||
get toolbox() {
|
||||
return this._toolbox;
|
||||
},
|
||||
|
||||
get inspector() {
|
||||
if (!this._target.form) {
|
||||
throw new Error("Target.inspector requires an initialized remote actor.");
|
||||
}
|
||||
if (!this._inspector) {
|
||||
this._inspector = InspectorFront(this._target.client, this._target.form);
|
||||
}
|
||||
return this._inspector;
|
||||
return this._toolbox.inspector;
|
||||
},
|
||||
|
||||
get walker() {
|
||||
return this._toolbox.walker;
|
||||
},
|
||||
|
||||
get selection() {
|
||||
return this._toolbox.selection;
|
||||
},
|
||||
|
||||
get isOuterHTMLEditable() {
|
||||
return this._target.client.traits.editOuterHTML;
|
||||
},
|
||||
|
||||
_deferredOpen: function(defaultSelection) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
this.outerHTMLEditable = this._target.client.traits.editOuterHTML;
|
||||
|
||||
this.onNewRoot = this.onNewRoot.bind(this);
|
||||
this.walker.on("new-root", this.onNewRoot);
|
||||
|
||||
@ -110,8 +114,6 @@ InspectorPanel.prototype = {
|
||||
this.nodemenu.addEventListener("popupshowing", this._setupNodeMenu, true);
|
||||
this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
|
||||
|
||||
// Create an empty selection
|
||||
this._selection = new Selection(this.walker);
|
||||
this.onNewSelection = this.onNewSelection.bind(this);
|
||||
this.selection.on("new-node-front", this.onNewSelection);
|
||||
this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
|
||||
@ -154,19 +156,6 @@ InspectorPanel.prototype = {
|
||||
this.updateDebuggerPausedWarning();
|
||||
}
|
||||
|
||||
this.highlighter = new Highlighter(this.target, this, this._toolbox);
|
||||
let button = this.panelDoc.getElementById("inspector-inspect-toolbutton");
|
||||
this.onLockStateChanged = function() {
|
||||
if (this.highlighter.locked) {
|
||||
button.removeAttribute("checked");
|
||||
this._toolbox.raise();
|
||||
} else {
|
||||
button.setAttribute("checked", "true");
|
||||
}
|
||||
}.bind(this);
|
||||
this.highlighter.on("locked", this.onLockStateChanged);
|
||||
this.highlighter.on("unlocked", this.onLockStateChanged);
|
||||
|
||||
this._initMarkup();
|
||||
this.isReady = false;
|
||||
|
||||
@ -174,7 +163,7 @@ InspectorPanel.prototype = {
|
||||
this.isReady = true;
|
||||
|
||||
// All the components are initialized. Let's select a node.
|
||||
this._selection.setNodeFront(defaultSelection);
|
||||
this.selection.setNodeFront(defaultSelection, "inspector-open");
|
||||
|
||||
this.markup.expandNode(this.selection.nodeFront);
|
||||
|
||||
@ -195,11 +184,8 @@ InspectorPanel.prototype = {
|
||||
this.isDirty = false;
|
||||
},
|
||||
|
||||
_getWalker: function() {
|
||||
return this.inspector.getWalker().then(walker => {
|
||||
this.walker = walker;
|
||||
return this.inspector.getPageStyle();
|
||||
}).then(pageStyle => {
|
||||
_getPageStyle: function() {
|
||||
return this._toolbox.inspector.getPageStyle().then(pageStyle => {
|
||||
this.pageStyle = pageStyle;
|
||||
});
|
||||
},
|
||||
@ -238,13 +224,6 @@ InspectorPanel.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Selection object (read only)
|
||||
*/
|
||||
get selection() {
|
||||
return this._selection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Target getter.
|
||||
*/
|
||||
@ -314,7 +293,6 @@ InspectorPanel.prototype = {
|
||||
}.bind(this);
|
||||
|
||||
this.sidebar.on("select", this._setDefaultSidebar);
|
||||
this.toggleHighlighter = this.toggleHighlighter.bind(this);
|
||||
|
||||
this.sidebar.addTab("ruleview",
|
||||
"chrome://browser/content/devtools/cssruleview.xhtml",
|
||||
@ -335,8 +313,6 @@ InspectorPanel.prototype = {
|
||||
"layoutview" == defaultTab);
|
||||
|
||||
let ruleViewTab = this.sidebar.getTab("ruleview");
|
||||
ruleViewTab.addEventListener("mouseover", this.toggleHighlighter, false);
|
||||
ruleViewTab.addEventListener("mouseout", this.toggleHighlighter, false);
|
||||
|
||||
this.sidebar.show();
|
||||
},
|
||||
@ -351,14 +327,11 @@ InspectorPanel.prototype = {
|
||||
this.isDirty = false;
|
||||
|
||||
this._getDefaultNodeForSelection().then(defaultNode => {
|
||||
if (this._destroyPromise) {
|
||||
return;
|
||||
}
|
||||
this.selection.setNodeFront(defaultNode, "navigateaway");
|
||||
|
||||
this._initMarkup();
|
||||
this.once("markuploaded", () => {
|
||||
if (this._destroyPromise) {
|
||||
if (!this.markup) {
|
||||
return;
|
||||
}
|
||||
this.markup.expandNode(this.selection.nodeFront);
|
||||
@ -399,6 +372,10 @@ InspectorPanel.prototype = {
|
||||
* When a new node is selected.
|
||||
*/
|
||||
onNewSelection: function InspectorPanel_onNewSelection(event, value, reason) {
|
||||
if (reason === "selection-destroy") {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cancelLayoutChange();
|
||||
|
||||
// Wait for all the known tools to finish updating and then let the
|
||||
@ -500,34 +477,14 @@ InspectorPanel.prototype = {
|
||||
* Destroy the inspector.
|
||||
*/
|
||||
destroy: function InspectorPanel__destroy() {
|
||||
if (this._destroyPromise) {
|
||||
return this._destroyPromise;
|
||||
if (this._panelDestroyer) {
|
||||
return this._panelDestroyer.promise;
|
||||
}
|
||||
|
||||
if (this.highlighter) {
|
||||
this.highlighter.off("locked", this.onLockStateChanged);
|
||||
this.highlighter.off("unlocked", this.onLockStateChanged);
|
||||
this.highlighter.destroy();
|
||||
}
|
||||
|
||||
delete this.onLockStateChanged;
|
||||
this._panelDestroyer = promise.defer();
|
||||
|
||||
if (this.walker) {
|
||||
this.walker.off("new-root", this.onNewRoot);
|
||||
this._destroyPromise = this.walker.release()
|
||||
.then(() => this._inspector.destroy(),
|
||||
(e) => {
|
||||
console.error("Walker.release() failed: " + e);
|
||||
return this._inspector.destroy();
|
||||
})
|
||||
.then(() => {
|
||||
this._inspector = null;
|
||||
}, console.error);
|
||||
|
||||
delete this.walker;
|
||||
delete this.pageStyle;
|
||||
} else {
|
||||
this._destroyPromise = promise.resolve(null);
|
||||
this.pageStyle = null;
|
||||
}
|
||||
|
||||
this.cancelUpdate();
|
||||
@ -544,8 +501,6 @@ InspectorPanel.prototype = {
|
||||
this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
|
||||
this._toolbox.off("select", this.updateDebuggerPausedWarning);
|
||||
|
||||
this._toolbox = null;
|
||||
|
||||
this.sidebar.off("select", this._setDefaultSidebar);
|
||||
this.sidebar.destroy();
|
||||
this.sidebar = null;
|
||||
@ -554,14 +509,12 @@ InspectorPanel.prototype = {
|
||||
this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
|
||||
this.breadcrumbs.destroy();
|
||||
this.searchSuggestions.destroy();
|
||||
delete this.searchBox;
|
||||
this.searchBox = null;
|
||||
this.selection.off("new-node-front", this.onNewSelection);
|
||||
this.selection.off("before-new-node", this.onBeforeNewSelection);
|
||||
this.selection.off("before-new-node-front", this.onBeforeNewSelection);
|
||||
this.selection.off("detached-front", this.onDetached);
|
||||
this._destroyMarkup();
|
||||
this._selection.destroy();
|
||||
this._selection = null;
|
||||
this.panelWin.inspector = null;
|
||||
this.target = null;
|
||||
this.panelDoc = null;
|
||||
@ -570,9 +523,10 @@ InspectorPanel.prototype = {
|
||||
this.searchSuggestions = null;
|
||||
this.lastNodemenuItem = null;
|
||||
this.nodemenu = null;
|
||||
this.highlighter = null;
|
||||
this._toolbox = null;
|
||||
|
||||
return this._destroyPromise;
|
||||
this._panelDestroyer.resolve(null);
|
||||
return this._panelDestroyer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -633,7 +587,7 @@ InspectorPanel.prototype = {
|
||||
}
|
||||
|
||||
let editHTML = this.panelDoc.getElementById("node-menu-edithtml");
|
||||
if (this.outerHTMLEditable && selectionIsElement) {
|
||||
if (this.isOuterHTMLEditable && selectionIsElement) {
|
||||
editHTML.removeAttribute("disabled");
|
||||
} else {
|
||||
editHTML.setAttribute("disabled", "true");
|
||||
@ -686,17 +640,17 @@ InspectorPanel.prototype = {
|
||||
_destroyMarkup: function InspectorPanel__destroyMarkup() {
|
||||
if (this._boundMarkupFrameLoad) {
|
||||
this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
|
||||
delete this._boundMarkupFrameLoad;
|
||||
this._boundMarkupFrameLoad = null;
|
||||
}
|
||||
|
||||
if (this.markup) {
|
||||
this.markup.destroy();
|
||||
delete this.markup;
|
||||
this.markup = null;
|
||||
}
|
||||
|
||||
if (this._markupFrame) {
|
||||
this._markupFrame.parentNode.removeChild(this._markupFrame);
|
||||
delete this._markupFrame;
|
||||
this._markupFrame = null;
|
||||
}
|
||||
|
||||
this._markupBox = null;
|
||||
@ -709,11 +663,11 @@ InspectorPanel.prototype = {
|
||||
if (this.selection.isElementNode()) {
|
||||
let node = this.selection.nodeFront;
|
||||
if (node.hasPseudoClassLock(aPseudo)) {
|
||||
return this.walker.removePseudoClassLock(node, aPseudo, { parents: true });
|
||||
return this.walker.removePseudoClassLock(node, aPseudo, {parents: true});
|
||||
}
|
||||
|
||||
let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
|
||||
return this.walker.addPseudoClassLock(node, aPseudo, { parents: hierarchical });
|
||||
return this.walker.addPseudoClassLock(node, aPseudo, {parents: hierarchical});
|
||||
}
|
||||
},
|
||||
|
||||
@ -727,22 +681,6 @@ InspectorPanel.prototype = {
|
||||
return this.walker.clearPseudoClassLocks().then(null, console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the highlighter when ruleview is hovered.
|
||||
*/
|
||||
toggleHighlighter: function InspectorPanel_toggleHighlighter(event)
|
||||
{
|
||||
if (!this.highlighter) {
|
||||
return;
|
||||
}
|
||||
if (event.type == "mouseover") {
|
||||
this.highlighter.hide();
|
||||
}
|
||||
else if (event.type == "mouseout") {
|
||||
this.highlighter.show();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit the outerHTML of the selected Node.
|
||||
*/
|
||||
|
@ -75,10 +75,6 @@
|
||||
<toolbar id="inspector-toolbar"
|
||||
class="devtools-toolbar"
|
||||
nowindowdrag="true">
|
||||
<toolbarbutton id="inspector-inspect-toolbutton"
|
||||
tooltiptext="&inspector.selectButton.tooltip;"
|
||||
class="devtools-toolbarbutton"
|
||||
oncommand="inspector.highlighter.toggleLockState()"/>
|
||||
<arrowscrollbox id="inspector-breadcrumbs"
|
||||
class="breadcrumbs-widget-container"
|
||||
flex="1" orient="horizontal"
|
||||
|
@ -24,14 +24,12 @@ support-files =
|
||||
[browser_inspector_bug_831693_input_suggestion.js]
|
||||
# [browser_inspector_bug_831693_searchbox_panel_navigation.js]
|
||||
# Disabled for too many intermittent failures (bug 851349)
|
||||
[browser_inspector_bug_835722_infobar_reappears.js]
|
||||
[browser_inspector_bug_840156_destroy_after_navigation.js]
|
||||
[browser_inspector_changes.js]
|
||||
[browser_inspector_cmd_inspect.js]
|
||||
[browser_inspector_dead_node_exception.js]
|
||||
[browser_inspector_destroyselection.js]
|
||||
[browser_inspector_highlighter.js]
|
||||
[browser_inspector_highlighter_autohide.js]
|
||||
[browser_inspector_iframeTest.js]
|
||||
[browser_inspector_infobar.js]
|
||||
[browser_inspector_initialization.js]
|
||||
|
@ -2,13 +2,12 @@
|
||||
* 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/. */
|
||||
|
||||
|
||||
function test() {
|
||||
let inspector, doc;
|
||||
let inspector, doc, toolbox;
|
||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let {require} = devtools;
|
||||
let promise = require("sdk/core/promise");
|
||||
let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
@ -19,75 +18,67 @@ function test() {
|
||||
waitForFocus(setupTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<h1>foo<h1><h2>bar</h2>";
|
||||
content.location = "data:text/html,<h1>foo</h1><h2>bar</h2>";
|
||||
|
||||
function setupTest() {
|
||||
let h = require("devtools/inspector/highlighter");
|
||||
h._forceBasic.value = true;
|
||||
openInspector(runTests);
|
||||
openInspector((aInspector, aToolbox) => {
|
||||
toolbox = aToolbox;
|
||||
inspector = aInspector;
|
||||
inspector.selection.setNode(doc.querySelector("h2"), null);
|
||||
inspector.once("inspector-updated", runTests);
|
||||
});
|
||||
}
|
||||
|
||||
function runTests(aInspector) {
|
||||
inspector = aInspector;
|
||||
|
||||
getHighlighterOutline().setAttribute("disable-transitions", "true");
|
||||
Task.spawn(function() {
|
||||
yield selectH1();
|
||||
yield verifyH1Selected();
|
||||
yield deselect();
|
||||
yield verifyNoNodeSelected();
|
||||
|
||||
yield selectH1();
|
||||
yield verifyH1Selected();
|
||||
yield destroyInspector();
|
||||
yield verifyNoNodeSelected();
|
||||
yield hoverH1InMarkupView();
|
||||
yield assertH1Highlighted();
|
||||
yield mouseLeaveMarkupView();
|
||||
yield assertNoNodeHighlighted();
|
||||
|
||||
finishUp();
|
||||
}).then(null, Cu.reportError);
|
||||
}
|
||||
|
||||
function selectH1() {
|
||||
function hoverH1InMarkupView() {
|
||||
let deferred = promise.defer();
|
||||
let h1 = doc.querySelector("h1");
|
||||
inspector.selection.once("new-node-front", () => {
|
||||
executeSoon(deferred.resolve);
|
||||
});
|
||||
inspector.selection.setNode(h1);
|
||||
|
||||
let container = getContainerForRawNode(inspector.markup, doc.querySelector("h1"));
|
||||
EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
|
||||
inspector.markup.doc.defaultView);
|
||||
inspector.markup.once("node-highlight", deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function verifyH1Selected() {
|
||||
let h1 = doc.querySelector("h1");
|
||||
let nodes = doc.querySelectorAll(":-moz-devtools-highlighted");
|
||||
is(nodes.length, 1, "only one node selected");
|
||||
is(nodes[0], h1, "h1 selected");
|
||||
function assertH1Highlighted() {
|
||||
ok(isHighlighting(), "The highlighter is shown on a markup container hover");
|
||||
is(getHighlitNode(), doc.querySelector("h1"), "The highlighter highlights the right node");
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
function deselect() {
|
||||
function mouseLeaveMarkupView() {
|
||||
let deferred = promise.defer();
|
||||
inspector.selection.once("new-node-front", () => {
|
||||
executeSoon(deferred.resolve);
|
||||
});
|
||||
inspector.selection.setNode(null);
|
||||
|
||||
// Find another element to mouseover over in order to leave the markup-view
|
||||
let btn = toolbox.doc.querySelector(".toolbox-dock-button");
|
||||
|
||||
EventUtils.synthesizeMouse(btn, 2, 2, {type: "mousemove"},
|
||||
toolbox.doc.defaultView);
|
||||
executeSoon(deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function destroyInspector() {
|
||||
return inspector.destroy();
|
||||
}
|
||||
|
||||
function verifyNoNodeSelected() {
|
||||
is(doc.querySelectorAll(":-moz-devtools-highlighted").length, 0, "no node selected");
|
||||
function assertNoNodeHighlighted() {
|
||||
ok(!isHighlighting(), "After the mouse left the markup view, the highlighter is hidden");
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
let h = require("devtools/inspector/highlighter");
|
||||
h._forceBasic.value = false;
|
||||
inspector = doc = null;
|
||||
inspector = doc = toolbox = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -57,7 +57,10 @@ function test()
|
||||
|
||||
if (cursor >= nodes.length) {
|
||||
inspector.off("breadcrumbs-updated", nodeSelected);
|
||||
finishUp();
|
||||
// breadcrumbs-updated is an event that is fired before the rest of the
|
||||
// inspector is updated, so there'll be hanging connections if we finish
|
||||
// up before waiting for everything to end.
|
||||
inspector.once("inspector-updated", finishUp);
|
||||
} else {
|
||||
let node = nodes[cursor].node;
|
||||
inspector.selection.setNode(node);
|
||||
|
@ -1,9 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
@ -19,22 +17,18 @@ function test()
|
||||
|
||||
content.location = "data:text/html,<object style='padding: 100px'><p>foobar</p></object>";
|
||||
|
||||
function setupObjectInspectionTest()
|
||||
{
|
||||
function setupObjectInspectionTest() {
|
||||
objectNode = doc.querySelector("object");
|
||||
ok(objectNode, "we have the object node");
|
||||
openInspector(runObjectInspectionTest);
|
||||
}
|
||||
|
||||
function runObjectInspectionTest(inspector)
|
||||
{
|
||||
inspector.highlighter.once("locked", performTestComparison);
|
||||
inspector.highlighter.unlock();
|
||||
function runObjectInspectionTest(inspector) {
|
||||
inspector.once("inspector-updated", performTestComparison);
|
||||
inspector.selection.setNode(objectNode, "");
|
||||
}
|
||||
|
||||
function performTestComparison()
|
||||
{
|
||||
function performTestComparison() {
|
||||
is(getActiveInspector().selection.node, objectNode, "selection matches node");
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
executeSoon(function() {
|
||||
@ -43,7 +37,6 @@ function test()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function finishUp() {
|
||||
doc = objectNode = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
|
@ -7,6 +7,7 @@ function test()
|
||||
|
||||
let doc;
|
||||
let iframeNode, iframeBodyNode;
|
||||
let inspector;
|
||||
|
||||
let iframeSrc = "<style>" +
|
||||
"body {" +
|
||||
@ -45,57 +46,63 @@ function test()
|
||||
iframeBodyNode = iframeNode.contentDocument.querySelector("body");
|
||||
ok(iframeNode, "we have the iframe node");
|
||||
ok(iframeBodyNode, "we have the body node");
|
||||
openInspector(runTests);
|
||||
}
|
||||
|
||||
function runTests(inspector)
|
||||
{
|
||||
inspector.highlighter.unlock();
|
||||
executeSoon(function() {
|
||||
inspector.highlighter.once("highlighting", isTheIframeSelected);
|
||||
moveMouseOver(iframeNode, 1, 1);
|
||||
openInspector(aInspector => {
|
||||
inspector = aInspector;
|
||||
// Make sure the highlighter is shown so we can disable transitions
|
||||
inspector.toolbox.highlighter.showBoxModel(getNodeFront(doc.body)).then(() => {
|
||||
getHighlighterOutline().setAttribute("disable-transitions", "true");
|
||||
runTests();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function isTheIframeSelected()
|
||||
function runTests()
|
||||
{
|
||||
let inspector = getActiveInspector();
|
||||
inspector.toolbox.startPicker().then(() => {
|
||||
moveMouseOver(iframeNode, 1, 1, isTheIframeHighlighted);
|
||||
});
|
||||
}
|
||||
|
||||
function isTheIframeHighlighted()
|
||||
{
|
||||
let outlineRect = getHighlighterOutlineRect();
|
||||
let iframeRect = iframeNode.getBoundingClientRect();
|
||||
for (let dim of ["width", "height", "top", "left"]) {
|
||||
is(Math.floor(outlineRect[dim]), Math.floor(iframeRect[dim]), "Outline dimension is correct");
|
||||
}
|
||||
|
||||
is(inspector.selection.node, iframeNode, "selection matches node");
|
||||
iframeNode.style.marginBottom = doc.defaultView.innerHeight + "px";
|
||||
doc.defaultView.scrollBy(0, 40);
|
||||
|
||||
executeSoon(function() {
|
||||
inspector.selection.once("new-node", isTheIframeContentSelected);
|
||||
moveMouseOver(iframeNode, 40, 40);
|
||||
moveMouseOver(iframeNode, 40, 40, isTheIframeContentHighlighted);
|
||||
}
|
||||
|
||||
function isTheIframeContentHighlighted()
|
||||
{
|
||||
is(getHighlitNode(), iframeBodyNode, "highlighter shows the right node");
|
||||
|
||||
// 184 == 200 + 11(border) + 13(padding) - 40(scroll)
|
||||
let outlineRect = getHighlighterOutlineRect();
|
||||
is(outlineRect.height, 184, "highlighter height");
|
||||
|
||||
inspector.toolbox.stopPicker().then(() => {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
finishUp();
|
||||
});
|
||||
}
|
||||
|
||||
function isTheIframeContentSelected()
|
||||
function finishUp()
|
||||
{
|
||||
let inspector = getActiveInspector();
|
||||
is(inspector.selection.node, iframeBodyNode, "selection matches node");
|
||||
// 184 == 200 + 11(border) + 13(padding) - 40(scroll)
|
||||
is(inspector.highlighter._highlightRect.height, 184,
|
||||
"highlighter height");
|
||||
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
finishUp();
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
doc = iframeNode = iframeBodyNode = null;
|
||||
doc = inspector = iframeNode = iframeBodyNode = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
function moveMouseOver(aElement, x, y)
|
||||
function moveMouseOver(aElement, x, y, cb)
|
||||
{
|
||||
EventUtils.synthesizeMouse(aElement, x, y, {type: "mousemove"},
|
||||
aElement.ownerDocument.defaultView);
|
||||
inspector.toolbox.once("picker-node-hovered", cb);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -8,21 +8,37 @@ function test() {
|
||||
let inspector;
|
||||
|
||||
function startTest() {
|
||||
openInspector(runInspectorTests);
|
||||
openInspector(aInspector => {
|
||||
inspector = aInspector;
|
||||
runInspectorTests();
|
||||
});
|
||||
}
|
||||
|
||||
function runInspectorTests(aInspector) {
|
||||
inspector = aInspector;
|
||||
function showHighlighter(cb) {
|
||||
inspector.toolbox.startPicker().then(() => {
|
||||
EventUtils.synthesizeMouse(content.document.body, 1, 1,
|
||||
{type: "mousemove"}, content);
|
||||
inspector.toolbox.once("picker-node-hovered", () => {
|
||||
executeSoon(() => {
|
||||
getHighlighterOutline().setAttribute("disable-transitions", "true");
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runInspectorTests() {
|
||||
iframe = content.document.querySelector("iframe");
|
||||
ok(iframe, "found the iframe element");
|
||||
|
||||
ok(inspector.highlighter._highlighting, "Inspector is highlighting");
|
||||
showHighlighter(() => {
|
||||
ok(isHighlighting(), "Inspector is highlighting");
|
||||
|
||||
iframe.addEventListener("load", onIframeLoad, false);
|
||||
iframe.addEventListener("load", onIframeLoad, false);
|
||||
|
||||
executeSoon(function() {
|
||||
iframe.contentWindow.location = "javascript:location.reload()";
|
||||
executeSoon(function() {
|
||||
iframe.contentWindow.location = "javascript:location.reload()";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -36,7 +52,7 @@ function test() {
|
||||
|
||||
iframe.removeEventListener("load", onIframeLoad, false);
|
||||
|
||||
ok(inspector.highlighter._highlighting, "Inspector is highlighting after iframe nav");
|
||||
ok(isHighlighting(), "Inspector is highlighting after iframe nav");
|
||||
|
||||
checksAfterLoads = true;
|
||||
|
||||
@ -47,9 +63,11 @@ function test() {
|
||||
is(iframeLoads, 2, "iframe loads");
|
||||
ok(checksAfterLoads, "the Inspector tests got the chance to run after iframe reloads");
|
||||
|
||||
iframe = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
executeSoon(finish);
|
||||
inspector.toolbox.stopPicker().then(() => {
|
||||
iframe = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
executeSoon(finish);
|
||||
});
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
@ -1,103 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let inspector, utils;
|
||||
|
||||
function startLocationTests() {
|
||||
openInspector(runInspectorTests);
|
||||
}
|
||||
|
||||
function runInspectorTests(aInspector) {
|
||||
inspector = aInspector;
|
||||
utils = inspector.panelWin
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
ok(utils, "utils is defined");
|
||||
executeSoon(function() {
|
||||
inspector.selection.once("new-node", onNewSelection);
|
||||
info("selecting the DOCTYPE node");
|
||||
inspector.selection.setNode(content.document.doctype, "test");
|
||||
});
|
||||
}
|
||||
|
||||
function sendMouseEvent(node, type, x, y) {
|
||||
let rect = node.getBoundingClientRect();
|
||||
let left = rect.left + x;
|
||||
let top = rect.top + y;
|
||||
utils.sendMouseEventToWindow(type, left, top, 0, 1, 0, false, 0, 0);
|
||||
}
|
||||
|
||||
function onNewSelection() {
|
||||
is(inspector.highlighter.isHidden(), true,
|
||||
"The infobar should be hidden now on selecting a non element node.");
|
||||
inspector.sidebar.select("ruleview");
|
||||
let ruleView = inspector.sidebar.getTab("ruleview");
|
||||
ruleView.addEventListener("mouseover", function onMouseOver() {
|
||||
ruleView.removeEventListener("mouseover", onMouseOver, false);
|
||||
is(inspector.highlighter.isHidden(), true,
|
||||
"The infobar was hidden so mouseover on the rules view did nothing");
|
||||
executeSoon(mouseOutAndContinue);
|
||||
}, false);
|
||||
sendMouseEvent(ruleView, "mouseover", 10, 10);
|
||||
}
|
||||
|
||||
function mouseOutAndContinue() {
|
||||
let ruleView = inspector.sidebar.getTab("ruleview");
|
||||
info("adding mouseout listener");
|
||||
ruleView.addEventListener("mouseout", function onMouseOut() {
|
||||
info("mouseout happened");
|
||||
ruleView.removeEventListener("mouseout", onMouseOut, false);
|
||||
is(inspector.highlighter.isHidden(), true,
|
||||
"The infobar should not be visible after we mouseout of rules view");
|
||||
switchToWebConsole();
|
||||
}, false);
|
||||
info("Synthesizing mouseout on " + ruleView);
|
||||
sendMouseEvent(inspector._markupBox, "mousemove", 50, 50);
|
||||
info("mouseout synthesized");
|
||||
}
|
||||
|
||||
function switchToWebConsole() {
|
||||
inspector.selection.once("new-node", function() {
|
||||
is(inspector.highlighter.isHidden(), false,
|
||||
"The infobar should be visible after we select a div.");
|
||||
gDevTools.showToolbox(inspector.target, "webconsole").then(function() {
|
||||
is(inspector.highlighter.isHidden(), true,
|
||||
"The infobar should not be visible after we switched to webconsole");
|
||||
reloadAndWait();
|
||||
});
|
||||
});
|
||||
inspector.selection.setNode(content.document.querySelector("div"), "test");
|
||||
}
|
||||
|
||||
function reloadAndWait() {
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
|
||||
waitForFocus(testAfterReload, content);
|
||||
}, true);
|
||||
content.location.reload();
|
||||
}
|
||||
|
||||
function testAfterReload() {
|
||||
is(inspector.highlighter.isHidden(), true,
|
||||
"The infobar should not be visible after we reload with webconsole shown");
|
||||
testEnd();
|
||||
}
|
||||
|
||||
function testEnd() {
|
||||
gBrowser.removeCurrentTab();
|
||||
utils = null;
|
||||
executeSoon(finish);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
|
||||
waitForFocus(startLocationTests, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<!DOCTYPE html><div>Infobar should not " +
|
||||
"reappear</div><p>init</p>";
|
||||
}
|
@ -33,6 +33,13 @@ function test() {
|
||||
// select the inspector
|
||||
.then(function () toolbox.selectTool("inspector"))
|
||||
|
||||
// wait until inspector ready
|
||||
.then(function () {
|
||||
let deferred = promise.defer();
|
||||
toolbox.getPanel("inspector").once("inspector-updated", deferred.resolve);
|
||||
return deferred.promise;
|
||||
})
|
||||
|
||||
// navigate to URL_2
|
||||
.then(function () {
|
||||
let deferred = promise.defer();
|
||||
|
@ -139,8 +139,5 @@ function test() {
|
||||
let breadcrumbs = inspector.panelDoc.getElementById("inspector-breadcrumbs");
|
||||
is(breadcrumbs.querySelector("button[checked=true]").textContent, crumbLabel,
|
||||
"The right breadcrumb is selected");
|
||||
|
||||
// Highlighter is shown?
|
||||
ok(!inspector.highlighter.isHidden(), "The highlighter is shown");
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ function test() {
|
||||
// select the inspector
|
||||
return toolbox.selectTool("inspector").then(i => {
|
||||
inspector = i;
|
||||
|
||||
// Verify we are on page one
|
||||
let testNode = content.document.querySelector("#one");
|
||||
ok(testNode, "We have the test node on page 1");
|
||||
|
@ -32,7 +32,7 @@ function test() {
|
||||
function runInspectorTests(aInspector)
|
||||
{
|
||||
inspector = aInspector;
|
||||
inspector.sidebar.once("computedview-ready", function() {
|
||||
inspector.sidebar.once("computedview-ready", () => {
|
||||
info("Computed View ready");
|
||||
inspector.sidebar.select("computedview");
|
||||
|
||||
@ -41,8 +41,9 @@ function test() {
|
||||
testDiv.style.fontSize = "10px";
|
||||
|
||||
// Start up the style inspector panel...
|
||||
inspector.once("computed-view-refreshed", computedStylePanelTests);
|
||||
|
||||
inspector.once("computed-view-refreshed", () => {
|
||||
executeSoon(computedStylePanelTests);
|
||||
});
|
||||
inspector.selection.setNode(testDiv);
|
||||
});
|
||||
}
|
||||
@ -59,7 +60,9 @@ function test() {
|
||||
|
||||
// Wait until layout-change fires from mutation to skip earlier refresh event
|
||||
inspector.once("layout-change", () => {
|
||||
inspector.once("computed-view-refreshed", computedStylePanelAfterChange);
|
||||
inspector.once("computed-view-refreshed", () => {
|
||||
executeSoon(computedStylePanelAfterChange);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -79,12 +82,11 @@ function test() {
|
||||
// Tests changes made while the style panel is not active.
|
||||
inspector.sidebar.select("ruleview");
|
||||
|
||||
testDiv.style.fontSize = "20px";
|
||||
testDiv.style.color = "blue";
|
||||
testDiv.style.textAlign = "center";
|
||||
testDiv.style.cssText = "font-size: 20px; color: blue; text-align: center";
|
||||
|
||||
inspector.once("computed-view-refreshed", computedStylePanelAfterSwitch);
|
||||
inspector.sidebar.select("computedview");
|
||||
inspector.once("computed-view-refreshed", () => {
|
||||
executeSoon(computedStylePanelAfterSwitch);
|
||||
});
|
||||
}
|
||||
|
||||
function computedStylePanelAfterSwitch()
|
||||
@ -110,13 +112,11 @@ function test() {
|
||||
let propView = getInspectorRuleProp("text-align");
|
||||
is(propView.value, "center", "Style inspector should be showing the new text align.");
|
||||
|
||||
testDiv.style.textAlign = "right";
|
||||
testDiv.style.color = "lightgoldenrodyellow";
|
||||
testDiv.style.fontSize = "3em";
|
||||
testDiv.style.textTransform = "uppercase";
|
||||
testDiv.style.cssText = "font-size: 3em; color: lightgoldenrodyellow; text-align: right; text-transform: uppercase";
|
||||
|
||||
|
||||
inspector.once("rule-view-refreshed", rulePanelAfterChange);
|
||||
inspector.once("rule-view-refreshed", () => {
|
||||
executeSoon(rulePanelAfterChange);
|
||||
});
|
||||
}
|
||||
|
||||
function rulePanelAfterChange()
|
||||
|
@ -4,7 +4,6 @@
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
//ignoreAllUncaughtExceptions();
|
||||
|
||||
let node, iframe, inspector;
|
||||
|
||||
@ -28,20 +27,24 @@ function test()
|
||||
inspector = aInspector;
|
||||
inspector.selection.setNode(node);
|
||||
|
||||
iframe.parentNode.removeChild(iframe);
|
||||
iframe = null;
|
||||
inspector.once("inspector-updated", () => {
|
||||
iframe.parentNode.removeChild(iframe);
|
||||
iframe = null;
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
|
||||
let lh = new tmp.LayoutHelpers(window.content);
|
||||
ok(!lh.isNodeConnected(node), "Node considered as disconnected.");
|
||||
ok(!inspector.selection.isConnected(), "Selection considered as disconnected");
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
|
||||
let lh = new tmp.LayoutHelpers(window.content);
|
||||
ok(!lh.isNodeConnected(node), "Node considered as disconnected.");
|
||||
ok(!inspector.selection.isConnected(), "Selection considered as disconnected");
|
||||
|
||||
finishUp();
|
||||
inspector.once("inspector-updated", () => {
|
||||
finishUp();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
node = null;
|
||||
node = inspector = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
@ -6,12 +6,11 @@
|
||||
|
||||
let doc;
|
||||
let h1;
|
||||
let div;
|
||||
let inspector;
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
function createDocument() {
|
||||
let div = doc.createElement("div");
|
||||
let h1 = doc.createElement("h1");
|
||||
h1 = doc.createElement("h1");
|
||||
let p1 = doc.createElement("p");
|
||||
let p2 = doc.createElement("p");
|
||||
let div2 = doc.createElement("div");
|
||||
@ -48,80 +47,38 @@ function createDocument()
|
||||
doc.body.appendChild(div2);
|
||||
doc.body.appendChild(div3);
|
||||
|
||||
openInspector(setupHighlighterTests);
|
||||
}
|
||||
|
||||
function setupHighlighterTests()
|
||||
{
|
||||
h1 = doc.querySelector("h1");
|
||||
ok(h1, "we have the header");
|
||||
|
||||
let i = getActiveInspector();
|
||||
i.selection.setNode(div);
|
||||
i.highlighter.unlockAndFocus();
|
||||
i.highlighter.outline.setAttribute("disable-transitions", "true");
|
||||
|
||||
executeSoon(function() {
|
||||
i.selection.once("new-node", performToggleComparisons);
|
||||
EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
|
||||
openInspector(aInspector => {
|
||||
inspector = aInspector;
|
||||
inspector.selection.setNode(div, null);
|
||||
inspector.once("inspector-updated", () => {
|
||||
getHighlighterOutline().setAttribute("disable-transitions", "true");
|
||||
inspector.toolbox.startPicker().then(testMouseOverH1Highlights);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function performToggleComparisons(evt)
|
||||
{
|
||||
let i = getActiveInspector();
|
||||
|
||||
i.highlighter.toggleLockState();
|
||||
ok(i.highlighter.locked, "highlighter locks");
|
||||
is(i.selection.node, div);
|
||||
i.highlighter.toggleLockState();
|
||||
ok(!i.highlighter.locked, "highlighter unlocks");
|
||||
|
||||
i.highlighter.toggleLockState();
|
||||
ok(i.highlighter.locked, "highlighter locks if selection is unchanged");
|
||||
i.highlighter.toggleLockState();
|
||||
|
||||
executeSoon(function() {
|
||||
i.selection.once("new-node", performTestComparisons);
|
||||
EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
|
||||
function testMouseOverH1Highlights() {
|
||||
inspector.toolbox.once("picker-node-hovered", () => {
|
||||
ok(isHighlighting(), "Highlighter is shown");
|
||||
is(getHighlitNode(), h1, "Highlighter's outline correspond to the selected node");
|
||||
testOutlineDimensions();
|
||||
});
|
||||
|
||||
EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
|
||||
}
|
||||
|
||||
function performTestComparisons(evt)
|
||||
{
|
||||
let i = getActiveInspector();
|
||||
i.highlighter.lock();
|
||||
ok(isHighlighting(), "highlighter is highlighting");
|
||||
is(getHighlitNode(), h1, "highlighter matches selection")
|
||||
is(i.selection.node, h1, "selection matches node");
|
||||
is(i.selection.node, getHighlitNode(), "selection matches highlighter");
|
||||
function testOutlineDimensions() {
|
||||
let h1Dims = h1.getBoundingClientRect();
|
||||
let h1Width = h1Dims.width;
|
||||
let h1Height = h1Dims.height;
|
||||
|
||||
|
||||
div = doc.querySelector("div#checkOutThisWickedSpread");
|
||||
|
||||
executeSoon(function() {
|
||||
i.selection.once("new-node", finishTestComparisons);
|
||||
i.selection.setNode(div);
|
||||
});
|
||||
}
|
||||
|
||||
function finishTestComparisons()
|
||||
{
|
||||
let i = getActiveInspector();
|
||||
|
||||
// get dimensions of div element
|
||||
let divDims = div.getBoundingClientRect();
|
||||
let divWidth = divDims.width;
|
||||
let divHeight = divDims.height;
|
||||
|
||||
// get dimensions of the outline
|
||||
let outlineDims = i.highlighter.outline.getBoundingClientRect();
|
||||
let outlineDims = getHighlighterOutlineRect();
|
||||
let outlineWidth = outlineDims.width;
|
||||
let outlineHeight = outlineDims.height;
|
||||
|
||||
// Disabled due to bug 716245
|
||||
//is(outlineWidth, divWidth, "outline width matches dimensions of element (no zoom)");
|
||||
//is(outlineHeight, divHeight, "outline height matches dimensions of element (no zoom)");
|
||||
is(outlineWidth, h1Width, "outline width matches dimensions of element (no zoom)");
|
||||
is(outlineHeight, h1Height, "outline height matches dimensions of element (no zoom)");
|
||||
|
||||
// zoom the page by a factor of 2
|
||||
let contentViewer = gBrowser.selectedBrowser.docShell.contentViewer
|
||||
@ -132,38 +89,36 @@ function finishTestComparisons()
|
||||
// resize event
|
||||
|
||||
window.setTimeout(function() {
|
||||
// check what zoom factor we're at, should be 2
|
||||
let zoom = i.highlighter.zoom;
|
||||
is(zoom, 2, "zoom is 2?");
|
||||
|
||||
// simulate the zoomed dimensions of the div element
|
||||
let divDims = div.getBoundingClientRect();
|
||||
let divWidth = divDims.width * zoom;
|
||||
let divHeight = divDims.height * zoom;
|
||||
let h1Dims = h1.getBoundingClientRect();
|
||||
// There seems to be some very minor differences in the floats, so let's
|
||||
// floor the values
|
||||
let h1Width = Math.floor(h1Dims.width * contentViewer.fullZoom);
|
||||
let h1Height = Math.floor(h1Dims.height * contentViewer.fullZoom);
|
||||
|
||||
// now zoomed, get new dimensions the outline
|
||||
let outlineDims = i.highlighter.outline.getBoundingClientRect();
|
||||
let outlineWidth = outlineDims.width;
|
||||
let outlineHeight = outlineDims.height;
|
||||
let outlineDims = getHighlighterOutlineRect();
|
||||
let outlineWidth = Math.floor(outlineDims.width);
|
||||
let outlineHeight = Math.floor(outlineDims.height);
|
||||
|
||||
// Disabled due to bug 716245
|
||||
//is(outlineWidth, divWidth, "outline width matches dimensions of element (no zoom)");
|
||||
//is(outlineHeight, divHeight, "outline height matches dimensions of element (no zoom)");
|
||||
is(outlineWidth, h1Width, "outline width matches dimensions of element (zoomed)");
|
||||
is(outlineHeight, h1Height, "outline height matches dimensions of element (zoomed)");
|
||||
|
||||
doc = h1 = div = null;
|
||||
executeSoon(finishUp);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
inspector.toolbox.stopPicker().then(() => {
|
||||
doc = h1 = inspector = null;
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||
@ -174,4 +129,3 @@ function test()
|
||||
|
||||
content.location = "data:text/html,basic tests for inspector";
|
||||
}
|
||||
|
||||
|
@ -1,46 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
let toolbox;
|
||||
let inspector;
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(startInspector, content);
|
||||
}, true);
|
||||
content.location = "data:text/html,mop"
|
||||
|
||||
function startInspector() {
|
||||
info("Tab loaded");
|
||||
openInspector(function(aInspector) {
|
||||
inspector = aInspector;
|
||||
ok(!inspector.highlighter.hidden, "Highlighter is visible");
|
||||
toolbox = inspector._toolbox;
|
||||
toolbox.once("webconsole-selected", onWebConsoleSelected);
|
||||
toolbox.selectTool("webconsole");
|
||||
});
|
||||
}
|
||||
|
||||
function onWebConsoleSelected() {
|
||||
executeSoon(function() {
|
||||
ok(inspector.highlighter.hidden, "Highlighter is hidden");
|
||||
toolbox.once("inspector-selected", onInspectorSelected);
|
||||
toolbox.selectTool("inspector");
|
||||
});
|
||||
}
|
||||
|
||||
function onInspectorSelected() {
|
||||
executeSoon(function() {
|
||||
ok(!inspector.highlighter.hidden, "Highlighter is visible once inspector reopen");
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ let div1;
|
||||
let div2;
|
||||
let iframe1;
|
||||
let iframe2;
|
||||
let inspector;
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
function createDocument() {
|
||||
doc.title = "Inspector iframe Tests";
|
||||
|
||||
iframe1 = doc.createElement('iframe');
|
||||
@ -32,7 +32,11 @@ function createDocument()
|
||||
div2.textContent = 'nested div';
|
||||
iframe2.contentDocument.body.appendChild(div2);
|
||||
|
||||
openInspector(runIframeTests);
|
||||
// Open the inspector, start the picker mode, and start the tests
|
||||
openInspector(aInspector => {
|
||||
inspector = aInspector;
|
||||
inspector.toolbox.startPicker().then(runTests);
|
||||
});
|
||||
}, false);
|
||||
|
||||
iframe2.src = 'data:text/html,nested iframe';
|
||||
@ -43,58 +47,56 @@ function createDocument()
|
||||
doc.body.appendChild(iframe1);
|
||||
}
|
||||
|
||||
function moveMouseOver(aElement)
|
||||
{
|
||||
function moveMouseOver(aElement, cb) {
|
||||
EventUtils.synthesizeMouse(aElement, 2, 2, {type: "mousemove"},
|
||||
aElement.ownerDocument.defaultView);
|
||||
}
|
||||
|
||||
function runIframeTests()
|
||||
{
|
||||
getActiveInspector().highlighter.unlock();
|
||||
getActiveInspector().selection.once("new-node", performTestComparisons1);
|
||||
moveMouseOver(div1)
|
||||
}
|
||||
|
||||
function performTestComparisons1()
|
||||
{
|
||||
let i = getActiveInspector();
|
||||
is(i.selection.node, div1, "selection matches div1 node");
|
||||
is(getHighlitNode(), div1, "highlighter matches selection");
|
||||
|
||||
i.selection.once("new-node", performTestComparisons2);
|
||||
executeSoon(function() {
|
||||
moveMouseOver(div2);
|
||||
inspector.toolbox.once("picker-node-hovered", () => {
|
||||
executeSoon(cb);
|
||||
});
|
||||
}
|
||||
|
||||
function performTestComparisons2()
|
||||
{
|
||||
let i = getActiveInspector();
|
||||
|
||||
is(i.selection.node, div2, "selection matches div2 node");
|
||||
is(getHighlitNode(), div2, "highlighter matches selection");
|
||||
|
||||
selectRoot();
|
||||
function runTests() {
|
||||
testDiv1Highlighter();
|
||||
}
|
||||
|
||||
function selectRoot()
|
||||
{
|
||||
function testDiv1Highlighter() {
|
||||
moveMouseOver(div1, () => {
|
||||
getHighlighterOutline().setAttribute("disable-transitions", "true");
|
||||
is(getHighlitNode(), div1, "highlighter matches selection");
|
||||
testDiv2Highlighter();
|
||||
});
|
||||
}
|
||||
|
||||
function testDiv2Highlighter() {
|
||||
moveMouseOver(div2, () => {
|
||||
is(getHighlitNode(), div2, "highlighter matches selection");
|
||||
selectRoot();
|
||||
});
|
||||
}
|
||||
|
||||
function selectRoot() {
|
||||
// Select the root document element to clear the breadcrumbs.
|
||||
let i = getActiveInspector();
|
||||
i.selection.setNode(doc.documentElement);
|
||||
i.once("inspector-updated", selectIframe);
|
||||
inspector.selection.setNode(doc.documentElement);
|
||||
inspector.once("inspector-updated", selectIframe);
|
||||
}
|
||||
|
||||
function selectIframe()
|
||||
{
|
||||
function selectIframe() {
|
||||
// Directly select an element in an iframe (without navigating to it
|
||||
// with mousemoves).
|
||||
let i = getActiveInspector();
|
||||
i.selection.setNode(div2);
|
||||
i.once("inspector-updated", () => {
|
||||
let breadcrumbs = i.breadcrumbs;
|
||||
inspector.selection.setNode(div2);
|
||||
inspector.once("inspector-updated", () => {
|
||||
let breadcrumbs = inspector.breadcrumbs;
|
||||
is(breadcrumbs.nodeHierarchy.length, 9, "Should have 9 items");
|
||||
finishUp();
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
inspector.toolbox.stopPicker().then(() => {
|
||||
doc = div1 = div2 = iframe1 = iframe2 = inspector = null;
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
@ -111,11 +113,4 @@ function test() {
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,iframe tests for inspector";
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test()
|
||||
{
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
ignoreAllUncaughtExceptions();
|
||||
|
||||
@ -23,8 +22,7 @@ function test()
|
||||
|
||||
content.location = "data:text/html," + encodeURIComponent(html);
|
||||
|
||||
function setupInfobarTest()
|
||||
{
|
||||
function setupInfobarTest() {
|
||||
nodes = [
|
||||
{node: doc.querySelector("#top"), position: "bottom", tag: "DIV", id: "#top", classes: ".class1.class2"},
|
||||
{node: doc.querySelector("#vertical"), position: "overlap", tag: "DIV", id: "#vertical", classes: ""},
|
||||
@ -40,18 +38,25 @@ function test()
|
||||
openInspector(runTests);
|
||||
}
|
||||
|
||||
function runTests(aInspector)
|
||||
{
|
||||
function mouseOverContainerToShowHighlighter(node, cb) {
|
||||
let container = getContainerForRawNode(inspector.markup, node);
|
||||
EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
|
||||
inspector.markup.doc.defaultView);
|
||||
executeSoon(cb);
|
||||
}
|
||||
|
||||
function runTests(aInspector) {
|
||||
inspector = aInspector;
|
||||
cursor = 0;
|
||||
executeSoon(function() {
|
||||
inspector.selection.setNode(nodes[0].node, "");
|
||||
nodeSelected();
|
||||
inspector.selection.setNode(content.document.querySelector("body"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
cursor = 0;
|
||||
executeSoon(function() {
|
||||
mouseOverContainerToShowHighlighter(nodes[0].node, nodeSelected);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function nodeSelected()
|
||||
{
|
||||
function nodeSelected() {
|
||||
executeSoon(function() {
|
||||
performTest();
|
||||
cursor++;
|
||||
@ -59,18 +64,16 @@ function test()
|
||||
finishUp();
|
||||
} else {
|
||||
let node = nodes[cursor].node;
|
||||
inspector.selection.setNode(node, "");
|
||||
nodeSelected();
|
||||
mouseOverContainerToShowHighlighter(node, nodeSelected);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function performTest()
|
||||
{
|
||||
function performTest() {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let stack = browser.parentNode;
|
||||
|
||||
let container = stack.querySelector(".highlighter-nodeinfobar-container");
|
||||
let container = stack.querySelector(".highlighter-nodeinfobar-positioner");
|
||||
is(container.getAttribute("position"), nodes[cursor].position, "node " + cursor + ": position matches.");
|
||||
|
||||
let tagNameLabel = stack.querySelector(".highlighter-nodeinfobar-tagname");
|
||||
@ -89,4 +92,3 @@ function test()
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,13 +35,11 @@ function startInspectorTests(toolbox)
|
||||
ok(inspector, "Inspector instance is accessible");
|
||||
ok(inspector.isReady, "Inspector instance is ready");
|
||||
is(inspector.target.tab, gBrowser.selectedTab, "Valid target");
|
||||
ok(inspector.highlighter, "Highlighter is up");
|
||||
|
||||
let p = doc.querySelector("p");
|
||||
|
||||
inspector.selection.setNode(p);
|
||||
inspector.once("inspector-updated", () => {
|
||||
testHighlighter(p);
|
||||
testMarkupView(p);
|
||||
testBreadcrumbs(p);
|
||||
|
||||
@ -50,7 +48,6 @@ function startInspectorTests(toolbox)
|
||||
|
||||
inspector.selection.setNode(span);
|
||||
inspector.once("inspector-updated", () => {
|
||||
testHighlighter(span);
|
||||
testMarkupView(span);
|
||||
testBreadcrumbs(span);
|
||||
|
||||
@ -65,13 +62,6 @@ function startInspectorTests(toolbox)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function testHighlighter(node)
|
||||
{
|
||||
ok(isHighlighting(), "Highlighter is highlighting");
|
||||
is(getHighlitNode(), node, "Right node is highlighted");
|
||||
}
|
||||
|
||||
let callNo = 0;
|
||||
function testMarkupView(node)
|
||||
{
|
||||
@ -112,7 +102,6 @@ function runContextMenuTest()
|
||||
}
|
||||
|
||||
function testInitialNodeIsSelected() {
|
||||
testHighlighter(salutation);
|
||||
testMarkupView(salutation);
|
||||
testBreadcrumbs(salutation);
|
||||
inspectNodesFromContextTestWhileOpen();
|
||||
@ -124,10 +113,9 @@ function inspectNodesFromContextTestWhileOpen()
|
||||
getActiveInspector().selection.once("new-node", function() {
|
||||
ok(true, "Get selection's 'new-node' selection");
|
||||
executeSoon(function() {
|
||||
testHighlighter(closing);
|
||||
testMarkupView(closing);
|
||||
testBreadcrumbs(closing);
|
||||
finishInspectorTests();
|
||||
getActiveInspector().once("inspector-updated", finishInspectorTests)
|
||||
}
|
||||
)});
|
||||
_clickOnInspectMenuItem(closing);
|
||||
|
@ -2,39 +2,42 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
|
||||
let doc;
|
||||
let div;
|
||||
let inspector;
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
function createDocument() {
|
||||
div = doc.createElement("div");
|
||||
div.setAttribute("style", "width: 100px; height: 100px; background:yellow;");
|
||||
doc.body.appendChild(div);
|
||||
|
||||
openInspector(runTest);
|
||||
openInspector(aInspector => {
|
||||
inspector = aInspector;
|
||||
inspector.toolbox.highlighter.showBoxModel(getNodeFront(div)).then(runTest);
|
||||
});
|
||||
}
|
||||
|
||||
function runTest(inspector)
|
||||
{
|
||||
inspector.selection.setNode(div);
|
||||
function runTest() {
|
||||
let outline = getHighlighterOutline();
|
||||
is(outline.style.width, "100px", "outline has the right width");
|
||||
|
||||
executeSoon(function() {
|
||||
let outline = inspector.highlighter.outline;
|
||||
is(outline.style.width, "100px", "selection has the right width");
|
||||
|
||||
div.style.width = "200px";
|
||||
function pollTest() {
|
||||
if (outline.style.width == "100px") {
|
||||
setTimeout(pollTest, 10);
|
||||
return;
|
||||
}
|
||||
is(outline.style.width, "200px", "selection updated");
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
div.style.width = "200px";
|
||||
function pollTest() {
|
||||
if (outline.style.width == "100px") {
|
||||
setTimeout(pollTest, 10);
|
||||
return;
|
||||
}
|
||||
setTimeout(pollTest, 10);
|
||||
is(outline.style.width, "200px", "outline updated");
|
||||
finishUp();
|
||||
}
|
||||
setTimeout(pollTest, 10);
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
inspector.toolbox.highlighter.hideBoxModel().then(() => {
|
||||
doc = div = inspector = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ function test() {
|
||||
let deleteNode = inspector.panelDoc.getElementById("node-menu-delete");
|
||||
ok(deleteNode, "the popup menu has a delete menu item");
|
||||
|
||||
inspector.once("markupmutation", deleteTest);
|
||||
inspector.once("inspector-updated", deleteTest);
|
||||
|
||||
let commandEvent = document.createEvent("XULCommandEvent");
|
||||
commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
|
||||
@ -120,12 +120,15 @@ function test() {
|
||||
|
||||
function deleteRootNode() {
|
||||
inspector.selection.setNode(doc.documentElement);
|
||||
let deleteNode = inspector.panelDoc.getElementById("node-menu-delete");
|
||||
let commandEvent = inspector.panelDoc.createEvent("XULCommandEvent");
|
||||
commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
|
||||
false, false, null);
|
||||
deleteNode.dispatchEvent(commandEvent);
|
||||
executeSoon(isRootStillAlive);
|
||||
|
||||
inspector.once("inspector-updated", () => {
|
||||
let deleteNode = inspector.panelDoc.getElementById("node-menu-delete");
|
||||
let commandEvent = inspector.panelDoc.createEvent("XULCommandEvent");
|
||||
commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
|
||||
false, false, null);
|
||||
deleteNode.dispatchEvent(commandEvent);
|
||||
executeSoon(isRootStillAlive);
|
||||
});
|
||||
}
|
||||
|
||||
function isRootStillAlive() {
|
||||
|
@ -37,7 +37,7 @@ function test() {
|
||||
{
|
||||
inspector = aInspector;
|
||||
inspector.selection.setNode(div);
|
||||
performTests();
|
||||
inspector.once("inspector-updated", performTests);
|
||||
}
|
||||
|
||||
function performTests()
|
||||
|
@ -67,22 +67,24 @@ function performTests()
|
||||
// Wait for the "pseudoclass" event so we know the
|
||||
// inspector has been told of the pseudoclass lock change.
|
||||
inspector.selection.once("pseudoclass", () => {
|
||||
// Give the rule view time to update.
|
||||
inspector.once("rule-view-refreshed", () => {
|
||||
testAdded();
|
||||
// Change the pseudo class and give the rule view time to update.
|
||||
inspector.togglePseudoClass(pseudo);
|
||||
inspector.selection.once("pseudoclass", () => {
|
||||
inspector.once("rule-view-refreshed", () => {
|
||||
testRemoved();
|
||||
testRemovedFromUI();
|
||||
|
||||
// toggle it back on
|
||||
inspector.togglePseudoClass(pseudo);
|
||||
inspector.selection.once("pseudoclass", () => {
|
||||
testNavigate(() => {
|
||||
// close the inspector
|
||||
finishUp();
|
||||
testAdded(() => {
|
||||
// Change the pseudo class and give the rule view time to update.
|
||||
inspector.togglePseudoClass(pseudo);
|
||||
inspector.selection.once("pseudoclass", () => {
|
||||
inspector.once("rule-view-refreshed", () => {
|
||||
testRemoved();
|
||||
testRemovedFromUI(() => {
|
||||
// toggle it back on
|
||||
inspector.togglePseudoClass(pseudo);
|
||||
inspector.selection.once("pseudoclass", () => {
|
||||
inspector.once("rule-view-refreshed", () => {
|
||||
testNavigate(() => {
|
||||
// close the inspector
|
||||
finishUp();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -98,46 +100,52 @@ function testNavigate(callback)
|
||||
|
||||
// make sure it's still on after naving to parent
|
||||
is(DOMUtils.hasPseudoClassLock(div, pseudo), true,
|
||||
"pseudo-class lock is still applied after inspecting ancestor");
|
||||
"pseudo-class lock is still applied after inspecting ancestor");
|
||||
|
||||
inspector.selection.setNode(div2);
|
||||
inspector.selection.once("pseudoclass", () => {
|
||||
// make sure it's removed after naving to a non-hierarchy node
|
||||
is(DOMUtils.hasPseudoClassLock(div, pseudo), false,
|
||||
"pseudo-class lock is removed after inspecting sibling node");
|
||||
"pseudo-class lock is removed after inspecting sibling node");
|
||||
|
||||
// toggle it back on
|
||||
inspector.selection.setNode(div);
|
||||
inspector.once("inspector-updated", () => {
|
||||
inspector.togglePseudoClass(pseudo);
|
||||
inspector.selection.once("pseudoclass", () => {
|
||||
callback();
|
||||
});
|
||||
inspector.once("computed-view-refreshed", callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testAdded()
|
||||
function showPickerOn(node, cb)
|
||||
{
|
||||
let highlighter = inspector.toolbox.highlighter;
|
||||
highlighter.showBoxModel(getNodeFront(node)).then(cb);
|
||||
}
|
||||
|
||||
function testAdded(cb)
|
||||
{
|
||||
// lock is applied to it and ancestors
|
||||
let node = div;
|
||||
do {
|
||||
is(DOMUtils.hasPseudoClassLock(node, pseudo), true,
|
||||
"pseudo-class lock has been applied");
|
||||
"pseudo-class lock has been applied");
|
||||
node = node.parentNode;
|
||||
} while (node.parentNode)
|
||||
|
||||
// infobar selector contains pseudo-class
|
||||
let pseudoClassesBox = getActiveInspector().highlighter.nodeInfo.pseudoClassesBox;
|
||||
is(pseudoClassesBox.textContent, pseudo, "pseudo-class in infobar selector");
|
||||
|
||||
// ruleview contains pseudo-class rule
|
||||
is(ruleview.element.children.length, 3,
|
||||
"rule view is showing 3 rules for pseudo-class locked div");
|
||||
let rules = ruleview.element.querySelectorAll(".ruleview-rule.theme-separator");
|
||||
is(rules.length, 3, "rule view is showing 3 rules for pseudo-class locked div");
|
||||
is(rules[1]._ruleEditor.rule.selectorText, "div:hover", "rule view is showing " + pseudo + " rule");
|
||||
|
||||
is(ruleview.element.children[1]._ruleEditor.rule.selectorText,
|
||||
"div:hover", "rule view is showing " + pseudo + " rule");
|
||||
// Show the highlighter by starting the pick mode and hovering over the div
|
||||
showPickerOn(div, () => {
|
||||
// infobar selector contains pseudo-class
|
||||
let pseudoClassesBox = getHighlighter().querySelector(".highlighter-nodeinfobar-pseudo-classes");
|
||||
is(pseudoClassesBox.textContent, pseudo, "pseudo-class in infobar selector");
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
function testRemoved()
|
||||
@ -151,15 +159,17 @@ function testRemoved()
|
||||
} while (node.parentNode)
|
||||
}
|
||||
|
||||
function testRemovedFromUI()
|
||||
function testRemovedFromUI(cb)
|
||||
{
|
||||
// infobar selector doesn't contain pseudo-class
|
||||
let pseudoClassesBox = getActiveInspector().highlighter.nodeInfo.pseudoClassesBox;
|
||||
is(pseudoClassesBox.textContent, "", "pseudo-class removed from infobar selector");
|
||||
|
||||
// ruleview no longer contains pseudo-class rule
|
||||
is(ruleview.element.children.length, 2,
|
||||
"rule view is showing 2 rules after removing lock");
|
||||
let rules = ruleview.element.querySelectorAll(".ruleview-rule.theme-separator");
|
||||
is(rules.length, 2, "rule view is showing 2 rules after removing lock");
|
||||
|
||||
showPickerOn(div, () => {
|
||||
let pseudoClassesBox = getHighlighter().querySelector(".highlighter-nodeinfobar-pseudo-classes");
|
||||
is(pseudoClassesBox.textContent, "", "pseudo-class removed from infobar selector");
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp()
|
||||
|
@ -44,6 +44,7 @@ function test() {
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, p, "Node re-selected.");
|
||||
toolbox.destroy();
|
||||
toolbox = inspector = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
|
@ -33,9 +33,8 @@ function inspectNode(aInspector)
|
||||
{
|
||||
inspector = aInspector;
|
||||
|
||||
inspector.highlighter.once("locked", performScrollingTest);
|
||||
inspector.once("inspector-updated", performScrollingTest);
|
||||
executeSoon(function() {
|
||||
inspector.highlighter.unlock();
|
||||
inspector.selection.setNode(div, "");
|
||||
});
|
||||
}
|
||||
|
@ -30,9 +30,8 @@ function test() {
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
inspector.destroy().then(() =>
|
||||
toolbox.destroy()
|
||||
).then(() => {
|
||||
inspector.destroy();
|
||||
toolbox.destroy().then(() => {
|
||||
toolbox = inspector = page1 = page2 = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
@ -40,7 +39,7 @@ function test() {
|
||||
}
|
||||
|
||||
function loadPageAnd(page, callback) {
|
||||
inspector.once("markuploaded", () => {
|
||||
inspector.once("new-root", () => {
|
||||
callback();
|
||||
});
|
||||
|
||||
|
@ -6,11 +6,10 @@ const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("devtools.debugger.log");
|
||||
});
|
||||
|
||||
// Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
// SimpleTest.registerCleanupFunction(() => {
|
||||
// Services.prefs.clearUserPref("devtools.debugger.log");
|
||||
// });
|
||||
|
||||
let tempScope = {};
|
||||
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tempScope);
|
||||
@ -52,40 +51,63 @@ function getNodeFront(node)
|
||||
return inspector.walker.frontForRawNode(node);
|
||||
}
|
||||
|
||||
function getHighlighter()
|
||||
{
|
||||
return gBrowser.selectedBrowser.parentNode.querySelector(".highlighter-container");
|
||||
}
|
||||
|
||||
function getHighlighterOutline()
|
||||
{
|
||||
let h = getHighlighter();
|
||||
if (h) {
|
||||
return h.querySelector(".highlighter-outline");
|
||||
}
|
||||
}
|
||||
|
||||
function getHighlighterOutlineRect() {
|
||||
let helper = new LayoutHelpers(window.content);
|
||||
let outline = getHighlighterOutline();
|
||||
|
||||
if (outline) {
|
||||
let browserOffsetRect = helper.getDirtyRect(gBrowser.selectedBrowser);
|
||||
let outlineRect = helper.getDirtyRect(outline);
|
||||
outlineRect.top -= browserOffsetRect.top;
|
||||
outlineRect.left -= browserOffsetRect.left;
|
||||
|
||||
return outlineRect;
|
||||
}
|
||||
}
|
||||
|
||||
function isHighlighting()
|
||||
{
|
||||
let outline = getActiveInspector().highlighter.outline;
|
||||
return !(outline.getAttribute("hidden") == "true");
|
||||
let outline = getHighlighterOutline();
|
||||
return outline && !outline.hasAttribute("hidden");
|
||||
}
|
||||
|
||||
function getHighlitNode()
|
||||
{
|
||||
let h = getActiveInspector().highlighter;
|
||||
if (!isHighlighting() || !h._contentRect)
|
||||
return null;
|
||||
if (isHighlighting()) {
|
||||
let helper = new LayoutHelpers(window.content);
|
||||
let outlineRect = getHighlighterOutlineRect();
|
||||
|
||||
let a = {
|
||||
x: h._contentRect.left,
|
||||
y: h._contentRect.top
|
||||
};
|
||||
let a = {
|
||||
x: outlineRect.left,
|
||||
y: outlineRect.top
|
||||
};
|
||||
|
||||
let b = {
|
||||
x: a.x + h._contentRect.width,
|
||||
y: a.y + h._contentRect.height
|
||||
};
|
||||
let b = {
|
||||
x: a.x + outlineRect.width,
|
||||
y: a.y + outlineRect.height
|
||||
};
|
||||
|
||||
// Get midpoint of diagonal line.
|
||||
let midpoint = midPoint(a, b);
|
||||
|
||||
let lh = new LayoutHelpers(window.content);
|
||||
return lh.getElementFromPoint(h.win.document, midpoint.x,
|
||||
midpoint.y);
|
||||
let {x, y} = getMidPoint(a, b);
|
||||
return helper.getElementFromPoint(window.content.document, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function midPoint(aPointA, aPointB)
|
||||
function getMidPoint(aPointA, aPointB)
|
||||
{
|
||||
let pointC = { };
|
||||
let pointC = {};
|
||||
pointC.x = (aPointB.x - aPointA.x) / 2 + aPointA.x;
|
||||
pointC.y = (aPointB.y - aPointA.y) / 2 + aPointA.y;
|
||||
return pointC;
|
||||
@ -189,6 +211,13 @@ function getComputedPropertyValue(aName)
|
||||
}
|
||||
}
|
||||
|
||||
function getContainerForRawNode(markupView, rawNode)
|
||||
{
|
||||
let front = markupView.walker.frontForRawNode(rawNode);
|
||||
let container = markupView.getContainer(front);
|
||||
return container;
|
||||
}
|
||||
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
|
@ -34,12 +34,8 @@ LayoutView.prototype = {
|
||||
this.update = this.update.bind(this);
|
||||
this.onNewNode = this.onNewNode.bind(this);
|
||||
this.onNewSelection = this.onNewSelection.bind(this);
|
||||
this.onHighlighterLocked = this.onHighlighterLocked.bind(this);
|
||||
this.inspector.selection.on("new-node-front", this.onNewSelection);
|
||||
this.inspector.sidebar.on("layoutview-selected", this.onNewNode);
|
||||
if (this.inspector.highlighter) {
|
||||
this.inspector.highlighter.on("locked", this.onHighlighterLocked);
|
||||
}
|
||||
|
||||
// Store for the different dimensions of the node.
|
||||
// 'selector' refers to the element that holds the value in view.xhtml;
|
||||
@ -106,9 +102,6 @@ LayoutView.prototype = {
|
||||
if (this.browser) {
|
||||
this.browser.removeEventListener("MozAfterPaint", this.update, true);
|
||||
}
|
||||
if (this.inspector.highlighter) {
|
||||
this.inspector.highlighter.off("locked", this.onHighlighterLocked);
|
||||
}
|
||||
this.sizeHeadingLabel = null;
|
||||
this.sizeLabel = null;
|
||||
this.inspector = null;
|
||||
@ -126,8 +119,7 @@ LayoutView.prototype = {
|
||||
onNewNode: function LV_onNewNode() {
|
||||
if (this.isActive() &&
|
||||
this.inspector.selection.isConnected() &&
|
||||
this.inspector.selection.isElementNode() &&
|
||||
this.inspector.selection.reason != "highlighter") {
|
||||
this.inspector.selection.isElementNode()) {
|
||||
this.undim();
|
||||
} else {
|
||||
this.dim();
|
||||
@ -135,14 +127,6 @@ LayoutView.prototype = {
|
||||
return this.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlighter 'locked' event handler
|
||||
*/
|
||||
onHighlighterLocked: function LV_onHighlighterLocked() {
|
||||
this.undim();
|
||||
this.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the layout boxes. No node are selected.
|
||||
*/
|
||||
|
@ -109,9 +109,7 @@ Tools.inspector = {
|
||||
|
||||
preventClosingOnKey: true,
|
||||
onkey: function(panel) {
|
||||
if (panel.highlighter) {
|
||||
panel.highlighter.toggleLockState();
|
||||
}
|
||||
panel.toolbox.togglePicker();
|
||||
},
|
||||
|
||||
isTargetSupported: function(target) {
|
||||
|
@ -70,7 +70,7 @@
|
||||
/* This extra element placed in each tag is positioned absolutely to cover the
|
||||
* whole tag line and is used for background styling (when a selection is made
|
||||
* or when the tag is flashing) */
|
||||
.tag-line .highlighter {
|
||||
.tag-line .tag-state {
|
||||
position: absolute;
|
||||
left: -1000em;
|
||||
right: 0;
|
||||
@ -120,7 +120,7 @@
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.highlighter.flash-out {
|
||||
.tag-state.flash-out {
|
||||
transition: background .5s;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
|
||||
const COLLAPSE_DATA_URL_LENGTH = 60;
|
||||
const CONTAINER_FLASHING_DURATION = 500;
|
||||
const IMAGE_PREVIEW_MAX_DIM = 400;
|
||||
const NEW_SELECTION_HIGHLIGHTER_TIMER = 1000;
|
||||
|
||||
const {UndoStack} = require("devtools/shared/undo");
|
||||
const {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
|
||||
@ -23,6 +24,7 @@ const {HTMLEditor} = require("devtools/markupview/html-editor");
|
||||
const {OutputParser} = require("devtools/output-parser");
|
||||
const promise = require("sdk/core/promise");
|
||||
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/Templater.jsm");
|
||||
@ -52,6 +54,10 @@ loader.lazyGetter(this, "AutocompletePopup", () => {
|
||||
* The inspector we're watching.
|
||||
* @param iframe aFrame
|
||||
* An iframe in which the caller has kindly loaded markup-view.xhtml.
|
||||
*
|
||||
* Fires the following events:
|
||||
* - node-highlight: When a node in the markup-view is hovered and the
|
||||
* corresponding node in the content gets highlighted
|
||||
*/
|
||||
function MarkupView(aInspector, aFrame, aControllerWindow) {
|
||||
this._inspector = aInspector;
|
||||
@ -100,10 +106,10 @@ function MarkupView(aInspector, aFrame, aControllerWindow) {
|
||||
gDevTools.on("pref-changed", this._handlePrefChange);
|
||||
|
||||
this._initPreview();
|
||||
this._initTooltips();
|
||||
this._initHighlighter();
|
||||
|
||||
this.tooltip = new Tooltip(this._inspector.panelDoc);
|
||||
this.tooltip.startTogglingOnHover(this._elt,
|
||||
this._buildTooltipContent.bind(this));
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
exports.MarkupView = MarkupView;
|
||||
@ -111,6 +117,121 @@ exports.MarkupView = MarkupView;
|
||||
MarkupView.prototype = {
|
||||
_selectedContainer: null,
|
||||
|
||||
_initTooltips: function() {
|
||||
this.tooltip = new Tooltip(this._inspector.panelDoc);
|
||||
this.tooltip.startTogglingOnHover(this._elt,
|
||||
this._buildTooltipContent.bind(this));
|
||||
},
|
||||
|
||||
_initHighlighter: function() {
|
||||
// Show the box model on markup-view mousemove
|
||||
this._onMouseMove = this._onMouseMove.bind(this);
|
||||
this._elt.addEventListener("mousemove", this._onMouseMove, false);
|
||||
this._onMouseLeave = this._onMouseLeave.bind(this);
|
||||
this._elt.addEventListener("mouseleave", this._onMouseLeave, false);
|
||||
|
||||
// Show markup-containers as hovered on toolbox "picker-node-hovered" event
|
||||
// which happens when the "pick" button is pressed
|
||||
this._onToolboxPickerHover = (event, nodeFront) => {
|
||||
this.showNode(nodeFront, true).then(() => {
|
||||
this._showContainerAsHovered(nodeFront);
|
||||
});
|
||||
}
|
||||
this._inspector.toolbox.on("picker-node-hovered", this._onToolboxPickerHover);
|
||||
},
|
||||
|
||||
_onMouseMove: function(event) {
|
||||
let target = event.target;
|
||||
|
||||
// Search target for a markupContainer reference, if not found, walk up
|
||||
while (!target.container) {
|
||||
if (target.tagName.toLowerCase() === "body") {
|
||||
return;
|
||||
}
|
||||
target = target.parentNode;
|
||||
}
|
||||
|
||||
let container = target.container;
|
||||
if (this._hoveredNode !== container.node) {
|
||||
if (container.node.nodeType !== Ci.nsIDOMNode.TEXT_NODE) {
|
||||
this._showBoxModel(container.node);
|
||||
} else {
|
||||
this._hideBoxModel();
|
||||
}
|
||||
}
|
||||
this._showContainerAsHovered(container.node);
|
||||
},
|
||||
|
||||
_hoveredNode: null,
|
||||
_showContainerAsHovered: function(nodeFront) {
|
||||
if (this._hoveredNode !== nodeFront) {
|
||||
if (this._hoveredNode) {
|
||||
this._containers.get(this._hoveredNode).hovered = false;
|
||||
}
|
||||
this._containers.get(nodeFront).hovered = true;
|
||||
|
||||
this._hoveredNode = nodeFront;
|
||||
}
|
||||
},
|
||||
|
||||
_onMouseLeave: function() {
|
||||
this._hideBoxModel();
|
||||
},
|
||||
|
||||
_showBoxModel: function(nodeFront, options={}) {
|
||||
let toolbox = this._inspector.toolbox;
|
||||
|
||||
// If the remote highlighter exists on the target, use it
|
||||
if (toolbox.isRemoteHighlightable) {
|
||||
toolbox.initInspector().then(() => {
|
||||
toolbox.highlighter.showBoxModel(nodeFront, options).then(() => {
|
||||
this.emit("node-highlight", nodeFront);
|
||||
});
|
||||
});
|
||||
}
|
||||
// Else, revert to the "older" version of the highlighter in the walker
|
||||
// actor
|
||||
else {
|
||||
this.walker.highlight(nodeFront).then(() => {
|
||||
this.emit("node-highlight", nodeFront);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_hideBoxModel: function() {
|
||||
let deferred = promise.defer();
|
||||
let toolbox = this._inspector.toolbox;
|
||||
|
||||
// If the remote highlighter exists on the target, use it
|
||||
if (toolbox.isRemoteHighlightable) {
|
||||
toolbox.initInspector().then(() => {
|
||||
toolbox.highlighter.hideBoxModel().then(deferred.resolve);
|
||||
});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
// If not, no need to unhighlight as the older highlight method uses a
|
||||
// setTimeout to hide itself
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
_briefBoxModelTimer: null,
|
||||
_brieflyShowBoxModel: function(nodeFront, options) {
|
||||
let win = this._frame.contentWindow;
|
||||
|
||||
if (this._briefBoxModelTimer) {
|
||||
win.clearTimeout(this._briefBoxModelTimer);
|
||||
this._briefBoxModelTimer = null;
|
||||
}
|
||||
|
||||
this._showBoxModel(nodeFront, options);
|
||||
|
||||
this._briefBoxModelTimer = this._frame.contentWindow.setTimeout(() => {
|
||||
this._hideBoxModel();
|
||||
}, NEW_SELECTION_HIGHLIGHTER_TIMER);
|
||||
},
|
||||
|
||||
template: function(aName, aDest, aOptions={stack: "markup-view.xhtml"}) {
|
||||
let node = this.doc.getElementById("template-" + aName).cloneNode(true);
|
||||
node.removeAttribute("id");
|
||||
@ -176,11 +297,22 @@ MarkupView.prototype = {
|
||||
* Highlight the inspector selected node.
|
||||
*/
|
||||
_onNewSelection: function() {
|
||||
let selection = this._inspector.selection;
|
||||
|
||||
this.htmlEditor.hide();
|
||||
let done = this._inspector.updating("markup-view");
|
||||
if (this._inspector.selection.isNode()) {
|
||||
this.showNode(this._inspector.selection.nodeFront, true).then(() => {
|
||||
this.markNodeAsSelected(this._inspector.selection.nodeFront);
|
||||
if (selection.isNode()) {
|
||||
let reason = selection.reason;
|
||||
if (reason && reason !== "inspector-open" && reason !== "navigateaway") {
|
||||
this._brieflyShowBoxModel(selection.nodeFront, {
|
||||
scrollIntoView: true
|
||||
});
|
||||
}
|
||||
|
||||
this.showNode(selection.nodeFront, true).then(() => {
|
||||
if (selection.reason !== "treepanel") {
|
||||
this.markNodeAsSelected(selection.nodeFront);
|
||||
}
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
@ -370,11 +502,6 @@ MarkupView.prototype = {
|
||||
let node = aContainer.node;
|
||||
this.markNodeAsSelected(node, "treepanel");
|
||||
|
||||
// This event won't be fired if the node is the same. But the highlighter
|
||||
// need to lock the node if it wasn't.
|
||||
this._inspector.selection.emit("new-node");
|
||||
this._inspector.selection.emit("new-node-front");
|
||||
|
||||
if (!aIgnoreFocus) {
|
||||
aContainer.focus();
|
||||
}
|
||||
@ -940,24 +1067,31 @@ MarkupView.prototype = {
|
||||
destroy: function() {
|
||||
gDevTools.off("pref-changed", this._handlePrefChange);
|
||||
|
||||
delete this._outputParser;
|
||||
// Note that if the toolbox is closed, this will work fine, but will fail
|
||||
// in case the browser is closed and will trigger a noSuchActor message.
|
||||
this._hideBoxModel();
|
||||
|
||||
this._hoveredNode = null;
|
||||
this._inspector.toolbox.off("picker-node-hovered", this._onToolboxPickerHover);
|
||||
|
||||
this._outputParser = null;
|
||||
|
||||
this.htmlEditor.destroy();
|
||||
delete this.htmlEditor;
|
||||
this.htmlEditor = null;
|
||||
|
||||
this.undo.destroy();
|
||||
delete this.undo;
|
||||
this.undo = null;
|
||||
|
||||
this.popup.destroy();
|
||||
delete this.popup;
|
||||
this.popup = null;
|
||||
|
||||
this._frame.removeEventListener("focus", this._boundFocus, false);
|
||||
delete this._boundFocus;
|
||||
this._boundFocus = null;
|
||||
|
||||
if (this._boundUpdatePreview) {
|
||||
this._frame.contentWindow.removeEventListener("scroll",
|
||||
this._boundUpdatePreview, true);
|
||||
delete this._boundUpdatePreview;
|
||||
this._boundUpdatePreview = null;
|
||||
}
|
||||
|
||||
if (this._boundResizePreview) {
|
||||
@ -967,28 +1101,30 @@ MarkupView.prototype = {
|
||||
this._boundResizePreview, true);
|
||||
this._frame.contentWindow.removeEventListener("underflow",
|
||||
this._boundResizePreview, true);
|
||||
delete this._boundResizePreview;
|
||||
this._boundResizePreview = null;
|
||||
}
|
||||
|
||||
this._frame.contentWindow.removeEventListener("keydown",
|
||||
this._boundKeyDown, false);
|
||||
delete this._boundKeyDown;
|
||||
this._boundKeyDown = null;
|
||||
|
||||
this._inspector.selection.off("new-node-front", this._boundOnNewSelection);
|
||||
delete this._boundOnNewSelection;
|
||||
this._boundOnNewSelection = null;
|
||||
|
||||
this.walker.off("mutations", this._boundMutationObserver)
|
||||
delete this._boundMutationObserver;
|
||||
this._boundMutationObserver = null;
|
||||
|
||||
delete this._elt;
|
||||
this._elt.removeEventListener("mousemove", this._onMouseMove, false);
|
||||
this._elt.removeEventListener("mouseleave", this._onMouseLeave, false);
|
||||
this._elt = null;
|
||||
|
||||
for (let [key, container] of this._containers) {
|
||||
container.destroy();
|
||||
}
|
||||
delete this._containers;
|
||||
this._containers = null;
|
||||
|
||||
this.tooltip.destroy();
|
||||
delete this.tooltip;
|
||||
this.tooltip = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1114,7 +1250,7 @@ function MarkupContainer(aMarkupView, aNode, aInspector) {
|
||||
// The template will fill the following properties
|
||||
this.elt = null;
|
||||
this.expander = null;
|
||||
this.highlighter = null;
|
||||
this.tagState = null;
|
||||
this.tagLine = null;
|
||||
this.children = null;
|
||||
this.markup.template("container", this);
|
||||
@ -1126,15 +1262,6 @@ function MarkupContainer(aMarkupView, aNode, aInspector) {
|
||||
this.elt.addEventListener("dblclick", this._onToggle, false);
|
||||
this.expander.addEventListener("click", this._onToggle, false);
|
||||
|
||||
// Dealing with the highlighting of the row via javascript rather than :hover
|
||||
// This is to allow highlighting the closing tag-line as well as reusing the
|
||||
// theme css classes (which wouldn't have been possible with a :hover pseudo)
|
||||
this._onMouseOver = this._onMouseOver.bind(this);
|
||||
this.elt.addEventListener("mouseover", this._onMouseOver, false);
|
||||
|
||||
this._onMouseOut = this._onMouseOut.bind(this);
|
||||
this.elt.addEventListener("mouseout", this._onMouseOut, false);
|
||||
|
||||
// Appending the editor element and attaching event listeners
|
||||
this.tagLine.appendChild(this.editor.elt);
|
||||
|
||||
@ -1235,13 +1362,11 @@ MarkupContainer.prototype = {
|
||||
let line = this.markup.doc.createElement("div");
|
||||
line.classList.add("tag-line");
|
||||
|
||||
let highlighter = this.markup.doc.createElement("div");
|
||||
highlighter.classList.add("highlighter");
|
||||
line.appendChild(highlighter);
|
||||
let tagState = this.markup.doc.createElement("div");
|
||||
tagState.classList.add("tag-state");
|
||||
line.appendChild(tagState);
|
||||
|
||||
line.appendChild(closingTag.cloneNode(true));
|
||||
line.addEventListener("mouseover", this._onMouseOver, false);
|
||||
line.addEventListener("mouseout", this._onMouseOut, false);
|
||||
|
||||
this.closeTagLine = line;
|
||||
}
|
||||
@ -1250,7 +1375,7 @@ MarkupContainer.prototype = {
|
||||
}
|
||||
this.elt.classList.remove("collapsed");
|
||||
this.expander.setAttribute("open", "");
|
||||
this.highlighted = false;
|
||||
this.hovered = false;
|
||||
} else if (!aValue) {
|
||||
if (this.editor instanceof ElementEditor && this.closeTagLine) {
|
||||
this.elt.removeChild(this.closeTagLine);
|
||||
@ -1268,19 +1393,9 @@ MarkupContainer.prototype = {
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
_onMouseOver: function(event) {
|
||||
this.highlighted = true;
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
_onMouseOut: function(event) {
|
||||
this.highlighted = false;
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
_onMouseDown: function(event) {
|
||||
if (event.target.nodeName !== "a") {
|
||||
this.highlighted = false;
|
||||
this.hovered = false;
|
||||
this.markup.navigate(this);
|
||||
event.stopPropagation();
|
||||
}
|
||||
@ -1319,10 +1434,10 @@ MarkupContainer.prototype = {
|
||||
set flashed(aValue) {
|
||||
if (aValue) {
|
||||
// Make sure the animation class is not here
|
||||
this.highlighter.classList.remove("flash-out");
|
||||
this.tagState.classList.remove("flash-out");
|
||||
|
||||
// Change the background
|
||||
this.highlighter.classList.add("theme-bg-contrast");
|
||||
this.tagState.classList.add("theme-bg-contrast");
|
||||
|
||||
// Change the text color
|
||||
this.editor.elt.classList.add("theme-fg-contrast");
|
||||
@ -1332,10 +1447,10 @@ MarkupContainer.prototype = {
|
||||
);
|
||||
} else {
|
||||
// Add the animation class to smoothly remove the background
|
||||
this.highlighter.classList.add("flash-out");
|
||||
this.tagState.classList.add("flash-out");
|
||||
|
||||
// Remove the background
|
||||
this.highlighter.classList.remove("theme-bg-contrast");
|
||||
this.tagState.classList.remove("theme-bg-contrast");
|
||||
|
||||
// Remove the text color
|
||||
this.editor.elt.classList.remove("theme-fg-contrast");
|
||||
@ -1346,27 +1461,27 @@ MarkupContainer.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_highlighted: false,
|
||||
_hovered: false,
|
||||
|
||||
/**
|
||||
* Highlight the currently hovered tag + its closing tag if necessary
|
||||
* (that is if the tag is expanded)
|
||||
*/
|
||||
set highlighted(aValue) {
|
||||
this.highlighter.classList.remove("flash-out");
|
||||
this._highlighted = aValue;
|
||||
set hovered(aValue) {
|
||||
this.tagState.classList.remove("flash-out");
|
||||
this._hovered = aValue;
|
||||
if (aValue) {
|
||||
if (!this.selected) {
|
||||
this.highlighter.classList.add("theme-bg-darker");
|
||||
this.tagState.classList.add("theme-bg-darker");
|
||||
}
|
||||
if (this.closeTagLine) {
|
||||
this.closeTagLine.querySelector(".highlighter").classList.add(
|
||||
this.closeTagLine.querySelector(".tag-state").classList.add(
|
||||
"theme-bg-darker");
|
||||
}
|
||||
} else {
|
||||
this.highlighter.classList.remove("theme-bg-darker");
|
||||
this.tagState.classList.remove("theme-bg-darker");
|
||||
if (this.closeTagLine) {
|
||||
this.closeTagLine.querySelector(".highlighter").classList.remove(
|
||||
this.closeTagLine.querySelector(".tag-state").classList.remove(
|
||||
"theme-bg-darker");
|
||||
}
|
||||
}
|
||||
@ -1389,15 +1504,15 @@ MarkupContainer.prototype = {
|
||||
},
|
||||
|
||||
set selected(aValue) {
|
||||
this.highlighter.classList.remove("flash-out");
|
||||
this.tagState.classList.remove("flash-out");
|
||||
this._selected = aValue;
|
||||
this.editor.selected = aValue;
|
||||
if (this._selected) {
|
||||
this.tagLine.setAttribute("selected", "");
|
||||
this.highlighter.classList.add("theme-selected");
|
||||
this.tagState.classList.add("theme-selected");
|
||||
} else {
|
||||
this.tagLine.removeAttribute("selected");
|
||||
this.highlighter.classList.remove("theme-selected");
|
||||
this.tagState.classList.remove("theme-selected");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
<ul class="children">
|
||||
<li id="template-container" save="${elt}" class="child collapsed">
|
||||
<div save="${tagLine}" class="tag-line"><span save="${highlighter}" class="highlighter"></span><span save="${expander}" class="theme-twisty expander"></span></div>
|
||||
<div save="${tagLine}" class="tag-line"><span save="${tagState}" class="tag-state"></span><span save="${expander}" class="theme-twisty expander"></span></div>
|
||||
<ul save="${children}" class="children"></ul>
|
||||
</li>
|
||||
|
||||
|
@ -681,9 +681,6 @@ function test() {
|
||||
assertAttributes(doc.querySelector("#node18"), {
|
||||
id: "node18",
|
||||
});
|
||||
|
||||
is(inspector.highlighter.nodeInfo.classesBox.textContent, "",
|
||||
"No classes in the infobar before edit.");
|
||||
},
|
||||
execute: function(after) {
|
||||
inspector.once("markupmutation", function() {
|
||||
@ -701,9 +698,6 @@ function test() {
|
||||
class: "newclass",
|
||||
style: "color:green"
|
||||
});
|
||||
|
||||
is(inspector.highlighter.nodeInfo.classesBox.textContent, ".newclass",
|
||||
"Correct classes in the infobar after edit.");
|
||||
}
|
||||
};
|
||||
testAsyncSetup(test, editTagName);
|
||||
|
@ -123,7 +123,7 @@ function test() {
|
||||
if(!container) {
|
||||
ok(false, "Node not found");
|
||||
} else {
|
||||
ok(container.highlighter.classList.contains("theme-bg-contrast"),
|
||||
ok(container.tagState.classList.contains("theme-bg-contrast"),
|
||||
"Node is flashing");
|
||||
}
|
||||
}
|
||||
|
@ -391,9 +391,6 @@ CssHtmlTree.prototype = {
|
||||
onItem: (aPropView) => {
|
||||
aPropView.refresh();
|
||||
},
|
||||
onCancel: () => {
|
||||
deferred.reject("refresh cancelled");
|
||||
},
|
||||
onDone: () => {
|
||||
this._refreshProcess = null;
|
||||
this.noResults.hidden = this.numVisibleProperties > 0;
|
||||
|
@ -1384,7 +1384,7 @@ CssRuleView.prototype = {
|
||||
let elementStyle = this._elementStyle;
|
||||
return this._elementStyle.populate().then(() => {
|
||||
if (this._elementStyle != elementStyle) {
|
||||
return promise.reject("element changed");
|
||||
return;
|
||||
}
|
||||
this._createEditors();
|
||||
|
||||
|
@ -82,9 +82,6 @@ function RuleViewTool(aInspector, aWindow, aIFrame)
|
||||
this.inspector.on("layout-change", this.refresh);
|
||||
|
||||
this.inspector.selection.on("pseudoclass", this.refresh);
|
||||
if (this.inspector.highlighter) {
|
||||
this.inspector.highlighter.on("locked", this._onSelect);
|
||||
}
|
||||
|
||||
this.onSelect();
|
||||
}
|
||||
@ -102,15 +99,6 @@ RuleViewTool.prototype = {
|
||||
}
|
||||
|
||||
if (!aEvent || aEvent == "new-node-front") {
|
||||
if (this.inspector.selection.reason == "highlighter") {
|
||||
this.view.highlight(null);
|
||||
} else {
|
||||
let done = this.inspector.updating("rule-view");
|
||||
this.view.highlight(this.inspector.selection.nodeFront).then(done, done);
|
||||
}
|
||||
}
|
||||
|
||||
if (aEvent == "locked") {
|
||||
let done = this.inspector.updating("rule-view");
|
||||
this.view.highlight(this.inspector.selection.nodeFront).then(done, done);
|
||||
}
|
||||
@ -124,9 +112,6 @@ RuleViewTool.prototype = {
|
||||
this.inspector.off("layout-change", this.refresh);
|
||||
this.inspector.selection.off("pseudoclass", this.refresh);
|
||||
this.inspector.selection.off("new-node-front", this._onSelect);
|
||||
if (this.inspector.highlighter) {
|
||||
this.inspector.highlighter.off("locked", this._onSelect);
|
||||
}
|
||||
|
||||
this.view.element.removeEventListener("CssRuleViewCSSLinkClicked",
|
||||
this._cssLinkHandler);
|
||||
@ -159,9 +144,6 @@ function ComputedViewTool(aInspector, aWindow, aIFrame)
|
||||
this._onSelect = this.onSelect.bind(this);
|
||||
this.inspector.selection.on("detached", this._onSelect);
|
||||
this.inspector.selection.on("new-node-front", this._onSelect);
|
||||
if (this.inspector.highlighter) {
|
||||
this.inspector.highlighter.on("locked", this._onSelect);
|
||||
}
|
||||
this.refresh = this.refresh.bind(this);
|
||||
this.inspector.on("layout-change", this.refresh);
|
||||
this.inspector.selection.on("pseudoclass", this.refresh);
|
||||
@ -185,17 +167,6 @@ ComputedViewTool.prototype = {
|
||||
}
|
||||
|
||||
if (!aEvent || aEvent == "new-node-front") {
|
||||
if (this.inspector.selection.reason == "highlighter") {
|
||||
// FIXME: We should hide view's content
|
||||
} else {
|
||||
let done = this.inspector.updating("computed-view");
|
||||
this.view.highlight(this.inspector.selection.nodeFront).then(() => {
|
||||
done();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (aEvent == "locked" && this.inspector.selection.nodeFront != this.view.viewedElement) {
|
||||
let done = this.inspector.updating("computed-view");
|
||||
this.view.highlight(this.inspector.selection.nodeFront).then(() => {
|
||||
done();
|
||||
@ -213,9 +184,6 @@ ComputedViewTool.prototype = {
|
||||
this.inspector.sidebar.off("computedview-selected", this.refresh);
|
||||
this.inspector.selection.off("pseudoclass", this.refresh);
|
||||
this.inspector.selection.off("new-node-front", this._onSelect);
|
||||
if (this.inspector.highlighter) {
|
||||
this.inspector.highlighter.off("locked", this._onSelect);
|
||||
}
|
||||
|
||||
this.view.destroy();
|
||||
delete this.view;
|
||||
|
@ -67,17 +67,18 @@ function testModifyRules()
|
||||
rule.editor.addProperty("font-weight", "bold", "");
|
||||
}
|
||||
|
||||
reselectElement(doc.querySelector("#target"), () => {
|
||||
executeSoon(() => {
|
||||
reselectElement(doc.querySelector("#target"), () => {
|
||||
for (let rule of ruleView._elementStyle.rules) {
|
||||
let lastRule = rule.textProps[rule.textProps.length - 1];
|
||||
|
||||
for (let rule of ruleView._elementStyle.rules) {
|
||||
let lastRule = rule.textProps[rule.textProps.length - 1];
|
||||
is (lastRule.name, "font-weight", "Last rule name is font-weight");
|
||||
is (lastRule.value, "bold", "Last rule value is bold");
|
||||
}
|
||||
|
||||
is (lastRule.name, "font-weight", "Last rule name is font-weight");
|
||||
is (lastRule.value, "bold", "Last rule value is bold");
|
||||
}
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
openXUL();
|
||||
gBrowser.removeCurrentTab();
|
||||
openXUL();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -20,34 +20,35 @@ function simpleOverride(aInspector, aRuleView)
|
||||
|
||||
let styleNode = addStyle(doc, style);
|
||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||
inspector.once("markupmutation", () => {
|
||||
inspector.selection.setNode(doc.getElementById("testid"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
let elementStyle = view._elementStyle;
|
||||
|
||||
inspector.selection.setNode(doc.getElementById("testid"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
let elementStyle = view._elementStyle;
|
||||
let idRule = elementStyle.rules[1];
|
||||
let idProp = idRule.textProps[0];
|
||||
is(idProp.name, "background-color", "First ID prop should be background-color");
|
||||
ok(!idProp.overridden, "ID prop should not be overridden.");
|
||||
|
||||
let idRule = elementStyle.rules[1];
|
||||
let idProp = idRule.textProps[0];
|
||||
is(idProp.name, "background-color", "First ID prop should be background-color");
|
||||
ok(!idProp.overridden, "ID prop should not be overridden.");
|
||||
let classRule = elementStyle.rules[2];
|
||||
let classProp = classRule.textProps[0];
|
||||
is(classProp.name, "background-color", "First class prop should be background-color");
|
||||
ok(classProp.overridden, "Class property should be overridden.");
|
||||
|
||||
let classRule = elementStyle.rules[2];
|
||||
let classProp = classRule.textProps[0];
|
||||
is(classProp.name, "background-color", "First class prop should be background-color");
|
||||
ok(classProp.overridden, "Class property should be overridden.");
|
||||
// Override background-color by changing the element style.
|
||||
let elementRule = elementStyle.rules[0];
|
||||
elementRule.createProperty("background-color", "purple", "");
|
||||
promiseDone(elementRule._applyingModifications.then(() => {
|
||||
let elementProp = elementRule.textProps[0];
|
||||
is(classProp.name, "background-color", "First element prop should now be background-color");
|
||||
ok(!elementProp.overridden, "Element style property should not be overridden");
|
||||
ok(idProp.overridden, "ID property should be overridden");
|
||||
ok(classProp.overridden, "Class property should be overridden");
|
||||
|
||||
// Override background-color by changing the element style.
|
||||
let elementRule = elementStyle.rules[0];
|
||||
elementRule.createProperty("background-color", "purple", "");
|
||||
promiseDone(elementRule._applyingModifications.then(() => {
|
||||
let elementProp = elementRule.textProps[0];
|
||||
is(classProp.name, "background-color", "First element prop should now be background-color");
|
||||
ok(!elementProp.overridden, "Element style property should not be overridden");
|
||||
ok(idProp.overridden, "ID property should be overridden");
|
||||
ok(classProp.overridden, "Class property should be overridden");
|
||||
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
partialOverride();
|
||||
}));
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
partialOverride();
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -65,25 +66,26 @@ function partialOverride()
|
||||
|
||||
let styleNode = addStyle(doc, style);
|
||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||
inspector.once("markupmutation", () => {
|
||||
inspector.selection.setNode(doc.getElementById("testid"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
let elementStyle = view._elementStyle;
|
||||
|
||||
inspector.selection.setNode(doc.getElementById("testid"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
let elementStyle = view._elementStyle;
|
||||
|
||||
let classRule = elementStyle.rules[2];
|
||||
let classProp = classRule.textProps[0];
|
||||
ok(!classProp.overridden, "Class prop shouldn't be overridden, some props are still being used.");
|
||||
for (let computed of classProp.computed) {
|
||||
if (computed.name.indexOf("margin-left") == 0) {
|
||||
ok(computed.overridden, "margin-left props should be overridden.");
|
||||
} else {
|
||||
ok(!computed.overridden, "Non-margin-left props should not be overridden.");
|
||||
let classRule = elementStyle.rules[2];
|
||||
let classProp = classRule.textProps[0];
|
||||
ok(!classProp.overridden, "Class prop shouldn't be overridden, some props are still being used.");
|
||||
for (let computed of classProp.computed) {
|
||||
if (computed.name.indexOf("margin-left") == 0) {
|
||||
ok(computed.overridden, "margin-left props should be overridden.");
|
||||
} else {
|
||||
ok(!computed.overridden, "Non-margin-left props should not be overridden.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
|
||||
importantOverride();
|
||||
importantOverride();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -100,29 +102,30 @@ function importantOverride()
|
||||
'}';
|
||||
let styleNode = addStyle(doc, style);
|
||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||
inspector.once("markupmutation", () => {
|
||||
inspector.selection.setNode(doc.getElementById("testid"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
let elementStyle = view._elementStyle;
|
||||
|
||||
inspector.selection.setNode(doc.getElementById("testid"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
let elementStyle = view._elementStyle;
|
||||
let idRule = elementStyle.rules[1];
|
||||
let idProp = idRule.textProps[0];
|
||||
ok(idProp.overridden, "Not-important rule should be overridden.");
|
||||
|
||||
let idRule = elementStyle.rules[1];
|
||||
let idProp = idRule.textProps[0];
|
||||
ok(idProp.overridden, "Not-important rule should be overridden.");
|
||||
let classRule = elementStyle.rules[2];
|
||||
let classProp = classRule.textProps[0];
|
||||
ok(!classProp.overridden, "Important rule should not be overridden.");
|
||||
|
||||
let classRule = elementStyle.rules[2];
|
||||
let classProp = classRule.textProps[0];
|
||||
ok(!classProp.overridden, "Important rule should not be overridden.");
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
let elementRule = elementStyle.rules[0];
|
||||
let elementProp = elementRule.createProperty("background-color", "purple", "important");
|
||||
promiseDone(elementRule._applyingModifications.then(() => {
|
||||
ok(classProp.overridden, "New important prop should override class property.");
|
||||
ok(!elementProp.overridden, "New important prop should not be overriden.");
|
||||
|
||||
let elementRule = elementStyle.rules[0];
|
||||
let elementProp = elementRule.createProperty("background-color", "purple", "important");
|
||||
promiseDone(elementRule._applyingModifications.then(() => {
|
||||
ok(classProp.overridden, "New important prop should override class property.");
|
||||
ok(!elementProp.overridden, "New important prop should not be overriden.");
|
||||
|
||||
disableOverride();
|
||||
}));
|
||||
disableOverride();
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -137,23 +140,24 @@ function disableOverride()
|
||||
'}';
|
||||
let styleNode = addStyle(doc, style);
|
||||
doc.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
|
||||
inspector.once("markupmutation", () => {
|
||||
inspector.selection.setNode(doc.getElementById("testid"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
let elementStyle = view._elementStyle;
|
||||
|
||||
inspector.selection.setNode(doc.getElementById("testid"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
let elementStyle = view._elementStyle;
|
||||
let idRule = elementStyle.rules[1];
|
||||
let idProp = idRule.textProps[0];
|
||||
idProp.setEnabled(false);
|
||||
promiseDone(idRule._applyingModifications.then(() => {
|
||||
let classRule = elementStyle.rules[2];
|
||||
let classProp = classRule.textProps[0];
|
||||
ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
|
||||
|
||||
let idRule = elementStyle.rules[1];
|
||||
let idProp = idRule.textProps[0];
|
||||
idProp.setEnabled(false);
|
||||
promiseDone(idRule._applyingModifications.then(() => {
|
||||
let classRule = elementStyle.rules[2];
|
||||
let classProp = classRule.textProps[0];
|
||||
ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
|
||||
finishTest();
|
||||
}));
|
||||
finishTest();
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -272,10 +272,11 @@ TiltVisualizer.prototype = {
|
||||
}
|
||||
let nodeIndex = this.presenter._currentSelection;
|
||||
if (nodeIndex < 0) {
|
||||
this.inspector.selection.setNode(null, "tilt");
|
||||
this.inspector.selection.setNodeFront(null, "tilt");
|
||||
}
|
||||
let node = this.presenter._traverseData.nodes[nodeIndex];
|
||||
this.inspector.selection.setNode(node, "tilt");
|
||||
node = this.inspector.walker.frontForRawNode(node);
|
||||
this.inspector.selection.setNodeFront(node, "tilt");
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -5,11 +5,13 @@
|
||||
|
||||
// Tests that the $0 console helper works as intended.
|
||||
|
||||
let inspector, h1;
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
let doc = content.document;
|
||||
let div = doc.createElement("div");
|
||||
let h1 = doc.createElement("h1");
|
||||
h1 = doc.createElement("h1");
|
||||
let p1 = doc.createElement("p");
|
||||
let p2 = doc.createElement("p");
|
||||
let div2 = doc.createElement("div");
|
||||
@ -42,39 +44,37 @@ function createDocument()
|
||||
|
||||
function setupHighlighterTests()
|
||||
{
|
||||
let h1 = content.document.querySelector("h1");
|
||||
ok(h1, "we have the header node");
|
||||
|
||||
openInspector(runSelectionTests);
|
||||
}
|
||||
|
||||
function runSelectionTests(aInspector)
|
||||
{
|
||||
aInspector.highlighter.unlockAndFocus();
|
||||
aInspector.highlighter.outline.setAttribute("disable-transitions", "true");
|
||||
|
||||
executeSoon(function() {
|
||||
aInspector.selection.once("new-node", performTestComparisons);
|
||||
let h1 = content.document.querySelector("h1");
|
||||
inspector = aInspector;
|
||||
inspector.toolbox.startPicker();
|
||||
inspector.toolbox.once("picker-started", () => {
|
||||
EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
|
||||
inspector.toolbox.once("picker-node-hovered", () => {
|
||||
executeSoon(performTestComparisons);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getHighlighterOutline()
|
||||
{
|
||||
return gBrowser.selectedBrowser.parentNode
|
||||
.querySelector(".highlighter-container .highlighter-outline");
|
||||
}
|
||||
|
||||
function performTestComparisons()
|
||||
{
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
let inspector = gDevTools.getToolbox(target).getPanel("inspector");
|
||||
inspector.highlighter.lock();
|
||||
let outline = getHighlighterOutline();
|
||||
ok(outline && !outline.hasAttribute("hidden"), "inspector is highlighting");
|
||||
|
||||
let isHighlighting =
|
||||
!(inspector.highlighter.outline.getAttribute("hidden") == "true");
|
||||
|
||||
ok(isHighlighting, "inspector is highlighting");
|
||||
|
||||
let h1 = content.document.querySelector("h1");
|
||||
is(inspector.selection.node, h1, "selection matches node");
|
||||
|
||||
openConsole(gBrowser.selectedTab, performWebConsoleTests);
|
||||
EventUtils.synthesizeMouseAtCenter(h1, {}, content);
|
||||
inspector.toolbox.once("picker-stopped", () => {
|
||||
openConsole(gBrowser.selectedTab, performWebConsoleTests);
|
||||
});
|
||||
}
|
||||
|
||||
function performWebConsoleTests(hud)
|
||||
@ -98,10 +98,10 @@ function performWebConsoleTests(hud)
|
||||
{
|
||||
isnot(node.textContent.indexOf("bug653531"), -1,
|
||||
"correct output for $0.textContent");
|
||||
let inspector = gDevTools.getToolbox(target).getPanel("inspector");
|
||||
is(inspector.selection.node.textContent, "bug653531",
|
||||
"node successfully updated");
|
||||
|
||||
inspector = h1 = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finishTest();
|
||||
}
|
||||
@ -110,6 +110,7 @@ function performWebConsoleTests(hud)
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
@ -118,4 +119,3 @@ function test()
|
||||
|
||||
content.location = "data:text/html;charset=utf-8,test for highlighter helper in web console";
|
||||
}
|
||||
|
||||
|
@ -188,6 +188,16 @@ function isShumwayEnabledFor(actions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function fallbackToNativePlugin(window, userAction, activateCTP) {
|
||||
var obj = window.frameElement;
|
||||
var doc = obj.ownerDocument;
|
||||
var e = doc.createEvent("CustomEvent");
|
||||
e.initCustomEvent("MozPlayPlugin", true, true, activateCTP);
|
||||
obj.dispatchEvent(e);
|
||||
|
||||
ShumwayTelemetry.onFallback(userAction);
|
||||
}
|
||||
|
||||
// All the priviledged actions.
|
||||
function ChromeActions(url, window, document) {
|
||||
this.url = url;
|
||||
@ -356,13 +366,8 @@ ChromeActions.prototype = {
|
||||
});
|
||||
},
|
||||
fallback: function(automatic) {
|
||||
var obj = this.window.frameElement;
|
||||
var doc = obj.ownerDocument;
|
||||
var e = doc.createEvent("CustomEvent");
|
||||
e.initCustomEvent("MozPlayPlugin", true, true, null);
|
||||
obj.dispatchEvent(e);
|
||||
|
||||
ShumwayTelemetry.onFallback(!automatic);
|
||||
automatic = !!automatic; // cast to boolean
|
||||
fallbackToNativePlugin(this.window, !automatic, automatic);
|
||||
},
|
||||
setClipboard: function (data) {
|
||||
if (typeof data !== 'string' ||
|
||||
@ -902,7 +907,7 @@ ShumwayStreamConverterBase.prototype = {
|
||||
converter.getUrlHint(originalURI));
|
||||
|
||||
if (!isShumwayEnabledFor(actions)) {
|
||||
actions.fallback(true);
|
||||
fallbackToNativePlugin(domWindow, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
Binary file not shown.
@ -187,32 +187,12 @@ function cloneObject(obj) {
|
||||
clone[prop] = obj[prop];
|
||||
return clone;
|
||||
}
|
||||
function sortByDepth(a, b) {
|
||||
var levelA = a._level;
|
||||
var levelB = b._level;
|
||||
if (a._parent !== b._parent && a._index > -1 && b._index > -1) {
|
||||
while (a._level > levelB) {
|
||||
a = a._parent;
|
||||
}
|
||||
while (b._level > levelA) {
|
||||
b = b._parent;
|
||||
}
|
||||
while (a._level > 1) {
|
||||
if (a._parent === b._parent) {
|
||||
break;
|
||||
}
|
||||
a = a._parent;
|
||||
b = b._parent;
|
||||
}
|
||||
}
|
||||
if (a === b) {
|
||||
return levelA - levelB;
|
||||
}
|
||||
return a._index - b._index;
|
||||
}
|
||||
function sortNumeric(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
function sortByZindex(a, b) {
|
||||
return a._zindex - b._zindex;
|
||||
}
|
||||
function rgbaObjToStr(color) {
|
||||
return 'rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + color.alpha / 255 + ')';
|
||||
}
|
||||
@ -296,158 +276,336 @@ function randomStyle() {
|
||||
}
|
||||
return randomStyleCache[nextStyle++ % randomStyleCache.length];
|
||||
}
|
||||
var Promise = function PromiseClosure() {
|
||||
function isPromise(obj) {
|
||||
return typeof obj === 'object' && obj !== null && typeof obj.then === 'function';
|
||||
}
|
||||
function defaultOnFulfilled(value) {
|
||||
return value;
|
||||
}
|
||||
function defaultOnRejected(reason) {
|
||||
throw reason;
|
||||
}
|
||||
function propagateFulfilled(subject, value) {
|
||||
subject.subpromisesValue = value;
|
||||
var subpromises = subject.subpromises;
|
||||
if (!subpromises) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < subpromises.length; i++) {
|
||||
subpromises[i].fulfill(value);
|
||||
}
|
||||
delete subject.subpromises;
|
||||
}
|
||||
function propagateRejected(subject, reason) {
|
||||
subject.subpromisesReason = reason;
|
||||
var subpromises = subject.subpromises;
|
||||
if (!subpromises) {
|
||||
if (!true) {
|
||||
console.warn(reason);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < subpromises.length; i++) {
|
||||
subpromises[i].reject(reason);
|
||||
}
|
||||
delete subject.subpromises;
|
||||
}
|
||||
function performCall(callback, arg, subject) {
|
||||
try {
|
||||
var value = callback(arg);
|
||||
if (isPromise(value)) {
|
||||
value.then(function Promise_queueCall_onFulfilled(value) {
|
||||
propagateFulfilled(subject, value);
|
||||
}, function Promise_queueCall_onRejected(reason) {
|
||||
propagateRejected(subject, reason);
|
||||
(function PromiseClosure() {
|
||||
var global = Function('return this')();
|
||||
if (global.Promise) {
|
||||
if (typeof global.Promise.all !== 'function') {
|
||||
global.Promise.all = function (iterable) {
|
||||
var count = 0, results = [], resolve, reject;
|
||||
var promise = new global.Promise(function (resolve_, reject_) {
|
||||
resolve = resolve_;
|
||||
reject = reject_;
|
||||
});
|
||||
return;
|
||||
iterable.forEach(function (p, i) {
|
||||
count++;
|
||||
p.then(function (result) {
|
||||
results[i] = result;
|
||||
count--;
|
||||
if (count === 0) {
|
||||
resolve(results);
|
||||
}
|
||||
}, reject);
|
||||
});
|
||||
if (count === 0) {
|
||||
resolve(results);
|
||||
}
|
||||
propagateFulfilled(subject, value);
|
||||
} catch (ex) {
|
||||
propagateRejected(subject, ex);
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
var queue = [];
|
||||
function processQueue() {
|
||||
while (queue.length > 0) {
|
||||
var task = queue[0];
|
||||
if (task.directCallback) {
|
||||
task.callback.call(task.subject, task.arg);
|
||||
} else {
|
||||
performCall(task.callback, task.arg, task.subject);
|
||||
}
|
||||
queue.shift();
|
||||
}
|
||||
if (typeof global.Promise.resolve !== 'function') {
|
||||
global.Promise.resolve = function (x) {
|
||||
return new global.Promise(function (resolve) {
|
||||
resolve(x);
|
||||
});
|
||||
};
|
||||
}
|
||||
function queueCall(callback, arg, subject, directCallback) {
|
||||
if (queue.length === 0) {
|
||||
setTimeout(processQueue, 0);
|
||||
return;
|
||||
}
|
||||
function getDeferred(C) {
|
||||
if (typeof C !== 'function') {
|
||||
throw new TypeError('Invalid deferred constructor');
|
||||
}
|
||||
var resolver = createDeferredConstructionFunctions();
|
||||
var promise = new C(resolver);
|
||||
var resolve = resolver.resolve;
|
||||
if (typeof resolve !== 'function') {
|
||||
throw new TypeError('Invalid resolve construction function');
|
||||
}
|
||||
var reject = resolver.reject;
|
||||
if (typeof reject !== 'function') {
|
||||
throw new TypeError('Invalid reject construction function');
|
||||
}
|
||||
return {
|
||||
promise: promise,
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
}
|
||||
function updateDeferredFromPotentialThenable(x, deferred) {
|
||||
if (typeof x !== 'object' || x === null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
var then = x.then;
|
||||
if (typeof then !== 'function') {
|
||||
return false;
|
||||
}
|
||||
queue.push({
|
||||
callback: callback,
|
||||
arg: arg,
|
||||
subject: subject,
|
||||
directCallback: directCallback
|
||||
var thenCallResult = then.call(x, deferred.resolve, deferred.reject);
|
||||
} catch (e) {
|
||||
var reject = deferred.reject;
|
||||
reject(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function isPromise(x) {
|
||||
return typeof x === 'object' && x !== null && typeof x.promiseStatus !== 'undefined';
|
||||
}
|
||||
function rejectPromise(promise, reason) {
|
||||
if (promise.promiseStatus !== 'unresolved') {
|
||||
return;
|
||||
}
|
||||
var reactions = promise.rejectReactions;
|
||||
promise.result = reason;
|
||||
promise.resolveReactions = undefined;
|
||||
promise.rejectReactions = undefined;
|
||||
promise.promiseStatus = 'has-rejection';
|
||||
triggerPromiseReactions(reactions, reason);
|
||||
}
|
||||
function resolvePromise(promise, resolution) {
|
||||
if (promise.promiseStatus !== 'unresolved') {
|
||||
return;
|
||||
}
|
||||
var reactions = promise.resolveReactions;
|
||||
promise.result = resolution;
|
||||
promise.resolveReactions = undefined;
|
||||
promise.rejectReactions = undefined;
|
||||
promise.promiseStatus = 'has-resolution';
|
||||
triggerPromiseReactions(reactions, resolution);
|
||||
}
|
||||
function triggerPromiseReactions(reactions, argument) {
|
||||
for (var i = 0; i < reactions.length; i++) {
|
||||
queueMicrotask({
|
||||
reaction: reactions[i],
|
||||
argument: argument
|
||||
});
|
||||
}
|
||||
function Promise(onFulfilled, onRejected) {
|
||||
this.state = 'pending';
|
||||
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : defaultOnFulfilled;
|
||||
this.onRejected = typeof onRejected === 'function' ? onRejected : defaultOnRejected;
|
||||
}
|
||||
function queueMicrotask(task) {
|
||||
if (microtasksQueue.length === 0) {
|
||||
setTimeout(handleMicrotasksQueue, 0);
|
||||
}
|
||||
Promise.prototype = {
|
||||
fulfill: function Promise_resolve(value) {
|
||||
if (this.state !== 'pending') {
|
||||
return;
|
||||
microtasksQueue.push(task);
|
||||
}
|
||||
function executePromiseReaction(reaction, argument) {
|
||||
var deferred = reaction.deferred;
|
||||
var handler = reaction.handler;
|
||||
var handlerResult, updateResult;
|
||||
try {
|
||||
handlerResult = handler(argument);
|
||||
} catch (e) {
|
||||
var reject = deferred.reject;
|
||||
return reject(e);
|
||||
}
|
||||
if (handlerResult === deferred.promise) {
|
||||
var reject = deferred.reject;
|
||||
return reject(new TypeError('Self resolution'));
|
||||
}
|
||||
try {
|
||||
updateResult = updateDeferredFromPotentialThenable(handlerResult, deferred);
|
||||
if (!updateResult) {
|
||||
var resolve = deferred.resolve;
|
||||
return resolve(handlerResult);
|
||||
}
|
||||
} catch (e) {
|
||||
var reject = deferred.reject;
|
||||
return reject(e);
|
||||
}
|
||||
}
|
||||
var microtasksQueue = [];
|
||||
function handleMicrotasksQueue() {
|
||||
while (microtasksQueue.length > 0) {
|
||||
var task = microtasksQueue[0];
|
||||
try {
|
||||
executePromiseReaction(task.reaction, task.argument);
|
||||
} catch (e) {
|
||||
if (typeof Promise.onerror === 'function') {
|
||||
Promise.onerror(e);
|
||||
}
|
||||
this.state = 'fulfilled';
|
||||
this.value = value;
|
||||
queueCall(this.onFulfilled, value, this, false);
|
||||
},
|
||||
reject: function Promise_reject(reason) {
|
||||
if (this.state !== 'pending') {
|
||||
return;
|
||||
}
|
||||
microtasksQueue.shift();
|
||||
}
|
||||
}
|
||||
function throwerFunction(e) {
|
||||
throw e;
|
||||
}
|
||||
function identityFunction(x) {
|
||||
return x;
|
||||
}
|
||||
function createRejectPromiseFunction(promise) {
|
||||
return function (reason) {
|
||||
rejectPromise(promise, reason);
|
||||
};
|
||||
}
|
||||
function createResolvePromiseFunction(promise) {
|
||||
return function (resolution) {
|
||||
resolvePromise(promise, resolution);
|
||||
};
|
||||
}
|
||||
function createDeferredConstructionFunctions() {
|
||||
var fn = function (resolve, reject) {
|
||||
fn.resolve = resolve;
|
||||
fn.reject = reject;
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
function createPromiseResolutionHandlerFunctions(promise, fulfillmentHandler, rejectionHandler) {
|
||||
return function (x) {
|
||||
if (x === promise) {
|
||||
return rejectionHandler(new TypeError('Self resolution'));
|
||||
}
|
||||
var cstr = promise.promiseConstructor;
|
||||
if (isPromise(x)) {
|
||||
var xConstructor = x.promiseConstructor;
|
||||
if (xConstructor === cstr) {
|
||||
return x.then(fulfillmentHandler, rejectionHandler);
|
||||
}
|
||||
this.state = 'rejected';
|
||||
this.reason = reason;
|
||||
queueCall(this.onRejected, reason, this, false);
|
||||
},
|
||||
then: function Promise_then(onFulfilled, onRejected) {
|
||||
var promise = new Promise(onFulfilled, onRejected);
|
||||
if ('subpromisesValue' in this) {
|
||||
queueCall(promise.fulfill, this.subpromisesValue, promise, true);
|
||||
} else if ('subpromisesReason' in this) {
|
||||
queueCall(promise.reject, this.subpromisesReason, promise, true);
|
||||
} else {
|
||||
var subpromises = this.subpromises || (this.subpromises = []);
|
||||
subpromises.push(promise);
|
||||
}
|
||||
return promise;
|
||||
},
|
||||
get resolved() {
|
||||
return this.state === 'fulfilled';
|
||||
},
|
||||
resolve: function (value) {
|
||||
this.fulfill(value);
|
||||
}
|
||||
var deferred = getDeferred(cstr);
|
||||
var updateResult = updateDeferredFromPotentialThenable(x, deferred);
|
||||
if (updateResult) {
|
||||
var deferredPromise = deferred.promise;
|
||||
return deferredPromise.then(fulfillmentHandler, rejectionHandler);
|
||||
}
|
||||
return fulfillmentHandler(x);
|
||||
};
|
||||
}
|
||||
function createPromiseAllCountdownFunction(index, values, deferred, countdownHolder) {
|
||||
return function (x) {
|
||||
values[index] = x;
|
||||
countdownHolder.countdown--;
|
||||
if (countdownHolder.countdown === 0) {
|
||||
deferred.resolve(values);
|
||||
}
|
||||
};
|
||||
Promise.when = function Promise_when() {
|
||||
var promise = new Promise();
|
||||
if (arguments.length === 0) {
|
||||
promise.resolve();
|
||||
return promise;
|
||||
}
|
||||
function Promise(resolver) {
|
||||
if (typeof resolver !== 'function') {
|
||||
throw new TypeError('resolver is not a function');
|
||||
}
|
||||
var promise = this;
|
||||
if (typeof promise !== 'object') {
|
||||
throw new TypeError('Promise to initialize is not an object');
|
||||
}
|
||||
promise.promiseStatus = 'unresolved';
|
||||
promise.resolveReactions = [];
|
||||
promise.rejectReactions = [];
|
||||
promise.result = undefined;
|
||||
var resolve = createResolvePromiseFunction(promise);
|
||||
var reject = createRejectPromiseFunction(promise);
|
||||
try {
|
||||
var result = resolver(resolve, reject);
|
||||
} catch (e) {
|
||||
rejectPromise(promise, e);
|
||||
}
|
||||
promise.promiseConstructor = Promise;
|
||||
return promise;
|
||||
}
|
||||
Promise.all = function (iterable) {
|
||||
var deferred = getDeferred(this);
|
||||
var values = [];
|
||||
var countdownHolder = {
|
||||
countdown: 0
|
||||
};
|
||||
var index = 0;
|
||||
iterable.forEach(function (nextValue) {
|
||||
var nextPromise = this.cast(nextValue);
|
||||
var fn = createPromiseAllCountdownFunction(index, values, deferred, countdownHolder);
|
||||
nextPromise.then(fn, deferred.reject);
|
||||
index++;
|
||||
countdownHolder.countdown++;
|
||||
}, this);
|
||||
if (index === 0) {
|
||||
deferred.resolve(values);
|
||||
}
|
||||
return deferred.promise;
|
||||
};
|
||||
Promise.cast = function (x) {
|
||||
if (isPromise(x)) {
|
||||
return x;
|
||||
}
|
||||
var deferred = getDeferred(this);
|
||||
deferred.resolve(x);
|
||||
return deferred.promise;
|
||||
};
|
||||
Promise.reject = function (r) {
|
||||
var deferred = getDeferred(this);
|
||||
var rejectResult = deferred.reject(r);
|
||||
return deferred.promise;
|
||||
};
|
||||
Promise.resolve = function (x) {
|
||||
var deferred = getDeferred(this);
|
||||
var rejectResult = deferred.resolve(x);
|
||||
return deferred.promise;
|
||||
};
|
||||
Promise.prototype = {
|
||||
'catch': function (onRejected) {
|
||||
this.then(undefined, onRejected);
|
||||
},
|
||||
then: function (onFulfilled, onRejected) {
|
||||
var promise = this;
|
||||
if (!isPromise(promise)) {
|
||||
throw new TypeError('this is not a Promises');
|
||||
}
|
||||
var promises = slice.call(arguments, 0);
|
||||
var result = [];
|
||||
var i = 1;
|
||||
function fulfill(value) {
|
||||
result.push(value);
|
||||
if (i < promises.length) {
|
||||
promises[i++].then(fulfill, reject);
|
||||
} else {
|
||||
promise.resolve(result);
|
||||
}
|
||||
return value;
|
||||
var cstr = promise.promiseConstructor;
|
||||
var deferred = getDeferred(cstr);
|
||||
var rejectionHandler = typeof onRejected === 'function' ? onRejected : throwerFunction;
|
||||
var fulfillmentHandler = typeof onFulfilled === 'function' ? onFulfilled : identityFunction;
|
||||
var resolutionHandler = createPromiseResolutionHandlerFunctions(promise, fulfillmentHandler, rejectionHandler);
|
||||
var resolveReaction = {
|
||||
deferred: deferred,
|
||||
handler: resolutionHandler
|
||||
};
|
||||
var rejectReaction = {
|
||||
deferred: deferred,
|
||||
handler: rejectionHandler
|
||||
};
|
||||
switch (promise.promiseStatus) {
|
||||
case 'unresolved':
|
||||
promise.resolveReactions.push(resolveReaction);
|
||||
promise.rejectReactions.push(rejectReaction);
|
||||
break;
|
||||
case 'has-resolution':
|
||||
var resolution = promise.result;
|
||||
queueMicrotask({
|
||||
reaction: resolveReaction,
|
||||
argument: resolution
|
||||
});
|
||||
break;
|
||||
case 'has-rejection':
|
||||
var rejection = promise.result;
|
||||
queueMicrotask({
|
||||
reaction: rejectReaction,
|
||||
argument: rejection
|
||||
});
|
||||
break;
|
||||
}
|
||||
function reject(reason) {
|
||||
promise.reject(reason);
|
||||
}
|
||||
promises[0].then(fulfill, reject);
|
||||
return promise;
|
||||
};
|
||||
return Promise;
|
||||
}();
|
||||
var QuadTree = function (x, y, width, height, level) {
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
global.Promise = Promise;
|
||||
}());
|
||||
var QuadTree = function (x, y, width, height, parent) {
|
||||
this.x = x | 0;
|
||||
this.y = y | 0;
|
||||
this.width = width | 0;
|
||||
this.height = height | 0;
|
||||
this.level = level | 0;
|
||||
this.stuckObjects = [];
|
||||
this.objects = [];
|
||||
if (parent) {
|
||||
this.root = parent.root;
|
||||
this.parent = parent;
|
||||
this.level = parent.level + 1;
|
||||
} else {
|
||||
this.root = this;
|
||||
this.parent = null;
|
||||
this.level = 0;
|
||||
}
|
||||
this.reset();
|
||||
};
|
||||
QuadTree.prototype.reset = function () {
|
||||
this.stuckObjects = null;
|
||||
this.objects = null;
|
||||
this.nodes = [];
|
||||
};
|
||||
QuadTree.prototype._findIndex = function (xMin, yMin, xMax, yMax) {
|
||||
QuadTree.prototype._findIndex = function (xMin, xMax, yMin, yMax) {
|
||||
var midX = this.x + (this.width / 2 | 0);
|
||||
var midY = this.y + (this.height / 2 | 0);
|
||||
var top = yMin < midY && yMax < midY;
|
||||
@ -470,57 +628,114 @@ QuadTree.prototype._findIndex = function (xMin, yMin, xMax, yMax) {
|
||||
QuadTree.prototype.insert = function (obj) {
|
||||
var nodes = this.nodes;
|
||||
if (nodes.length) {
|
||||
var index = this._findIndex(obj.xMin, obj.yMin, obj.xMax, obj.yMax);
|
||||
var index = this._findIndex(obj.xMin, obj.xMax, obj.yMin, obj.yMax);
|
||||
if (index > -1) {
|
||||
nodes[index].insert(obj);
|
||||
} else {
|
||||
this.stuckObjects.push(obj);
|
||||
obj._qtree = this;
|
||||
obj.prev = null;
|
||||
if (this.stuckObjects) {
|
||||
obj.next = this.stuckObjects;
|
||||
this.stuckObjects.prev = obj;
|
||||
} else {
|
||||
obj.next = null;
|
||||
}
|
||||
this.stuckObjects = obj;
|
||||
obj.parent = this;
|
||||
}
|
||||
return;
|
||||
}
|
||||
var objects = this.objects;
|
||||
objects.push(obj);
|
||||
if (objects.length > 4 && this.level < 10) {
|
||||
this._subdivide();
|
||||
while (objects.length) {
|
||||
this.insert(objects.shift());
|
||||
}
|
||||
return;
|
||||
}
|
||||
obj._qtree = this;
|
||||
};
|
||||
QuadTree.prototype.delete = function (obj) {
|
||||
if (obj._qtree !== this) {
|
||||
return;
|
||||
}
|
||||
var index = this.objects.indexOf(obj);
|
||||
if (index > -1) {
|
||||
this.objects.splice(index, 1);
|
||||
var numChildren = 1;
|
||||
var item = this.objects;
|
||||
if (!item) {
|
||||
obj.prev = null;
|
||||
obj.next = null;
|
||||
this.objects = obj;
|
||||
} else {
|
||||
index = this.stuckObjects.indexOf(obj);
|
||||
this.stuckObjects.splice(index, 1);
|
||||
while (item.next) {
|
||||
numChildren++;
|
||||
item = item.next;
|
||||
}
|
||||
obj.prev = item;
|
||||
obj.next = null;
|
||||
item.next = obj;
|
||||
}
|
||||
obj._qtree = null;
|
||||
if (numChildren > 4 && this.level < 10) {
|
||||
this._subdivide();
|
||||
item = this.objects;
|
||||
while (item) {
|
||||
var next = item.next;
|
||||
this.insert(item);
|
||||
item = next;
|
||||
}
|
||||
this.objects = null;
|
||||
return;
|
||||
}
|
||||
obj.parent = this;
|
||||
};
|
||||
QuadTree.prototype._stack = [];
|
||||
QuadTree.prototype._out = [];
|
||||
QuadTree.prototype.retrieve = function (xMin, yMin, xMax, yMax) {
|
||||
var stack = this._stack;
|
||||
var out = this._out;
|
||||
out.length = 0;
|
||||
QuadTree.prototype.update = function (obj) {
|
||||
var node = obj.parent;
|
||||
if (node) {
|
||||
if (obj.xMin >= node.x && obj.xMax <= node.x + node.width && obj.yMin >= node.y && obj.yMax <= node.y + node.height) {
|
||||
if (node.nodes.length) {
|
||||
var index = this._findIndex(obj.xMin, obj.xMax, obj.yMin, obj.yMax);
|
||||
if (index > -1) {
|
||||
node.remove(obj);
|
||||
node = this.nodes[index];
|
||||
node.insert(obj);
|
||||
}
|
||||
} else {
|
||||
node.remove(obj);
|
||||
node.insert(obj);
|
||||
}
|
||||
return;
|
||||
}
|
||||
node.remove(obj);
|
||||
}
|
||||
this.root.insert(obj);
|
||||
};
|
||||
QuadTree.prototype.remove = function (obj) {
|
||||
var prev = obj.prev;
|
||||
var next = obj.next;
|
||||
if (prev) {
|
||||
prev.next = next;
|
||||
obj.prev = null;
|
||||
} else {
|
||||
var node = obj.parent;
|
||||
if (node.objects === obj) {
|
||||
node.objects = next;
|
||||
} else if (node.stuckObjects === obj) {
|
||||
node.stuckObjects = next;
|
||||
}
|
||||
}
|
||||
if (next) {
|
||||
next.prev = prev;
|
||||
obj.next = null;
|
||||
}
|
||||
obj.parent = null;
|
||||
};
|
||||
QuadTree.prototype.retrieve = function (xMin, xMax, yMin, yMax) {
|
||||
var stack = [];
|
||||
var out = [];
|
||||
var node = this;
|
||||
do {
|
||||
if (node.nodes.length) {
|
||||
var index = node._findIndex(xMin, yMin, xMax, yMax);
|
||||
var index = node._findIndex(xMin, xMax, yMin, yMax);
|
||||
if (index > -1) {
|
||||
stack.push(node.nodes[index]);
|
||||
} else {
|
||||
stack.push.apply(stack, node.nodes);
|
||||
}
|
||||
}
|
||||
out.push.apply(out, node.stuckObjects);
|
||||
out.push.apply(out, node.objects);
|
||||
var item = node.objects;
|
||||
for (var i = 0; i < 2; i++) {
|
||||
while (item) {
|
||||
if (!(item.xMin > xMax || item.xMax < xMin || item.yMin > yMax || item.yMax < yMin)) {
|
||||
out.push(item);
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
item = node.stuckObjects;
|
||||
}
|
||||
node = stack.pop();
|
||||
} while (node);
|
||||
return out;
|
||||
@ -530,11 +745,91 @@ QuadTree.prototype._subdivide = function () {
|
||||
var halfHeight = this.height / 2 | 0;
|
||||
var midX = this.x + halfWidth;
|
||||
var midY = this.y + halfHeight;
|
||||
var level = this.level + 1;
|
||||
this.nodes[0] = new QuadTree(midX, this.y, halfWidth, halfHeight, level);
|
||||
this.nodes[1] = new QuadTree(this.x, this.y, halfWidth, halfHeight, level);
|
||||
this.nodes[2] = new QuadTree(this.x, midY, halfWidth, halfHeight, level);
|
||||
this.nodes[3] = new QuadTree(midX, midY, halfWidth, halfHeight, level);
|
||||
this.nodes[0] = new QuadTree(midX, this.y, halfWidth, halfHeight, this);
|
||||
this.nodes[1] = new QuadTree(this.x, this.y, halfWidth, halfHeight, this);
|
||||
this.nodes[2] = new QuadTree(this.x, midY, halfWidth, halfHeight, this);
|
||||
this.nodes[3] = new QuadTree(midX, midY, halfWidth, halfHeight, this);
|
||||
};
|
||||
var RegionCluster = function () {
|
||||
this.regions = [];
|
||||
};
|
||||
RegionCluster.prototype.reset = function () {
|
||||
this.regions.length = 0;
|
||||
};
|
||||
RegionCluster.prototype.insert = function (region) {
|
||||
var regions = this.regions;
|
||||
if (regions.length < 3) {
|
||||
regions.push({
|
||||
xMin: region.xMin,
|
||||
xMax: region.xMax,
|
||||
yMin: region.yMin,
|
||||
yMax: region.yMax
|
||||
});
|
||||
return;
|
||||
}
|
||||
var a = region;
|
||||
var b = regions[0];
|
||||
var c = regions[1];
|
||||
var d = regions[2];
|
||||
var ab = (max(a.xMax, b.xMax) - min(a.xMin, b.xMin)) * (max(a.yMax, b.yMax) - min(a.yMin, b.yMin));
|
||||
var rb = regions[0];
|
||||
var ac = (max(a.xMax, c.xMax) - min(a.xMin, c.xMin)) * (max(a.yMax, c.yMax) - min(a.yMin, c.yMin));
|
||||
var ad = (max(a.xMax, d.xMax) - min(a.xMin, d.xMin)) * (max(a.yMax, d.yMax) - min(a.yMin, d.yMin));
|
||||
if (ac < ab) {
|
||||
ab = ac;
|
||||
rb = c;
|
||||
}
|
||||
if (ad < ab) {
|
||||
ab = ad;
|
||||
rb = d;
|
||||
}
|
||||
var bc = (max(b.xMax, c.xMax) - min(b.xMin, c.xMin)) * (max(b.yMax, c.yMax) - min(b.yMin, c.yMin));
|
||||
var bd = (max(b.xMax, d.xMax) - min(b.xMin, d.xMin)) * (max(b.yMax, d.yMax) - min(b.yMin, d.yMin));
|
||||
var cd = (max(c.xMax, d.xMax) - min(c.xMin, d.xMin)) * (max(c.yMax, d.yMax) - min(c.yMin, d.yMin));
|
||||
if (ab < bc && ab < bd && ab < cd) {
|
||||
if (a.xMin < rb.xMin) {
|
||||
rb.xMin = a.xMin;
|
||||
}
|
||||
if (a.xMax > rb.xMax) {
|
||||
rb.xMax = a.xMax;
|
||||
}
|
||||
if (a.yMin < rb.yMin) {
|
||||
rb.yMin = a.yMin;
|
||||
}
|
||||
if (a.yMax > rb.yMax) {
|
||||
rb.yMax = a.yMax;
|
||||
}
|
||||
return;
|
||||
}
|
||||
rb = regions[0];
|
||||
var rc = regions[1];
|
||||
if (bd < bc) {
|
||||
bc = bd;
|
||||
rc = regions[2];
|
||||
}
|
||||
if (cd < bc) {
|
||||
rb = regions[1];
|
||||
rc = regions[2];
|
||||
}
|
||||
if (rc.xMin < rb.xMin) {
|
||||
rb.xMin = rc.xMin;
|
||||
}
|
||||
if (rc.xMax > rb.xMax) {
|
||||
rb.xMax = rc.xMax;
|
||||
}
|
||||
if (rc.yMin < rb.yMin) {
|
||||
rb.yMin = rc.yMin;
|
||||
}
|
||||
if (rc.yMax > rb.yMax) {
|
||||
rb.yMax = rc.yMax;
|
||||
}
|
||||
rc.xMin = a.xMin;
|
||||
rc.xMax = a.xMax;
|
||||
rc.yMin = a.yMin;
|
||||
rc.yMax = a.yMax;
|
||||
};
|
||||
RegionCluster.prototype.retrieve = function () {
|
||||
return this.regions;
|
||||
};
|
||||
var EXTERNAL_INTERFACE_FEATURE = 1;
|
||||
var CLIPBOARD_FEATURE = 2;
|
||||
@ -1852,6 +2147,9 @@ function ShapePath(fillStyle, lineStyle, commandsCount, dataLength, isMorph, tra
|
||||
}
|
||||
}
|
||||
ShapePath.prototype = {
|
||||
get isEmpty() {
|
||||
return this.commands.length === 0;
|
||||
},
|
||||
moveTo: function (x, y) {
|
||||
if (this.commands[this.commands.length - 1] === SHAPE_MOVE_TO) {
|
||||
this.data[this.data.length - 2] = x;
|
||||
@ -2013,6 +2311,8 @@ ShapePath.prototype = {
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
} else {
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.closePath();
|
||||
},
|
||||
@ -2694,7 +2994,7 @@ function extendBoundsByY(bounds, y) {
|
||||
function morph(start, end, ratio) {
|
||||
return start + (end - start) * ratio;
|
||||
}
|
||||
function finishShapePath(path, dictionary) {
|
||||
function finishShapePath(path, dictionaryResolved) {
|
||||
if (path.fullyInitialized) {
|
||||
return path;
|
||||
}
|
||||
@ -2708,8 +3008,8 @@ function finishShapePath(path, dictionary) {
|
||||
}
|
||||
path.buffers = null;
|
||||
}
|
||||
path.fillStyle && initStyle(path.fillStyle, dictionary);
|
||||
path.lineStyle && initStyle(path.lineStyle, dictionary);
|
||||
path.fillStyle && initStyle(path.fillStyle, dictionaryResolved);
|
||||
path.lineStyle && initStyle(path.lineStyle, dictionaryResolved);
|
||||
path.fullyInitialized = true;
|
||||
return path;
|
||||
}
|
||||
@ -2769,7 +3069,7 @@ function buildBitmapPatternFactory(img, repeat) {
|
||||
fn.defaultFillStyle = defaultPattern;
|
||||
return fn;
|
||||
}
|
||||
function initStyle(style, dictionary) {
|
||||
function initStyle(style, dictionaryResolved) {
|
||||
if (style.type === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -2801,9 +3101,9 @@ function initStyle(style, dictionary) {
|
||||
case GRAPHICS_FILL_CLIPPED_BITMAP:
|
||||
case GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP:
|
||||
case GRAPHICS_FILL_NONSMOOTHED_CLIPPED_BITMAP:
|
||||
var bitmap = dictionary[style.bitmapId];
|
||||
var bitmap = dictionaryResolved[style.bitmapId];
|
||||
var repeat = style.type === GRAPHICS_FILL_REPEATING_BITMAP || style.type === GRAPHICS_FILL_NONSMOOTHED_REPEATING_BITMAP;
|
||||
style.style = buildBitmapPatternFactory(bitmap.value.props.img, repeat ? 'repeat' : 'no-repeat');
|
||||
style.style = buildBitmapPatternFactory(bitmap.props.img, repeat ? 'repeat' : 'no-repeat');
|
||||
break;
|
||||
default:
|
||||
fail('invalid fill style', 'shape');
|
||||
@ -5746,7 +6046,7 @@ BodyParser.prototype = {
|
||||
var read = stream.pos;
|
||||
buffer.removeHead(read);
|
||||
this.totalRead += read;
|
||||
if (this.totalRead >= this.length && options.oncomplete) {
|
||||
if (options.oncomplete && swf.tags[swf.tags.length - 1].finalTag) {
|
||||
options.oncomplete(swf);
|
||||
}
|
||||
}
|
||||
@ -5853,6 +6153,7 @@ SWF.parse = function (buffer, options) {
|
||||
bytesTotal: bytes.length
|
||||
};
|
||||
pipe.push(bytes, progressInfo);
|
||||
pipe.close();
|
||||
};
|
||||
(function (global) {
|
||||
global['SWF']['parse'] = SWF.parse;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
0.7.806
|
||||
0.7.933
|
||||
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<base href=""/>
|
||||
<title>Shumway viewer</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
@ -36,6 +35,7 @@ limitations under the License.
|
||||
position:fixed !important;
|
||||
left:0;top:0;bottom:0;right:0;
|
||||
overflow: hidden;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
#fallback {
|
||||
|
@ -54,3 +54,7 @@ scratchpad.keycode=VK_F4
|
||||
# Used for toggling the browser console from the detached toolbox window
|
||||
# Needs to match browserConsoleCmd.commandkey from browser.dtd
|
||||
browserConsoleCmd.commandkey=j
|
||||
|
||||
# LOCALIZATION NOTE (pickButton.tooltip)
|
||||
# This is the tooltip of the pick button in the toolbox toolbar
|
||||
pickButton.tooltip=Pick an element from the page
|
||||
|
@ -1289,6 +1289,17 @@
|
||||
]]></setter>
|
||||
</property>
|
||||
|
||||
<field name="_remoteFinder">null</field>
|
||||
<property name="finder" readonly="true">
|
||||
<getter><![CDATA[
|
||||
if (!this._remoteFinder) {
|
||||
let jsm = "resource://gre/modules/RemoteFinder.jsm";
|
||||
let RemoteFinder = Cu.import(jsm, {}).RemoteFinder;
|
||||
this._remoteFinder = new RemoteFinder(this);
|
||||
}
|
||||
return this._remoteFinder;
|
||||
]]></getter>
|
||||
</property>
|
||||
</implementation>
|
||||
|
||||
</binding>
|
||||
|
@ -63,7 +63,6 @@ var Browser = {
|
||||
messageManager.loadFrameScript("chrome://browser/content/library/SelectionPrototype.js", true);
|
||||
messageManager.loadFrameScript("chrome://browser/content/contenthandlers/SelectionHandler.js", true);
|
||||
messageManager.loadFrameScript("chrome://browser/content/contenthandlers/ContextMenuHandler.js", true);
|
||||
messageManager.loadFrameScript("chrome://browser/content/contenthandlers/FindHandler.js", true);
|
||||
messageManager.loadFrameScript("chrome://browser/content/contenthandlers/ConsoleAPIObserver.js", true);
|
||||
} catch (e) {
|
||||
// XXX whatever is calling startup needs to dump errors!
|
||||
@ -1017,7 +1016,7 @@ nsBrowserAccess.prototype = {
|
||||
_getOpenAction: function _getOpenAction(aURI, aOpener, aWhere, aContext) {
|
||||
let where = aWhere;
|
||||
/*
|
||||
* aWhere:
|
||||
* aWhere:
|
||||
* OPEN_DEFAULTWINDOW: default action
|
||||
* OPEN_CURRENTWINDOW: current window/tab
|
||||
* OPEN_NEWWINDOW: not allowed, converted to newtab below
|
||||
|
@ -1,91 +0,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/. */
|
||||
|
||||
let Ci = Components.interfaces;
|
||||
let Cc = Components.classes;
|
||||
|
||||
dump("### FindHandler.js loaded\n");
|
||||
|
||||
var FindHandler = {
|
||||
get _fastFind() {
|
||||
delete this._fastFind;
|
||||
this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
|
||||
this._fastFind.init(docShell);
|
||||
return this._fastFind;
|
||||
},
|
||||
|
||||
init: function findHandlerInit() {
|
||||
addMessageListener("FindAssist:Find", this);
|
||||
addMessageListener("FindAssist:Next", this);
|
||||
addMessageListener("FindAssist:Previous", this);
|
||||
},
|
||||
|
||||
receiveMessage: function findHandlerReceiveMessage(aMessage) {
|
||||
let findResult = Ci.nsITypeAheadFind.FIND_NOTFOUND;
|
||||
let json = aMessage.json;
|
||||
switch (aMessage.name) {
|
||||
case "FindAssist:Find":
|
||||
findResult = this._fastFind.find(json.searchString, false);
|
||||
break;
|
||||
|
||||
case "FindAssist:Previous":
|
||||
findResult = this._fastFind.findAgain(true, false);
|
||||
break;
|
||||
|
||||
case "FindAssist:Next":
|
||||
findResult = this._fastFind.findAgain(false, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (findResult == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
|
||||
sendAsyncMessage("FindAssist:Show", { rect: null, result: findResult });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._fastFind.currentWindow)
|
||||
return;
|
||||
|
||||
let selection = this._fastFind.currentWindow.getSelection();
|
||||
if (!selection.rangeCount || selection.isCollapsed) {
|
||||
// The selection can be into an input or a textarea element
|
||||
let nodes = content.document.querySelectorAll("input[type='text'], textarea");
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let node = nodes[i];
|
||||
if (node instanceof Ci.nsIDOMNSEditableElement && node.editor) {
|
||||
selection = node.editor.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL);
|
||||
if (selection.rangeCount && !selection.isCollapsed)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the bounding selection rect in content coordinates
|
||||
// including the scroll offset.
|
||||
|
||||
let offset = ContentScroll.getScrollOffset(content);
|
||||
for (let frame = this._fastFind.currentWindow; frame != content; frame = frame.parent) {
|
||||
let rect = frame.frameElement.getBoundingClientRect();
|
||||
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
|
||||
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
|
||||
offset.add(rect.left + parseInt(left), rect.top + parseInt(top));
|
||||
}
|
||||
|
||||
let rangeRect = selection.getRangeAt(0).getBoundingClientRect();
|
||||
let rect = new Rect(offset.x + rangeRect.left, offset.y + rangeRect.top,
|
||||
rangeRect.width, rangeRect.height);
|
||||
|
||||
// Ensure the potential "scroll" event fired during a search as already fired
|
||||
let timer = new Util.Timeout(function() {
|
||||
sendAsyncMessage("FindAssist:Show", {
|
||||
rect: rect.isEmpty() ? null: rect,
|
||||
contentHeight: content.document.documentElement.scrollHeight,
|
||||
result: findResult
|
||||
});
|
||||
});
|
||||
timer.once(0);
|
||||
}
|
||||
};
|
||||
this.FindHandler = FindHandler;
|
||||
|
||||
FindHandler.init();
|
@ -15,8 +15,10 @@ var FindHelperUI = {
|
||||
close: "cmd_findClose"
|
||||
},
|
||||
|
||||
_finder: null,
|
||||
_open: false,
|
||||
_status: null,
|
||||
_searchString: "",
|
||||
|
||||
/*
|
||||
* Properties
|
||||
@ -50,35 +52,12 @@ var FindHelperUI = {
|
||||
|
||||
this._textbox.addEventListener("keydown", this);
|
||||
|
||||
// Listen for find assistant messages from content
|
||||
messageManager.addMessageListener("FindAssist:Show", this);
|
||||
messageManager.addMessageListener("FindAssist:Hide", this);
|
||||
|
||||
// Listen for events where form assistant should be closed
|
||||
Elements.tabList.addEventListener("TabSelect", this, true);
|
||||
Elements.browsers.addEventListener("URLChanged", this, true);
|
||||
window.addEventListener("MozAppbarShowing", this);
|
||||
},
|
||||
|
||||
receiveMessage: function findHelperReceiveMessage(aMessage) {
|
||||
let json = aMessage.json;
|
||||
switch(aMessage.name) {
|
||||
case "FindAssist:Show":
|
||||
ContextUI.dismiss();
|
||||
this.status = json.result;
|
||||
// null rect implies nothing found
|
||||
if (json.rect) {
|
||||
this._zoom(Rect.fromRect(json.rect), json.contentHeight);
|
||||
}
|
||||
break;
|
||||
|
||||
case "FindAssist:Hide":
|
||||
if (this._container.getAttribute("type") == this.type)
|
||||
this.hide();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function findHelperHandleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "TabSelect":
|
||||
@ -92,11 +71,8 @@ var FindHelperUI = {
|
||||
|
||||
case "keydown":
|
||||
if (aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
|
||||
if (aEvent.shiftKey) {
|
||||
this.goToPrevious();
|
||||
} else {
|
||||
this.goToNext();
|
||||
}
|
||||
let backwardsSearch = aEvent.shiftKey;
|
||||
this.searchAgain(this._searchString, backwardsSearch);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -151,7 +127,10 @@ var FindHelperUI = {
|
||||
this._textbox.value = "";
|
||||
this.status = null;
|
||||
this._open = false;
|
||||
|
||||
if (this._finder) {
|
||||
this._finder.removeResultListener(this);
|
||||
this._finder = null
|
||||
}
|
||||
// Restore the scroll synchronisation
|
||||
Browser.selectedBrowser.scrollSync = true;
|
||||
};
|
||||
@ -161,22 +140,47 @@ var FindHelperUI = {
|
||||
this._container.dismiss();
|
||||
},
|
||||
|
||||
search: function findHelperSearch(aValue) {
|
||||
if (!this._finder) {
|
||||
this._finder = Browser.selectedBrowser.finder;
|
||||
this._finder.addResultListener(this);
|
||||
}
|
||||
this._searchString = aValue;
|
||||
if (aValue != "") {
|
||||
this._finder.fastFind(aValue, false, false);
|
||||
} else {
|
||||
this.updateCommands();
|
||||
}
|
||||
},
|
||||
|
||||
searchAgain: function findHelperSearchAgain(aValue, aFindBackwards) {
|
||||
// This can happen if the user taps next/previous after re-opening the search bar
|
||||
if (!this._finder) {
|
||||
this.search(aValue);
|
||||
return;
|
||||
}
|
||||
|
||||
this._finder.findAgain(aFindBackwards, false, false);
|
||||
},
|
||||
|
||||
goToPrevious: function findHelperGoToPrevious() {
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Previous", { });
|
||||
this.searchAgain(this._searchString, true);
|
||||
},
|
||||
|
||||
goToNext: function findHelperGoToNext() {
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Next", { });
|
||||
this.searchAgain(this._searchString, false);
|
||||
},
|
||||
|
||||
search: function findHelperSearch(aValue) {
|
||||
this.updateCommands(aValue);
|
||||
|
||||
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Find", { searchString: aValue });
|
||||
onFindResult: function(aResult, aFindBackwards, aLinkURL, aRect) {
|
||||
this._status = aResult;
|
||||
if (aRect) {
|
||||
this._zoom(aRect, Browser.selectedBrowser.contentDocumentHeight);
|
||||
}
|
||||
this.updateCommands();
|
||||
},
|
||||
|
||||
updateCommands: function findHelperUpdateCommands(aValue) {
|
||||
let disabled = (this._status == Ci.nsITypeAheadFind.FIND_NOTFOUND) || (aValue == "");
|
||||
updateCommands: function findHelperUpdateCommands() {
|
||||
let disabled = (this._status == Ci.nsITypeAheadFind.FIND_NOTFOUND) || (this._searchString == "");
|
||||
this._cmdPrevious.setAttribute("disabled", disabled);
|
||||
this._cmdNext.setAttribute("disabled", disabled);
|
||||
},
|
||||
|
@ -103,11 +103,9 @@
|
||||
<label id="crashpromptDetailedMessage">
|
||||
&crashprompt.detailedMessage.firstParagraph;<br />
|
||||
&crashprompt.detailedMessage.secondParagraph;<br />
|
||||
&crashprompt.detailedMessage.thirdParagraph;
|
||||
<a id="privacyPolicyLink"
|
||||
href="javascript:togglePrivacyDetails();">
|
||||
&crashprompt.privacyPolicyLink;
|
||||
</a>
|
||||
&crashprompt.detailedMessage.thirdParagraphBeforeLink;<!--
|
||||
--><a id="privacyPolicyLink" href="javascript:togglePrivacyDetails();">&crashprompt.privacyPolicyLink;</a><!--
|
||||
-->&crashprompt.detailedMessage.thirdParagraphAfterLink;
|
||||
</label>
|
||||
</div>
|
||||
</p>
|
||||
|
@ -56,7 +56,6 @@ chrome.jar:
|
||||
content/contenthandlers/ContextMenuHandler.js (content/contenthandlers/ContextMenuHandler.js)
|
||||
content/contenthandlers/SelectionHandler.js (content/contenthandlers/SelectionHandler.js)
|
||||
content/contenthandlers/FormHelper.js (content/contenthandlers/FormHelper.js)
|
||||
content/contenthandlers/FindHandler.js (content/contenthandlers/FindHandler.js)
|
||||
content/contenthandlers/ConsoleAPIObserver.js (content/contenthandlers/ConsoleAPIObserver.js)
|
||||
content/contenthandlers/Content.js (content/contenthandlers/Content.js)
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
<title>Find bar tests</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Find bar tests</title>
|
||||
<p>Find bar tests</p>
|
||||
<p style="position: absolute; bottom: 0;">bottom</p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -88,3 +88,45 @@ gTests.push({
|
||||
Browser.closeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "Text at bottom of screen is not obscured by findbar",
|
||||
run: function() {
|
||||
let textbox = document.getElementById("findbar-textbox");
|
||||
|
||||
let tab = yield addTab(chromeRoot + "browser_findbar.html");
|
||||
yield waitForCondition(() => BrowserUI.ready);
|
||||
is(Elements.findbar.isShowing, false, "Find bar is hidden by default");
|
||||
|
||||
FindHelperUI.show();
|
||||
yield waitForCondition(() => FindHelperUI.isActive);
|
||||
|
||||
EventUtils.sendString("bottom");
|
||||
let event = yield waitForEvent(window, "MozDeckOffsetChanged");
|
||||
ok(!(event instanceof Error), "MozDeckOffsetChanged received (1)");
|
||||
ok(event.detail > 0, "Browser deck shifted upward");
|
||||
|
||||
textbox.select();
|
||||
EventUtils.sendString("bar");
|
||||
event = yield waitForEvent(window, "MozDeckOffsetChanged");
|
||||
ok(!(event instanceof Error), "MozDeckOffsetChanged received (2)");
|
||||
is(event.detail, 0, "Browser deck shifted back to normal");
|
||||
|
||||
textbox.select();
|
||||
EventUtils.sendString("bottom");
|
||||
event = yield waitForEvent(window, "MozDeckOffsetChanged");
|
||||
ok(!(event instanceof Error), "MozDeckOffsetChanged received (3)");
|
||||
ok(event.detail > 0, "Browser deck shifted upward again");
|
||||
|
||||
let waitForDeckOffset = waitForEvent(window, "MozDeckOffsetChanged");
|
||||
let waitForTransitionEnd = waitForEvent(Elements.findbar, "transitionend");
|
||||
FindHelperUI.hide();
|
||||
event = yield waitForDeckOffset;
|
||||
ok(!(event instanceof Error), "MozDeckOffsetChanged received (4)");
|
||||
is(event.detail, 0, "Browser deck shifted back to normal when findbar hides");
|
||||
|
||||
// Cleanup.
|
||||
yield waitForTransitionEnd;
|
||||
Browser.closeTab(tab);
|
||||
}
|
||||
});
|
||||
|
@ -8,8 +8,13 @@
|
||||
<!ENTITY crashprompt.detailsLink "What's in a crash report?">
|
||||
<!ENTITY crashprompt.detailedMessage.firstParagraph "A crash report contains some details about the crash, your machine, as well as a snapshot of the state of your browser when it crashed.">
|
||||
<!ENTITY crashprompt.detailedMessage.secondParagraph "This may include things like open pages, text typed into forms and the content of open messages, recent browsing history, or geolocation shared with a site.">
|
||||
<!ENTITY crashprompt.detailedMessage.thirdParagraph "We use crash reports to try to fix problems and improve our products. We handle your information as we describe in our ">
|
||||
<!-- LOCALIZATION NOTE (crashprompt.detailedMessage.thirdParagraphBeforeLink, crashprompt.privacyPolicyLink, crashprompt.detailedMessage.thirdParagraphAfterLink):
|
||||
These three entities form a single sentence. crashprompt.detailedMessage.thirdParagraphBeforeLink is the start of the sentence,
|
||||
crashprompt.privacyPolicyLink is a clickable link the middle and crashprompt.detailedMessage.thirdParagraphAfterLink is the end of the sentence.
|
||||
-->
|
||||
<!ENTITY crashprompt.detailedMessage.thirdParagraphBeforeLink "We use crash reports to try to fix problems and improve our products. We handle your information as we describe in our ">
|
||||
<!ENTITY crashprompt.privacyPolicyLink "&brandShortName; privacy policy">
|
||||
<!ENTITY crashprompt.detailedMessage.thirdParagraphAfterLink ".">
|
||||
<!ENTITY crashprompt.includeURLLabel "Include the address of the page I was on">
|
||||
<!ENTITY crashprompt.acceptbutton "Send reports">
|
||||
<!ENTITY crashprompt.refusebutton "Don't send">
|
||||
|
@ -157,6 +157,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/command-responsivemode@2x.png (../shared/devtools/images/command-responsivemode@2x.png)
|
||||
skin/classic/browser/devtools/command-scratchpad@2x.png (../shared/devtools/images/command-scratchpad@2x.png)
|
||||
skin/classic/browser/devtools/command-tilt@2x.png (../shared/devtools/images/command-tilt@2x.png)
|
||||
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
|
||||
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
|
||||
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
|
||||
skin/classic/browser/devtools/ruleview.css (devtools/ruleview.css)
|
||||
@ -269,6 +270,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/app-manager/index-icons.svg (../shared/devtools/app-manager/images/index-icons.svg)
|
||||
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
|
||||
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
|
||||
skin/classic/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-16-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
|
@ -260,6 +260,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/command-responsivemode@2x.png (../shared/devtools/images/command-responsivemode@2x.png)
|
||||
skin/classic/browser/devtools/command-scratchpad@2x.png (../shared/devtools/images/command-scratchpad@2x.png)
|
||||
skin/classic/browser/devtools/command-tilt@2x.png (../shared/devtools/images/command-tilt@2x.png)
|
||||
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
|
||||
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
|
||||
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
|
||||
skin/classic/browser/devtools/ruleview.css (devtools/ruleview.css)
|
||||
@ -372,6 +373,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/app-manager/index-icons.svg (../shared/devtools/app-manager/images/index-icons.svg)
|
||||
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
|
||||
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
|
||||
skin/classic/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
@ -511,14 +511,6 @@
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#resumption-panel-desc {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#resumption-order-panel {
|
||||
-moz-margin-start: -8px;
|
||||
}
|
||||
|
||||
#resume {
|
||||
list-style-image: url(debugger-pause.png);
|
||||
-moz-image-region: rect(0px,16px,16px,0px);
|
||||
|
@ -12,22 +12,22 @@
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.highlighter-outline[locked] {
|
||||
box-shadow: 0 0 0 1px rgba(0,0,0,0.3);
|
||||
outline-color: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar */
|
||||
|
||||
.highlighter-nodeinfobar {
|
||||
color: hsl(216,33%,97%);
|
||||
border-radius: 3px;
|
||||
background: hsl(214,13%,24%) no-repeat padding-box;
|
||||
padding: 5px;
|
||||
/* Avoid cases where the infobar is smaller than the arrow, when the text is
|
||||
short */
|
||||
min-width: 75px;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - text */
|
||||
|
||||
.highlighter-nodeinfobar-text {
|
||||
text-align: center;
|
||||
/* 100% - size of the buttons and margins */
|
||||
max-width: calc(100% - 2 * (26px + 6px));
|
||||
padding-bottom: 1px;
|
||||
@ -46,43 +46,6 @@ html|*.highlighter-nodeinfobar-pseudo-classes {
|
||||
color: hsl(200,74%,57%);
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - buttons */
|
||||
|
||||
.highlighter-nodeinfobar-button {
|
||||
-moz-appearance: none;
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
width: 26px;
|
||||
min-height: 26px;
|
||||
%if !defined(MOZ_WIDGET_GTK) && !defined(MOZ_WIDGET_QT)
|
||||
background-color: transparent;
|
||||
%endif
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-inspectbutton {
|
||||
-moz-border-end: 1px solid #5a6169;
|
||||
-moz-margin-end: 12px;
|
||||
list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
|
||||
-moz-image-region: rect(0px 16px 16px 0px);
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-inspectbutton:active:hover,
|
||||
.highlighter-nodeinfobar-container:not([locked]) > .highlighter-nodeinfobar > .highlighter-nodeinfobar-inspectbutton {
|
||||
-moz-image-region: rect(0px 32px 16px 16px);
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-menu {
|
||||
-moz-border-start: 1px solid #5a6169;
|
||||
-moz-margin-start: 12px;
|
||||
}
|
||||
|
||||
.highlighter-nodeinfobar-menu > .toolbarbutton-menu-dropmarker {
|
||||
-moz-appearance: none !important;
|
||||
list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
|
||||
-moz-box-align: center;
|
||||
-moz-margin-start: -1px;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - box & arrow */
|
||||
|
||||
.highlighter-nodeinfobar-arrow {
|
||||
|
BIN
browser/themes/shared/devtools/images/command-pick@2x.png
Normal file
BIN
browser/themes/shared/devtools/images/command-pick@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
@ -437,6 +437,10 @@
|
||||
background-size: 48px 16px;
|
||||
}
|
||||
|
||||
#command-button-pick > image {
|
||||
background-image: url("chrome://browser/skin/devtools/command-pick@2x.png");
|
||||
}
|
||||
|
||||
#command-button-splitconsole > image {
|
||||
background-image: url("chrome://browser/skin/devtools/command-console@2x.png");
|
||||
}
|
||||
|
@ -262,7 +262,7 @@
|
||||
|
||||
/* Need to constrain the glass fog to avoid overlapping layers, see bug 886281. */
|
||||
#main-window:not([customizing]) #navigator-toolbox:not(:-moz-lwtheme) {
|
||||
overflow-y: hidden;
|
||||
overflow: -moz-hidden-unscrollable;
|
||||
}
|
||||
|
||||
#main-window[sizemode=normal] .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox > .scrollbox-innerbox:not(:-moz-lwtheme) {
|
||||
|
@ -188,14 +188,15 @@ browser.jar:
|
||||
skin/classic/browser/devtools/command-responsivemode@2x.png (../shared/devtools/images/command-responsivemode@2x.png)
|
||||
skin/classic/browser/devtools/command-scratchpad@2x.png (../shared/devtools/images/command-scratchpad@2x.png)
|
||||
skin/classic/browser/devtools/command-tilt@2x.png (../shared/devtools/images/command-tilt@2x.png)
|
||||
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
|
||||
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
|
||||
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
|
||||
skin/classic/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)
|
||||
skin/classic/browser/devtools/editor-error.png (devtools/editor-error.png)
|
||||
skin/classic/browser/devtools/editor-breakpoint.png (devtools/editor-breakpoint.png)
|
||||
skin/classic/browser/devtools/editor-debug-location.png (devtools/editor-debug-location.png)
|
||||
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css)
|
||||
skin/classic/browser/devtools/webconsole.png (devtools/webconsole.png)
|
||||
skin/classic/browser/devtools/editor-error.png (devtools/editor-error.png)
|
||||
skin/classic/browser/devtools/editor-breakpoint.png (devtools/editor-breakpoint.png)
|
||||
skin/classic/browser/devtools/editor-debug-location.png (devtools/editor-debug-location.png)
|
||||
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css)
|
||||
skin/classic/browser/devtools/webconsole.png (devtools/webconsole.png)
|
||||
skin/classic/browser/devtools/breadcrumbs-scrollbutton.png (devtools/breadcrumbs-scrollbutton.png)
|
||||
skin/classic/browser/devtools/breadcrumbs/ltr-end-pressed.png (devtools/breadcrumbs/ltr-end-pressed.png)
|
||||
skin/classic/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png (devtools/breadcrumbs/ltr-end-selected-pressed.png)
|
||||
@ -297,6 +298,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/app-manager/index-icons.svg (../shared/devtools/app-manager/images/index-icons.svg)
|
||||
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
|
||||
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
|
||||
skin/classic/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/browser/sync-throbber.png
|
||||
skin/classic/browser/sync-16.png
|
||||
@ -498,6 +500,7 @@ browser.jar:
|
||||
skin/classic/aero/browser/devtools/command-responsivemode@2x.png (../shared/devtools/images/command-responsivemode@2x.png)
|
||||
skin/classic/aero/browser/devtools/command-scratchpad@2x.png (../shared/devtools/images/command-scratchpad@2x.png)
|
||||
skin/classic/aero/browser/devtools/command-tilt@2x.png (../shared/devtools/images/command-tilt@2x.png)
|
||||
skin/classic/aero/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
|
||||
skin/classic/aero/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
|
||||
skin/classic/aero/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
|
||||
skin/classic/aero/browser/devtools/ruleview.css (devtools/ruleview.css)
|
||||
@ -610,6 +613,8 @@ browser.jar:
|
||||
skin/classic/aero/browser/devtools/app-manager/index-icons.svg (../shared/devtools/app-manager/images/index-icons.svg)
|
||||
skin/classic/aero/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
|
||||
skin/classic/aero/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
|
||||
skin/classic/aero/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
skin/classic/aero/browser/sync-throbber.png
|
||||
skin/classic/aero/browser/sync-16.png
|
||||
|
@ -786,11 +786,11 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
|
||||
final OnFaviconLoadedListener listener = new GeckoAppShell.CreateShortcutFaviconLoadedListener(url, title);
|
||||
Favicons.getFaviconForSize(url,
|
||||
tab.getFaviconURL(),
|
||||
Integer.MAX_VALUE,
|
||||
LoadFaviconTask.FLAG_PERSIST,
|
||||
listener);
|
||||
Favicons.getSizedFavicon(url,
|
||||
tab.getFaviconURL(),
|
||||
Integer.MAX_VALUE,
|
||||
LoadFaviconTask.FLAG_PERSIST,
|
||||
listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1382,8 +1382,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
|
||||
private void openUrlAndStopEditing(String url, String searchEngine, boolean newTab) {
|
||||
mBrowserToolbar.setProgressVisibility(true);
|
||||
|
||||
int flags = Tabs.LOADURL_NONE;
|
||||
if (newTab) {
|
||||
flags |= Tabs.LOADURL_NEW_TAB;
|
||||
@ -1415,15 +1413,16 @@ abstract public class BrowserApp extends GeckoApp
|
||||
final int tabFaviconSize = getResources().getDimensionPixelSize(R.dimen.browser_toolbar_favicon_size);
|
||||
|
||||
int flags = (tab.isPrivate() || tab.getErrorType() != Tab.ErrorType.NONE) ? 0 : LoadFaviconTask.FLAG_PERSIST;
|
||||
int id = Favicons.getFaviconForSize(tab.getURL(), tab.getFaviconURL(), tabFaviconSize, flags, sFaviconLoadedListener);
|
||||
int id = Favicons.getSizedFavicon(tab.getURL(), tab.getFaviconURL(), tabFaviconSize, flags, sFaviconLoadedListener);
|
||||
|
||||
tab.setFaviconLoadId(id);
|
||||
if (id != Favicons.LOADED &&
|
||||
Tabs.getInstance().isSelectedTab(tab)) {
|
||||
|
||||
final Tabs tabs = Tabs.getInstance();
|
||||
if (id != Favicons.LOADED && tabs.isSelectedTab(tab)) {
|
||||
// We're loading the current tab's favicon from somewhere
|
||||
// other than the cache.
|
||||
// Display the globe favicon until then.
|
||||
mBrowserToolbar.showDefaultFavicon();
|
||||
// other than the cache. Display the globe favicon until then.
|
||||
tab.updateFavicon(Favicons.sDefaultFavicon);
|
||||
tabs.notifyListeners(tab, Tabs.TabEvents.FAVICON);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,10 +235,6 @@ public class Tab {
|
||||
return mHasOpenSearch;
|
||||
}
|
||||
|
||||
public SecurityMode getSecurityMode() {
|
||||
return mSiteIdentity.getSecurityMode();
|
||||
}
|
||||
|
||||
public SiteIdentity getSiteIdentity() {
|
||||
return mSiteIdentity;
|
||||
}
|
||||
|
@ -766,7 +766,7 @@ public class Tabs implements GeckoEventListener {
|
||||
*/
|
||||
private Bitmap getAboutPageFavicon(final String url) {
|
||||
int faviconSize = Math.round(mAppContext.getResources().getDimension(R.dimen.browser_toolbar_favicon_size));
|
||||
return Favicons.getCachedFaviconForSize(url, faviconSize);
|
||||
return Favicons.getSizedFaviconForPageFromCache(url, faviconSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2974,7 +2974,7 @@ public class BrowserProvider extends ContentProvider {
|
||||
|
||||
// If no URL is provided, insert using the default one.
|
||||
if (TextUtils.isEmpty(faviconUrl) && !TextUtils.isEmpty(pageUrl)) {
|
||||
values.put(Favicons.URL, org.mozilla.gecko.favicons.Favicons.guessDefaultFaviconURL(pageUrl));
|
||||
values.put(Favicons.URL, org.mozilla.gecko.favicons.Favicons.guessDefaultFaviconUrl(pageUrl));
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
@ -65,7 +65,7 @@ public class Favicons {
|
||||
// doing so is not necessary.
|
||||
private static final NonEvictingLruCache<String, String> sPageURLMappings = new NonEvictingLruCache<String, String>(NUM_PAGE_URL_MAPPINGS_TO_STORE);
|
||||
|
||||
public static String getFaviconURLForPageURLFromCache(String pageURL) {
|
||||
public static String getFaviconUrlForPageUrlFromCache(String pageURL) {
|
||||
return sPageURLMappings.get(pageURL);
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ public class Favicons {
|
||||
* Insert the given pageUrl->faviconUrl mapping into the memory cache of such mappings.
|
||||
* Useful for short-circuiting local database access.
|
||||
*/
|
||||
public static void putFaviconURLForPageURLInCache(String pageURL, String faviconURL) {
|
||||
public static void putFaviconUrlForPageUrlInCache(String pageURL, String faviconURL) {
|
||||
sPageURLMappings.put(pageURL, faviconURL);
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ public class Favicons {
|
||||
*
|
||||
* Returns null otherwise.
|
||||
*/
|
||||
public static Bitmap getCachedFaviconForSize(final String pageURL, int targetSize) {
|
||||
public static Bitmap getSizedFaviconForPageFromCache(final String pageURL, int targetSize) {
|
||||
final String faviconURL = sPageURLMappings.get(pageURL);
|
||||
if (faviconURL == null) {
|
||||
return null;
|
||||
@ -134,7 +134,7 @@ public class Favicons {
|
||||
* @return The id of the asynchronous task created, NOT_LOADING if none is created, or
|
||||
* LOADED if the value could be dispatched on the current thread.
|
||||
*/
|
||||
public static int getFaviconForSize(String pageURL, String faviconURL, int targetSize, int flags, OnFaviconLoadedListener listener) {
|
||||
public static int getSizedFavicon(String pageURL, String faviconURL, int targetSize, int flags, OnFaviconLoadedListener listener) {
|
||||
// Do we know the favicon URL for this page already?
|
||||
String cacheURL = faviconURL;
|
||||
if (cacheURL == null) {
|
||||
@ -143,7 +143,7 @@ public class Favicons {
|
||||
|
||||
// If there's no favicon URL given, try and hit the cache with the default one.
|
||||
if (cacheURL == null) {
|
||||
cacheURL = guessDefaultFaviconURL(pageURL);
|
||||
cacheURL = guessDefaultFaviconUrl(pageURL);
|
||||
}
|
||||
|
||||
// If it's something we can't even figure out a default URL for, just give up.
|
||||
@ -222,6 +222,7 @@ public class Favicons {
|
||||
public static int getSizedFaviconForPageFromLocal(final String pageURL, final OnFaviconLoadedListener callback) {
|
||||
return getSizedFaviconForPageFromLocal(pageURL, sDefaultFaviconSize, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine the URL of the Favicon image for a given page URL by querying the
|
||||
* history database. Should only be called from the background thread - does database access.
|
||||
@ -245,7 +246,7 @@ public class Favicons {
|
||||
targetURL = BrowserDB.getFaviconUrlForHistoryUrl(sContext.getContentResolver(), pageURL);
|
||||
if (targetURL == null) {
|
||||
// Nothing in the history database. Fall back to the default URL and hope for the best.
|
||||
targetURL = guessDefaultFaviconURL(pageURL);
|
||||
targetURL = guessDefaultFaviconUrl(pageURL);
|
||||
}
|
||||
return targetURL;
|
||||
}
|
||||
@ -406,7 +407,7 @@ public class Favicons {
|
||||
* @param pageURL Page URL for which a default Favicon URL is requested
|
||||
* @return The default Favicon URL.
|
||||
*/
|
||||
public static String guessDefaultFaviconURL(String pageURL) {
|
||||
public static String guessDefaultFaviconUrl(String pageURL) {
|
||||
// Special-casing for about: pages. The favicon for about:pages which don't provide a link tag
|
||||
// is bundled in the database, keyed only by page URL, hence the need to return the page URL
|
||||
// here. If the database ever migrates to stop being silly in this way, this can plausibly
|
||||
|
@ -240,14 +240,14 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
|
||||
// If favicon is empty, fall back to the stored one.
|
||||
if (TextUtils.isEmpty(mFaviconUrl)) {
|
||||
// Try to get the favicon URL from the memory cache.
|
||||
storedFaviconUrl = Favicons.getFaviconURLForPageURLFromCache(mPageUrl);
|
||||
storedFaviconUrl = Favicons.getFaviconUrlForPageUrlFromCache(mPageUrl);
|
||||
|
||||
// If that failed, try to get the URL from the database.
|
||||
if (storedFaviconUrl == null) {
|
||||
storedFaviconUrl = Favicons.getFaviconUrlForPageUrl(mPageUrl);
|
||||
if (storedFaviconUrl != null) {
|
||||
// If that succeeded, cache the URL loaded from the database in memory.
|
||||
Favicons.putFaviconURLForPageURLInCache(mPageUrl, storedFaviconUrl);
|
||||
Favicons.putFaviconUrlForPageUrlInCache(mPageUrl, storedFaviconUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +256,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
|
||||
mFaviconUrl = storedFaviconUrl;
|
||||
} else {
|
||||
// If we don't have a stored one, fall back to the default.
|
||||
mFaviconUrl = Favicons.guessDefaultFaviconURL(mPageUrl);
|
||||
mFaviconUrl = Favicons.guessDefaultFaviconUrl(mPageUrl);
|
||||
|
||||
if (TextUtils.isEmpty(mFaviconUrl)) {
|
||||
return null;
|
||||
@ -335,7 +335,7 @@ public class LoadFaviconTask extends UiAsyncTask<Void, Void, Bitmap> {
|
||||
}
|
||||
|
||||
// If we're not already trying the default URL, try it now.
|
||||
final String guessed = Favicons.guessDefaultFaviconURL(mPageUrl);
|
||||
final String guessed = Favicons.guessDefaultFaviconUrl(mPageUrl);
|
||||
if (guessed == null) {
|
||||
Favicons.putFaviconInFailedCache(mFaviconUrl);
|
||||
return null;
|
||||
|
@ -42,7 +42,7 @@ abstract class HomeFragment extends Fragment {
|
||||
private static final String LOGTAG="GeckoHomeFragment";
|
||||
|
||||
// Share MIME type.
|
||||
private static final String SHARE_MIME_TYPE = "text/plain";
|
||||
protected static final String SHARE_MIME_TYPE = "text/plain";
|
||||
|
||||
// Default value for "can load" hint
|
||||
static final boolean DEFAULT_CAN_LOAD_HINT = false;
|
||||
@ -123,9 +123,11 @@ abstract class HomeFragment extends Fragment {
|
||||
if (itemId == R.id.home_share) {
|
||||
if (info.url == null) {
|
||||
Log.e(LOGTAG, "Can't share because URL is null");
|
||||
return false;
|
||||
} else {
|
||||
GeckoAppShell.openUriExternal(info.url, SHARE_MIME_TYPE, "", "",
|
||||
Intent.ACTION_SEND, info.getDisplayTitle());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
||||
import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper;
|
||||
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.home.HomeListView.HomeContextMenuInfo;
|
||||
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
|
||||
@ -27,6 +28,7 @@ import org.mozilla.gecko.util.ThreadUtils;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
@ -358,6 +360,28 @@ public class TopSitesPage extends HomeFragment {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.home_share) {
|
||||
if (info.url == null) {
|
||||
Log.w(LOGTAG, "Share not enabled for context menu because URL is null.");
|
||||
return false;
|
||||
} else {
|
||||
GeckoAppShell.openUriExternal(info.url, SHARE_MIME_TYPE, "", "",
|
||||
Intent.ACTION_SEND, info.getDisplayTitle());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemId == R.id.home_add_to_launcher) {
|
||||
if (info.url == null) {
|
||||
Log.w(LOGTAG, "Not enabling 'Add to home page' because URL is null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch the largest cacheable icon size.
|
||||
Favicons.getLargestFaviconForPage(info.url, new GeckoAppShell.CreateShortcutFaviconLoadedListener(info.url, info.getDisplayTitle()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -313,8 +313,10 @@ gbjar.sources += [
|
||||
'toolbar/ShapedButton.java',
|
||||
'toolbar/SiteIdentityPopup.java',
|
||||
'toolbar/TabCounter.java',
|
||||
'toolbar/ToolbarDisplayLayout.java',
|
||||
'toolbar/ToolbarEditLayout.java',
|
||||
'toolbar/ToolbarEditText.java',
|
||||
'toolbar/ToolbarTitlePrefs.java',
|
||||
'TouchEventInterceptor.java',
|
||||
'updater/UpdateService.java',
|
||||
'updater/UpdateServiceHelper.java',
|
||||
|
@ -69,58 +69,10 @@
|
||||
android:layout_toRightOf="@id/back"
|
||||
android:layout_toLeftOf="@id/menu_items"/>
|
||||
|
||||
<LinearLayout android:id="@+id/url_display_container"
|
||||
<org.mozilla.gecko.toolbar.ToolbarDisplayLayout android:id="@+id/display_layout"
|
||||
style="@style/UrlBar.Button.Container"
|
||||
android:layout_toRightOf="@id/back"
|
||||
android:layout_toLeftOf="@id/menu_items">
|
||||
|
||||
<ImageButton android:id="@+id/favicon"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/browser_toolbar_favicon_size"
|
||||
android:layout_height="fill_parent"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_marginLeft="4dip"
|
||||
android:paddingLeft="4dip"
|
||||
android:paddingRight="4dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/favicon"/>
|
||||
|
||||
<ImageButton android:id="@+id/site_security"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/browser_toolbar_lock_width"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_marginLeft="-4dip"
|
||||
android:src="@drawable/site_security_level"
|
||||
android:contentDescription="@string/site_security"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoTextView android:id="@+id/url_bar_title"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1.0"
|
||||
android:singleLine="true"
|
||||
android:paddingRight="8dp"
|
||||
android:textColor="@color/url_bar_title"
|
||||
android:textColorHint="@color/url_bar_title_hint"
|
||||
android:gravity="center_vertical|left"
|
||||
android:layout_gravity="center_vertical"
|
||||
gecko:autoUpdateTheme="false"/>
|
||||
|
||||
<org.mozilla.gecko.toolbar.PageActionLayout android:id="@+id/page_action_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="@dimen/browser_toolbar_button_padding"
|
||||
android:visibility="gone"
|
||||
android:orientation="horizontal"/>
|
||||
|
||||
<ImageButton android:id="@+id/stop"
|
||||
style="@style/UrlBar.ImageButton.Icon"
|
||||
android:src="@drawable/urlbar_stop"
|
||||
android:contentDescription="@string/stop"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_toLeftOf="@id/menu_items"/>
|
||||
|
||||
<LinearLayout android:id="@+id/menu_items"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -93,59 +93,10 @@
|
||||
android:paddingLeft="8dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<LinearLayout android:id="@+id/url_display_container"
|
||||
<org.mozilla.gecko.toolbar.ToolbarDisplayLayout android:id="@+id/display_layout"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_toLeftOf="@id/tabs"
|
||||
android:layout_marginRight="-24dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton android:id="@+id/favicon"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/browser_toolbar_favicon_size"
|
||||
android:layout_height="fill_parent"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_marginLeft="8dip"
|
||||
android:paddingLeft="4dip"
|
||||
android:paddingRight="4dip"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
<ImageButton android:id="@+id/site_security"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/browser_toolbar_lock_width"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_marginLeft="-4dip"
|
||||
android:src="@drawable/site_security_level"
|
||||
android:contentDescription="@string/site_security"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoTextView android:id="@+id/url_bar_title"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1.0"
|
||||
android:singleLine="true"
|
||||
android:paddingRight="8dp"
|
||||
android:textColor="@color/url_bar_title"
|
||||
android:textColorHint="@color/url_bar_title_hint"
|
||||
android:gravity="center_vertical|left"
|
||||
android:hint="@string/url_bar_default_text"
|
||||
android:layout_gravity="center_vertical"
|
||||
gecko:autoUpdateTheme="false"/>
|
||||
|
||||
<org.mozilla.gecko.toolbar.PageActionLayout android:id="@+id/page_action_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="12dp"
|
||||
android:visibility="gone"
|
||||
android:orientation="horizontal"/>
|
||||
|
||||
<ImageButton android:id="@+id/stop"
|
||||
style="@style/UrlBar.ImageButton.Icon"
|
||||
android:src="@drawable/urlbar_stop"
|
||||
android:contentDescription="@string/stop"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_marginRight="-24dp"/>
|
||||
|
||||
<ImageView android:id="@+id/shadow"
|
||||
android:layout_width="fill_parent"
|
||||
|
@ -0,0 +1,55 @@
|
||||
<?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/. -->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<ImageButton android:id="@+id/favicon"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/browser_toolbar_favicon_size"
|
||||
android:layout_height="fill_parent"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_marginLeft="8dip"
|
||||
android:paddingLeft="4dip"
|
||||
android:paddingRight="4dip"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
<ImageButton android:id="@+id/site_security"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/browser_toolbar_lock_width"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_marginLeft="-4dip"
|
||||
android:src="@drawable/site_security_level"
|
||||
android:contentDescription="@string/site_security"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoTextView android:id="@+id/url_bar_title"
|
||||
style="@style/UrlBar.Button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1.0"
|
||||
android:singleLine="true"
|
||||
android:paddingRight="8dp"
|
||||
android:textColor="@color/url_bar_title"
|
||||
android:textColorHint="@color/url_bar_title_hint"
|
||||
android:gravity="center_vertical|left"
|
||||
android:hint="@string/url_bar_default_text"
|
||||
android:layout_gravity="center_vertical"
|
||||
gecko:autoUpdateTheme="false"/>
|
||||
|
||||
<org.mozilla.gecko.toolbar.PageActionLayout android:id="@+id/page_action_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="@dimen/browser_toolbar_button_padding"
|
||||
android:visibility="gone"
|
||||
android:orientation="horizontal"/>
|
||||
|
||||
<ImageButton android:id="@+id/stop"
|
||||
style="@style/UrlBar.ImageButton.Icon"
|
||||
android:src="@drawable/urlbar_stop"
|
||||
android:contentDescription="@string/stop"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</merge>
|
@ -11,6 +11,9 @@
|
||||
<item android:id="@+id/top_sites_open_private_tab"
|
||||
android:title="@string/contextmenu_open_private_tab"/>
|
||||
|
||||
<item android:id="@+id/home_share"
|
||||
android:title="@string/contextmenu_share"/>
|
||||
|
||||
<item android:id="@+id/top_sites_edit"
|
||||
android:title="@string/contextmenu_top_sites_edit"/>
|
||||
|
||||
@ -20,4 +23,6 @@
|
||||
<item android:id="@+id/top_sites_unpin"
|
||||
android:title="@string/contextmenu_top_sites_unpin"/>
|
||||
|
||||
<item android:id="@+id/home_add_to_launcher"
|
||||
android:title="@string/contextmenu_add_to_launcher"/>
|
||||
</menu>
|
||||
|
@ -59,8 +59,8 @@ public class ToolbarComponent extends BaseComponent {
|
||||
return (EditText) getToolbarView().findViewById(R.id.url_edit_text);
|
||||
}
|
||||
|
||||
private View getUrlDisplayContainer() {
|
||||
return getToolbarView().findViewById(R.id.url_display_container);
|
||||
private View getUrlDisplayLayout() {
|
||||
return getToolbarView().findViewById(R.id.display_layout);
|
||||
}
|
||||
|
||||
private TextView getUrlTitleText() {
|
||||
@ -106,7 +106,7 @@ public class ToolbarComponent extends BaseComponent {
|
||||
}
|
||||
|
||||
private boolean isEditing() {
|
||||
return getUrlDisplayContainer().getVisibility() != View.VISIBLE &&
|
||||
return getUrlDisplayLayout().getVisibility() != View.VISIBLE &&
|
||||
getUrlEditText().getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,12 @@
|
||||
|
||||
package org.mozilla.gecko.toolbar;
|
||||
|
||||
import org.mozilla.gecko.AboutPages;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.GeckoApplication;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.LightweightTheme;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.SiteIdentity;
|
||||
import org.mozilla.gecko.SiteIdentity.SecurityMode;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
@ -21,16 +19,16 @@ import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.menu.GeckoMenu;
|
||||
import org.mozilla.gecko.menu.MenuPopup;
|
||||
import org.mozilla.gecko.PrefsHelper;
|
||||
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener;
|
||||
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener;
|
||||
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags;
|
||||
import org.mozilla.gecko.util.Clipboard;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.StringUtils;
|
||||
import org.mozilla.gecko.widget.GeckoImageButton;
|
||||
import org.mozilla.gecko.widget.GeckoImageView;
|
||||
import org.mozilla.gecko.widget.GeckoRelativeLayout;
|
||||
import org.mozilla.gecko.widget.GeckoTextView;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
@ -40,11 +38,6 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
@ -56,11 +49,7 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.MarginLayoutParams;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
@ -69,16 +58,15 @@ import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class BrowserToolbar extends GeckoRelativeLayout
|
||||
implements Tabs.OnTabsChangedListener,
|
||||
GeckoMenu.ActionItemBarPresenter,
|
||||
Animation.AnimationListener,
|
||||
GeckoEventListener {
|
||||
private static final String LOGTAG = "GeckoToolbar";
|
||||
public static final String PREF_TITLEBAR_MODE = "browser.chrome.titlebarMode";
|
||||
public static final String PREF_TRIM_URLS = "browser.urlbar.trimURLs";
|
||||
|
||||
public interface OnActivateListener {
|
||||
public void onActivate();
|
||||
@ -104,37 +92,26 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
public void onStopEditing();
|
||||
}
|
||||
|
||||
private enum ForwardButtonAnimation {
|
||||
enum ForwardButtonAnimation {
|
||||
SHOW,
|
||||
HIDE
|
||||
}
|
||||
|
||||
private View mUrlDisplayContainer;
|
||||
private ToolbarDisplayLayout mUrlDisplayLayout;
|
||||
private ToolbarEditLayout mUrlEditLayout;
|
||||
private View mUrlBarEntry;
|
||||
private ImageView mUrlBarRightEdge;
|
||||
private GeckoTextView mTitle;
|
||||
private int mTitlePadding;
|
||||
private boolean mSiteSecurityVisible;
|
||||
private boolean mSwitchingTabs;
|
||||
private ShapedButton mTabs;
|
||||
private ImageButton mBack;
|
||||
private ImageButton mForward;
|
||||
private ImageButton mStop;
|
||||
|
||||
// To de-bounce sets.
|
||||
private Bitmap mLastFavicon;
|
||||
private ImageButton mFavicon;
|
||||
|
||||
private ImageButton mSiteSecurity;
|
||||
private PageActionLayout mPageActionLayout;
|
||||
private Animation mProgressSpinner;
|
||||
private TabCounter mTabsCounter;
|
||||
private GeckoImageButton mMenu;
|
||||
private GeckoImageView mMenuIcon;
|
||||
private LinearLayout mActionItemBar;
|
||||
private MenuPopup mMenuPopup;
|
||||
private List<? extends View> mFocusOrder;
|
||||
private List<View> mFocusOrder;
|
||||
|
||||
private OnActivateListener mActivateListener;
|
||||
private OnCommitListener mCommitListener;
|
||||
@ -143,46 +120,21 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
private OnStartEditingListener mStartEditingListener;
|
||||
private OnStopEditingListener mStopEditingListener;
|
||||
|
||||
private SiteIdentityPopup mSiteIdentityPopup;
|
||||
|
||||
final private BrowserApp mActivity;
|
||||
private boolean mHasSoftMenuButton;
|
||||
|
||||
private boolean mShowSiteSecurity;
|
||||
private boolean mSpinnerVisible;
|
||||
|
||||
private boolean mIsEditing;
|
||||
private boolean mAnimatingEntry;
|
||||
|
||||
private AlphaAnimation mLockFadeIn;
|
||||
private TranslateAnimation mTitleSlideLeft;
|
||||
private TranslateAnimation mTitleSlideRight;
|
||||
|
||||
private int mUrlBarViewOffset;
|
||||
private int mDefaultForwardMargin;
|
||||
private PropertyAnimator mForwardAnim = null;
|
||||
|
||||
private int mFaviconSize;
|
||||
|
||||
private PropertyAnimator mVisibilityAnimator;
|
||||
private static final Interpolator sButtonsInterpolator = new AccelerateInterpolator();
|
||||
|
||||
private static final int TABS_CONTRACTED = 1;
|
||||
private static final int TABS_EXPANDED = 2;
|
||||
|
||||
private static final int FORWARD_ANIMATION_DURATION = 450;
|
||||
private final ForegroundColorSpan mUrlColor;
|
||||
private final ForegroundColorSpan mBlockedColor;
|
||||
private final ForegroundColorSpan mDomainColor;
|
||||
private final ForegroundColorSpan mPrivateDomainColor;
|
||||
|
||||
private final LightweightTheme mTheme;
|
||||
|
||||
private boolean mShowUrl;
|
||||
private boolean mTrimURLs;
|
||||
|
||||
private Integer mPrefObserverId;
|
||||
|
||||
public BrowserToolbar(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@ -199,75 +151,17 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
|
||||
Tabs.registerOnTabsChangedListener(this);
|
||||
mSwitchingTabs = true;
|
||||
|
||||
mAnimatingEntry = false;
|
||||
mShowUrl = false;
|
||||
mTrimURLs = true;
|
||||
|
||||
final String[] prefs = {
|
||||
PREF_TITLEBAR_MODE,
|
||||
PREF_TRIM_URLS
|
||||
};
|
||||
// listen to the title bar pref.
|
||||
mPrefObserverId = PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
|
||||
@Override
|
||||
public void prefValue(String pref, String str) {
|
||||
// Handles PREF_TITLEBAR_MODE, which is always a string.
|
||||
int value = Integer.parseInt(str);
|
||||
boolean shouldShowUrl = (value == 1);
|
||||
|
||||
if (shouldShowUrl == mShowUrl) {
|
||||
return;
|
||||
}
|
||||
mShowUrl = shouldShowUrl;
|
||||
|
||||
triggerTitleUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prefValue(String pref, boolean value) {
|
||||
// Handles PREF_TRIM_URLS, which should usually be a boolean.
|
||||
if (value == mTrimURLs) {
|
||||
return;
|
||||
}
|
||||
mTrimURLs = value;
|
||||
|
||||
triggerTitleUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isObserver() {
|
||||
// We want to be notified of changes to be able to switch mode
|
||||
// without restarting.
|
||||
return true;
|
||||
}
|
||||
|
||||
private void triggerTitleUpdate() {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateTitle();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Resources res = getResources();
|
||||
mUrlColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_urltext));
|
||||
mBlockedColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_blockedtext));
|
||||
mDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext));
|
||||
mPrivateDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext_private));
|
||||
|
||||
registerEventListener("Reader:Click");
|
||||
registerEventListener("Reader:LongClick");
|
||||
|
||||
mShowSiteSecurity = false;
|
||||
|
||||
mAnimatingEntry = false;
|
||||
|
||||
final Resources res = getResources();
|
||||
mUrlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left);
|
||||
mDefaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset);
|
||||
mUrlDisplayContainer = findViewById(R.id.url_display_container);
|
||||
mUrlDisplayLayout = (ToolbarDisplayLayout) findViewById(R.id.display_layout);
|
||||
mUrlBarEntry = findViewById(R.id.url_bar_entry);
|
||||
mUrlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout);
|
||||
|
||||
@ -277,9 +171,6 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
mUrlBarRightEdge.getDrawable().setLevel(6000);
|
||||
}
|
||||
|
||||
mTitle = (GeckoTextView) findViewById(R.id.url_bar_title);
|
||||
mTitlePadding = mTitle.getPaddingRight();
|
||||
|
||||
mTabs = (ShapedButton) findViewById(R.id.tabs);
|
||||
mTabsCounter = (TabCounter) findViewById(R.id.tabs_counter);
|
||||
|
||||
@ -288,26 +179,6 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
mForward = (ImageButton) findViewById(R.id.forward);
|
||||
setButtonEnabled(mForward, false);
|
||||
|
||||
mFavicon = (ImageButton) findViewById(R.id.favicon);
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
mFavicon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
}
|
||||
mFavicon.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
mFaviconSize = Math.round(res.getDimension(R.dimen.browser_toolbar_favicon_size));
|
||||
|
||||
mSiteSecurity = (ImageButton) findViewById(R.id.site_security);
|
||||
mSiteSecurityVisible = (mSiteSecurity.getVisibility() == View.VISIBLE);
|
||||
|
||||
mSiteIdentityPopup = new SiteIdentityPopup(mActivity);
|
||||
mSiteIdentityPopup.setAnchor(mSiteSecurity);
|
||||
|
||||
mProgressSpinner = AnimationUtils.loadAnimation(mActivity, R.anim.progress_spinner);
|
||||
|
||||
mStop = (ImageButton) findViewById(R.id.stop);
|
||||
mPageActionLayout = (PageActionLayout) findViewById(R.id.page_action_layout);
|
||||
|
||||
mMenu = (GeckoImageButton) findViewById(R.id.menu);
|
||||
mMenuIcon = (GeckoImageView) findViewById(R.id.menu_icon);
|
||||
mActionItemBar = (LinearLayout) findViewById(R.id.menu_items);
|
||||
@ -315,12 +186,15 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
|
||||
// We use different layouts on phones and tablets, so adjust the focus
|
||||
// order appropriately.
|
||||
mFocusOrder = new ArrayList<View>();
|
||||
if (HardwareUtils.isTablet()) {
|
||||
mFocusOrder = Arrays.asList(mTabs, mBack, mForward, this,
|
||||
mSiteSecurity, mPageActionLayout, mStop, mActionItemBar, mMenu);
|
||||
mFocusOrder.addAll(Arrays.asList(mTabs, mBack, mForward, this));
|
||||
mFocusOrder.addAll(mUrlDisplayLayout.getFocusOrder());
|
||||
mFocusOrder.addAll(Arrays.asList(mActionItemBar, mMenu));
|
||||
} else {
|
||||
mFocusOrder = Arrays.asList(this, mSiteSecurity, mPageActionLayout, mStop,
|
||||
mTabs, mMenu);
|
||||
mFocusOrder.add(this);
|
||||
mFocusOrder.addAll(mUrlDisplayLayout.getFocusOrder());
|
||||
mFocusOrder.addAll(Arrays.asList(mTabs, mMenu));
|
||||
}
|
||||
|
||||
setIsEditing(false);
|
||||
@ -380,6 +254,35 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
});
|
||||
|
||||
mUrlDisplayLayout.setOnStopListener(new OnStopListener() {
|
||||
@Override
|
||||
public Tab onStop() {
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null) {
|
||||
tab.doStop();
|
||||
return tab;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
mUrlDisplayLayout.setOnTitleChangeListener(new OnTitleChangeListener() {
|
||||
@Override
|
||||
public void onTitleChange(CharSequence title) {
|
||||
final String contentDescription;
|
||||
if (title != null) {
|
||||
contentDescription = title.toString();
|
||||
} else {
|
||||
contentDescription = mActivity.getString(R.string.url_bar_default_text);
|
||||
}
|
||||
|
||||
// The title and content description should
|
||||
// always be sync.
|
||||
setContentDescription(contentDescription);
|
||||
}
|
||||
});
|
||||
|
||||
mUrlEditLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
@ -421,58 +324,6 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
});
|
||||
|
||||
Button.OnClickListener faviconListener = new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mSiteSecurity.getVisibility() != View.VISIBLE)
|
||||
return;
|
||||
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
|
||||
final SiteIdentity siteIdentity = tab.getSiteIdentity();
|
||||
if (siteIdentity.getSecurityMode() == SecurityMode.UNKNOWN) {
|
||||
Log.e(LOGTAG, "Selected tab has no identity data");
|
||||
return;
|
||||
}
|
||||
|
||||
mSiteIdentityPopup.updateIdentity(siteIdentity);
|
||||
mSiteIdentityPopup.show();
|
||||
}
|
||||
};
|
||||
|
||||
mFavicon.setOnClickListener(faviconListener);
|
||||
mSiteSecurity.setOnClickListener(faviconListener);
|
||||
|
||||
mStop.setOnClickListener(new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null)
|
||||
tab.doStop();
|
||||
setProgressVisibility(false);
|
||||
}
|
||||
});
|
||||
|
||||
float slideWidth = getResources().getDimension(R.dimen.browser_toolbar_lock_width);
|
||||
|
||||
LinearLayout.LayoutParams siteSecParams = (LinearLayout.LayoutParams) mSiteSecurity.getLayoutParams();
|
||||
final float scale = getResources().getDisplayMetrics().density;
|
||||
slideWidth += (siteSecParams.leftMargin + siteSecParams.rightMargin) * scale + 0.5f;
|
||||
|
||||
mLockFadeIn = new AlphaAnimation(0.0f, 1.0f);
|
||||
mLockFadeIn.setAnimationListener(this);
|
||||
|
||||
mTitleSlideLeft = new TranslateAnimation(slideWidth, 0, 0, 0);
|
||||
mTitleSlideLeft.setAnimationListener(this);
|
||||
|
||||
mTitleSlideRight = new TranslateAnimation(-slideWidth, 0, 0, 0);
|
||||
mTitleSlideRight.setAnimationListener(this);
|
||||
|
||||
final int lockAnimDuration = 300;
|
||||
mLockFadeIn.setDuration(lockAnimDuration);
|
||||
mTitleSlideLeft.setDuration(lockAnimDuration);
|
||||
mTitleSlideRight.setDuration(lockAnimDuration);
|
||||
|
||||
if (mHasSoftMenuButton) {
|
||||
mMenu.setVisibility(View.VISIBLE);
|
||||
mMenuIcon.setVisibility(View.VISIBLE);
|
||||
@ -487,11 +338,11 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
dismissSiteIdentityPopup();
|
||||
mUrlDisplayLayout.dismissSiteIdentityPopup();
|
||||
}
|
||||
|
||||
public boolean onBackPressed() {
|
||||
return dismissSiteIdentityPopup();
|
||||
return mUrlDisplayLayout.dismissSiteIdentityPopup();
|
||||
}
|
||||
|
||||
public boolean onKey(int keyCode, KeyEvent event) {
|
||||
@ -570,39 +421,41 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
case RESTORED:
|
||||
// TabCount fixup after OOM
|
||||
case SELECTED:
|
||||
dismissSiteIdentityPopup();
|
||||
mUrlDisplayLayout.dismissSiteIdentityPopup();
|
||||
updateTabCount(tabs.getDisplayCount());
|
||||
mSwitchingTabs = true;
|
||||
// Fall through.
|
||||
}
|
||||
|
||||
if (tabs.isSelectedTab(tab)) {
|
||||
final EnumSet<UpdateFlags> flags = EnumSet.noneOf(UpdateFlags.class);
|
||||
|
||||
switch (msg) {
|
||||
case TITLE:
|
||||
updateTitle();
|
||||
flags.add(UpdateFlags.TITLE);
|
||||
break;
|
||||
|
||||
case START:
|
||||
updateBackButton(tab);
|
||||
updateForwardButton(tab);
|
||||
if (tab.getState() == Tab.STATE_LOADING) {
|
||||
setProgressVisibility(true);
|
||||
}
|
||||
setSecurityMode(tab.getSecurityMode());
|
||||
setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
|
||||
|
||||
flags.add(UpdateFlags.PROGRESS);
|
||||
break;
|
||||
|
||||
case STOP:
|
||||
updateBackButton(tab);
|
||||
updateForwardButton(tab);
|
||||
setProgressVisibility(false);
|
||||
// Reset the title in case we haven't navigated to a new page yet.
|
||||
updateTitle();
|
||||
|
||||
flags.add(UpdateFlags.PROGRESS);
|
||||
|
||||
// Reset the title in case we haven't navigated
|
||||
// to a new page yet.
|
||||
flags.add(UpdateFlags.TITLE);
|
||||
break;
|
||||
|
||||
case SELECTED:
|
||||
case LOAD_ERROR:
|
||||
updateTitle();
|
||||
flags.add(UpdateFlags.TITLE);
|
||||
// Fall through.
|
||||
case LOCATION_CHANGE:
|
||||
// A successful location change will cause Tab to notify
|
||||
@ -617,16 +470,16 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
break;
|
||||
|
||||
case FAVICON:
|
||||
setFavicon(tab.getFavicon());
|
||||
flags.add(UpdateFlags.FAVICON);
|
||||
break;
|
||||
|
||||
case SECURITY_CHANGE:
|
||||
setSecurityMode(tab.getSecurityMode());
|
||||
flags.add(UpdateFlags.SITE_IDENTITY);
|
||||
break;
|
||||
}
|
||||
|
||||
case READER_ENABLED:
|
||||
setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
|
||||
break;
|
||||
if (!flags.isEmpty()) {
|
||||
updateDisplayLayout(tab, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@ -642,55 +495,16 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
return ViewHelper.getTranslationY(this) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextFocusDownId(int nextId) {
|
||||
super.setNextFocusDownId(nextId);
|
||||
mTabs.setNextFocusDownId(nextId);
|
||||
mBack.setNextFocusDownId(nextId);
|
||||
mForward.setNextFocusDownId(nextId);
|
||||
mFavicon.setNextFocusDownId(nextId);
|
||||
mStop.setNextFocusDownId(nextId);
|
||||
mSiteSecurity.setNextFocusDownId(nextId);
|
||||
mPageActionLayout.setNextFocusDownId(nextId);
|
||||
mUrlDisplayLayout.setNextFocusDownId(nextId);
|
||||
mMenu.setNextFocusDownId(nextId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
if (animation.equals(mLockFadeIn)) {
|
||||
if (mSiteSecurityVisible)
|
||||
mSiteSecurity.setVisibility(View.VISIBLE);
|
||||
} else if (animation.equals(mTitleSlideLeft)) {
|
||||
// These two animations may be scheduled to start while the forward
|
||||
// animation is occurring. If we're showing the site security icon, make
|
||||
// sure it doesn't take any space during the forward transition.
|
||||
mSiteSecurity.setVisibility(View.GONE);
|
||||
} else if (animation.equals(mTitleSlideRight)) {
|
||||
// If we're hiding the icon, make sure that we keep its padding
|
||||
// in place during the forward transition
|
||||
mSiteSecurity.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
if (animation.equals(mTitleSlideRight)) {
|
||||
mSiteSecurity.startAnimation(mLockFadeIn);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean dismissSiteIdentityPopup() {
|
||||
if (mSiteIdentityPopup != null && mSiteIdentityPopup.isShowing()) {
|
||||
mSiteIdentityPopup.dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getUrlBarEntryTranslation() {
|
||||
return getWidth() - mUrlBarEntry.getRight();
|
||||
}
|
||||
@ -731,7 +545,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTabCountAndAnimate(int count) {
|
||||
private void updateTabCountAndAnimate(int count) {
|
||||
// Don't animate if the toolbar is hidden.
|
||||
if (!isVisible()) {
|
||||
updateTabCount(count);
|
||||
@ -751,7 +565,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTabCount(int count) {
|
||||
private void updateTabCount(int count) {
|
||||
// If toolbar is in edit mode on a phone, this means the entry is expanded
|
||||
// and the tabs button is translated offscreen. Don't trigger tabs counter
|
||||
// updates until the tabs button is back on screen.
|
||||
@ -773,84 +587,22 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
mActivity.getString(R.string.one_tab));
|
||||
}
|
||||
|
||||
public void setProgressVisibility(boolean visible) {
|
||||
Log.d(LOGTAG, "setProgressVisibility: " + visible);
|
||||
// The "Throbber start" and "Throbber stop" log messages in this method
|
||||
// are needed by S1/S2 tests (http://mrcote.info/phonedash/#).
|
||||
// See discussion in Bug 804457. Bug 805124 tracks paring these down.
|
||||
if (visible) {
|
||||
mFavicon.setImageResource(R.drawable.progress_spinner);
|
||||
mLastFavicon = null;
|
||||
|
||||
// To stop the glitch caused by multiple start() calls.
|
||||
if (!mSpinnerVisible) {
|
||||
setPageActionVisibility(true);
|
||||
mFavicon.setAnimation(mProgressSpinner);
|
||||
mProgressSpinner.start();
|
||||
mSpinnerVisible = true;
|
||||
}
|
||||
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber start");
|
||||
} else {
|
||||
Tab selectedTab = Tabs.getInstance().getSelectedTab();
|
||||
if (selectedTab != null) {
|
||||
setFavicon(selectedTab.getFavicon());
|
||||
}
|
||||
|
||||
if (mSpinnerVisible) {
|
||||
setPageActionVisibility(false);
|
||||
mFavicon.setAnimation(null);
|
||||
mProgressSpinner.cancel();
|
||||
mSpinnerVisible = false;
|
||||
}
|
||||
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber stop");
|
||||
}
|
||||
}
|
||||
|
||||
public void setPageActionVisibility(boolean isLoading) {
|
||||
// Handle the loading mode page actions
|
||||
mStop.setVisibility(isLoading ? View.VISIBLE : View.GONE);
|
||||
|
||||
// Handle the viewing mode page actions
|
||||
setSiteSecurityVisibility(mShowSiteSecurity && !isLoading);
|
||||
mPageActionLayout.setVisibility(!isLoading ? View.VISIBLE : View.GONE);
|
||||
|
||||
// We want title to fill the whole space available for it when there are icons
|
||||
// being shown on the right side of the toolbar as the icons already have some
|
||||
// padding in them. This is just to avoid wasting space when icons are shown.
|
||||
mTitle.setPadding(0, 0, (!isLoading ? mTitlePadding : 0), 0);
|
||||
updateFocusOrder();
|
||||
}
|
||||
|
||||
private void setSiteSecurityVisibility(final boolean visible) {
|
||||
if (visible == mSiteSecurityVisible)
|
||||
return;
|
||||
|
||||
mSiteSecurityVisible = visible;
|
||||
|
||||
private void updateDisplayLayout(Tab tab, EnumSet<UpdateFlags> flags) {
|
||||
if (mSwitchingTabs) {
|
||||
mSiteSecurity.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
return;
|
||||
flags.add(UpdateFlags.DISABLE_ANIMATIONS);
|
||||
}
|
||||
|
||||
mTitle.clearAnimation();
|
||||
mSiteSecurity.clearAnimation();
|
||||
mUrlDisplayLayout.updateFromTab(tab, flags);
|
||||
|
||||
// If any of these animations were cancelled as a result of the
|
||||
// clearAnimation() calls above, we need to reset them.
|
||||
mLockFadeIn.reset();
|
||||
mTitleSlideLeft.reset();
|
||||
mTitleSlideRight.reset();
|
||||
|
||||
if (mForwardAnim != null) {
|
||||
long delay = mForwardAnim.getRemainingTime();
|
||||
mTitleSlideRight.setStartOffset(delay);
|
||||
mTitleSlideLeft.setStartOffset(delay);
|
||||
} else {
|
||||
mTitleSlideRight.setStartOffset(0);
|
||||
mTitleSlideLeft.setStartOffset(0);
|
||||
if (flags.contains(UpdateFlags.TITLE)) {
|
||||
if (!isEditing()) {
|
||||
mUrlEditLayout.setText(tab.getURL());
|
||||
}
|
||||
}
|
||||
|
||||
mTitle.startAnimation(visible ? mTitleSlideRight : mTitleSlideLeft);
|
||||
if (flags.contains(UpdateFlags.PROGRESS)) {
|
||||
updateFocusOrder();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFocusOrder() {
|
||||
@ -901,95 +653,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
mTitle.setText(title);
|
||||
setContentDescription(title != null ? title : mTitle.getHint());
|
||||
}
|
||||
|
||||
// Sets the toolbar title according to the selected tab, obeying the mShowUrl preference.
|
||||
private void updateTitle() {
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
// Keep the title unchanged if there's no selected tab, or if the tab is entering reader mode.
|
||||
if (tab == null || tab.isEnteringReaderMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String url = tab.getURL();
|
||||
|
||||
if (!isEditing()) {
|
||||
mUrlEditLayout.setText(url);
|
||||
}
|
||||
|
||||
// Setting a null title will ensure we just see the "Enter Search or Address" placeholder text.
|
||||
if (AboutPages.isTitlelessAboutPage(url)) {
|
||||
setTitle(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the about:blocked page title in red, regardless of prefs
|
||||
if (tab.getErrorType() == Tab.ErrorType.BLOCKED) {
|
||||
String title = tab.getDisplayTitle();
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(title);
|
||||
builder.setSpan(mBlockedColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
setTitle(builder);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the pref to show the URL isn't set, just use the tab's display title.
|
||||
if (!mShowUrl || url == null) {
|
||||
setTitle(tab.getDisplayTitle());
|
||||
return;
|
||||
}
|
||||
|
||||
CharSequence title = url;
|
||||
if (mTrimURLs) {
|
||||
title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url));
|
||||
}
|
||||
|
||||
String baseDomain = tab.getBaseDomain();
|
||||
if (!TextUtils.isEmpty(baseDomain)) {
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(title);
|
||||
int index = title.toString().indexOf(baseDomain);
|
||||
if (index > -1) {
|
||||
builder.setSpan(mUrlColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
builder.setSpan(tab.isPrivate() ? mPrivateDomainColor : mDomainColor, index, index+baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
title = builder;
|
||||
}
|
||||
}
|
||||
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
public void showDefaultFavicon() {
|
||||
mFavicon.setImageResource(R.drawable.favicon);
|
||||
mLastFavicon = null;
|
||||
}
|
||||
|
||||
private void setFavicon(Bitmap image) {
|
||||
Log.d(LOGTAG, "setFavicon(" + image + ")");
|
||||
if (Tabs.getInstance().getSelectedTab().getState() == Tab.STATE_LOADING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (image == mLastFavicon) {
|
||||
Log.d(LOGTAG, "Ignoring favicon set: new favicon is identical to previous favicon.");
|
||||
return;
|
||||
}
|
||||
|
||||
mLastFavicon = image; // Cache the original so we can debounce without scaling.
|
||||
|
||||
if (image != null) {
|
||||
image = Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, false);
|
||||
mFavicon.setImageBitmap(image);
|
||||
} else {
|
||||
mFavicon.setImageDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSecurityMode(SecurityMode mode) {
|
||||
mSiteSecurity.setImageLevel(mode.ordinal());
|
||||
mShowSiteSecurity = (mode != SecurityMode.UNKNOWN);
|
||||
|
||||
setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
|
||||
mUrlDisplayLayout.setTitle(title);
|
||||
}
|
||||
|
||||
public void prepareTabsAnimation(PropertyAnimator animator, boolean tabsAreShown) {
|
||||
@ -1083,8 +747,8 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
|
||||
private void setUrlEditLayoutVisibility(final boolean showEditLayout, PropertyAnimator animator) {
|
||||
final View viewToShow = (showEditLayout ? mUrlEditLayout : mUrlDisplayContainer);
|
||||
final View viewToHide = (showEditLayout ? mUrlDisplayContainer : mUrlEditLayout);
|
||||
final View viewToShow = (showEditLayout ? mUrlEditLayout : mUrlDisplayLayout);
|
||||
final View viewToHide = (showEditLayout ? mUrlDisplayLayout : mUrlEditLayout);
|
||||
|
||||
if (showEditLayout) {
|
||||
mUrlEditLayout.prepareShowAnimation(animator);
|
||||
@ -1163,7 +827,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
}
|
||||
|
||||
public void setIsEditing(boolean isEditing) {
|
||||
private void setIsEditing(boolean isEditing) {
|
||||
mIsEditing = isEditing;
|
||||
mUrlEditLayout.setEnabled(isEditing);
|
||||
}
|
||||
@ -1225,9 +889,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
// Highlight the toolbar from the start of the animation.
|
||||
setSelected(true);
|
||||
|
||||
// Hide page actions/stop buttons immediately
|
||||
ViewHelper.setAlpha(mPageActionLayout, 0);
|
||||
ViewHelper.setAlpha(mStop, 0);
|
||||
mUrlDisplayLayout.prepareStartEditingAnimation();
|
||||
|
||||
// Slide the right side elements of the toolbar
|
||||
|
||||
@ -1376,16 +1038,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
|
||||
PropertyAnimator buttonsAnimator = new PropertyAnimator(300);
|
||||
|
||||
// Fade toolbar buttons (page actions, stop) after the entry
|
||||
// is schrunk back to its original size.
|
||||
buttonsAnimator.attach(mPageActionLayout,
|
||||
PropertyAnimator.Property.ALPHA,
|
||||
1);
|
||||
buttonsAnimator.attach(mStop,
|
||||
PropertyAnimator.Property.ALPHA,
|
||||
1);
|
||||
|
||||
mUrlDisplayLayout.prepareStopEditingAnimation(buttonsAnimator);
|
||||
buttonsAnimator.start();
|
||||
|
||||
mAnimatingEntry = false;
|
||||
@ -1435,17 +1088,18 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
|
||||
// We want the forward button to show immediately when switching tabs
|
||||
mForwardAnim = new PropertyAnimator(mSwitchingTabs ? 10 : FORWARD_ANIMATION_DURATION);
|
||||
final PropertyAnimator forwardAnim =
|
||||
new PropertyAnimator(mSwitchingTabs ? 10 : FORWARD_ANIMATION_DURATION);
|
||||
final int width = mForward.getWidth() / 2;
|
||||
|
||||
mForwardAnim.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
||||
forwardAnim.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
||||
@Override
|
||||
public void onPropertyAnimationStart() {
|
||||
if (!showing) {
|
||||
// Set the margin before the transition when hiding the forward button. We
|
||||
// have to do this so that the favicon isn't clipped during the transition
|
||||
MarginLayoutParams layoutParams =
|
||||
(MarginLayoutParams) mUrlDisplayContainer.getLayoutParams();
|
||||
(MarginLayoutParams) mUrlDisplayLayout.getLayoutParams();
|
||||
layoutParams.leftMargin = 0;
|
||||
|
||||
// Do the same on the URL edit container
|
||||
@ -1463,28 +1117,25 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
public void onPropertyAnimationEnd() {
|
||||
if (showing) {
|
||||
MarginLayoutParams layoutParams =
|
||||
(MarginLayoutParams) mUrlDisplayContainer.getLayoutParams();
|
||||
(MarginLayoutParams) mUrlDisplayLayout.getLayoutParams();
|
||||
layoutParams.leftMargin = mUrlBarViewOffset;
|
||||
|
||||
layoutParams = (MarginLayoutParams) mUrlEditLayout.getLayoutParams();
|
||||
layoutParams.leftMargin = mUrlBarViewOffset;
|
||||
|
||||
ViewHelper.setTranslationX(mTitle, 0);
|
||||
ViewHelper.setTranslationX(mFavicon, 0);
|
||||
ViewHelper.setTranslationX(mSiteSecurity, 0);
|
||||
}
|
||||
|
||||
mUrlDisplayLayout.finishForwardAnimation();
|
||||
|
||||
MarginLayoutParams layoutParams = (MarginLayoutParams) mForward.getLayoutParams();
|
||||
layoutParams.leftMargin = mDefaultForwardMargin + (showing ? width : 0);
|
||||
ViewHelper.setTranslationX(mForward, 0);
|
||||
|
||||
requestLayout();
|
||||
mForwardAnim = null;
|
||||
}
|
||||
});
|
||||
|
||||
prepareForwardAnimation(mForwardAnim, animation, width);
|
||||
mForwardAnim.start();
|
||||
prepareForwardAnimation(forwardAnim, animation, width);
|
||||
forwardAnim.start();
|
||||
}
|
||||
|
||||
public void updateForwardButton(Tab tab) {
|
||||
@ -1506,22 +1157,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
anim.attach(mForward,
|
||||
PropertyAnimator.Property.ALPHA,
|
||||
0);
|
||||
anim.attach(mTitle,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
0);
|
||||
anim.attach(mFavicon,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
0);
|
||||
anim.attach(mSiteSecurity,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
0);
|
||||
|
||||
// We're hiding the forward button. We're going to reset the margin before
|
||||
// the animation starts, so we shift these items to the right so that they don't
|
||||
// appear to move initially.
|
||||
ViewHelper.setTranslationX(mTitle, mUrlBarViewOffset);
|
||||
ViewHelper.setTranslationX(mFavicon, mUrlBarViewOffset);
|
||||
ViewHelper.setTranslationX(mSiteSecurity, mUrlBarViewOffset);
|
||||
} else {
|
||||
anim.attach(mForward,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
@ -1529,16 +1165,9 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
anim.attach(mForward,
|
||||
PropertyAnimator.Property.ALPHA,
|
||||
1);
|
||||
anim.attach(mTitle,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
mUrlBarViewOffset);
|
||||
anim.attach(mFavicon,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
mUrlBarViewOffset);
|
||||
anim.attach(mSiteSecurity,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
mUrlBarViewOffset);
|
||||
}
|
||||
|
||||
mUrlDisplayLayout.prepareForwardAnimation(anim, animation, width);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1563,17 +1192,16 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
private void refreshState() {
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null) {
|
||||
setFavicon(tab.getFavicon());
|
||||
setProgressVisibility(tab.getState() == Tab.STATE_LOADING);
|
||||
setSecurityMode(tab.getSecurityMode());
|
||||
setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
|
||||
updateDisplayLayout(tab, EnumSet.of(UpdateFlags.FAVICON,
|
||||
UpdateFlags.SITE_IDENTITY,
|
||||
UpdateFlags.PROGRESS,
|
||||
UpdateFlags.PRIVATE_MODE));
|
||||
updateBackButton(tab);
|
||||
updateForwardButton(tab);
|
||||
|
||||
final boolean isPrivate = tab.isPrivate();
|
||||
setPrivateMode(isPrivate);
|
||||
mTabs.setPrivateMode(isPrivate);
|
||||
mTitle.setPrivateMode(isPrivate);
|
||||
mMenu.setPrivateMode(isPrivate);
|
||||
mMenuIcon.setPrivateMode(isPrivate);
|
||||
mUrlEditLayout.setPrivateMode(isPrivate);
|
||||
@ -1587,14 +1215,10 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
}
|
||||
|
||||
public View getDoorHangerAnchor() {
|
||||
return mFavicon;
|
||||
return mUrlDisplayLayout.getDoorHangerAnchor();
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
if (mPrefObserverId != null) {
|
||||
PrefsHelper.removeObserver(mPrefObserverId);
|
||||
mPrefObserverId = null;
|
||||
}
|
||||
Tabs.unregisterOnTabsChangedListener(this);
|
||||
|
||||
unregisterEventListener("Reader:Click");
|
||||
@ -1636,11 +1260,11 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void registerEventListener(String event) {
|
||||
private void registerEventListener(String event) {
|
||||
GeckoAppShell.getEventDispatcher().registerEventListener(event, this);
|
||||
}
|
||||
|
||||
protected void unregisterEventListener(String event) {
|
||||
private void unregisterEventListener(String event) {
|
||||
GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this);
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,8 @@ public class SiteIdentityPopup extends ArrowPopup {
|
||||
private static final String MIXED_CONTENT_SUPPORT_URL =
|
||||
"https://support.mozilla.org/kb/how-does-content-isnt-secure-affect-my-safety";
|
||||
|
||||
private SiteIdentity mSiteIdentity;
|
||||
|
||||
private Resources mResources;
|
||||
|
||||
private LinearLayout mIdentity;
|
||||
@ -72,27 +74,31 @@ public class SiteIdentityPopup extends ArrowPopup {
|
||||
mVerifier = (TextView) mIdentity.findViewById(R.id.verifier);
|
||||
}
|
||||
|
||||
private void setIdentity(SiteIdentity siteIdentity) {
|
||||
if (siteIdentity.getSecurityMode() == SecurityMode.MIXED_CONTENT_LOADED) {
|
||||
private void updateUi() {
|
||||
if (!mInflated) {
|
||||
init();
|
||||
}
|
||||
|
||||
if (mSiteIdentity.getSecurityMode() == SecurityMode.MIXED_CONTENT_LOADED) {
|
||||
// Hide the identity data if there isn't valid site identity data.
|
||||
// Set some top padding on the popup content to create a of light blue
|
||||
// between the popup arrow and the mixed content notification.
|
||||
mContent.setPadding(0, (int) mResources.getDimension(R.dimen.identity_padding_top), 0, 0);
|
||||
mIdentity.setVisibility(View.GONE);
|
||||
} else {
|
||||
mHost.setText(siteIdentity.getHost());
|
||||
mHost.setText(mSiteIdentity.getHost());
|
||||
|
||||
String owner = siteIdentity.getOwner();
|
||||
String owner = mSiteIdentity.getOwner();
|
||||
|
||||
// Supplemental data is optional.
|
||||
final String supplemental = siteIdentity.getSupplemental();
|
||||
final String supplemental = mSiteIdentity.getSupplemental();
|
||||
if (!TextUtils.isEmpty(supplemental)) {
|
||||
owner += "\n" + supplemental;
|
||||
}
|
||||
mOwner.setText(owner);
|
||||
|
||||
final String verifier = siteIdentity.getVerifier();
|
||||
final String encrypted = siteIdentity.getEncrypted();
|
||||
final String verifier = mSiteIdentity.getVerifier();
|
||||
final String encrypted = mSiteIdentity.getEncrypted();
|
||||
mVerifier.setText(verifier + "\n" + encrypted);
|
||||
|
||||
mContent.setPadding(0, 0, 0, 0);
|
||||
@ -140,22 +146,31 @@ public class SiteIdentityPopup extends ArrowPopup {
|
||||
/*
|
||||
* @param identityData A JSONObject that holds the current tab's identity data.
|
||||
*/
|
||||
void updateIdentity(SiteIdentity siteIdentity) {
|
||||
final SecurityMode mode = siteIdentity.getSecurityMode();
|
||||
void setSiteIdentity(SiteIdentity siteIdentity) {
|
||||
mSiteIdentity = siteIdentity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
if (mSiteIdentity == null) {
|
||||
Log.e(LOGTAG, "Can't show site identity popup for undefined state");
|
||||
return;
|
||||
}
|
||||
|
||||
final SecurityMode mode = mSiteIdentity.getSecurityMode();
|
||||
if (mode == SecurityMode.UNKNOWN) {
|
||||
Log.e(LOGTAG, "Can't show site identity popup in non-identified state");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mInflated)
|
||||
init();
|
||||
|
||||
setIdentity(siteIdentity);
|
||||
updateUi();
|
||||
|
||||
if (mode == SecurityMode.MIXED_CONTENT_LOADED ||
|
||||
mode == SecurityMode.MIXED_CONTENT_BLOCKED) {
|
||||
addMixedContentNotification(mode == SecurityMode.MIXED_CONTENT_BLOCKED);
|
||||
}
|
||||
|
||||
super.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
554
mobile/android/base/toolbar/ToolbarDisplayLayout.java
Normal file
554
mobile/android/base/toolbar/ToolbarDisplayLayout.java
Normal file
@ -0,0 +1,554 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.toolbar;
|
||||
|
||||
import org.mozilla.gecko.AboutPages;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.SiteIdentity;
|
||||
import org.mozilla.gecko.SiteIdentity.SecurityMode;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.toolbar.BrowserToolbar.ForwardButtonAnimation;
|
||||
import org.mozilla.gecko.util.StringUtils;
|
||||
import org.mozilla.gecko.widget.GeckoLinearLayout;
|
||||
import org.mozilla.gecko.widget.GeckoTextView;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class ToolbarDisplayLayout extends GeckoLinearLayout
|
||||
implements Animation.AnimationListener {
|
||||
|
||||
private static final String LOGTAG = "GeckoToolbarDisplayLayout";
|
||||
|
||||
enum UpdateFlags {
|
||||
TITLE,
|
||||
FAVICON,
|
||||
PROGRESS,
|
||||
SITE_IDENTITY,
|
||||
PRIVATE_MODE,
|
||||
DISABLE_ANIMATIONS
|
||||
}
|
||||
|
||||
private enum UIMode {
|
||||
PROGRESS,
|
||||
DISPLAY
|
||||
}
|
||||
|
||||
interface OnStopListener {
|
||||
public Tab onStop();
|
||||
}
|
||||
|
||||
interface OnTitleChangeListener {
|
||||
public void onTitleChange(CharSequence title);
|
||||
}
|
||||
|
||||
final private BrowserApp mActivity;
|
||||
|
||||
private UIMode mUiMode;
|
||||
|
||||
private GeckoTextView mTitle;
|
||||
private int mTitlePadding;
|
||||
private ToolbarTitlePrefs mTitlePrefs;
|
||||
private OnTitleChangeListener mTitleChangeListener;
|
||||
|
||||
private ImageButton mSiteSecurity;
|
||||
private boolean mSiteSecurityVisible;
|
||||
|
||||
// To de-bounce sets.
|
||||
private Bitmap mLastFavicon;
|
||||
private ImageButton mFavicon;
|
||||
private int mFaviconSize;
|
||||
|
||||
private ImageButton mStop;
|
||||
private OnStopListener mStopListener;
|
||||
|
||||
private PageActionLayout mPageActionLayout;
|
||||
|
||||
private Animation mProgressSpinner;
|
||||
|
||||
private AlphaAnimation mLockFadeIn;
|
||||
private TranslateAnimation mTitleSlideLeft;
|
||||
private TranslateAnimation mTitleSlideRight;
|
||||
|
||||
private SiteIdentityPopup mSiteIdentityPopup;
|
||||
private SecurityMode mSecurityMode;
|
||||
|
||||
private PropertyAnimator mForwardAnim;
|
||||
|
||||
private final ForegroundColorSpan mUrlColor;
|
||||
private final ForegroundColorSpan mBlockedColor;
|
||||
private final ForegroundColorSpan mDomainColor;
|
||||
private final ForegroundColorSpan mPrivateDomainColor;
|
||||
|
||||
public ToolbarDisplayLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setOrientation(HORIZONTAL);
|
||||
|
||||
mActivity = (BrowserApp) context;
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.toolbar_display_layout, this);
|
||||
|
||||
mTitle = (GeckoTextView) findViewById(R.id.url_bar_title);
|
||||
mTitlePadding = mTitle.getPaddingRight();
|
||||
|
||||
final Resources res = getResources();
|
||||
|
||||
mUrlColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_urltext));
|
||||
mBlockedColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_blockedtext));
|
||||
mDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext));
|
||||
mPrivateDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext_private));
|
||||
|
||||
mFavicon = (ImageButton) findViewById(R.id.favicon);
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
mFavicon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
}
|
||||
mFavicon.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
mFaviconSize = Math.round(res.getDimension(R.dimen.browser_toolbar_favicon_size));
|
||||
|
||||
mSiteSecurity = (ImageButton) findViewById(R.id.site_security);
|
||||
mSiteSecurityVisible = (mSiteSecurity.getVisibility() == View.VISIBLE);
|
||||
|
||||
mSiteIdentityPopup = new SiteIdentityPopup(mActivity);
|
||||
mSiteIdentityPopup.setAnchor(mSiteSecurity);
|
||||
|
||||
mProgressSpinner = AnimationUtils.loadAnimation(mActivity, R.anim.progress_spinner);
|
||||
|
||||
mStop = (ImageButton) findViewById(R.id.stop);
|
||||
mPageActionLayout = (PageActionLayout) findViewById(R.id.page_action_layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
mTitlePrefs = new ToolbarTitlePrefs();
|
||||
|
||||
Button.OnClickListener faviconListener = new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mSiteSecurity.getVisibility() != View.VISIBLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
mSiteIdentityPopup.show();
|
||||
}
|
||||
};
|
||||
|
||||
mFavicon.setOnClickListener(faviconListener);
|
||||
mSiteSecurity.setOnClickListener(faviconListener);
|
||||
|
||||
mStop.setOnClickListener(new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mStopListener != null) {
|
||||
// Force toolbar to switch to Display mode
|
||||
// immediately based on the stopped tab.
|
||||
final Tab tab = mStopListener.onStop();
|
||||
if (tab != null) {
|
||||
updateUiMode(tab, UIMode.DISPLAY, EnumSet.noneOf(UpdateFlags.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
float slideWidth = getResources().getDimension(R.dimen.browser_toolbar_lock_width);
|
||||
|
||||
LayoutParams siteSecParams = (LayoutParams) mSiteSecurity.getLayoutParams();
|
||||
final float scale = getResources().getDisplayMetrics().density;
|
||||
slideWidth += (siteSecParams.leftMargin + siteSecParams.rightMargin) * scale + 0.5f;
|
||||
|
||||
mLockFadeIn = new AlphaAnimation(0.0f, 1.0f);
|
||||
mLockFadeIn.setAnimationListener(this);
|
||||
|
||||
mTitleSlideLeft = new TranslateAnimation(slideWidth, 0, 0, 0);
|
||||
mTitleSlideLeft.setAnimationListener(this);
|
||||
|
||||
mTitleSlideRight = new TranslateAnimation(-slideWidth, 0, 0, 0);
|
||||
mTitleSlideRight.setAnimationListener(this);
|
||||
|
||||
final int lockAnimDuration = 300;
|
||||
mLockFadeIn.setDuration(lockAnimDuration);
|
||||
mTitleSlideLeft.setDuration(lockAnimDuration);
|
||||
mTitleSlideRight.setDuration(lockAnimDuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
mTitlePrefs.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
if (animation.equals(mLockFadeIn)) {
|
||||
if (mSiteSecurityVisible)
|
||||
mSiteSecurity.setVisibility(View.VISIBLE);
|
||||
} else if (animation.equals(mTitleSlideLeft)) {
|
||||
// These two animations may be scheduled to start while the forward
|
||||
// animation is occurring. If we're showing the site security icon, make
|
||||
// sure it doesn't take any space during the forward transition.
|
||||
mSiteSecurity.setVisibility(View.GONE);
|
||||
} else if (animation.equals(mTitleSlideRight)) {
|
||||
// If we're hiding the icon, make sure that we keep its padding
|
||||
// in place during the forward transition
|
||||
mSiteSecurity.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
if (animation.equals(mTitleSlideRight)) {
|
||||
mSiteSecurity.startAnimation(mLockFadeIn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextFocusDownId(int nextId) {
|
||||
mFavicon.setNextFocusDownId(nextId);
|
||||
mStop.setNextFocusDownId(nextId);
|
||||
mSiteSecurity.setNextFocusDownId(nextId);
|
||||
mPageActionLayout.setNextFocusDownId(nextId);
|
||||
}
|
||||
|
||||
void updateFromTab(Tab tab, EnumSet<UpdateFlags> flags) {
|
||||
if (flags.contains(UpdateFlags.TITLE)) {
|
||||
updateTitle(tab);
|
||||
}
|
||||
|
||||
if (flags.contains(UpdateFlags.FAVICON)) {
|
||||
updateFavicon(tab);
|
||||
}
|
||||
|
||||
if (flags.contains(UpdateFlags.SITE_IDENTITY)) {
|
||||
updateSiteIdentity(tab, flags);
|
||||
}
|
||||
|
||||
if (flags.contains(UpdateFlags.PROGRESS)) {
|
||||
updateProgress(tab, flags);
|
||||
}
|
||||
|
||||
if (flags.contains(UpdateFlags.PRIVATE_MODE)) {
|
||||
mTitle.setPrivateMode(tab != null && tab.isPrivate());
|
||||
}
|
||||
}
|
||||
|
||||
void setTitle(CharSequence title) {
|
||||
mTitle.setText(title);
|
||||
|
||||
if (mTitleChangeListener != null) {
|
||||
mTitleChangeListener.onTitleChange(title);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTitle(Tab tab) {
|
||||
// Keep the title unchanged if there's no selected tab,
|
||||
// or if the tab is entering reader mode.
|
||||
if (tab == null || tab.isEnteringReaderMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String url = tab.getURL();
|
||||
|
||||
// Setting a null title will ensure we just see the
|
||||
// "Enter Search or Address" placeholder text.
|
||||
if (AboutPages.isTitlelessAboutPage(url)) {
|
||||
setTitle(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the about:blocked page title in red, regardless of prefs
|
||||
if (tab.getErrorType() == Tab.ErrorType.BLOCKED) {
|
||||
final String title = tab.getDisplayTitle();
|
||||
|
||||
final SpannableStringBuilder builder = new SpannableStringBuilder(title);
|
||||
builder.setSpan(mBlockedColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
setTitle(builder);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the pref to show the URL isn't set, just use the tab's display title.
|
||||
if (!mTitlePrefs.shouldShowUrl() || url == null) {
|
||||
setTitle(tab.getDisplayTitle());
|
||||
return;
|
||||
}
|
||||
|
||||
CharSequence title = url;
|
||||
if (mTitlePrefs.shouldTrimUrls()) {
|
||||
title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url));
|
||||
}
|
||||
|
||||
final String baseDomain = tab.getBaseDomain();
|
||||
if (!TextUtils.isEmpty(baseDomain)) {
|
||||
final SpannableStringBuilder builder = new SpannableStringBuilder(title);
|
||||
|
||||
int index = title.toString().indexOf(baseDomain);
|
||||
if (index > -1) {
|
||||
builder.setSpan(mUrlColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
builder.setSpan(tab.isPrivate() ? mPrivateDomainColor : mDomainColor,
|
||||
index, index + baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
title = builder;
|
||||
}
|
||||
}
|
||||
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
private void updateFavicon(Tab tab) {
|
||||
if (tab == null) {
|
||||
mFavicon.setImageDrawable(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab.getState() == Tab.STATE_LOADING) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap image = tab.getFavicon();
|
||||
if (image == mLastFavicon) {
|
||||
Log.d(LOGTAG, "Ignoring favicon: new image is identical to previous one.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the original so we can debounce without scaling
|
||||
mLastFavicon = image;
|
||||
|
||||
Log.d(LOGTAG, "updateFavicon(" + image + ")");
|
||||
|
||||
if (image != null) {
|
||||
image = Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, false);
|
||||
mFavicon.setImageBitmap(image);
|
||||
} else {
|
||||
mFavicon.setImageDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSiteIdentity(Tab tab, EnumSet<UpdateFlags> flags) {
|
||||
final SiteIdentity siteIdentity;
|
||||
if (tab == null) {
|
||||
siteIdentity = null;
|
||||
} else {
|
||||
siteIdentity = tab.getSiteIdentity();
|
||||
}
|
||||
|
||||
mSiteIdentityPopup.setSiteIdentity(siteIdentity);
|
||||
|
||||
final SecurityMode securityMode;
|
||||
if (siteIdentity == null) {
|
||||
securityMode = SecurityMode.UNKNOWN;
|
||||
} else {
|
||||
securityMode = siteIdentity.getSecurityMode();
|
||||
}
|
||||
|
||||
if (mSecurityMode != securityMode) {
|
||||
mSecurityMode = securityMode;
|
||||
mSiteSecurity.setImageLevel(mSecurityMode.ordinal());
|
||||
updatePageActions(flags);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateProgress(Tab tab, EnumSet<UpdateFlags> flags) {
|
||||
final boolean shouldShowThrobber = (tab != null &&
|
||||
tab.getState() == Tab.STATE_LOADING);
|
||||
|
||||
updateUiMode(tab, shouldShowThrobber ? UIMode.PROGRESS : UIMode.DISPLAY, flags);
|
||||
}
|
||||
|
||||
private void updateUiMode(Tab tab, UIMode uiMode, EnumSet<UpdateFlags> flags) {
|
||||
if (mUiMode == uiMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
mUiMode = uiMode;
|
||||
|
||||
// The "Throbber start" and "Throbber stop" log messages in this method
|
||||
// are needed by S1/S2 tests (http://mrcote.info/phonedash/#).
|
||||
// See discussion in Bug 804457. Bug 805124 tracks paring these down.
|
||||
if (mUiMode == UIMode.PROGRESS) {
|
||||
mLastFavicon = null;
|
||||
mFavicon.setImageResource(R.drawable.progress_spinner);
|
||||
mFavicon.setAnimation(mProgressSpinner);
|
||||
mProgressSpinner.start();
|
||||
|
||||
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber start");
|
||||
} else {
|
||||
updateFavicon(tab);
|
||||
mFavicon.setAnimation(null);
|
||||
mProgressSpinner.cancel();
|
||||
|
||||
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber stop");
|
||||
}
|
||||
|
||||
updatePageActions(flags);
|
||||
}
|
||||
|
||||
private void updatePageActions(EnumSet<UpdateFlags> flags) {
|
||||
final boolean isShowingProgress = (mUiMode == UIMode.PROGRESS);
|
||||
|
||||
mStop.setVisibility(isShowingProgress ? View.VISIBLE : View.GONE);
|
||||
mPageActionLayout.setVisibility(!isShowingProgress ? View.VISIBLE : View.GONE);
|
||||
|
||||
boolean shouldShowSiteSecurity = (!isShowingProgress &&
|
||||
mSecurityMode != SecurityMode.UNKNOWN);
|
||||
|
||||
setSiteSecurityVisibility(shouldShowSiteSecurity, flags);
|
||||
|
||||
// We want title to fill the whole space available for it when there are icons
|
||||
// being shown on the right side of the toolbar as the icons already have some
|
||||
// padding in them. This is just to avoid wasting space when icons are shown.
|
||||
mTitle.setPadding(0, 0, (!isShowingProgress ? mTitlePadding : 0), 0);
|
||||
}
|
||||
|
||||
private void setSiteSecurityVisibility(boolean visible, EnumSet<UpdateFlags> flags) {
|
||||
if (visible == mSiteSecurityVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
mSiteSecurityVisible = visible;
|
||||
|
||||
if (flags.contains(UpdateFlags.DISABLE_ANIMATIONS)) {
|
||||
mSiteSecurity.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
mTitle.clearAnimation();
|
||||
mSiteSecurity.clearAnimation();
|
||||
|
||||
// If any of these animations were cancelled as a result of the
|
||||
// clearAnimation() calls above, we need to reset them.
|
||||
mLockFadeIn.reset();
|
||||
mTitleSlideLeft.reset();
|
||||
mTitleSlideRight.reset();
|
||||
|
||||
if (mForwardAnim != null) {
|
||||
long delay = mForwardAnim.getRemainingTime();
|
||||
mTitleSlideRight.setStartOffset(delay);
|
||||
mTitleSlideLeft.setStartOffset(delay);
|
||||
} else {
|
||||
mTitleSlideRight.setStartOffset(0);
|
||||
mTitleSlideLeft.setStartOffset(0);
|
||||
}
|
||||
|
||||
mTitle.startAnimation(visible ? mTitleSlideRight : mTitleSlideLeft);
|
||||
}
|
||||
|
||||
List<View> getFocusOrder() {
|
||||
return Arrays.asList(mSiteSecurity, mPageActionLayout, mStop);
|
||||
}
|
||||
|
||||
void setOnStopListener(OnStopListener listener) {
|
||||
mStopListener = listener;
|
||||
}
|
||||
|
||||
void setOnTitleChangeListener(OnTitleChangeListener listener) {
|
||||
mTitleChangeListener = listener;
|
||||
}
|
||||
|
||||
View getDoorHangerAnchor() {
|
||||
return mFavicon;
|
||||
}
|
||||
|
||||
void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) {
|
||||
mForwardAnim = anim;
|
||||
|
||||
if (animation == ForwardButtonAnimation.HIDE) {
|
||||
anim.attach(mTitle,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
0);
|
||||
anim.attach(mFavicon,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
0);
|
||||
anim.attach(mSiteSecurity,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
0);
|
||||
|
||||
// We're hiding the forward button. We're going to reset the margin before
|
||||
// the animation starts, so we shift these items to the right so that they don't
|
||||
// appear to move initially.
|
||||
ViewHelper.setTranslationX(mTitle, width);
|
||||
ViewHelper.setTranslationX(mFavicon, width);
|
||||
ViewHelper.setTranslationX(mSiteSecurity, width);
|
||||
} else {
|
||||
anim.attach(mTitle,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
width);
|
||||
anim.attach(mFavicon,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
width);
|
||||
anim.attach(mSiteSecurity,
|
||||
PropertyAnimator.Property.TRANSLATION_X,
|
||||
width);
|
||||
}
|
||||
}
|
||||
|
||||
void finishForwardAnimation() {
|
||||
ViewHelper.setTranslationX(mTitle, 0);
|
||||
ViewHelper.setTranslationX(mFavicon, 0);
|
||||
ViewHelper.setTranslationX(mSiteSecurity, 0);
|
||||
|
||||
mForwardAnim = null;
|
||||
}
|
||||
|
||||
void prepareStartEditingAnimation() {
|
||||
// Hide page actions/stop buttons immediately
|
||||
ViewHelper.setAlpha(mPageActionLayout, 0);
|
||||
ViewHelper.setAlpha(mStop, 0);
|
||||
}
|
||||
|
||||
void prepareStopEditingAnimation(PropertyAnimator anim) {
|
||||
// Fade toolbar buttons (page actions, stop) after the entry
|
||||
// is schrunk back to its original size.
|
||||
anim.attach(mPageActionLayout,
|
||||
PropertyAnimator.Property.ALPHA,
|
||||
1);
|
||||
|
||||
anim.attach(mStop,
|
||||
PropertyAnimator.Property.ALPHA,
|
||||
1);
|
||||
}
|
||||
|
||||
boolean dismissSiteIdentityPopup() {
|
||||
if (mSiteIdentityPopup != null && mSiteIdentityPopup.isShowing()) {
|
||||
mSiteIdentityPopup.dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user