mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge f-t to m-c
This commit is contained in:
commit
cef532b380
@ -179,18 +179,3 @@ function MixedTest6D() {
|
||||
ok(content.document.getElementById('f1').contentDocument.getElementById('p1').innerHTML == "hello","Mixed script didn't load in Test 6");
|
||||
MixedTestsCompleted();
|
||||
}
|
||||
|
||||
function waitForCondition(condition, nextTest, errorMsg) {
|
||||
var tries = 0;
|
||||
var interval = setInterval(function() {
|
||||
if (tries >= 30) {
|
||||
ok(false, errorMsg);
|
||||
moveOn();
|
||||
}
|
||||
if (condition()) {
|
||||
moveOn();
|
||||
}
|
||||
tries++;
|
||||
}, 100);
|
||||
var moveOn = function() { clearInterval(interval); nextTest(); };
|
||||
}
|
||||
|
@ -41,26 +41,6 @@ function cleanUpAfterTests() {
|
||||
window.focus();
|
||||
finish();
|
||||
}
|
||||
/*
|
||||
* Whenever we disable the Mixed Content Blocker of the page
|
||||
* we have to make sure that our condition is properly loaded.
|
||||
*/
|
||||
function waitForCondition(condition, nextTest, errorMsg) {
|
||||
var tries = 0;
|
||||
var interval = setInterval(function() {
|
||||
if (tries >= 30) {
|
||||
ok(false, errorMsg);
|
||||
moveOn();
|
||||
}
|
||||
if (condition()) {
|
||||
moveOn();
|
||||
}
|
||||
tries++;
|
||||
}, 100);
|
||||
var moveOn = function() {
|
||||
clearInterval(interval); nextTest();
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------ Test 1 ------------------------------
|
||||
|
||||
|
@ -89,7 +89,14 @@ function waitForCondition(condition, nextTest, errorMsg) {
|
||||
ok(false, errorMsg);
|
||||
moveOn();
|
||||
}
|
||||
if (condition()) {
|
||||
var conditionPassed;
|
||||
try {
|
||||
conditionPassed = condition();
|
||||
} catch (e) {
|
||||
ok(false, e + "\n" + e.stack);
|
||||
conditionPassed = false;
|
||||
}
|
||||
if (conditionPassed) {
|
||||
moveOn();
|
||||
}
|
||||
tries++;
|
||||
|
@ -18,7 +18,14 @@ function waitForCondition(condition, nextTest, errorMsg) {
|
||||
ok(false, errorMsg);
|
||||
moveOn();
|
||||
}
|
||||
if (condition()) {
|
||||
var conditionPassed;
|
||||
try {
|
||||
conditionPassed = condition();
|
||||
} catch (e) {
|
||||
ok(false, e + "\n" + e.stack);
|
||||
conditionPassed = false;
|
||||
}
|
||||
if (conditionPassed) {
|
||||
moveOn();
|
||||
}
|
||||
tries++;
|
||||
|
@ -102,6 +102,7 @@
|
||||
<button class="action-primary" onclick="UI.installSimulator()" title="&connection.installOneSimulatorTooltip;">&connection.installOneSimulator;</button>
|
||||
</div>
|
||||
<div class="found-simulator">
|
||||
<span>&connection.startRegisteredSimulator;</span>
|
||||
<span template-loop='{"arrayPath":"simulators.versions","childSelector":"#simulator-item-template"}'></span>
|
||||
<button class="action-primary" onclick="UI.installSimulator()" title="&connection.installAnotherSimulatorTooltip;">&connection.installAnotherSimulator;</button>
|
||||
</div>
|
||||
@ -124,7 +125,7 @@
|
||||
|
||||
<template id="simulator-item-template">
|
||||
<span>
|
||||
<button class="simulator-item" onclick="UI.startSimulator(this.dataset.version)" template='{"type":"attribute","path":"version","name":"data-version"}' title="&connection.startSimulatorTooltip;">
|
||||
<button class="simulator-item action-primary" onclick="UI.startSimulator(this.dataset.version)" template='{"type":"attribute","path":"version","name":"data-version"}' title="&connection.startSimulatorTooltip;">
|
||||
<span template='{"type":"textContent", "path":"version"}'></span>
|
||||
</button>
|
||||
</span>
|
||||
|
@ -21,6 +21,7 @@ const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
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");
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/Templater.jsm");
|
||||
@ -374,7 +375,7 @@ MarkupView.prototype = {
|
||||
this._elt.appendChild(container.elt);
|
||||
this._rootNode = aNode;
|
||||
} else {
|
||||
var container = new MarkupContainer(this, aNode);
|
||||
var container = new MarkupContainer(this, aNode, this._inspector);
|
||||
if (aFlashNode) {
|
||||
container.flashMutation();
|
||||
}
|
||||
@ -1046,12 +1047,15 @@ MarkupView.prototype = {
|
||||
* The markup view that owns this container.
|
||||
* @param DOMNode aNode
|
||||
* The node to display.
|
||||
* @param Inspector aInspector
|
||||
* The inspector tool container the markup-view
|
||||
*/
|
||||
function MarkupContainer(aMarkupView, aNode) {
|
||||
function MarkupContainer(aMarkupView, aNode, aInspector) {
|
||||
this.markup = aMarkupView;
|
||||
this.doc = this.markup.doc;
|
||||
this.undo = this.markup.undo;
|
||||
this.node = aNode;
|
||||
this._inspector = aInspector;
|
||||
|
||||
if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE) {
|
||||
this.editor = new TextEditor(this, aNode, "text");
|
||||
@ -1094,6 +1098,9 @@ function MarkupContainer(aMarkupView, aNode) {
|
||||
|
||||
this._onMouseDown = this._onMouseDown.bind(this);
|
||||
this.elt.addEventListener("mousedown", this._onMouseDown, false);
|
||||
|
||||
this.tooltip = null;
|
||||
this._attachTooltipIfNeeded();
|
||||
}
|
||||
|
||||
MarkupContainer.prototype = {
|
||||
@ -1101,6 +1108,39 @@ MarkupContainer.prototype = {
|
||||
return "[MarkupContainer for " + this.node + "]";
|
||||
},
|
||||
|
||||
_attachTooltipIfNeeded: function() {
|
||||
if (this.node.tagName) {
|
||||
let tagName = this.node.tagName.toLowerCase();
|
||||
let isImage = tagName === "img" &&
|
||||
this.editor.getAttributeElement("src");
|
||||
let isCanvas = tagName && tagName === "canvas";
|
||||
|
||||
// Get the image data for later so that when the user actually hovers over
|
||||
// the element, the tooltip does contain the image
|
||||
if (isImage || isCanvas) {
|
||||
this.tooltip = new Tooltip(this._inspector.panelDoc);
|
||||
|
||||
this.node.getImageData().then(data => {
|
||||
if (data) {
|
||||
data.string().then(str => {
|
||||
this.tooltip.setImageContent(str);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If it's an image, show the tooltip on the src attribute
|
||||
if (isImage) {
|
||||
this.tooltip.startTogglingOnHover(this.editor.getAttributeElement("src"));
|
||||
}
|
||||
|
||||
// If it's a canvas, show it on the tag
|
||||
if (isCanvas) {
|
||||
this.tooltip.startTogglingOnHover(this.editor.tag);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* True if the current node has children. The MarkupView
|
||||
* will set this attribute for the MarkupContainer.
|
||||
@ -1335,6 +1375,12 @@ MarkupContainer.prototype = {
|
||||
|
||||
// Destroy my editor
|
||||
this.editor.destroy();
|
||||
|
||||
// Destroy the tooltip if any
|
||||
if (this.tooltip) {
|
||||
this.tooltip.destroy();
|
||||
this.tooltip = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1348,7 +1394,7 @@ function RootContainer(aMarkupView, aNode) {
|
||||
this.elt.container = this;
|
||||
this.children = this.elt;
|
||||
this.node = aNode;
|
||||
this.toString = function() { return "[root container]"}
|
||||
this.toString = () => "[root container]";
|
||||
}
|
||||
|
||||
RootContainer.prototype = {
|
||||
@ -1578,6 +1624,16 @@ ElementEditor.prototype = {
|
||||
return this.node.startModifyingAttributes();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the element used for one of the attributes of this element
|
||||
* @param string attrName The name of the attribute to get the element for
|
||||
* @return DOMElement
|
||||
*/
|
||||
getAttributeElement: function(attrName) {
|
||||
return this.attrList.querySelector(
|
||||
".attreditor[data-attr=" + attrName + "] .attr-value");
|
||||
},
|
||||
|
||||
_createAttribute: function(aAttr, aBefore = null) {
|
||||
// Create the template editor, which will save some variables here.
|
||||
let data = {
|
||||
|
@ -15,3 +15,5 @@ skip-if = true
|
||||
[browser_inspector_markup_navigation.js]
|
||||
[browser_inspector_markup_subset.html]
|
||||
[browser_inspector_markup_subset.js]
|
||||
[browser_inspector_markup_765105_tooltip.js]
|
||||
[browser_inspector_markup_765105_tooltip.png]
|
||||
|
@ -0,0 +1,137 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let {PanelFactory} = devtools.require("devtools/shared/widgets/Tooltip");
|
||||
|
||||
let contentDoc;
|
||||
let inspector;
|
||||
let markup;
|
||||
|
||||
const PAGE_CONTENT = [
|
||||
'<img class="local" src="chrome://branding/content/about-logo.png" />',
|
||||
'<img class="data" src="data:image/png;base64,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" />',
|
||||
'<img class="remote" src="http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.png" />',
|
||||
'<canvas class="canvas" width="600" height="600"></canvas>'
|
||||
].join("\n");
|
||||
|
||||
const TEST_NODES = [
|
||||
"img.local",
|
||||
"img.data",
|
||||
"img.remote",
|
||||
".canvas"
|
||||
];
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||
contentDoc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,markup view tooltip test";
|
||||
}
|
||||
|
||||
function createDocument() {
|
||||
contentDoc.body.innerHTML = PAGE_CONTENT;
|
||||
|
||||
var target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
|
||||
inspector = toolbox.getCurrentPanel();
|
||||
markup = inspector.markup;
|
||||
startTests();
|
||||
});
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
// Draw something in the canvas :)
|
||||
let doc = content.document;
|
||||
let context = doc.querySelector(".canvas").getContext("2d");
|
||||
|
||||
context.beginPath();
|
||||
context.moveTo(300, 0);
|
||||
context.lineTo(600, 600);
|
||||
context.lineTo(0, 600);
|
||||
context.closePath();
|
||||
context.fillStyle = "#ffc821";
|
||||
context.fill();
|
||||
|
||||
// Actually start testing
|
||||
inspector.selection.setNode(contentDoc.querySelector("img"));
|
||||
inspector.once("inspector-updated", () => {
|
||||
testImageTooltip(0);
|
||||
});
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
contentDoc = inspector = markup = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function testImageTooltip(index) {
|
||||
if (index === TEST_NODES.length) {
|
||||
return endTests();
|
||||
}
|
||||
|
||||
let node = contentDoc.querySelector(TEST_NODES[index]);
|
||||
ok(node, "We have the [" + TEST_NODES[index] + "] image node to test for tooltip");
|
||||
let isImg = node.tagName.toLowerCase() === "img";
|
||||
|
||||
let container = getContainerForRawNode(markup, node);
|
||||
|
||||
let target = container.editor.tag;
|
||||
if (isImg) {
|
||||
target = container.editor.getAttributeElement("src");
|
||||
}
|
||||
|
||||
assertTooltipShownOn(container.tooltip, target, () => {
|
||||
let images = container.tooltip.panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip for [" + TEST_NODES[index] + "] contains an image");
|
||||
if (isImg) {
|
||||
compareImageData(node, images[0].src);
|
||||
}
|
||||
|
||||
container.tooltip.hide();
|
||||
|
||||
testImageTooltip(index + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function compareImageData(img, imgData) {
|
||||
let canvas = content.document.createElement("canvas");
|
||||
canvas.width = img.naturalWidth;
|
||||
canvas.height = img.naturalHeight;
|
||||
let ctx = canvas.getContext("2d");
|
||||
let data = "";
|
||||
try {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
data = canvas.toDataURL("image/png");
|
||||
} catch (e) {}
|
||||
|
||||
is(data, imgData, "Tooltip image has the right content");
|
||||
}
|
||||
|
||||
function assertTooltipShownOn(tooltip, element, cb) {
|
||||
// If there is indeed a show-on-hover on element, the xul panel will be shown
|
||||
tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
|
||||
// Poll until the image gets loaded in the tooltip. This is required because
|
||||
// markup containers only load images in their associated tooltips when
|
||||
// the image data comes back from the server. However, this test is executed
|
||||
// synchronously as soon as "inspector-updated" is fired, which is before
|
||||
// the data for images is known.
|
||||
let hasImage = () => tooltip.panel.getElementsByTagName("image").length;
|
||||
let poll = setInterval(() => {
|
||||
if (hasImage()) {
|
||||
clearInterval(poll);
|
||||
cb();
|
||||
}
|
||||
}, 200);
|
||||
}, true);
|
||||
tooltip._showOnHover(element);
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -9,3 +9,4 @@ libs::
|
||||
$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
|
||||
$(NSINSTALL) $(srcdir)/widgets/*.jsm $(FINAL_TARGET)/modules/devtools
|
||||
$(NSINSTALL) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools/shared
|
||||
$(NSINSTALL) $(srcdir)/widgets/*.js $(FINAL_TARGET)/modules/devtools/shared/widgets
|
||||
|
420
browser/devtools/shared/widgets/Tooltip.js
Normal file
420
browser/devtools/shared/widgets/Tooltip.js
Normal file
@ -0,0 +1,420 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cc, Cu, Ci} = require("chrome");
|
||||
const promise = require("sdk/core/promise");
|
||||
const IOService = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
|
||||
const GRADIENT_RE = /\b(repeating-)?(linear|radial)-gradient\(((rgb|hsl)a?\(.+?\)|[^\)])+\)/gi;
|
||||
const BORDERCOLOR_RE = /^border-[-a-z]*color$/ig;
|
||||
const BORDER_RE = /^border(-(top|bottom|left|right))?$/ig;
|
||||
const BACKGROUND_IMAGE_RE = /url\([\'\"]?(.*?)[\'\"]?\)/;
|
||||
|
||||
/**
|
||||
* Tooltip widget.
|
||||
*
|
||||
* This widget is intended at any tool that may need to show rich content in the
|
||||
* form of floating panels.
|
||||
* A common use case is image previewing in the CSS rule view, but more complex
|
||||
* use cases may include color pickers, object inspection, etc...
|
||||
*
|
||||
* Tooltips are based on XUL (namely XUL arrow-type <panel>s), and therefore
|
||||
* need a XUL Document to live in.
|
||||
* This is pretty much the only requirement they have on their environment.
|
||||
*
|
||||
* The way to use a tooltip is simply by instantiating a tooltip yourself and
|
||||
* attaching some content in it, or using one of the ready-made content types.
|
||||
*
|
||||
* A convenient `startTogglingOnHover` method may avoid having to register event
|
||||
* handlers yourself if the tooltip has to be shown when hovering over a
|
||||
* specific element or group of elements (which is usually the most common case)
|
||||
*/
|
||||
|
||||
/**
|
||||
* The low level structure of a tooltip is a XUL element (a <panel>, although
|
||||
* <tooltip> is supported too, it won't have the nice arrow shape).
|
||||
*/
|
||||
let PanelFactory = {
|
||||
get: function(doc, xulTag="panel") {
|
||||
// Create the tooltip
|
||||
let panel = doc.createElement(xulTag);
|
||||
panel.setAttribute("hidden", true);
|
||||
|
||||
if (xulTag === "panel") {
|
||||
// Prevent the click used to close the panel from being consumed
|
||||
panel.setAttribute("consumeoutsideclicks", false);
|
||||
panel.setAttribute("type", "arrow");
|
||||
panel.setAttribute("level", "top");
|
||||
}
|
||||
|
||||
panel.setAttribute("class", "devtools-tooltip devtools-tooltip-" + xulTag);
|
||||
doc.querySelector("window").appendChild(panel);
|
||||
|
||||
return panel;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tooltip class.
|
||||
*
|
||||
* Basic usage:
|
||||
* let t = new Tooltip(xulDoc);
|
||||
* t.content = someXulContent;
|
||||
* t.show();
|
||||
* t.hide();
|
||||
* t.destroy();
|
||||
*
|
||||
* Better usage:
|
||||
* let t = new Tooltip(xulDoc);
|
||||
* t.startTogglingOnHover(container, target => {
|
||||
* if (<condition based on target>) {
|
||||
* t.setImageContent("http://image.png");
|
||||
* return true;
|
||||
* }
|
||||
* });
|
||||
* t.destroy();
|
||||
*
|
||||
* @param XULDocument doc
|
||||
* The XUL document hosting this tooltip
|
||||
*/
|
||||
function Tooltip(doc) {
|
||||
this.doc = doc;
|
||||
this.panel = PanelFactory.get(doc);
|
||||
|
||||
// Used for namedTimeouts in the mouseover handling
|
||||
this.uid = "tooltip-" + Date.now();
|
||||
}
|
||||
|
||||
module.exports.Tooltip = Tooltip;
|
||||
|
||||
Tooltip.prototype = {
|
||||
/**
|
||||
* Show the tooltip. It might be wise to append some content first if you
|
||||
* don't want the tooltip to be empty. You may access the content of the
|
||||
* tooltip by setting a XUL node to t.tooltip.content.
|
||||
* @param {node} anchor
|
||||
* Which node should the tooltip be shown on
|
||||
* @param {string} position
|
||||
* https://developer.mozilla.org/en-US/docs/XUL/PopupGuide/Positioning
|
||||
* Defaults to before_start
|
||||
*/
|
||||
show: function(anchor, position="before_start") {
|
||||
this.panel.hidden = false;
|
||||
this.panel.openPopup(anchor, position);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the tooltip
|
||||
*/
|
||||
hide: function() {
|
||||
this.panel.hidden = true;
|
||||
this.panel.hidePopup();
|
||||
},
|
||||
|
||||
/**
|
||||
* Empty the tooltip's content
|
||||
*/
|
||||
empty: function() {
|
||||
while (this.panel.hasChildNodes()) {
|
||||
this.panel.removeChild(this.panel.firstChild);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get rid of references and event listeners
|
||||
*/
|
||||
destroy: function () {
|
||||
this.hide();
|
||||
this.content = null;
|
||||
|
||||
this.doc = null;
|
||||
|
||||
this.panel.parentNode.removeChild(this.panel);
|
||||
this.panel = null;
|
||||
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show/hide the tooltip when the mouse hovers over particular nodes.
|
||||
*
|
||||
* 2 Ways to make this work:
|
||||
* - Provide a single node to attach the tooltip to, as the baseNode, and
|
||||
* omit the second targetNodeCb argument
|
||||
* - Provide a baseNode that is the container of possibly numerous children
|
||||
* elements that may receive a tooltip. In this case, provide the second
|
||||
* targetNodeCb argument to decide wether or not a child should receive
|
||||
* a tooltip.
|
||||
*
|
||||
* This works by tracking mouse movements on a base container node (baseNode)
|
||||
* and showing the tooltip when the mouse stops moving. The targetNodeCb
|
||||
* callback is used to know whether or not the particular element being
|
||||
* hovered over should indeed receive the tooltip. If you don't provide it
|
||||
* it's equivalent to a function that always returns true.
|
||||
*
|
||||
* Note that if you call this function a second time, it will itself call
|
||||
* stopTogglingOnHover before adding mouse tracking listeners again.
|
||||
*
|
||||
* @param {node} baseNode
|
||||
* The container for all target nodes
|
||||
* @param {Function} targetNodeCb
|
||||
* A function that accepts a node argument and returns true or false
|
||||
* to signify if the tooltip should be shown on that node or not.
|
||||
* Additionally, the function receives a second argument which is the
|
||||
* tooltip instance itself, to be used to add/modify the content of the
|
||||
* tooltip if needed. If omitted, the tooltip will be shown everytime.
|
||||
* @param {Number} showDelay
|
||||
* An optional delay that will be observed before showing the tooltip.
|
||||
* Defaults to 750ms
|
||||
*/
|
||||
startTogglingOnHover: function(baseNode, targetNodeCb, showDelay = 750) {
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
|
||||
this._basedNode = baseNode;
|
||||
this._showDelay = showDelay;
|
||||
this._targetNodeCb = targetNodeCb || (() => true);
|
||||
|
||||
this._onBaseNodeMouseMove = this._onBaseNodeMouseMove.bind(this);
|
||||
this._onBaseNodeMouseLeave = this._onBaseNodeMouseLeave.bind(this);
|
||||
|
||||
baseNode.addEventListener("mousemove", this._onBaseNodeMouseMove, false);
|
||||
baseNode.addEventListener("mouseleave", this._onBaseNodeMouseLeave, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* If the startTogglingOnHover function has been used previously, and you want
|
||||
* to get rid of this behavior, then call this function to remove the mouse
|
||||
* movement tracking
|
||||
*/
|
||||
stopTogglingOnHover: function() {
|
||||
clearNamedTimeout(this.uid);
|
||||
|
||||
this._basedNode.removeEventListener("mousemove",
|
||||
this._onBaseNodeMouseMove, false);
|
||||
this._basedNode.removeEventListener("mouseleave",
|
||||
this._onBaseNodeMouseLeave, false);
|
||||
|
||||
this._basedNode = null;
|
||||
this._targetNodeCb = null;
|
||||
this._lastHovered = null;
|
||||
},
|
||||
|
||||
_onBaseNodeMouseMove: function(event) {
|
||||
if (event.target !== this._lastHovered) {
|
||||
this.hide();
|
||||
this._lastHovered = null;
|
||||
setNamedTimeout(this.uid, this._showDelay, () => {
|
||||
this._showOnHover(event.target);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_showOnHover: function(target) {
|
||||
if (this._targetNodeCb && this._targetNodeCb(target, this)) {
|
||||
this.show(target);
|
||||
this._lastHovered = target;
|
||||
}
|
||||
},
|
||||
|
||||
_onBaseNodeMouseLeave: function() {
|
||||
clearNamedTimeout(this.uid);
|
||||
this._lastHovered = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the content of this tooltip. Will first empty the tooltip and then
|
||||
* append the new content element.
|
||||
* Consider using one of the set<type>Content() functions instead.
|
||||
* @param {node} content
|
||||
* A node that can be appended in the tooltip XUL element
|
||||
*/
|
||||
set content(content) {
|
||||
this.empty();
|
||||
if (content) {
|
||||
this.panel.appendChild(content);
|
||||
}
|
||||
},
|
||||
|
||||
get content() {
|
||||
return this.panel.firstChild;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill the tooltip with an image, displayed over a tiled background useful
|
||||
* for transparent images.
|
||||
* Also adds the image dimension as a label at the bottom.
|
||||
*/
|
||||
setImageContent: function(imageUrl, maxDim=400) {
|
||||
// Main container
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.setAttribute("align", "center")
|
||||
|
||||
// Transparency tiles (image will go in there)
|
||||
let tiles = createTransparencyTiles(this.doc, vbox);
|
||||
|
||||
// Temporary label during image load
|
||||
let label = this.doc.createElement("label");
|
||||
label.classList.add("devtools-tooltip-caption");
|
||||
label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage");
|
||||
vbox.appendChild(label);
|
||||
|
||||
// Display the image
|
||||
let image = this.doc.createElement("image");
|
||||
image.setAttribute("src", imageUrl);
|
||||
if (maxDim) {
|
||||
image.style.maxWidth = maxDim + "px";
|
||||
image.style.maxHeight = maxDim + "px";
|
||||
}
|
||||
tiles.appendChild(image);
|
||||
|
||||
this.content = vbox;
|
||||
|
||||
// Load the image to get dimensions and display it when done
|
||||
let imgObj = new this.doc.defaultView.Image();
|
||||
imgObj.src = imageUrl;
|
||||
imgObj.onload = () => {
|
||||
imgObj.onload = null;
|
||||
|
||||
// Display dimensions
|
||||
label.textContent = imgObj.naturalWidth + " x " + imgObj.naturalHeight;
|
||||
if (imgObj.naturalWidth > maxDim ||
|
||||
imgObj.naturalHeight > maxDim) {
|
||||
label.textContent += " *";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Exactly the same as the `image` function but takes a css background image
|
||||
* value instead : url(....)
|
||||
*/
|
||||
setCssBackgroundImageContent: function(cssBackground, sheetHref, maxDim=400) {
|
||||
let uri = getBackgroundImageUri(cssBackground, sheetHref);
|
||||
if (uri) {
|
||||
this.setImageContent(uri, maxDim);
|
||||
}
|
||||
},
|
||||
|
||||
setCssGradientContent: function(cssGradient) {
|
||||
let tiles = createTransparencyTiles(this.doc);
|
||||
|
||||
let gradientBox = this.doc.createElement("box");
|
||||
gradientBox.width = "100";
|
||||
gradientBox.height = "100";
|
||||
gradientBox.style.background = this.cssGradient;
|
||||
gradientBox.style.borderRadius = "2px";
|
||||
gradientBox.style.boxShadow = "inset 0 0 4px #333";
|
||||
|
||||
tiles.appendChild(gradientBox)
|
||||
|
||||
this.content = tiles;
|
||||
},
|
||||
|
||||
_setSimpleCssPropertiesContent: function(properties, width, height) {
|
||||
let tiles = createTransparencyTiles(this.doc);
|
||||
|
||||
let box = this.doc.createElement("box");
|
||||
box.width = width + "";
|
||||
box.height = height + "";
|
||||
properties.forEach(({name, value}) => {
|
||||
box.style[name] = value;
|
||||
});
|
||||
tiles.appendChild(box);
|
||||
|
||||
this.content = tiles;
|
||||
},
|
||||
|
||||
setCssColorContent: function(cssColor) {
|
||||
this._setSimpleCssPropertiesContent([
|
||||
{name: "background", value: cssColor},
|
||||
{name: "borderRadius", value: "2px"},
|
||||
{name: "boxShadow", value: "inset 0 0 4px #333"},
|
||||
], 50, 50);
|
||||
},
|
||||
|
||||
setCssBoxShadowContent: function(cssBoxShadow) {
|
||||
this._setSimpleCssPropertiesContent([
|
||||
{name: "background", value: "white"},
|
||||
{name: "boxShadow", value: cssBoxShadow}
|
||||
], 80, 80);
|
||||
},
|
||||
|
||||
setCssBorderContent: function(cssBorder) {
|
||||
this._setSimpleCssPropertiesContent([
|
||||
{name: "background", value: "white"},
|
||||
{name: "border", value: cssBorder}
|
||||
], 80, 80);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal utility function that creates a tiled background useful for
|
||||
* displaying semi-transparent images
|
||||
*/
|
||||
function createTransparencyTiles(doc, parentEl) {
|
||||
let tiles = doc.createElement("box");
|
||||
tiles.classList.add("devtools-tooltip-tiles");
|
||||
if (parentEl) {
|
||||
parentEl.appendChild(tiles);
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal util, checks whether a css declaration is a gradient
|
||||
*/
|
||||
function isGradientRule(property, value) {
|
||||
return (property === "background" || property === "background-image") &&
|
||||
value.match(GRADIENT_RE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal util, checks whether a css declaration is a color
|
||||
*/
|
||||
function isColorOnly(property, value) {
|
||||
return property === "background-color" ||
|
||||
property === "color" ||
|
||||
property.match(BORDERCOLOR_RE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal util, returns the background image uri if any
|
||||
*/
|
||||
function getBackgroundImageUri(value, sheetHref) {
|
||||
let uriMatch = BACKGROUND_IMAGE_RE.exec(value);
|
||||
let uri = null;
|
||||
|
||||
if (uriMatch && uriMatch[1]) {
|
||||
uri = uriMatch[1];
|
||||
if (sheetHref) {
|
||||
let sheetUri = IOService.newURI(sheetHref, null, null);
|
||||
uri = sheetUri.resolve(uri);
|
||||
}
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* L10N utility class
|
||||
*/
|
||||
function L10N() {}
|
||||
L10N.prototype = {};
|
||||
|
||||
let l10n = new L10N();
|
||||
|
||||
loader.lazyGetter(L10N.prototype, "strings", () => {
|
||||
return Services.strings.createBundle(
|
||||
"chrome://browser/locale/devtools/inspector.properties");
|
||||
});
|
@ -11,8 +11,8 @@ let {CssLogic} = require("devtools/styleinspector/css-logic");
|
||||
let {ELEMENT_STYLE} = require("devtools/server/actors/styles");
|
||||
let promise = require("sdk/core/promise");
|
||||
let {EventEmitter} = require("devtools/shared/event-emitter");
|
||||
|
||||
const {OutputParser} = require("devtools/output-parser");
|
||||
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PluralForm.jsm");
|
||||
@ -169,6 +169,11 @@ function CssHtmlTree(aStyleInspector, aPageStyle)
|
||||
// The element that we're inspecting, and the document that it comes from.
|
||||
this.viewedElement = null;
|
||||
|
||||
// Properties preview tooltip
|
||||
this.tooltip = new Tooltip(this.styleInspector.inspector.panelDoc);
|
||||
this.tooltip.startTogglingOnHover(this.propertyContainer,
|
||||
this._buildTooltipContent.bind(this));
|
||||
|
||||
this._buildContextMenu();
|
||||
this.createStyleViews();
|
||||
}
|
||||
@ -490,6 +495,29 @@ CssHtmlTree.prototype = {
|
||||
win.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify that target is indeed a css value we want a tooltip on, and if yes
|
||||
* prepare some content for the tooltip
|
||||
*/
|
||||
_buildTooltipContent: function(target)
|
||||
{
|
||||
// If the hovered element is not a property view and is not a background
|
||||
// image, then don't show a tooltip
|
||||
let isPropertyValue = target.classList.contains("property-value");
|
||||
if (!isPropertyValue) {
|
||||
return false;
|
||||
}
|
||||
let propName = target.parentNode.querySelector(".property-name");
|
||||
let isBackgroundImage = propName.textContent === "background-image";
|
||||
if (!isBackgroundImage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill some content
|
||||
this.tooltip.setCssBackgroundImageContent(target.textContent);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a context menu.
|
||||
*/
|
||||
@ -648,6 +676,9 @@ CssHtmlTree.prototype = {
|
||||
this._contextmenu = null;
|
||||
}
|
||||
|
||||
this.tooltip.stopTogglingOnHover(this.propertyContainer);
|
||||
this.tooltip.destroy();
|
||||
|
||||
// Remove bound listeners
|
||||
this.styleDocument.removeEventListener("contextmenu", this._onContextMenu);
|
||||
this.styleDocument.removeEventListener("copy", this._onCopy);
|
||||
@ -831,9 +862,8 @@ PropertyView.prototype = {
|
||||
{
|
||||
let doc = this.tree.styleDocument;
|
||||
|
||||
this.onMatchedToggle = this.onMatchedToggle.bind(this);
|
||||
|
||||
// Build the container element
|
||||
this.onMatchedToggle = this.onMatchedToggle.bind(this);
|
||||
this.element = doc.createElementNS(HTML_NS, "div");
|
||||
this.element.setAttribute("class", this.propertyHeaderClassName);
|
||||
this.element.addEventListener("dblclick", this.onMatchedToggle, false);
|
||||
@ -858,6 +888,8 @@ PropertyView.prototype = {
|
||||
this.matchedExpander.addEventListener("click", this.onMatchedToggle, false);
|
||||
this.element.appendChild(this.matchedExpander);
|
||||
|
||||
this.focusElement = () => this.element.focus();
|
||||
|
||||
// Build the style name element
|
||||
this.nameNode = doc.createElementNS(HTML_NS, "div");
|
||||
this.nameNode.setAttribute("class", "property-name theme-fg-color5");
|
||||
@ -1034,7 +1066,7 @@ PropertyView.prototype = {
|
||||
};
|
||||
|
||||
/**
|
||||
* A container to view us easy access to display data from a CssRule
|
||||
* A container to give us easy access to display data from a CssRule
|
||||
* @param CssHtmlTree aTree, the owning CssHtmlTree
|
||||
* @param aSelectorInfo
|
||||
*/
|
||||
|
@ -13,6 +13,7 @@ let {CssLogic} = require("devtools/styleinspector/css-logic");
|
||||
let {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor");
|
||||
let {ELEMENT_STYLE, PSEUDO_ELEMENTS} = require("devtools/server/actors/styles");
|
||||
let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
let {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
|
||||
const {OutputParser} = require("devtools/output-parser");
|
||||
|
||||
@ -1029,6 +1030,7 @@ TextProperty.prototype = {
|
||||
* apply to a given element. After construction, the 'element'
|
||||
* property will be available with the user interface.
|
||||
*
|
||||
* @param {Inspector} aInspector
|
||||
* @param {Document} aDoc
|
||||
* The document that will contain the rule view.
|
||||
* @param {object} aStore
|
||||
@ -1039,8 +1041,9 @@ TextProperty.prototype = {
|
||||
* The PageStyleFront for communicating with the remote server.
|
||||
* @constructor
|
||||
*/
|
||||
function CssRuleView(aDoc, aStore, aPageStyle)
|
||||
function CssRuleView(aInspector, aDoc, aStore, aPageStyle)
|
||||
{
|
||||
this.inspector = aInspector;
|
||||
this.doc = aDoc;
|
||||
this.store = aStore || {};
|
||||
this.pageStyle = aPageStyle;
|
||||
@ -1067,6 +1070,9 @@ function CssRuleView(aDoc, aStore, aPageStyle)
|
||||
};
|
||||
this.popup = new AutocompletePopup(aDoc.defaultView.parent.document, options);
|
||||
|
||||
this.tooltip = new Tooltip(this.inspector.panelDoc);
|
||||
this.tooltip.startTogglingOnHover(this.element, this._buildTooltipContent.bind(this));
|
||||
|
||||
this._buildContextMenu();
|
||||
this._showEmpty();
|
||||
}
|
||||
@ -1107,6 +1113,37 @@ CssRuleView.prototype = {
|
||||
popupset.appendChild(this._contextmenu);
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify that target is indeed a css value we want a tooltip on, and if yes
|
||||
* prepare some content for the tooltip
|
||||
*/
|
||||
_buildTooltipContent: function(target) {
|
||||
let isValueWithImage = target.classList.contains("ruleview-propertyvalue") &&
|
||||
target.querySelector(".theme-link");
|
||||
|
||||
let isImageHref = target.classList.contains("theme-link") &&
|
||||
target.parentNode.classList.contains("ruleview-propertyvalue");
|
||||
if (isImageHref) {
|
||||
target = target.parentNode;
|
||||
}
|
||||
|
||||
let isEditing = this.isEditing;
|
||||
|
||||
// If the inplace-editor is visible or if this is not a background image
|
||||
// don't show the tooltip
|
||||
if (this.isEditing || (!isImageHref && !isValueWithImage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the TextProperty for the hovered element
|
||||
let property = target.textProperty;
|
||||
let href = property.rule.domRule.href;
|
||||
|
||||
// Fill some content
|
||||
this.tooltip.setCssBackgroundImageContent(property.value, href);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the context menu. This means enabling or disabling menuitems as
|
||||
* appropriate.
|
||||
@ -1240,6 +1277,9 @@ CssRuleView.prototype = {
|
||||
// We manage the popupNode ourselves so we also need to destroy it.
|
||||
this.doc.popupNode = null;
|
||||
|
||||
this.tooltip.stopTogglingOnHover(this.element);
|
||||
this.tooltip.destroy();
|
||||
|
||||
if (this.element.parentNode) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
@ -1307,7 +1347,6 @@ CssRuleView.prototype = {
|
||||
}
|
||||
this._createEditors();
|
||||
|
||||
|
||||
// Notify anyone that cares that we refreshed.
|
||||
var evt = this.doc.createEvent("Events");
|
||||
evt.initEvent("CssRuleViewRefreshed", true, false);
|
||||
@ -1853,6 +1892,10 @@ TextPropertyEditor.prototype = {
|
||||
tabindex: "0",
|
||||
});
|
||||
|
||||
// Storing the TextProperty on the valuespan for easy access
|
||||
// (for instance by the tooltip)
|
||||
this.valueSpan.textProperty = this.prop;
|
||||
|
||||
// Save the initial value as the last committed value,
|
||||
// for restoring after pressing escape.
|
||||
this.committed = { name: this.prop.name,
|
||||
@ -1971,7 +2014,6 @@ TextPropertyEditor.prototype = {
|
||||
});
|
||||
|
||||
a.addEventListener("click", (aEvent) => {
|
||||
|
||||
// Clicks within the link shouldn't trigger editing.
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
@ -2133,6 +2175,7 @@ TextPropertyEditor.prototype = {
|
||||
{
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
this.ruleEditor.rule.editClosestTextProperty(this.prop);
|
||||
this.valueSpan.textProperty = null;
|
||||
this.prop.remove();
|
||||
},
|
||||
|
||||
|
@ -25,7 +25,7 @@ function RuleViewTool(aInspector, aWindow, aIFrame)
|
||||
this.doc = aWindow.document;
|
||||
this.outerIFrame = aIFrame;
|
||||
|
||||
this.view = new RuleView.CssRuleView(this.doc);
|
||||
this.view = new RuleView.CssRuleView(aInspector, this.doc);
|
||||
this.doc.documentElement.appendChild(this.view.element);
|
||||
|
||||
this._changeHandler = () => {
|
||||
|
@ -38,6 +38,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_ruleview_pseudoelement.js \
|
||||
browser_computedview_bug835808_keyboard_nav.js \
|
||||
browser_bug913014_matched_expand.js \
|
||||
browser_bug765105_background_image_tooltip.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -0,0 +1,162 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let contentDoc;
|
||||
let inspector;
|
||||
let ruleView;
|
||||
let computedView;
|
||||
|
||||
const PAGE_CONTENT = [
|
||||
'<style type="text/css">',
|
||||
' body {',
|
||||
' padding: 1em;',
|
||||
' background-image: url(data:image/png;base64,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);',
|
||||
' background-repeat: repeat-y;',
|
||||
' background-position: right top;',
|
||||
' }',
|
||||
' .test-element {',
|
||||
' font-family: verdana;',
|
||||
' color: #333;',
|
||||
' background: url(chrome://global/skin/icons/warning-64.png) no-repeat left center;',
|
||||
' padding-left: 70px;',
|
||||
' }',
|
||||
'</style>',
|
||||
'<div class="test-element">test element</div>',
|
||||
'<div class="test-element-2">test element 2</div>'
|
||||
].join("\n");
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||
contentDoc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,rule view tooltip test";
|
||||
}
|
||||
|
||||
function createDocument() {
|
||||
contentDoc.body.innerHTML = PAGE_CONTENT;
|
||||
|
||||
openRuleView((aInspector, aRuleView) => {
|
||||
inspector = aInspector;
|
||||
ruleView = aRuleView;
|
||||
startTests();
|
||||
});
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
// let testElement = contentDoc.querySelector(".test-element");
|
||||
|
||||
inspector.selection.setNode(contentDoc.body);
|
||||
inspector.once("inspector-updated", testBodyRuleView);
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
contentDoc = inspector = ruleView = computedView = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function assertTooltipShownOn(tooltip, element, cb) {
|
||||
// If there is indeed a show-on-hover on element, the xul panel will be shown
|
||||
tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
cb();
|
||||
}, true);
|
||||
tooltip._showOnHover(element);
|
||||
}
|
||||
|
||||
function testBodyRuleView() {
|
||||
info("Testing tooltips in the rule view");
|
||||
|
||||
let panel = ruleView.tooltip.panel;
|
||||
|
||||
// Check that the rule view has a tooltip and that a XUL panel has been created
|
||||
ok(ruleView.tooltip, "Tooltip instance exists");
|
||||
ok(panel, "XUL panel exists");
|
||||
|
||||
// Get the background-image property inside the rule view
|
||||
let {nameSpan, valueSpan} = getRuleViewProperty("background-image");
|
||||
// And verify that the tooltip gets shown on this property
|
||||
assertTooltipShownOn(ruleView.tooltip, valueSpan, () => {
|
||||
let images = panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].src.indexOf("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHe") !== -1, "The image URL seems fine");
|
||||
|
||||
ruleView.tooltip.hide();
|
||||
|
||||
inspector.selection.setNode(contentDoc.querySelector(".test-element"));
|
||||
inspector.once("inspector-updated", testDivRuleView);
|
||||
});
|
||||
}
|
||||
|
||||
function testDivRuleView() {
|
||||
let panel = ruleView.tooltip.panel;
|
||||
|
||||
// Get the background property inside the rule view
|
||||
let {nameSpan, valueSpan} = getRuleViewProperty("background");
|
||||
let uriSpan = valueSpan.querySelector(".theme-link");
|
||||
|
||||
// And verify that the tooltip gets shown on this property
|
||||
assertTooltipShownOn(ruleView.tooltip, uriSpan, () => {
|
||||
let images = panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].src === "chrome://global/skin/icons/warning-64.png");
|
||||
|
||||
ruleView.tooltip.hide();
|
||||
|
||||
testComputedView();
|
||||
});
|
||||
}
|
||||
|
||||
function testComputedView() {
|
||||
info("Testing tooltips in the computed view");
|
||||
|
||||
inspector.sidebar.select("computedview");
|
||||
computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
|
||||
let doc = computedView.styleDocument;
|
||||
|
||||
let panel = computedView.tooltip.panel;
|
||||
let {nameSpan, valueSpan} = getComputedViewProperty("background-image");
|
||||
|
||||
assertTooltipShownOn(computedView.tooltip, valueSpan, () => {
|
||||
let images = panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].src === "chrome://global/skin/icons/warning-64.png");
|
||||
|
||||
computedView.tooltip.hide();
|
||||
|
||||
endTests();
|
||||
});
|
||||
}
|
||||
|
||||
function getRuleViewProperty(name) {
|
||||
let prop = null;
|
||||
[].forEach.call(ruleView.doc.querySelectorAll(".ruleview-property"), property => {
|
||||
let nameSpan = property.querySelector(".ruleview-propertyname");
|
||||
let valueSpan = property.querySelector(".ruleview-propertyvalue");
|
||||
|
||||
if (nameSpan.textContent === name) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
}
|
||||
});
|
||||
return prop;
|
||||
}
|
||||
|
||||
function getComputedViewProperty(name) {
|
||||
let prop = null;
|
||||
[].forEach.call(computedView.styleDocument.querySelectorAll(".property-view"), property => {
|
||||
let nameSpan = property.querySelector(".property-name");
|
||||
let valueSpan = property.querySelector(".property-value");
|
||||
|
||||
if (nameSpan.textContent === name) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
}
|
||||
});
|
||||
return prop;
|
||||
}
|
@ -57,6 +57,7 @@
|
||||
<!ENTITY connection.installOneSimulatorTooltip "Install a version of the Simulator by downloading the relevant add-on.">
|
||||
<!ENTITY connection.installAnotherSimulator "Add">
|
||||
<!ENTITY connection.installAnotherSimulatorTooltip "Install an additional version of the Simulator by downloading the relevant add-on.">
|
||||
<!ENTITY connection.startRegisteredSimulator "Start:">
|
||||
|
||||
<!ENTITY projects.localApps "Local Apps">
|
||||
<!ENTITY projects.addApp "Add">
|
||||
|
@ -31,7 +31,6 @@ debuggerPausedWarning.message=Debugger is paused. Some features like mouse selec
|
||||
# the node is selected.
|
||||
nodeMenu.tooltiptext=Node operations
|
||||
|
||||
|
||||
# LOCALIZATION NOTE (inspector.*)
|
||||
# Used for the menuitem in the tool menu
|
||||
inspector.label=Inspector
|
||||
@ -44,3 +43,6 @@ inspector.accesskey=I
|
||||
markupView.more.showing=Some nodes were hidden.
|
||||
markupView.more.showAll=Show All %S Nodes
|
||||
inspector.tooltip=DOM and Style Inspector
|
||||
|
||||
#LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded
|
||||
previewTooltip.image.brokenImage=Could not load the image
|
||||
|
@ -17,6 +17,13 @@
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
||||
/* Sources toolbar */
|
||||
|
||||
#sources-toolbar {
|
||||
border: none; /* Remove the devtools-toolbar's black bottom border. */
|
||||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#pretty-print {
|
||||
min-width: 0;
|
||||
font-weight: bold;
|
||||
@ -435,3 +442,8 @@
|
||||
#body[layout=vertical] .side-menu-widget-item-arrow {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
#body[layout=vertical] .side-menu-widget-group,
|
||||
#body[layout=vertical] .side-menu-widget-item {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
@ -279,6 +279,8 @@
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* SideMenuWidget container */
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(ltr) {
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
@ -287,6 +289,18 @@
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-group {
|
||||
/* To allow visibility of the dark margin shadow. */
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-item {
|
||||
/* To compensate for the arrow image's dark margin. */
|
||||
-moz-margin-end: -1px;
|
||||
}
|
||||
|
||||
/* SideMenuWidget groups */
|
||||
|
||||
.side-menu-widget-group-title {
|
||||
padding: 4px;
|
||||
}
|
||||
@ -308,6 +322,8 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* SideMenuWidget items */
|
||||
|
||||
.side-menu-widget-item[theme="dark"] {
|
||||
border-top: 1px solid hsla(210,8%,5%,.25);
|
||||
border-bottom: 1px solid hsla(210,16%,76%,.1);
|
||||
@ -343,15 +359,17 @@
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
background-image: url(itemArrow-ltr.png), linear-gradient(to right, black, black);
|
||||
background-image: url(itemArrow-ltr.png), linear-gradient(to right, #222426, #222426);
|
||||
background-position: center right, top right;
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
background-image: url(itemArrow-rtl.png), linear-gradient(to right, black, black);
|
||||
background-image: url(itemArrow-rtl.png), linear-gradient(to right, #222426, #222426);
|
||||
background-position: center left, top left;
|
||||
}
|
||||
|
||||
/* SideMenuWidget items contents */
|
||||
|
||||
.side-menu-widget-item-label {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
@ -385,6 +403,8 @@
|
||||
text-shadow: 0 1px 1px #111;
|
||||
}
|
||||
|
||||
/* SideMenuWidget misc */
|
||||
|
||||
.side-menu-widget-empty-notice-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
@ -19,6 +19,13 @@
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
||||
/* Sources toolbar */
|
||||
|
||||
#sources-toolbar {
|
||||
border: none; /* Remove the devtools-toolbar's black bottom border. */
|
||||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#pretty-print {
|
||||
min-width: 0;
|
||||
font-weight: bold;
|
||||
@ -437,3 +444,8 @@
|
||||
#body[layout=vertical] .side-menu-widget-item-arrow {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
#body[layout=vertical] .side-menu-widget-group,
|
||||
#body[layout=vertical] .side-menu-widget-item {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
@ -279,6 +279,8 @@
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* SideMenuWidget container */
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(ltr) {
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
@ -287,6 +289,18 @@
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-group {
|
||||
/* To allow visibility of the dark margin shadow. */
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-item {
|
||||
/* To compensate for the arrow image's dark margin. */
|
||||
-moz-margin-end: -1px;
|
||||
}
|
||||
|
||||
/* SideMenuWidget groups */
|
||||
|
||||
.side-menu-widget-group-title {
|
||||
padding: 4px;
|
||||
}
|
||||
@ -308,6 +322,8 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* SideMenuWidget items */
|
||||
|
||||
.side-menu-widget-item[theme="dark"] {
|
||||
border-top: 1px solid hsla(210,8%,5%,.25);
|
||||
border-bottom: 1px solid hsla(210,16%,76%,.1);
|
||||
@ -343,15 +359,17 @@
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
background-image: url(itemArrow-ltr.png), linear-gradient(to right, black, black);
|
||||
background-image: url(itemArrow-ltr.png), linear-gradient(to right, #222426, #222426);
|
||||
background-position: center right, top right;
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
background-image: url(itemArrow-rtl.png), linear-gradient(to right, black, black);
|
||||
background-image: url(itemArrow-rtl.png), linear-gradient(to right, #222426, #222426);
|
||||
background-position: center left, top left;
|
||||
}
|
||||
|
||||
/* SideMenuWidget items contents */
|
||||
|
||||
.side-menu-widget-item-label {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
@ -385,6 +403,8 @@
|
||||
text-shadow: 0 1px 1px #111;
|
||||
}
|
||||
|
||||
/* SideMenuWidget misc */
|
||||
|
||||
.side-menu-widget-empty-notice-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
@ -111,3 +111,25 @@
|
||||
max-height: 75vh;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tooltip widget (see browser/devtools/shared/widgets/Tooltip.js) */
|
||||
|
||||
.devtools-tooltip.devtools-tooltip-tooltip {
|
||||
/* If the tooltip uses a <tooltip> XUL element */
|
||||
-moz-appearance: none;
|
||||
padding: 4px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.devtools-tooltip.devtools-tooltip-panel .panel-arrowcontent {
|
||||
/* If the tooltip uses a <panel> XUL element instead */
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.devtools-tooltip-tiles {
|
||||
background-color: #eee;
|
||||
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
|
||||
linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 10px 10px;
|
||||
}
|
||||
|
@ -17,6 +17,13 @@
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
||||
/* Sources toolbar */
|
||||
|
||||
#sources-toolbar {
|
||||
border: none; /* Remove the devtools-toolbar's black bottom border. */
|
||||
-moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */
|
||||
}
|
||||
|
||||
#pretty-print {
|
||||
min-width: 0;
|
||||
font-weight: bold;
|
||||
@ -440,3 +447,8 @@
|
||||
#body[layout=vertical] .side-menu-widget-item-arrow {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
#body[layout=vertical] .side-menu-widget-group,
|
||||
#body[layout=vertical] .side-menu-widget-item {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
@ -283,6 +283,8 @@
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* SideMenuWidget container */
|
||||
|
||||
.side-menu-widget-container[with-arrows=true]:-moz-locale-dir(ltr) {
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
@ -291,6 +293,18 @@
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-group {
|
||||
/* To allow visibility of the dark margin shadow. */
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
.side-menu-widget-container[with-arrows=true] .side-menu-widget-item {
|
||||
/* To compensate for the arrow image's dark margin. */
|
||||
-moz-margin-end: -1px;
|
||||
}
|
||||
|
||||
/* SideMenuWidget groups */
|
||||
|
||||
.side-menu-widget-group-title {
|
||||
padding: 4px;
|
||||
}
|
||||
@ -312,6 +326,8 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* SideMenuWidget items */
|
||||
|
||||
.side-menu-widget-item[theme="dark"] {
|
||||
border-top: 1px solid hsla(210,8%,5%,.25);
|
||||
border-bottom: 1px solid hsla(210,16%,76%,.1);
|
||||
@ -347,15 +363,17 @@
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
background-image: url(itemArrow-ltr.png), linear-gradient(to right, black, black);
|
||||
background-image: url(itemArrow-ltr.png), linear-gradient(to right, #222426, #222426);
|
||||
background-position: center right, top right;
|
||||
}
|
||||
|
||||
.side-menu-widget-item.selected > .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
background-image: url(itemArrow-rtl.png), linear-gradient(to right, black, black);
|
||||
background-image: url(itemArrow-rtl.png), linear-gradient(to right, #222426, #222426);
|
||||
background-position: center left, top left;
|
||||
}
|
||||
|
||||
/* SideMenuWidget items contents */
|
||||
|
||||
.side-menu-widget-item-label {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
@ -388,6 +406,8 @@
|
||||
color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* SideMenuWidget misc */
|
||||
|
||||
.side-menu-widget-empty-notice-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
@ -631,6 +631,7 @@ public class FaviconCache {
|
||||
startWrite();
|
||||
|
||||
try {
|
||||
mCurrentSize.set(0);
|
||||
mBackingMap.clear();
|
||||
mOrdering.clear();
|
||||
} finally {
|
||||
|
Binary file not shown.
Binary file not shown.
@ -56,25 +56,6 @@ var provider = {
|
||||
};
|
||||
dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
|
||||
|
||||
/**
|
||||
* Imports a download test file to use. Works with rdf and sqlite files.
|
||||
*
|
||||
* @param aFName
|
||||
* The name of the file to import. This file should be located in the
|
||||
* same directory as this file.
|
||||
*/
|
||||
function importDownloadsFile(aFName)
|
||||
{
|
||||
var file = do_get_file(aFName);
|
||||
var newFile = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
if (/\.rdf$/i.test(aFName))
|
||||
file.copyTo(newFile, "downloads.rdf");
|
||||
else if (/\.sqlite$/i.test(aFName))
|
||||
file.copyTo(newFile, "downloads.sqlite");
|
||||
else
|
||||
do_throw("Unexpected filename!");
|
||||
}
|
||||
|
||||
var gDownloadCount = 0;
|
||||
/**
|
||||
* Adds a download to the DM, and starts it.
|
||||
|
@ -1,36 +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/. */
|
||||
|
||||
// This tests that downloads in the scanning state are set to a completed state
|
||||
// upon service initialization.
|
||||
|
||||
importDownloadsFile("bug_401582_downloads.sqlite");
|
||||
|
||||
const nsIDownloadManager = Ci.nsIDownloadManager;
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].getService(nsIDownloadManager);
|
||||
|
||||
function test_noScanningDownloads()
|
||||
{
|
||||
var stmt = dm.DBConnection.createStatement(
|
||||
"SELECT * " +
|
||||
"FROM moz_downloads " +
|
||||
"WHERE state = ?1");
|
||||
stmt.bindByIndex(0, nsIDownloadManager.DOWNLOAD_SCANNING);
|
||||
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.reset();
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
var tests = [test_noScanningDownloads];
|
||||
|
||||
function run_test()
|
||||
{
|
||||
if (oldDownloadManagerDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length; i++)
|
||||
tests[i]();
|
||||
}
|
@ -1,24 +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/. */
|
||||
|
||||
// This file ensures that the download manager service can be instantiated with
|
||||
// a certain downloads.sqlite file that had incorrect data.
|
||||
|
||||
importDownloadsFile("bug_409179_downloads.sqlite");
|
||||
|
||||
function run_test()
|
||||
{
|
||||
if (oldDownloadManagerDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var caughtException = false;
|
||||
try {
|
||||
var dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
} catch (e) {
|
||||
caughtException = true;
|
||||
}
|
||||
do_check_false(caughtException);
|
||||
}
|
@ -1,24 +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/. */
|
||||
|
||||
// Make sure we remove old, now-unused downloads.rdf (pre-Firefox 3 storage)
|
||||
// when starting the download manager.
|
||||
|
||||
function run_test()
|
||||
{
|
||||
if (oldDownloadManagerDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the downloads.rdf file
|
||||
importDownloadsFile("empty_downloads.rdf");
|
||||
|
||||
// Make sure it got created
|
||||
let rdfFile = dirSvc.get("DLoads", Ci.nsIFile);
|
||||
do_check_true(rdfFile.exists());
|
||||
|
||||
// Initialize the download manager, which will delete downloads.rdf
|
||||
Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
|
||||
do_check_false(rdfFile.exists());
|
||||
}
|
@ -3,10 +3,7 @@ head = head_download_manager.js
|
||||
tail = tail_download_manager.js
|
||||
firefox-appdir = browser
|
||||
support-files =
|
||||
bug_401582_downloads.sqlite
|
||||
bug_409179_downloads.sqlite
|
||||
downloads_manifest.js
|
||||
empty_downloads.rdf
|
||||
test_downloads.manifest
|
||||
data/digest.chunk
|
||||
|
||||
@ -15,9 +12,7 @@ support-files =
|
||||
[test_bug_384744.js]
|
||||
[test_bug_395092.js]
|
||||
[test_bug_401430.js]
|
||||
[test_bug_401582.js]
|
||||
[test_bug_406857.js]
|
||||
[test_bug_409179.js]
|
||||
[test_bug_420230.js]
|
||||
[test_cancel_download_files_removed.js]
|
||||
# Bug 676989: test hangs consistently on Android
|
||||
@ -29,7 +24,6 @@ skip-if = os == "android"
|
||||
[test_guid.js]
|
||||
[test_history_expiration.js]
|
||||
[test_offline_support.js]
|
||||
[test_old_download_files_removed.js]
|
||||
[test_private_resume.js]
|
||||
[test_privatebrowsing.js]
|
||||
[test_privatebrowsing_cancel.js]
|
||||
|
@ -362,7 +362,7 @@ this.DownloadIntegration = {
|
||||
#else
|
||||
// For Metro mode on Windows 8, we want searchability for documents
|
||||
// that the user chose to open with an external application.
|
||||
if (this._isImmersiveProcess()) {
|
||||
if (Services.metro && Services.metro.immersive) {
|
||||
directoryPath = yield this.getSystemDownloadsDirectory();
|
||||
} else {
|
||||
directoryPath = this._getDirectory("TmpD");
|
||||
@ -454,14 +454,6 @@ this.DownloadIntegration = {
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines whether it's a Windows Metro app.
|
||||
*/
|
||||
_isImmersiveProcess: function() {
|
||||
// TODO: to be implemented
|
||||
return false;
|
||||
},
|
||||
|
||||
/*
|
||||
* Launches a file represented by the target of a download. This can
|
||||
* open the file with the default application for the target MIME type
|
||||
|
@ -248,6 +248,47 @@ var NodeActor = protocol.ActorClass({
|
||||
response: {}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get the node's image data if any (for canvas and img nodes).
|
||||
* Returns a LongStringActor with the image or canvas' image data as png
|
||||
* a data:image/png;base64,.... string
|
||||
* A null return value means the node isn't an image
|
||||
* An empty string return value means the node is an image but image data
|
||||
* could not be retrieved (missing/broken image).
|
||||
*/
|
||||
getImageData: method(function() {
|
||||
let isImg = this.rawNode.tagName.toLowerCase() === "img";
|
||||
let isCanvas = this.rawNode.tagName.toLowerCase() === "canvas";
|
||||
|
||||
if (!isImg && !isCanvas) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let imageData;
|
||||
if (isImg) {
|
||||
let canvas = this.rawNode.ownerDocument.createElement("canvas");
|
||||
canvas.width = this.rawNode.naturalWidth;
|
||||
canvas.height = this.rawNode.naturalHeight;
|
||||
let ctx = canvas.getContext("2d");
|
||||
try {
|
||||
// This will fail if the image is missing
|
||||
ctx.drawImage(this.rawNode, 0, 0);
|
||||
imageData = canvas.toDataURL("image/png");
|
||||
} catch (e) {
|
||||
imageData = "";
|
||||
}
|
||||
} else if (isCanvas) {
|
||||
imageData = this.rawNode.toDataURL("image/png");
|
||||
}
|
||||
|
||||
return LongStringActor(this.conn, imageData);
|
||||
}, {
|
||||
request: {},
|
||||
response: {
|
||||
data: RetVal("nullable:longstring")
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Modify a node's attributes. Passed an array of modifications
|
||||
* similar in format to "attributes" mutations.
|
||||
@ -283,8 +324,7 @@ var NodeActor = protocol.ActorClass({
|
||||
modifications: Arg(0, "array:json")
|
||||
},
|
||||
response: {}
|
||||
}),
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
|
Binary file not shown.
@ -1,137 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2
|
||||
* 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/. */
|
||||
|
||||
/**
|
||||
* Test added with bug 460086 to test the behavior of the new API that was added
|
||||
* to remove all traces of visiting a site.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Utility Functions
|
||||
|
||||
/**
|
||||
* Creates an nsIURI object for the given file.
|
||||
*
|
||||
* @param aFile
|
||||
* The nsIFile of the URI to create.
|
||||
* @returns an nsIURI representing aFile.
|
||||
*/
|
||||
function uri(aFile)
|
||||
{
|
||||
return Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService).
|
||||
newFileURI(aFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to ensure a URI string is in download history or not.
|
||||
*
|
||||
* @param aURIString
|
||||
* The string of the URI to check.
|
||||
* @param aIsActive
|
||||
* True if the URI should be actively downloaded, false otherwise.
|
||||
*/
|
||||
function check_active_download(aURIString, aIsActive)
|
||||
{
|
||||
let dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
let enumerator = dm.activeDownloads;
|
||||
let found = false;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
|
||||
if (dl.source.spec == aURIString)
|
||||
found = true;
|
||||
}
|
||||
let checker = aIsActive ? do_check_true : do_check_false;
|
||||
checker(found);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Functions
|
||||
|
||||
let destFile = dirSvc.get("TmpD", Ci.nsIFile);
|
||||
destFile.append("dm-test-file");
|
||||
destFile = uri(destFile);
|
||||
let data = [
|
||||
{ source: "http://mozilla.org/direct_match",
|
||||
target: destFile.spec,
|
||||
removed: true
|
||||
},
|
||||
{ source: "http://www.mozilla.org/subdomain",
|
||||
target: destFile.spec,
|
||||
removed: true
|
||||
},
|
||||
{ source: "http://ilovemozilla.org/contains_domain",
|
||||
target: destFile.spec,
|
||||
removed: false
|
||||
},
|
||||
];
|
||||
|
||||
function makeGUID() {
|
||||
let guid = "";
|
||||
for (var i = 0; i < 12; i++)
|
||||
guid += Math.floor(Math.random() * 10);
|
||||
return guid;
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
if (oldDownloadManagerDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We add this data to the database first, but we cannot instantiate the
|
||||
// download manager service, otherwise these downloads will not be placed in
|
||||
// the active downloads array.
|
||||
|
||||
// Copy the empty downloads database to our profile directory
|
||||
let downloads = do_get_file("downloads.empty.sqlite");
|
||||
downloads.copyTo(dirSvc.get("ProfD", Ci.nsIFile), "downloads.sqlite");
|
||||
|
||||
// Open the database
|
||||
let ss = Cc["@mozilla.org/storage/service;1"].
|
||||
getService(Ci.mozIStorageService);
|
||||
let file = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.append("downloads.sqlite");
|
||||
let db = ss.openDatabase(file);
|
||||
|
||||
// Insert the data
|
||||
let stmt = db.createStatement(
|
||||
"INSERT INTO moz_downloads (source, target, state, autoResume, entityID, guid) " +
|
||||
"VALUES (:source, :target, :state, :autoResume, :entityID, :guid)"
|
||||
);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
stmt.params.source = data[i].source;
|
||||
stmt.params.target = data[i].target;
|
||||
stmt.params.state = Ci.nsIDownloadManager.DOWNLOAD_PAUSED;
|
||||
stmt.params.autoResume = 0; // DONT_RESUME is 0
|
||||
stmt.params.entityID = "foo" // just has to be non-null for our test
|
||||
stmt.params.guid = makeGUID();
|
||||
stmt.execute();
|
||||
stmt.reset();
|
||||
}
|
||||
stmt.finalize();
|
||||
stmt = null;
|
||||
db.close();
|
||||
db = null;
|
||||
|
||||
// Check to make sure it's all there
|
||||
for (let i = 0; i < data.length; i++)
|
||||
check_active_download(data[i].source, true);
|
||||
|
||||
// Dispatch the remove call
|
||||
ForgetAboutSite.removeDataFromDomain("mozilla.org");
|
||||
|
||||
// And check our data
|
||||
for (let i = 0; i < data.length; i++)
|
||||
check_active_download(data[i].source, !data[i].removed);
|
||||
|
||||
// Shutdown the download manager.
|
||||
Services.obs.notifyObservers(null, "quit-application", null);
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
[DEFAULT]
|
||||
head = head_forgetaboutsite.js
|
||||
tail =
|
||||
support-files = downloads.empty.sqlite
|
||||
|
||||
[test_removeDataFromDomain.js]
|
||||
[test_removeDataFromDomain_activeDownloads.js]
|
||||
|
@ -1241,7 +1241,14 @@ function waitForCondition(condition, nextTest, errorMsg) {
|
||||
ok(false, errorMsg);
|
||||
moveOn();
|
||||
}
|
||||
if (condition()) {
|
||||
var conditionPassed;
|
||||
try {
|
||||
conditionPassed = condition();
|
||||
} catch (e) {
|
||||
ok(false, e + "\n" + e.stack);
|
||||
conditionPassed = false;
|
||||
}
|
||||
if (conditionPassed) {
|
||||
moveOn();
|
||||
}
|
||||
tries++;
|
||||
|
Loading…
Reference in New Issue
Block a user